Some checks failed
Build and publish policy-nexus image / build-and-push (push) Failing after 2s
349 lines
16 KiB
Markdown
349 lines
16 KiB
Markdown
---
|
||
id: POLICY-NEXUS-WP-0001
|
||
type: workplan
|
||
title: "Stand up policy.coulomb.social as the permanent publication surface"
|
||
domain: infotech
|
||
repo: policy-nexus
|
||
status: finished
|
||
owner: the-custodian
|
||
topic_slug: policy-nexus
|
||
created: "2026-08-17"
|
||
updated: "2026-08-18"
|
||
state_hub_workstream_id: "7e3c9720-fe13-482c-9b3d-db1b741cbe6c"
|
||
---
|
||
|
||
# POLICY-NEXUS-WP-0001 — permanent publication surface
|
||
|
||
## Goal
|
||
|
||
Replace disposable artifact-page publication with permanent infrastructure at
|
||
`policy.coulomb.social` that keeps policy **available**, **addressable**,
|
||
**current**, and **honest about its own staleness**.
|
||
|
||
Done means: a governing document lives in its owning repo, is published
|
||
automatically, keeps its URL, shows its status and review date, retains its
|
||
superseded versions, and can be reached by someone outside the estate.
|
||
|
||
## The forcing case
|
||
|
||
`net-kingdom/canon/standards/tenancy-posture_v0.1.md` (*Tenancy Posture*,
|
||
draft-8) was reviewed by six repos —
|
||
`tenant-engine`, `flex-auth`, `rapp-postgres`, `railiance-platform`,
|
||
`adaptive-pricing`, `audit-core`. It is currently served from a private,
|
||
disposable artifact URL. Routing a document that governs six repos to a link
|
||
that may not resolve later is the problem this workplan exists to end.
|
||
|
||
Tenancy Posture is therefore the first publication and the acceptance test. If the site
|
||
cannot carry it correctly — six ladders, a threat matrix, an E×P grid, and its
|
||
owner-attributed questions and resolutions — the site is not finished.
|
||
|
||
## Existing structure this workplan must respect
|
||
|
||
**The renderer is already here.** `tools/render.py` and `tools/style.css`
|
||
arrived from `the-custodian` on 2026-08-17, along with `make build`. Stdlib
|
||
only, no dependency tree, recognising conventions already present in the
|
||
document rather than requiring extra markup. It was written because the page
|
||
and the source had diverged. T02 generalises it from one document to many; it
|
||
does not start from scratch.
|
||
|
||
**Source of truth stays upstream.** Canon lives in its owning repo — including
|
||
`the-custodian/canon` and `net-kingdom/canon` — while per-repo ADRs live in
|
||
their own repos. This repo reads and never writes back. A publication surface
|
||
with write authority is a second source of truth, and the estate has a standing
|
||
rule against that.
|
||
|
||
**The adoption stance applies** (Tenancy Posture §14): adopt published standards and
|
||
structural patterns; build tooling ground-up unless it is an established
|
||
industry standard with broad application. A static-site generator with a
|
||
plugin ecosystem is exactly what this rule excludes.
|
||
|
||
**Platform packages are consumed, not rebuilt.** TLS, DNS, ingress, and any
|
||
credential come from the existing platform. If this repo needs storage it is a
|
||
`rapp-postgres` consumer and declares a posture vector like anyone else.
|
||
|
||
## Non-goals
|
||
|
||
- No editing surface. No CMS, no browser drafting, no comments.
|
||
- No ratification workflow. The site may *show* that a draft is in flight and
|
||
for how long; advancing it is a canon-process decision.
|
||
- No authentication in this workplan. Everything published here is
|
||
public-by-intent; anything that is not does not belong on this surface.
|
||
- No search index in this workplan. Deferred until there is enough content for
|
||
search to beat a good index page.
|
||
|
||
## Tasks
|
||
|
||
### T01 — Addressing scheme and permanence contract
|
||
|
||
```task
|
||
id: POLICY-NEXUS-WP-0001-T01
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "04988e76-bde8-42a2-9269-90467c767a65"
|
||
```
|
||
|
||
Decide, once, how a document maps to a URL, and write down what the estate is
|
||
promising about that URL.
|
||
|
||
- URL shape for a document, and for a specific revision of it.
|
||
- What happens on supersession: the old URL keeps resolving and gains a
|
||
superseded banner pointing forward.
|
||
- What happens on withdrawal: distinguish *withdrawn* (kept, marked) from
|
||
*deleted* (does not happen).
|
||
- Where the version history lives, given the source is git in another repo.
|
||
- The commitment being made — a URL published here is expected to resolve
|
||
indefinitely, and what would have to happen for that to be broken.
|
||
|
||
**Output:** `docs/adr/ADR-0001-addressing-and-permanence.md`.
|
||
|
||
**Why first:** everything downstream bakes in the answer, and changing it later
|
||
breaks the one promise the repo exists to make.
|
||
|
||
Completed 2026-08-18 in `docs/adr/ADR-0001-addressing-and-permanence.md`.
|
||
The contract distinguishes stable current addresses from immutable revision
|
||
addresses and retains superseded and withdrawn publications.
|
||
|
||
### T02 — Generalise the renderer
|
||
|
||
```task
|
||
id: POLICY-NEXUS-WP-0001-T02
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "d20a3d19-65a7-4479-b46a-a42fa96cbe81"
|
||
```
|
||
|
||
Lift `tools/render.py` from one document to many.
|
||
|
||
- Multi-document: a manifest of sources rather than one path argument.
|
||
- Front-matter driven: title, status, revision, review date, owner from the
|
||
source document rather than from command-line flags.
|
||
- Index generation: a landing page listing documents with status and currency.
|
||
- Keep the convention recognisers (level ladders, threat matrix, E×P grid,
|
||
evidence chips, section rail) and keep it stdlib-only.
|
||
- Keep the "generated from canonical source — do not edit" marker on every page.
|
||
|
||
**Acceptance:** Tenancy Posture renders byte-identically in substance to the current
|
||
generated page, plus an index entry.
|
||
|
||
Completed 2026-08-18. `publication.json` drives a stdlib-only multi-document
|
||
builder. It emits a current page, immutable revision, legacy alias, index and
|
||
machine-readable publication manifest. Tests cover path safety, revision
|
||
immutability, lifecycle notices and currency.
|
||
|
||
### T03 — Source ingestion
|
||
|
||
```task
|
||
id: POLICY-NEXUS-WP-0001-T03
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "0b76e184-dcd2-4c12-92e1-49d1f2e0a431"
|
||
```
|
||
|
||
Define how a document gets from its owning repo to this one.
|
||
|
||
- Manifest format: source repo, path, publication URL, owner.
|
||
- **Scope is canon and ADRs only.** The corpus is bounded and countable today:
|
||
two canon trees (`the-custodian/canon`, `net-kingdom/canon`) and roughly 68
|
||
ADRs across 18 repositories. Workplans, evidence and runbooks are out — a
|
||
site that publishes everything publishes nothing in particular. Enumerate the
|
||
actual list during T03; a glob over `docs/adr/*.md` plus the canon trees is
|
||
the starting point, but each canon subdirectory (`standards`, `architecture`,
|
||
`constitution`, `values`, `tpsc`, `projects`) needs a yes or no rather than a
|
||
wildcard.
|
||
- Fetch mechanism for documents in other repositories, and how a fetch failure
|
||
is surfaced rather than silently serving stale content.
|
||
- Determinism: the same source commit must produce the same page.
|
||
- Record the source commit on the published page, so a reader can tell exactly
|
||
what was rendered.
|
||
|
||
**Open decision for T03:** pull (this repo fetches on a schedule) or push (the
|
||
owning repo triggers on merge). Pull is simpler and keeps the direction of
|
||
dependency clean; push is fresher. Recommend pull with a manual trigger, and
|
||
record the choice.
|
||
|
||
Completed 2026-08-18. The pull model is now explicit and automated. A reviewed
|
||
inventory enumerates 124 committed sources across 21 repositories: one is
|
||
published, 113 await source-owner publication metadata/addressing, eight are
|
||
non-Markdown formats without a renderer, and two are ADR directory indexes.
|
||
The inventory also records the explicit exclusion of `values`, `tpsc`, and
|
||
`projects`. New or removed matching sources fail the audit until their
|
||
disposition is reviewed.
|
||
|
||
The Forgejo workflow checks out every external source at an exact commit on a
|
||
daily schedule and manual dispatch, records a deterministic source-set digest,
|
||
and fails visibly on fetch, inventory, currency, or release verification
|
||
errors. It publishes an immutable source-set candidate only. Promotion remains
|
||
the separate paired OCI/publication-digest approval owned by
|
||
`rapp-policy-nexus` and `railiance-apps`. Release builds also retain the prior
|
||
generated tree so an unchanged semantic revision cannot acquire rewritten
|
||
immutable provenance when an upstream repository advances for unrelated work.
|
||
|
||
### T04 — Deploy to policy.coulomb.social
|
||
|
||
```task
|
||
id: POLICY-NEXUS-WP-0001-T04
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "dd6169e4-9c93-4c5a-b312-d5cd21d0971a"
|
||
```
|
||
|
||
- DNS, TLS, ingress via the existing platform packages.
|
||
- Static hosting — the output is static files by construction, so the serving
|
||
layer should be the least interesting part of this workplan.
|
||
- Availability expectation stated plainly, with the honest caveat that a
|
||
single-node rail gives restart recovery, not high availability. Do not claim
|
||
an SLA the substrate cannot support.
|
||
- Rollback: republishing a previous build must be a single command.
|
||
- Smoke check after every deploy: the index resolves, Tenancy Posture resolves, and a
|
||
known superseded URL still resolves.
|
||
|
||
2026-08-18: `policy-nexus` now owns a digest-pinned, non-root OCI artifact
|
||
contract and a Forgejo publication workflow. `rapp-policy-nexus` owns the
|
||
separately reviewable Helm package, runtime hardening, public smoke checks and
|
||
rollback; `railiance-apps` owns only the production digest binding. DNS already
|
||
resolves and the reef has the required Traefik/cert-manager substrate. A clean
|
||
source release, registry digest, server-side dry-run, deployment and live smoke
|
||
evidence were the closing gates described below.
|
||
|
||
2026-08-18 completion: Forgejo Actions published the clean draft-8 artifact at
|
||
OCI digest `sha256:23318442d1b3a5bdced54f0b407fa5b32834020b80c60d72dea04c03bfbf51e7`.
|
||
Helm revision 1 is deployed and Ready on `reef-railiance`; Let's Encrypt TLS is
|
||
Ready; the index, canonical current page, immutable draft-8 revision, legacy
|
||
permanent redirect, image pin, and publication digest all pass the live rApp
|
||
verifier. The package records the single-node restart-recovery caveat and
|
||
defers rollback exercise until a predecessor revision exists.
|
||
|
||
### T05 — Currency and staleness
|
||
|
||
```task
|
||
id: POLICY-NEXUS-WP-0001-T05
|
||
status: done
|
||
priority: medium
|
||
state_hub_task_id: "d5a6feee-5b57-47b8-a026-cb0c8dd9c3df"
|
||
```
|
||
|
||
The relevance half of the repo's purpose. A permanently available document that
|
||
is quietly out of date is worse than no document.
|
||
|
||
- Every page shows status, revision, and last-reviewed date.
|
||
- A review interval per document, and a visible marker once exceeded.
|
||
- A report of documents past review, and where it is delivered.
|
||
- Drafts show how long they have been in flight. The
|
||
`shared-platform-relational-storage_v0.1` draft has been routed and
|
||
unratified since 2026-08-10; that fact should be visible on the site, because
|
||
invisibility is precisely why it stalled.
|
||
|
||
Completed 2026-08-18 for the published corpus. Pages and the index expose
|
||
review due dates and overdue state; `make currency` exits non-zero for stale or
|
||
undeclared review metadata. The daily source-pull workflow is the delivery
|
||
channel for that failure, and expansion follows the reviewed T03 inventory.
|
||
|
||
### T06 — withdrawn
|
||
|
||
```task
|
||
id: POLICY-NEXUS-WP-0001-T06
|
||
status: done
|
||
priority: low
|
||
state_hub_task_id: "8b71def7-4820-406f-99fd-8c38248f9bea"
|
||
```
|
||
|
||
Regulatory intake moved to `risk-nexus` on 2026-08-17. Deciding what a rule
|
||
demands of the estate is a judgement about risk, not an act of publishing, and
|
||
it wanted a different owner and a different skill from everything above.
|
||
|
||
`risk-nexus` publishes *through* this repo. When it has records to publish they
|
||
arrive as another source in the T03 manifest, not as a second content type this
|
||
repo curates.
|
||
|
||
The consequence is the point: **this repo now does one thing.** Publication,
|
||
permanence, currency. That makes its ownership answerable and its boundary
|
||
defensible.
|
||
|
||
## Sequencing
|
||
|
||
T01 gates everything. T02 and T03 can proceed in parallel once addressing is
|
||
fixed. T04 needs both. T05 is additive. T06 is deliberately last.
|
||
|
||
The temporary artifact URL for Tenancy Posture stays live until T04 passes its smoke
|
||
check, and the review is routed to the permanent URL, not before.
|
||
|
||
## Risks
|
||
|
||
**This repo becomes a second source of truth.** The likeliest failure and the
|
||
most damaging. Mitigation: no editing surface, generated pages carry a
|
||
do-not-edit marker, and the manifest records the source commit for every page.
|
||
|
||
**Permanence is promised before it can be delivered.** A URL committed to in
|
||
T01 and broken in year two is worse than never having promised. Mitigation:
|
||
T01 states what would break the promise, and T04 declines to claim an
|
||
availability level the single-node rail cannot support.
|
||
|
||
**Scope drift toward a CMS.** Every publication surface attracts requests for
|
||
editing, comments, and workflow. Mitigation: the non-goals above are part of
|
||
the workplan, not a preface to it.
|
||
|
||
**The renderer grows a dependency tree.** The moment it needs a framework, it
|
||
becomes something only CI can run. Mitigation: stdlib-only is an acceptance
|
||
criterion on T02, not a preference.
|
||
|
||
## Resolved by the operator, 2026-08-17
|
||
|
||
- **Publication scope is canon and ADRs.** Not workplans, evidence, runbooks or
|
||
general documentation. Folded into T03 and INTENT.
|
||
- **Owner is `the-custodian`**, which owns both policy and risk, and carries
|
||
the duty of deciding what must be discussed with the operator personally.
|
||
- **Regulatory intake left the repo.** It is `risk-nexus`'s (T06, withdrawn).
|
||
|
||
**Authority is three-way and stated, not held by one repo.** `policy-nexus`
|
||
owns the surface — addressing, permanence, rendering, currency.
|
||
`the-custodian` owns what counts as canon and when it is ratified; this repo
|
||
renders that judgement and never makes it, which is the guard against a
|
||
cross-repo reader accreting authority over "what is current".
|
||
`railiance-platform` owns the substrate — DNS, TLS, ingress, hosting.
|
||
|
||
The tell that the split is right: **T01 and T04 need different competences.**
|
||
T01 is a canon-process question — what supersession means, what a permanent URL
|
||
promises. T04 is infrastructure. A single owner would be weak at one.
|
||
|
||
The README's one-line description — "a convergence and publication point for
|
||
government policies" — reads broader than this. Worth updating so the repo does
|
||
not attract the wrong contributions.
|
||
|
||
## Resolved publication scope
|
||
|
||
ADR-0001 records the bounded answer: `standards`, `architecture` and
|
||
`constitution` are in; `values`, `tpsc` and `projects` are out unless their
|
||
owner explicitly identifies an individual governing document. T03 must still
|
||
enumerate each publication rather than globbing those trees.
|
||
|
||
## Deferred: controlled disclosure
|
||
|
||
**Resolved for now (operator, 2026-08-17): full public disclosure is fine.** The
|
||
estate is in build mode, not production. Publishing architecture, known gaps and
|
||
residual risks openly costs little while there are no users to expose and no
|
||
attacker with anything to gain.
|
||
|
||
**That stops being true at production.** ADR-0001 §5 publishes a blast radius
|
||
because its consumers must read it; the same discipline applied publicly, once
|
||
real tenant data exists, hands an attacker a map. The estate will then need a
|
||
**controlled-disclosure scheme**: a way to hold a finding while it is fixed, and
|
||
publish it once it is — embargo, coordinated timing, and a record that the delay
|
||
was deliberate rather than a document quietly going missing.
|
||
|
||
This workplan does **not** build that, and should not. Two reasons:
|
||
|
||
1. It is a different problem. Publication is about permanence and currency;
|
||
embargo is about risk assessment, severity and timing. Building embargo into
|
||
a publication surface would put risk judgement in the repo least qualified to
|
||
make it.
|
||
2. It likely belongs to a service of its own — a **`risk-nexus`**, by analogy —
|
||
owning finding intake, severity, remediation tracking and disclosure timing,
|
||
with this repo as its publication surface rather than its brain.
|
||
|
||
**What T01 must do about it now:** nothing more than leave room. The addressing
|
||
scheme should not assume every document is public from the moment it exists, so
|
||
that adding an embargo state later is a new status rather than a URL migration.
|
||
Recording that constraint costs nothing today and is expensive to retrofit.
|
||
|
||
**Trigger to revisit:** the first real tenant, or the first finding that would
|
||
be dangerous to publish before it is fixed — whichever comes first.
|