From 17635cb0a76e5595ec06421ab7419ae889f6d731 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 18 Oct 2026 19:28:36 +0200 Subject: [PATCH] Prepare verified Forgejo public-PKCE build profile revision 2 for scoped review --- .../agents/custodian-codex/supervision.json | 51 +++++++ .../README.md | 116 +++++++++++++++ .../check_upstream_release.py | 104 +++++++++++++ .../checker-reproduction.json | 9 ++ .../client-public-build.yaml | 13 ++ .../client.yaml | 14 ++ .../decision-request-r2.json | 18 +++ .../decision-request.json | 18 +++ .../delivery-metadata.json | 124 ++++++++++++++++ .../deployment-preconditions.json | 34 +++++ .../execution-steps.json | 94 ++++++++++++ .../forgejo-auth-source-inventory.json | 8 + .../forgejo-values.postimage.yaml | 138 ++++++++++++++++++ .../helm-render-validation.json | 12 ++ .../issuer-start-probe.json | 31 ++++ .../native-signin-settings.json | 12 ++ .../net-kingdom-client.postimage.yaml | 15 ++ .../prepublication-validation-r1.json | 14 ++ .../prepublication-validation.json | 18 +++ .../proposal.json | 136 +++++++++++++++++ .../proposal.sha256 | 1 + .../protocol-attempt1.json | 86 +++++++++++ .../protocol-attempt1.jsonl | 7 + .../protocol-attempt2.json | 86 +++++++++++ .../protocol-attempt2.jsonl | 7 + .../protocol-attempt3.json | 86 +++++++++++ .../protocol-attempt3.jsonl | 8 + .../protocol-baseline.json | 9 ++ .../protocol-result.json | 14 ++ .../public-build-values.yaml | 15 ++ .../public-registration-dry-run.json | 30 ++++ .../public-registration-inspection.json | 28 ++++ .../public_pkce_interop_test.go | 92 ++++++++++++ .../railiance-apps.patch | 27 ++++ .../rebuild_checker.py | 35 +++++ .../registration-inspection.json | 17 +++ .../revision-history.json | 12 ++ .../run_public_protocol.py | 41 ++++++ .../upstream-release-result.json | 39 +++++ docs/security-close-down-checklist.md | 1 + ...gdom-security-blueprint-and-gap-closure.md | 15 +- ...T-WP-0082-netkingdom-security-hardening.md | 2 + 42 files changed, 1632 insertions(+), 5 deletions(-) create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/README.md create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/check_upstream_release.py create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/checker-reproduction.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/client-public-build.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/client.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/decision-request-r2.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/decision-request.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/delivery-metadata.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/deployment-preconditions.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/execution-steps.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/forgejo-auth-source-inventory.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/forgejo-values.postimage.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/helm-render-validation.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/issuer-start-probe.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/native-signin-settings.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/net-kingdom-client.postimage.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/prepublication-validation-r1.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/prepublication-validation.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/proposal.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/proposal.sha256 create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt1.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt1.jsonl create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt2.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt2.jsonl create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt3.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt3.jsonl create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-baseline.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-result.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-build-values.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-registration-dry-run.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-registration-inspection.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public_pkce_interop_test.go create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/railiance-apps.patch create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/rebuild_checker.py create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/registration-inspection.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/revision-history.json create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/run_public_protocol.py create mode 100644 docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/upstream-release-result.json diff --git a/.kaizen/agents/custodian-codex/supervision.json b/.kaizen/agents/custodian-codex/supervision.json index ebe2fb3..933c718 100644 --- a/.kaizen/agents/custodian-codex/supervision.json +++ b/.kaizen/agents/custodian-codex/supervision.json @@ -1074,6 +1074,57 @@ "executed_digest": "90bb3a1a35729396b02a377d8db62e1d1f8f38d8e24e601dbc3d4446cffd05e8", "refinement_or_rescue": false, "reporting_note": "Compatibility aliases for the existing summarizer; values copied exactly from original retained *_digest_sha256 fields and receipts. No new approval, trial, execution or authority." + }, + { + "proposal_id": "CUST80-FORGEJO-PUBLIC-BUILD-20261018", + "proposal_ref": "docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/proposal.json", + "original_revision": 1, + "original_digest": "61eff8b488674e75510133fe118de86d895be04c033e109c3597c03ed2ecf30e", + "original_digest_sha256": "61eff8b488674e75510133fe118de86d895be04c033e109c3597c03ed2ecf30e", + "supervisor": "Bernd Worsch", + "target": { + "cluster_node_ip": "92.205.62.239", + "issuer_config": "sso/keycape-config", + "issuer_client_id": "forgejo", + "issuer_config_uid": "2e94519d-1550-41c7-9701-2efe47fe1fd3", + "issuer_config_resource_version": "75372162", + "forgejo_auth_source": "netkingdom", + "callback_uri": "https://forgejo.coulomb.social/user/oauth2/netkingdom/callback" + }, + "expected_result": "Deliver actual Forgejo NetKingdom sign-in using the existing upstream release under a temporary public PKCE build profile.", + "verification_ref": "docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/proposal.json#verification", + "rollback_ref": "docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/execution-steps.json#rollback", + "cost_estimate_eur": 0, + "risk_estimate_eur": 300, + "risk_estimate": "Founder-reviewed planning estimate: up to three hours recovery at EUR100/hour; temporary build-mode identity-profile exposure is unpriced and requires explicit human acceptance rather than being declared covered by that number. No customer production approval.", + "disposition": "pending", + "outcome": "not_executed", + "approval_ref": null, + "approved_revision": null, + "approved_digest": null, + "executed_revision": null, + "executed_digest": null, + "refinement_or_rescue": true, + "verified_outcome": null, + "decision_request_ref": "ad88edeb-7c7c-486f-a695-1a7abae5b20c", + "owner_reviews_ref": "docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/README.md#exact-activation-scope", + "reason": "Prepared and verified source/local/native dry-run evidence; actual founder go and authorization of independent reviewers remain pending. No promotion or live execution.", + "current_proposed_revision": 2, + "current_proposed_digest": "fcea2cd32b8ee2634d2ebeb9ef4b659b6b85f8a4878413ed96e5d06cb09892c3", + "revisions": [ + { + "revision": 1, + "digest": "61eff8b488674e75510133fe118de86d895be04c033e109c3597c03ed2ecf30e", + "disposition": "pending, never executed", + "source_publication": "refused for two public-hash detector matches; raw original retained locally and exactly reconstructible from r2" + }, + { + "revision": 2, + "digest": "fcea2cd32b8ee2634d2ebeb9ef4b659b6b85f8a4878413ed96e5d06cb09892c3", + "disposition": "pending", + "reason": "The unchanged scanner classified two public source SHA-256 hashes as generic API keys when filenames were used as JSON keys. Represent identical source pins as typed path/sha256 records. No profile, command, target, source hash, verification or rollback changed." + } + ] } ], "trials": [ diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/README.md b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/README.md new file mode 100644 index 0000000..80ad984 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/README.md @@ -0,0 +1,116 @@ +# Forgejo working sign-in — proposed build profile, revision 2 + +Proposal `CUST80-FORGEJO-PUBLIC-BUILD-20261018`, revision 2. +SHA-256 of [proposal.json](proposal.json): +`fcea2cd32b8ee2634d2ebeb9ef4b659b6b85f8a4878413ed96e5d06cb09892c3`. + +**Proposed; no live change has been executed.** The founder's good-case-first +direction permits preparing this option; a separate explicit go and independent +NetKingdom, Apps and KeyCape reviews are required before activation. Previous +scanner approval and the confidential-client case acceptance do not authorize it. + +## Working flow and the deliberate build-mode choice + +Connect the existing Forgejo release to the existing KeyCape issuer using an exact +HTTPS callback, authorization-code flow with S256 PKCE, and required MFA. For this +temporary profile the client is public: it has no client secret. Existing Forgejo +accounts retain their local permissions and require explicit account linking; +automatic new-account registration stays disabled. Local sign-in and local 2FA +remain available. + +The accepted confidential-client profile remains the target. Its secret +generation, issuer/consumer delivery and complete case lifecycle stay in +CUST-WP-0082-T20 and the existing owner workplans. Full ongoing current-identity, +group-policy, revocation and isolation acceptance also stays in WP-82. This +candidate needs explicit acceptance of that temporary security deferral. It +does not make the original SecurityCaseType implemented or the confidential case +materialized. No grant is inferred from this preparation. + +This uses the maintained NetKingdom client-registration capability and the +unmodified Forgejo CLI, chart and OIDC client. There is no custom backend build, +new registration service, evaluator or credential-distribution mechanism. + +## Evidence from preparation + +| Check | Observed result | +| --- | --- | +| Live issuer client | `forgejo` is unknown; registration is needed. [Probe](issuer-start-probe.json). | +| Existing registration/delivery | Confidential entry absent, env binding absent, consumer delivery absent. [Inspection](registration-inspection.json), [delivery metadata](delivery-metadata.json). | +| Existing Forgejo sources | No authentication sources. Local sign-in is available. [Inventory](forgejo-auth-source-inventory.json). | +| Actual issuer + upstream client | HTTPS token exchange and UserInfo passed without a client secret; wrong PKCE and replay refused. MFA/human authorization was a synthetic session seed. [Result](protocol-result.json). | +| Unchanged Forgejo image | Disposable local SQLite migration and source creation passed. Browser login page showed NetKingdom and retained the local form; redirect carried the intended issuer, scopes, state and S256 challenge. [Result](upstream-release-result.json). This is local proof, not deployed sign-in. | +| Chart 12.5.0 | Source postimage renders the same public CLI parameters; scanner guard/template source stays byte-equivalent. [Render check](helm-render-validation.json). | +| Startup checker | Reproduced the exact previously owner-reviewed `be37738f…` binary. [Reproduction](checker-reproduction.json). | +| Native registration preview | Both live and proposed registries pass the checker; server dry-run preserves unrelated registry/config data. [Dry-run](public-registration-dry-run.json). | + +The first two source-protocol attempts failed during offline fixture cache setup. +Their receipts/logs are retained; attempt 3 passed. No runtime security code was +changed to obtain the passing result. Real human sign-in and account continuity +remain T43; actual agent Git/authenticated API use remains T44. + +## Exact activation scope + +The binding proposal includes all source hashes, current deployment identities, +registry UID/resourceVersion, command arguments, verification and rollback. +[execution-steps.json](execution-steps.json) contains the four native actions: + +1. Register only the proposed public `forgejo` client through the maintained + helper, with exact registry CAS and the reproduced startup checker. +2. Restart `sso/keycape` once and verify Ready, discovery and the authorization + start before proceeding. +3. Create only the `netkingdom` Forgejo authentication source through the existing + upstream CLI, with an empty client secret and the exact discovery/scopes. +4. Restart `forgejo/forgejo-gitea` once and verify Ready, the sign-in option, + exact HTTPS callback and PKCE redirect, and preserved local sign-in. + +Publish the exact Apps values postimage and NetKingdom build-profile declaration +through their existing file-scoped publication/consistency path. Source is +reviewed against the pinned owner revisions; any drift stops the action. +The source declarations make future normal reconciliation reproduce the profile. +No Helm upgrade, image, resource, runner, scanner-hook or network-policy change +is part of this activation. The existing Recreate strategies keep one replica +each; no extra pod capacity is needed. Current node CPU requests are 3605m/4000m. + +Both restarts briefly interrupt their service and in-flight sign-in. The readiness +budget is 180 seconds per phase, with the founder present. No unattended or later +standing execution grant is requested. Expected extra infrastructure spend is +EUR 0; recovery planning estimate is EUR 300. Temporary identity-profile exposure +is not monetized by that estimate and requires explicit founder acceptance. + +## Verification, rollback and human trial + +Verify every source/input pin and the observed native preconditions immediately +before effects. Record actual source ID and registry before/after revisions; +compare pod templates allowing only the approved restart annotation. Preserve +the existing images, resources, scanner template/guard/hooks and unrelated data. +Stop at a failed or ambiguous phase; retain the failure and do not replay blindly. + +Before human linking, rollback removes only the proved-created source, then +restarts Forgejo if needed; removes only the exact public registry entry through +the same helper, validates the result and restarts KeyCape. At most one additional +restart per changed deployment is allowed for rollback. Unexpected links, +creation uncertainty or drift requires owner reconciliation before deletion. +Revert only this trial's source declarations; preserve others' work. + +After successful activation, the actual human trial is: + +1. Sign in to the existing Forgejo account using its current local path. +2. Open [Security settings](https://forgejo.coulomb.social/user/settings/security), + choose **Link account**, then **netkingdom**, and complete the actual KeyCape + sign-in/MFA. Confirm the intended existing account was linked. +3. Sign out, use **netkingdom** on the login page, and confirm the same username, + repositories, organisations and expected rights. Report the result without + passwords, codes, cookies or token material. + +These instructions are prepared from the upstream account-link template; they +are not a claim that the live option already exists. Credentials remain with the +human. T44 then verifies the coding agent's supported Git and authenticated API +flow with its actual build profile stated. + +Revision 2 represents the same public source hashes as typed `path`/`sha256` records. +The ordinary scanner had classified two filename-as-key hash values in revision 1 +as generic API keys; those values exactly match the actual source files. The +profile, commands and input hashes are unchanged, and revision 2 scans clean +under the same rules. [Revision history](revision-history.json) retains the +original digest, local original and exact reconstruction from committed public +data. No rule, exemption or secret-encoding bypass was added. diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/check_upstream_release.py b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/check_upstream_release.py new file mode 100644 index 0000000..d428f48 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/check_upstream_release.py @@ -0,0 +1,104 @@ +"""Disposable local validation of the unchanged Forgejo runtime and CLI.""" +from pathlib import Path +import json +import secrets +import subprocess +import time +import urllib.error +import urllib.parse +import urllib.request + +packet = Path(__file__).resolve().parent +work = Path('/tmp/custodian-forgejo-public-release-r1') +work.mkdir(exist_ok=False) +data = work/'data' +data.mkdir() +image = 'code.forgejo.org/forgejo/forgejo@sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b' +container = 'custodian-forgejo-public-release-r1' +config = '''APP_NAME = Synthetic Forgejo trial +RUN_MODE = prod +[server] +PROTOCOL = http +HTTP_ADDR = 0.0.0.0 +HTTP_PORT = 3000 +ROOT_URL = http://127.0.0.1:18092/ +DISABLE_SSH = true +[database] +DB_TYPE = sqlite3 +PATH = /data/fixture.db +[security] +INSTALL_LOCK = true +SECRET_KEY = {secret} +[service] +DISABLE_REGISTRATION = true +[oauth2_client] +ENABLE_AUTO_REGISTRATION = false +ACCOUNT_LINKING = login +OPENID_CONNECT_SCOPES = openid profile email groups +[session] +PROVIDER = memory +[log] +MODE = console +LEVEL = Error +''' +(data/'fixture.ini').write_text(config.format(secret=secrets.token_hex(32))) +(data/'fixture.ini').chmod(0o600) +common = ['docker','run','--rm','--read-only','--tmpfs','/tmp:rw,size=128m', + '--mount',f'type=bind,source={data},target=/data', + '--env','GITEA_WORK_DIR=/data','--env','GITEA_CUSTOM=/data/gitea', + '--entrypoint','/usr/local/bin/gitea'] +receipt = {'image':image,'profile_approved':False,'production_mutation':False, + 'database':'disposable local SQLite only','commands':[]} +for name, args in [('migrate',['migrate']),('add-oauth',['admin','auth','add-oauth', + '--name','netkingdom','--provider','openidConnect','--key','forgejo', + '--secret','','--auto-discover-url','https://kc.coulomb.social/.well-known/openid-configuration', + '--scopes','openid,profile,email,groups'])]: + command = common+[image,'--config','/data/fixture.ini',*args] + result = subprocess.run(command,capture_output=True,timeout=90) + (work/(name+'.log')).write_bytes(result.stdout+result.stderr) + receipt['commands'].append({'name':name,'exit_code':result.returncode}) + if result.returncode: + (packet/'upstream-release-result.json').write_text(json.dumps(receipt,indent=2)+'\n') + raise SystemExit('local upstream CLI check failed; private log retained') +command = common[:3]+['--name',container,'--detach','-p','127.0.0.1:18092:3000']+common[3:]+[image,'--config','/data/fixture.ini','web'] +started = subprocess.run(command,capture_output=True,timeout=30) +assert started.returncode == 0, 'local server could not start' +class NoRedirect(urllib.request.HTTPRedirectHandler): + def redirect_request(self,*args): + return None +try: + for _ in range(50): + try: + with urllib.request.urlopen('http://127.0.0.1:18092/user/login',timeout=2) as r: + login=r.read(200000).decode() + break + except (OSError,urllib.error.URLError): + time.sleep(0.2) + else: + raise RuntimeError('local server readiness failed') + receipt['netkingdom_option_visible'] = '/user/oauth2/netkingdom' in login + receipt['local_signin_form_preserved'] = 'user_name' in login + opener=urllib.request.build_opener(NoRedirect) + try: + with opener.open('http://127.0.0.1:18092/user/oauth2/netkingdom',timeout=10) as r: + status=r.status; location=r.headers.get('Location','') + except urllib.error.HTTPError as e: + status=e.code; location=e.headers.get('Location','') + redirect=urllib.parse.urlsplit(location);query=urllib.parse.parse_qs(redirect.query) + receipt['authorization_start']={'http_status':status,'issuer_host':redirect.hostname, + 'path':redirect.path,'client_id':query.get('client_id'), + 'callback':query.get('redirect_uri'),'scope':query.get('scope'), + 'pkce_method':query.get('code_challenge_method'), + 'pkce_challenge_present':bool(query.get('code_challenge')), + 'state_present':bool(query.get('state'))} + receipt['passed'] = (receipt['netkingdom_option_visible'] and receipt['local_signin_form_preserved'] + and status in [302,303,307] and redirect.hostname=='kc.coulomb.social' + and query.get('client_id')==['forgejo'] + and query.get('code_challenge_method')==['S256'] and bool(query.get('code_challenge'))) +finally: + stopped=subprocess.run(['docker','stop','--time','5',container],capture_output=True,timeout=20) + receipt['local_container_stopped']=stopped.returncode==0 + (packet/'upstream-release-result.json').write_text(json.dumps(receipt,indent=2)+'\n') +print(json.dumps(receipt)) +if not receipt['passed']: + raise SystemExit(1) diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/checker-reproduction.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/checker-reproduction.json new file mode 100644 index 0000000..1574d55 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/checker-reproduction.json @@ -0,0 +1,9 @@ +{ + "verified": true, + "artifact": "/tmp/custodian-forgejo-public-checker-r1/keycape", + "sha256": "be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5", + "source_revision": "3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987", + "byte_identical_to_existing_owner_review": true, + "seconds": 17.647, + "real_runtime_configuration_access": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/client-public-build.yaml b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/client-public-build.yaml new file mode 100644 index 0000000..13020d0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/client-public-build.yaml @@ -0,0 +1,13 @@ +clientId: forgejo +displayName: Forgejo source forge +redirectUris: +- https://forgejo.coulomb.social/user/oauth2/netkingdom/callback +allowedScopes: +- openid +- profile +- email +- groups +grantTypes: +- authorization_code +clientType: public +mfaRequired: true diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/client.yaml b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/client.yaml new file mode 100644 index 0000000..3a5cf98 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/client.yaml @@ -0,0 +1,14 @@ +clientId: forgejo +displayName: Forgejo source forge +redirectUris: +- https://forgejo.coulomb.social/user/oauth2/netkingdom/callback +allowedScopes: +- openid +- profile +- email +- groups +grantTypes: +- authorization_code +clientType: confidential +secretRef: env:KEYCAPE_FORGEJO_CLIENT_SECRET +mfaRequired: true diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/decision-request-r2.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/decision-request-r2.json new file mode 100644 index 0000000..8c6d57f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/decision-request-r2.json @@ -0,0 +1,18 @@ +{ + "workplan_id": "dfadb19b-445e-59c6-bcd6-48e87b912c52", + "id": "ad88edeb-7c7c-486f-a695-1a7abae5b20c", + "topic_id": "cee7bedf-2b48-46ef-8601-006474f2ad7a", + "title": "WP-80: admit temporary Forgejo public-PKCE build profile, revision 2", + "description": "Pending actual founder go for CUST80-FORGEJO-PUBLIC-BUILD-20261018@2, SHA-256 fcea2cd32b8ee2634d2ebeb9ef4b659b6b85f8a4878413ed96e5d06cb09892c3. Packet /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/README.md. Same temporary public/S256-PKCE/MFA profile, source publication and four native actions as r1: register one client, create one Forgejo source and restart each existing deployment once; one additional restart each only for rollback. Confidential target/full security acceptance remain in CUST-WP-0082. Conditional on independent NetKingdom/Apps/KeyCape reviews; authorize those reviewer agents. Revision2 changes only the representation of public source hashes after the scanner refused the filename-as-key layout; no scanner rule or source/execution semantic changed. Original revision1 digest 61eff8b488674e75510133fe118de86d895be04c033e109c3597c03ed2ecf30e retained in supervision/revision-history. No native action or actual approval yet.", + "decision_type": "pending", + "status": "open", + "rationale": null, + "decided_by": null, + "decided_at": null, + "deadline": null, + "escalation_note": null, + "superseded_by": null, + "created_at": "2026-10-18T17:19:22.260406Z", + "updated_at": "2026-10-18T17:27:12.192435Z", + "workstream_id": "dfadb19b-445e-59c6-bcd6-48e87b912c52" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/decision-request.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/decision-request.json new file mode 100644 index 0000000..8f0160c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/decision-request.json @@ -0,0 +1,18 @@ +{ + "workplan_id": "dfadb19b-445e-59c6-bcd6-48e87b912c52", + "id": "ad88edeb-7c7c-486f-a695-1a7abae5b20c", + "topic_id": "cee7bedf-2b48-46ef-8601-006474f2ad7a", + "title": "WP-80: admit temporary Forgejo public-PKCE build profile, revision 1", + "description": "Pending actual founder decision for CUST80-FORGEJO-PUBLIC-BUILD-20261018@1; proposal SHA-256 61eff8b488674e75510133fe118de86d895be04c033e109c3597c03ed2ecf30e. Packet: /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/README.md. Temporary public authorization-code/S256-PKCE client forgejo, MFA required, no client secret; confidential-client target/full security acceptance retained in CUST-WP-0082. Exact scope: publish two owner source declarations, register one issuer client, create one Forgejo auth source, restart each existing deployment once, verify; one extra restart each only for rollback. Preserve local login, images/resources/scanner/data. Execution conditional on independent NetKingdom, Apps and KeyCape approvals bound to the exact revision. Founder must authorize those reviewer agents. No native action has run; no source acceptance or prior scanner approval is carried forward as this go. Expected new infrastructure cost EUR0; recovery planning EUR300; temporary profile exposure needs explicit acceptance.", + "decision_type": "pending", + "status": "open", + "rationale": null, + "decided_by": null, + "decided_at": null, + "deadline": null, + "escalation_note": null, + "superseded_by": null, + "created_at": "2026-10-18T17:19:22.260406Z", + "updated_at": "2026-10-18T17:19:22.260406Z", + "workstream_id": "dfadb19b-445e-59c6-bcd6-48e87b912c52" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/delivery-metadata.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/delivery-metadata.json new file mode 100644 index 0000000..4ee9764 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/delivery-metadata.json @@ -0,0 +1,124 @@ +{ + "sso": { + "externalsecrets": [ + { + "name": "keycape-approval-engine-operator-client", + "uid": "962bce90-349d-4406-9da8-9fb9d145d1e6", + "conditions": [ + { + "type": "Ready", + "status": "True", + "reason": "SecretSynced" + } + ], + "target": "keycape-approval-engine-operator-client", + "secretStoreRef": { + "kind": "ClusterSecretStore", + "name": "openbao-keycape-approval-engine-operator" + } + }, + { + "name": "keycape-factor-read", + "uid": "c348de2a-4c3f-44d5-b20b-c1939db33df7", + "conditions": [ + { + "type": "Ready", + "status": "True", + "reason": "SecretSynced" + } + ], + "target": "keycape-factor-read", + "secretStoreRef": { + "kind": "ClusterSecretStore", + "name": "openbao-keycape-factor-read" + } + }, + { + "name": "keycape-informed-decision-sitting-requester-client", + "uid": "44a79a03-7ce7-4da3-92c3-7e674fd2f7a6", + "conditions": [ + { + "type": "Ready", + "status": "True", + "reason": "SecretSynced" + } + ], + "target": "keycape-informed-decision-sitting-requester-client", + "secretStoreRef": { + "kind": "ClusterSecretStore", + "name": "openbao-keycape-informed-decision-sitting-requester" + } + }, + { + "name": "keycape-secrets-engine-approval-client", + "uid": "2b7cda9e-0310-4c8c-9bec-f3c5a4f39507", + "conditions": [ + { + "type": "Ready", + "status": "True", + "reason": "SecretSynced" + } + ], + "target": "keycape-secrets-engine-approval-client", + "secretStoreRef": { + "kind": "ClusterSecretStore", + "name": "openbao-keycape-secrets-engine-approval" + } + }, + { + "name": "keycape-secrets-engine-openbao-client", + "uid": "2a6f6c12-2c09-4e13-ae09-8e6d7a99265d", + "conditions": [ + { + "type": "Ready", + "status": "True", + "reason": "SecretSynced" + } + ], + "target": "keycape-secrets-engine-openbao-client", + "secretStoreRef": { + "kind": "ClusterSecretStore", + "name": "openbao-keycape-secrets-engine-openbao" + } + }, + { + "name": "keycape-secrets-engine-requester-client", + "uid": "5e397d7c-180d-4ea6-9f45-08ba63cf52a5", + "conditions": [ + { + "type": "Ready", + "status": "True", + "reason": "SecretSynced" + } + ], + "target": "keycape-secrets-engine-requester-client", + "secretStoreRef": { + "kind": "ClusterSecretStore", + "name": "openbao-keycape-secrets-engine-requester" + } + } + ], + "secretstores": [] + }, + "forgejo": { + "externalsecrets": [ + { + "name": "forgejo-mailer", + "uid": "cbbea71e-70a8-4010-aca9-9136920f2f58", + "conditions": [ + { + "type": "Ready", + "status": "True", + "reason": "SecretSynced" + } + ], + "target": "forgejo-mailer", + "secretStoreRef": { + "kind": "ClusterSecretStore", + "name": "openbao-forgejo" + } + } + ], + "secretstores": [] + } +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/deployment-preconditions.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/deployment-preconditions.json new file mode 100644 index 0000000..643ae02 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/deployment-preconditions.json @@ -0,0 +1,34 @@ +[ + { + "namespace": "sso", + "name": "keycape", + "uid": "99ddd83c-cb3f-4847-bcf8-35f1aa87627f", + "generation": 65, + "resource_version": "75372295", + "replicas": 1, + "strategy": { + "type": "Recreate" + }, + "ready_replicas": 1, + "images": { + "keycape": "forgejo.coulomb.social/coulomb/key-cape@sha256:cdec9dd7c515f7e0a6169fc3fa560ee2cc47bf40d899755dec808c362f5ce010" + }, + "pod_template_sha256": "e7d56eb38923824299934f92f5b5768f93bbf6bd4042a06a1fad0d86f05439a7" + }, + { + "namespace": "forgejo", + "name": "forgejo-gitea", + "uid": "e8eff0aa-64bb-4b44-9a80-a66612986355", + "generation": 15, + "resource_version": "77118297", + "replicas": 1, + "strategy": { + "type": "Recreate" + }, + "ready_replicas": 1, + "images": { + "gitea": "code.forgejo.org/forgejo/forgejo:11.0.3-rootless" + }, + "pod_template_sha256": "fedc046d4512be337cf0a66427a8393843ba214007f043ee52e7cd9123ad5421" + } +] diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/execution-steps.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/execution-steps.json new file mode 100644 index 0000000..7c48485 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/execution-steps.json @@ -0,0 +1,94 @@ +{ + "apply": [ + { + "step": "register_public_client", + "argv": [ + "/home/worsch/repo-manager/.venv/bin/python", + "/home/worsch/net-kingdom/sso-mfa/k8s/keycape/keycape-client.py", + "apply", + "--client-file", + "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/client-public-build.yaml", + "--check-config", + "/tmp/custodian-forgejo-public-checker-r1/keycape", + "--expected-uid", + "2e94519d-1550-41c7-9701-2efe47fe1fd3", + "--expected-resource-version", + "75372162", + "--case-acceptance", + "" + ] + }, + { + "step": "restart_keycape_once", + "argv": [ + "ssh", + "railiance01", + "kubectl -n sso rollout restart deployment/keycape" + ] + }, + { + "step": "add_forgejo_source", + "argv": [ + "ssh", + "railiance01", + "kubectl -n forgejo exec deployment/forgejo-gitea -c gitea -- /usr/local/bin/gitea --config /data/gitea/conf/app.ini admin auth add-oauth --name netkingdom --provider openidConnect --key forgejo --secret '' --auto-discover-url https://kc.coulomb.social/.well-known/openid-configuration --scopes openid,profile,email,groups" + ] + }, + { + "step": "restart_forgejo_once", + "argv": [ + "ssh", + "railiance01", + "kubectl -n forgejo rollout restart deployment/forgejo-gitea" + ] + } + ], + "rollback": [ + { + "step": "remove_only_this_proved_created_source", + "argv": [ + "ssh", + "railiance01", + "kubectl -n forgejo exec deployment/forgejo-gitea -c gitea -- /usr/local/bin/gitea --config /data/gitea/conf/app.ini admin auth delete --id ''" + ], + "condition": "Only before human linking; uncertain creation, drift or possible new links requires owner reconciliation, never blind deletion." + }, + { + "step": "restart_forgejo_if_source_was_removed", + "argv": [ + "ssh", + "railiance01", + "kubectl -n forgejo rollout restart deployment/forgejo-gitea" + ] + }, + { + "step": "remove_only_exact_created_public_client", + "argv": [ + "/home/worsch/repo-manager/.venv/bin/python", + "/home/worsch/net-kingdom/sso-mfa/k8s/keycape/keycape-client.py", + "apply", + "--operation", + "remove", + "--client-file", + "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/client-public-build.yaml", + "--check-config", + "/tmp/custodian-forgejo-public-checker-r1/keycape", + "--expected-uid", + "2e94519d-1550-41c7-9701-2efe47fe1fd3", + "--expected-resource-version", + "", + "--case-acceptance", + "" + ] + }, + { + "step": "restart_keycape_if_registry_was_reverted", + "argv": [ + "ssh", + "railiance01", + "kubectl -n sso rollout restart deployment/keycape" + ] + } + ], + "dynamic_arguments": "Only actual approval reference, proved-created source ID and verified registry resourceVersion are filled from real receipts. They do not authorize changed profile/source inputs." +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/forgejo-auth-source-inventory.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/forgejo-auth-source-inventory.json new file mode 100644 index 0000000..36617c6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/forgejo-auth-source-inventory.json @@ -0,0 +1,8 @@ +{ + "exit_code": 0, + "header_recognized": true, + "sources": [], + "raw_output_discarded": true, + "configuration_values_emitted": false, + "mode": "read-only authentication-source inventory" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/forgejo-values.postimage.yaml b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/forgejo-values.postimage.yaml new file mode 100644 index 0000000..3bb9812 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/forgejo-values.postimage.yaml @@ -0,0 +1,138 @@ +# Non-secret Forgejo Helm values (gitea-charts/gitea chart + Forgejo image). +# Layer helm/forgejo-secrets.sops.yaml for database password and admin credentials. +# Recreate avoids leveldb queue lock fights on the shared RWO PVC during upgrades. +strategy: + type: Recreate + +service: + http: + type: ClusterIP + +image: + registry: code.forgejo.org + repository: forgejo/forgejo + tag: "11.0.3" + rootless: true + +persistence: + enabled: true + size: 10Gi + +postgresql: + enabled: false + +postgresql-ha: + enabled: false + +redis-cluster: + enabled: false + +valkey-cluster: + enabled: false + +# File subPath keeps Git from copying ConfigMap projection symlinks. +extraVolumes: +- name: secret-scan-template + configMap: + name: forgejo-secret-template-832f6dc6b809 + defaultMode: 365 +extraContainerVolumeMounts: +- &id001 + name: secret-scan-template + mountPath: /opt/forge-secret-template/hooks/pre-receive.d/10-custodian-secret-scan + subPath: hook + readOnly: true +extraInitVolumeMounts: +- *id001 + +# Verify admitted scanner custody after Kubernetes fsGroup volume processing. +# Only the existing security subtree is mounted; no credentials are needed. +preExtraInitContainers: +- name: verify-secret-scanner + image: code.forgejo.org/forgejo/forgejo@sha256:691657c7e64af7730a2e885e6d022cf16ae60724931c5392bd61838afadcaeba + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - "#!/bin/sh\nset +vx\nset -eu\ntest \"$(id -u)\" = 1000\ntest -d /data && test ! -L /data\ntest -d /data/security && test ! -L /data/security\ntest -d /data/security/receive-secret-scan && test ! -L /data/security/receive-secret-scan\ntest -d /data/security/receive-secret-scan/20261012-57a0642b6251 && test ! -L /data/security/receive-secret-scan/20261012-57a0642b6251\ntest -f /data/security/receive-secret-scan/20261012-57a0642b6251/receive-secret-scan && test ! -L /data/security/receive-secret-scan/20261012-57a0642b6251/receive-secret-scan\ntest \"$(stat -c %u /data/security/receive-secret-scan/20261012-57a0642b6251/receive-secret-scan)\" = 1000\ntest \"$(sha256sum /data/security/receive-secret-scan/20261012-57a0642b6251/receive-secret-scan | cut -d \" \" -f 1)\" = 57a0642b625105440fe146fceb4d244fd2dd08a75beb316831675643e6cc27ec\ncase \"$(stat -c %a /data/security/receive-secret-scan/20261012-57a0642b6251/receive-secret-scan)\" in 755|775) ;; *) exit 21;; esac\ntest -f /data/security/receive-secret-scan/20261012-57a0642b6251/gitleaks\ + \ && test ! -L /data/security/receive-secret-scan/20261012-57a0642b6251/gitleaks\ntest \"$(stat -c %u /data/security/receive-secret-scan/20261012-57a0642b6251/gitleaks)\" = 1000\ntest \"$(sha256sum /data/security/receive-secret-scan/20261012-57a0642b6251/gitleaks | cut -d \" \" -f 1)\" = 88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509\ncase \"$(stat -c %a /data/security/receive-secret-scan/20261012-57a0642b6251/gitleaks)\" in 755|775) ;; *) exit 21;; esac\nif [ \"${1:-check}\" = apply ]; then\n chmod 755 /data/security/receive-secret-scan/20261012-57a0642b6251/receive-secret-scan\n test \"$(stat -c %a /data/security/receive-secret-scan/20261012-57a0642b6251/receive-secret-scan)\" = 755\n chmod 755 /data/security/receive-secret-scan/20261012-57a0642b6251/gitleaks\n test \"$(stat -c %a /data/security/receive-secret-scan/20261012-57a0642b6251/gitleaks)\" = 755\nfi\n\n#!/bin/sh\nset +vx\nset -eu\ntest \"$(id -u)\" = 1000\ntest -d /data && test ! -L /data\ntest -d /data/security\ + \ && test ! -L /data/security\ntest -d /data/security/receive-secret-scan && test ! -L /data/security/receive-secret-scan\ntest -d /data/security/receive-secret-scan/20261018-73ee04078786 && test ! -L /data/security/receive-secret-scan/20261018-73ee04078786\ntest -f /data/security/receive-secret-scan/20261018-73ee04078786/receive-secret-scan && test ! -L /data/security/receive-secret-scan/20261018-73ee04078786/receive-secret-scan\ntest \"$(stat -c %u /data/security/receive-secret-scan/20261018-73ee04078786/receive-secret-scan)\" = 1000\ntest \"$(sha256sum /data/security/receive-secret-scan/20261018-73ee04078786/receive-secret-scan | cut -d \" \" -f 1)\" = 73ee04078786801fc74d80d2bef122b7764c93856e403e8de5b078336d7f9e1a\ncase \"$(stat -c %a /data/security/receive-secret-scan/20261018-73ee04078786/receive-secret-scan)\" in 755|775) ;; *) exit 21;; esac\ntest -f /data/security/receive-secret-scan/20261018-73ee04078786/gitleaks && test ! -L /data/security/receive-secret-scan/20261018-73ee04078786/gitleaks\n\ + test \"$(stat -c %u /data/security/receive-secret-scan/20261018-73ee04078786/gitleaks)\" = 1000\ntest \"$(sha256sum /data/security/receive-secret-scan/20261018-73ee04078786/gitleaks | cut -d \" \" -f 1)\" = 88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509\ncase \"$(stat -c %a /data/security/receive-secret-scan/20261018-73ee04078786/gitleaks)\" in 755|775) ;; *) exit 21;; esac\nif [ \"${1:-check}\" = apply ]; then\n chmod 755 /data/security/receive-secret-scan/20261018-73ee04078786/receive-secret-scan\n test \"$(stat -c %a /data/security/receive-secret-scan/20261018-73ee04078786/receive-secret-scan)\" = 755\n chmod 755 /data/security/receive-secret-scan/20261018-73ee04078786/gitleaks\n test \"$(stat -c %a /data/security/receive-secret-scan/20261018-73ee04078786/gitleaks)\" = 755\nfi\n" + - sh + - apply + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + resources: + requests: + cpu: 10m + memory: 32Mi + limits: + cpu: 100m + memory: 64Mi + volumeMounts: + - name: data + mountPath: /data/security + subPath: security + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + +gitea: + oauth: + - name: netkingdom + provider: openidConnect + key: forgejo + secret: "" + autoDiscoverUrl: https://kc.coulomb.social/.well-known/openid-configuration + scopes: openid,profile,email,groups + admin: + username: forgejo_admin + email: admin@coulomb.social + passwordMode: initialOnlyRequireReset + # Mailer password from OpenBao via ExternalSecret forgejo-mailer (not Helm/SOPS). + additionalConfigFromEnvs: + - name: GITEA__GIT_0X2E_CONFIG__INIT_0X2E_TEMPLATEDIR + value: /opt/forge-secret-template + - name: GITEA__mailer__PASSWD + valueFrom: + secretKeyRef: + name: forgejo-mailer + key: MAILER_PASSWD + config: + oauth2_client: + ENABLE_AUTO_REGISTRATION: false + ACCOUNT_LINKING: login + OPENID_CONNECT_SCOPES: openid profile email groups + database: + DB_TYPE: postgres + HOST: forgejo-db-rw.databases.svc.cluster.local:5432 + NAME: forgejo + USER: forgejo + server: + DOMAIN: forgejo.coulomb.social + SSH_DOMAIN: forgejo.coulomb.social + SSH_PORT: 22 + SSH_LISTEN_PORT: 2222 + DISABLE_SSH: false + service: + DISABLE_REGISTRATION: true + security: + INSTALL_LOCK: true + session: + COOKIE_SECURE: true + mailer: + ENABLED: true + FROM: forgejo@coulomb.social + # IONOS submission port 587 uses STARTTLS, not implicit TLS (smtps). + PROTOCOL: smtp+starttls + SMTP_ADDR: smtp.ionos.de + SMTP_PORT: 587 + USER: forgejo@coulomb.social + actions: + ENABLED: true \ No newline at end of file diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/helm-render-validation.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/helm-render-validation.json new file mode 100644 index 0000000..d730b55 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/helm-render-validation.json @@ -0,0 +1,12 @@ +{ + "verified": true, + "chart": "gitea 12.5.0", + "chart_sha256": "2d6f35f3d1ff0cfcab0f22142d20a1d6405fa974a79d7a18000904d233131823", + "public_add_oauth_command": "gitea admin auth add-oauth --auto-discover-url \"https://kc.coulomb.social/.well-known/openid-configuration\" --key \"forgejo\" --name \"netkingdom\" --provider \"openidConnect\" --scopes \"openid,profile,email,groups\" --secret \"\"", + "rendered_resource_count": 8, + "real_credentials_used": false, + "private_render_discarded": true, + "apps_base_sha256": "64ee8cbac800d9c5743ccdba47eb019064d23afdbc2d92c2568c7eafb107c18f", + "apps_postimage_sha256": "210e6e450ad9fbe7e25ad7ec8ae5d4e9bf9bbc9e7f65d52ac670a66a9dd79d06", + "installed_scanner_bytes_preserved": true +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/issuer-start-probe.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/issuer-start-probe.json new file mode 100644 index 0000000..bb0f405 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/issuer-start-probe.json @@ -0,0 +1,31 @@ +{ + "scope": "Read-only public discovery and unauthenticated authorization-start probe; no factor entry, admin access or config mutation", + "issuer": "https://kc.coulomb.social", + "authorization_endpoint_path": "/authorize", + "client_id": "forgejo", + "callback_uri": "https://forgejo.coulomb.social/user/oauth2/netkingdom/callback", + "requested_scopes": [ + "openid", + "profile", + "email", + "groups" + ], + "pkce": "S256", + "http_status": 400, + "known_oauth_error_markers": [], + "redirect_host": null, + "redirect_path": "", + "login_form_present": false, + "tokens_or_codes_obtained": false, + "raw_html_or_state_published": false, + "interpretation": "The live issuer refuses the declared forgejo client as unknown client_id. Native client registration remains a real prerequisite, separate from deferred hardening.", + "followup_closed_message_matches": { + "unknown client_id": true, + "invalid redirect_uri": false, + "redirect_uri not allowed": false, + "unsupported response_type": false, + "invalid scope": false, + "MFA required": false, + "PKCE required": false + } +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/native-signin-settings.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/native-signin-settings.json new file mode 100644 index 0000000..3b0deac --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/native-signin-settings.json @@ -0,0 +1,12 @@ +{ + "exit_code": 0, + "safe_settings": { + "ROOT_URL": "https://forgejo.coulomb.social/", + "DISABLE_REGISTRATION": "true" + }, + "unrecognized_output_discarded": false, + "default_enable_auto_registration": false, + "default_account_linking": "login", + "default_openid_scopes": "use the source scopes", + "native_changes": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/net-kingdom-client.postimage.yaml b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/net-kingdom-client.postimage.yaml new file mode 100644 index 0000000..4ffc903 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/net-kingdom-client.postimage.yaml @@ -0,0 +1,15 @@ +# Temporary build profile; activation requires the exact CUST80-FORGEJO-PUBLIC-BUILD-20261018 approval. +# The accepted confidential-client case remains the target; full acceptance is CUST-WP-0082-T20. +clientId: forgejo +displayName: Forgejo source forge +redirectUris: +- https://forgejo.coulomb.social/user/oauth2/netkingdom/callback +allowedScopes: +- openid +- profile +- email +- groups +grantTypes: +- authorization_code +clientType: public +mfaRequired: true diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/prepublication-validation-r1.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/prepublication-validation-r1.json new file mode 100644 index 0000000..bf31a2d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/prepublication-validation-r1.json @@ -0,0 +1,14 @@ +{ + "verified": true, + "proposal_sha256": "61eff8b488674e75510133fe118de86d895be04c033e109c3597c03ed2ecf30e", + "profile_approved": false, + "native_effects_executed": false, + "source_input_pins_matched": 12, + "upstream_protocol_and_release": "passed", + "native_registry_dry_run": "passed without mutation", + "original_task_bindings_preserved": 40, + "current_task_bindings": 69, + "unrelated_work_preserved": 8, + "T41": "done preparation only", + "T42": "wait actual scoped founder go and three authorized owner reviews" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/prepublication-validation.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/prepublication-validation.json new file mode 100644 index 0000000..54c743f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/prepublication-validation.json @@ -0,0 +1,18 @@ +{ + "verified": true, + "proposal_sha256": "fcea2cd32b8ee2634d2ebeb9ef4b659b6b85f8a4878413ed96e5d06cb09892c3", + "profile_approved": false, + "native_effects_executed": false, + "source_input_pins_matched": 12, + "upstream_protocol_and_release": "passed", + "native_registry_dry_run": "passed without mutation", + "original_task_bindings_preserved": 40, + "current_task_bindings": 69, + "unrelated_work_preserved": 8, + "T41": "done preparation only", + "T42": "wait actual scoped founder go and three authorized owner reviews", + "proposal_revision": 2, + "original_revision_retained": true, + "source_pins_semantically_unchanged": true, + "proposal_scan_with_unchanged_rules": "clean" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/proposal.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/proposal.json new file mode 100644 index 0000000..8d5caae --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/proposal.json @@ -0,0 +1,136 @@ +{ + "proposal_id": "CUST80-FORGEJO-PUBLIC-BUILD-20261018", + "revision": 2, + "state": "proposed_not_approved", + "purpose": "Deliver actual Forgejo NetKingdom sign-in using the existing upstream release under a temporary public PKCE build profile.", + "decision_basis": "Founder good-case-first direction9663d4ca; this new native/profile action needs separate explicit go.", + "mode_of_authority": "ADMINISTER @ realm:kubernetes/railiance01; activation only after actual scoped APPROVED disposition", + "target": { + "cluster_node_ip": "92.205.62.239", + "issuer_config": "sso/keycape-config", + "issuer_client_id": "forgejo", + "issuer_config_uid": "2e94519d-1550-41c7-9701-2efe47fe1fd3", + "issuer_config_resource_version": "75372162", + "forgejo_auth_source": "netkingdom", + "callback_uri": "https://forgejo.coulomb.social/user/oauth2/netkingdom/callback" + }, + "profile": { + "client_type": "public", + "authorization_code_pkce": "S256", + "scopes": [ + "openid", + "profile", + "email", + "groups" + ], + "mfa_required": true, + "client_secret_generated_or_distributed": false, + "automatic_new_account_registration": false, + "existing_account_linking": "login", + "local_signin_and_local_2fa": "preserved", + "role_or_group_automation": "none; existing Forgejo account permissions retained" + }, + "explicit_deferrals": [ + "Confidential-client credential generation and issuer/consumer delivery remain the unchanged target in CUST-WP-0082-T20 and existing owner plans.", + "This scoped build-profile action does not claim the full SecurityCaseType lifecycle, protected executor/kernel, approval/PDP or independent audit acceptance.", + "Full current KeyCape checks on each API/Git/SSH operation, any-of group policy, revocation and isolation remain unaccepted hardening work." + ], + "admission": { + "founder": "Actual explicit go bound to this revision/digest, never inferred from previous scanner or sequencing decisions.", + "independent_reviews_required": [ + "net-kingdom", + "railiance-apps", + "key-cape" + ], + "reviewer_authorization": "Founder must authorize those owner agents; no new worker has been spawned.", + "each_review_must_bind": "This proposal digest and exact producer/consumer source pins, declared build-profile limits, activation and rollback.", + "owner_reviews_status": "not requested or written yet" + }, + "source_pins": [ + { + "path": "/home/worsch/net-kingdom/sso-mfa/k8s/keycape/keycape-registration-adapter.py", + "sha256": "a53beea4dbbe373ce7a5b84d55e79be8407ab9688037c7ee057afcc7be576047" + }, + { + "path": "/home/worsch/net-kingdom/sso-mfa/k8s/keycape/keycape-client.py", + "sha256": "4b63161cf3365d34dd684a3116e2c935baaf7f6aa4b30983f6883546db3d6591" + }, + { + "path": "/home/worsch/net-kingdom/sso-mfa/k8s/keycape/portal-client-rollout.py", + "sha256": "158e1d36eb881cdd0a2eda91b472adea5e8fde5061e00d26dda368580aeb871f" + }, + { + "path": "/home/worsch/net-kingdom/sso-mfa/k8s/keycape/openbao-client-config.py", + "sha256": "1b908b4c36134212d78f9fbe7a2a201eb3ced06d31bb1dcea4a59d80689d84b1" + }, + { + "path": "/home/worsch/net-kingdom/sso-mfa/k8s/keycape/register-user-engine-portal.py", + "sha256": "67173edc44f5457e7b6a3e0fb2321b398159cfce5dea8ead01eabf835fb38d76" + }, + { + "path": "/tmp/custodian-forgejo-public-checker-r1/keycape", + "sha256": "be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5" + }, + { + "path": "/home/worsch/.cache/helm/repository/gitea-12.5.0.tgz", + "sha256": "2d6f35f3d1ff0cfcab0f22142d20a1d6405fa974a79d7a18000904d233131823" + } + ], + "packet_input_sha256": { + "client-public-build.yaml": "942d642a40bf76f11d09336fa6e90f861b5fccffa7ba6143bdcbfa8a1498bb6b", + "forgejo-values.postimage.yaml": "210e6e450ad9fbe7e25ad7ec8ae5d4e9bf9bbc9e7f65d52ac670a66a9dd79d06", + "net-kingdom-client.postimage.yaml": "3210f662cf65d54e912969ad03767053baf5c867402fc5f24b7017fb3b82d8e1", + "execution-steps.json": "98cb80a42e22315a7e846418f5157091c2e246ed3eb86e26dc46a3d5bec69ee6", + "deployment-preconditions.json": "940594b48dfc024ed3eb83f86f051ee539b2ad90a18aa088cfa73483618e529e" + }, + "owner_source_publication": { + "railiance-apps": { + "base_revision": "2fdc198b2bb8e6d4ad50768c22cf99328828eb4f", + "path": "helm/forgejo-values.yaml", + "base_sha256": "64ee8cbac800d9c5743ccdba47eb019064d23afdbc2d92c2568c7eafb107c18f", + "postimage": "forgejo-values.postimage.yaml" + }, + "net-kingdom": { + "base_revision": "b3f31d9f26b98a3b92690e0be34a205e49fa3956", + "path": "sso-mfa/k8s/keycape/client-profiles/forgejo-public-build.yaml", + "precondition": "absent", + "postimage": "net-kingdom-client.postimage.yaml" + }, + "method": "Existing file-scoped publication helper, ordinary hooks and consistency/projection; preserve unrelated index/worktree states." + }, + "preconditions": [ + "Revalidate all source and packet hashes.", + "Revalidate deployment UID, generation, images and pod-template hashes from deployment-preconditions.json immediately before mutation.", + "KeyCape live/proposed config must pass the reproduced be37738f checker; registry entry remains absent and UID/resourceVersion exact.", + "Forgejo authentication-source inventory must remain empty before creation; native local settings remain auto-registration false, linking login and expected ROOT_URL.", + "Read node resource requests and retain Recreate, one replica each, same images/resources. Current CPU requests3605m/4000m; no additional pod requested.", + "Run in the founder-attended window; no unattended schedule or later generic grant." + ], + "execution": "Exactly the four apply commands in execution-steps.json, with Ready and discovery/readback between phases; retain both changed and unchanged failures.", + "verification": [ + "KeyCape same image Ready, no startup restart loop, discovery200 and authorization start accepts the exact forgejo client/callback with MFA still required.", + "Forgejo same image Ready; one netkingdom source; local sign-in still available; redirect has exact HTTPS callback, issuer, scopes, state and S256 challenge.", + "Compare pod templates after stripping only the approved restart annotation; preserve all scanner hook/template/guard and other configuration.", + "Record actual source ID and registry before/after UID/resourceVersion. Do not claim browser login from readiness.", + "Then T43 actual founder account linking/sign-in and T44 actual coding-agent Git/authenticated API trials; no trial impersonation." + ], + "rollback": "execution-steps.json: created-only source removal before human linking, exact public registry removal, up to one additional restart per changed deployment. Preserve unrelated resources. Uncertainty/drift/new links stops automatic undo for owner reconciliation.", + "restart_budget": { + "normal": { + "keycape": 1, + "forgejo": 1 + }, + "additional_only_if_rollback": { + "keycape": 1, + "forgejo": 1 + }, + "readiness_timeout_seconds_each": 180, + "impact": "Each single-replica Recreate briefly interrupts its service and in-flight sign-in. Stop at a failed phase; no continuing to consumer if issuer fails." + }, + "cost_estimate_eur": 0, + "cost_basis": "No purchase, extra deployment or resource allocation; existing infrastructure only. Agent/model/tool spend is outside this estimate.", + "risk_estimate_eur": 300, + "risk_basis": "Founder-reviewed planning estimate: up to three hours recovery at EUR100/hour; temporary build-mode identity-profile exposure is unpriced and requires explicit human acceptance rather than being declared covered by that number. No customer production approval.", + "native_changes_already_executed": false, + "ordinary_confidential_case_status_changed": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/proposal.sha256 b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/proposal.sha256 new file mode 100644 index 0000000..d9914d6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/proposal.sha256 @@ -0,0 +1 @@ +fcea2cd32b8ee2634d2ebeb9ef4b659b6b85f8a4878413ed96e5d06cb09892c3 proposal.json diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt1.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt1.json new file mode 100644 index 0000000..6145d73 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt1.json @@ -0,0 +1,86 @@ +{ + "exit_code": 1, + "seconds": 1.018, + "argv": [ + "bwrap", + "--unshare-net", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--ro-bind", + "/home/worsch/go/pkg/mod", + "/home/worsch/go/pkg/mod", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-public-pkce-r1", + "/work", + "--ro-bind", + "/tmp/custodian-forgejo-successor/go", + "/toolchain", + "--chdir", + "/work/source/src", + "--clearenv", + "--setenv", + "PATH", + "/toolchain/bin:/usr/bin:/bin", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "0", + "--setenv", + "PUBLIC_PKCE_REPORT", + "/work/public-pkce-report.json", + "--", + "go", + "test", + "-mod=mod", + "-p", + "4", + "-json", + "-count=1", + "-run", + "^TestUpstreamForgejoPublicPKCE$", + "./internal/server/oidc" + ], + "real_credentials": false, + "network": "isolated loopback only", + "live_changes": false, + "profile_approved": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt1.jsonl b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt1.jsonl new file mode 100644 index 0000000..d5a7daf --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt1.jsonl @@ -0,0 +1,7 @@ +go: downloading github.com/markbates/goth v1.80.0 +{"ImportPath":"github.com/markbates/goth/providers/openidConnect","Action":"build-output","Output":"# keycape/internal/server/oidc\n"} +{"ImportPath":"github.com/markbates/goth/providers/openidConnect","Action":"build-output","Output":"internal/server/oidc/forgejo_public_interop_test.go:19:2: mkdir /work/modules/cache/download/github.com/markbates: file exists\n"} +{"ImportPath":"github.com/markbates/goth/providers/openidConnect","Action":"build-fail"} +{"Time":"2026-10-18T17:04:14.412495465Z","Action":"start","Package":"keycape/internal/server/oidc"} +{"Time":"2026-10-18T17:04:14.412578559Z","Action":"output","Package":"keycape/internal/server/oidc","Output":"FAIL\tkeycape/internal/server/oidc [setup failed]\n"} +{"Time":"2026-10-18T17:04:14.412620525Z","Action":"fail","Package":"keycape/internal/server/oidc","Elapsed":0,"FailedBuild":"github.com/markbates/goth/providers/openidConnect"} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt2.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt2.json new file mode 100644 index 0000000..1d83c16 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt2.json @@ -0,0 +1,86 @@ +{ + "exit_code": 1, + "seconds": 0.266, + "argv": [ + "bwrap", + "--unshare-net", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--ro-bind", + "/home/worsch/go/pkg/mod", + "/home/worsch/go/pkg/mod", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "--bind", + "/tmp/custodian-forgejo-public-pkce-r1", + "/work", + "--ro-bind", + "/tmp/custodian-forgejo-successor/go", + "/toolchain", + "--chdir", + "/work/source/src", + "--clearenv", + "--setenv", + "PATH", + "/toolchain/bin:/usr/bin:/bin", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "0", + "--setenv", + "PUBLIC_PKCE_REPORT", + "/work/public-pkce-report.json", + "--", + "go", + "test", + "-mod=mod", + "-p", + "4", + "-json", + "-count=1", + "-run", + "^TestUpstreamForgejoPublicPKCE$", + "./internal/server/oidc" + ], + "real_credentials": false, + "network": "isolated loopback only", + "live_changes": false, + "profile_approved": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt2.jsonl b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt2.jsonl new file mode 100644 index 0000000..e233dd0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt2.jsonl @@ -0,0 +1,7 @@ +go: downloading golang.org/x/oauth2 v0.28.0 +{"ImportPath":"golang.org/x/oauth2","Action":"build-output","Output":"# keycape/internal/server/oidc\n"} +{"ImportPath":"golang.org/x/oauth2","Action":"build-output","Output":"/work/modules/github.com/markbates/goth@v1.80.0/provider.go:8:2: open /work/modules/cache/download/golang.org/x/oauth2/@v/v0.28.0.lock: read-only file system\n"} +{"ImportPath":"golang.org/x/oauth2","Action":"build-fail"} +{"Time":"2026-10-18T17:04:41.667384334Z","Action":"start","Package":"keycape/internal/server/oidc"} +{"Time":"2026-10-18T17:04:41.667420944Z","Action":"output","Package":"keycape/internal/server/oidc","Output":"FAIL\tkeycape/internal/server/oidc [setup failed]\n"} +{"Time":"2026-10-18T17:04:41.667446255Z","Action":"fail","Package":"keycape/internal/server/oidc","Elapsed":0,"FailedBuild":"golang.org/x/oauth2"} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt3.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt3.json new file mode 100644 index 0000000..c77cff0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt3.json @@ -0,0 +1,86 @@ +{ + "exit_code": 0, + "seconds": 19.159, + "argv": [ + "bwrap", + "--unshare-net", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--ro-bind", + "/home/worsch/go/pkg/mod", + "/home/worsch/go/pkg/mod", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "--bind", + "/tmp/custodian-forgejo-public-pkce-r1", + "/work", + "--ro-bind", + "/tmp/custodian-forgejo-successor/go", + "/toolchain", + "--chdir", + "/work/source/src", + "--clearenv", + "--setenv", + "PATH", + "/toolchain/bin:/usr/bin:/bin", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "0", + "--setenv", + "PUBLIC_PKCE_REPORT", + "/work/public-pkce-report.json", + "--", + "go", + "test", + "-mod=mod", + "-p", + "4", + "-json", + "-count=1", + "-run", + "^TestUpstreamForgejoPublicPKCE$", + "./internal/server/oidc" + ], + "real_credentials": false, + "network": "isolated loopback only", + "live_changes": false, + "profile_approved": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt3.jsonl b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt3.jsonl new file mode 100644 index 0000000..65e2a12 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-attempt3.jsonl @@ -0,0 +1,8 @@ +{"Time":"2026-10-18T17:05:47.587707851Z","Action":"start","Package":"keycape/internal/server/oidc"} +{"Time":"2026-10-18T17:05:47.593659497Z","Action":"run","Package":"keycape/internal/server/oidc","Test":"TestUpstreamForgejoPublicPKCE"} +{"Time":"2026-10-18T17:05:47.593752356Z","Action":"output","Package":"keycape/internal/server/oidc","Test":"TestUpstreamForgejoPublicPKCE","Output":"=== RUN TestUpstreamForgejoPublicPKCE\n"} +{"Time":"2026-10-18T17:05:47.770524191Z","Action":"output","Package":"keycape/internal/server/oidc","Test":"TestUpstreamForgejoPublicPKCE","Output":"--- PASS: TestUpstreamForgejoPublicPKCE (0.18s)\n"} +{"Time":"2026-10-18T17:05:47.770589125Z","Action":"pass","Package":"keycape/internal/server/oidc","Test":"TestUpstreamForgejoPublicPKCE","Elapsed":0.18} +{"Time":"2026-10-18T17:05:47.770608259Z","Action":"output","Package":"keycape/internal/server/oidc","Output":"PASS\n"} +{"Time":"2026-10-18T17:05:47.770911599Z","Action":"output","Package":"keycape/internal/server/oidc","Output":"ok \tkeycape/internal/server/oidc\t0.182s\n"} +{"Time":"2026-10-18T17:05:47.770938327Z","Action":"pass","Package":"keycape/internal/server/oidc","Elapsed":0.183} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-baseline.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-baseline.json new file mode 100644 index 0000000..c0a430f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-baseline.json @@ -0,0 +1,9 @@ +{ + "issuer_source_revision": "e2c5a947829568baf837543a6708fd0c3f6a863b", + "issuer_go_mod_sha256": "66e219373666294d37395082700f6e62fa9a55b02bc996655883b07d04436b14", + "upstream_archive_sha256": "1ddd8c926ca07d40436897971458ace7dcf1d3496a0813ab9e0d869edef2365c", + "goth_source": "/tmp/custodian-forgejo-successor-attempt2/modules/github.com/markbates/goth@v1.80.0", + "test_source_sha256": "8968c617d83bb041f0c2a4c251bf1efc88db45036f51c6eb55c5058bdb662aed", + "candidate_profile": "Public PKCE build-mode candidate only; NOT approved; original confidential target unchanged", + "live_changes": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-result.json new file mode 100644 index 0000000..48895ea --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/protocol-result.json @@ -0,0 +1,14 @@ +{ + "actual_https_discovery_token_and_userinfo": true, + "code_replay_refused": true, + "identity_matches": true, + "live_changes": false, + "native_signin_or_account_linking": false, + "provider": "github.com/markbates/goth v1.80.0 (upstream Forgejo 11.0.3)", + "public_client_no_secret": true, + "status": "passed", + "synthetic_human_mfa_session": true, + "token_requests": 4, + "userinfo_requests": 1, + "wrong_pkce_refused": true +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-build-values.yaml b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-build-values.yaml new file mode 100644 index 0000000..660a029 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-build-values.yaml @@ -0,0 +1,15 @@ +# PROPOSED, NOT APPLIED. Temporary public-PKCE development profile. +# The confidential-client and full security acceptance target remains WP-82. +gitea: + oauth: + - name: netkingdom + provider: openidConnect + key: forgejo + secret: "" + autoDiscoverUrl: https://kc.coulomb.social/.well-known/openid-configuration + scopes: openid,profile,email,groups + config: + oauth2_client: + ENABLE_AUTO_REGISTRATION: false + ACCOUNT_LINKING: login + OPENID_CONNECT_SCOPES: openid profile email groups diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-registration-dry-run.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-registration-dry-run.json new file mode 100644 index 0000000..0d2f704 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-registration-dry-run.json @@ -0,0 +1,30 @@ +{ + "before": { + "resource_version": "75372162", + "uid": "2e94519d-1550-41c7-9701-2efe47fe1fd3" + }, + "change_needed": true, + "changed": false, + "check_config": { + "live": { + "clients": 13, + "errors": [], + "valid": true + }, + "proposed": { + "clients": 14, + "errors": [], + "valid": true + } + }, + "client_id": "forgejo", + "client_type": "public", + "deployment_env_bound": null, + "mode": "dry-run", + "operation": "add", + "other_secret_data_unchanged": true, + "schema": "netkingdom.keycape-client.v1", + "target": "sso/keycape-config", + "unrelated_config_bytes_preserved": true, + "values_emitted": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-registration-inspection.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-registration-inspection.json new file mode 100644 index 0000000..b862feb --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public-registration-inspection.json @@ -0,0 +1,28 @@ +{ + "before": { + "resource_version": "75372162", + "uid": "2e94519d-1550-41c7-9701-2efe47fe1fd3" + }, + "change_needed": true, + "changed": false, + "check_config": { + "live": { + "clients": 13, + "errors": [], + "valid": true + }, + "proposed": { + "clients": 14, + "errors": [], + "valid": true + } + }, + "client_id": "forgejo", + "client_type": "public", + "deployment_env_bound": null, + "mode": "inspect", + "operation": "add", + "schema": "netkingdom.keycape-client.v1", + "target": "sso/keycape-config", + "values_emitted": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public_pkce_interop_test.go b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public_pkce_interop_test.go new file mode 100644 index 0000000..d6458a9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/public_pkce_interop_test.go @@ -0,0 +1,92 @@ +package oidc_test + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "os" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/markbates/goth/providers/openidConnect" + "keycape/internal/domain" + "keycape/internal/server/oidc" + "keycape/internal/server/telemetry" +) + +type fixtureDirectory struct{ user domain.User } + +func (d fixtureDirectory) LookupUser(context.Context, string) (*domain.User, error) { u := d.user; return &u, nil } +func (d fixtureDirectory) LookupGroups(context.Context, string) ([]domain.Group, error) { return nil, nil } +func (d fixtureDirectory) ListUsers(context.Context) ([]domain.User, error) { return []domain.User{d.user}, nil } +func (d fixtureDirectory) ValidatePassword(context.Context, string, string) (bool, error) { return false, nil } + +// Source compatibility proof only: actual producer token/UserInfo handlers and +// the exact upstream Forgejo Goth version. Human/MFA authorization is a synthetic +// session seed; this cannot count as deployed sign-in or account linking. +func TestUpstreamForgejoPublicPKCE(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { t.Fatal("fixture signing key failed") } + directory := fixtureDirectory{domain.User{ID: "uid=fixture,ou=people,dc=invalid", Username: "fixture", DisplayName: "Synthetic fixture", Email: "fixture@example.invalid", Enabled: true, Tenant: "tenant:fixture", Groups: []string{"forge-admins"}}} + client := &domain.Client{ClientID: "forgejo", ClientType: "public", RedirectURIs: []string{"https://forgejo.invalid/user/oauth2/netkingdom/callback"}, AllowedScopes: []string{"openid", "profile", "email", "groups"}, GrantTypes: []string{"authorization_code"}} + sessions := oidc.NewSessionStore() + mux := http.NewServeMux() + server := httptest.NewTLSServer(mux) + defer server.Close() + clients := map[string]*domain.Client{client.ClientID: client} + tokens := &oidc.TokenHandler{ClientConfig: clients, Sessions: sessions, Users: directory, SigningKey: key, Issuer: server.URL, TokenLifetime: time.Minute, Emitter: telemetry.NoopEmitter{}} + userinfo := &oidc.UserinfoHandler{Users: directory, SigningKey: &key.PublicKey, Issuer: server.URL, ClientConfig: clients, Emitter: telemetry.NoopEmitter{}} + var tokenCalls, userinfoCalls atomic.Int64 + var secretPresented atomic.Bool + mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { + tokenCalls.Add(1) + _ = r.ParseForm() + _, secret, _ := r.BasicAuth() + if secret != "" || r.FormValue("client_secret") != "" { secretPresented.Store(true) } + tokens.ServeHTTP(w, r) + }) + mux.HandleFunc("/userinfo", func(w http.ResponseWriter, r *http.Request) { userinfoCalls.Add(1); userinfo.ServeHTTP(w, r) }) + mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(map[string]any{"issuer": server.URL, "authorization_endpoint": server.URL+"/authorize", "token_endpoint": server.URL+"/token", "userinfo_endpoint": server.URL+"/userinfo"}) + }) + previous := http.DefaultClient + http.DefaultClient = server.Client() + defer func(){ http.DefaultClient = previous }() + provider, err := openidConnect.New(client.ClientID, "", client.RedirectURIs[0], server.URL+"/.well-known/openid-configuration", client.AllowedScopes...) + if err != nil { t.Fatal("upstream provider discovery failed") } + provider.HTTPClient = server.Client() + session, err := provider.BeginAuth("synthetic-state") + if err != nil { t.Fatal("upstream authorization URL failed") } + authURL, _ := session.GetAuthURL() + parsed, _ := url.Parse(authURL) + if parsed.Query().Get("client_id") != client.ClientID || parsed.Query().Get("redirect_uri") != client.RedirectURIs[0] || parsed.Query().Get("state") != "synthetic-state" { t.Fatal("upstream authorization binding differs") } + verifier := strings.Repeat("v", 43) + digest := sha256.Sum256([]byte(verifier)) + issue := func() string { + return sessions.Create(&oidc.PKCESession{ClientID: client.ClientID, Username: directory.user.Username, RedirectURI: client.RedirectURIs[0], PKCEChallengeMethod: "S256", PKCEChallenge: base64.RawURLEncoding.EncodeToString(digest[:]), Scopes: client.AllowedScopes, MFAVerified: true, AuthTime: time.Now().Add(-time.Minute), ExpiresAt: time.Now().Add(time.Minute)}) + } + goodCode := issue() + _, err = session.Authorize(provider, url.Values{"code": {goodCode}, "code_verifier": {verifier}}) + if err != nil { t.Fatal("public PKCE exchange failed") } + identity, err := provider.FetchUser(session) + if err != nil || identity.UserID != directory.user.ID || identity.NickName != directory.user.Username || identity.Email != directory.user.Email || identity.AccessToken == "" || identity.IDToken == "" { t.Fatal("upstream user identity did not match") } + if secretPresented.Load() { t.Fatal("public client unexpectedly presented a secret") } + badSession, _ := provider.BeginAuth("synthetic-state-bad") + _, err = badSession.Authorize(provider, url.Values{"code": {issue()}, "code_verifier": {strings.Repeat("x",43)}}) + if err == nil { t.Fatal("wrong public PKCE verifier accepted") } + replay, _ := provider.BeginAuth("synthetic-state-replay") + _, err = replay.Authorize(provider, url.Values{"code": {goodCode}, "code_verifier": {verifier}}) + if err == nil { t.Fatal("used authorization code accepted") } + report := map[string]any{"status":"passed", "provider":"github.com/markbates/goth v1.80.0 (upstream Forgejo 11.0.3)", "actual_https_discovery_token_and_userinfo":true, "public_client_no_secret":true, "identity_matches":true, "wrong_pkce_refused":true, "code_replay_refused":true, "token_requests":tokenCalls.Load(), "userinfo_requests":userinfoCalls.Load(), "synthetic_human_mfa_session":true, "native_signin_or_account_linking":false, "live_changes":false} + raw, _ := json.MarshalIndent(report, "", " ") + if err := os.WriteFile(os.Getenv("PUBLIC_PKCE_REPORT"), append(raw,'\n'), 0600); err != nil { t.Fatal("public report write failed") } +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/railiance-apps.patch b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/railiance-apps.patch new file mode 100644 index 0000000..a220ada --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/railiance-apps.patch @@ -0,0 +1,27 @@ +--- a/helm/forgejo-values.yaml ++++ b/helm/forgejo-values.yaml +@@ -84,6 +84,13 @@ + terminationMessagePolicy: File + + gitea: ++ oauth: ++ - name: netkingdom ++ provider: openidConnect ++ key: forgejo ++ secret: "" ++ autoDiscoverUrl: https://kc.coulomb.social/.well-known/openid-configuration ++ scopes: openid,profile,email,groups + admin: + username: forgejo_admin + email: admin@coulomb.social +@@ -98,6 +105,10 @@ + name: forgejo-mailer + key: MAILER_PASSWD + config: ++ oauth2_client: ++ ENABLE_AUTO_REGISTRATION: false ++ ACCOUNT_LINKING: login ++ OPENID_CONNECT_SCOPES: openid profile email groups + database: + DB_TYPE: postgres + HOST: forgejo-db-rw.databases.svc.cluster.local:5432 diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/rebuild_checker.py b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/rebuild_checker.py new file mode 100644 index 0000000..1f26e68 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/rebuild_checker.py @@ -0,0 +1,35 @@ +"""Reproduce the already owner-reviewed checker; no runtime inputs are read.""" +from pathlib import Path +import hashlib +import io +import json +import subprocess +import tarfile +import time + +packet = Path(__file__).resolve().parent +work = Path('/tmp/custodian-forgejo-public-checker-r1') +work.mkdir(exist_ok=False) +revision = '3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987' +archive = subprocess.check_output(['git','-C','/home/worsch/key-cape','archive',revision,'src']) +assert hashlib.sha256(archive).hexdigest() == '770a15933e6c45d4b94beb37d4322fd4ae1436d8193dfb7f5655bbc851f3fc73' +with tarfile.open(fileobj=io.BytesIO(archive)) as tar: + tar.extractall(work, filter='data') +toolchain = Path('/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.0.linux-amd64') +env = {'PATH':str(toolchain/'bin')+':/usr/bin:/bin','TMPDIR':str(work), + 'GOTOOLCHAIN':'local','GOPROXY':'off','GOSUMDB':'off', + 'GOMODCACHE':'/home/worsch/go/pkg/mod','GOCACHE':str(work/'gocache'), + 'CGO_ENABLED':'0','GOOS':'linux','GOARCH':'amd64','GOAMD64':'v1', + 'GOFLAGS':'-mod=readonly'} +command = [str(toolchain/'bin/go'),'build','-trimpath','-buildvcs=false','-o',str(work/'keycape'),'./cmd/keycape'] +start = time.monotonic() +result = subprocess.run(command, cwd=work/'src', env=env, capture_output=True, timeout=240) +(packet/'checker-build.log').write_bytes(result.stdout+result.stderr) +assert result.returncode == 0, 'checker build failed; contained log retained' +digest = hashlib.sha256((work/'keycape').read_bytes()).hexdigest() +assert digest == 'be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5' +receipt = {'verified':True,'artifact':str(work/'keycape'),'sha256':digest, + 'source_revision':revision,'byte_identical_to_existing_owner_review':True, + 'seconds':round(time.monotonic()-start,3),'real_runtime_configuration_access':False} +(packet/'checker-reproduction.json').write_text(json.dumps(receipt,indent=2)+'\n') +print(json.dumps(receipt)) diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/registration-inspection.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/registration-inspection.json new file mode 100644 index 0000000..7d614b7 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/registration-inspection.json @@ -0,0 +1,17 @@ +{ + "before": { + "resource_version": "75372162", + "uid": "2e94519d-1550-41c7-9701-2efe47fe1fd3" + }, + "change_needed": true, + "changed": false, + "check_config": null, + "client_id": "forgejo", + "client_type": "confidential", + "deployment_env_bound": false, + "mode": "inspect", + "operation": "add", + "schema": "netkingdom.keycape-client.v1", + "target": "sso/keycape-config", + "values_emitted": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/revision-history.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/revision-history.json new file mode 100644 index 0000000..e6e067d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/revision-history.json @@ -0,0 +1,12 @@ +{ + "proposal_id": "CUST80-FORGEJO-PUBLIC-BUILD-20261018", + "original_revision": 1, + "original_sha256": "61eff8b488674e75510133fe118de86d895be04c033e109c3597c03ed2ecf30e", + "original_local_receipt": "/tmp/custodian-forgejo-public-proposal-r1/proposal.json", + "current_revision": 2, + "current_sha256": "fcea2cd32b8ee2634d2ebeb9ef4b659b6b85f8a4878413ed96e5d06cb09892c3", + "reason": "The unchanged scanner classified two public source SHA-256 hashes as generic API keys when filenames were used as JSON keys. Represent identical source pins as typed path/sha256 records. No profile, command, target, source hash, verification or rollback changed.", + "original_bytes_reconstruction": "Load proposal.json preserving order; set revision to1; convert source_pins records to an ordered path:sha256 mapping; serialize json.dumps(indent=2) plus newline. Assert original_sha256. The exact original bytes are reconstructible from committed public data.", + "scanner_rules_or_exemptions_changed": false, + "native_effects": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/run_public_protocol.py b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/run_public_protocol.py new file mode 100644 index 0000000..12c17f8 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/run_public_protocol.py @@ -0,0 +1,41 @@ +"""Run the source-only upstream-provider/KeyCape compatibility check offline.""" +from pathlib import Path +import argparse +import json +import subprocess +import time + +packet = Path(__file__).resolve().parent +parser = argparse.ArgumentParser() +parser.add_argument('--attempt', required=True, type=int) +args = parser.parse_args() +stem = packet / f'protocol-attempt{args.attempt}' +work = Path('/tmp/custodian-forgejo-public-pkce-r1') +cache_a = '/home/worsch/go/pkg/mod' +cache_b = '/tmp/custodian-forgejo-successor-attempt2/modules' +argv = ['bwrap', '--unshare-net', '--unshare-pid', '--die-with-parent', '--new-session'] +for path in ['/usr', '/lib', '/lib64', cache_a]: + argv += ['--ro-bind', path, path] +argv += ['--symlink', 'usr/bin', '/bin', '--proc', '/proc', '--dev', '/dev', + '--tmpfs', '/tmp', '--ro-bind', cache_b, cache_b, + '--bind', str(work), '/work', + '--ro-bind', '/tmp/custodian-forgejo-successor/go', '/toolchain', + '--chdir', '/work/source/src', '--clearenv'] +for key, value in {'PATH':'/toolchain/bin:/usr/bin:/bin', 'GOTOOLCHAIN':'local', + 'GOPROXY':'off', 'GOSUMDB':'off', 'GOMODCACHE':'/work/modules', + 'GOCACHE':'/work/gocache', 'GOMAXPROCS':'4', 'CGO_ENABLED':'0', + 'PUBLIC_PKCE_REPORT':'/work/public-pkce-report.json'}.items(): + argv += ['--setenv', key, value] +argv += ['--', 'go', 'test', '-mod=mod', '-p', '4', '-json', '-count=1', + '-run', '^TestUpstreamForgejoPublicPKCE$', './internal/server/oidc'] +start = time.monotonic() +with stem.with_suffix('.jsonl').open('xb') as output: + result = subprocess.run(argv, stdout=output, stderr=subprocess.STDOUT, timeout=240) +receipt = {'exit_code':result.returncode, 'seconds':round(time.monotonic()-start, 3), + 'argv':argv, 'real_credentials':False, 'network':'isolated loopback only', + 'live_changes':False, 'profile_approved':False} +stem.with_suffix('.json').write_text(json.dumps(receipt, indent=2)+'\n') +if result.returncode == 0: + (packet/'protocol-result.json').write_bytes((work/'public-pkce-report.json').read_bytes()) +print(json.dumps({key:receipt[key] for key in ['exit_code','seconds','live_changes','profile_approved']})) +raise SystemExit(result.returncode) diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/upstream-release-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/upstream-release-result.json new file mode 100644 index 0000000..36bee89 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/upstream-release-result.json @@ -0,0 +1,39 @@ +{ + "image": "code.forgejo.org/forgejo/forgejo@sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b", + "profile_approved": false, + "production_mutation": false, + "database": "disposable local SQLite only", + "commands": [ + { + "name": "migrate", + "exit_code": 0 + }, + { + "name": "add-oauth", + "exit_code": 0 + } + ], + "netkingdom_option_visible": true, + "local_signin_form_preserved": true, + "authorization_start": { + "http_status": 307, + "issuer_host": "kc.coulomb.social", + "path": "/authorize", + "client_id": [ + "forgejo" + ], + "callback": [ + "http://127.0.0.1:18092/user/oauth2/netkingdom/callback" + ], + "scope": [ + "openid profile email groups" + ], + "pkce_method": [ + "S256" + ], + "pkce_challenge_present": true, + "state_present": true + }, + "passed": true, + "local_container_stopped": true +} diff --git a/docs/security-close-down-checklist.md b/docs/security-close-down-checklist.md index 671d7a8..8c9afdf 100644 --- a/docs/security-close-down-checklist.md +++ b/docs/security-close-down-checklist.md @@ -32,3 +32,4 @@ Add a row whenever you loosen, shortcut or leave unarmed a control. Remove nothi | 23 | Traefik HTTPS upload read budget | Activated 2026-10-13 under the founder-approved publication fix: shared websecure readTimeout raised from 60s to bounded 600s. Traefik generation 2 Ready; five previously failing Forgejo OCI blobs completed in 85–183s, exact tested image digest published. Evidence: railiance-cluster RCLUSTER-WP-0016 and rapp-playwright/docs/evidence/2026-10-13-publication.md. TLS and registry authentication stay enforced. | At production close-down review the 600s body-read budget and slow-upload exposure; retain a measured artifact-upload budget or isolate the registry entry point. Do not disable timeouts. | railiance-cluster / railiance-forge | 2026-10-13 | | 24 | Metered spend settlement (rein-aharness spend envelopes) | Opt-in per envelope since 2026-10-14 (rein-aharness `c7dceee`, `c04d1c6`; owner decision `a93619a9`): `settlement: observed-usage` charges a completed provider request its gateway-metered cost (provider-reported tokens at admitted rates) instead of its full hold, and a run max(metered requests, reported/attested cost). Unfinished requests keep full hold; overrun still freezes the envelope; `full-hold` stays the default. First use: memo `infd-20261014-b01` (3 attempts, EUR 10). Provider-reported usage is trusted until billing is compared. | Compare each observed-usage envelope's charges with provider billing before renewal; decide whether observed-usage stays allowed or envelopes return to full-hold. | rein-aharness, owner | 2026-10-14 | | 25 | WP-80 functional delivery before full security acceptance | Founder decision `9663d4ca-3793-4ad7-9dc1-954001d43596` prioritizes working development flows and permits later hardening. Actual Playwright human/agent commands pass through the existing authenticated workstation port-forward; this is a supervised build profile, without independent per-caller identity acceptance. No credentials distributed, new target enabled or installed control disabled. Nineteen full checkpoints and six remaining acceptance scopes transfer to live CUST-WP-0082; ledger statuses and the full target stay unchanged. [Scope and evidence](changes/netkingdom-target-2026-10-18/wp80-build-mode-r1/README.md). | Promote/revalidate relevant hardening before customer production, target expansion or broader unattended authority. Verify the full current-identity, per-caller transport/isolation, lifecycle, recovery and audit criteria in CUST-WP-0082; close all 16 target gates and reconcile all 116 ledger entries. No automatic production approval or blanket formal requirement exception. | Custodian coordinates; existing KeyCape / Platform / NetKingdom / Forge / Apps / custody and audit owners implement | 2026-10-18 | +| 26 | Proposed temporary Forgejo public-PKCE build profile | Not activated. Proposal `CUST80-FORGEJO-PUBLIC-BUILD-20261018` r2, SHA `fcea2cd32b8ee2634d2ebeb9ef4b659b6b85f8a4878413ed96e5d06cb09892c3`, awaits actual founder decision `ad88edeb-7c7c-486f-a695-1a7abae5b20c` and independent NetKingdom/Apps/KeyCape reviews. Candidate uses the unchanged Forgejo release, exact callback, S256 and required MFA, preserving local sign-in and existing account permissions. It intentionally defers the accepted confidential-client secret/delivery/full case lifecycle; no value is generated or distributed and no current control is disabled by preparation. [Packet](changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/README.md). | If activated, complete the confidential-client registration and issuer/consumer custody path plus original full security acceptance under CUST-WP-0082-T20 and existing owners when promoted; revalidate before customer production or broader unattended authority. Record actual approval/execution and preserve rollback/unknown outcomes. | Custodian coordinates; NetKingdom, railiance-apps, key-cape; Platform owns later confidential custody | 2026-10-18 | diff --git a/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md b/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md index f661ab7..fdb56ab 100644 --- a/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md +++ b/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md @@ -23,7 +23,7 @@ decision `9663d4ca-3793-4ad7-9dc1-954001d43596`. | Delivery milestone | Status | Observable result / remaining work | | --- | --- | --- | | 1. Playwright for a human and coding agent | **Delivered in the supervised build profile** | Both actual supported-command trials passed on railiance01, each with 14 steps, keyboard interaction and desktop/mobile screenshots. T25/T26. | -| 2. Everyday Forgejo development | **Next; not yet accepted** | Current 11.0.3 release identified; API and Git respond, but NetKingdom sign-in is absent. T41 prepares the existing-owner connection; T42 activates the reviewed change; T43/T44 verify human sign-in and agent Git/API use. T23 collects acceptance. | +| 2. Everyday Forgejo development | **Next; not yet accepted** | Current 11.0.3 release identified; API and Git respond, but NetKingdom sign-in is absent. T41 prepared and tested the candidate; T42 awaits the founder and independent owner reviews before activation; T43/T44 verify human sign-in and agent Git/API use. T23 collects acceptance. | | 3. Repeatable entry points and close-out | **In progress** | Supported commands, measured steps/manual touches, exact source/Hub readback and final functional audit. T33/T37/T38/T04. | | Earlier delivered engineering | **8 checkpoints** | Seven bounded source reviews T07-T13 plus the native scanner/publication repair T20. These retain their original scope. | | Security hardening | **Deferred, not fixed** | 19 complete checkpoints and 6 remaining acceptance scopes transferred to live CUST-WP-0082. All 16 full architecture gates remain unaccepted. | @@ -36,8 +36,9 @@ and registered-agent transport remains hardening work. No new service, gateway, identity evaluator or browser client was built. **Next:** T19 identified the current upstream Forgejo release and the concrete -missing NetKingdom sign-in option. T41 prepares the smallest existing-owner -connection for that release. T42-T44 then cover activation and actual use. +missing NetKingdom sign-in option. T41 prepared the smallest existing-owner +connection for that release. The exact public-PKCE build-profile candidate now awaits +its actual scoped go and independent reviews in T42. T42-T44 then cover activation and actual use. Build or integrate only a demonstrated functional blocker. Broader current identity, inverse, writer-exclusion and negative matrices are no longer blanket prerequisites for the good case. Keep at most two delivery checkpoints active. @@ -550,19 +551,23 @@ Transferred, not implemented: the original security acceptance and implementatio ```task id: CUST-WP-0080-T41 -status: progress +status: done priority: high depends_on: [CUST-WP-0080-T19] state_hub_task_id: "d6f1da6c-4e5f-5bb6-bbf2-ddddd69646e8" ``` +Prepared: [exact public-PKCE build-profile proposal](../docs/changes/netkingdom-target-2026-10-18/forgejo-good-case-preparation-r1/README.md), revision 2, digest fcea2cd32b8ee2634d2ebeb9ef4b659b6b85f8a4878413ed96e5d06cb09892c3. Real source compatibility, unchanged-image local CLI/page/PKCE, current settings, native config checker and server dry-run pass. The temporary public-client option needs actual founder approval; original confidential target and full security acceptance remain in CUST-WP-0082-T20. No native action or owner assent is claimed. + Implementation owners: Apps RAPPS-WP-0016-T02; KeyCape KEY-WP-0038-T02; existing NetKingdom registration/delivery path. Produce exact current issuer/client/callback and value-free custody references, then prepare the supported upstream 11.0.3 authentication-source configuration. Inspect current metadata and reuse existing registrations/delivery where present. Prove positive protocol compatibility before requiring a custom backend build. Exit: a concrete source diff and exact activation/verification/rollback packet, including actual prerequisites still missing. Do not resume the deferred per-operation client draft or implement a parallel issuer/registration service. ## T42 - Activate the reviewed Forgejo sign-in connection ```task id: CUST-WP-0080-T42 -status: todo +status: wait +needs_human: true +intervention_note: "Review CUST80-FORGEJO-PUBLIC-BUILD-20261018 r2; pending decision ad88edeb-7c7c-486f-a695-1a7abae5b20c; authorize exact temporary public-PKCE rollout and independent NetKingdom/Apps/KeyCape reviews." priority: high depends_on: [CUST-WP-0080-T41] state_hub_task_id: "bd135b74-05db-5f51-ac89-d1967fdcf36b" diff --git a/workplans/CUST-WP-0082-netkingdom-security-hardening.md b/workplans/CUST-WP-0082-netkingdom-security-hardening.md index 8b340e8..03727f0 100644 --- a/workplans/CUST-WP-0082-netkingdom-security-hardening.md +++ b/workplans/CUST-WP-0082-netkingdom-security-hardening.md @@ -369,6 +369,8 @@ Stage: native acceptance. Owner implementation: NetKingdom / Mason / Warden / Ke Completion test: Admit exact case/type/provider/package revisions and private execution custody. Independently verify native structure, contained registration/delivery, sign-in, denied groups/actions, current ongoing API/Git/SSH access, revocation, exact inverse and rollback. Owner assent and actual deployed receipts are mandatory; a compiled patch or acknowledged case is insufficient. +The proposed WP-80 public-PKCE build profile does not change the confidential-client target. If separately approved/activated, replace it through the existing owner registration/custody path when this hardening scope is promoted; complete confidential issuer/consumer delivery and the original full case criteria. No activation, secret generation or credential cutover is authorized here. + ## T21 - Accept independent human and registered-agent Playwright identity ```task