prj-forgejo-org-refactor/workplans/ORGREF-WP-0002-forge-closure-netkingdom-access.md
tegwick bc83a2fb16 ORGREF-WP-0002-T02 done: ArgoCD and k3s pull anonymously
Founder checks: no ArgoCD repository credentials; k3s registries.yaml holds
auth only for the frozen Gitea host, none for forgejo.coulomb.social.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5457@bnt-lap001
Assistant-Session: 792bf84a-3783-4f5d-a811-cded61dd2012
2026-10-08 16:54:05 +02:00

329 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: ORGREF-WP-0002
type: workplan
title: "Close the forge: NetKingdom access control first, then private by default"
domain: infotech
repo: prj-forgejo-org-refactor
status: active
flavor: implementation
owner: worsch
topic_slug: railiance
created: "2026-10-08"
updated: "2026-10-08"
depends_on: []
related_repos:
- key-cape
- railiance-forge
- railiance-platform
- railiance-apps
- ops-warden
- fluid-telegram
- pr-hall-of-helix
- the-custodian
state_hub_workstream_id: "f873c10f-1408-5bf2-a834-fed88387804b"
---
# ORGREF-WP-0002 - Close the forge: NetKingdom access control first, then private by default
## Why
`forgejo.coulomb.social` does not distinguish public-facing repositories from
private ones. The 2026-09-28 anonymous inventory listed **141 server
repositories, all public**. That includes `hall-of-helix` draft seats, agent
session accounts, operational docs and workplans. Anyone can read them, and
their raw files can be fetched anonymously.
The founder's decision (2026-10-08): switch repositories to private, but only
**after** Forgejo access control is integrated with NetKingdom (key-cape). Every
human, agent and service reader then has a governed identity before anonymous
access ends. A flip without that would push every consumer to ad-hoc local
accounts and tokens that would later have to be unwound.
## Relation to ORGREF-WP-0001
This plan is **independent of the ORGREF-WP-0001 entry gate**. It moves no
repository between orgs, renames nothing and changes no repository content, so
the org-migration gate (validator, deployable coverage, G1, transfer rollback)
does not apply. It changes **who may read**, not where things live.
It reuses ORGREF-WP-0001's preparation: the `inventory/` collectors, the
stewardship assignments in `taxonomy/` (as owners for each consumer) and the
reference scan. It also gives back to ORGREF-WP-0001: once the forge is closed,
the anonymous collector can no longer see private repositories, so T09 moves it
to an authenticated read identity. That resolves the access half of decision
`5f3680fc` (authenticated metadata).
## Target state
- Forgejo signs humans in through NetKingdom OIDC (key-cape). Agents and services
use NetKingdom-issued identities or scoped Forgejo tokens with an owner and a
rotation path. A local administrator account remains only as sealed
break-glass, with its credentials in OpenBao.
- Organisations and repositories are **private by default**. Public visibility
is an explicit, recorded allowlist entry with a reason.
- Every consumer that read the forge anonymously reads it with its own
credential, with least privilege, before its repositories close.
- Campaign images that must stay world-readable (Telegram posts fetch them by
URL) come from a deliberate public asset location, not from a forge repo that
happens to be public.
- Anonymous probes show only the allowlist; the evidence is recorded.
## Boundaries
This project may:
- decide sequencing, run read-only inventories and anonymous/authenticated probes
- rehearse on throwaway repositories and packages it creates for that purpose
- open and track child work records in owner repos, linked by ID, not copied
It must not:
- configure key-cape, Forgejo, OpenBao or any consumer itself. Each change lands
through the owner repo's own workplan.
- flip a production repository's visibility before T07's rehearsal passes and
the founder approves that wave
- read or print secret values (credential routing: `warden route`)
- move, rename or restructure repositories (that is ORGREF-WP-0001)
Visibility changes are reversible; exposure is not. Content that has been public
stays public in clones, caches and archives. T03 treats that history as a
separate review, not something the flip repairs.
## Tasks
```task
id: ORGREF-WP-0002-T01
status: wait
needs_human: true
blocking_reason: "The founder decides the visibility policy, the public allowlist and whether an interim lockdown runs before the integration."
priority: high
state_hub_task_id: "6387cb32-dd73-5ee0-b51e-d4aea475bcca"
```
### T01 — Decide the visibility policy
Record a founder decision covering:
1. **Default:** private for organisations and repositories, plus whether
instance-level `REQUIRE_SIGNIN_VIEW` is set so that nothing is anonymous
except the allowlist.
2. **Public allowlist:** which repositories stay public and why (for example,
intentionally open-source repos or a published campaign repo). Each entry
names an owner.
3. **Packages:** container images and charts follow repository visibility or
are private independently (28 running workload images come from
`forgejo.coulomb.social`; see `inventory/runtime-2026-09-28.json`).
4. **Public assets:** where world-readable campaign images live once
`hall-of-helix` and `pr-hall-of-helix` close (a public asset repo, object
storage, or attaching the image as an upload instead of a URL).
5. **Interim lockdown:** whether to close a short list of sensitive repositories
now with existing local accounts. If yes: which ones, accepting that their
consumers move to local tokens temporarily and are re-homed in T06.
```task
id: ORGREF-WP-0002-T02
status: done
priority: high
state_hub_task_id: "ace26a22-50e2-58c6-b4ad-85d95c284fce"
```
### T02 — Inventory every anonymous reader
Read-only. Extend `inventory/` with an access-consumer inventory: every party
that reads the forge today without credentials, the repositories or packages it
reads, and the owner (from `taxonomy/assignments.csv`) who will give it an
identity. Known classes to cover:
- cluster pulls: ArgoCD/Flux `repoURL`s, Helm chart repositories, and image pulls
by k3s nodes from the Forgejo registry (namespaces lacking `imagePullSecrets`)
- Forgejo Actions runners: checkouts and cross-repo `uses:` of reusable actions
- agent and workstation clones, `go`/`pip`/`npm` fetches of forge-hosted modules
- State Hub, repo-manager and forge-projection reads; ORGREF's own collector
- fluid-telegram: Telegram fetching post images from raw URLs
- external links: READMEs, badges, docs, published sites and canon links
Output a machine-readable `inventory/access-consumers-<date>.json` plus a
summary listing, per consumer class, the owner and the proposed credential
kind. Uncertain entries are flagged, not dropped.
2026-10-08: inventory done except two founder checks. `tools/access_consumers.py`
over 161 working trees and a fresh runtime capture (`inventory/runtime-2026-10-08.json`)
produced `inventory/access-consumers-2026-10-08.json`, reviewed in
`inventory/access-consumers.md`. Breaks on closure: 43 workloads pulling forge
images without `imagePullSecrets` (anonymous registry pulls confirmed working);
10 ArgoCD applications over 5 repos; CI in about 20 repos downloading source
tarballs anonymously; PyPI index reads; reuse-surface federation (about 60 raw
indexes); about 8 code readers without token handling; campaign images. Local
clones are SSH and unaffected. Open: founder presence checks for ArgoCD
repository credentials and k3s node registry auth (commands in the summary),
and owner confirmation of the code readers.
2026-10-08: T02 done. Founder checks: ArgoCD has no repository credentials
(all 10 Applications read anonymously); k3s `registries.yaml` holds auth only for
the frozen Gitea host `92.205.130.254`, none for `forgejo.coulomb.social` (all 43
pulls anonymous). One node-level registry auth entry would cover every pull
(T06). The stale Gitea credential on the node is a cleanup item. Code-reader
confirmation moves to the owners under T06.
```task
id: ORGREF-WP-0002-T03
status: todo
priority: high
state_hub_task_id: "9c2ca7be-23f8-58c5-9bfd-e44a26ce4584"
```
### T03 — Review what has already been public
Before closing, and independent of it: run a secret and sensitive-content scan
over the public repositories' history (credentials, tokens, private keys,
internal hostnames or IPs, personal data). Findings go to their owners for
rotation through `warden route`. Closing a repository does not un-publish
history, so a leaked credential needs rotating either way. Record the scan scope
and results without reproducing any value.
```task
id: ORGREF-WP-0002-T04
status: wait
depends_on: [ORGREF-WP-0002-T01]
blocking_reason: "The NetKingdom side follows the visibility and identity decisions; open the key-cape child workplan once T01 is resolved."
priority: high
state_hub_task_id: "112617e1-a7c3-59c1-bf25-235d58ec118b"
```
### T04 — NetKingdom: Forgejo as an OIDC client (child workplan in key-cape)
Open a key-cape workplan, linked here by ID, to:
- register Forgejo as a confidential OIDC client (redirect URI, scopes, PKCE)
with its client secret in OpenBao, through the railiance-platform custody path
- define the claims Forgejo consumes: subject, email, `groups` mapped to Forgejo
organisations and teams, and `principal_type`, so that human, agent and service
identities are distinguishable
- apply the MFA policy for forge administrators, and decide which agent and
service identities get forge access as NetKingdom service identities, not as
user accounts
Acceptance: a test login against a staging client returns the expected claims
for one human, one agent and one service identity.
```task
id: ORGREF-WP-0002-T05
status: wait
depends_on: [ORGREF-WP-0002-T04]
blocking_reason: "Forgejo can only add the authentication source once the NetKingdom client and claims exist."
priority: high
state_hub_task_id: "6c635b25-a4ae-5259-9317-de3fe38f6234"
```
### T05 — Forgejo: NetKingdom login and team mapping (child workplan in railiance-forge)
Open a railiance-forge workplan, linked here by ID, to:
- add the NetKingdom OIDC authentication source and claim-to-team mapping;
disable open registration; link existing local accounts (founder, agent
accounts) to their NetKingdom identities
- seal the local administrator as break-glass, with its credentials in OpenBao
and recovery documented
- create the organisation teams with least-privilege read/write that T06's
consumers will use
- change **no** repository visibility yet
Acceptance: the founder and one agent sign in through NetKingdom and see what
their team grants. Break-glass login is verified once and recorded.
```task
id: ORGREF-WP-0002-T06
status: wait
depends_on: [ORGREF-WP-0002-T02, ORGREF-WP-0002-T05]
blocking_reason: "Each consumer gets its credential from the T02 inventory, using the identities T05 makes available."
priority: high
state_hub_task_id: "1285f94d-3b57-502b-8aeb-96fab9578287"
```
### T06 — Give every consumer its own read identity
For each consumer class in T02, its owner provisions a scoped credential and
proves it works **against a private canary repository and package** created by
this project, while anonymous access to that canary fails:
- cluster: deploy tokens for ArgoCD/Flux repo credentials, `imagePullSecrets`
for registry pulls (railiance-apps, railiance-platform)
- runners: tokens or OIDC for checkout and reusable actions (railiance-forge)
- agents and workstations: NetKingdom identities or scoped tokens routed through
ops-warden; nothing pasted into repositories
- State Hub, repo-manager and the ORGREF collector: authenticated read identity
- fluid-telegram and pr-hall-of-helix: images moved to the T01 public asset
location, with the `compose visual` raw-URL bases updated
Each owner records its proof in its own workplan. This plan tracks the list.
```task
id: ORGREF-WP-0002-T07
status: wait
depends_on: [ORGREF-WP-0002-T06]
blocking_reason: "Rehearsal needs every consumer class migrated onto the canary."
priority: high
state_hub_task_id: "0dcf25e2-cda1-5f87-b7fe-5b7e27635565"
```
### T07 — Rehearse the flip and the rollback
On throwaway repositories and packages: switch public → private → public, and
verify consumer reads, anonymous denial, web links, Actions runs and image pulls
at each step. Record the exact commands or API calls, the time each step takes,
and what a rollback restores (visibility) and what it does not (anything already
fetched). This rehearsal is the gate for T08.
```task
id: ORGREF-WP-0002-T08
status: wait
depends_on: [ORGREF-WP-0002-T07]
needs_human: true
blocking_reason: "Each production visibility wave needs founder approval after a green rehearsal."
priority: high
state_hub_task_id: "42724077-9837-5aef-ad3b-6551f2103aa2"
```
### T08 — Close the forge in waves
Change production visibility in founder-approved waves. Each wave lists its
repositories and packages and the consumers that read them. Proposed order:
1. repositories with no consumers outside their owner (fastest proof of the path)
2. sensitive repositories (operational docs, workplans, agent sessions)
3. repositories read by the cluster and runners
4. instance-level `REQUIRE_SIGNIN_VIEW`, if T01 chose it
After each wave: consumer health checks, anonymous probe, and a recorded
rollback point. A failing consumer rolls its repositories back to public and
stops the wave. The fix lands with the consumer's owner.
```task
id: ORGREF-WP-0002-T09
status: wait
depends_on: [ORGREF-WP-0002-T08]
blocking_reason: "Verification and handback follow the final wave."
priority: medium
state_hub_task_id: "82f53328-02f5-5953-830a-00019d7dee92"
```
### T09 — Verify, hand back and record
- Anonymous probe of the server: only the T01 allowlist is visible. Record the
evidence under `inventory/`.
- Move ORGREF-WP-0001's collector to the authenticated identity from T06 and
note the effect on decision `5f3680fc`.
- Add a row to the-custodian `docs/security-close-down-checklist.md`
recording that the forge is closed by default, plus anything relaxed along the
way that must be re-armed.
- Record any consumer still on a temporary credential as live owner work, not as
prose in this file.
## Open questions for review
- Owner: is the founder the right owner, or should an agent own the driver with
the founder holding T01/T08?
- Should T03 (history review) block T08, or run in parallel and only gate
rotations?
- Is an interim lockdown (T01 point 5) worth the temporary-token churn, given how
long T04–T07 will take?