new module
Run this to add a new module to a project: it scaffolds the entry point, the first surface, the contract and the module-level shared skeletons in one call.
go tool gpsystem new module <name> [--surface <name>]
Run inside a project (the CLI walks up to gpsystem.yaml). The module name must be lowercase letters/digits: it becomes a Go package name, directory name and URL segment. The same rule applies to the first surface's name (--surface, default api).
What it generates
cmd/<name>/main.go: the ~20-line entry point callingserver.Run, mounting a sub-router at/api/v1internal/modules/<name>/surfaces/register.go(package surfaces):Dependenciesstruct + one exportedRegister<Surface>(router, deps)function per surface (e.g.RegisterApi,RegisterWeb)- One first surface (default
api) undersurfaces/<surface>/: a handler implementing the generated strict interface (its sampleListcalls a minimalservice/seam), apolicy/registry stub, plus a kept-emptyhttp/mapper/directory (.gitkeep) for your own code - The module-level shared skeletons: the module root package (
<name>.go+errors.go: home of the rules, entities and sentinel errors shared by the surfaces, with anErrNotFoundexample) andrepository/(the module's single persistence layer); the first surface stamps these, lateradd surfacecalls skip them spec/typespec/modules/<name>/:<surface>.tspwith a sample operation +models/<surface>.tspapi/oapi-codegen/<name>-<surface>.yamlcodegen config wired to the project's template set- The module's app service in
compose.yml: built from the project'sDockerfile(target: development), wired withdepends_onon postgres/valkey/migrate, dual Traefik routers with path-based routing (see new project)
Anchor insertions
main.tsp← module imports (gpsystem:modules)cmd/<name>/main.go← the<name>surfaces.Register<Surface>(api, deps)call (gpsystem:registrations)surfaces/register.go← a per-surfaceRegister<Surface>function + imports (rendered, then goimports-formatted)compose.yml← the module service at thegpsystem:servicesanchor
Compose insertion
compose.yml and Dockerfile already exist in every project (from new project), so new module only inserts the new module's service at the gpsystem:services anchor, alongside depends_on on postgres/valkey/migrate. If compose.yml is missing its networks:/volumes: section or a postgres pg_isready healthcheck, the module service's YAML is only printed to the terminal instead, for manual insertion; in that case it's often simpler to run add compose --force and get a clean compose.yml rebuilt from the manifest.
After generation
mise run generate # tsp → OpenAPI → strict server code → build
go run ./cmd/<name>
The skeleton has no persistence yet: build the shared rules in the module root package and the persistence in the module-level repository/, and call them from the handler through the surface's service/ seam.