CI / verify (pull_request) Successful in 54s
CI / infrastructure-smoke (pull_request) Successful in 21s
CI / migration-smoke (pull_request) Successful in 1m48s
CI / image-build (pull_request) Successful in 27s
CI / deploy-smoke (pull_request) Successful in 42s
Implement ADR 0044's reference deployment as a base Compose file plus overlays, so an installation that already runs a proxy can use the base alone. - compose.yaml: api, postgres, object-storage, and the operator-only migrate (profile ops, plan by default). No published port. api and migrate run init, read-only, tmpfs /tmp, cap_drop ALL, no-new-privileges. postgres is only on the internal data network. - TLS everywhere from one installation-private CA: PostgreSQL TLS 1.3 only, with pg_hba rejecting every non-TLS connection, and the API on verify-full via EKKLESIA_DATABASE_CA (PEM text). SeaweedFS serves S3 over HTTPS (ADR 0030 requires https in production), trusted by the API through NODE_EXTRA_CA_CERTS. No Core change. - compose.proxy.yaml + Caddyfile.example: Caddy pinned by digest, ACME HTTP-01 (DNS-01 documented), routes /, /api/*, /display/*, /assets/* to api and refuses everything else; the storage hostname preserves Host. The API trusts exactly the proxy's fixed /32 on an edge network whose dynamic range excludes the fixed addresses; Caddy overwrites X-Forwarded-For and strips caller-supplied forwarding headers. - compose.tunnel.yaml: cloudflared pinned by digest, token from its own env file; Caddy honors CF-Connecting-IP only from its fixed address. - Four per-service root-owned env files, examples with no values; scripts/init-tls.sh (idempotent, never overwrites a key) and scripts/gen-secrets.sh (prints only). - CI: pnpm deploy:config in ci:verify for all three combinations, and a deploy-smoke job (scripts/smoke-deploy.sh) proving readiness, the Dashboard, signed storage delivery, the client-address chain, initial owner sign-in/out, verify-full database TLS, and Doctor through the proxy, against disposable services. tests/deploy.test.ts pins the structural guarantees. - ADR 0044 (still Proposed) amended to match: API-to-storage HTTPS, DNS-01 needs a custom Caddy build and tunnel-only needs DNS-01, the tunnel's origin TLS settings live in the Cloudflare dashboard, port 80 serves HTTP-01, and secrets are one env file per service. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Architecture decision records
ADRs capture durable choices that affect more than one package or phase.
- Filenames use a four-digit sequence followed by a short kebab-case title.
- Status is
Proposed,Accepted,Superseded, orRejected. - Accepted ADRs are immutable except for status and cross-reference corrections.
- A changed decision receives a new ADR that links to and supersedes the old one.
Index
- 0001: Four-layer platform boundary — Accepted
- 0002: pnpm workspace and strict TypeScript — Accepted
- 0003: Development infrastructure — Accepted
- 0004: Replace unmaintained MinIO baseline — Accepted
- 0005: Static module manifests and atomic discovery — Superseded
- 0006: Versioned capabilities and compiled module runtime — Accepted
- 0007: Module-owned Drizzle schemas and table isolation — Accepted
- 0008: Module-owned migration contracts and offline validation — Accepted
- 0009: Transactional module migration execution — Accepted
- 0010: Core identity architecture and Better Auth adoption — Accepted
- 0011: Identity HTTP transport and public enrollment policy — Accepted
- 0012: Production-equivalent identity origin/CSRF protection under test — Accepted
- 0013: Core authorization, permission registry, and platform-owner bootstrap — Accepted
- 0014: Module permission declarations and platform permission registry — Accepted
- 0015: Static module HTTP routing and production runtime lifecycle — Accepted
- 0016: Module database access — Accepted
- 0017: Public request perimeter hardening — client address, signup gating, body limits, timeouts — Accepted
- 0018: Identity session resolution, canonical public origins, and PostgreSQL transport policy — Accepted
- 0019: Durable authorization history and grant management — Accepted
- 0020: Module runtime and migration execution hardening — Accepted
- 0021: Modules as independently hostable products — Accepted
- 0022: Module UI authoring model and host contract — Accepted
- 0023: Module UI build pipeline — Accepted
- 0024: Browser-safe module UI SDK and the generic host loader — Accepted
- 0025: Trusted module UI asset publication — Accepted
- 0026: The minimal Ekklesia module UI host — Accepted
- 0027: Dashboard navigation is presentation configuration, and modules stay flat — Accepted
- 0028: Module operational lifecycle — enablement, eligibility, and what "running" means — Accepted
- 0029: Module configuration is manifest-declared, host-resolved, and secret-free at rest — Accepted
- 0030: Modules declare logical buckets; hosts provide object storage — Accepted
- 0031: Browsers fetch objects directly, through opaque temporary read URLs — Accepted
- 0032: Native module realtime is scoped snapshot state over SSE — Accepted
- 0033: Display UI is a separate, permission-gated module entrypoint — Accepted
- 0034: Dashboard browser authentication is host-owned and principal-first — Accepted
- 0035: The first platform owner is created by an operator command, never by a browser — Accepted
- 0036: A display device is its own principal, paired by an administrator — Accepted
- 0037: Outbound email is a host-provided transport, and SMTP is its first provider — Accepted
- 0038: Email verification and password recovery, with a trusted-bootstrap exception — Accepted
- 0039: Shared design tokens and foundational elements — Accepted
- 0040: Module stylesheets, and every browser artifact gets an isolated root — Accepted
- 0041: One module may contribute a browser renderer to another module's capability — Accepted
- 0042: Explicit, bounded streaming request bodies — Accepted
- 0043: The display caches its shell and declared code, and nothing else — Accepted
- 0044: Ekklesia ships one self-hosted reference deployment, composed from pinned sources — Proposed