Skip to content

The Docs Platform Deploys — ADR-114

The Docs Platform Deploys — ADR-114

One ruling. It discharges ADR-113 clause 6 for exactly one audience — repository collaborators — and re-arms it for every wider one, with the public path’s prerequisites named as gates. Numbering: 113 is the highest allocated; this is ADR-114. Derivation records: Corpus_Publication_Disclosure_Review.md (CP-F), and the Epic C disclosure pass recorded in this file’s §Findings (CF-01…CF-06).


ADR-114 — The Docs Site Deploys Behind a Repository-Membership Access Boundary; Thresholds Stay Unpublished Because the Audience Does Not Widen

Status: Accepted · Date: 2026-08-08 · Depends-on 113 (clause 6 is the condition this record discharges and re-arms; the manifest and gates are the artefact being deployed) · interacts with 111, 112

Context

ADR-113 authorised rendering and withheld reachability: any audience beyond repository collaborators required (a) a disposition of the registry’s operational thresholds against §3.1’s calibration argument and (b) a content-level disclosure pass. The pass ran 2026-08-08. Its two structural findings: the corpus is not merely a value store but a maintained map of absent protections (the registry’s Outstanding inventory, the Marketplace’s open-items table), and the value class leaks corpus-wide (~103 uncited quantities in the whitepaper body alone), so no registry-scoped redaction can close it.

Decision

  1. Deployment target: a static host behind an identity-aware access proxy whose allow-predicate is GitHub repository access, evaluated by the IdP per request. The audience equals ADR-113’s “repository collaborators” by construction — one predicate, one source of truth, never a copied list (P3 over P6). Ratified topology: Cloudflare Pages + Cloudflare Access with GitHub as IdP (Cloudflare is already in the platform stack; the artefact stays static per ADR-112). Named fallback: Azure Static Web Apps with Entra. GitHub Pages private visibility is rejected below.
  2. Clause 6(a) disposition: the thresholds remain unpublished because deployment does not widen the audience. Every §3.1-class value, and — found by this review to matter more — the absent-protection inventories, stay exactly as reachable as the repository itself. ADR-113 clause 6 remains in force verbatim for any wider audience.
  3. The public path is specified as a gate chain, not authorised: (i) the whitepaper body satisfies ADR-106 and bare_number_lint --strict gates green, making the Q-ID citation the single value chokepoint; (ii) the registry gains a per-row disclosure class, default withheld (Q-M-032’s “unpublished by design” generalised); (iii) the resolver and renderer consult that class fail-closed, with a dist-side belt asserting no withheld value shipped; (iv) findings CF-01…CF-04 are closed and the line-by-line pass is completed over the full rendered set. A future ADR walks that chain; this one only names it.
  4. CF-01 is dispositioned independently of deployment: Marketplace whitepaper §1.6 contradicted §1.5/§8.1 (a DRM promise the corpus itself calls false). Raised to the Marketplace corpus as M-F-03 with a §1.6 rewrite. Not gated on, and not gating, the deploy.
  5. CI owns the deploy. docs-platform-ci.yml gains a deploy job that runs only on main, only after the three existing gates (route check, token belt, read-only witness) — and a post-deploy smoke assertion: an unauthenticated request to a corpus page MUST NOT return it (expect the IdP redirect). Enforcement point: the smoke step in that job, which fails the pipeline on an open door. A deploy whose access boundary is asserted only in the proxy’s console is a boundary one console misclick removes silently; the smoke check makes the misclick a red pipeline. The job is armed by an explicit repository variable set when the Cloudflare project and Access policy are provisioned — a wired deploy that fails for want of provisioning would leave main permanently red, which is the no-CI failure mode this corpus has recorded.
  6. astro.config.mjs gains site in this change and no earlier — its absence was ADR-113 clause 6’s structural witness, and this record is the reviewed act that replaces the absence with a gated presence. The declared site is https://docs.descentvtt.com — a declaration of target, not a claim of provisioning; DNS, the Pages project and the Access policy are operational acts recorded in the release checklist, not in this repository.

§Findings — the Epic C content-level disclosure pass (2026-08-08)

CF-01 — Marketplace §1.6 contradicted §1.5/§8.1 (“the exact same Zero-Trust IP protection and DRM… buyers cannot simply copy the source files” against “No marketplace-side DRM… Any claim to the contrary would be false” and “every paid PDF is public within days”). The claim class VTT §6.1 forbids. Disposition: M-F-03, §1.6 rewritten (clause 4).

CF-02 — The aggregated absent-protection ledger. The registry’s Outstanding inventory; the Marketplace’s OI-M-19 (PII-in-logs unenforced), OI-M-20 (no DR posture), OI-M-21 (no general rate limit — its own text names the ticket-exchange amplifier); the threat model’s residual column; R9’s “Q-076 … currently unenforced”. Harmless at collaborator audience; at public audience this class requires either closing the named gaps or an explicit ruling that publishing the honest ledger is acceptable — which does not exist and is not smuggled in here.

CF-03 — The commercial class. §8.8’s cost floor, §1.2’s contribution table, the capacity model in Open_Items_S1_and_BENCH04.md, and the free-egress breaker printed as a formula with a worked example — public, these let an attacker size a denial-of-wallet campaign against a computable budget and hand competitors the unit economics. Covered by the private audience now; the public path treats them as candidates for ADR-113’s file-granular exclusion or the value-redaction class.

CF-04 — Security-tunable Q-M values (free-egress tiers, the consumer byte budget, the maker-checker threshold): the §3.1 class again, in the second registry. The neighbouring user-facing values (recovery windows, retention periods — CPRA obliges publishing those) are why the public-path disposition must be per-row, never per-file.

CF-05 — Clean classes, affirmatively: no credentials, endpoints, keys, internal hostnames, emails or personal data anywhere in the reviewed scope (swept); infrastructure detail is design-level; Studio_Architecture.md and Marketplace_Architecture.md are disclosure-clean; every attack-shaped narrative in the decision records reviews as dispositioned, with residuals named as accepted.

CF-06 — The pass’s own depth, stated: read line-by-line — Quantity_Registry.md (core tables and outstanding inventory), Marketplace whitepaper §1 and §8 with Appendices C/D and the threat model, Studio_Architecture.md, Marketplace_Architecture.md, Open_Items_S1_and_BENCH04.md, R19, R20. Reviewed by multi-class sweep plus their verbatim reproductions in Open_Questions.txt: the remaining rulings files and the two large record files. A literal line-by-line of the full rendered set is a clause 3(iv) gate for any public audience, not a claim this record makes.

Alternatives Considered and Why Rejected

  • Public deployment with a redacted registry. The pass measured why this fails today: the value class is corpus-wide (~103 uncited in the whitepaper body; Q-017 printed in Studio §4.2), so registry-scoped redaction is the unrelated-neighbour defect. Becomes clause 3’s chain, not a rejection forever.
  • Withhold only Quantity_Registry.md from rendering. Same failure, smaller costume — and it would also hide the Pending-row discipline, the registry’s most honest content, from the audience that may see everything anyway.
  • GitHub Pages (private visibility). Requires GitHub Enterprise, which this organisation is not verified to have; a decision must not rest on an unverified plan tier (P2). Re-proposable with evidence.
  • A VPN / private network. Collaborators are individuals without shared network infrastructure; this buys operational burden to enforce the same predicate the IdP already evaluates.
  • Keep the CI-artifact-only flow (no deploy). Rejected as the status quo Epic C exists to end: a 7-day artifact with manual download is not a documentation site, and the scrubber/resolver/route gates protect a publication step that should now exist.

Consequences (including negative)

  • The access policy becomes security configuration living outside the repository. A proxy console change can widen the audience with no diff and no review. Mitigations: clause 5’s smoke assertion (converts silent widening into a red pipeline on the next deploy) and a release-checklist item; the residual — a widening between deploys — is named, not solved.
  • The audience predicate is “can authenticate to the IdP with repository access”, which approximates but may not exactly equal the collaborator set (org-level grants, outside collaborators). The setup act must verify the approximation fails closed: identities the IdP cannot map are refused.
  • A second vendor joins the serving path (P8): the seam registry gains rows for the pages host and the access proxy, exit costs recorded at adoption — the site is static output plus one DNS change to leave; the access policy is the sticky part.
  • CI gains a deploy credential, scoped to the Pages project only, stored as a repository secret — the first outward-facing credential this repository holds.
  • Adding site re-enables the sitemap. A sitemap of a gated site is served behind the same boundary and is harmless there; it becomes one more thing the public path must re-examine (clause 3).

Rights-holders (ADR-079)

The access proxy retains authentication logs about collaborators — a new class of retained personal data about the team, held by the proxy vendor under its retention policy. Rights-holders: the collaborators themselves; honouring a claim means the proxy-side log retention configuration, which the provisioning act must set to the vendor minimum. Not “none”, and recording that is the reason this field exists.

Enforcement

  • Clause 1/5: the deploy job’s post-deploy smoke assertion (an unauthenticated fetch of a manifest-derived page must not return content; the IdP redirect is the passing shape), plus the three existing gates as job prerequisites.
  • Clause 2/3: ADR-113 clause 6 remains the standing gate; the chain’s own gates are the named lints and belts, each already existing or specified fail-closed.
  • Clause 4: the M-F-03 finding record in the Marketplace whitepaper §1.6; cross-corpus, per the OI-M pattern.
  • Clause 6: review of this change asserts site and the deploy job land in the same reviewed diff as this record.
  • The provisioning act (Pages project, Access policy, arming variable, log retention) is a named release-checklist item — it happens outside this repository and nothing in this tree can enforce it, which is stated rather than implied (the ADR-111 archival precedent).