Agents get a filtered mirror of your project — real code, real structure. Secrets are redacted byte-for-byte, right where they read; forbidden files fail closed.
curl -fsSL https://raw.githubusercontent.com/sarathsp06/janusfs/main/install.sh | sh
Linux · one static binary · all install methods → · optional --net=none kills the network too
why it exists
A file with a secret inside it has no good binary answer. Deny it and the agent that needed the file's shape breaks; allow it and the secret lands in the model's context. That dilemma is why agents either stall or over-share — and it's the gap JanusFS fills, one layer down at the filesystem.
answer 1 · deny
A deny rule, Landlock, an .aiexclude. The secret is safe — but the agent that needed that config value, that fixture, that file's shape now fails, loops, or hallucinates around the hole.
✗ the agent breaks
answer 2 · allow
Let it read the file so it can work. Now the secret sits in the context window, the transcript, the provider's logs — and one prompt injection turns cat .env into curl attacker.com.
✗ the secret leaks
janusfs · the third answer
The file lists at its real size and reads back with only the secret spans replaced by *, byte-for-byte. The agent gets the shape and the safe content; the secret never enters. Truly forbidden files still fail closed.
✓ the agent works, the secret stays
the three faces
Every file still lists with its real size — masking preserves byte length, so getattr and offsets are identical whether or not a span is redacted. One .janusfs.yml drives all three.
01 — rules
Compile .janusfs.yml globs + pattern library.
02 — engine
Resolve a Decision behind an atomic, immutable snapshot.
03 — provider
Serve redacted bytes from a RAM cache — never disk.
04 — mount
Translate the Decision into a kernel answer over FUSE.
use cases
Because it's a filesystem, whatever reads files works unchanged — the agent, its shell, git, the build, an MCP server. You don't wire JanusFS into your tool; you run your tool inside JanusFS.
aider, Claude Code, Codex, Cursor read your source and reason about it, but .env values and API tokens read back as **** and private keys fail closed. Every child process inherits the view.
janusfs exec -- aider
Masking keeps secret bytes out of the agent; --net=none keeps whatever it did read from leaving the box — loopback only, no route out, kernel-enforced.
janusfs exec --net=none -- claude
Hand a long-lived mountpoint to an MCP server, a code indexer, a REPL, or a teammate's script. The daemon owns it and remounts after a reboot.
janusfs daemon --background janusfs mount . cd "$(janusfs path .)"
See exactly what an agent would read — every likely-secret still Allowed, and why any single path resolves the way it does — before you run anything against it.
janusfs check --secrets janusfs explain .env
enforcement
janusfs exec confines the entire subprocess tree at once: its read_file tool, its Bash tool, git, a build, a subprocess — all inherit the filtered view, with no per-tool wiring and no way to opt out.
platform
mount and exec error out on macOS and Windows rather than pretend an advisory mount is a boundary. On a Mac, run the agent in a Linux container/VM started with --device /dev/fuse --cap-add SYS_ADMIN.
one caveat, warned loudly
Because .git/ passes through to the real object store, git add on a masked file stages the **** bytes. check and exec both report every masked file git would stage.
where it fits
Sandboxes and containers decide allow/deny and protect the machine; JanusFS masks the bytes. Run janusfs exec inside them.
why not .gitignore?
Whole-file only. A config with one secret line is all-or-nothing; JanusFS redacts the line and keeps the rest of the file readable.
why not a sandbox?
It guards the machine, not the model's context. Files it lets through still flow verbatim into the agent and its provider.
why not scrub once?
A frozen copy drifts the moment the agent edits a file. JanusFS decides per read, against the live tree, so it never goes stale.
get running
JanusFS runs on Linux only. Install the FUSE runtime, drop in the binary, seed a policy, then run your agent inside the boundary — or hand a persistent mount to the daemon.
sudo apt-get install -y fuse3 # Debian/Ubuntu # sudo dnf install -y fuse3 # RHEL/Fedora
cd my-project janusfs init janusfs check --secrets janusfs explain .env
janusfs exec -- aider janusfs exec --net=none -- aider
janusfs daemon --background janusfs mount ~/proj cd "$(janusfs path ~/proj)" janusfs umount ~/proj
On macOS or Windows, run all of this inside a Linux container or VM (Docker Desktop, Colima, OrbStack, a devcontainer) started with --device /dev/fuse --cap-add SYS_ADMIN — both mount and exec refuse to run natively off Linux. Prefer Go? go install github.com/sarathsp06/janusfs/cmd/janusfs@latest builds from source.
stand at the doorway
In Roman myth, Janus is the two-faced god of doorways. JanusFS stands between your code and any untrusted agent, deciding which face of each file is safe to show — allowed, masked, or hidden.
Linux amd64 & arm64 · all install methods → · prebuilt releases →