Repository navigation
leaf/357-creds-net-enforcement: implement creds axis in prep-spawn-gh.sh and net tier allowlists in proxy.py #925
Description
Charter: #357
Depends-on: #920
Purpose
Implement the creds and net enforcement axes in crewboss-prep-spawn-gh.sh and proxy.py. This leaf modifies the spawn pipeline so that CB_CREDS and CB_NET extracted from the role manifest are enforced at runtime. The test gate (capabilities-static.test.sh, classified ALLOW in leaf #920) runs when this PR merges and must pass.
You own: reference/runtime/crewboss-prep-spawn-gh.sh, reference/runtime/proxy.py.
Do NOT touch: files under reference/tests/, crewboss-spawn.sh (except the CB_PROFILE mapping in sibling leaf #924), manifest.sh, role .md files, .kafel files, or runtime-manifest.tsv.
Charter context (self-contained)
Charter #357 extends the PoC fs-ro physical-barrier (charter #356) with three enforcement axes. This leaf implements creds and net. Key code facts:
crewboss-prep-spawn-gh.shline 10: setsGH_TOKENunconditionally. Lines 12-19: export block forGH_TOKEN/GIT_CONFIG_*. Manifest library is sourced starting around line 32-61 (gated onCB_MANIFEST). Lines ~454-460: CB_FS_WORK/CB_FS_CBNET are extracted viamanifest_role_fieldin this block. Line ~462:execinto spawn.sh.crewboss-spawn.shline 93: runs nsjail with-e(inherit-all-env). Any env var set at exec time flows into the jail, regardless of--envflags. This means merely skipping the export ofGH_TOKENis NOT sufficient — ifGH_TOKENis already in the calling environment (from launcher, CI, or shell), it still enters the jail via-e.proxy.py: currently reads no CB_NET; allowlist is static. The Anthropic API endpoints (api.anthropic.com,console.anthropic.com) are already in ALLOW_EXACT and must remain always-on regardless of tier.platform.claude.comis explicitly NOT in the always-on baseline becausecrewboss-spawn.shline 95 setsCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1in every jail (programmatic agents communicate exclusively via api.anthropic.com).
Task A — creds enforcement in crewboss-prep-spawn-gh.sh
All capability-var extraction happens in the 454-460 block (after manifest load), before the exec at line 462. NOT at lines 12-19, which precede manifest load.
In reference/runtime/crewboss-prep-spawn-gh.sh, within the same block as CB_FS_WORK/CB_FS_CBNET (lines ~454-460):
-
Extract
CB_CREDSviamanifest_role_field "$CB_MANIFEST" "$ROLE" creds(same pattern as CB_FS_WORK/CB_FS_CBNET). Normalise to lowercase. -
Extract
CB_NETviamanifest_role_field "$CB_MANIFEST" "$ROLE" net. Normalise to lowercase.export CB_NET— this is the value proxy.py reads from the process environment before exec. Without this step,os.environ.get("CB_NET")in proxy.py returns None on every spawn and the default-deny branch fires unconditionally (net:board/git/open would be silently unreachable). -
If
CB_CREDS=none: addunset GH_TOKEN GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0HERE, in this block, BEFORE exec. Do NOT try to gate lines 12-19 instead — manifest is not loaded at line 12 somanifest_role_fieldis unavailable there. Theunsetclears the token that was set at line 10/12, so it is absent when spawn.sh is exec'd. The-eflag carries all set vars into the jail, so unset must happen before exec, not merely skip the line-12 export block. -
If
CB_CREDS=board: existing export block at lines 12-19 is unchanged. -
If
CB_CREDS=git-write: existing export block at lines 12-19 unchanged; additionally register an inline git credential helper that serves the token for git push authentication (e.g.git config credential.helper "..."). -
Add an inline comment in the CB_CREDS block documenting:
- Why unset is in the 454-460 block rather than at lines 12-19 (manifest not loaded until line 32+)
- Why unset is required rather than conditional export skip (GH_TOKEN may be pre-set in calling env from launcher/CI;
-ecarries all set vars into the jail) - Whether
-ein spawn.sh will be narrowed or retained, and why the unset-before-exec approach is sufficient either way
Task B — net enforcement in proxy.py
In reference/runtime/proxy.py:
-
Declare
{api.anthropic.com, console.anthropic.com}as a hardcoded always-on baseline set, NOT gated by CB_NET. These must be reachable at every tier because every Claude Code agent communicates via the Anthropic API. Add an inline comment: "platform.claude.com is intentionally excluded from the always-on baseline: crewboss-spawn.sh line 95 sets CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 in every jail; programmatic agents use api.anthropic.com exclusively." -
Read
CB_NETfromos.environ(set by prep-spawn-gh.sh Task A above) and build the effective allowlist:none(or unset/unrecognized): Anthropic baseline only — deny all other CONNECTboard: Anthropic baseline +{api.github.com, github.com}git: board +{codeload.github.com, objects.githubusercontent.com}+ npm/pypi registriesopen: current full ALLOW_EXACT + ALLOW_SUFFIX set (unchanged from today)
-
Default-deny on unrecognized or unset CB_NET (fall back to
none= Anthropic only). Log the effective net level at startup. -
Pass CB_NET to the proxy process via env — ensure the extraction in prep-spawn-gh.sh (Task A step 2) is how proxy.py receives the value.
Acceptance (machine)
- check: grep -q "unset GH_TOKEN GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0" reference/runtime/crewboss-prep-spawn-gh.sh
- check: grep -q "CB_NET" reference/runtime/crewboss-prep-spawn-gh.sh
- check: grep -q "export CB_NET" reference/runtime/crewboss-prep-spawn-gh.sh
- check: grep -q "api.anthropic.com" reference/runtime/proxy.py
- check: grep -q "CB_NET" reference/runtime/proxy.py
- check: bash reference/tests/capabilities-static.test.sh
Activity