Exfiltrate gives your running program a command line.
You write the commands in Rust, inside the program, against its live state. Then you — or a coding agent — run them from a terminal while the program is running: on your desktop, on a phone, in a browser tab, or on a box you can only reach over SSH.
# // no_run because: the example starts a live server and requires an external CLI.
# #[cfg(target_arch = "wasm32")]
wasm_lite::set_panic_hook();
use exfiltrate::command::{Command, Response};
# fn connected_players() -> usize { 3 }
struct Players;
impl Command for Players {
fn name(&self) -> &'static str { "players" }
fn short_description(&self) -> &'static str { "Count connected players" }
fn full_description(&self) -> &'static str { "Prints the number of connected players." }
fn execute(&self, _args: Vec<String>) -> Result<Response, Response> {
Ok(format!("{} players online", connected_players()).into())
}
}
exfiltrate::begin(); // once, at startup
exfiltrate::add_command(Players);
// ... the rest of your program$ exfiltrate help players
Prints the number of connected players.
$ exfiltrate players
3 players online
Because most of what you want from a running program isn't a breakpoint. It's what's in the cache right now, flip this flag, kick that job, dump the scene graph, save me a screenshot of the framebuffer. A debugger can get you there, but you'll be typing expressions at an lldb prompt, and the moment you want the output formatted your way you're writing formatters in a scripting dialect nobody else on the team knows.
With exfiltrate you write that tooling once, in Rust, next to the code it inspects. It costs one dependency and one call at startup, and it works the same everywhere your program runs — including places a debugger can't attach. It's as useful for a library as for an application: register a command that dumps whatever your library knows and you have a live inspector for it.
Because agents, like people, reason better about a program they can poke than one they can only read. Giving an LLM a purpose-built interface for running and observing the target — instead of retrieval over source — took SWE-bench issue resolution from 3.8% to 12.5%: more than 3× from the harness alone, with no change to the model.1 Expose your program's nouns and verbs, and an agent can list the nouns and do the verbs instead of guessing from source.
- One call to start. Add the dependency, call
begin(). No daemon, no config file. - No async runtime. Plain threads, no
tokio. - Text, files, images. Commands return strings, binary files, or RGBA images; the CLI prints or saves them.
- Desktop, mobile, WebAssembly. Browser targets go through a small proxy (see below).
- Log capture, separately. Logs come from the
logwise_agent_exfiltratepackage rather than a feature of this one.
-
Add
exfiltrateas a dependency. Callbegin()as early as possible — the top ofmain— and register your commands withadd_command(), as in the example above. -
With the program running, drive it from the
exfiltrateCLI. The native server listens on127.0.0.1:1337; the CLI connects there automatically.In a sandbox that forbids binding a port, point both ends somewhere else with
EXFILTRATE_ADDR(orConfig::with_addrand--addr):unix:/path/to/socketuses a Unix socket, access-controlled by its0700parent directory rather than by "anyone on loopback";unix:@nameuses the Linux abstract namespace; andfd:3adopts a connectedsocketpair(2)a supervising process already handed the program, which needs no new socket at all.An address that is reachable from outside this machine needs a token, and gets one without you arranging anything: the server invents one for the run and prints it, and you type it into
exfiltrate --token. Set$EXFILTRATE_TOKENon both ends instead if you would rather pin one, in which case nothing is printed. A token authenticates but does not encrypt — debug output crossing a hostile network still wants a tunnel.exfiltrate connectingexplains the whole picture.
exfiltrate list # every command available right now
exfiltrate help <command> # details for one command
exfiltrate <command> [args] # run it against the live program
list shows the CLI's own built-ins plus whatever the running program registered (a
local name wins on collision); if the program is down, it says so. The CLI is
self-documenting — list, then help, and there's nothing to memorize — which is also
what makes it easy to hand to an agent:
claude "Run `exfiltrate list`, then integrate the exfiltrate library into my program."
Not on Rust, but on C or C++? That's a use case exfiltrate doesn't cover yet — file a feature request with details about your setup.
Implement command::Command and register it with add_command(). name is what
you type; short_description is what list shows; full_description is what help
prints — so write them for someone reading cold, human or agent.
A command's Response can carry:
- Text — the common case; anything that's
Into<Response>from aString. - Files — binary payloads via
FileInfo. - Images — RGBA images via
ImageInfo, built fromrgb::RGBA8pixels (thergbcrate is re-exported).
The CLI prints text and writes files and images to disk. For worked examples of every
response type, run exfiltrate help custom_commands.
Most Rust networking crates pull in tokio or another async runtime. That's the right
call for a high-concurrency server and the wrong one for a debug shim you just want to
embed. Exfiltrate has no tokio dependency; it uses threads. Threads for everyone.
A browser WASM app can't open raw TCP sockets, so exfiltrate bridges through a proxy:
- The WASM app connects out to
exfiltrate proxyover WebSockets. - Another
exfiltrateinvocation connects to that proxy over TCP. - The proxy relays between them, so the CLI drives the WASM app as if it were local.
Start an installed copy with exfiltrate proxy --help, or run it from this
workspace with cargo run -p exfiltrate_cli -- proxy --help.
This crate has no feature flags. Log capture used to live here behind a
logwise feature; it is now the separate logwise_agent_exfiltrate package, so
a program that wants live state does not pull a logging integration in with it,
and one that wants logs asks for it by name.
Run exfiltrate help integration for embedding guidance, or exfiltrate help custom_commands for the full response-type reference.
Footnotes
-
Yang et al., SWE-agent: Agent–Computer Interfaces Enable Automated Software Engineering, NeurIPS 2024. https://arxiv.org/abs/2405.15793 ↩
