# 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.
   ```bash
   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.
