Operations
Operations
Section titled “Operations”Environment variables
Section titled “Environment variables”Two binaries ship: agents-server and agents-migrator. Both bind the same
Config struct from environment variables via sdk/config.MustBind.
Required — server
Section titled “Required — server”| Variable | Description |
|---|---|
DATABASE_URL | PgBouncer connection string, port 6432, transaction-pool mode |
IDENTITY_GRPC_ADDR | gRPC address of the identity service, e.g. identity.paper-board.svc.cluster.local:50051 |
ANTHROPIC_API_KEY | Anthropic API key; omit to fall back to the deterministic dev mock |
Required — migrator
Section titled “Required — migrator”| Variable | Description |
|---|---|
MIGRATION_DB_URL | Direct Postgres connection string, port 5432 (advisory lock requires session pool) |
Do not swap these two URLs. PgBouncer breaks advisory locks; direct Postgres loses prepared statements at server scale.
Optional — server
Section titled “Optional — server”| Variable | Default | Description |
|---|---|---|
SERVER_PORT | 8080 | HTTP listen port |
READ_HEADER_TIMEOUT | 5s | Go http.Server.ReadHeaderTimeout |
SHUTDOWN_TIMEOUT | 30s | Graceful shutdown deadline |
REQUEST_TIMEOUT | 30s | Timeout for non-SSE routes |
SSE_TIMEOUT | 300s | Timeout for streaming routes only |
BODY_LIMIT_BYTES | 1048576 | Max request body size (1 MiB) |
DB_MAX_CONNS | 20 | PgBouncer pool max connections |
DB_MIN_CONNS | 2 | PgBouncer pool min connections |
DB_MAX_CONN_LIFETIME | 30m | PgBouncer connection max lifetime |
COMPUTE_GRPC_ADDR | — | gRPC address of the compute service; empty disables tool dispatch |
LOG_LEVEL | info | debug | info | warn | error |
OTEL_EXPORTER_OTLP_ENDPOINT | — | OTLP endpoint for traces; empty disables |
ENV | dev | dev | staging | prod |
ANTHROPIC_API_KEY is injected from a Kubernetes Secret (anthropicKeySecret
in Helm values). Non-secret tunables are surfaced via a ConfigMap
(server.config in Helm values).
Migrator commands
Section titled “Migrator commands”# Apply all pending migrations (Helm pre-install/pre-upgrade hook runs this automatically)agents-migrator up
# Dry-run: print SQL without executingagents-migrator up --dry-run
# Roll back N migrationsagents-migrator down 1
# Force schema version (use after a partial failure)agents-migrator force 2
# Print current versionagents-migrator version
# Drop schema (dev only — requires MIGRATOR_ENV=dev)MIGRATOR_ENV=dev agents-migrator dropThe migrator holds Postgres advisory lock id 3 (agents) during up and down
so parallel runs from two Helm jobs cannot race. Lock id 3 is cluster-wide;
do not reuse it in another service.
// from paper-board/agents/cmd/migrator/main.go:23-29mc := migrator.Config{ DBURL: cfg.MigrationDatabaseURL, Schema: "agents", EmbedFS: migrations.SchemaFS, EmbedRoot: "schema",}if err := migrator.Run(context.Background(), mc, os.Args[1:]); err != nil {Helm values
Section titled “Helm values”Chart: oci://ghcr.io/paper-board/helm/agents (current version 0.2.3).
Migrator job
Section titled “Migrator job”# from paper-board/agents/helm/agents/values.yaml:2-18migrator: enabled: true image: repository: ghcr.io/paper-board/agents-migrator tag: v0.2.3 pullPolicy: IfNotPresent resources: requests: { cpu: 100m, memory: 128Mi } limits: { cpu: 200m, memory: 256Mi } migrationDbUrlSecret: name: agents-migration-db-url key: urlThe migrator Job runs as a Helm pre-install,pre-upgrade hook with
backoffLimit: 0. A failed migration blocks the release.
Server deployment
Section titled “Server deployment”# from paper-board/agents/helm/agents/values.yaml:20-54server: enabled: true replicas: 1 image: repository: ghcr.io/paper-board/agents-server tag: v0.2.3 pullPolicy: IfNotPresent service: type: ClusterIP port: 8080 databaseUrlSecret: name: agents-app-db-url key: url anthropicKeySecret: name: anthropic-api-key key: key config: serverPort: 8080 sseTimeout: "300s" logLevel: "info"Network policy
Section titled “Network policy”# from paper-board/agents/helm/agents/values.yaml:56-74networkPolicy: enabled: true egress: pgbouncer: enabled: true port: 6432 identity: enabled: true grpcPort: 50051Egress is allowlisted to PgBouncer (6432) and identity gRPC (50051) only. Ingress from gateway is added in Phase 7.
Deployment topology
Section titled “Deployment topology”flowchart TB subgraph cluster["Kubernetes cluster"] subgraph ns["paper-board namespace"] migrator["agents-migrator\nJob (pre-upgrade hook)"] server["agents-server\nDeployment"] cm["server-configmap\nConfigMap"] svc["agents\nService :8080"] end subgraph data["data plane"] pgb[("PgBouncer\n:6432")] pg[("Postgres\nagents schema\n:5432")] end subgraph identity_ns["identity namespace"] idgrpc["identity-server\ngRPC :50051"] end end ext{{"Anthropic API"}}
migrator -->|"MIGRATION_DB_URL"| pg server -->|"DATABASE_URL"| pgb pgb --> pg server -->|"IDENTITY_GRPC_ADDR"| idgrpc server -->|"ANTHROPIC_API_KEY"| ext cm --> server svc --> server
classDef controlPlane fill:#10b981,stroke:#047857,color:#fff classDef dataPlane fill:#3b82f6,stroke:#1d4ed8,color:#fff classDef sandbox fill:#f97316,stroke:#c2410c,color:#fff classDef external fill:#ef4444,stroke:#b91c1c,color:#fff classDef persistence fill:#6b7280,stroke:#374151,color:#fff
class migrator,idgrpc controlPlane class server,svc,cm dataPlane class pgb,pg persistence class ext externalCI / release
Section titled “CI / release”CI reuses the shared paper-board/.github/.github/workflows/go-ci.yml
reusable workflow. The local wrapper is .github/workflows/ci.yml.
On a semver tag (v*), the release workflow builds agents-server and
agents-migrator images, pushes them to
ghcr.io/paper-board/agents-{server,migrator}:<tag>, then packages and pushes
the Helm chart as an OCI artifact to ghcr.io/paper-board/helm/agents.
The chart version in helm/agents/Chart.yaml must match the release tag. The
release workflow enforces this with a version-match step before helm push.