docs: close Phase 5D security hardening
CI / verify (pull_request) Successful in 27s
CI / infrastructure-smoke (pull_request) Successful in 12s
CI / migration-smoke (pull_request) Successful in 42s

The documentation still described Phase 5D as in progress, Phase 6 as
blocked, and a disposable-runner isolation proof as a prerequisite for
opening the real pull request. All five passes and the independent
whole-repository re-audit have landed, so those statements no longer
match the accepted operating model.

The README's current status now records Phase 5D as complete under the
current trusted-contributor, not-yet-deployed threat model, with Phase 6
unblocked, and gains a pass 5 closure paragraph covering the re-audit,
the repository-local supply-chain and dependency fixes, the accepted
trusted-code-only CI boundary, and what remains required before
untrusted CI or a public launch. The pass 1-4 paragraphs keep their
technical content unchanged; only their tense changes, so each reads as
what was outstanding at the close of that pass rather than as a current
claim.

`platform-architecture.md` no longer stops at pass 1 or says passes 2-5
are unfinished; it summarizes all five passes and defers the remaining
operational limitations to the security document.

`security.md` states the trusted-code-only operating rule explicitly and
replaces "CI blockers before the real pull request is opened" with
"Requirements before executing untrusted contributions", framing the five
hardening items as prerequisites for relaxing that rule rather than as
Phase 6 blockers, and naming an isolated hosted-runner service as an
accepted alternative. The Gitea 1.24.2 warnings, the scope of
`persist-credentials: false`, and the unverified runner topology and
job-token authority are preserved verbatim; no external Gitea setting is
claimed. The launch checklist is marked as gating a public launch, not
development.

Both Doctor examples are corrected to the real three-migration output,
adding `core:0002_authorization_integrity · 634294731180`. The stale
sentence calling Core authorization the next architectural phase is
refreshed.

ADRs 0017-0020 are untouched: their statements that later passes were
outstanding are historical records made when each was accepted.

Documentation only — no production code, test, workflow, dependency,
migration, or schema change.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
ekklesia
2026-07-29 21:59:26 -05:00
co-authored by Claude Opus 5
parent 27088fc054
commit 288c088377
4 changed files with 117 additions and 41 deletions
+34 -17
View File
@@ -114,28 +114,45 @@ Phase 5A wired module-declared permissions into that same authorization system
manifest, one composed platform permission registry spanning Core's own declarations and every
installed module's, enforced through the identical middleware Core's own routes use. Phase 5B let a
module mount its own framework-neutral HTTP routes in production, under
`/api/v1/modules/<module-id>`, enforced and response-sanitized exactly like Core's own routes. This
branch completes Phase 5C: typed module database access (`context.services.database`) for a
module's lifecycle hooks and authorized HTTP handlers, over the platform's one existing PostgreSQL
pool. See [ADR 0014](decisions/0014-module-permission-declarations-and-platform-registry.md),
`/api/v1/modules/<module-id>`, enforced and response-sanitized exactly like Core's own routes. Phase
5C added typed module database access (`context.services.database`) for a module's lifecycle hooks
and authorized HTTP handlers, over the platform's one existing PostgreSQL pool.
See [ADR 0014](decisions/0014-module-permission-declarations-and-platform-registry.md),
[ADR 0015](decisions/0015-static-module-http-routing-and-runtime-lifecycle.md),
[ADR 0016](decisions/0016-module-database-access.md), and the repository root
[README](../../README.md#current-status) for the complete, current status of every phase.
Phase 5D pass 1 of 5 hardens the public request perimeter against an independent security audit of
the Phase 5C checkpoint: a server-owned client-address resolution boundary (direct mode by default,
explicit trusted-proxy mode otherwise) replaces trusting a caller-controlled forwarded header for
the authentication rate limiter; public sign-up is disabled by default behind an explicit typed
opt-in; every public route — Better Auth, Core's grant mutation, and every module route via a new
required `maxBodyBytes` field on the executable route contract — has an explicit, enforced
request-body ceiling with one sanitized `413` contract; and the Node HTTP server has finite
header/request timeouts. See
[ADR 0017](decisions/0017-public-request-perimeter-hardening.md). This pass does not add
distributed rate limiting or durable signup-abuse control, and Phase 5D itself is not complete —
passes 2–5 and a final independent re-audit remain before Phase 6 may begin.
**Phase 5D is complete.** Five passes hardened the platform against an independent security audit of
the Phase 5C checkpoint, and an independent whole-repository re-audit closed the phase:
The project continues finishing the module platform's backend foundations before Phase 6 Surface
work begins. Target architecture not yet implemented includes production approval-provider
1. the public request perimeter — a server-owned client-address resolution boundary for the
authentication rate limiter, public sign-up disabled by default behind an explicit typed opt-in,
an enforced request-body ceiling on every public route (including a module route's own declared
contract), and finite Node header/request timeouts;
2. identity session resolution, and a mandatory certificate- **and** hostname-verified PostgreSQL
transport with no plaintext or `rejectUnauthorized: false` exception;
3. durable authorization history that survives user deletion, a one-time owner bootstrap that
deleting a user cannot reopen, and bounded, unambiguous explicit-grant management;
4. module route-overlap rejection at registration, finite startup rollback and shutdown deadlines,
and hardened migration command timeouts and SQL execution;
5. deployment and supply-chain closure plus the final re-audit.
See [ADR 0017](decisions/0017-public-request-perimeter-hardening.md),
[ADR 0018](decisions/0018-identity-session-resolution-and-database-transport.md),
[ADR 0019](decisions/0019-durable-authorization-history-and-grant-management.md), and
[ADR 0020](decisions/0020-module-runtime-and-migration-execution-hardening.md).
These guarantees hold under a **trusted-contributor, not-yet-deployed** threat model. The remaining
limitations are operational rather than architectural: CI is a trusted-code-only boundary whose
runner topology and job-token authority are unverified, and Ekklesia is not deployed, so the origin,
proxy, and throttling properties a public launch requires are unproven. Neither set of requirements
blocks Phase 6. Read
[Security model and deployment trust boundaries](../development/security.md) for what each boundary
does and does not protect against, and the repository root
[README](../../README.md#current-status) for the current status of every phase.
The module platform's backend foundations are now in place and Phase 6 Surface work is unblocked.
Target architecture not yet implemented includes production approval-provider
composition, physical storage-bucket provisioning, jobs/workflow automation, richer public resource
and action contracts, a Dashboard, a Builder, and reference feature modules; none of these are
scheduled by this document.