CI / verify (pull_request) Successful in 1m10s
CI / infrastructure-smoke (pull_request) Successful in 34s
CI / package-release (pull_request) Successful in 50s
CI / migration-smoke (pull_request) Successful in 2m26s
CI / image-build (pull_request) Successful in 1m12s
CI / deploy-smoke (same-origin) (pull_request) Successful in 42s
CI / package-publication (pull_request) Successful in 54s
CI / deploy-smoke (split) (pull_request) Successful in 3m17s
Stage 1 PR 6 (ADR 0062). /account shows who is signed in; /account/appearance chooses System, Light, or Dark and reduced motion (System, Reduce, Allow). - Core: ekklesia_core_user_preference, one typed, CHECK-constrained, nullable column per preference (null = never chosen), migration 0008_user_preferences (additive), @ekklesia/core/account store, and the authenticated-baseline permission core.account.self.manage. - API: GET /api/v1/account (name, email, preferences) and partial PATCH /api/v1/account/preferences, self-only by construction: no user id is accepted anywhere; trusted Origin and body limit on the mutation. - Shell: the account control shows name, email, and initials (never the unvetted image URL) and offers Account, Appearance, Sign out; native radio groups apply a choice at once through the ADR 0060 controller and save it; the server's answer wins and a failed save reverts. - First paint: a classic, same-origin, render-blocking appearance.js in <head> applies a two-word localStorage cache before paint (no inline script, CSP unchanged). The account replaces the cache on every load, sign-out clears it, and other tabs follow via the storage event. - Deploy smoke: real-Chromium checks for light/dark, OS emulation, overrides, reload persistence, pre-paint, Shadow DOM modules, reduced motion, the display staying light, and phone width; the browser container gets a 512 MiB /dev/shm so a long session no longer fails requests with ERR_INSUFFICIENT_RESOURCES. 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 (session lifetime superseded by 0056)
- 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 — Accepted
- 0045: The Dashboard and the API may live on separate origins — Accepted
- 0046: Ekklesia's branding is never forced on an installation — Proposed
- 0047: Every module proves itself inside Ekklesia, with one harness the platform publishes — Proposed
- 0048: Ekklesia ships one generic image, and installed modules live outside it — Accepted
- 0049: A module release is one immutable, self-contained package: Ekklesia Module Package v1 — Accepted
- 0050: Module Database ABI v1: modules and Core exchange SQL, never ORM objects — Accepted
- 0051: Module packages declare their own license and carry its text — Accepted
- 0052: Reviewed license supplements cover redistributed dependencies whose packages omit required material — Accepted
- 0053: A module builds outside the monorepo, from Ekklesia packages published over the npm registry protocol — Accepted
- 0054: Ekklesia's module-development packages are publicly readable before launch — Accepted
- 0055: Native redistributable packages carry corresponding-source artifacts — Accepted
- 0056: A device credential lasts while the device is in use — Accepted
- 0057: Dashboard URLs are real, host-owned routes — Accepted
- 0058: The Dashboard shell's navigation, and Lucide as Ekklesia's icon vocabulary — Accepted
- 0059: Shared interaction primitives — confirmation, toasts, errors, loading, Back, and no native dialogs — Accepted
- 0060: Light, Dark, and System appearance; reduced motion; and one radius scale — Accepted
- 0061: Settings is one host-owned hierarchy; module lifecycle is Discover, and a module's settings live at
/settings/<module-id>— Accepted - 0062: The account area, and appearance preferences that belong to the account — Accepted