Registry

Deploy (Docker + Vercel)

Standing up a Handoff registry locally with Docker, or in production on Vercel.

Both paths deploy the same handoff-app repository, unmodified: no per-client materialization, no customized build. See Model for why. Docker is for local dev/test against a real Postgres; Vercel is the production path.

Start Postgres

Terminal
docker run -d \
  --name handoff-registry \
  -e POSTGRES_DB=handoff_registry \
  -e POSTGRES_USER=handoff \
  -e POSTGRES_PASSWORD=changeme \
  -p 5433:5432 \
  postgres:16-alpine

until docker exec handoff-registry pg_isready -U handoff; do sleep 1; done

Start the registry from handoff-app

Terminal
cd /path/to/handoff-app

DATABASE_URL=postgresql://handoff:changeme@localhost:5433/handoff_registry \
AUTH_SECRET=$(openssl rand -hex 32) \
HANDOFF_SYNC_SECRET=dev-registry-secret \
  npm run build:registry && \
  cd src/app && npx next start -p 4002

# Or for hot-reload development: npm run dev (from the handoff-app root)

The registry runs at http://localhost:4002. npm run build:registry applies pending migrations as a build step (scripts/migrate-on-deploy.mjs) before next build runs. Outside Vercel, instrumentation.ts also applies pending migrations at process startup as a fallback, see First-admin setup for the /setup flow that follows.

Point a workspace at it

Terminal
cd my-design-system

# Device OAuth: opens a browser, saves a token to .handoff/cli-auth.json
handoff-app login --url http://localhost:4002

# Or shared-secret auth via env vars
echo 'HANDOFF_CLOUD_URL=http://localhost:4002' >> .env
echo 'HANDOFF_CLOUD_TOKEN=dev-registry-secret' >> .env

handoff-app sync-status   # confirm the connection

Push content

Terminal
handoff-app push:all

Visit http://localhost:4002/ to see the design system. See Workspace → CLI reference for every push/ push:all flag.

Updating an existing registry

  • Ship a handoff-app fix or feature to all clients: merge to main and every registry pointed at it auto-redeploys on Vercel. To hold a client back, point that project's Settings → Git → Production Branch at a branch you control, the published v1.x tags are Handoff 1 and are not a valid pin for a 2.0 registry.
  • Ship a content change: no registry redeploy at all: handoff-app push:all (or a selective push) from the workspace.
  • Move a workspace to a newer CLI: npm update handoff-app in the workspace repo.

Not the same as the deprecated per-project deploy

If you're coming from an older handoff-app deployment that materializes and deploys the client's own repo (prepare-runtime / vercel-build run against a workspace, path-contract options like materialization_layout), that model still runs for existing deployments but is deprecated, see Model → What this replaced. New registries deploy handoff-app itself, as above; the workspace repo never deploys.

On this page