security: prepare and rehearse contained Forgejo HTTP acceptance
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 7s

This commit is contained in:
codex 2026-10-13 18:26:47 +02:00
parent a3b56d689f
commit c12c087b89
18 changed files with 956 additions and 0 deletions

View file

@ -656,6 +656,29 @@
"execution_note": "Exact revision 1 completed with process exit 0. All 143 targets verified; final read-only audit preserves 23 baseline profiles and confirms 166 of 167 current repositories admitted, zero unknown profiles, and no probe refs, vetoes or ownership markers. Repository 55 remains explicit compatibility work. No refinement or rescue; standing authorization remains unscored.",
"final_verification_ref": "docs/evidence/2026-10-13-netkingdom-security/forge-expansion-final.json",
"verified_at": "2026-10-13T15:25:26.589770+00:00"
},
{
"proposal_id": "FORGE6-ROUTES-001",
"disposition": "unscored",
"outcome": "not_executed",
"original_revision": 1,
"original_digest_sha256": "a253fce3a565a2af94133da019bdc434dc57b21a8c61b06b4c27e036cf5a01d3",
"original_proposal_ref": "docs/changes/CUST-WP-0080/forge-routes-proposal-pre-rehearsal.json",
"prepared_revision": 2,
"prepared_digest_sha256": "d12c12857e9ed5f4afa3c1c231fd142adad580a458cc175e74f0ea0e3118d792",
"proposal_ref": "docs/changes/CUST-WP-0080/forge-routes-proposal.json",
"authorization_ref": "canon/standards/build-mode-change-authorization_v0.1.md",
"reason": "Reversible build-mode acceptance preparation; live operation needs the founder identity act in the existing attended window. No exact-revision approval, completed login or autopilot grant inferred.",
"target": "One newly created private disposable repository; pinned deployed Forgejo and admitted hook. Existing repository content, hooks, accounts and policies remain untouched.",
"expected_result": "Authenticated native HTTPS clean acceptance and synthetic-secret refusal with detector evidence; API private creation inherits exact hook; clean/content-secret and clean/merge-message refusal with unchanged refs; quarantine commit absent; anonymous private access refused; owned test repository deleted and absent.",
"verification_ref": "docs/evidence/2026-10-13-netkingdom-security/forge-routes-preparation.json",
"rollback": "Automatic cleanup checks numeric repository ID, owner, private flag, name and ownership description before deleting only the newly created test repository. On interrupted known creation, rerun run_forge_routes.py with the same proposal and receipt plus --cleanup-only. Ambiguous POST before numeric ID receipt requires reconciliation, never replay or delete by name.",
"abort_owner": "railiance-forge; custodian-codex prepares and verifies receipts",
"cost_estimate_eur": 0,
"risk_estimate": "Bounded synthetic test writes to a unique private disposable repository. No real secret fixtures, account creation, access-policy change or external publication. Unexpected acceptance may retain an unbound synthetic private-key fixture until cleanup. API/network failure stops the trial; ambiguous creation and cleanup remain explicitly unverified.",
"promotion_evidence": false,
"refinement_or_rescue": true,
"prepared_at": "2026-10-13T16:26:09.780197+00:00"
}
]
}

View file

@ -95,3 +95,11 @@ After those prerequisites, NetKingdom supplies the registry/env session and the
attended human token through the already prepared runner, with the coding-agent
owner custody path and Platform refusal/capability/cleanup proof. The runner
passes 17 local checks; those do not prove native identity or cleanup.
## Added: FORGE-WP-0006-T02 authenticated route acceptance
The [prepared contained command](2026-10-13-forge-route-acceptance.md) combines
the existing Forgejo-reader OIDC/MFA login with the complete HTTP/API probe and
owned disposable-repository cleanup. The exact shared suite passes 27 isolated
checks and five boundary tests; live execution remains pending. No new policy
scope or token provisioning is requested.

View file

@ -0,0 +1,60 @@
# Forgejo authenticated route acceptance
Existing work: FORGE-WP-0006-T02, RMGR-WP-0021-T03 and CUST-WP-0080-T03.
This is one contained login plus the prepared operation, added to the existing
attended window. It requests no new credential, policy scope or publication.
Status: prepared. The exact shared suite passed 27 checks on the pinned isolated
image; five boundary tests pass and native read-only preflight passes under a
private HOME. Evidence: `docs/evidence/2026-10-13-netkingdom-security/forge-routes-preparation.json`.
The first failed rehearsal and correction remain recorded; no live API test has run.
Proposal: `docs/changes/CUST-WP-0080/forge-routes-proposal.json`, stable trial
FORGE6-ROUTES-001. Every runtime source file is bound by digest. A changed source
refuses before credential fetch. The current API route failed before child
handoff and OpenBao capability discovery denied access; repeating that fetch
without an attended login does not advance acceptance.
The command uses the existing `forgejo-admin-workload-kv-read` OIDC role. The
founder supplies their own browser/MFA interaction. Warden contains the OpenBao
session, injects the existing PAT only into the silent child, then revokes the
login session. No login-only session or token paste is needed. Warden's current
plan identifies `key-cape-oidc-login` as `founder_required`; the governing rule is
`canon/standards/build-mode-change-authorization_v0.1.md` (orange lane).
Run from the founder's workstation terminal after the rehearsal receipt is
verified:
```sh
BAO_ADDR=http://127.0.0.1:18200 VAULT_ADDR=http://127.0.0.1:18200 \
WARDEN_AGENT_ID=custodian-codex \
warden access key-cape-oidc-login --domain forgejo-admin-workload-kv-read --exec -- \
python3 /home/worsch/the-custodian/docs/changes/CUST-WP-0080/run_forge_routes.py \
--proposal /home/worsch/the-custodian/docs/changes/CUST-WP-0080/forge-routes-proposal.json \
--receipt /home/worsch/the-custodian/docs/evidence/2026-10-13-netkingdom-security/forge-routes-live.json
```
The child creates one uniquely named private repository, checks hook inheritance,
anonymous denial, real HTTPS clean/secret pushes, API clean/secret writes, and
clean/secret-bearing merge messages. Ref and quarantine readbacks distinguish
actual enforcement from a generic HTTP failure. The synthetic key has no account
or authorization anywhere. No workflows or external jobs are created. The child
deletes only its newly created repository after checking its numeric ID, owner,
private flag, name and ownership description. Existing repositories are untouched.
Estimated new infrastructure cost: EUR 0. Allow about five minutes after login;
the operation is serialized and each network call is bounded.
The abort owner is railiance-forge. On failure, inspect only the sanitized
`forge-routes-live.json` and `.transport.json` receipts. Never repeat the operation
because a terminal observation timed out. If a known created repository needs
cleanup, use the same contained command with `--cleanup-only` appended. It checks
the recorded numeric identity again. If creation returned ambiguously before an
ID was recorded, stop for identity reconciliation; cleanup never guesses by name.
Readback must show `result: verified`, all checks true, and deletion confirmed.
The containing login must also report session revocation. A successful child
receipt with unconfirmed revocation is not complete attended-session acceptance.
No raw child output, PAT, response body or log content belongs in the handoff.
This proves the specified deployed HTTP/API routes only. Browser/SSO journeys,
the external-submodule repository, import/mirror, historical/LFS/log/artifact
coverage, CI independence and private finding response remain required work.

View file

@ -0,0 +1,316 @@
#!/usr/bin/env python3
"""Contained HTTP/API acceptance on one owned private disposable repository.
Production entrypoint accepts API_TOKEN only from the protected child environment.
No token, finding body, raw response or child diagnostic is written to receipts.
The same Suite runs against the pinned isolated image before live admission.
"""
import argparse
import base64
import datetime
import hashlib
import importlib.util
import json
import os
from pathlib import Path
import re
import subprocess
import tempfile
import time
import urllib.error
import urllib.request
HERE = Path(__file__).parent
ORIGIN = 'https://forgejo.coulomb.social'
PROFILE = json.loads((HERE / 'receive-hook-proposal.json').read_text())
def detected(body):
# Only parse the control's fixed status; never preserve response text.
return re.search(rb'"status"\s*:\s*"blocked"', body) is not None
class Suite:
def __init__(self, backend, receipt, name):
if not re.fullmatch(r'custodian-routes-[a-f0-9]{24}', name):
raise ValueError('invalid-probe-name')
self.b, self.receipt, self.name = backend, receipt, name
self.path = '/api/v1/repos/' + backend.user + '/' + name
self.created_id = None
self.phase = 'preflight'
self.evidence = {'result': 'unverified', 'repository_name': name,
'observed_at': datetime.datetime.now(datetime.timezone.utc).isoformat(),
'checks': {}, 'scope': backend.scope}
def save(self):
self.evidence['phase'] = self.phase
self.evidence['executor_sha256'] = hashlib.sha256(Path(__file__).read_bytes()).hexdigest()
self.receipt.write_text(json.dumps(self.evidence, indent=2) + '\n')
def check(self, key, value):
self.evidence['checks'][key] = bool(value)
self.save()
if not value:
raise ValueError('acceptance-incomplete')
def api(self, method, suffix='', data=None, authenticated=True):
return self.b.api(method, self.path + suffix, data, authenticated)
def head(self):
code, body = self.api('GET', '/branches/main')
if code != 200:
raise ValueError('branch-read-refused')
oid = json.loads(body)['commit']['id']
if not re.fullmatch(r'[a-f0-9]{40}', oid):
raise ValueError('invalid-object-id')
return oid
def content(self, name, content, branch='main'):
return self.api('POST', '/contents/' + name,
{'content': base64.b64encode(content).decode(),
'message': 'Disposable route acceptance', 'branch': branch})
def run(self):
code, body = self.b.api('GET', '/api/v1/user')
self.check('expected_authenticated_caller', code == 200 and
json.loads(body).get('login') == self.b.user)
self.check('target_does_not_preexist', self.api('GET')[0] == 404)
self.phase = 'create'
self.evidence['creation_attempted'] = True
self.save() # An interrupted POST is ambiguous; never replay automatically.
code, body = self.b.api('POST', '/api/v1/user/repos',
{'name': self.name, 'private': True, 'auto_init': True,
'default_branch': 'main', 'description': self.name})
if code == 201:
data = json.loads(body)
if (type(data.get('id')) is int and data.get('name') == self.name and
data.get('private') is True and data.get('owner', {}).get('login') == self.b.user):
self.created_id = data['id']
self.evidence['created_repository_id'] = self.created_id
self.save()
self.check('owned_private_repository_created', self.created_id is not None)
self.check('anonymous_private_read_denied', self.api('GET', authenticated=False)[0] in (401, 403, 404))
self.b.profile(self.name)
self.check('new_repository_inherits_exact_hook', True)
self.phase = 'http-git'
remote = self.b.origin + '/' + self.b.user + '/' + self.name + '.git'
self.b.git('init', '-q', '-b', 'main')
self.b.git('config', 'core.hooksPath', '/dev/null')
self.b.git('config', 'user.name', 'Disposable route probe')
self.b.git('config', 'user.email', 'probe@example.test')
self.b.git('fetch', '-q', remote, 'main')
self.b.git('reset', '--hard', 'FETCH_HEAD')
self.b.put('clean.txt', b'Ordinary disposable source.\n')
self.b.git('add', 'clean.txt'); self.b.git('commit', '-qm', 'Clean HTTP proof')
r = self.b.git('push', remote, 'HEAD:refs/heads/main', allow_failure=True)
self.check('http_clean_push_accepted', r.returncode == 0 and
re.search(rb'"status"\s*:\s*"clean"', r.stdout + r.stderr) is not None)
clean_head = self.head()
self.check('http_clean_ref_matches', self.b.git('rev-parse', 'HEAD').stdout.decode().strip() == clean_head)
fixture = self.b.fixture()
self.b.put('synthetic.txt', fixture)
self.b.git('add', 'synthetic.txt'); self.b.git('commit', '-qm', 'Unbound synthetic detection fixture')
oid = self.b.git('rev-parse', 'HEAD').stdout.decode().strip()
r = self.b.git('push', remote, 'HEAD:refs/heads/refused', allow_failure=True)
self.check('http_secret_push_detected_and_refused', r.returncode != 0 and detected(r.stdout + r.stderr))
self.check('http_refused_ref_absent', self.api('GET', '/branches/refused')[0] == 404)
self.check('http_rejection_preserves_main', self.head() == clean_head)
self.b.absent_object(self.name, oid)
self.check('http_quarantine_object_absent', True)
self.phase = 'content-api'
self.check('api_clean_content_accepted', self.content('accepted.txt', b'Clean API source.\n')[0] == 201)
before = self.head()
code, body = self.content('refused.txt', fixture)
self.check('api_secret_content_refused', code == 500)
# API error bodies encode the hook message as JSON; decode privately.
message = json.loads(body).get('message', '').encode()
self.check('api_refusal_has_detector_evidence', detected(message))
self.check('api_refusal_preserves_main', self.head() == before)
self.check('api_refused_content_absent', self.api('GET', '/contents/refused.txt')[0] == 404)
self.phase = 'merge-api'
self.check('feature_branch_created', self.api('POST', '/branches',
{'new_branch_name': 'feature', 'old_branch_name': 'main'})[0] == 201)
self.check('feature_content_accepted', self.content('feature.txt', b'Clean feature.\n', 'feature')[0] == 201)
code, body = self.api('POST', '/pulls', {'head': 'feature', 'base': 'main', 'title': 'Disposable merge proof'})
self.check('pull_request_created', code == 201)
number = json.loads(body)['number']
if type(number) is not int:
raise ValueError('invalid-pull-number')
suffix = '/pulls/' + str(number)
for _ in range(40):
code, body = self.api('GET', suffix)
if code == 200 and json.loads(body).get('mergeable') is True:
break
time.sleep(.5)
else:
raise ValueError('pull-not-mergeable')
before = self.head()
code, body = self.api('POST', suffix + '/merge', {'Do': 'merge',
'MergeTitleField': 'Disposable merge', 'MergeMessageField': fixture.decode(),
'force_merge': False})
self.check('merge_secret_message_detected_and_refused', code == 409 and
detected(json.loads(body).get('message', '').encode()))
code, body = self.api('GET', suffix)
self.check('rejected_merge_not_applied', code == 200 and json.loads(body).get('merged') is False and self.head() == before)
self.check('clean_merge_accepted', self.api('POST', suffix + '/merge', {'Do': 'merge',
'MergeTitleField': 'Clean merge', 'MergeMessageField': 'Clean proof.'})[0] in (200, 201))
code, body = self.api('GET', suffix)
self.check('clean_merge_applied', code == 200 and json.loads(body).get('merged') is True and self.head() != before)
self.evidence['acceptance_passed'] = True
def close(self):
self.phase = 'cleanup'
if self.created_id is not None:
code, body = self.api('GET')
data = json.loads(body) if code == 200 else {}
self.check('cleanup_identity_matches', code == 200 and data.get('id') == self.created_id and
data.get('name') == self.name and data.get('private') is True and
data.get('description') == self.name and data.get('owner', {}).get('login') == self.b.user)
self.check('owned_disposable_repository_deleted', self.api('DELETE')[0] == 204)
self.check('deleted_repository_absent', self.api('GET')[0] == 404)
elif self.evidence.get('creation_attempted'):
self.evidence['creation_outcome_requires_reconciliation'] = True
if self.evidence.get('acceptance_passed') and self.evidence['checks'].get('deleted_repository_absent'):
self.evidence['result'] = 'verified'
self.save()
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, *args, **kwargs):
return None
class Production:
user = 'tegwick'
origin = ORIGIN
scope = ('Deployed HTTPS Git, repository creation, content API and merge API on one owned private '
'disposable repository. No browser/SSO, CI, import/mirror or finding-response conformance inferred.')
def __init__(self, token, directory):
if not token or '\n' in token or '\r' in token:
raise ValueError('injected-token-absent-or-invalid')
self.token, self.directory = token, directory
spec = importlib.util.spec_from_file_location('template', HERE / 'activate_forge_template.py')
self.native = importlib.util.module_from_spec(spec); spec.loader.exec_module(self.native)
self.pod = self.native.target_pod(self.native.deployment(), PROFILE)
self.native.check_artifacts(self.pod)
self.opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), NoRedirect())
def api(self, method, path, data=None, authenticated=True):
if not path.startswith('/api/v1/') or '://' in path:
raise ValueError('invalid-api-path')
headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
if authenticated:
headers['Authorization'] = 'token ' + self.token
request = urllib.request.Request(self.origin + path, method=method, headers=headers,
data=None if data is None else json.dumps(data).encode())
try:
response = self.opener.open(request, timeout=120)
except urllib.error.HTTPError as error:
response = error
with response:
body = response.read(1024 * 1024 + 1)
if len(body) > 1024 * 1024:
raise ValueError('response-too-large')
return response.code, body
def git(self, *args, allow_failure=False):
auth = base64.b64encode((self.user + ':' + self.token).encode()).decode()
settings = {'credential.helper': '', 'http.followRedirects': 'false', 'http.proxy': '',
'http.sslVerify': 'true', 'http.' + self.origin + '/.extraHeader': 'Authorization: Basic ' + auth}
env = {'PATH': '/usr/bin:/bin', 'HOME': str(self.directory), 'LANG': 'C',
'GIT_CONFIG_NOSYSTEM': '1', 'GIT_CONFIG_GLOBAL': '/dev/null', 'GIT_TERMINAL_PROMPT': '0',
'GIT_ALLOW_PROTOCOL': 'https', 'GIT_CONFIG_COUNT': str(len(settings))}
for i, (key, value) in enumerate(settings.items()):
env['GIT_CONFIG_KEY_' + str(i)] = key; env['GIT_CONFIG_VALUE_' + str(i)] = value
result = subprocess.run(['git', '-C', str(self.directory), *args], env=env, capture_output=True, timeout=120)
if result.returncode and not allow_failure:
raise ValueError('git-command-refused')
return result
def put(self, name, content):
(self.directory / name).write_bytes(content)
def fixture(self):
result = subprocess.run(['ssh-keygen', '-q', '-t', 'rsa', '-b', '2048', '-N', '',
'-f', str(self.directory / 'unbound-key')], capture_output=True, timeout=30)
if result.returncode:
raise ValueError('fixture-generation-failed')
return (self.directory / 'unbound-key').read_bytes()
def profile(self, name):
root = '/data/git/gitea-repositories/' + self.user + '/' + name + '.git'
hook = root + '/hooks/pre-receive.d/' + PROFILE['hook_name']
self.native.native.execute(self.pod, 'sh', '-c',
'test -f "$1" && test ! -L "$1" && test -x "$1"', 'sh', hook)
observed = self.native.native.execute(self.pod, 'sha256sum', hook).decode().split()[0]
mode = self.native.native.execute(self.pod, 'stat', '-c', '%a %u %g', hook).decode().strip()
if observed != PROFILE['hook_sha256'] or mode not in ('555 1000 1000', '755 1000 1000'):
raise ValueError('inherited-profile-mismatch')
def absent_object(self, name, oid):
if not re.fullmatch(r'[a-f0-9]{40}', oid):
raise ValueError('invalid-object-id')
root = '/data/git/gitea-repositories/' + self.user + '/' + name + '.git'
raw = self.native.native.execute(self.pod, 'git', '--git-dir=' + root, 'cat-file',
'--batch-check', payload=(oid + '\n').encode()).decode().strip()
if raw != oid + ' missing':
raise ValueError('quarantine-retained')
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--receipt', type=Path, required=True)
parser.add_argument('--name', required=True)
parser.add_argument('--proposal', type=Path, required=True)
parser.add_argument('--cleanup-only', action='store_true')
args = parser.parse_args()
# Remove injection before invoking SSH or other unrelated child tools.
token = os.environ.pop('API_TOKEN', '')
if args.receipt.exists() and not args.cleanup_only:
return 2
suite = None
try:
proposal_bytes = args.proposal.read_bytes()
proposal = json.loads(proposal_bytes)
if (proposal['repository_name'] != args.name or proposal['origin'] != ORIGIN or
proposal['user'] != Production.user):
raise ValueError('proposal-target-mismatch')
for name, digest in proposal['source_sha256'].items():
if Path(name).name != name or hashlib.sha256((HERE / name).read_bytes()).hexdigest() != digest:
raise ValueError('proposal-source-mismatch')
if 'accept_forge_routes.py' not in proposal['source_sha256']:
raise ValueError('proposal-executor-unbound')
with tempfile.TemporaryDirectory(prefix='forge-routes-') as directory:
backend = Production(token, Path(directory))
suite = Suite(backend, args.receipt, args.name)
if args.cleanup_only:
previous = json.loads(args.receipt.read_text())
if (previous.get('repository_name') != args.name or
previous.get('proposal_sha256') != hashlib.sha256(proposal_bytes).hexdigest() or
type(previous.get('created_repository_id')) is not int):
raise ValueError('cleanup-ownership-unverified')
suite.evidence = previous
suite.created_id = previous['created_repository_id']
suite.close()
return 0 if suite.evidence['checks'].get('deleted_repository_absent') else 1
suite.evidence['proposal_sha256'] = hashlib.sha256(proposal_bytes).hexdigest()
try:
suite.run()
except Exception:
suite.evidence['failure_phase'] = suite.phase
finally:
try:
suite.close()
except Exception:
suite.evidence['cleanup_unverified'] = True
suite.save()
return 0 if suite.evidence['result'] == 'verified' else 1
except Exception:
if not args.receipt.exists():
args.receipt.write_text(json.dumps({'result': 'unverified', 'failure_phase': 'preflight'}) + '\n')
return 1
if __name__ == '__main__':
raise SystemExit(main())

View file

@ -0,0 +1,28 @@
{
"proposal_id": "FORGE6-ROUTES-001",
"revision": 1,
"state": "prepared-awaiting-isolated-proof-and-attended-login",
"origin": "https://forgejo.coulomb.social",
"user": "tegwick",
"repository_name": "custodian-routes-f140c6b840da4bbb9de64a74",
"source_sha256": {
"accept_forge_routes.py": "f87e643f1d49de031c2bfc3f956280eca0c61d2126fb20fd04541aa6324ef565",
"run_forge_routes.py": "6258140d408e5fbed7373611cdcd65e64aee743a0b36ce4fe55b5dc94663120f",
"receive-hook-proposal.json": "9c8ea3b7fbe16a997359432af7bcf09f429881fd23cc567188c21129e80ccf8d",
"activate_forge_template.py": "ef2b38f655d5520d69dcd69cdb4997eaad4b50773f32c1d798d16331b204c789",
"activate_receive_hooks.py": "9b01ee383e16463981112226e19156b0de59af2d03dd71b9403e144e280a9fb3",
"verify_receive_native.py": "c9120122c6f6f0b7c41fe4fa975e5a2a340e776885d852b861b8c13b7664adcd"
},
"target": "One newly created private disposable repository; pinned deployed Forgejo and admitted hook. Existing repository content, hooks, accounts and policies remain untouched.",
"expected_result": "Authenticated native HTTPS clean acceptance and synthetic-secret refusal with detector evidence; API private creation inherits exact hook; clean/content-secret and clean/merge-message refusal with unchanged refs; quarantine commit absent; anonymous private access refused; owned test repository deleted and absent.",
"verification": "Exact shared Suite rehearsed against pinned isolated Forgejo; four boundary regression tests. Live receipt required; rehearsal is not deployed acceptance.",
"rollback": "Automatic cleanup checks numeric repository ID, owner, private flag, name and ownership description before deleting only the newly created test repository. On interrupted known creation, rerun run_forge_routes.py with the same proposal and receipt plus --cleanup-only. Ambiguous POST before numeric ID receipt requires reconciliation, never replay or delete by name.",
"abort_owner": "railiance-forge; custodian-codex prepares and verifies receipts",
"cost_estimate_eur": 0,
"cost_basis": "Existing service only; serialized small private probes, no added resources or spend commitments.",
"risk_estimate": "Bounded synthetic test writes to a unique private disposable repository. No real secret fixtures, account creation, access-policy change or external publication. Unexpected acceptance may retain an unbound synthetic private-key fixture until cleanup. API/network failure stops the trial; ambiguous creation and cleanup remain explicitly unverified.",
"authorization_ref": "canon/standards/build-mode-change-authorization_v0.1.md",
"lane": "orange: existing caller OIDC/MFA required; no access or secret-policy scope change",
"live_receipt": "docs/evidence/2026-10-13-netkingdom-security/forge-routes-live.json",
"remaining_scope": "Browser/SSO journey, external gitlink repository, CI, import/mirror, historical/LFS/log/artifact detection and finding response remain open."
}

View file

@ -0,0 +1,30 @@
{
"proposal_id": "FORGE6-ROUTES-001",
"revision": 2,
"state": "prepared-isolated-verified-awaiting-attended-login",
"origin": "https://forgejo.coulomb.social",
"user": "tegwick",
"repository_name": "custodian-routes-f140c6b840da4bbb9de64a74",
"source_sha256": {
"accept_forge_routes.py": "96608481ab62445d2688e20f48c76a35c3d7be26c37452be3c46202eec4e103c",
"run_forge_routes.py": "6258140d408e5fbed7373611cdcd65e64aee743a0b36ce4fe55b5dc94663120f",
"receive-hook-proposal.json": "9c8ea3b7fbe16a997359432af7bcf09f429881fd23cc567188c21129e80ccf8d",
"activate_forge_template.py": "ef2b38f655d5520d69dcd69cdb4997eaad4b50773f32c1d798d16331b204c789",
"activate_receive_hooks.py": "9b01ee383e16463981112226e19156b0de59af2d03dd71b9403e144e280a9fb3",
"verify_receive_native.py": "c9120122c6f6f0b7c41fe4fa975e5a2a340e776885d852b861b8c13b7664adcd"
},
"target": "One newly created private disposable repository; pinned deployed Forgejo and admitted hook. Existing repository content, hooks, accounts and policies remain untouched.",
"expected_result": "Authenticated native HTTPS clean acceptance and synthetic-secret refusal with detector evidence; API private creation inherits exact hook; clean/content-secret and clean/merge-message refusal with unchanged refs; quarantine commit absent; anonymous private access refused; owned test repository deleted and absent.",
"verification": "Exact shared Suite rehearsed against pinned isolated Forgejo; five boundary regression tests. Live receipt required; rehearsal is not deployed acceptance.",
"rollback": "Automatic cleanup checks numeric repository ID, owner, private flag, name and ownership description before deleting only the newly created test repository. On interrupted known creation, rerun run_forge_routes.py with the same proposal and receipt plus --cleanup-only. Ambiguous POST before numeric ID receipt requires reconciliation, never replay or delete by name.",
"abort_owner": "railiance-forge; custodian-codex prepares and verifies receipts",
"cost_estimate_eur": 0,
"cost_basis": "Existing service only; serialized small private probes, no added resources or spend commitments.",
"risk_estimate": "Bounded synthetic test writes to a unique private disposable repository. No real secret fixtures, account creation, access-policy change or external publication. Unexpected acceptance may retain an unbound synthetic private-key fixture until cleanup. API/network failure stops the trial; ambiguous creation and cleanup remain explicitly unverified.",
"authorization_ref": "canon/standards/build-mode-change-authorization_v0.1.md",
"lane": "orange: existing caller OIDC/MFA required; no access or secret-policy scope change",
"live_receipt": "docs/evidence/2026-10-13-netkingdom-security/forge-routes-live.json",
"remaining_scope": "Browser/SSO journey, external gitlink repository, CI, import/mirror, historical/LFS/log/artifact detection and finding response remain open.",
"original_revision_sha256": "a253fce3a565a2af94133da019bdc434dc57b21a8c61b06b4c27e036cf5a01d3",
"refinement": "Pre-execution rehearsal corrected inherited Git hook mode to accept exact hash/owner with 0555 or 0755; initial failed rehearsal and source-binding limitation are retained."
}

View file

@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""Silent attended child: protected PAT injection, then a value-free receipt."""
import argparse
import hashlib
import json
from pathlib import Path
import subprocess
import sys
HERE = Path(__file__).resolve().parent
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--proposal', type=Path, required=True)
parser.add_argument('--receipt', type=Path, required=True)
parser.add_argument('--cleanup-only', action='store_true')
args = parser.parse_args()
proposal_path, receipt = args.proposal.resolve(), args.receipt.resolve()
transport = receipt.with_suffix('.cleanup-transport.json' if args.cleanup_only else '.transport.json')
if transport.exists() or (receipt.exists() and not args.cleanup_only):
return 2
try:
proposal = json.loads(proposal_path.read_text())
for name, digest in proposal['source_sha256'].items():
if Path(name).name != name or hashlib.sha256((HERE / name).read_bytes()).hexdigest() != digest:
return 2
if not {'run_forge_routes.py', 'accept_forge_routes.py'} <= set(proposal['source_sha256']):
return 2
child = [sys.executable, str(HERE / 'accept_forge_routes.py'), '--proposal', str(proposal_path),
'--receipt', str(receipt), '--name', proposal['repository_name']]
if args.cleanup_only:
child.append('--cleanup-only')
# Capture/discard both streams, including warden proxy notices. The
# containing login lane requires silence and handles session revocation.
result = subprocess.run(['warden', 'access', 'forgejo-admin-api-token', '--field', 'API_TOKEN',
'--exec', '--', *child], capture_output=True)
transport.write_text(json.dumps({'route_exit_code': result.returncode,
'receipt_present': receipt.exists(), 'cleanup_only': args.cleanup_only,
'proposal_sha256': hashlib.sha256(proposal_path.read_bytes()).hexdigest(),
'raw_diagnostics_retained': False, 'credential_returned_to_agent': False}, indent=2) + '\n')
return result.returncode
except Exception:
transport.write_text(json.dumps({'result': 'handoff-unverified', 'raw_diagnostics_retained': False}) + '\n')
return 1
if __name__ == '__main__':
raise SystemExit(main())

View file

@ -0,0 +1,121 @@
#!/usr/bin/env python3
"""Run the exact shared route suite on the pinned disposable Forgejo image."""
import argparse
import base64
import hashlib
import importlib.util
import json
from pathlib import Path
import uuid
HERE = Path(__file__).parent
def load(name, filename):
spec = importlib.util.spec_from_file_location(name, HERE / filename)
module = importlib.util.module_from_spec(spec); spec.loader.exec_module(module)
return module
base = load('lifecycle', 'verify_forge_lifecycle.py')
routes = load('routes', 'accept_forge_routes.py')
LOADED_ROUTES_SHA256 = hashlib.sha256((HERE / 'accept_forge_routes.py').read_bytes()).hexdigest()
class FixtureReady(Exception):
pass
class SetupProbe(base.Probe):
def check(self, name, condition):
super().check(name, condition)
if name == 'native_repository_created':
raise FixtureReady()
class Backend:
user = 'probe'
origin = 'http://127.0.0.1:3000'
scope = 'Exact shared HTTP/API suite on pinned isolated Forgejo, SQLite, no external network; no live credential or deployed acceptance.'
def __init__(self, probe):
self.p = probe
self.directory = base.ROOT + '/routes-client'
probe.execute('mkdir', self.directory)
def api(self, method, path, data=None, authenticated=True):
return self.p.api(method, path, data, authenticated)
def git(self, *args, allow_failure=False):
auth = base64.b64encode(('probe:' + self.p.password).encode()).decode()
return self.p.execute('env', 'GIT_CONFIG_COUNT=1',
'GIT_CONFIG_KEY_0=http.http://127.0.0.1:3000/.extraHeader',
'GIT_CONFIG_VALUE_0=Authorization: Basic ' + auth,
'git', '-C', self.directory, *args,
allowed=(0, 1, 128) if allow_failure else (0,), timeout=120)
def put(self, name, content):
self.p.put(self.directory + '/' + name, content)
def fixture(self):
return self.p.execute('cat', base.ROOT + '/synthetic-key').stdout
def profile(self, name):
path = self.p.profile(name)
digest = self.p.execute('sha256sum', path).stdout.decode().split()[0]
mode = self.p.execute('stat', '-c', '%a %u %g', path).stdout.decode().strip()
self.p.evidence['inherited_hook_mode_owner'] = mode
if digest != routes.PROFILE['hook_sha256'] or mode not in ('555 1000 1000', '755 1000 1000'):
raise ValueError('inherited-profile-mismatch')
def absent_object(self, name, oid):
root = base.REPOS + '/' + name + '.git'
raw = self.p.execute('git', '--git-dir=' + root, 'cat-file', '--batch-check',
payload=(oid + '\n').encode()).stdout.decode().strip()
if raw != oid + ' missing':
raise ValueError('quarantine-retained')
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--artifact-dir', type=Path, required=True)
parser.add_argument('--receipt', type=Path, required=True)
args = parser.parse_args()
if args.receipt.exists():
raise SystemExit('Retain prior attempt; use a new receipt.')
probe = SetupProbe(args.receipt.with_suffix('.baseline.json'))
suite = None
try:
try:
probe.run_probe(args.artifact_dir)
except FixtureReady:
pass
probe.evidence['scope'] = 'Pinned-image fixture setup only; shared suite has its own acceptance receipt.'
template = base.ROOT + '/template'
probe.execute('mkdir', '-p', template + '/hooks/pre-receive.d')
probe.put(template + '/hooks/pre-receive.d/10-custodian-secret-scan',
(HERE / 'receive-profile-hook').read_bytes(), '555')
probe.execute('git', 'config', '--file', base.ROOT + '/git-home/.gitconfig', 'init.templateDir', template)
probe.execute('ssh-keygen', '-q', '-t', 'rsa', '-b', '2048', '-N', '', '-f', base.ROOT + '/synthetic-key')
probe.evidence['result'] = 'fixture-ready'
suite = routes.Suite(Backend(probe), args.receipt, 'custodian-routes-' + uuid.uuid4().hex[:24])
suite.evidence['loaded_executor_sha256'] = LOADED_ROUTES_SHA256
suite.run()
suite.check('executor_unchanged_during_rehearsal', LOADED_ROUTES_SHA256 ==
hashlib.sha256((HERE / 'accept_forge_routes.py').read_bytes()).hexdigest())
except Exception:
if suite:
suite.evidence['failure_phase'] = suite.phase
raise SystemExit('Shared route acceptance incomplete; raw diagnostics suppressed.')
finally:
try:
if suite:
suite.close()
finally:
probe.close()
print(json.dumps({'result': suite.evidence['result'], 'checks': len(suite.evidence['checks']),
'receipt': str(args.receipt)}))
if __name__ == '__main__':
main()

View file

@ -0,0 +1,111 @@
"""Boundary regressions for receipt-scoped cleanup and credential forwarding."""
import importlib.util
import contextlib
import hashlib
import io
import json
from pathlib import Path
import tempfile
import threading
import unittest
from unittest import mock
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
spec = importlib.util.spec_from_file_location('routes', Path(__file__).with_name('accept_forge_routes.py'))
m = importlib.util.module_from_spec(spec); spec.loader.exec_module(m)
class CleanupTests(unittest.TestCase):
def test_replaced_or_changed_repository_is_not_deleted(self):
for change in ({'id': 18}, {'private': False}, {'description': 'changed'},
{'owner': {'login': 'someone-else'}}):
with self.subTest(change=change), tempfile.TemporaryDirectory() as directory:
name = 'custodian-routes-' + 'a' * 24
record = dict(id=17, name=name, private=True, description=name, owner={'login': 'tegwick'})
record.update(change)
class Backend:
user = 'tegwick'
scope = 'test'
def api(self, method, *args):
self_outer.assertEqual(method, 'GET', 'must not delete changed ownership')
return 200, json.dumps(record).encode()
self_outer = self
suite = m.Suite(Backend(), Path(directory) / 'receipt.json', name)
suite.created_id = 17
with self.assertRaises(ValueError):
suite.close()
def test_ambiguous_creation_never_deletes_by_name(self):
class Backend:
user = 'tegwick'
scope = 'test'
def api(self, *args):
raise AssertionError('unowned creation must not issue API calls')
with tempfile.TemporaryDirectory() as directory:
suite = m.Suite(Backend(), Path(directory) / 'receipt.json', 'custodian-routes-' + 'a' * 24)
suite.evidence['creation_attempted'] = True
suite.close()
self.assertTrue(suite.evidence['creation_outcome_requires_reconciliation'])
self.assertEqual(suite.evidence['result'], 'unverified')
def test_generic_error_is_not_detection_evidence(self):
self.assertFalse(m.detected(b'{"message":"internal error"}'))
self.assertFalse(m.detected(b'{"status":"unverified"}'))
class TransportTests(unittest.TestCase):
def test_http_redirect_does_not_forward_injected_credential(self):
hits = []
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
hits.append(self.path)
self.send_response(302)
self.send_header('Location', '/receiver')
self.end_headers()
def log_message(self, *args):
pass
server = ThreadingHTTPServer(('127.0.0.1', 0), Handler)
worker = threading.Thread(target=server.serve_forever, daemon=True); worker.start()
try:
backend = object.__new__(m.Production)
backend.origin = 'http://127.0.0.1:' + str(server.server_port)
backend.token = 'unbound-test-value'
backend.opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), m.NoRedirect())
code, _ = backend.api('GET', '/api/v1/user')
self.assertEqual(code, 302)
self.assertEqual(hits, ['/api/v1/user'])
finally:
server.shutdown(); server.server_close(); worker.join()
def test_handoff_is_silent_and_rejects_changed_source_before_fetch(self):
spec = importlib.util.spec_from_file_location('handoff', Path(__file__).with_name('run_forge_routes.py'))
wrapper = importlib.util.module_from_spec(spec); spec.loader.exec_module(wrapper)
for changed in (False, True):
with self.subTest(changed=changed), tempfile.TemporaryDirectory() as directory:
root = Path(directory)
proposal = {'repository_name': 'custodian-routes-' + 'a' * 24, 'source_sha256': {
n: hashlib.sha256((wrapper.HERE / n).read_bytes()).hexdigest()
for n in ('run_forge_routes.py', 'accept_forge_routes.py')}}
if changed:
proposal['source_sha256']['accept_forge_routes.py'] = '0' * 64
(root / 'proposal.json').write_text(json.dumps(proposal))
out, err = io.StringIO(), io.StringIO()
with mock.patch('sys.argv', ['handoff', '--proposal', str(root / 'proposal.json'),
'--receipt', str(root / 'result.json')]), mock.patch.object(
wrapper.subprocess, 'run', return_value=mock.Mock(returncode=5,
stdout=b'UNBOUND-CREDENTIAL-MARKER', stderr=b'UNBOUND-CREDENTIAL-MARKER')) as child:
with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
result = wrapper.main()
self.assertEqual(out.getvalue() + err.getvalue(), '')
if changed:
child.assert_not_called()
self.assertEqual(result, 2)
else:
self.assertEqual(result, 5)
self.assertTrue(child.call_args.kwargs['capture_output'])
self.assertNotIn('UNBOUND-CREDENTIAL-MARKER', (root / 'result.transport.json').read_text())
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,21 @@
{
"result": "verified",
"repositories": [
{
"repo": "the-custodian",
"head": "a3b56d689fd8ad0c702a027ab56b773546c8ee1c",
"remote_matches": true
},
{
"repo": "railiance-forge",
"head": "bb513010af327bc6b80d02ee4b9d25fb49a2ad36",
"remote_matches": true
},
{
"repo": "repo-manager",
"head": "7677b00f69cd6362ac16a1021dc180ec6df4ebfa",
"remote_matches": true
}
],
"scope": "Ordinary source push acceptance after scanner expansion; exact remote heads verified. Custodian and Forge consistency: zero automation/assessment failures; Repo Manager projection applied at exact commit."
}

View file

@ -0,0 +1,42 @@
{
"observed_at": "2026-10-13T16:13:18.104345+00:00",
"image": "code.forgejo.org/forgejo/forgejo@sha256:691657c7e64af7730a2e885e6d022cf16ae60724931c5392bd61838afadcaeba",
"result": "verified",
"checks": {
"synthetic_identity_authenticated": true,
"native_repository_created": true,
"new_repository_missing_profile_without_template": true,
"http_push_clean": true,
"http_push_blocked": true,
"http_quarantine_discarded": true,
"api_accepted_http_status": 201,
"api_accepted": true,
"api_refused_http_status": 500,
"api_refused": true,
"api_refusal_has_scanner_detection": true,
"rejected_api_content_absent": true,
"feature_branch_created": true,
"feature_content_created": true,
"pull_request_created": true,
"merge_refusal_status": 409,
"merge_message_secret_refused": true,
"merge_refusal_has_scanner_detection": true,
"rejected_merge_not_applied": true,
"clean_merge_accepted": true,
"regeneration_preserves_exact_profile": true,
"native_repository_with_template_created": true,
"new_repository_inherits_exact_profile": true,
"restart_preserves_coverage_profile": true,
"restart_preserves_templated_profile": true,
"inherited_profile_blocks_after_restart": true
},
"scope": "Disposable local exact-image instance, SQLite, network none, no host mounts. Real Forgejo HTTP and repository services; no production credentials or data. Not deployed PostgreSQL, SSO, ingress or production lifecycle acceptance.",
"identity_http_status": 200,
"repository_create_http_status": 201,
"repository_create_error_kinds": [
"email"
],
"isolated_container_and_volumes_removed": true,
"last_phase": "restart",
"executor_sha256": "88bbf4235086b7ffc40ca4d7df7fafa1d67e2e1b41d2f0055ec82421ed4840cb"
}

View file

@ -0,0 +1,20 @@
{
"result": "unverified",
"repository_name": "custodian-routes-15ec9d1fd7404c37a50f5839",
"observed_at": "2026-10-13T16:17:58.202566+00:00",
"checks": {
"expected_authenticated_caller": true,
"target_does_not_preexist": true,
"owned_private_repository_created": true,
"anonymous_private_read_denied": true,
"cleanup_identity_matches": true,
"owned_disposable_repository_deleted": true,
"deleted_repository_absent": true
},
"scope": "Exact shared HTTP/API suite on pinned isolated Forgejo, SQLite, no external network; no live credential or deployed acceptance.",
"phase": "cleanup",
"executor_sha256": "f87e643f1d49de031c2bfc3f956280eca0c61d2126fb20fd04541aa6324ef565",
"creation_attempted": true,
"created_repository_id": 3,
"failure_phase": "create"
}

View file

@ -0,0 +1,24 @@
{
"observed_at": "2026-10-13T16:20:34.506024+00:00",
"image": "code.forgejo.org/forgejo/forgejo@sha256:691657c7e64af7730a2e885e6d022cf16ae60724931c5392bd61838afadcaeba",
"result": "fixture-ready",
"checks": {
"synthetic_identity_authenticated": true,
"native_repository_created": true
},
"scope": "Pinned-image fixture setup only; shared suite has its own acceptance receipt.",
"identity_http_status": 200,
"repository_create_http_status": 201,
"repository_create_error_kinds": [
"email"
],
"inherited_hook_mode_owner": "755 1000 1000",
"failure_diagnostics": [
"PushRejected",
"app.ini",
"exit status 1"
],
"isolated_container_and_volumes_removed": true,
"last_phase": "baseline",
"executor_sha256": "88bbf4235086b7ffc40ca4d7df7fafa1d67e2e1b41d2f0055ec82421ed4840cb"
}

View file

@ -0,0 +1,41 @@
{
"result": "verified",
"repository_name": "custodian-routes-8e57114f10464ce7b4bdc8c1",
"observed_at": "2026-10-13T16:21:13.925168+00:00",
"checks": {
"expected_authenticated_caller": true,
"target_does_not_preexist": true,
"owned_private_repository_created": true,
"anonymous_private_read_denied": true,
"new_repository_inherits_exact_hook": true,
"http_clean_push_accepted": true,
"http_clean_ref_matches": true,
"http_secret_push_detected_and_refused": true,
"http_refused_ref_absent": true,
"http_rejection_preserves_main": true,
"http_quarantine_object_absent": true,
"api_clean_content_accepted": true,
"api_secret_content_refused": true,
"api_refusal_has_detector_evidence": true,
"api_refusal_preserves_main": true,
"api_refused_content_absent": true,
"feature_branch_created": true,
"feature_content_accepted": true,
"pull_request_created": true,
"merge_secret_message_detected_and_refused": true,
"rejected_merge_not_applied": true,
"clean_merge_accepted": true,
"clean_merge_applied": true,
"executor_unchanged_during_rehearsal": true,
"cleanup_identity_matches": true,
"owned_disposable_repository_deleted": true,
"deleted_repository_absent": true
},
"scope": "Exact shared HTTP/API suite on pinned isolated Forgejo, SQLite, no external network; no live credential or deployed acceptance.",
"loaded_executor_sha256": "96608481ab62445d2688e20f48c76a35c3d7be26c37452be3c46202eec4e103c",
"phase": "cleanup",
"executor_sha256": "96608481ab62445d2688e20f48c76a35c3d7be26c37452be3c46202eec4e103c",
"creation_attempted": true,
"created_repository_id": 2,
"acceptance_passed": true
}

View file

@ -0,0 +1,30 @@
{
"proposal_id": "FORGE6-ROUTES-001",
"proposal_revision": 2,
"proposal_sha256": "d12c12857e9ed5f4afa3c1c231fd142adad580a458cc175e74f0ea0e3118d792",
"result": "prepared-and-rehearsed",
"live_execution": "not-executed",
"isolated_receipt": "forge-routes-isolated-2.json",
"isolated_boolean_checks": 27,
"loaded_executor_sha256": "96608481ab62445d2688e20f48c76a35c3d7be26c37452be3c46202eec4e103c",
"boundary_tests": {
"command": "python3 docs/changes/CUST-WP-0080/test_forge_routes_safety.py",
"passed": 5,
"covers": [
"refuse replaced/changed ownership",
"ambiguous-create cannot delete by name",
"generic HTTP error is not detection proof",
"redirect does not forward injected credential",
"silent handoff and changed-source refusal before fetch"
]
},
"read_only_native_preflight": "forge-routes-private-home-preflight.json",
"prior_attempt": "forge-routes-rehearsal-refinement.json",
"login_plan": {
"verdict": "founder_required",
"lane": "key-cape-oidc-login",
"role": "forgejo-admin-workload-kv-read"
},
"attended_packet": "docs/attended-window/2026-10-13-forge-route-acceptance.md",
"remaining": "Live authenticated route proof and containing session revocation are required. No browser/SSO, CI, external submodule, import/mirror or response closure."
}

View file

@ -0,0 +1,8 @@
{
"result": "verified",
"scope": "Read-only pinned pod/artifact preflight under a private HOME, before any login or API request. Synthetic placeholder was not sent anywhere.",
"pod_uid": "d4728052-b8df-4574-8a3e-74490a04472d",
"image_id": "code.forgejo.org/forgejo/forgejo@sha256:691657c7e64af7730a2e885e6d022cf16ae60724931c5392bd61838afadcaeba",
"no_api_request": true,
"no_credential_fetch": true
}

View file

@ -0,0 +1,9 @@
{
"trial": "FORGE6-ROUTES-001",
"attempt": 1,
"result": "unverified",
"failure": "Inherited-hook verifier incorrectly required 0555; Git copies executable template files as 0755. Read-only observation of a live inherited hook confirmed 0755 uid/gid1000. Corrected to admit 0555 or 0755 with exact content hash and owner; no group/other write permission admitted.",
"cleanup": "Owned disposable repository deleted; isolated container and volumes removed.",
"source_binding_limit": "Executor was edited after this rehearsal loaded it; its end-of-run current-file digest does not attest the loaded revision. This attempt does not prove the final source. Attempt 2 binds the loaded revision and refuses edits during rehearsal.",
"production_mutations": false
}

View file

@ -309,6 +309,21 @@ Evidence: Custodian `docs/evidence/2026-10-13-netkingdom-security/forge-expansio
and `forge-api-route-readiness.json`.
2026-10-13 authenticated-route preparation: FORGE6-ROUTES-001 revision 2
binds a silent contained operation to one newly created private disposable
repository. The exact shared suite passes 27 isolated pinned-image checks:
creation/inherited hook, anonymous denial, HTTPS clean/secret pushes, API content
acceptance/refusal, merge-message refusal/clean merge, unchanged refs, quarantine
and owned cleanup. Five boundary tests cover changed ownership, ambiguous-create
cleanup refusal, generic-error non-acceptance, redirect credential protection,
and silent digest-bound credential handoff. Read-only live preflight passes
under a private HOME. The failed first rehearsal and source-binding limitation
remain recorded. No production API mutation or credential fetch was performed.
The existing attended-window command is ready for caller OIDC/MFA; live route
and session-revocation receipts remain required. Packet: Custodian
`docs/attended-window/2026-10-13-forge-route-acceptance.md`; evidence:
`docs/evidence/2026-10-13-netkingdom-security/forge-routes-preparation.json`.
## Verify native closure against every requirement
```task