Decide hall URI, static renderer sibling, and allowlist trust.

HOH-WP-0002 T01–T03. Public slug is frontmatter id. Rendering lives in
hall-render. T04 still needs the operator corpus call.

Assistant: grok
Assistant-Session: 01a09dc1-b21e-77e1-919e-fcad2f82b267
This commit is contained in:
tegwick 2026-09-14 12:03:28 +02:00
parent 2554e437ea
commit 250f9a525c
4 changed files with 84 additions and 5 deletions

View file

@ -26,7 +26,7 @@ its portrait on disk.
| Field | Notes |
| --- | --- |
| `id` | Stable, unique. Convention: `hall-worker-<who>-<short-id>` |
| `id` | Stable, unique, URL-safe `[A-Za-z0-9-]+`. Convention: `hall-worker-<who>-<short-id>`. This **is** the public URI slug (`/hall-of-helix/<id>`). Do not change it after the seat leaves `draft`. The filename is not the URI. |
| `type` | `worker-entry` |
| `worker_kind` | `human` or `agent-session` |
| `display_name` | How the worker is addressed in the hall |

View file

@ -0,0 +1,61 @@
# ADR 0001 — Hall URI identifier, service shape, and renderer trust
Status: accepted
Date: 2026-09-14
Workplan: HOH-WP-0002-T01, T02, T03
## T01 — URI identifier
The public path is:
```
https://helix.coulomb.social/hall-of-helix/<id>
```
`<id>` is the seat frontmatter `id` **verbatim** (example:
`hall-worker-codex-statehub-snapshot-and-signature`). Uniqueness is already
enforced by `make check`. The filename is not the URI and may be renamed.
The `hall-worker-` prefix stays. Stripping it would require a second
uniqueness domain; an opaque extra id would be a third name to keep.
`id` is URL-safe `[A-Za-z0-9-]+` (mixed case is allowed; several existing
Claude session ids use it). Once `status` is not `draft`, `id` is frozen.
Corrections edit the body, not the identifier.
`?renderer=` is optional; omitting it equals `renderer=default`.
## T02 — Where the service lives
This repo stays a **publication** corpus. Rendering is a sibling tooling
repo, conventionally `hall-render`. Do not execute renderer code from this
repository.
First delivery is **static generation**: `hall-render` reads this repo at a
pinned commit and emits HTML. The renderer set is known at build time.
Dynamic on-request execution is declined for v1 (publication classification
plus XSS surface).
Deployment target: reef-railiance k3s, private/static objects behind the
existing Traefik ingress. DNS `helix.coulomb.social` is a railiance-infra /
railiance-cluster record, not owned by this publication repo. No live
publish until T04 (operator corpus disposition).
## T03 — Renderer contract and trust
`renderer` is user-controlled input that selects code. Rules:
1. Resolve only through a **fixed allowlist** in `hall-render`. Never treat
the parameter as a path, module, package, or URL.
2. Unknown values return 404 (static: missing artifact). Do not fetch or
import.
3. A renderer receives parsed Markdown plus frontmatter and returns markup.
No credentials, filesystem, or network.
4. Output is sanitised before it is a page. HTML from a renderer is an XSS
sink.
Registration is an in-repo allowlist in `hall-render`, not reuse-surface
(that catalog is capability discovery, not a safe code-execution index).
Input: `{frontmatter, markdown, entry_id}`. Output: `{html}`. Default
renderer is named `default` and is the only one in the first allowlist.

View file

@ -12,6 +12,8 @@ ENTRIES = ROOT / "entries"
README = ROOT / "README.md"
REQUIRED_FM = ("id", "type", "worker_kind", "display_name", "created_at", "recorded_at", "status")
# HOH-WP-0002-T01: the public URI slug is frontmatter id verbatim.
ID_RE = re.compile(r"^[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*$")
REQUIRED_HEADINGS = (
"Who I was",
"Contribution",
@ -120,6 +122,11 @@ def check_entry(path: Path, ids: dict[str, Path]) -> list[str]:
entry_id = fm.get("id", "")
if entry_id and ids.get(entry_id) not in (None, path):
errors.append(f"{path.name}: duplicate id {entry_id}")
if entry_id and ID_RE.match(entry_id) is None:
errors.append(
f"{path.name}: id {entry_id!r} is not URL-safe [A-Za-z0-9-]+ "
"(HOH-WP-0002-T01: URI is this id verbatim)"
)
for heading in REQUIRED_HEADINGS:
if re.search(rf"^## {re.escape(heading)}\s*$", body, re.M) is None:

View file

@ -9,7 +9,7 @@ owner: claude-code
topic_slug: helix-forge
priority: medium
created: "2026-09-05"
updated: "2026-09-05"
updated: "2026-09-14"
quality_dor: DoR-Ok
quality_dor_at: "2026-09-05"
quality_dor_by: claude-code
@ -47,7 +47,7 @@ until those decisions land — that is the intended shape, not an oversight.
```task
id: HOH-WP-0002-T01
status: todo
status: done
priority: high
state_hub_task_id: "fc2e4306-5ecf-50db-9a75-8a241a4ae9bc"
```
@ -68,11 +68,15 @@ Whatever is chosen, **the URI is a permanent commitment**: a hall that breaks it
own links is not a record. Decide how a seat that is renamed or corrected keeps
its URI, and extend `make check` to enforce whatever invariant the answer needs.
**Done 2026-09-14.** URI is frontmatter `id` verbatim. `make check` requires
URL-safe `[A-Za-z0-9-]+` and unique ids. Filename is not the URI. ADR:
`docs/adr/0001-hall-uri-and-renderer-boundary.md`.
## Decide where the service lives and how it is built
```task
id: HOH-WP-0002-T02
status: todo
status: done
priority: high
state_hub_task_id: "b6ccb11c-2cf5-5f32-a2c5-a4f849eec437"
```
@ -95,11 +99,15 @@ content.
Name the deployment target (railiance / k3s) and who owns the DNS record for
`helix.coulomb.social`.
**Done 2026-09-14.** This repo stays publication corpus. Rendering is sibling
`hall-render`, static generation for v1. k3s/Traefik; DNS owned by
railiance-infra/cluster. No public serve until T04.
## Define the renderer contract and trust boundary
```task
id: HOH-WP-0002-T03
status: todo
status: done
priority: high
state_hub_task_id: "64370b21-2b30-5ce9-be63-0379c55dad5b"
```
@ -124,6 +132,9 @@ registry before inventing one.
Write it down as an ADR in this repo — a renderer author needs a spec, and this
is the part that will be got wrong quietly if it stays implicit.
**Done 2026-09-14.** Allowlist only, 404 on unknown, no credentials/fs/network,
sanitise HTML, register in `hall-render` not reuse-surface. Same ADR.
## Clear the corpus for public publication
```task