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
329 lines
14 KiB
Markdown
329 lines
14 KiB
Markdown
---
|
||
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?
|