Repository navigation
Credential inventory: github-review-app key file and a --from-file custody mode - #1057
Merged
seathatflowsinourveins merged 4 commits intoOct 13, 2026
Merged
Conversation
…stody mode
The command center's request of 2026-10-12 (22:06Z): the owner is
creating the review GitHub App, and its .pem private key needs custody
that no agent reads.
- adoption/credential-inventory.json: github-review-app, class
github_app_private_key (new in scripts/credential_status.py), store a
private_file at <store>/github-review-app.pem, no variable in the
file, GITHUB_REVIEW_APP_ID (the App's ID, not secret) as an optional
public variable kept beside its future loader, pointer
GITHUB_REVIEW_APP_KEY_PATH, no loader yet.
- tools/credentials/set_credential.py --from-file (separate strict
load_file_entry; the hidden prompt and --from-env still refuse the
entry):
- in the owner's terminal it asks for the downloaded file's path at a
visible prompt;
- it opens the file without following a symlink and refuses anything
but a regular file of 1 byte to 64 KiB;
- it copies the file unread and unprinted into a new 0600 temporary
file through the checked store handle;
- it checks that copy with `openssl pkey -noout -passin pass:` on
standard input (exit status only);
- only then does it give the copy its final name (replace needs the
hidden word) and remove the downloaded file, if the typed path still
names the copied inode.
A failed check removes the copy and keeps the original.
- tools/credentials/open_credential_terminal.sh --from-file: the
window's session runs the --from-file mode; the path is typed there,
never written into the script.
- scripts/hooks/secret_path_guard.py: the new pointer joins
POINTER_VARIABLE (a reader, copy or key tool on it is
credential_file_read). Literal store paths were already refused.
- docs/secret-storage.md: the table row, the terminal usage and the
custody paragraph, and the pointer line.
- Tests use RSA keys generated per test with openssl genpkey, never
embedded:
- custody: success, a non-key file, a passphrase-protected key, a
symlink, a missing, empty or oversized source, replace, and no
openssl;
- the entry refused by the other modes, the launcher's dry run, the
inventory row, and guard rows for the pointer.
With main's files they give 5 failures and 13 errors.
The guard pin, five dispositions locators and the manifest are
re-derived.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
seathatflowsinourveins
force-pushed
the
claude/api-actions-github-review-app-20261012
branch
from
October 13, 2026 03:41
ea6e0e0 to
d9a7199
Compare
…ecord the custody reads Fix round 1 of #1057, after Astra's first read at d9a7199 (findings 1-3) and the command center's tests-only check. - Finding 1: --from-file refuses a source the operator does not own or that has another hard link, before anything moves, and removes the source only if it still has one link. Mode bits stay unchecked (WSL reports invented ones); the one link is removed once the stored copy passes its check. - Finding 2: a failed file operation never shows its text or a path. Before the key is stored it is a refusal with fixed text and nothing moves; after it, the tool reports the key stored and the source cleanup incomplete (exit 3). The CLI's file mode gains the value-free handler --from-env already has. run_from_file looks up input and stdout per call. Six FileCustodyTests regressions fail on d9a7199's set_credential.py and pass here. - Finding 3: a dated advisory gate-read delta, appended to the index, records the four custody reads from the native derivation at d9a7199 (348 reads against the #1016 baseline's 344); earlier deltas are unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…dy link race Fix round 2 of #1057, after Astra's delta read at 60fbe1c (two P1s and P3s; the command center's ruling of 05:31Z). - Finding 1: interpreter code that looks the review App's key-file pointer up from its environment (os.environ, os.getenv, process.env, ENV) is refused as credential_file_read, as a whole-text rule after B(T)'s. stat, ls and test -f on the pointer stay allowed. Rows join BLOCKED and ALLOWED; a hook-protocol test runs the read's three forms. All fail on 60fbe1c's guard and pass here. - Finding 2: after removing the source, the still-open descriptor must show no link left; otherwise custody reports the source cleanup incomplete (exit 3) and removes no unknown name. - P3s: an interrupt after the key is stored reports that state; the stored key given as its own source changes nothing; the custody steps cite CPython v3.13.16 (os.rst replace, fsync, lstat, st_nlink). - The guard pin, the evidence manifest and five catalog locators follow the new bytes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…-file custody Scope reduction of #1057 (the command center's ruling of 2026-10-13, 06:24Z, after Astra's delta read at 74cd74c). - The interpreter-reader protection family leaves this change: scripts/hooks/secret_path_guard.py and tests/test_secret_path_guard.py return to their 60fbe1c bytes, and the docs drop their sentences on it. Main's guard is unchanged for that family, so nothing is weaker than main; the family moves to the guard's owner under route B (native sandbox read denial, cc-native-practice). - Kept: the --from-file custody mode with finding 2's post-removal link check and the round-2 custody P3s, the github-review-app inventory entry, the pointer row and their tests. - The guard pin returns to round 1's value; the evidence manifest and five catalog locators follow the new bytes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
seathatflowsinourveins
marked this pull request as ready for review
October 13, 2026 07:33
seathatflowsinourveins
deleted the
claude/api-actions-github-review-app-20261012
branch
October 13, 2026 07:45
seathatflowsinourveins
added a commit
that referenced
this pull request
Oct 13, 2026
… round 1 removes from scripts/skills_status.py Round 1 moved the registry read of the native plugin checker into plugin_status and removed the twin check's own registry reader, so the OpenHands push gate's derivation counts two reads fewer in scripts/skills_status.py (346 against the 348 that main's newest delta expects). tests.test_runtime_worker_openhands_push_gate derives its reads from the committed tree; round 1 was first checked on the working tree, so the continuous integration found the count first. - blueprints/runtime-workers/openhands/evidence: unclassified-gate-reads-20261013-pr1074-delta.json and its index entry (removed_by_script: scripts/skills_status.py 2; the four set_credential.py reads are #1057's, restated because the newest delta is cumulative from its baseline), derived with the native GitTree and derive_ci_protected at the committed head. - the record and the receipts README name the delta. Nothing else changes. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
seathatflowsinourveins
added a commit
that referenced
this pull request
Oct 13, 2026
… round 1 removes from scripts/skills_status.py Round 1 moved the registry read of the native plugin checker into plugin_status and removed the twin check's own registry reader, so the OpenHands push gate's derivation counts two reads fewer in scripts/skills_status.py (346 against the 348 that main's newest delta expects). tests.test_runtime_worker_openhands_push_gate derives its reads from the committed tree; round 1 was first checked on the working tree, so the continuous integration found the count first. - blueprints/runtime-workers/openhands/evidence: unclassified-gate-reads-20261013-pr1074-delta.json and its index entry (removed_by_script: scripts/skills_status.py 2; the four set_credential.py reads are #1057's, restated because the newest delta is cumulative from its baseline), derived with the native GitTree and derive_ci_protected at the committed head. - the record and the receipts README name the delta. Nothing else changes. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
seathatflowsinourveins
added a commit
that referenced
this pull request
Oct 13, 2026
… round 1 removes from scripts/skills_status.py Round 1 moved the registry read of the native plugin checker into plugin_status and removed the twin check's own registry reader, so the OpenHands push gate's derivation counts two reads fewer in scripts/skills_status.py (346 against the 348 that main's newest delta expects). tests.test_runtime_worker_openhands_push_gate derives its reads from the committed tree; round 1 was first checked on the working tree, so the continuous integration found the count first. - blueprints/runtime-workers/openhands/evidence: unclassified-gate-reads-20261013-pr1074-delta.json and its index entry (removed_by_script: scripts/skills_status.py 2; the four set_credential.py reads are #1057's, restated because the newest delta is cumulative from its baseline), derived with the native GitTree and derive_ci_protected at the committed head. - the record and the receipts README name the delta. Nothing else changes. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
seathatflowsinourveins
added a commit
that referenced
this pull request
Oct 13, 2026
…whatever skillOverrides says, so each of the two doubly delivered skills keeps one copy per client (CPA-V-07) (#1074) * Plugin skill identity: a plugin's skill is listed under its own name whatever skillOverrides says, so each of the two doubly delivered skills keeps one copy per client (CPA-V-07) typesafe-ai and claude-api are each delivered to Claude Code by two routes (this repository's skills.sh copy and the vendor's plugin; the client's bundled copy and the pinned plugin), so each is listed twice with one description (660 + 1,068 characters in every session), and the practice page said that skillOverrides governs skills. Measured on Claude Code 2.1.296 in headless init records: an entry hides only the copy it names (the personal or the bundled one), the qualified names typesafe:typesafe-ai and claude-api:claude-api stay listed and a qualified key changes nothing; the documentation says so (skills.md L909, L928). Ten preload probes show that an agent that preloads the skill by its short or its qualified name still gets the body with the personal copy hidden. - adoption/skills/manifest.json: typesafe-ai names its plugin (claude_plugin, claude_skill_name typesafe:typesafe-ai); claude-api names the bare name the client lists its own copy under (bundled_twin). load_manifest checks the fields. - scripts/skills_status.py: where the plugin is present (a user-scope install in installed_plugins.json and enabledPlugins true) the other copy must be hidden; the row gets a claude_twin entry, duplicate_listing when it is not, and the personal copy's expected listing there is off. A host without the plugin is unchanged. - tools/skill-usage/skill_usage.py: the usage tracker adds the uses and the context of the two listings of a skill. - The practice page (instructions-skills.md, slots.json) states the plugin exception. - Derived files that follow: the line citations of two catalogs that the added manifest lines shifted (upstream-surface-dispositions, new-wsl-architecture), the generated new-WSL handbook and its receipt's digests (regenerated with scripts/build_new_wsl_handbook.py --write), and the practice snapshot label's hash of slots.json; their tests failed in the first sweeps and pass. - The claude-api currency comparison is recorded: 91 of the 94 files of the bundled folder are byte for byte the plugin's, the other three differ in early-access wording, so the plugin stays and the bundled copy is the one hidden. The host step (skillOverrides typesafe-ai off, claude-api off) is the command center's act and is simulated against the first host's real settings: fail before, ok after. The profile template is unchanged. Tests: 22 of the 142 tests of the three modules fail on the sources of the base, among them the audit's two fail-first tests; 41 of 41 mutants killed. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * Plugin skill identity, round 1: a globally disabled bundled twin is one copy, the replacement plugin must be the pinned one, the registry is read from the client's folder, and the last use is the most recent Astra's read of 2dae975 (CHANGES_REQUESTED: two P1 and a P2) and the local codex review (three P2) were reproduced on that head; each of the six has a test that fails there (receipts/fail-before-round1.txt). - evidence/.../receipts/claude-api-currency.txt (P1, Astra): the digest of the prompt that the client expands for its bundled skill is removed, with every prefix of it: its first line names the folder the client unpacked the skill into on this host, and no published file hashes to it. The plugin file's digest (an upstream blob at the pin) stays. tests/test_plugin_skill_identity_receipts.py holds that every digest in the evidence, its README and the record resolves to a registered file, a manifest pin or a public page quoted with its URL and fetch time. - scripts/skills_status.py (P1, Astra): a bundled twin is hidden by disabling bundled skills as a whole, disableBundledSkills true or CLAUDE_CODE_DISABLE_BUNDLED_SKILLS. Measured on 2.1.296, 30 init-record runs (receipts/global-disable-init-records.json): 77 skills listed, 61 with either, the bundled claude-api gone and the plugin's kept; the spellings and the precedence of the setting, the variable and the settings env entry are the tests'. A personal twin is not a bundled skill and stays a duplicate. - scripts/skills_status.py (P2, local review): the plugin that stays must be the pinned one (pin, folder, bytes: plugin_status, the native plugin checker's own read); an installed and enabled plugin that is not is plugin_invalid whether or not the other copy is hidden. - scripts/skills_status.py (P2, local review): the registry is read from the folder of the settings that enable the plugin (the client's configuration folder), not from the skills CLI's trimmed link folder; the native plugin status reads it too. - scripts/skills_status.py (P2, local review): a Claude-only copied row with claude_plugin gets the twin check and the adjusted listing, as a linked one does. - tools/skill-usage/skill_usage.py (P2, Astra): the combined last use is the most recent of the used listings, the cells put in order (never, today, N days, N hours ago); an unorderable cell makes it unknown. - The record has a review round 1, the receipts README five rows, the older fail-before receipt names the base it was taken against and its replay at the exact base 993cc89 gives the same 22 of 142; 30 of 30 mutants killed. The new helper sits beside claude_row so that no cited line number of the file moves. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * Plugin skill identity, round 1: the advisory delta for the reads that round 1 removes from scripts/skills_status.py Round 1 moved the registry read of the native plugin checker into plugin_status and removed the twin check's own registry reader, so the OpenHands push gate's derivation counts two reads fewer in scripts/skills_status.py (346 against the 348 that main's newest delta expects). tests.test_runtime_worker_openhands_push_gate derives its reads from the committed tree; round 1 was first checked on the working tree, so the continuous integration found the count first. - blueprints/runtime-workers/openhands/evidence: unclassified-gate-reads-20261013-pr1074-delta.json and its index entry (removed_by_script: scripts/skills_status.py 2; the four set_credential.py reads are #1057's, restated because the newest delta is cumulative from its baseline), derived with the native GitTree and derive_ci_protected at the committed head. - the record and the receipts README name the delta. Nothing else changes. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * Plugin skill identity, round 2: the client's switch for its bundled claude-api hides the bundled twin, and a bundled twin that no route lists is not_listed The local codex review of f7e0cd7 said "patch is incorrect" (one P2) and Astra's read of 48e083e is CHANGES_REQUESTED (one P2 blocker, answered by f7e0cd7, and an adjacent P2); the co-op asked for one commit for the two visibility cases. Each was reproduced on f7e0cd7 and has a test that fails there (receipts/fail-before-round2.txt). - scripts/skills_status.py (P2, local review): the bundled twin is also hidden by the client's switch for that skill, CLAUDE_CODE_DISABLE_CLAUDE_API_SKILL: registerClaudeApiSkill is called with disabled: env.CLAUDE_CODE_DISABLE_CLAUDE_API_SKILL === true in the installed 2.1.296 binary (the function that initializes the bundled skills; neither the documentation nor the changelog at the pin names it). Measured in 14 init-record runs (receipts/skill-switch-init-records.json): 77 skills listed, 76 with the switch, the bundled copy gone and the plugin's kept; the spellings are the all-bundled switch's, and the settings env entry overrides the process environment in both directions. A bundled twin of another name has no such switch; a personal twin is not hidden by it. - scripts/skills_status.py (P2, Astra, follow-up 2): with the plugin not listed (disabled, or not registered) and the bundled twin hidden (skillOverrides, the all-bundled switch or the skill's own) no route lists the skill: the twin entry is not_listed and the row fails. A disabled plugin with the bundled copy visible is one copy and passes. - The push gate's read inventory (Astra's blocker 1) was answered by f7e0cd7 (the delta, 346 reads); this commit changes no count (the module passes on the committed tree, 127 tests). - Truth fixes: the record says what is reported (never an install path or a credential) and what is read (the plugin's SKILL.md inside the plugin cache); the receipts README names the base that the older fail-before receipt was taken against. - 14 of 14 mutants killed (receipts/mutants-round2.txt). Nothing else changes. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * Plugin skill identity, round 3: a number or an array in a settings env entry is read as the client reads it, and a missing settings file is empty settings The local review of the rebased head found two states in which the checker said ok or duplicate_listing where the client's listing is otherwise, and the command center ruled a checker that reports a wrong state a gate defect: one final bounded commit, pushed with the registry-only rebase onto main 24a2090. Each case was reproduced on 7c7d667 and has a test that fails there (receipts/fail-before-round3.txt). - scripts/skills_status.py env_text (P2-1, :482-484): the client reads a settings env value as JavaScript's String() of the parsed JSON, so a switch spelled 1.0, 1e0 or [1] is set as 1 is. The reader took only Python int and bool and str, so 1.0 and 1e0 (floats) were no text and the switch was reported unset. Measured on 2.1.296 in 28 init-record runs (receipts/numeric-env-init-records.json): 61 skills listed, the bundled claude-api gone, with 1, 1.0, 1e0, 10e-1, 0.1e1, 1E0, 1.00, true, "1", [1], ["1"], [true] and [[1]]; 77 with 0, 0.0, 2, 1.5, -0, null, [], an object, [1, 2], ["1", "x"], [null], [null, 1] and [1, true]; the skill's own switch reads 1.0 and 1e0 the same way. env_text now writes a number as its integral value without a point or an exponent below 1e21, an array as its elements joined by commas, an object as "[object Object]"; an array nested beyond the interpreter's depth is no switch. - scripts/skills_status.py read_switches (P2-2, :514-516): a settings file that does not exist is empty settings, so the process environment's CLAUDE_CODE_DISABLE_BUNDLED_SKILLS (or the skill's own switch) still counts, as the client starts with none and reads its environment; with no file no plugin is enabled either, so a hidden bundled copy is not_listed and the row fails instead of passing. A file that exists and cannot be read still disables nothing. - tests/test_skills_status.py: test_a_number_or_an_array_in_an_env_entry_is_the_text_javascript_makes_of_it (27 subtests of raw settings text through the reader and the row's twin state, three for the skill's own switch, the deep array) and test_a_missing_settings_file_is_empty_settings_and_the_environment_switch_still_counts; 12 of 12 mutants killed (receipts/mutants-round3-12.txt, the first run left an array read as its first element only, and the test now holds the measured multi-element spellings). - The push gate's read inventory is not changed by this commit (the module passes on the committed tree); the rebase re-derived the delta of the round-1 commit over main's tree (347 reads). - Nothing else changes. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> --------- Co-authored-by: Claude Sonnet 5.5 <[email protected]>
5 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Scope
github-review-appinventory slot for the review GitHub App's PEM private key, and a--from-filecustody mode. The owner types the downloaded key's path in the credential terminal; the tool moves the file into the store as a new 0600 file without reading or printing it, checks it withopenssl pkey -noout(exit status only), and removes the original only after that check passes.c63c83a8(main). Four commits:f3619dfc, the scope reduction,74cd74c3, fix round 2, and60fbe1c6, fix round 1 (all below), ond9a7199d, a pure rebase ofea6e0e01(the samegit patch-id --stablefor the whole patch and for the patch outside the registries; no conflict, and the registries needed no change).lane:foundationadoption/credential-inventory.json(the new entry only);scripts/credential_status.py(thegithub_app_private_keyclass);tools/credentials/set_credential.py(--from-file);tools/credentials/open_credential_terminal.sh(--from-file);scripts/hooks/secret_path_guard.py(the new pointer) and its pin inadoption/hooks/claude/SHA256SUMS;docs/secret-storage.md;tests/test_credential_tools.py,tests/test_credential_status.py,tests/test_secret_path_guard.py;catalogs/foundation/upstream-surface-dispositions.json(five locators);manifests/evidence.json(re-registration).POINTER_VARIABLE, which Remove the guard's here-document false positives; read after reserved words #1048 rewrites. Remove the guard's here-document false positives; read after reserved words #1048 lands first. This PR then rebases, and its guard change becomes one name added to Remove the guard's here-document false positives; read after reserved words #1048's_POINTER_NAME, which also brings the pointer into Remove the guard's here-document false positives; read after reserved words #1048's presence check. The owner keeps the downloaded key in Downloads, unopened, until this lands.Source. The command center's request of 2026-10-12 (22:06Z): the owner creates the review GitHub App, and its key needs custody that no agent reads.
The slot.
github_app_private_key, new inscripts/credential_status.py.private_fileat${XDG_CONFIG_HOME:-$HOME/.config}/native-agent-stack/github-review-app.pem.GITHUB_REVIEW_APP_ID, the App's ID, is not secret and is not in the file: it is an optional public variable, set beside the loader that will sign the App's tokens.GITHUB_REVIEW_APP_KEY_PATH; no loader yet.credential_status.pychecks the file's mode, owner and directory and never reads its contents (it parses onlyprivate_env_filestores).--from-file(set_credential.py, throughopen_credential_terminal.sh github-review-app --from-file):load_file_entrytakes only an operator-suppliedprivate_fileof a key-file class. The hidden-prompt mode and--from-envkeep refusing the entry, so noexportline is ever written to a key file.O_NOFOLLOW; anything but a regular file of 1 byte to 64 KiB, owned by the operator and with exactly one link, is refused. Custody removes that link; a second link would keep the key where custody cannot see it. Mode bits are not checked, since a Windows drive in WSL reports invented ones.O_EXCL0600 temporary file through the checked store handle and fsynced.openssl pkey -noout -passin pass:on standard input, with output discarded. Only the exit status counts, so a passphrase-protected key fails instead of prompting.replaceat the hidden prompt), the directory is fsynced, and only then is the original removed, provided the typed path still names the copied inode and it still has one link.Guard. Literal store paths (
~/.config/native-agent-stack/…, the$XDG_CONFIG_HOMEspellings, readers, copies andopensslon them) are already refused ascredential_store_path, and the settings deny Read and Edit under the store. The new pointer was the one gap:cat "$GITHUB_REVIEW_APP_KEY_PATH"passed main's guard. It now refuses ascredential_file_read;statandlson it stay allowed. The interpreter-reader protection family is not part of this change (the scope reduction below).Follow-ups (not in this change):
GITHUB_REVIEW_APP_ID.d9a7199d(P3):blueprints/runtime-workers/openhands/resolver/patch_policy.py:137citesdocs/secret-storage.md:199-200, a locator already stale on main; a section anchor would hold. It is inherited and outside this change's paths.readers/review-app-20261012/upstream/.74cd74c3(P3):patch_policy.py:137locator and hostname labels, and the two captures, as above.f3619dfc(P3), each with its trigger:set_credential.py's custody path. Make cancellation reporting conservative across that interval, with a path-free instruction to check the stored key and the source, and add a real-replace-then-interrupt regression (CPython v3.13.16Doc/library/signal.rst:783-801).patch_policy.py:137locator and hostname labels. Trigger: the owning cleanup of those files; use a stable section anchor and generic host roles there.Fix round 1 (
60fbe1c6). Astra's first read atd9a7199d(the read's verdict, a private record cited by name) asked for changes with 2 P1s and 1 P2. The command center's tests-only check had passed. Every test below uses keys generated per test under a temporary root; no key byte or digest is printed or stored.--from-envalready has.run_from_filelooks upinputand stdout per call. Defaults bound at import time had made the CLI path untestable.FileCustodyTestsregressions cover a second link, another owner, a link made during the copy, failures of the source check or removal after storing, failures of the store write before it (fsync, replace), and the CLI. Each fails ond9a7199d'sset_credential.py(8 failing subtests) and passes here.unclassified-gate-reads-20261013-pr1057-delta.json) is appended to the index; earlier deltas and baselines are unchanged.GitTree,derive_ci_protected) atd9a7199d: 348 reads against the Finalize 373 starred repositories with guarded retention and source coverage #1016 baseline's 344.60fbe1c6gives the same total, script counters and shapes.d9a7199dand pass here.Fix round 2 (
74cd74c3). Astra's delta read of60fbe1c6(the read's verdict, a private record cited by name) found 2 P1s and 5 P3s. The command center ruled one commit for the P1s and the P3s (05:31Z). Keys are still generated per test under a temporary root.credential_file_read. That coversos.environ,os.getenv,process.envandENV.SECRET_LOOKUP, and passes the guard's no-backtracking test.stat,lsandtest -fon the pointer stay allowed.BLOCKED, so the keyring-exec and rtk-proxy tests read them too. A hook-protocol test runs the read's three forms through the real hook.60fbe1c6's guard (8 rows; 3 hook exits of 0) and pass here.cbc944f4):Doc/library/os.rst:2754(replace),:1108(fsync),:2412(lstat) and:3125(st_nlink).60fbe1c6'sset_credential.py, exactly the three new custody tests fail, one by an uncaught interrupt. The other 13 pass.74cd74c3gives 348 with the same counters and shapes, so the dated delta of round 1 stands.patch_policy.py:137locator and hostnames (inherited), and the two captures, which the lane holds for the packet.Scope reduction (
f3619dfc). Astra's delta read of74cd74c3(the read's verdict, a private record cited by name) found one more P1 in the interpreter-reader protection family, and 4 P3s. Round 2 was the last fix round. The command center ruled a split (2026-10-13, 06:24Z).scripts/hooks/secret_path_guard.pyandtests/test_secret_path_guard.pyreturn to their60fbe1c6bytes, and the docs drop their sentences on it.--from-filecustody mode, with finding 2's post-removal link check and the round-2 custody P3s;github-review-appinventory entry and the pointer row;SOTA sources
f07ab5ee42454bed23f650d9d24eff2ecb239dccaf7fe988c61e37dad5893b1a). Line 39: the key downloads as a PEM file and "GitHub only stores the public portion of the key". Line 42: the PEM isPKCS#1 RSAPrivateKey. Lines 81-85: storing private keys. Captured in the api-actions lane'sreaders/review-app-20261012/upstream/.-noout"Do not output the key in encoded form" (line 88 of the capture, sha256407550a20cc1c7d0f034e7315ed8456c347620308ffce9438a5ae00623c5c23e);-passintakes the password source.set_credential.py's checked directory handle,O_EXCLtemporary files and atomic replace, reused here.Evidence-class table
local_integrationFileCustodyTestswith RSA keys generated per testlocal_integrationFileCustodyTestslocal_integrationset_credential.py, launcher, guard andcredential_status.pyswapped in:FAILED (failures=5, errors=13)local_integrationcredential_store_path), the two pointer forms passLocal commands run
Decision record
None: the inventory row and
docs/secret-storage.mdrecord the command center's request cited above.Host evidence
Not applicable: no receipt changes.
Checklist
version comment (no floating tags). (none changed)
permissions: {}and grant each jobonly what it needs (
contents: read, or an explicitly justified addition). (none changed)added without a documented owner.
moved or overwritten).
🤖 Generated with Claude Code