Prepare verified Forgejo public-PKCE build profile revision 2 for scoped review
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Reject generated-only commits / generated-commit-guard (push) Successful in 18s

This commit is contained in:
codex 2026-10-18 19:28:36 +02:00
parent bab3dd1c9d
commit 17635cb0a7
42 changed files with 1632 additions and 5 deletions

View file

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

View file

@ -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)

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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": []
}
}

View file

@ -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"
}
]

View file

@ -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",
"<ACTUAL_RESOLVED_FOUNDER_DECISION_FOR_THIS_BUILD_PROFILE>"
]
},
{
"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 '<ID_PROVED_CREATED_BY_THIS_EXECUTION>'"
],
"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",
"<VERIFIED_CURRENT_REGISTRY_REVISION>",
"--case-acceptance",
"<SAME_ACTUAL_RESOLVED_FOUNDER_DECISION>"
]
},
{
"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."
}

View file

@ -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"
}

View file

@ -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

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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
}

View file

@ -0,0 +1 @@
fcea2cd32b8ee2634d2ebeb9ef4b659b6b85f8a4878413ed96e5d06cb09892c3 proposal.json

View file

@ -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
}

View file

@ -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"}

View file

@ -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
}

View file

@ -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"}

View file

@ -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
}

View file

@ -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}

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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") }
}

View file

@ -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

View file

@ -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))

View file

@ -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
}

View file

@ -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
}

View file

@ -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)

View file

@ -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
}

View file

@ -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 |