Skip to main content

Deployment and Operations

This document describes how to run the backend and frontend (locally and in production), environment variables, and optional observability. For the full local Docker Compose stack (PostgreSQL, OpenTelemetry Collector, Loki, Tempo, Prometheus, Grafana), see deploy/README.md.

Audience: Operators and developers setting up or deploying the zero-trust control plane.

Overview

  • Backend: Go gRPC server (cmd/server); requires PostgreSQL and (for auth) JWT keys.
  • Frontend: Next.js app (BFF); calls backend gRPC via env-configured URL.
  • Optional: Observability stack (OTLP collector, Loki, Tempo, Prometheus, Grafana) via deploy/docker-compose.yml.

Local development (Makefile)

From the repo root you can drive local setup entirely with the Makefile. For step-by-step manual steps and Docker Compose details, see deploy/README.md.

Prerequisites: Docker and Docker Compose (v2+), Go, Node.js 20+.

One-command setup

From the repo root run:

make setup

This runs in order:

  1. ensure-env — Creates backend/.env and frontend/.env from deploy/.env.example if missing; if backend/.env exists but DATABASE_URL uses user root, it is replaced with the local deploy default (ztcp/ztcp).
  2. up — Starts the Docker stack (PostgreSQL + observability) from deploy/.
  3. wait-postgres — Waits for Postgres to be ready (up to 30s).
  4. migrate — Runs backend/scripts/migrate.sh up (requires DATABASE_URL in backend/.env).
  5. seed — Runs backend/scripts/seed.sh (inserts dev users, e.g. dev@example.com). Skip with SKIP_SEED=1 make setup.
  6. configure-grafana — Waits for Grafana and notes that datasources and the ZTCP Telemetry dashboard are provisioned at http://localhost:3002.

Starting the app after setup

In one terminal run make run-backend; in another run make run-frontend. Open http://localhost:3000. Optionally run the docs site with make run-docs (serves at http://localhost:3001).

To stop the Docker stack later: make down.

Makefile targets reference

TargetDescription
setupFull local setup (ensure-env, up, wait-postgres, migrate, seed, configure-grafana).
envCopy deploy/.env.example to backend/.env and frontend/.env if missing (no overwrite).
ensure-envSame as env plus fix DATABASE_URL when it uses user root.
upStart Docker stack from deploy/.
downStop Docker stack.
wait-postgresBlock until Postgres is ready.
wait-grafanaBlock until Grafana health responds (optional).
configure-grafanaEnsure Grafana is ready; used by setup.
migrateRun backend migrations (ensure-env then backend/scripts/migrate.sh up).
seedRun backend seed script (ensure-env then backend/scripts/seed.sh).
run-backendRun backend gRPC server (go run ./cmd/server in backend).
run-frontendInstall frontend deps and run Next.js dev server.
install-frontendnpm install in frontend.
install-docsnpm install in docs-site.
run-docsInstall docs deps and run Docusaurus on port 3001.

Variables: The Makefile uses BACKEND_DIR=backend, DEPLOY_DIR=deploy, DOCS_DIR=docs-site, FRONTEND_DIR=frontend. These are rarely overridden.

Running the server

From the backend directory (or from repo root use make run-backend):

go run ./cmd/server

Or from repo root: make run-backend. The gRPC server listens on :8080 by default (GRPC_ADDR in env).

Before first run: Set DATABASE_URL and run migrations (./scripts/migrate.sh from backend). For login/register, set JWT_PRIVATE_KEY and JWT_PUBLIC_KEY. See Backend auth and backend/.env.example.

Running the frontend

From the frontend directory (or from repo root use make run-frontend):

npm install
npm run dev

Or from repo root: make run-frontend. The app is served at http://localhost:3000 and calls the backend at the URL set in BACKEND_GRPC_URL (default localhost:8080).

Environment checklist

Backend (backend/.env.example)

VariableRequiredNotes
GRPC_ADDRNoListen address (default :8080)
DATABASE_URLYes (for full features)Postgres DSN
JWT_PRIVATE_KEYYes (for auth)PEM or path to file
JWT_PUBLIC_KEYYes (for auth)PEM or path to file
JWT_ISSUER, JWT_AUDIENCENoDefaults: ztcp-auth, ztcp-api
JWT_ACCESS_TTL, JWT_REFRESH_TTLNoe.g. 15m, 168h
SMS_LOCAL_*For SMS OTPPoC MFA
APP_ENV, OTP_RETURN_TO_CLIENTNoDev OTP; must not be production when OTP_RETURN_TO_CLIENT=true
OTEL_EXPORTER_OTLP_*NoOptional OTLP export

Frontend (frontend/.env.example)

VariableRequiredNotes
BACKEND_GRPC_URLYesBackend gRPC host:port (e.g. localhost:8080)
NEXT_PUBLIC_DOCS_URLNoWhen set, the "Docs" link in the header and on the home alert points to this URL (e.g. external docs site). If unset, the link may point to a default or be hidden depending on implementation.
NEXT_PUBLIC_DEV_OTP_ENABLEDNoWhen true, OTP is returned to client (no SMS); can enable in prod for PoC

Local one-shot setup: from repo root run make setup (creates .env from deploy/.env.example if missing, starts Docker, migrates, optional seed). Then run make run-backend and make run-frontend in two terminals.

Production

  • Environment: Set APP_ENV=production; do not set OTP_RETURN_TO_CLIENT=true. Use strong JWT_PRIVATE_KEY/JWT_PUBLIC_KEY and a secure DATABASE_URL.
  • Migrations: Run migrations before or during deployment (e.g. backend/scripts/migrate.sh up).
  • TLS: For production, expose the gRPC server behind TLS (e.g. reverse proxy or server-side TLS). Configure BACKEND_GRPC_URL on the frontend to use the correct scheme and host.
  • Docker/Kubernetes: Use deploy/docker-compose.yml as a reference for Postgres and observability; the backend and frontend can be run in containers or on VMs with the same env and migration steps.