prj-forgejo-org-refactor/workplans/ORGREF-WP-0002-forge-closure-netkingdom-access.md
tegwick 57ad4abefc Fix access inventory evidence and reconcile closure coordination
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a11beb-4eef-7fc1-94a7-597130a01ae9
2026-10-09 00:23:04 +02:00

18 KiB
Raw Blame History

id type title domain repo status flavor owner topic_slug created updated depends_on related_repos state_hub_workstream_id
ORGREF-WP-0002 workplan Close the forge: NetKingdom access control first, then private by default infotech prj-forgejo-org-refactor active implementation claude-code railiance 2026-10-08 2026-10-09
key-cape
railiance-platform
railiance-apps
ops-warden
fluid-telegram
pr-hall-of-helix
the-custodian
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. T06 must prove its authenticated read identity before closure; T09 verifies the handback. Accepted complete metadata evidence can resolve the access half of decision 5f3680fc; having a token alone does not establish completeness.

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, repositories and packages are private, with no anonymous forge access. Deliberate publication goes to a separate location through the reviewed per-repository public/ convention.
  • Every consumer that read the forge anonymously reads it with its own credential, with least privilege, before its repositories close.
  • The fluid-telegram adapter reads private images with its own identity and uploads them to Telegram (FT-WP-0001-T14). A public asset store is later work.
  • Anonymous probes show no forge repositories or packages after the final wave; 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, except the separately approved visibility-only interim route in decisions/forge-visibility-policy.md section 5, whose clone, sign-in, recovery and one-repository rollback checks remain mandatory
  • 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

T01 — Decide the visibility policy

id: ORGREF-WP-0002-T01
status: done
priority: high
state_hub_task_id: "6387cb32-dd73-5ee0-b51e-d4aea475bcca"

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.

2026-10-08: all six policy points and the driver role decided, recorded in decisions/forge-visibility-policy.md. The forge is private with no anonymous access, and public codebases go to a separate location. Publication is a per-repo public/ folder of symlinks only, projected outward by a shared publisher with sanitization, withdrawn by renaming the folder. Hall of Helix stays a published PR edge, tied to coulomb.social spaces as projections of repos. Point 4 decided: the adapter uploads Telegram images itself (fluid-telegram FT-WP-0001-T14); a dedicated public asset store with creator and trust management comes later, not now. Points 5 and 6 decided: interim lockdown of 13 repositories under the founder's guardrail (tighten gradually, no lockout, no asset loss, nothing hard to repair: clones first, one repository, verify and roll back once, then small batches, visibility only). T03 runs in parallel. Packages are private; node registry auth, an ArgoCD credential template and scoped CI/package identities are the accepted approach. An agent drives the plan; the founder holds policy and production wave approval. Pre-check: executor-sandbox (diverged, unpushed local commits) and ops-mason (uncommitted changes) wait for their owners. All policy points are settled; implementation proof remains outstanding.

T02 — Inventory every anonymous reader

id: ORGREF-WP-0002-T02
status: done
priority: high
state_hub_task_id: "ace26a22-50e2-58c6-b4ad-85d95c284fce"

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 repoURLs, 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.

T03 — Review what has already been public

id: ORGREF-WP-0002-T03
status: done
priority: high
state_hub_task_id: "9c2ca7be-23f8-58c5-9bfd-e44a26ce4584"

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.

2026-10-08: done. gitleaks (redacted) over all local refs of 160 forge repositories: 283 findings in 25 repositories. One real exposure: a Nextcloud backup token (NC_TOKEN) hardcoded in tools/cmd/railiance-backup in railiance-bootstrap and railiance-cluster, public since 2026-02-25 and still in HEAD. It is the predecessor credential of railiance-platform RPF-WP-0029, whose old share was attested unshared on 2026-09-15; what remains is removing the literal: railiance-cluster had already done it (2026-07-07), and railiance-bootstrap was cleaned on 2026-10-08 (25d2154). The founder confirmed the provider side. The rest is benign (identifiers, digests, test fixtures, placeholders). Record and limits: inventory/history-exposure-2026-10-08.md. The rotation is tracked with the owners.

T04 — NetKingdom: Forgejo as an OIDC client (child workplan in key-cape)

id: ORGREF-WP-0002-T04
status: wait
depends_on: [ORGREF-WP-0002-T01, KEY-WP-0038-T03]
blocking_reason: "KEY-WP-0038-T03 must deliver claims evidence; registration T02 waits on NK-WP-0048-T06 (KnownSecurityCase), not an ad-hoc helper."
priority: high
state_hub_task_id: "112617e1-a7c3-59c1-bf25-235d58ec118b"

Owner record: key-cape KEY-WP-0038 (098e3e2c-a138-560e-b2d3-3a15eb49d8a9). Reviewed October 9: T01 claim contract accepted; T02 registration waits on net-kingdom NK-WP-0048-T06 (d9b26bcf-a7c6-59a2-92a0-f913d76a203c); T03 claims proof is pending. The owner uses the accepted KnownSecurityCase route, with ops-mason and ops-warden, rather than a case-specific script. The owner record covers:

  • 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.

T05 — Forgejo: NetKingdom login and team mapping (child workplan in railiance-apps)

id: ORGREF-WP-0002-T05
status: wait
depends_on: [ORGREF-WP-0002-T04, RAPPS-WP-0016-T03]
blocking_reason: "RAPPS-WP-0016-T03 must prove account linking and team grants after KEY-WP-0038 delivers the client and claims."
priority: high
state_hub_task_id: "6c635b25-a4ae-5259-9317-de3fe38f6234"

railiance-apps owns the Forgejo Helm release (docs/forgejo-on-railiance01.md). Owner record: railiance-apps RAPPS-WP-0016 (f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b). Reviewed October 9: break-glass preparation T01 is todo; authentication, linking and grants remain waiting. The owner record covers:

  • 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.

T06 — Give every consumer its own read identity

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"

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: the accepted ArgoCD repository credential template and node-level read-only registry login (railiance-apps, railiance-platform and node owner); prove fresh pulls, including service-account and node credential behavior
  • runners: tokens or OIDC for checkout and reusable actions (railiance-apps, which runs forgejo-runner)
  • 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, with private repository/package enumeration proven before T08
  • fluid-telegram and pr-hall-of-helix: private image fetch and Telegram upload through FT-WP-0001-T14; prove posts without anonymous raw-image reads

Each owner records its proof in its own workplan. This plan tracks the list.

T07 — Rehearse the flip and the rollback

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"

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.

T08 — Close the forge in waves

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"

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.

T09 — Verify, hand back and record

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"
  • Anonymous probe of the server: no repositories or packages are visible. Record the evidence under inventory/.
  • Verify ORGREF-WP-0001's collector still enumerates private metadata with the identity proven in T06; reconcile completeness and note the effect on decision 5f3680fc. Do not postpone collector migration until after closure.
  • 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.

Review — 2026-10-09

T01–T03 are done; T04–T09 remain waiting on owner implementation and proof. The October 8 policy settles driver ownership, package treatment, interim lockdown and parallel exposure review. No new production wave is authorized by this review. The interim route must satisfy its own recorded guardrails; the normal closure route retains T06/T07 and founder wave approval.

Collector schema 0.2 fixes package-index classification, separates legacy hosts, records scan exclusions/errors and treats missing pull secrets as unknown access. October 8 founder checks remain historical proof for that runtime snapshot; new runtime records require current owner confirmation. This review does not reopen T02 or replace its accepted evidence.