Summary
The compiler emits two independent write-permission surfaces that are allowed to disagree:
- The engine's tool permission list (e.g. Claude
--allowed-tools), and
- The AWF sandbox policy
filesystem.allowWrite.
When a path appears in (1) but not (2), the agent is explicitly told it may write there, attempts the write, and receives EROFS from the sandbox. Nothing fails loudly — the step exits 0 and the data is silently dropped.
This is currently reproducible for cache-memory, which is provisioned by the compiler, granted to the engine, and omitted from allowWrite.
Reported downstream as github/gh-aw-firewall#7696 ("Unclear location for agent temp files"), surfaced by the api-consumption-report workflow in discussion #55364.
Evidence
From .github/workflows/api-consumption-report.lock.yml (firewall pinned to v0.28.6 — see fix step 1, this predates the /tmp fix in v0.28.7 — and --container-runtime cloud-hypervisor), all in the same job:
| Line |
Step |
Effect |
| 544 |
Create cache-memory directory |
Creates /tmp/gh-aw/cache-memory on the runner |
| 546–553 |
actions/cache/restore |
Restores prior contents into /tmp/gh-aw/cache-memory |
| 554–558 |
Setup cache-memory git repository |
GH_AW_CACHE_DIR=/tmp/gh-aw/cache-memory |
| 1076 |
Claude --allowed-tools |
Grants Write(/tmp/gh-aw/cache-memory/*), Edit(...), MultiEdit(...) |
| 1063 |
Compiled AWF config |
"filesystem":{"allowWrite":["/tmp/gh-aw/agent","/workspace","/workspace/.awf-home"]} |
/tmp/gh-aw/cache-memory is absent from allowWrite.
Why this fails
filesystem.allowWrite is a narrowing policy. Supplying it makes AWF re-publish every other writable export read-only, then add writable overlays only at the listed paths. So the covering /tmp bind becomes ro, with rw overlays at /tmp/gh-aw/agent, /workspace, and /workspace/.awf-home only. Writes anywhere else under /tmp/gh-aw correctly return EROFS.
This is the firewall working as designed — exempting /tmp/gh-aw wholesale would defeat the purpose of the feature and is explicitly ruled out by its design requirements. The gap is on the compiler side.
Observed impact
In discussion #55364, the agent merged a 5-entry trending history but could not persist it to /tmp/gh-aw/cache-memory/trending/api-consumption/history.jsonl. The run reported success; the next run silently restarts from the stale 2-entry cache. Any cache-memory-enabled workflow running under a sandbox with allowWrite is affected, and the failure is invisible unless the agent happens to self-report it.
Proposed fix
1. Bump the pinned firewall version to v0.28.7 (required).
The failing workflow pins "imageTag":"0.28.6". v0.28.6 does not contain the restricted-/tmp fix from github/gh-aw-firewall#7679 (merge 796ce1b9); v0.28.7 (tag ac4466f8) does. That fix is a separate concern from the allowWrite omission below — it repairs agent startup under a narrowed /tmp, whereas this issue is about a running agent hitting a correctly-narrowed path — but both are needed for cache-memory workflows to work end to end under a narrowing policy. Resolving this issue must include bumping the awf/firewall pin from 0.28.6 to 0.28.7.
Status at time of writing: v0.28.7 is tagged in github/gh-aw-firewall but not yet published as a release (latest published is v0.28.6). The bump should land once the release is available.
2. Include compiler-provisioned write targets in allowWrite (immediate).
When cache-memory is enabled, add /tmp/gh-aw/cache-memory to the generated filesystem.allowWrite. The directory is created at line 544, well before awf is invoked at line 1075, so it satisfies the planner's requirement that allowWrite paths already exist on the host. No firewall change is needed.
3. Derive allowWrite from the same source as engine write permissions (durable).
The two lists should not be authored independently. Ideally allowWrite is generated from — or validated against — the set of write-capable tool permissions, so a path can never be granted to the engine without also being writable in the sandbox.
4. Fail closed at compile time (guardrail).
Emit a compiler error (or at minimum a warning) when a Write/Edit/MultiEdit path prefix is not covered by any allowWrite entry. This converts a silent runtime data-loss bug into a build-time failure.
5. Ensure provisioned directories exist before awf starts.
allowWrite requires existing host paths. Any path the compiler adds must be created by an earlier step — the same prerequisite already documented for $GITHUB_WORKSPACE/.awf-home in github/gh-aw-firewall#7670. Worth auditing other feature-provisioned directories under /tmp/gh-aw for the same mismatch.
Related
Note on the downstream report
The linked report claims "the mount table reports rw" while writes return EROFS. That detail is agent-written and I could not corroborate it; a narrowed bind should show ro. The substantive symptom (writes outside the allowWrite set fail) is unambiguous, but the mount-table claim should not be treated as established.
Summary
The compiler emits two independent write-permission surfaces that are allowed to disagree:
--allowed-tools), andfilesystem.allowWrite.When a path appears in (1) but not (2), the agent is explicitly told it may write there, attempts the write, and receives
EROFSfrom the sandbox. Nothing fails loudly — the step exits 0 and the data is silently dropped.This is currently reproducible for cache-memory, which is provisioned by the compiler, granted to the engine, and omitted from
allowWrite.Reported downstream as github/gh-aw-firewall#7696 ("Unclear location for agent temp files"), surfaced by the
api-consumption-reportworkflow in discussion #55364.Evidence
From
.github/workflows/api-consumption-report.lock.yml(firewall pinned tov0.28.6— see fix step 1, this predates the/tmpfix inv0.28.7— and--container-runtime cloud-hypervisor), all in the same job:Create cache-memory directory/tmp/gh-aw/cache-memoryon the runneractions/cache/restore/tmp/gh-aw/cache-memorySetup cache-memory git repositoryGH_AW_CACHE_DIR=/tmp/gh-aw/cache-memory--allowed-toolsWrite(/tmp/gh-aw/cache-memory/*),Edit(...),MultiEdit(...)"filesystem":{"allowWrite":["/tmp/gh-aw/agent","/workspace","/workspace/.awf-home"]}/tmp/gh-aw/cache-memoryis absent fromallowWrite.Why this fails
filesystem.allowWriteis a narrowing policy. Supplying it makes AWF re-publish every other writable export read-only, then add writable overlays only at the listed paths. So the covering/tmpbind becomesro, with rw overlays at/tmp/gh-aw/agent,/workspace, and/workspace/.awf-homeonly. Writes anywhere else under/tmp/gh-awcorrectly returnEROFS.This is the firewall working as designed — exempting
/tmp/gh-awwholesale would defeat the purpose of the feature and is explicitly ruled out by its design requirements. The gap is on the compiler side.Observed impact
In discussion #55364, the agent merged a 5-entry trending history but could not persist it to
/tmp/gh-aw/cache-memory/trending/api-consumption/history.jsonl. The run reported success; the next run silently restarts from the stale 2-entry cache. Any cache-memory-enabled workflow running under a sandbox withallowWriteis affected, and the failure is invisible unless the agent happens to self-report it.Proposed fix
1. Bump the pinned firewall version to
v0.28.7(required).The failing workflow pins
"imageTag":"0.28.6".v0.28.6does not contain the restricted-/tmpfix from github/gh-aw-firewall#7679 (merge796ce1b9);v0.28.7(tagac4466f8) does. That fix is a separate concern from theallowWriteomission below — it repairs agent startup under a narrowed/tmp, whereas this issue is about a running agent hitting a correctly-narrowed path — but both are needed for cache-memory workflows to work end to end under a narrowing policy. Resolving this issue must include bumping the awf/firewall pin from0.28.6to0.28.7.Status at time of writing:
v0.28.7is tagged ingithub/gh-aw-firewallbut not yet published as a release (latest published isv0.28.6). The bump should land once the release is available.2. Include compiler-provisioned write targets in
allowWrite(immediate).When cache-memory is enabled, add
/tmp/gh-aw/cache-memoryto the generatedfilesystem.allowWrite. The directory is created at line 544, well beforeawfis invoked at line 1075, so it satisfies the planner's requirement that allowWrite paths already exist on the host. No firewall change is needed.3. Derive
allowWritefrom the same source as engine write permissions (durable).The two lists should not be authored independently. Ideally
allowWriteis generated from — or validated against — the set of write-capable tool permissions, so a path can never be granted to the engine without also being writable in the sandbox.4. Fail closed at compile time (guardrail).
Emit a compiler error (or at minimum a warning) when a
Write/Edit/MultiEditpath prefix is not covered by anyallowWriteentry. This converts a silent runtime data-loss bug into a build-time failure.5. Ensure provisioned directories exist before
awfstarts.allowWriterequires existing host paths. Any path the compiler adds must be created by an earlier step — the same prerequisite already documented for$GITHUB_WORKSPACE/.awf-homein github/gh-aw-firewall#7670. Worth auditing other feature-provisioned directories under/tmp/gh-awfor the same mismatch.Related
allowWritesupport and the existing-path prerequisite/tmpagent startup fix, shipping inv0.28.7Note on the downstream report
The linked report claims "the mount table reports
rw" while writes returnEROFS. That detail is agent-written and I could not corroborate it; a narrowed bind should showro. The substantive symptom (writes outside the allowWrite set fail) is unambiguous, but the mount-table claim should not be treated as established.