Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
212 lines
9.9 KiB
Markdown
212 lines
9.9 KiB
Markdown
---
|
|
id: KEY-WP-0038
|
|
type: workplan
|
|
title: "Forgejo as a NetKingdom OIDC client, so the forge can close"
|
|
domain: infotech
|
|
repo: key-cape
|
|
status: active
|
|
flavor: implementation
|
|
owner: claude-code
|
|
topic_slug: railiance
|
|
created: "2026-10-08"
|
|
updated: "2026-10-16"
|
|
depends_on: []
|
|
related_repos:
|
|
- prj-forgejo-org-refactor
|
|
- railiance-apps
|
|
- railiance-platform
|
|
state_hub_workstream_id: "098e3e2c-a138-560e-b2d3-3a15eb49d8a9"
|
|
---
|
|
|
|
# KEY-WP-0038 - Forgejo as a NetKingdom OIDC client, so the forge can close
|
|
|
|
## Why
|
|
|
|
`forgejo.coulomb.social` is closing: private by default, no anonymous access
|
|
(prj-forgejo-org-refactor ORGREF-WP-0002, decision
|
|
`decisions/forge-visibility-policy.md`). The founder's condition is that Forgejo
|
|
sign-in runs through NetKingdom **before** anything closes, so that every human,
|
|
agent and service reader has a governed identity rather than an ad-hoc local
|
|
account. This plan is ORGREF-WP-0002-T04. Its Forgejo-side counterpart is
|
|
railiance-apps `RAPPS-WP-0016` (ORGREF-WP-0002-T05), which owns the Forgejo
|
|
Helm release.
|
|
|
|
## Boundaries
|
|
|
|
- Registration follows the existing human browser-client pattern
|
|
(`docs/approval-engine-provisioning-request.yaml`, `human_registration`) and the
|
|
client fields validated in KEY-WP-0028. Redirect URIs are exact, never
|
|
wildcards.
|
|
- The client secret goes to OpenBao through railiance-platform custody. No secret
|
|
value in git, State Hub, logs or chat.
|
|
- Founder guardrail for the whole closure: tighten gradually, never lock anyone
|
|
out, never risk assets. Nothing in this plan changes who can read the forge
|
|
today. It only makes the NetKingdom path available.
|
|
|
|
## T01 — Define what Forgejo receives
|
|
|
|
```task
|
|
id: KEY-WP-0038-T01
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "5f019d13-1fa3-5b88-a27a-75a3339ca96e"
|
|
```
|
|
|
|
Write the claim contract Forgejo consumes, as a non-secret request packet under
|
|
`docs/`:
|
|
|
|
- issuer `https://kc.coulomb.social`; confidential client `forgejo`; authorization
|
|
code with PKCE; scopes `openid profile email groups`
|
|
- redirect URI
|
|
`https://forgejo.coulomb.social/user/oauth2/netkingdom/callback`. The
|
|
authentication-source name `netkingdom` must match railiance-apps'
|
|
configuration exactly.
|
|
- subject and username: canonical directory subject, plus a stable
|
|
`preferred_username` that Forgejo can match to the existing local accounts
|
|
(founder and agent accounts) for linking
|
|
- `groups` naming the Forgejo organisations and teams to map to (for example
|
|
`forge:coulomb:owners`, `forge:coulomb:readers`), and `principal_type` so that
|
|
human and service principals stay distinguishable
|
|
- MFA: forge administrators and owners require assurance with a fresh second
|
|
factor; plain readers follow the client-aware MFA policy (KEY-WP-0008)
|
|
|
|
Acceptance: the founder accepts the packet. The group names are agreed with
|
|
railiance-apps.
|
|
|
|
2026-10-08: packet drafted at `docs/forgejo-oidc-client-request.yaml`. It covers a
|
|
confidential client `forgejo` with PKCE and `mfaRequired: true`, the exact
|
|
callback, scopes, a custody path, the claims as emitted at this revision, new
|
|
LLDAP groups `forge-admins`, `forge-coulomb-owners` and `forge-coulomb-readers`
|
|
with a required-claim gate, no automatic account creation, and linking of local
|
|
accounts. It also proposes that agents and services use scoped tokens bound to
|
|
service identities, not browser sign-in.
|
|
|
|
2026-10-08: accepted by the founder (in session). railiance-apps' RAPPS-WP-0016
|
|
uses the same authentication-source name `netkingdom` and the same group names,
|
|
so the agreement condition holds. Before T02, the LLDAP groups `forge-admins`,
|
|
`forge-coulomb-owners` and `forge-coulomb-readers` must exist, with the founder
|
|
in the first two.
|
|
|
|
## T02 — Register the client and hand over its secret
|
|
|
|
```task
|
|
id: KEY-WP-0038-T02
|
|
status: wait
|
|
depends_on: [KEY-WP-0038-T01, NK-WP-0048-T06]
|
|
blocking_reason: "Registration runs as the first KnownSecurityCase of type oidc-browser-relying-party (NK-WP-0048-T06), not as a hand-built rollout."
|
|
priority: high
|
|
state_hub_task_id: "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30"
|
|
```
|
|
|
|
**Changed 2026-10-08 (founder direction):** no case-specific helper script.
|
|
Registration is the first case of the SecurityCaseType `oidc-browser-relying-party`
|
|
(net-kingdom NK-WP-0048, canon `security-case-types_v0.1`). The parameters
|
|
accepted in T01 become the case; ops-mason materializes it (MASON-WP-0010) and
|
|
ops-warden follows through (WARDEN-WP-0044). The registration capability is
|
|
net-kingdom's (`net-kingdom.keycape-client-registration`); key-cape's part is its
|
|
field rules and reviewing the type's composition.
|
|
|
|
The original intent below still describes what must result.
|
|
|
|
Register `forgejo` from the packet. Mint the client secret straight into custody
|
|
(OpenBao path `platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, derived
|
|
by the case type since net-kingdom 074b3a0; custody owner railiance-platform,
|
|
consumers KeyCape via ESO and Forgejo). Record a receipt with no
|
|
secret value.
|
|
|
|
Acceptance: discovery metadata lists the client's redirect URI. The secret is
|
|
readable by Forgejo's custody path only.
|
|
|
|
2026-10-10: key-cape reviewed the type's composition
|
|
(`docs/oidc-browser-relying-party-type-review.md`). net-kingdom fixed the findings
|
|
in `61ab6bc`, a 1.0 patch: redirects with `?` or `#` refused, the groups scope
|
|
required, and descriptions corrected. A new 1.1 (`e2174ce`, founder go) adds the
|
|
`consumer-enforces-groups` verification. For Forgejo, that is the required-claim
|
|
setting railiance-apps configures. `security-cases/forgejo-netkingdom-oidc.yaml`
|
|
is re-pinned to @1.1, with parameters unchanged. KeyCape added `keycape
|
|
check-config` (`c528daa`), which register-client runs before the restart. T02 still
|
|
waits on NK-WP-0048-T06, which needs both executors' light paths (MASON-WP-0010-T03,
|
|
WARDEN-WP-0044-T03).
|
|
|
|
### Exact offline checker owner review — 2026-10-16
|
|
|
|
KeyCape owner review under KEY-WP-0038-T02 endorses only the offline config
|
|
validation behavior of source `3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`,
|
|
`src` tree `f0010b9cab17dbc3a787628a29657d98b900320f`, compiled artifact
|
|
`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`.
|
|
The exact source/archive, 108 source files, 9876 cached Go 1.23.0 toolchain
|
|
files and all ten selected module trees matched the candidate closure. Fourteen
|
|
actual artifact checks and the cmd/config/policy Go tests passed using only
|
|
synthetic inputs. The check dispatches before private-key loading, upstream
|
|
adapter construction or server startup and uses placeholder client secrets.
|
|
|
|
[Owner review](../docs/offline-checker-owner-review-2026-10-16.md) records the
|
|
exact evidence and limits: non-secret portal/logout URL environment overlays,
|
|
optional/nonempty env-name assertion and policy-store assertion, permissive YAML
|
|
loading, and no key/custody/readiness or authentication proof. It does not endorse
|
|
the caller's inherited environment, executable storage/custody, the NetKingdom
|
|
descriptor/type, Warden admission or any browser/native identity. Parent review
|
|
identified caller environment inheritance; NetKingdom owns its correction and
|
|
regression checks. T02 remains `wait`; T03 and all live acceptance gates remain
|
|
unchanged. No live config, credentials, cluster objects or paid calls were used.
|
|
|
|
## T03 — Prove the claims for each kind of identity
|
|
|
|
```task
|
|
id: KEY-WP-0038-T03
|
|
status: wait
|
|
depends_on: [KEY-WP-0038-T02]
|
|
blocking_reason: "Claims are verified against the registered client."
|
|
priority: high
|
|
state_hub_task_id: "40ddaa29-7245-508c-ad48-ddc063e25115"
|
|
```
|
|
|
|
Before Forgejo uses the client, verify the issued ID token and UserInfo for one
|
|
human (the founder, with MFA), one agent identity and one service identity:
|
|
subject, `preferred_username`, `groups`, `principal_type`, assurance. Record the
|
|
claims, minus tokens, as evidence.
|
|
|
|
Decide with railiance-apps whether agents and services sign in to Forgejo at
|
|
all, or receive scoped Forgejo tokens bound to a NetKingdom service identity
|
|
(ORGREF-WP-0002-T06). Record the decision.
|
|
|
|
Acceptance: evidence for all three principals; the agent and service decision is
|
|
recorded. Then hand over to RAPPS-WP-0016.
|
|
|
|
## T04 — Implement the `keycape-client` entry point
|
|
|
|
```task
|
|
id: KEY-WP-0038-T04
|
|
status: cancel
|
|
priority: high
|
|
state_hub_task_id: "583cf1f1-16dc-515f-83bc-1efc45024a89"
|
|
```
|
|
|
|
**Cancelled 2026-10-08: moved to net-kingdom NK-WP-0048-T09 (done).** The live
|
|
registration machinery (contained kubectl, cluster and issuer pins) already lives
|
|
in net-kingdom `sso-mfa/k8s/keycape/`, where KeyCape is deployed; a copy here
|
|
would drift. The entry point is `net-kingdom/sso-mfa/k8s/keycape/keycape-client.py`
|
|
and the declaration is `net-kingdom.keycape-client-registration`; key-cape's draft
|
|
declaration is removed. key-cape still owns the client field rules (canonical
|
|
model) and their enforcement at authorization time.
|
|
|
|
`capabilities/playbooks/key-cape.oidc-client-registration.yaml` (contract PASS,
|
|
`maturity: planned`) declares client registration as a playbook capability, so
|
|
that the SecurityCaseType `oidc-browser-relying-party` (net-kingdom NK-WP-0048)
|
|
can compose it. Implement the entry point it names, `tools/keycape-client/`:
|
|
|
|
- `keycape-client register --client-file <rendered-client.yaml>` with modes
|
|
`inspect`, `dry-run`, `apply` and `remove`; adds, updates or removes exactly one
|
|
client in the live registry; idempotent; never prints or writes secret values;
|
|
a confidential client's secret is read from its custody reference
|
|
- one implementation replacing the per-client helpers in net-kingdom
|
|
`sso-mfa/k8s/keycape/` (register-coulomb-social, portal-client-rollout,
|
|
vergabe-client-rollout, secrets-engine-openbao-client-rollout), keeping their
|
|
safeguards (byte-preserving merge, compare-and-swap, cluster pin, safe receipts)
|
|
and their tests as regression cases
|
|
- client field validation by the KEY-WP-0028 rules
|
|
- then set the declaration's `maturity` to `implemented`
|
|
|
|
Longer term, loading clients from a non-secret registry with secrets referenced
|
|
from custody would remove the type's only attended step (see the type README,
|
|
"Attended steps").
|