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
- 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.
- 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.
- The public path is specified as a gate chain, not authorised: (i) the whitepaper
body satisfies ADR-106 and
bare_number_lint --strictgates 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. - 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-03with a §1.6 rewrite. Not gated on, and not gating, the deploy. - CI owns the deploy.
docs-platform-ci.ymlgains a deploy job that runs only onmain, 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 leavemainpermanently red, which is the no-CI failure mode this corpus has recorded. astro.config.mjsgainssitein 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 ishttps://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.mdfrom 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
sitere-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-03finding record in the Marketplace whitepaper §1.6; cross-corpus, per theOI-Mpattern. - Clause 6: review of this change asserts
siteand 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).