Files
stack/deploy/portainer
Jarvis 79442a8e8e
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
feat(federation): Step-CA client service for grant certs (FED-M2-04)
- Add CaService (@Injectable) that POSTs CSRs to step-ca /1.0/sign over
  HTTPS with a pinned CA root cert; builds HS256 OTT with custom claims
  mosaic_grant_id and mosaic_subject_user_id plus step.sha CSR fingerprint
- Add CaServiceError with cause + remediation for fail-loud contract
- Add IssueCertRequestDto and IssuedCertDto with class-validator decorators
- Add FederationModule exporting CaService; wire into AppModule
- Replace federation.tpl TODO placeholder with real step-ca Go template
  emitting OID 1.3.6.1.4.1.99999.1 (grantId) and .2 (subjectUserId) as
  DER UTF8String extensions (tag 0x0C, length 0x24, base64-encoded value)
- Update infra/step-ca/init.sh to patch mosaic-fed provisioner config with
  templateFile path via jq on first boot (idempotent)
- Append OID assignment registry and CA env var table to docs/federation/SETUP.md
- 11 unit tests pass: happy path, certChain fallbacks, HTTP 401/4xx, malformed
  CSR (no HTTP call), non-JSON response, connection error, JWT claim assertions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 22:00:06 -05:00
..

deploy/portainer/

Portainer stack templates for Mosaic Stack deployments.

Files

File Purpose
federated-test.stack.yml Docker Swarm stack for federation end-to-end test instances (mos-test-1.woltje.com, mos-test-2.woltje.com)

federated-test.stack.yml

A self-contained Swarm stack that boots a federated-tier Mosaic gateway with co-located Postgres 17 (pgvector) and Valkey 8. This is a test template — production deployments will use a separate template with stricter resource limits and Docker secrets.

Deploy via Portainer UI

  1. Log into Portainer.
  2. Navigate to Stacks → Add stack.
  3. Set a stack name matching STACK_NAME below (e.g. mos-test-1).
  4. Choose Web editor and paste the contents of federated-test.stack.yml.
  5. Scroll to Environment variables and add each variable listed below.
  6. Click Deploy the stack.

Required environment variables

Variable Example Notes
STACK_NAME mos-test-1 Unique per stack — used in Traefik router/service names.
HOST_FQDN mos-test-1.woltje.com Fully-qualified hostname served by this stack.
POSTGRES_PASSWORD (generate randomly) Database password. Do not reuse between stacks.
BETTER_AUTH_SECRET (generate: openssl rand -base64 32) BetterAuth session signing key.
BETTER_AUTH_URL https://mos-test-1.woltje.com Public base URL of the gateway.

Optional variables (uncomment in the YAML or set in Portainer):

Variable Notes
ANTHROPIC_API_KEY Enable Claude models.
OPENAI_API_KEY Enable OpenAI models.
OTEL_EXPORTER_OTLP_ENDPOINT Forward traces to a collector (e.g. http://jaeger:4318).

Required external resources

Before deploying, ensure the following exist on the Swarm:

  1. traefik-public overlay network — shared network Traefik uses to route traffic to stacks.
    docker network create --driver overlay --attachable traefik-public
    
  2. letsencrypt cert resolver — configured in the Traefik Swarm stack. The stack template references tls.certresolver=letsencrypt; the name must match your Traefik config.
  3. DNS A record${HOST_FQDN} must resolve to the Swarm ingress IP (or a Cloudflare-proxied address pointing there).

Deployed instances

Stack name HOST_FQDN Purpose
mos-test-1 mos-test-1.woltje.com DEPLOY-03 — first federation peer
mos-test-2 mos-test-2.woltje.com DEPLOY-04 — second federation peer

Image

The gateway image is pinned by digest to fed-v0.1.0-m1 (verified in DEPLOY-01). Update the digest in the YAML when promoting a new build — never use :latest or a mutable tag in Swarm.

Notes

  • This template boots a vanilla M1-baseline gateway in federated tier. Federation grants (Step-CA, mTLS) are M2+ scope and not included here.
  • Each stack gets its own Postgres volume (postgres-data) and Valkey volume (valkey-data) scoped to the stack name by Swarm.
  • depends_on is honoured by Compose but ignored by Swarm — healthchecks on Postgres and Valkey ensure the gateway retries until they are ready.