Architecture
August 3, 2026 · View on GitHub
Status
Implemented
Decision
Official name: "Bashkit" (not "BashKit"). Crate/package identifiers use lowercase bashkit.
Bashkit uses a Cargo workspace with multiple crates:
| Crate | Purpose |
|---|---|
crates/bashkit/ | Core library (parser, interpreter, VFS, builtins, tool contract) |
crates/bashkit-cli/ | CLI binary |
crates/bashkit-python/ | Python bindings (PyO3) |
crates/bashkit-js/ | JavaScript bindings (NAPI-RS) |
crates/bashkit-capi/ | Versioned native C ABI |
crates/bashkit-eval/ | LLM eval study (mira framework) |
Core library modules: parser/, interpreter/, fs/, builtins/,
network/, git/, ssh/, scripted_tool/. See source — structure evolves.
Public API
Main entry points: Bash (library) and BashTool (LLM tool contract).
See crates/bashkit/src/lib.rs for the full public API surface.
Per-invocation exec state
ExecOptions carries everything scoped to a single exec_with_options
call: streaming callback, builtin extensions, arg0, positional, and
stdin. All of it is per-call by construction, not session state.
- Positional parameters exist only inside a call frame, so the host
boundary pushes a synthetic top-level frame before
Interpreter::executeand truncates the call stack back to its baseline afterwards — including on error paths, so$#is 0 again on the next exec.$0defaults tobashwhen noarg0is supplied;set --at top level uses the same synthetic frame and must not change$0. stdinseedspipeline_stdin, whichreset_transient_stateclears at the start of every exec — so it is installed after that reset and immediately before execution. A pipe or redirect inside the script still wins for the command it applies to.
Both are installed late (just before execute) so the size, hook, and
parse checks that can return early cannot leave state behind.
Design Principles
- Async-first: All filesystem and execution is async (tokio)
- Virtual: No real filesystem access by default
- Multi-tenant safe: Isolated state per Bash instance
- Trait-based: FileSystem and Builtin traits for extensibility
Alternatives Considered
- Single crate: rejected — CLI bloats library; Python/JS packages need separate crates.
- Sync filesystem: rejected — network ops need async; tokio already a dep.
See also
- Parser — script text to AST, ahead of the interpreter
- Virtual Filesystem — filesystem abstraction the interpreter executes against
- Builtin Commands — command layer the interpreter dispatches into
- Parallel Execution — threading model and shared-ownership rules
- Threat Model — trust boundaries these module boundaries enforce
- Known Limitations — what this architecture intentionally does not do