[FEATURE] Path anchor prefix for the current project's metadata directory in sandbox filesystem rules

Open 💬 0 comments Opened Jul 4, 2026 by sbuzonas

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

Project metadata — most notably auto-memory files under ~/.claude/projects/<project-slug>/memory/ — lives outside the sandbox's default write boundary (working directory + session $TMPDIR). This creates an asymmetry for anyone using auto memory with sandboxing enabled:

  • Creating/editing memory works, because it goes through Claude's built-in file tools, which are governed by the permission system, not the sandbox.
  • Deleting memory fails, because deletion has no built-in tool equivalent — it is always rm via Bash — and the sandbox denies the write at the OS level regardless of permission-prompt approval.

The practical result: Claude can accumulate cross-session project memory but cannot perform memory hygiene (pruning stale topic files, removing obsolete notes) without the user intervening manually outside the session.

Every currently available workaround has a meaningful drawback:

  1. allowWrite: ["~/.claude/projects/**"] in user-level settings — works, but violates least privilege. It grants every sandboxed command (including anything a prompt injection manages to run) write and delete access over every project's session metadata and memory, not just the current project's. For users treating the sandbox as a security gate, this is not an acceptable trade.
  2. Hardcoding the slug path per project in .claude/settings.local.json (e.g. allowWrite: ["~/.claude/projects/-home-user-repos-myproject/memory/**"]) — correctly scoped, but brittle. The slug is derived from the checkout path, so the rule silently becomes a no-op if the repository moves (compounded by #32287: misresolved sandbox paths produce no warning). It is also per-checkout boilerplate that must be manually recomputed.
  3. Relocating memory into the worktree via autoMemoryDirectory — solves the access problem, but changes the storage layout (memory files now live in the repo tree, need gitignoring, and become per-checkout) to work around what is fundamentally an access-control gap.

Proposed Solution

Sandbox filesystem paths already resolve through a small family of anchor prefixes: absolute paths, ~/ for home, ./ for project-relative resolution, and the legacy // absolute form. This proposal adds one more anchor to that existing table rather than introducing any new expansion mechanism: a named prefix that resolves to the current project's metadata directory.

{
  "sandbox": {
    "filesystem": {
      "allowWrite": ["#meta/memory/**"]
    }
  }
}

Where #meta/ resolves to ~/.claude/projects/<slug-for-current-working-directory>/ at sandbox initialization, in the same resolver that handles the existing prefixes.

Properties this achieves:

  • Least privilege by construction: the grant automatically tracks the current project. A single user-level rule scopes correctly for every project without granting cross-project access.
  • Safe in user-level settings: no per-checkout boilerplate, no hand-computed slugs.
  • Robust to relocation: the anchor re-resolves per session, so moving a checkout doesn't silently break the rule.
  • Minimal mechanism: this is one additional branch in the existing path-prefix resolution, consistent with how ./ already provides a dynamic project-relative anchor. No string interpolation, no environment lookups, no mid-path substitution semantics to define.
  • Named, not sigil-only: a self-documenting prefix like #meta/ avoids extending the class of subtle look-alike prefixes that #32287 showed can silently misresolve. A path literally beginning with #meta in the project can still be addressed as ./#meta/....

Choice of sigil: # is proposed because it is currently unclaimed across Claude Code's configuration and prompt surfaces, avoiding overload of sigils that already carry meaning: @ (CLAUDE.md @path imports, CLI @file mentions, plugin@marketplace identifiers) and ~ (home expansion, plus the Unix ~username convention a ~meta/ spelling would shadow). Two edges were considered and are worth defining explicitly if adopted: (1) the gitignore specification treats a leading # as a comment, so if this anchor is later extended to Read/Edit permission rules — which follow gitignore semantics — leading-# handling must be explicitly defined there to avoid the silent no-op class documented in #32287; sandbox filesystem paths use their own resolver and are unaffected. (2) # begins a comment in shell, which is irrelevant for JSON settings but would matter if the anchor were ever accepted in CLI arguments. If maintainers weigh those edges heavily, @meta/ remains a workable alternative (no parsing conflict with the import/mention surfaces, which are separate code paths); the mechanism, not the sigil, is the proposal.

Extending the same anchor to Edit/Read permission rules would be a natural follow-on, but the sandbox filesystem arrays are the core need.

Alternative Solutions

  • The three workarounds described above (broad allowWrite, hardcoded slug, autoMemoryDirectory relocation) — each functional, each with the drawbacks noted.
  • A settings variable, e.g. ${CLAUDE_PROJECT_META} — same resolved value, but introduces variable-expansion machinery into settings paths. Prefix anchoring is a better fit: the metadata directory is a start-of-path anchor, which prefixes encode positionally for free, and it stays consistent with the resolution model users already configure against.
  • General environment-variable expansion in settings.json (#4276) — would be a superset of this, but is a much broader change with a larger security surface (arbitrary user-environment values flowing into security-relevant config). This proposal is deliberately narrower: the anchor's value is computed internally by Claude Code from the working directory, not read from the user's environment, so it introduces no new injection surface into sandbox policy.

Priority

Medium - Would be very helpful

Feature Category

Configuration and settings

Use Case Example

Memory hygiene with sandboxing enabled: a user runs periodic sessions asking Claude to consolidate and prune its auto-memory files. Today, consolidation (rewrites via Edit) succeeds while pruning (rm via Bash) is OS-denied, forcing manual cleanup. With allowWrite: ["#meta/memory/**"] in user settings, the full hygiene loop works per-project with no cross-project exposure.

Additional Context

Related Issues (not duplicates)
  • #25947 — store project memory in a project-local .claude/memory/ folder. Attacks the storage location axis; this request attacks the access control axis. They are complementary: even if #25947 ships, the ~/.claude/projects/<slug>/ directory still holds other per-project metadata the sandbox cannot reach, and users who deliberately keep memory out of the worktree (multiple checkouts, avoiding repo-tree pollution) still need a scoped grant.
  • #28276 — configurable memoryDirectory setting (labeled duplicate; effectively addressed by autoMemoryDirectory). Same distinction applies: relocation changes where the data lives to work around what the sandbox can reach, rather than letting sandbox policy express "this project's own metadata" directly.
  • #4276 — general environment-variable expansion in settings.json (see Alternatives Considered).
  • #32287 — silent misresolution of sandbox path prefixes (see Security Considerations).
Security Considerations
  • The anchor resolves to a path Claude Code already owns and computes; it is not user- or repo-controllable, so a malicious repository cannot redirect it.
  • If desired, acceptance could be restricted by settings scope (e.g. honored from user/local/policy settings but not project settings), mirroring the existing restriction on autoMemoryDirectory that prevents a cloned repo from redirecting memory paths.
  • Because the prefix is visually distinct from ordinary paths, it avoids the silent-misresolution failure mode documented in #32287 for near-identical prefixes (/ vs //); pairing the new anchor with resolution-time validation/warning would strengthen both.

View original on GitHub ↗