- Backend (GoLang): cmd/ with pkg/ (GoLang code), and test/.
- Frontend (TypeScript/React): web_src/ built with Vite.
- Tooling: Makefile (common tasks), protos/ (protobuf definitions for the API), scripts/ (protobuf generation), db/ (database structure and migrations).
- Documentation: Markdown files in docs/.
- gRPC API implementation in in pkg/grpc/actions
- Database models in pkg/models
- Integration component implementations: pkg/integrations//
- UI component mappers: web_src/src/pages/app/mappers//
- PR titles must follow Conventional Commits and include a release-type prefix:
feat:,fix:,chore:, ordocs:(CI enforces this). - All commits must include a DCO sign-off trailer (
Signed-off-by: Name <email>). Usegit commit -sorgit commit --amend -swhen creating or updating commits.
- Bring up dev containers:
make dev.up - Install deps, codegen, DB:
make dev.setupafterdev.up(re-run when protos, Go modules, or frontend deps change). By default onlysuperplane_devis migrated; useDEV_SETUP_DBS="superplane_dev superplane_test"when you also needsuperplane_test(E2E; backend CI sets this via the environment). Ifgo mod download/go buildfail with missing files undertmp/go/pkg/mod, runmake dev.clean.go.cachethenmake dev.setup.go(often after disk-full or interrupted downloads). - Start API + Vite:
make dev.server(aftermake dev.up) — UI at http://localhost:8000; usemake dev.server.fgfor foreground logs - One-shot backend tests:
make test(Golang). - Targeted backend tests:
make test PKG_TEST_PACKAGES=./pkg/workers - Targeted E2E tests:
make e2e E2E_TEST_PACKAGES=./test/e2e/workflows - For E2E test authoring, see docs/contributing/e2e-tests.md
- For performance profiling of the dev server, see docs/contributing/profiling.md
- After updating UI code, always run
make check.build.uito verify everything is correct - After editing JS code, always run
make format.jsto make sure that the files are consistently formatted - After editing Golang code, always run
make format.goto make sure that files are consistently formatted - After updating GoLang code, always check it with
make lint && make check.build.app - NEVER MANUALLY CREATE MIGRATION FILES. ALWAYS use
make db.migration.create NAME=<name>to generate DB migrations. Always use dashes instead of underscores in the name. We do not write migrations to rollback, so leave the*.down.sqlfiles empty. After adding a migration, runmake db.migrate DB_NAME=<DB_NAME>(requires a running app container frommake dev.up), where DB_NAME can besuperplane_devorsuperplane_test - When validating enum fields in protobuf requests, ensure that the enums are properly mapped to constants in the
pkg/modelspackage. Check theProto*and*ToProtofunctions in pkg/grpc/actions/common.go. - When adding a new worker in pkg/workers, always add its startup to
cmd/server/main.go, and update the docker compose files with the new environment variables that are needed. - After adding new API endpoints, ensure the new endpoints have their authorization covered in
pkg/authorization/interceptor.go - For UI component workflow, see web_src/AGENTS.md
- For new components or triggers, see docs/contributing/component-implementations.md
- For component design guidelines and quality standards, see docs/contributing/component-design.md
- After updating the proto definitions in protos/, always regenerate them, the OpenAPI spec for the API, and SDKs for the CLI and the UI (requires a running
appcontainer frommake dev.up):make pb.gento regenerate protobuf filesmake openapi.spec.gento generate OpenAPI spec for the APImake openapi.client.gento generate GoLang SDK for the APImake openapi.web.client.gento generate TypeScript SDK for the UI
- Always write clean code: work test-first by default, then keep names clear, functions focused, side effects explicit, control flow shallow, and error handling useful.
- Tests end with _test.go
- Always prefer early returns over else blocks when possible
- GoLang: prefer
anyoverinterface{}types - GoLang: when checking for the existence of an item on a list, use
slice.Containsorslice.ContainsFunc - When naming variables, avoid names like
*Stror*UUID; Go is a typed language, we don't need types in the variables names - When writing tests that require specific timestamps to be used, always use timestamps based off of
time.Now(), instead of absolute times created withtime.Date - The name of the application is "SuperPlane", not "Superplane" in all user-facing text (user interfaces, emails, notifications, documentation, etc.).
- Frontend: do not create or use
web_src/src/utils/*orutils.tsfiles. Put shared non-React helpers inweb_src/src/lib/, and put React-specific reusable logic inweb_src/src/hooks/.
When working with database transactions, follow these rules to ensure data consistency:
-
NEVER call
database.Conn()inside a function that receives atx *gorm.DBparameter- ❌ Bad:
func process(tx *gorm.DB) { user, _ := models.FindUser(id) }where FindUser callsdatabase.Conn() - ✅ Good:
func process(tx *gorm.DB) { user, _ := models.FindUserInTransaction(tx, id) }
- ❌ Bad:
-
Always propagate the transaction context through the entire call chain
- Pass
txas the first parameter to all functions that need database access - If a model method is used within a transaction, create an
*InTransaction()variant that acceptstx
- Pass
-
Context constructors must accept
tx *gorm.DBif they perform database queries- ❌ Bad:
NewAuthContext(orgID, service)that internally callsdatabase.Conn() - ✅ Good:
NewAuthContext(tx, orgID, service)that uses the passed transaction
- ❌ Bad:
-
When creating new model methods:
- Create both variants:
FindUser()andFindUserInTransaction(tx *gorm.DB) - The non-transaction variant should call the transaction variant:
return FindUserInTransaction(database.Conn(), ...)
- Create both variants:
Why this matters: Using database.Conn() inside transaction contexts breaks isolation, causes data inconsistency on rollback, and can lead to race conditions.
Order declarations in each model file as follows:
- Struct — package constants used by the model, then the struct type
- Constructors —
New…functions that build values for the model (including name/ID helpers) - Getters — methods on the struct (e.g.
TableName(), computed accessors) - Conn wrappers — functions that call
…InTransaction(database.Conn(), …)(or start a transaction withdatabase.Conn().Transactionwhen the whole operation must be atomic) - InTransaction methods — functions whose first parameter is
tx *gorm.DB
List all conn wrappers before all …InTransaction methods (sections 4 then 5). Place private helpers after the public API in the file.
The dev environment is entirely Docker-based. The VM needs Docker installed (with fuse-overlayfs storage driver and iptables-legacy for nested container support). All build, lint, test, and run commands execute inside Docker containers via docker compose exec.
make dev.up— builds the dev-base Docker image and starts containers (app, db/PostgreSQL, rabbitmq). First run builds the image (~3-5 min); subsequent runs reuse the cached image.make dev.setup— installs npm deps, downloads Go modules, runs protobuf codegen, creates and migrates bothsuperplane_devandsuperplane_testdatabases.make dev.server— startsair(Go hot-reload) and Vite dev server inside the app container. Health check athttp://localhost:8000/health.
- The
appcontainer starts withsleep infinityby default. You must explicitly runmake dev.serverto start the API + UI stack. - All
makecommands must be run from the/agent/repos/superplanedirectory. - The Docker daemon must be started manually in cloud VMs:
sudo dockerd &>/tmp/dockerd.log &— wait ~3-4 seconds before issuing Docker commands. - After Docker daemon starts, ensure the socket is accessible:
sudo chmod 666 /var/run/docker.sock. - Owner setup is enabled by default (
OWNER_SETUP_ENABLED=yes). On first UI load athttp://localhost:8000, you'll be prompted to create an admin account with email/password. BLOCK_SIGNUP=yesis the default, so only the owner setup flow works for account creation (no open registration).- If
make dev.setupfails ongo mod downloadwith missing files, runmake dev.clean.go.cachethenmake dev.setup.go. - The
make dev.setupMakefile target already creates and migrates bothsuperplane_devandsuperplane_testdatabases.