A persona-governed, cryptographically auditable contract lifecycle system for IBM Confidential Computing Container Runtime (CCRT) deployments.
| Requirement | Minimum version |
|---|---|
| Docker Engine | 20.10+ |
| Docker Compose plugin | v2.0+ |
| Git | any |
| RAM | 2 GB |
| Disk | 5 GB |
No Go toolchain is required on the host — the image is built inside a multi-stage Docker build.
git clone <repository-url>
cd contract-managerAll configuration lives in compose/.env. Copy the example and fill in the required values:
cp compose/.env.example compose/.envOpen compose/.env and set at minimum the four required fields (the compose file will refuse to start without them):
# Required — pick strong unique values
POSTGRES_PASSWORD=<strong-password>
ADMIN_NAME=System Admin
[email protected]
ADMIN_PASSWORD=<strong-password>All other values have working defaults. See
compose/.env.examplefor the full reference.
The contract-manager:local image does not exist in any registry — it must be built from source:
docker build -t contract-manager:local .This runs a two-stage build: compiles the Go binary and the migrate tool inside the builder stage, then copies them into a minimal Alpine runtime image. Expect 2–5 minutes on first run (module download + compile); subsequent builds use the layer cache.
docker compose -f compose/docker-compose.yaml --project-directory compose up -dThis starts five containers in dependency order:
hpcr-postgres— PostgreSQL 16 databasehpcr-migrate— runs all schema migrations then exitshpcr-backend— Go API server (waits for migrate to complete)hpcr-reverse-proxy— nginx reverse proxy (waits for backend healthy)hpcr-pgadmin— pgAdmin 4 web UI
# All four long-running containers should show "healthy"
docker compose -f compose/docker-compose.yaml --project-directory compose ps
# Backend health endpoint
curl http://localhost:8080/health
# Expected: {"status":"ok"}| Interface | URL | Default credentials |
|---|---|---|
| API / App | http://localhost:8080 |
ADMIN_EMAIL / ADMIN_PASSWORD from .env |
| pgAdmin | http://localhost:5050 |
PGADMIN_DEFAULT_EMAIL / PGADMIN_DEFAULT_PASSWORD from .env |
| Swagger UI | http://localhost:8080/swagger |
— |
First login: Every new account (including the seeded admin) must complete a mandatory setup flow before accessing any functionality: change the initial password, generate an RSA-4096 key pair, and register the public key.
Each client may register its own identity key: at most five active RSA-4096 keys per user, each valid for 90 days. Later registrations require password confirmation and, while an active key exists, a request signed with that key. Owners and admins can revoke individual keys; admins can also revoke all active keys. See the API reference and multi-key testing guide.
Existing clients remain compatible for normal use with one active key. Clients must update their registration flow before renewing keys or adding devices, and select the local key's fingerprint for signed requests instead of using the newest key returned by login.
docker build -t contract-manager:local . \
&& docker compose -f compose/docker-compose.yaml --project-directory compose up -d --no-deps backend# All services
docker compose -f compose/docker-compose.yaml --project-directory compose logs -f
# Single service
docker compose -f compose/docker-compose.yaml --project-directory compose logs -f backenddocker compose -f compose/docker-compose.yaml --project-directory compose downdocker compose -f compose/docker-compose.yaml --project-directory compose down -v
sudo rm -rf compose/data/postgresdocker compose -f compose/docker-compose.yaml --project-directory compose \
exec postgres psql -U hpcr hpcr_builder.
├── cmd/server/ # Main entrypoint — wires services, builds router
├── internal/
│ ├── config/ # Environment variable config
│ ├── contract/ # Engine interface + contract-go v3 implementation
│ ├── handler/ # HTTP handlers (writeJSON / writeError)
│ ├── keymgmt/ # KeyProvider interface; db implementation
│ ├── middleware/ # Auth, RBAC, signature, rate-limit, setup guard
│ ├── model/ # Domain types + AppError constructors
│ ├── repository/ # SQLC-generated DB layer (do not edit *.sql.go)
│ ├── repository/queries/ # SQL source files — edit these, then run ./scripts/generate-repository.sh
│ └── service/ # Business logic
├── migrations/ # golang-migrate SQL files (020 migrations)
├── compose/
│ ├── docker-compose.yaml # Stack definition
│ ├── .env.example # Config template — copy to .env
│ └── config/nginx/ # nginx default.conf and tls.conf
├── Dockerfile # Multi-stage build: Go builder → Alpine runtime
└── .dockerignore # Excludes compose/data/, .bob/, .git/
| Container | Image | Exposed port | Purpose |
|---|---|---|---|
hpcr-reverse-proxy |
nginx:1.27-alpine |
8080 → 80 |
Reverse proxy, rate limiting, TLS termination |
hpcr-backend |
contract-manager:local |
internal 8080 | Go API server |
hpcr-postgres |
postgres:16-alpine |
internal 5432 | PostgreSQL database |
hpcr-migrate |
contract-manager:local |
— | Schema migration runner (exits on completion) |
hpcr-pgadmin |
dpage/pgadmin4:9 |
5050 → 80 |
Database management UI |
All variables are set in compose/.env. The table below lists those that must be changed from the example defaults before first run:
| Variable | Required | Description |
|---|---|---|
POSTGRES_PASSWORD |
✅ | PostgreSQL password for the hpcr user |
ADMIN_NAME |
✅ | Display name for the seeded admin account |
ADMIN_EMAIL |
✅ | Email for the seeded admin account |
ADMIN_PASSWORD |
✅ | Initial password for the seeded admin account |
DATABASE_URL |
✅ | Full postgres DSN — must match POSTGRES_* values |
MIGRATE_DATABASE_URL |
✅ | Same DSN used by the migration runner |
PGADMIN_DEFAULT_PASSWORD |
recommended | pgAdmin login password |
DATABASE_URLandMIGRATE_DATABASE_URLin.env.exampleuse${POSTGRES_*}variable interpolation — if your shell or Docker Compose version does not expand these, set them explicitly as literal strings.
SSO login is disabled unless all four variables below are set. Users must already exist (matched by email); SSO never creates accounts, and password login keeps working.
| Variable | Description |
|---|---|
APPID_CLIENT_ID |
OIDC client ID registered for the application |
APPID_CLIENT_SECRET |
OIDC client secret |
APPID_OAUTH_SERVER_URL |
Base URL of the OIDC endpoint (without /authorize) |
APPID_REDIRECT_URI |
Redirect URI registered with the provider; must match exactly |
go test ./cmd/... ./internal/...go vet ./cmd/... ./internal/...
gofmt -w .After editing any .sql file under internal/repository/queries/:
./scripts/generate-repository.shNever edit
internal/repository/*.sql.godirectly — they are overwritten by./scripts/generate-repository.sh. The script runs SQLC and copies license headers from the SQL source files into the generated query files.
pull access denied for contract-manager
The image must be built locally first. Run step 3 above (docker build -t contract-manager:local .).
POSTGRES_PASSWORD must be set / ADMIN_PASSWORD must be set
The compose file enforces these with :?. Copy .env.example to .env and fill in the required fields.
hpcr-migrate exits with non-zero / backend won't start
docker compose -f compose/docker-compose.yaml --project-directory compose logs migrateA migration failure (e.g. dirty state from a previous partial run) will block the backend. To reset:
docker compose -f compose/docker-compose.yaml --project-directory compose down -v
sudo rm -rf compose/data/postgres
docker compose -f compose/docker-compose.yaml --project-directory compose up -d/health returns 429
This should not happen — the /health endpoint is registered outside the rate-limited middleware group. If you see it, ensure you are running the image built from the current source.
compose/data/postgres permission errors
The postgres data directory is written by the postgres container running as its internal UID. If you see permission errors after a sudo rm -rf, recreate the directory:
mkdir -p compose/data/postgresDocker will repopulate it on next start.
Workload and environment inputs are validated against the embedded CCRT schema before encryption, separately or as a complete plaintext contract. Builds use one runtime encryption certificate for workload, environment and attestation. See contract input validation and encryption for accepted formats, verification and compatibility limits.
Authenticated clients can discover platform-specific versions using GET /v2/encryption-certificates/versions and fetch an exact embedded PEM using GET /v2/encryption-certificates/certificate?platform=ccrt&version=26.7.2. The legacy flat versions field remains compatible. Catalog data comes from pinned contract-go v3.5.0; only CCRT is supported by the current contract workflow. See certificate API and rollout details. Deploy this additive API before the companion desktop selector; its custom certificate upload remains available.
Deploy this backend before releasing the paired UI validation change: the UI requires POST /v2/contract-validate. Existing unpinned builds cannot retrospectively verify the encryption recipient of older sections. No database migration is added by this change.