GitHub - dirac-run/braket: Classify shell command effects in microseconds. TypeScript and Rust implementations

GitHub

4 min read Original article ↗

Classify shell command effects in microseconds. TypeScript and Rust.

BraKet analyzes Bash commands without executing them. It reports filesystem, network, process, and other capabilities for use in coding-agent permissions. The command data covers 906 root commands and 8,194 command paths.

Performance

Median time per direct integer API call:

Workload TypeScript / Node.js Rust
Single command 1.936 µs 0.550 µs
Compound input: 2–32 commands, averaging 10 11.659 µs 2.474 µs
Long input: averaging about 18,000 characters 126.080 µs 13.057 µs

AMD Ryzen 7 3800X, Linux x86-64, Node.js 24.11.0, Rust 1.98.1 (release). Seven samples of 20,000 calls per workload, following 5,000 warmup calls.

Reproduce the benchmarks · Node.js results · Rust results

Install

Node.js 24 or later:

npm install -g braket-cli

For library use:

The Rust library can be added as a local Cargo dependency:

[dependencies]
braket = { path = "/path/to/BraKet/rust" }

CLI

braket [--json] [--] [COMMAND]

Pass a quoted command or read it from stdin:

$ braket 'pwd'
3

$ braket --json 'pwd' 
{"filesystem":"read"}

$ printf '%s' 'rg -n "TODO" src/ | head -20' | braket --json
{"filesystem":"read"}

$ braket --json "find . -name '*.log' -delete"
{"filesystem":"mutate"}

$ braket --json 'ps'
{"processes":"read"}

$ braket --json 'curl -o result.json https://example.com/data'
{"filesystem":"mutate","network":"use","remote_resources":"read"}

$ braket --json 'npm install'
{"filesystem":"mutate","network":"use","execution":"use","software":"mutate","remote_resources":"read"}

$ braket --json 'git config --global user.name'
{"configuration":"read"}

$ braket --json 'kubectl get pods'
{"network":"use","remote_resources":"read"}

$ braket --json 'zip backup.zip notes.txt'
{"filesystem":"mutate"}

$ braket --json 'python -c "print(1)"'
{"execution":"use"}

$ braket --json 'gpg --list-keys'
{"credentials":"read"}

$ braket --json 'lsblk'
{"devices":"read"}

$ braket --json 'uname -a'
{"system":"read"}

$ braket --json 'true'
{}

$ braket --json 'du -sh src/; unknown_command_xyz'
"unknown"

Results exit with status 0. Usage and I/O errors exit with status 2.

From a local checkout, use node typescript/dist/cli.js or rust/target/debug/braket after building the respective implementation.

Library API

import { classify, classifyReadable } from 'braket-cli';

const mask = classify('ls -la');           // 3
const effects = classifyReadable('rm report.txt'); // { filesystem: 'mutate' }
use braket::{classify, classify_readable};

let mask = classify("df -h"); // 3
println!("{}", classify_readable("chmod 644 report.txt").to_json());
// {"filesystem":"mutate"}

Readable results use "use", "read", and "mutate". The highest access level wins for each capability. An empty result is {}. If any part of the input is unknown, the whole result is "unknown".

Rust returns ReadableDecision::Capabilities(map) or ReadableDecision::Unknown; to_json() serializes either result.

Integer format

classify() returns -1 for unknown input and 0 for an empty result. Each capability occupies three bits: 0 absent, 1 use, 3 read, 7 mutate.

Capability Bit offset
filesystem 0
network 3
processes 6
execution 9
credentials 12
software 15
configuration 18
devices 21
remote_resources 24
system 27
if (mask !== -1) {
  const filesystem = mask & 7;
  const network = (mask >>> 3) & 7;
}

Classification rules

Effects accumulate across pipelines, substitutions, redirections, and branches. Unsupported syntax and unresolved command or option combinations return unknown.

execution covers selected programs, plugins, project scripts, tests, and package build backends. Ambient hooks and ordinary helpers are excluded. Filesystem mutation covers output files and explicit writes; temporary working files and compiler caches are excluded. Literal output redirection to /dev/null adds no effect.

TypeScript reference · Rust reference

Why this exists

To alleviate coding agent permission fatigue.

Dirac originally used a primitive static allowlist of safe commands, which was neither safe nor comprehensive. I kept thinking it would be neat to give permissions like “filesystem read” or “network use.”

To do that, you need a Bash parser to accurately identify the commands and flags. I initially tried Tree-sitter and mvdan/sh. Both were functional, but took more time and resources than I could justify for a high-volume command evaluator.

Then I looked for a parser I could customize and repurpose specifically for this use case. I found Unbash, tested it, and it worked well. So I used it as the starting point for a parser dedicated to classifying command effects.

It worked so well that I decided to create a Rust implementation too.

Development

# TypeScript
cd typescript
npm ci
npm run typecheck
npm test
npm run package:smoke

# Rust
cd ../rust
cargo test --offline --locked
cargo test --offline --locked --release
cargo build --offline --locked --release --bin classify-batch

# Shared reference fixtures
cd ..
python3 scripts/verify-parity.py

Validation includes 217 TypeScript tests, Rust debug and release tests, and 36,953 shared reference inputs. Builds use the checked-in command data.

License and acknowledgment

Apache-2.0. Shell recognition derives from Unbash by Lars Kappert and contributors. Its ISC license is preserved in LICENSE.unbash, with attribution in NOTICE.