Commit Graph

6 Commits

Author SHA1 Message Date
db4f517e44 #203: pin all third-party deploy images by digest
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 5m32s
CI / Build container images (pull_request) Successful in 1m13s
CI / Auth e2e pack (pull_request) Successful in 8m22s
CI / Import/export fidelity gate (pull_request) Successful in 57s
CD / Build and push images (push) Successful in 16s
CD / Deploy to Test (push) Successful in 56s
CD / Smoke tests against Test (push) Successful in 1m24s
CD / Promote to Int (push) Successful in 52s
CI / Lint, typecheck, test (push) Successful in 5m36s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 8m3s
CI / Import/export fidelity gate (push) Successful in 57s
The four third-party images in the deploy compose (postgres, pandoc,
gotenberg — previously a floating MAJOR tag —, caddy) are now
name:tag@sha256 pins; the tag stays for readability, the digest decides
what runs. The pinned digests are exactly what the stages already run
(verified against the live containers' RepoDigests on ONE), so the next
recreation is byte-identical. A new early CI step fails on any
third-party compose image without a digest; compose.dev.yml is a local
convenience and deliberately exempt (its node helpers now follow the
#236 pin). Update + rollout procedure in deploy/stages.md — CD does not
sync stage composes, so the hand rollout to test/int/prod is part of
this issue's definition of done.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168Ph5uBmHm8X28CSVpbpnJ
2026-07-31 05:13:13 +02:00
d4ebcfcfbe Add collaboration token issuance and connection authentication (#34)
All checks were successful
CD / Build and push images (push) Successful in 2m45s
CI / Lint, typecheck, test (push) Successful in 1m56s
CI / Auth e2e pack (push) Successful in 2m1s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m9s
CD / Promote to Int (push) Successful in 12s
The api mints a short-lived (60 s) HS256 JWT per page open after an interim
permission check; the collab server authenticates every connection with it
(ADR 0003/0007 — the only JWTs in the system).

- packages/shared: browser-safe token schema/types in `collab-token`, and the
  Node `crypto` sign/verify in `token-crypto` behind its own subpath export
  (`@dorfteich/shared/token-crypto`) so the web bundle never pulls in
  `node:crypto`. Only HS256 is produced/accepted; the signature is checked in
  constant time before any untrusted field is read.
- api: `GET /pages/:id/collab-token` (auth-required) returns
  {token, mode, expiresInSeconds}; `mode` is rw/ro via the interim access
  service; issuance is logged at debug level without the token value.
- collab: `onAuthenticate` verifies the token, checks the pageId matches the
  document name, stores {userId, mode} context, and enforces `ro` via
  Hocuspocus' read-only connection flag. Hocuspocus' own signal handling is
  disabled so index.ts remains the single shutdown owner.
- Shared COLLAB_TOKEN_SECRET env for api + collab (compose, dev overlay,
  .env.example, stage docs); a dev default keeps native dev/test/CI running.

Tests: shared token round-trip/rejection; api endpoint e2e (auth required,
claims, 404 for non-members/unknown ids); collab integration via
HocuspocusProvider (valid token connects; expired/tampered/mismatched-page/
wrong-secret rejected; read-only writes dropped, verified with two clients).

Closes #34

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 15:52:19 +02:00
8316c617d2 Add collaboration server skeleton (Hocuspocus) with health, container, and CI/CD (#33)
All checks were successful
CD / Build and push images (push) Successful in 2m36s
CI / Lint, typecheck, test (push) Successful in 1m50s
CI / Auth e2e pack (push) Successful in 1m58s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m13s
CD / Promote to Int (push) Successful in 11s
Bootstrap apps/collab as a Hocuspocus WebSocket server (ADR 0003):
- pino JSON logging (service=collab) and shared Zod env validation
  (collabEnvSchema); structured connection open/close logs.
- /healthz endpoint (process liveness + PostgreSQL ping) served via the
  onRequest hook, matching the container-internal path and the proxied
  /collab/healthz path; any WebSocket handshake is accepted for now
  (authentication arrives with #34, persistence with #35).
- Dockerfile (ESM workspace build) and a compose service on the frontend
  and internal networks with a healthcheck; dev overlay service and a new
  COLLAB_PORT variable.
- CD builds, pushes, and promotes the collab image; CI builds it on PRs;
  the smoke suite asserts /collab/healthz through the reverse proxy.
- deployment.md/stages.md: proxy routing, per-stage COLLAB_PORT, checklist.

Closes #33

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 14:53:44 +02:00
f00fb19f32 Add mail outbox with SMTP delivery worker and templates
MailService renders transactional mails (verify-email, reset-password)
from the new de/en `mails` i18n namespace — text plus minimal HTML
with escaped interpolation — and enqueues them into mail_outbox.
MailWorker delivers pending rows every 15s through an injectable
transport (nodemailer; faked in tests) with quadratic backoff and a
permanent FAILED state after five attempts, logged as a warning.
SMTP_* and APP_BASE_URL join the environment schema with defaults
matching the new Mailpit container in the dev overlay.

Closes #12

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 00:46:12 +02:00
6e5a632c31 Add production Dockerfiles and the Compose stack with dev overlay
Multi-stage images: web (workspace build baked into unprivileged
nginx with SPA fallback, asset caching, /healthz) and api (pnpm deploy
bundle with the prisma CLI for migrate-on-start, non-root, node-based
healthcheck). deploy/compose/docker-compose.yml defines the stage
stack (web, api, db) with frontend/internal networks, localhost-only
published ports for the host reverse proxy, log rotation, and named
volumes; .env.example documents every variable. compose.dev.yml layers
hot-reloading dev containers (or database-only usage) over the same
definition. Verified locally: full stack healthy, SPA fallback, readyz
green after automatic migration, db not reachable from outside.

Closes #6

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 19:30:21 +02:00
ca0f7cf4b1 Add Prisma with PostgreSQL, automatic migrations, and /readyz
apps/api gains Prisma (instance_settings as the first model) with the
initial migration applied automatically at startup via prisma migrate
deploy, a lazy-connecting PrismaService, and GET /api/v1/readyz
reporting named checks (database reachable, migrations applied) with
200/503. DATABASE_URL joins the validated environment schema;
MIGRATE_ON_START=false skips deploys for tests and tooling. An
idempotent seed script and a Compose dev overlay with PostgreSQL
(host port 5434 — 5433 is taken locally) complete the loop.

Closes #3

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 19:16:44 +02:00