CLI Reference

new module

Generate a module (entry point, first surface, contract) into the current project.

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 calling server.Run, mounting a sub-router at /api/v1
  • internal/modules/<name>/surfaces/register.go (package surfaces): Dependencies struct + one exported Register<Surface>(router, deps) function per surface (e.g. RegisterApi, RegisterWeb)
  • One first surface (default api) under surfaces/<surface>/: a handler implementing the generated strict interface (its sample List calls a minimal service/ seam), a policy/ registry stub, plus a kept-empty http/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 an ErrNotFound example) and repository/ (the module's single persistence layer); the first surface stamps these, later add surface calls skip them
  • spec/typespec/modules/<name>/: <surface>.tsp with a sample operation + models/<surface>.tsp
  • api/oapi-codegen/<name>-<surface>.yaml codegen config wired to the project's template set
  • The module's app service in compose.yml: built from the project's Dockerfile (target: development), wired with depends_on on 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-surface Register<Surface> function + imports (rendered, then goimports-formatted)
  • compose.yml ← the module service at the gpsystem:services anchor

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.

The skeleton compiles and serves immediately (including correct problem+json error responses), so you can wire CI and deployment before writing business logic.
Copyright © 2026