Advance security target with checker custody and recovery lifecycle proof

This commit is contained in:
codex 2026-10-16 23:37:06 +02:00
parent 3607d3e08f
commit a3e98b1f3e
17 changed files with 1211 additions and 2 deletions

View file

@ -0,0 +1,85 @@
# Browser registration executable snapshot candidate — R1
Source candidate only, 2026-10-16. No canonical repository edits, production
access, credentials, messages, owner assent or type acknowledgment occurred.
Base: NetKingdom `cbabb6dc3611dc9b9a304b175b17e132ae057bac` (documentation
successor of implementation `916ba4b33e03179274d03b78bd898a4843e8653f`).
`manifest.json` records every preimage/postimage SHA-256, exact checker digest,
Warden revision, test commands/environment and descriptor composition digest.
Candidate checkout: `/tmp/netkingdom-browser-resume-r1`.
## Change and evidence
The existing registration adapter hashes a mutable file before the helper later
executes its pathname. Replacing that file in between can select different code.
The retained predecessor counterexample exercises the actual Go checker helper
boundary: replacement after selection makes the unchanged predecessor execute a
decoy and fail. It is not a successful native case or an observed production
exploit. Historical R1/R2/R3 and environment correction evidence remain untouched.
The candidate opens the selected file with O_NOFOLLOW, O_NONBLOCK and CLOEXEC,
refuses non-regular or over-64-MiB input, verifies the original descriptor digest,
and snapshots exact bytes into a Linux memfd. Write/grow/shrink/seal-removal
seals are installed and read back before helper use. Both helper checker calls
inherit only that descriptor and execute `/proc/self/fd/N`, with the existing
empty environment and timeout. The fd closes before evidence publication and
also on exceptions. Snapshot failure still reaches existing parent-session
revocation and yields no success evidence. The redundant earlier unbounded path
read was removed following coordinator review; reachable adapter tests cover the
refusal paths rather than only testing the snapshot function.
No fexecve dependency is introduced. The runtime must support Linux memfd seals,
allow executable memfds and provide usable procfs; refusal remains unavailable
rather than falling back to mutable execution. The compiled static Linux/amd64
Go artifact from the existing verified build executes successfully this way.
This is a per-attempt immutable executable snapshot. It does not establish native
owner artifact storage, independently admitted executable provenance, protection
against a compromised interpreter/kernel, dynamic-library custody or agent
isolation. Final-component symlinks are refused; parent-directory trust is not
established by O_NOFOLLOW. The descriptor binds content, not a location claim.
The existing descriptor structure, reference, fixed source filename closure,
owner callbacks and normal Warden producer pins remain unchanged. Changed adapter
and helper bytes change the source composition and descriptor digest. The
proposed successor descriptor in this packet requires separate source review and
ordinary owner/type admission; it is not written into an acknowledged type.
Verification: 49 affected source checks pass, including ten new snapshot/adapter
refusal checks; 12 actual-artifact helper/kernel scenarios pass. These sets
overlap intentionally. Tests include read-only fd write/truncate refusal,
reopened proc fd write/shared writable mmap refusal even after mode changes,
symlink/FIFO/oversized-source refusal before helper, unavailable memfd revocation,
actual path replacement and in-place overwrite, config allow/deny, source drift,
CAS/readback, kernel rollback and uncertainty. The first expanded mutation test
failed because mode 0500 already blocked O_RDWR; its retained log records that
fixture mistake. The corrected fixture restores mode before proving the seals.
## Remaining acceptance and next source steps
- T16: select actual owner evidence publisher/authenticator and exact attempt
parent-session revoker. Warden's Executor still ships no native adapters or
verifier. A JSON reference, a successful fixture or this packet cannot replace
its independent purpose/source/evidence verifier. Review complete descriptor
and artifact/source/config pins and select an explicit type version normally.
- T12: Deployment env binding, exact config readback, restart/readiness remain
source integration work; a successful registry edit does not restart KeyCape.
- T14: `identity-provisioner/provisioner.py:230` returns only groups ensured and
preexisting-group coverage. It omits newly created memberships. Its revoke at
line 252 removes listed current memberships and can delete any empty listed
group. Do not expose it as reversible case creation. Next implementation must
retain prestate and exact created group/membership identities before effects,
preserve preexisting/shared state and bind rollback to authenticated provenance.
- T15: the adapter retains only process-local authenticated creation records and
intentionally has no reconcile callback. Next source work needs the selected
owner evidence holder and authenticated original-attempt lookup, current native
revision check and restart/lost-receipt fixtures. Do not reconstruct rollback
authority from unsigned JSON or replay an uncertain add.
- Platform structural/mint/broker/delivery receipts, Apps login/group denial,
session/revocation proof, native rollback and separately authorized attended
recovery remain required. This candidate closes none of those gates.
The knowledge package reported 0.2.6, digest
`8d1d6a9d4d07fec7b619618de7dfe2254c1790f85f4198c7fe19db9751c02`, with guidance
withheld because source changed. Direct NetKingdom/KeyCape/Warden source and
instructions were reviewed instead; no offline guidance was treated as authority.

View file

@ -0,0 +1,112 @@
{
"source_commit": "8790dd18ae83c4651ef3d1d923312a118a5b436b",
"published_remote_main": "8790dd18ae83c4651ef3d1d923312a118a5b436b",
"canonical_source_hashes_match": true,
"normal_sync_exit": 0,
"normal_sync_summary": {
"fail": 0,
"automation_error": 0,
"warn": 27,
"info": 5
},
"tasks": [
{
"id": "NK-WP-0048-T01",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T02",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T03",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T04",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T05",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T06",
"status": "wait",
"matches": true
},
{
"id": "NK-WP-0048-T07",
"status": "wait",
"matches": true
},
{
"id": "NK-WP-0048-T08",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T09",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T10",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T11",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T12",
"status": "todo",
"matches": true
},
{
"id": "NK-WP-0048-T13",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T14",
"status": "todo",
"matches": true
},
{
"id": "NK-WP-0048-T15",
"status": "todo",
"matches": true
},
{
"id": "NK-WP-0048-T16",
"status": "todo",
"matches": true
},
{
"id": "NK-WP-0048-T17",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T18",
"status": "done",
"matches": true
},
{
"id": "NK-WP-0048-T19",
"status": "done",
"matches": true
}
],
"original_staged_and_working_brief_restored": true,
"subagent_limit": "Agent exhausted model usage after source sync completed; coordinator verified actual receipts and projection.",
"native_acceptance": false
}

View file

@ -0,0 +1,48 @@
{
"observed_at": "2026-10-16T21:18:15.001232+00:00",
"reviewer": "custodian-coordinator",
"scope": "source integration only; no native admission or custody",
"patch_sha256": "5ad1202976684a9fc8ced266a0148c0660e355301c9e6e6df25cdaff92e01337",
"independent_checks": {
"affected_source": {
"tests": 49,
"exit_code": 0
},
"real_checker_helper_kernel": {
"tests": 12,
"exit_code": 0
},
"canonical_apply_check": true
},
"canonical_applied": true,
"files": [
{
"path": "sso-mfa/k8s/keycape/keycape-registration-adapter.py",
"sha256": "d7e3643cc794223dfaa310775eacbbeb4d353f9382e4481322d704d6b7ab1c51"
},
{
"path": "sso-mfa/k8s/keycape/keycape-client.py",
"sha256": "4b63161cf3365d34dd684a3116e2c935baaf7f6aa4b30983f6883546db3d6591"
},
{
"path": "sso-mfa/k8s/keycape/registration-adapter.md",
"sha256": "2fadcbd28e34566173428bcb92e42ac464ef73c03909072d0fc2ee988b175e29"
},
{
"path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py",
"sha256": "cc5956d811db031e437ebca60bf70eb41177c87db75cc9478fe84075ad224464"
},
{
"path": "sso-mfa/k8s/keycape/test_keycape_checker_snapshot.py",
"sha256": "9271f25c760ad732c257d59d259b5c767436a2dc2d5ba24fca259bc57761d6d1"
}
],
"refinement": "Initial candidate kept unbounded read_bytes in binding; removed and reachable symlink/FIFO/oversize refusal regressions passed.",
"remaining": [
"native descriptor and artifact custody admission",
"owner evidence and session revocation callbacks",
"group provenance",
"durable restart reconciliation",
"native browser acceptance"
]
}

View file

@ -0,0 +1,86 @@
{
"observed_at": "2026-10-16T21:27:29.368669+00:00",
"engine": {
"id": "74f93783-6cef-43a8-a3c4-f62586cd3bc1",
"name": "bnt-lap001"
},
"manifest_sha256": "3f39a33891f25c36adcdc8137afac3be064a3011e15d9a06da60a3155bacea56",
"cases": [
{
"case": "positive",
"containers_absent": [
"nk49-kc-2d751a5178a143a582e9258fcf587370-transport",
"nk49-kc-2d751a5178a143a582e9258fcf587370-issuer",
"nk49-kc-2d751a5178a143a582e9258fcf587370-image-probe"
],
"volumes_absent": [
"nk49-kc-2d751a5178a143a582e9258fcf587370-v0",
"nk49-kc-2d751a5178a143a582e9258fcf587370-v1",
"nk49-kc-2d751a5178a143a582e9258fcf587370-v2",
"nk49-kc-2d751a5178a143a582e9258fcf587370-v3"
],
"plaintext_absent": true,
"label_inventories_empty": true
},
{
"case": "hard-parent-loss",
"containers_absent": [
"nk49-kc-279dd8bb0c83428a961da8833dbdbe07-transport",
"nk49-kc-279dd8bb0c83428a961da8833dbdbe07-issuer",
"nk49-kc-279dd8bb0c83428a961da8833dbdbe07-image-probe"
],
"volumes_absent": [
"nk49-kc-279dd8bb0c83428a961da8833dbdbe07-v0",
"nk49-kc-279dd8bb0c83428a961da8833dbdbe07-v1",
"nk49-kc-279dd8bb0c83428a961da8833dbdbe07-v2",
"nk49-kc-279dd8bb0c83428a961da8833dbdbe07-v3"
],
"plaintext_absent": true,
"label_inventories_empty": true,
"reaper_status": "bounded_cleanup_verified",
"reaper_sha256": "ff177a85a0f2fe21cf78cada1d453f5e312fcd5e8eb5992350267bfc3c3d9be0"
},
{
"case": "deadline",
"containers_absent": [
"nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-transport",
"nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-issuer",
"nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-image-probe"
],
"volumes_absent": [
"nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-v0",
"nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-v1",
"nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-v2",
"nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-v3"
],
"plaintext_absent": true,
"label_inventories_empty": true,
"reaper_status": "bounded_cleanup_verified",
"reaper_sha256": "00e65562e2dfacfd4aa70cb6f5f3d9f6b6259a5c7314e29ac91311513f4f21f1"
},
{
"case": "nonzero-after-accept",
"containers_absent": [
"nk49-kc-4e758a13c1424cdb9253e492fb650755-transport",
"nk49-kc-4e758a13c1424cdb9253e492fb650755-issuer",
"nk49-kc-4e758a13c1424cdb9253e492fb650755-image-probe"
],
"volumes_absent": [
"nk49-kc-4e758a13c1424cdb9253e492fb650755-v0",
"nk49-kc-4e758a13c1424cdb9253e492fb650755-v1",
"nk49-kc-4e758a13c1424cdb9253e492fb650755-v2",
"nk49-kc-4e758a13c1424cdb9253e492fb650755-v3"
],
"plaintext_absent": true,
"label_inventories_empty": true,
"reaper_status": "bounded_cleanup_uncertain",
"reaper_sha256": "2b04a7bb5a5bae9ce5bd13331a6125df4ac8f8f35e1a236903d8ffff04edc1af"
}
],
"mount_namespace": "mnt:[4026532210]",
"shm_device": 65,
"fixture_source_directories_absent": true,
"real_credentials_used": false,
"native_acceptance": false,
"retained_observation_failure": "Initial sandbox lookup could not find reaper receipts: sandbox /dev/shm device97 differs from trial host device65. Host-bound verification supersedes sandbox plaintext absence observations."
}

View file

@ -0,0 +1,13 @@
{
"scope": "publication encoding only",
"scanner_exit_code": 0,
"runtime_adapter_bytes_unchanged": true,
"source_digests_changed_only_for": [
"tests/run_isolated_keycape_native_fixture.py",
"tests/test_isolated_keycape_fixture_plan.py"
],
"preserved_original_refusal": "../r6-public-manifest-scanner-diagnostic.json",
"manifest_sha256": "975fc395e7df2e8ee942c2068aeb5b78f38023ce4a933380c03e7dec5ebad3e2",
"native_runtime_proof": "../actual-fixture-r6-all.json",
"strict_digest_guard_test_added": true
}

View file

@ -0,0 +1 @@
{"status": "bounded_cleanup_verified", "execution_quiescence_verified": true, "cleanup_trigger": "execution_deadline", "in_flight_daemon_request_uncertain": false, "container_cleanup_obligations": [{"container": "nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-image-probe", "status": "absence_verified"}, {"container": "nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-issuer", "status": "absence_verified"}, {"container": "nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-transport", "status": "absence_verified"}], "tmpfs_volume_cleanup_obligations": [{"volume": "nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-v0", "status": "absence_verified"}, {"volume": "nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-v1", "status": "absence_verified"}, {"volume": "nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-v2", "status": "absence_verified"}, {"volume": "nk49-kc-b4de71b2e3fd4a42a3c4ff4a592735c4-v3", "status": "absence_verified"}], "container_cleanup_verified": true, "plaintext_cleanup_obligation": {"path": "/dev/shm/nk49-keycape-b4de71b2e3fd4a42a3c4ff4a592735c4-xet3pr5k", "device": 65, "inode": 715, "status": "absence_verified"}, "temporary_plaintext_cleanup_verified": true}

View file

@ -0,0 +1 @@
{"status": "bounded_cleanup_verified", "execution_quiescence_verified": true, "cleanup_trigger": "parent_exit", "in_flight_daemon_request_uncertain": false, "container_cleanup_obligations": [{"container": "nk49-kc-279dd8bb0c83428a961da8833dbdbe07-image-probe", "status": "absence_verified"}, {"container": "nk49-kc-279dd8bb0c83428a961da8833dbdbe07-issuer", "status": "absence_verified"}, {"container": "nk49-kc-279dd8bb0c83428a961da8833dbdbe07-transport", "status": "absence_verified"}], "tmpfs_volume_cleanup_obligations": [{"volume": "nk49-kc-279dd8bb0c83428a961da8833dbdbe07-v0", "status": "absence_verified"}, {"volume": "nk49-kc-279dd8bb0c83428a961da8833dbdbe07-v1", "status": "absence_verified"}, {"volume": "nk49-kc-279dd8bb0c83428a961da8833dbdbe07-v2", "status": "absence_verified"}, {"volume": "nk49-kc-279dd8bb0c83428a961da8833dbdbe07-v3", "status": "absence_verified"}], "container_cleanup_verified": true, "plaintext_cleanup_obligation": {"path": "/dev/shm/nk49-keycape-279dd8bb0c83428a961da8833dbdbe07-fmc3l8gz", "device": 65, "inode": 631, "status": "absence_verified"}, "temporary_plaintext_cleanup_verified": true}

View file

@ -0,0 +1 @@
{"status": "bounded_cleanup_uncertain", "execution_quiescence_verified": true, "cleanup_trigger": "parent_exit", "in_flight_daemon_request_uncertain": true, "container_cleanup_obligations": [{"container": "nk49-kc-4e758a13c1424cdb9253e492fb650755-image-probe", "status": "absence_verified"}, {"container": "nk49-kc-4e758a13c1424cdb9253e492fb650755-issuer", "status": "absence_verified"}, {"container": "nk49-kc-4e758a13c1424cdb9253e492fb650755-transport", "status": "absence_verified"}], "tmpfs_volume_cleanup_obligations": [{"volume": "nk49-kc-4e758a13c1424cdb9253e492fb650755-v0", "status": "absence_verified"}, {"volume": "nk49-kc-4e758a13c1424cdb9253e492fb650755-v1", "status": "absence_verified"}, {"volume": "nk49-kc-4e758a13c1424cdb9253e492fb650755-v2", "status": "absence_verified"}, {"volume": "nk49-kc-4e758a13c1424cdb9253e492fb650755-v3", "status": "absence_verified"}], "container_cleanup_verified": true, "plaintext_cleanup_obligation": {"path": "/dev/shm/nk49-keycape-4e758a13c1424cdb9253e492fb650755-ha60s8tc", "device": 65, "inode": 799, "status": "absence_verified"}, "temporary_plaintext_cleanup_verified": true}

View file

@ -0,0 +1,45 @@
{
"exit_code": 20,
"findings": [
{
"RuleID": "generic-api-key",
"StartLine": 6,
"EndLine": 6
},
{
"RuleID": "generic-api-key",
"StartLine": 9,
"EndLine": 9
},
{
"RuleID": "generic-api-key",
"StartLine": 10,
"EndLine": 10
},
{
"RuleID": "generic-api-key",
"StartLine": 11,
"EndLine": 11
},
{
"RuleID": "generic-api-key",
"StartLine": 17,
"EndLine": 17
},
{
"RuleID": "generic-api-key",
"StartLine": 27,
"EndLine": 27
},
{
"RuleID": "generic-api-key",
"StartLine": 28,
"EndLine": 28
},
{
"RuleID": "generic-api-key",
"StartLine": 35,
"EndLine": 35
}
]
}

View file

@ -0,0 +1,84 @@
"""Read-only fixture resource checks; retain safe reaper outcomes on the host."""
from datetime import datetime, timezone
import hashlib
import json
import os
from pathlib import Path
import re
import subprocess
BASE = Path(__file__).resolve().parent
SOURCE = Path('/tmp/nk49-keycape-native-fixture-review-djek4ev1/result.json')
ENDPOINT = 'unix:///var/run/docker.sock'
def require(value):
if not value:
raise RuntimeError('bounded fixture verification refused')
def docker(args):
result = subprocess.run(['docker', '--host', ENDPOINT, *args],
capture_output=True, timeout=20)
require(result.returncode == 0)
return result.stdout.decode()
def main():
result = json.loads(SOURCE.read_bytes())
require(result['status'] == 'synthetic_native_fixture_passed')
require(result['manifest_sha256'] == '3f39a33891f25c36adcdc8137afac3be064a3011e15d9a06da60a3155bacea56')
engine = json.loads(docker(['info', '--format', '{"id":{{json .ID}},"name":{{json .Name}}}']))
require(engine == {'id': '74f93783-6cef-43a8-a3c4-f62586cd3bc1', 'name': 'bnt-lap001'})
rows = []
for case, receipt in result['receipts'].items():
require(case in ('positive', 'hard-parent-loss', 'deadline', 'nonzero-after-accept'))
row = {'case': case, 'containers_absent': [], 'volumes_absent': []}
for name in receipt['registered_cleanup']['containers']:
require(re.fullmatch('nk49-kc-[a-f0-9]{32}-[a-z-]+', name))
require(not docker(['container', 'ls', '--all', '--filter', 'name=^/'+name+'$', '--format', '{{.Names}}']).strip())
row['containers_absent'].append(name)
for name in receipt['registered_cleanup']['tmpfs_volumes']:
require(re.fullmatch('nk49-kc-[a-f0-9]{32}-v[0-3]', name))
require(not docker(['volume', 'ls', '--filter', 'name=^'+name+'$', '--format', '{{.Name}}']).strip())
row['volumes_absent'].append(name)
run_id = receipt['registered_cleanup']['containers'][0].split('-')[2]
for args in (
['container', 'ls', '--all', '--filter', 'label=net-kingdom.recovery-run='+run_id, '--format', '{{.Names}}'],
['volume', 'ls', '--filter', 'label=net-kingdom.recovery-run='+run_id, '--format', '{{.Name}}'],
):
require(not docker(args).strip())
work = Path(receipt['plaintext_cleanup_obligation']['path'])
# The sandbox has a different /dev/shm. Its absence is not host evidence.
require(work.parent == Path('/dev/shm'))
require(os.stat(work.parent).st_dev == receipt['plaintext_cleanup_obligation']['device'])
require(not work.exists() and not work.is_symlink())
row.update(plaintext_absent=True, label_inventories_empty=True)
if case != 'positive':
reaper = Path(receipt['reaper_outcome_path'])
require(reaper == Path(str(work)+'-reaper.json'))
data = reaper.read_bytes()
outcome = json.loads(data)
require(outcome['execution_quiescence_verified'])
require(outcome['temporary_plaintext_cleanup_verified'])
require(outcome['container_cleanup_verified'])
expected = 'bounded_cleanup_uncertain' if case == 'nonzero-after-accept' else 'bounded_cleanup_verified'
require(outcome['status'] == expected)
(BASE/('r6-'+case+'-reaper.json')).write_bytes(data)
row.update(reaper_status=outcome['status'], reaper_sha256=hashlib.sha256(data).hexdigest())
rows.append(row)
require(not list(Path('/dev/shm').glob('nk49-keycape-fixture-*')))
review = {'observed_at': datetime.now(timezone.utc).isoformat(), 'engine': engine,
'manifest_sha256': result['manifest_sha256'], 'cases': rows,
'mount_namespace': os.readlink('/proc/self/ns/mnt'),
'shm_device': os.stat('/dev/shm').st_dev,
'fixture_source_directories_absent': True,
'real_credentials_used': False, 'native_acceptance': False,
'retained_observation_failure': 'Initial sandbox lookup could not find reaper receipts: sandbox /dev/shm device97 differs from trial host device65. Host-bound verification supersedes sandbox plaintext absence observations.'}
(BASE/'actual-fixture-r6-all.json').write_bytes(SOURCE.read_bytes())
(BASE/'coordinator-r6-all-review.json').write_text(json.dumps(review, indent=2)+'\n')
print(json.dumps({'cases_verified': len(rows), 'host_cleanup_verified': True, 'reaper_outcomes_retained': 3}))
if __name__ == '__main__':
main()

View file

@ -0,0 +1,97 @@
# Independent resume audit, 16 October 2026
All TSA gates remain open. This audit reads the target contract, coordinator
record and actual owner work records/source. Historical receipts establish only
their recorded observation; this audit does not reassert present remote runtime.
Recovery and browser implementation belong to the other current assignments.
| Gate | Existing evidence and precise remaining acceptance | Next executable preparation or actual gate |
| --- | --- | --- |
| TSA-01 | Secrets WP14 T02/T03 retains strict fixed-verifier source, inert installed release and zero accepted protected effects at 11 sites/10 handlers. Access WP24 T06 retains signed producer/image/storage preparation. | Independently admit actual signer/public trust/caller and complete native negatives/rotation. Maintenance window, approved CCR and exact owner dispositions remain real gates; fixture keys cannot replace them. |
| TSA-02 | Platform successor whole-stack fixture uses real local Access/Approval, synthetic admission and disposable OpenBao. | Bind the actual CCR/catalog/plan/grant/journal and exact owner-mediated native effect. Approval issuer/tenant provenance and actual caller remain requirements. |
| TSA-03 | Journal consume/confirm seam, metadata readback and recovery source/fixture proofs exist. | Native durable restart/concurrency, uncertain issuance reconciliation and scoped revocation/cleanup need actual owner admission. No lost-response replay. |
| TSA-04 | Registration adapter, Warden pins, exact offline checker build and contained helper correction published. | Other assignment owns descriptor/callback/executable custody/restart/group work, then native positive/negative/revocation/rollback acceptance. |
| TSA-05 | Human and agent browser journeys remain open; archive proofs do not establish existing-token MFA/service continuation. | Other assignments own identity recovery and browser proof; real human factor/OIDC act remains attended. |
| TSA-06 | Canonical combined runtime manager/gateway proof denies synthetic host/environment access, scopes delivery and cleans cancellation/expiry. It explicitly reports zero model calls and no assignment migration. | Prepare exact admitted assignment/runner proof; actual model spend/provider and runtime authority must use existing scoped owner path. CI job credentials/workflow abuse remain separate. |
| TSA-07 | Existing lifecycle hardening source preserves KV custody on ordinary revoke; target inventory/consumer denominator is incomplete. | Reconcile public catalog, CCR and owner delivery metadata into current owner/purpose/scope/expiry/revocation/recovery/elimination rows. Mark unknown/orphan rows explicitly. No secret read or deferred rotation is needed for this preparation. |
| TSA-08 | Independent archive restore and continuity slices exist; isolated restore adapter's failed Docker positive is preserved. | Recovery assignment owns corrected disposable positive then loss/deadline trials. Actual independent owner-held root recovery, ordinary issuer unavailable, reconnect/audit and measured RTO/RPO remain required. |
| TSA-09 | Audit owner has native receiver capability history plus a precise first-pin admission/full-ledger reconciliation packet. Initial Access signer rollout keeps delivery disabled. | Prepare exact per-pin class/cadence/retention payload from owner source, independently retain complete ledger and every-ID/all-class accepted-time reconciliation inputs; custody/reader/source activation remains separately admitted. No selected-success-ID shortcut. |
| TSA-10 | Forge WP6 reports 166/167 admitted receive profiles, retained failed lifecycle trial and repaired mode guard. One gitlink repository, authenticated remote HTTP/UI, imports/mirrors, CI and response remain open. | Existing attended route command is prepared; credential-free source work can map clone/fetch paths and CI authority, and prepare RMGR WP21 T04 synthetic finding/private response. Actual OIDC/MFA gate belongs to native authenticated route execution. |
| TSA-11 | Retained hosted readback at 19:25 UTC shows 70 reviewed IDs exactly once, correct owners and corrected descriptions; strengthened validator passed 60 provider indexes. | Enumerate usable links/version aliases/package-family dispositions and obtain recurring drift evidence. One successful refresh does not prove a running loop or runtime capabilities. |
| TSA-12 | ACTIVITY WP47 accepted source schedule but definition remains disabled. This audit's actual daily host dry-run succeeds against real hub/checkouts; selected rein startup readiness check fails because profile is unverified. | Owner preparation must reconcile deterministic fixed command with model-backed pinned profile, admit runner filesystem/hub/checkpoint/queue scope, then Activity activation/manual trigger and first unassisted fire. This is owner execution preparation, not a new founder gate. See below. |
| TSA-13 | Producer Go fact adapter and 14,114 cross-language canonical comparisons exist; no two real accepted consumers. | Keep producer-format verifier code pinned; enumerate actual consumer compatibility/denied-path matrix and migration rollback. Real admitted consumers remain the acceptance boundary. |
| TSA-14 | Type/provider locks and installed package proofs exist; first accepted complete integration and second clean installation remain absent. | Prepare minimal required/optional provider lock and second clean-install journey without promoting plan-only materialization to acceptance. Native identities/effects remain governed. |
| TSA-15 | Prior bounded node snapshot reports 3580m/4000m requested CPU and 9760Mi memory, with dated headroom not reserved. Complete comparable optimization baseline is absent. | This audit measures the actual admitted workstation scanner against disposable generated workloads; retain p50/p95 and failed/unavailable refusals. Baseline is not improvement or production receive-path performance. Measure onboarding/touches/effect latency only on admitted consumers. |
| TSA-16 | Requirement ledger and close-down records exist; all 16 target gates remain open and original failed fixtures/publication refusals remain retained. | Reconcile exact evidence/owner task denominator and stable proposed revisions, without manufacturing assent, changing deferrals or bulk promoting residuals. |
## Actual no-spend scheduling prerequisite checks
`daily-host-readiness.json` records the existing script run **without** `--apply`,
using only this audit directory for state. It completed exit 0 in 0.809 seconds,
read the real local hub and workstation work records, generated a report and sent
zero messages. Report classifications were READY 25, UNBLOCKED 2, ATTENDED 12,
STALLED 16 and DEPENDENT 9. Raw notification stdout was hashed and discarded;
the generated local report remains a local preparation artifact. This confirms
host prerequisites only, not an admitted rein or actual checkpoint delivery.
`rein-profile-readiness.json` records the canonical rein environment's actual
`readiness._profile_checks` with
`AGENT_HARNESS_REQUIRED_PROFILE_REFS=harness.agent-dev-local@1.1.1`.
It returns `ok: false`, `readiness=unverified`. No worker token, provider key,
claim, model request or runtime owner configuration was read.
The exact current source explains the outstanding owner action:
1. `claim_loop.py` chooses `glas-profile` whenever `harness_profile_ref` exists,
before consulting legacy approach selection. A proposed `approach_hint` cannot
replace the pinned runtime semantics.
2. The selected profile binds Anthropic `claude-sonnet-5`, `claude-code-cli` and
USD 5 per-run maximum. Its ordinary execution starts the selected rein and
dispatches `run_task`; the fixed command in the task description does not
create a deterministic, zero-model executor.
3. Startup readiness requires `ready`; gateway `require_operational` rejects
`blocked` but deliberately allows `unverified` proof attempts. A direct proof
invocation therefore does not establish authoritative worker startup readiness.
4. The adopted fixed daily command resolves the script from the target checkout,
uses `Path.home()` for estate root and durable state by default, and expects
local hub reachability. Existing namespace/private-home proof cannot be
assumed to mount the entire estate or preserve delivery state. Define the
exact read-only roots, script/source pins, durable checkpoint root and scoped
hub writes; validate them inside the actual selected worker boundary.
The highest-value next owner preparation is a reviewed fixed-operation contract
for this existing daily command, either admitted by the current governed runtime
or via an explicitly supported deterministic runner. It must bind trusted script
bytes and arguments, estate roots, per-recipient state, lock, queue lease and hub
write scope, forbid repository mutation and model/provider access, and retain
failure/overlap/retry receipts. This is a concrete missing execution contract;
merely toggling the Activity definition or adding an arbitrary shell runner
would not satisfy it. No replacement scheduler is proposed. Activity retains
activation/ops_run ownership and an unassisted scheduled fire is still required.
## Scanner baseline
Run `python3 measure_scanner.py` from this directory or the repository root.
The script imports the real current Repo Manager adapter, checks the exact
installed scanner pin, stages distinct generated blobs in disposable Git roots,
and exercises clean, generated-private-key and unavailable/wrong-digest cases.
It captures only statuses/counts/timing/public hashes and removes the entire
fixture scope, including generated key material and Git objects. No user source,
index, branch, hook or scanner installation changes.
`scanner-baseline.json` is the actual measurement receipt when the bounded run
finishes. Seven warm repetitions plus the first call are descriptive samples;
nearest-rank sample p95 is not a production tail estimate. The benchmark preserves
the current profile and does not tune scanner policy or owner source.
Actual source explains the quantity to measure before proposing optimization:
`secret_scan.scan_staged` validates and hashes the complete 21,958,840-byte
scanner on every invocation. For each distinct changed blob it separately runs
`git cat-file -s`, `git cat-file blob` and one scanner subprocess with a fresh
default-rule initialization. Object deduplication already exists. Tiny-blob
workloads therefore scale with distinct object count, not only byte volume.
The receive adapter already batches Git object metadata but likewise invokes
the scanner separately per object; these local timing samples cannot establish
its remote wall time. A proposed persistent/batch scanner or cache would change
the current per-object attribution and executable-custody assumptions and needs
owner review plus unchanged refusal coverage; this audit changes neither.

View file

@ -0,0 +1,29 @@
{
"exit_code": 0,
"stdout_sha256": "9e85a9df19a574d4281da28d5b22cf3f374dd4d1455d97776ea333a3a17f615b",
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"stderr_bytes": 0,
"stdout_bytes": 25897,
"report_categories": {
"READY": 25,
"UNBLOCKED": 2,
"ATTENDED": 12,
"STALLED": 16,
"DEPENDENT": 9
},
"report_sha256": "a972948fa2947f213290f84fb2c89463736008c4499e6866c2c8d682b48b55be",
"observed_at": "2026-10-16T21:13:31.451341+00:00",
"wall_seconds": 0.809,
"command": [
"python3",
"scripts/security_flow_daily.py",
"--state-dir",
"docs/changes/netkingdom-target-2026-10-16/resume-audit-r1/daily-dry-run-state"
],
"source_sha256": "4a4e84702b3fb38d086695bb6d601430d8914bd79286b4f683794206f110c353",
"hub": "http://127.0.0.1:8000",
"apply": false,
"messages_sent": false,
"provider_calls": 0,
"scope": "Host filesystem and read-only hub/report preparation only; not claiming rein runtime or Activity owner acceptance."
}

View file

@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""Bounded synthetic baseline of the actual admitted workstation adapter.
Never reads a real repository index or credential. All fixture Git objects and
generated key material live in an automatically removed disposable directory.
Only fixed statuses, timings, counts and public source hashes are retained.
"""
import hashlib
import importlib.util
import json
import math
import os
from pathlib import Path
import resource
import statistics
import subprocess
import sys
import tempfile
import time
from datetime import datetime, timezone
sys.dont_write_bytecode = True
HERE = Path(__file__).resolve().parent
SOURCE = Path('/home/worsch/repo-manager/src/repo_manager/secret_scan.py')
BINARY = Path('/home/worsch/.local/lib/repo-manager/gitleaks/8.30.1/gitleaks')
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def command(argv, cwd, *, env=None):
subprocess.run(argv, cwd=cwd, env=env, check=True, timeout=20,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
def main():
source_before = sha(SOURCE)
binary_before = sha(BINARY)
spec = importlib.util.spec_from_file_location('admitted_scanner', SOURCE)
adapter = importlib.util.module_from_spec(spec)
spec.loader.exec_module(adapter)
if sha(BINARY) != adapter.BINARY_SHA256:
raise RuntimeError('public scanner pin mismatch')
observations = []
with tempfile.TemporaryDirectory(prefix='synthetic-scanner-', dir=HERE) as td:
root = Path(td)
env = {'PATH': '/usr/bin:/bin', 'HOME': td, 'LANG': 'C',
'GIT_CONFIG_NOSYSTEM': '1', 'GIT_CONFIG_GLOBAL': '/dev/null'}
for name, count, size, expected in [
('clean-1x1KiB', 1, 1024, 'clean'),
('clean-16x1KiB', 16, 1024, 'clean'),
('clean-64x1KiB', 64, 1024, 'clean'),
('clean-1x1MiB', 1, 1024*1024, 'clean'),
('synthetic-private-key', 1, None, 'blocked'),
]:
repo = root / name
repo.mkdir()
command(['git', '-c', 'core.hooksPath=/dev/null', 'init', '-q', str(repo)], root, env=env)
if size is None:
command(['openssl', 'genrsa', '-traditional', '-out', str(repo/'fixture.txt'), '2048'], repo, env=env)
byte_count = (repo/'fixture.txt').stat().st_size
else:
for i in range(count):
# Distinct contents avoid Git-object deduplication.
prefix = f'public synthetic baseline row {i:03d}\n'.encode()
(repo/f'fixture-{i}.txt').write_bytes(prefix + b'a'*(size-len(prefix)))
byte_count = count * size
command(['git', '-C', str(repo), 'add', '--all'], repo, env=env)
samples = []
# The first call is retained separately, not silently discarded.
for repeat in range(8):
cpu_before = resource.getrusage(resource.RUSAGE_CHILDREN)
start = time.perf_counter_ns()
result = adapter.scan_staged(repo, BINARY)
wall_ms = (time.perf_counter_ns()-start)/1e6
cpu_after = resource.getrusage(resource.RUSAGE_CHILDREN)
sample = {'repeat': repeat, 'wall_ms': round(wall_ms, 3),
'child_cpu_ms': round(1000*((cpu_after.ru_utime+cpu_after.ru_stime)-(cpu_before.ru_utime+cpu_before.ru_stime)), 3),
'status': result['status'], 'scanned_blobs': result['scanned_blobs']}
samples.append(sample)
if result['status'] != expected or result['scanned_blobs'] != count:
raise RuntimeError('synthetic baseline expectation failed')
ordered = sorted(item['wall_ms'] for item in samples[1:])
observations.append({'case': name, 'bytes': byte_count, 'unique_blobs': count,
'samples': samples, 'warm_samples': len(ordered),
'warm_p50_ms': statistics.median(ordered),
'warm_p95_nearest_rank_ms': ordered[math.ceil(.95*len(ordered))-1],
'warm_median_MiB_per_second': round(byte_count/(1024*1024)/(statistics.median(ordered)/1000), 3)})
negative = []
for label, binary in [('missing-scanner', root/'absent-scanner'), ('wrong-scanner-digest', Path('/usr/bin/true'))]:
start = time.perf_counter_ns()
result = adapter.scan_staged(repo, binary)
negative.append({'case': label, 'status': result['status'], 'reason': result.get('reason'),
'scanned_blobs': result['scanned_blobs'], 'wall_ms': round((time.perf_counter_ns()-start)/1e6, 3)})
if result['status'] != 'unverified' or adapter.exit_code(result) != 2:
raise RuntimeError('unavailable scanner did not refuse')
receipt = {'observed_at': datetime.now(timezone.utc).isoformat(),
'scope': 'Actual local repo-manager staged-object adapter; generated disposable fixtures only. Not receive-path, CI, native remote coverage, workload latency, performance improvement or TSA-15 closure.',
'benchmark_sha256': sha(Path(__file__)), 'adapter_path': str(SOURCE),
'adapter_sha256': sha(SOURCE), 'scanner_sha256': sha(BINARY),
'scanner_version': adapter.VERSION, 'platform': os.uname().sysname+' '+os.uname().machine,
'timer': 'perf_counter_ns; child CPU from RUSAGE_CHILDREN deltas',
'percentile_method': 'nearest rank, seven warm repetitions; descriptive sample, not production tail estimate',
'observations': observations, 'negative_cases': negative,
'fixture_removed': not root.exists(), 'provider_calls': 0, 'real_credentials_read': False,
'owner_source_unchanged': sha(SOURCE) == source_before,
'scanner_unchanged': sha(BINARY) == binary_before}
(HERE/'scanner-baseline.json').write_text(json.dumps(receipt, indent=2)+'\n')
print(json.dumps({'ok': True, 'cases': len(observations), 'negative_cases': len(negative), 'fixture_removed': not root.exists()}))
if __name__ == '__main__':
main()

View file

@ -0,0 +1,12 @@
{
"scope": "Actual canonical read-only startup profile check only; not model execution, worker queue or owner admission.",
"checks": [
{
"name": "profile:harness.agent-dev-local@1.1.1",
"ok": false,
"detail": "readiness=unverified rein=rein-aharness@0.1.0 sandbox=profile.bwrap-local"
}
],
"provider_calls": 0,
"credentials_read": false
}

View file

@ -0,0 +1,370 @@
{
"observed_at": "2026-10-16T21:22:55.040291+00:00",
"scope": "Actual local repo-manager staged-object adapter; generated disposable fixtures only. Not receive-path, CI, native remote coverage, workload latency, performance improvement or TSA-15 closure.",
"benchmark_sha256": "d6e4bd0976a3cf54de242414e33289a0e1820bd04d341da171eae64bf8968264",
"adapter_path": "/home/worsch/repo-manager/src/repo_manager/secret_scan.py",
"adapter_sha256": "4559d7714308eaa70ce5f8fa91dfcc6b10fe74b607e9f2a94269d31ff76bce9d",
"scanner_sha256": "88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509",
"scanner_version": "8.30.1",
"platform": "Linux x86_64",
"timer": "perf_counter_ns; child CPU from RUSAGE_CHILDREN deltas",
"percentile_method": "nearest rank, seven warm repetitions; descriptive sample, not production tail estimate",
"observations": [
{
"case": "clean-1x1KiB",
"bytes": 1024,
"unique_blobs": 1,
"samples": [
{
"repeat": 0,
"wall_ms": 703.927,
"child_cpu_ms": 776.155,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 1,
"wall_ms": 702.395,
"child_cpu_ms": 750.066,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 2,
"wall_ms": 702.358,
"child_cpu_ms": 740.743,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 3,
"wall_ms": 655.772,
"child_cpu_ms": 727.378,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 4,
"wall_ms": 702.463,
"child_cpu_ms": 747.63,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 5,
"wall_ms": 701.337,
"child_cpu_ms": 739.513,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 6,
"wall_ms": 701.727,
"child_cpu_ms": 755.673,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 7,
"wall_ms": 703.201,
"child_cpu_ms": 769.852,
"status": "clean",
"scanned_blobs": 1
}
],
"warm_samples": 7,
"warm_p50_ms": 702.358,
"warm_p95_nearest_rank_ms": 703.201,
"warm_median_MiB_per_second": 0.001
},
{
"case": "clean-16x1KiB",
"bytes": 16384,
"unique_blobs": 16,
"samples": [
{
"repeat": 0,
"wall_ms": 10853.446,
"child_cpu_ms": 12106.522,
"status": "clean",
"scanned_blobs": 16
},
{
"repeat": 1,
"wall_ms": 10644.5,
"child_cpu_ms": 11768.741,
"status": "clean",
"scanned_blobs": 16
},
{
"repeat": 2,
"wall_ms": 11361.227,
"child_cpu_ms": 12475.837,
"status": "clean",
"scanned_blobs": 16
},
{
"repeat": 3,
"wall_ms": 11559.776,
"child_cpu_ms": 12810.786,
"status": "clean",
"scanned_blobs": 16
},
{
"repeat": 4,
"wall_ms": 12222.433,
"child_cpu_ms": 13629.1,
"status": "clean",
"scanned_blobs": 16
},
{
"repeat": 5,
"wall_ms": 12065.334,
"child_cpu_ms": 13384.758,
"status": "clean",
"scanned_blobs": 16
},
{
"repeat": 6,
"wall_ms": 14989.135,
"child_cpu_ms": 16572.421,
"status": "clean",
"scanned_blobs": 16
},
{
"repeat": 7,
"wall_ms": 13941.789,
"child_cpu_ms": 15429.613,
"status": "clean",
"scanned_blobs": 16
}
],
"warm_samples": 7,
"warm_p50_ms": 12065.334,
"warm_p95_nearest_rank_ms": 14989.135,
"warm_median_MiB_per_second": 0.001
},
{
"case": "clean-64x1KiB",
"bytes": 65536,
"unique_blobs": 64,
"samples": [
{
"repeat": 0,
"wall_ms": 52485.495,
"child_cpu_ms": 57977.359,
"status": "clean",
"scanned_blobs": 64
},
{
"repeat": 1,
"wall_ms": 44423.85,
"child_cpu_ms": 49219.204,
"status": "clean",
"scanned_blobs": 64
},
{
"repeat": 2,
"wall_ms": 49171.849,
"child_cpu_ms": 54522.182,
"status": "clean",
"scanned_blobs": 64
},
{
"repeat": 3,
"wall_ms": 49415.077,
"child_cpu_ms": 54741.074,
"status": "clean",
"scanned_blobs": 64
},
{
"repeat": 4,
"wall_ms": 44525.475,
"child_cpu_ms": 49252.97,
"status": "clean",
"scanned_blobs": 64
},
{
"repeat": 5,
"wall_ms": 45476.787,
"child_cpu_ms": 50531.704,
"status": "clean",
"scanned_blobs": 64
},
{
"repeat": 6,
"wall_ms": 53676.076,
"child_cpu_ms": 59559.115,
"status": "clean",
"scanned_blobs": 64
},
{
"repeat": 7,
"wall_ms": 57199.498,
"child_cpu_ms": 63448.939,
"status": "clean",
"scanned_blobs": 64
}
],
"warm_samples": 7,
"warm_p50_ms": 49171.849,
"warm_p95_nearest_rank_ms": 57199.498,
"warm_median_MiB_per_second": 0.001
},
{
"case": "clean-1x1MiB",
"bytes": 1048576,
"unique_blobs": 1,
"samples": [
{
"repeat": 0,
"wall_ms": 2428.561,
"child_cpu_ms": 2617.579,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 1,
"wall_ms": 2670.102,
"child_cpu_ms": 2900.615,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 2,
"wall_ms": 2493.679,
"child_cpu_ms": 2716.212,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 3,
"wall_ms": 2578.0,
"child_cpu_ms": 2824.688,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 4,
"wall_ms": 2548.887,
"child_cpu_ms": 2792.171,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 5,
"wall_ms": 2055.215,
"child_cpu_ms": 2259.514,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 6,
"wall_ms": 2007.619,
"child_cpu_ms": 2182.369,
"status": "clean",
"scanned_blobs": 1
},
{
"repeat": 7,
"wall_ms": 2565.986,
"child_cpu_ms": 2807.337,
"status": "clean",
"scanned_blobs": 1
}
],
"warm_samples": 7,
"warm_p50_ms": 2548.887,
"warm_p95_nearest_rank_ms": 2670.102,
"warm_median_MiB_per_second": 0.392
},
{
"case": "synthetic-private-key",
"bytes": 1675,
"unique_blobs": 1,
"samples": [
{
"repeat": 0,
"wall_ms": 1175.309,
"child_cpu_ms": 1245.314,
"status": "blocked",
"scanned_blobs": 1
},
{
"repeat": 1,
"wall_ms": 1064.51,
"child_cpu_ms": 1158.645,
"status": "blocked",
"scanned_blobs": 1
},
{
"repeat": 2,
"wall_ms": 1065.706,
"child_cpu_ms": 1155.864,
"status": "blocked",
"scanned_blobs": 1
},
{
"repeat": 3,
"wall_ms": 802.468,
"child_cpu_ms": 862.132,
"status": "blocked",
"scanned_blobs": 1
},
{
"repeat": 4,
"wall_ms": 752.66,
"child_cpu_ms": 822.679,
"status": "blocked",
"scanned_blobs": 1
},
{
"repeat": 5,
"wall_ms": 859.756,
"child_cpu_ms": 934.281,
"status": "blocked",
"scanned_blobs": 1
},
{
"repeat": 6,
"wall_ms": 854.193,
"child_cpu_ms": 907.219,
"status": "blocked",
"scanned_blobs": 1
},
{
"repeat": 7,
"wall_ms": 803.553,
"child_cpu_ms": 837.327,
"status": "blocked",
"scanned_blobs": 1
}
],
"warm_samples": 7,
"warm_p50_ms": 854.193,
"warm_p95_nearest_rank_ms": 1065.706,
"warm_median_MiB_per_second": 0.002
}
],
"negative_cases": [
{
"case": "missing-scanner",
"status": "unverified",
"reason": "scan-unavailable",
"scanned_blobs": 0,
"wall_ms": 0.079
},
{
"case": "wrong-scanner-digest",
"status": "unverified",
"reason": "scanner-file-refused",
"scanned_blobs": 0,
"wall_ms": 0.047
}
],
"fixture_removed": true,
"provider_calls": 0,
"real_credentials_read": false,
"owner_source_unchanged": true,
"scanner_unchanged": true
}