fxsh.

A small, security- and privacy-focused scripting language for coding agents.

$ curl -fsSL https://example.invalid/install.sh | sh
Linux (x86_64, aarch64) · macOS (aarch64) · manual install

In each project where you want your agent to use fxsh: fxsh init, to interactively integrate fxsh with the agent harness (supported are Claude Code, opencode and pi). You can choose to offer fxsh as an option, or — if you are eager — to force the agent to use it instead of its other tools.

Help us improve fxsh — install it, use it from your agent, and run fxsh telemetry collect to share where the language was insufficient. We rely on your feedback to complete the language.

Algebraic effects

Effects like proc.spawn and net.fetch are values. Handlers decide how (or whether) they execute — letting the host swap real I/O for sandboxes, fixtures, or audit logs without touching script code.

Policy-gated

A .fxsh/policy.toml in your project says which paths a script may write and which hosts it may reach. Every effect is checked against it as the script runs — a denied one fails with policy denied rather than happening. See writing policies.

A tool your agent can call

fxsh init registers fxsh as an MCP server, so your agent gets a run_fxsh tool and the language reference to go with it. One static binary, no runtime — the same one serves the tool and runs scripts from your shell.

State

We are currently investigating how usable fxsh is by both the agents and users. We ask that you use the telemetry upload every so often to let us know if the agent struggled to use it. And we are also interested in your feedback as to the ergonomics of policy authoring.

Quick taste

$ fxsh -c 'fn main() {
    let pat = re.compile(r"https?://\S+")
    let matches = pat.scan(fs.read("notes.md"))
    for m in matches { stdout.write(m.group(0)) }
}'

This is one of the shell forms. Your agent also has access to it through an MCP server. Both are bounded by the same policy.

Writing policies

A policy is a TOML file. fxsh init leaves a sensible baseline in your project's .fxsh/policy.toml — writable paths, reachable hosts — and every effect a script performs is checked against it as the script runs.

The policy is yours to edit, but you can leverage your agent for more convenience. The agent can't change the policy, but it can suggest a change which you review and approve or deny. You would see a dialogue like this:

The agent is asking to change what fxsh scripts may do here.

Agent's reason: I need the closed issues for this milestone to write the changelog.
  • allow network requests to api.github.com

Applies until this fxsh server exits; your policy file is not changed.

This dialogue is natural language, but generated by fxsh, not by the agent. You can trust its accuracy. Your approval and denial also goes directly to fxsh, all facilitated by trusted MCP protocol provisions.1 Agent's can request temporary allowances for the current session or you can ask them to permanently edit your policy.

Telemetry

Each script run emits one structured record to stderr (also mirrored to a local JSONL file) — outcome, error kind, source hash, model name.

Upload them with fxsh telemetry collect when you've got a few. You can review (and edit) the exact data before it's sent. We use this information to make the language more useful; fxsh telemetry collect --help spells out what gets shared.

Telemetry collection for sandboxed agents is currently supported only for Claude Code. More platforms coming (next planned is Copilot).

Manual install

The curl | sh line on the install card downloads the right prebuilt binary for your platform, verifies its checksum, drops it into ~/.local/bin, and offers to register fxsh with Claude Code. If you'd rather see what's happening before running it — or you're on a platform we don't ship a binary for (Intel Mac, Windows, BSD) — here's the same thing, by hand.

Option A: prebuilt binary

Pick the asset matching your platform from https://pub-c19bb22cdb2d42909de545e416be1e90.r2.dev/latest/ — currently fxsh-linux-x86_64, fxsh-linux-aarch64, or fxsh-darwin-aarch64. Each has a sibling .sha256 file with its checksum.

$ ASSET=fxsh-linux-x86_64   # adjust for your platform
$ curl -fsSL -o fxsh        "https://pub-c19bb22cdb2d42909de545e416be1e90.r2.dev/latest/$ASSET"
$ curl -fsSL -o fxsh.sha256 "https://pub-c19bb22cdb2d42909de545e416be1e90.r2.dev/latest/$ASSET.sha256"
$ sha256sum -c fxsh.sha256  # or `shasum -a 256 -c` on macOS
$ chmod +x fxsh
$ mv fxsh ~/.local/bin/

Option B: build from source

If you'd rather build, the same source tarball our release pipeline uses lives next to the binaries. Needs a Rust toolchain (install via rustup.rs) but no submodule fetch or workspace setup. Swap the path segment for v0.0.0 (or whichever) to pin a different version; the tarball is named after the same segment.

$ curl -fsSL https://pub-c19bb22cdb2d42909de545e416be1e90.r2.dev/latest/fxsh-src.tar.gz | tar xz
$ cd fxsh-*-src
$ cargo build --release -p fxsh
$ cp target/release/fxsh ~/.local/bin/

Tell Claude Code about fxsh, without fxsh init

Either install path drops the binary on your PATH but doesn't tell your coding agent it exists. In a project, fxsh init is the way to fix that. If you'd rather mention fxsh globally — or just try it before setting up a project — append a short blurb to ~/.claude/CLAUDE.md instead. This gets you the shell form only: no run_fxsh tool and no policy, so the agent decides for itself whether to use fxsh, and its other tools remain unaffected. The guard skips the append if the snippet is already there, so it's safe to re-run:

$ grep -qx '## fxsh' ~/.claude/CLAUDE.md 2>/dev/null \
    || curl -fsSL https://example.invalid/claude-snippet.md \
       >> ~/.claude/CLAUDE.md

Or view the snippet and paste it into a CLAUDE.md of your choice.