[claude-hackernews] Reply draft: bwrap+sshfs agent sandbox, in-mount rm -rf still possible (id=48000009)#38
Conversation
… still possible (id=48000009) Reply on obiwahn's "Safe(R) Repo Access for Agents" post. The bwrap+sshfs setup is a strong host-side perimeter, but inside the mount the agent can still rm -rf or git reset --hard over uncommitted work. Frames the PreToolUse-hook layer as complementary, not competing. One policy mention (block-rm-rf), no install commands, ASCII-only.
📝 WalkthroughWalkthroughA draft Markdown file documenting a Hacker News reply about safely mounting git repositories into agent VMs. The draft outlines the original post's threat model, endorses host-side enforcement, notes repo destruction risks, provides insights for the FailProof team on layered security approaches, and records metadata about thread engagement and composition preferences. ChangesDraft HN Reply on Agent Repo Access
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Review rate limit: 1/5 review remaining, refill in 36 minutes and 40 seconds. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@drafts/2026-05-03T191243Z.md`:
- Line 27: The inline code span contains a trailing space before the closing
backtick in the example `command="...sftp-force-directory /path/to/repos-claude"
`; remove the extra space so it reads `command="...sftp-force-directory
/path/to/repos-claude"` to satisfy markdownlint MD038 and eliminate the
trailing-space inside the inline code span.
- Around line 15-21: The markdown fenced block that begins with the disclosure
line is missing a language tag, causing markdownlint MD040; update the opening
code fence (the ``` that precedes "(disclosure: I work on FailProof AI...") to
include a language identifier such as text (i.e. change ``` to ```text) so the
block is explicitly tagged and the lint error is resolved.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d2f92b12-fd55-4889-8cd7-41fba4c642cf
📒 Files selected for processing (1)
drafts/2026-05-03T191243Z.md
| ``` | ||
| (disclosure: I work on FailProof AI: https://github.com/exospherehost/failproofai) | ||
|
|
||
| Like the host-side enforcement here. The "VM cannot push" property holds even if the agent gets root inside the VM, which the in-process approaches (Claude Code permissions, harness hook policies) cannot match. | ||
|
|
||
| These layers compose well. bwrap gives a kernel-enforced perimeter around the agent, but inside the sandbox the agent can still happily `rm -rf /mnt/repo/build` and clobber a sibling directory, or `git reset --hard` over uncommitted changes you wanted to review. A PreToolUse hook that catches those calls before they reach the FUSE layer turns "agent destroyed your branch but at least it stayed in the mount" into "agent got told no and tried something else." Even with this setup I'd still want `block-rm-rf` running in the harness on top. | ||
| ``` |
There was a problem hiding this comment.
Add a language identifier to the reply code fence.
The fenced block currently has no language tag, which triggers markdownlint MD040.
Proposed fix
-```
+```text
(disclosure: I work on FailProof AI: https://github.com/exospherehost/failproofai)
Like the host-side enforcement here. The "VM cannot push" property holds even if the agent gets root inside the VM, which the in-process approaches (Claude Code permissions, harness hook policies) cannot match.
These layers compose well. bwrap gives a kernel-enforced perimeter around the agent, but inside the sandbox the agent can still happily `rm -rf /mnt/repo/build` and clobber a sibling directory, or `git reset --hard` over uncommitted changes you wanted to review. A PreToolUse hook that catches those calls before they reach the FUSE layer turns "agent destroyed your branch but at least it stayed in the mount" into "agent got told no and tried something else." Even with this setup I'd still want `block-rm-rf` running in the harness on top.</details>
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 15-15: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@drafts/2026-05-03T191243Z.md` around lines 15 - 21, The markdown fenced block
that begins with the disclosure line is missing a language tag, causing
markdownlint MD040; update the opening code fence (the ``` that precedes
"(disclosure: I work on FailProof AI...") to include a language identifier such
as text (i.e. change ``` to ```text) so the block is explicitly tagged and the
lint error is resolved.
|
|
||
| - This thread is a good template for the "complementary, not competing" framing. The bwrap+sshfs setup is a *strong outer perimeter*: kernel-enforced, root-in-VM-resistant, and unbypassable by anything inside the agent. The natural FailProof pitch isn't "you don't need this if you have FailProof" - it's "this protects the host from the agent; FailProof protects the repo contents from the agent." Two threat models, two layers. | ||
| - A blog post titled something like *"Sandboxing the agent vs. policy-gating the agent: when do you need both?"* would land with this audience. The argument writes itself: bwrap stops the agent from `cat /etc/shadow` on the host, but does nothing if the agent runs `git reset --hard` inside the mount. Hook-layer policies do the inverse. Real setups want both. | ||
| - The OP's design has a feature FailProof currently lacks but could add: *baked-in path scoping per agent invocation*. The `command="...sftp-force-directory /path/to/repos-claude" ` line in `authorized_keys` makes the path-allowlist part of the credential rather than the agent config. There's a clean analog for the harness side: a per-session "scope" (cwd allowlist, write-target allowlist) injected via env at agent-start time, that custom policies can read and enforce. Worth considering as a `--scope <dir>` ergonomics improvement on top of the existing `block-read-outside-cwd` / `allowPaths`. |
There was a problem hiding this comment.
Remove the trailing space inside the inline code span.
There is an extra space before the closing backtick in the authorized_keys example (markdownlint MD038).
Proposed fix
-- The OP's design has a feature FailProof currently lacks but could add: *baked-in path scoping per agent invocation*. The `command="...sftp-force-directory /path/to/repos-claude" ` line in `authorized_keys` makes the path-allowlist part of the credential rather than the agent config. There's a clean analog for the harness side: a per-session "scope" (cwd allowlist, write-target allowlist) injected via env at agent-start time, that custom policies can read and enforce. Worth considering as a `--scope <dir>` ergonomics improvement on top of the existing `block-read-outside-cwd` / `allowPaths`.
+- The OP's design has a feature FailProof currently lacks but could add: *baked-in path scoping per agent invocation*. The `command="...sftp-force-directory /path/to/repos-claude"` line in `authorized_keys` makes the path-allowlist part of the credential rather than the agent config. There's a clean analog for the harness side: a per-session "scope" (cwd allowlist, write-target allowlist) injected via env at agent-start time, that custom policies can read and enforce. Worth considering as a `--scope <dir>` ergonomics improvement on top of the existing `block-read-outside-cwd` / `allowPaths`.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - The OP's design has a feature FailProof currently lacks but could add: *baked-in path scoping per agent invocation*. The `command="...sftp-force-directory /path/to/repos-claude" ` line in `authorized_keys` makes the path-allowlist part of the credential rather than the agent config. There's a clean analog for the harness side: a per-session "scope" (cwd allowlist, write-target allowlist) injected via env at agent-start time, that custom policies can read and enforce. Worth considering as a `--scope <dir>` ergonomics improvement on top of the existing `block-read-outside-cwd` / `allowPaths`. | |
| - The OP's design has a feature FailProof currently lacks but could add: *baked-in path scoping per agent invocation*. The `command="...sftp-force-directory /path/to/repos-claude"` line in `authorized_keys` makes the path-allowlist part of the credential rather than the agent config. There's a clean analog for the harness side: a per-session "scope" (cwd allowlist, write-target allowlist) injected via env at agent-start time, that custom policies can read and enforce. Worth considering as a `--scope <dir>` ergonomics improvement on top of the existing `block-read-outside-cwd` / `allowPaths`. |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 27-27: Spaces inside code span elements
(MD038, no-space-in-code)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@drafts/2026-05-03T191243Z.md` at line 27, The inline code span contains a
trailing space before the closing backtick in the example
`command="...sftp-force-directory /path/to/repos-claude" `; remove the extra
space so it reads `command="...sftp-force-directory /path/to/repos-claude"` to
satisfy markdownlint MD038 and eliminate the trailing-space inside the inline
code span.
Discovery
Found via
/newestsweep on HN. Story id=48000009 ("Safe(R) Repo Access for Agents") was ~30 minutes old at draft time, posted by userobiwahn, linking to https://obiwahn.org/posts/safe-sftp-access-for-agents/. OP self-replied with "A little idea to share repos with agents in a more secure manner." No third-party comments yet.Thread
OP runs AI agents in a dedicated VM, mounts host repos via
sshfs, and uses anauthorized_keyscommand="..."forced wrapper plus abwrapnamespace to constrain what the agent can see. Push happens on the host after manual review; the agents VM has no git remote credentials. Stated invariant: even root in the VM cannot escape the bwrap mount.Why this thread fits
Thread-fit gate: this is a "concrete-failure / proposed-solution" post about agent sandboxing, where the OP is sharing a defense-in-depth recipe and explicitly inviting engagement on it. Adjacent-product Show-HN-style framing, even though it's a personal-blog submission rather than a Show HN. Substantive engagement on the design first; one policy mention.
Proposed reply
The complementary point: bwrap protects the host from the agent, but inside the mount the agent can still happily
rm -rf /mnt/repo/buildorgit reset --hardover uncommitted changes. Different threat models, different layers. Mentions exactly one policy (block-rm-rf) tied to the in-sandbox failure mode. ~128 words. ASCII-only punctuation. No install command, no feature catalog, single repo URL in the disclosure line.Full draft text in
drafts/2026-05-03T191243Z.md(also rendered in the diff).Workflow
CLAUDE.md"Comments via PR (never direct post)", nothing was typed into the HN composer; the textarea on the story page is open and the thread accepts replies, but Claude does not click submit.Summary by CodeRabbit