Dicklesworthstone/slb has 82 GitHub stars and 15 forks. GitHot measured 0 stars in 24 hours, 0 in 7 days, and 0 in 15 days. Last checked 2026-11-15 07:19 UTC. Last push 2026-11-04.
What you can do
- Risk-Based Classification: Commands are automatically classified by risk level
- Client-Side Execution: Commands run in YOUR shell environment (inheriting AWS credentials, kubeconfig, virtualenvs, etc.)
- Command Hash Binding: Approvals bind to the exact command via SHA-256 hash
- SQLite Source of Truth: Project state lives in .slb/state.db
Measured star growth
- in 24h
- 0
- in 7 days
- 0
- in 15 days
- 0
- in 1 month
- Collecting history
Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.
About
Two-person rule CLI for AI coding agents: peer review and approval required before running potentially destructive commands
Developer ToolsREADME.md
OriginalSimultaneous Launch Button (slb)
A cross-platform CLI for reviewing potentially destructive commands from AI coding agents before execution.
When an agent wants to run something risky (e.g., rm -rf, git push --force, kubectl delete, DROP TABLE), slb requires peer review and explicit approval before execution.
Choose the assurance level explicitly: The default independent approval policy refuses local approvals and execution requiring independently authenticated reviewers. No external identity provider is implemented yet. Cooperating agents on one host can opt into same_host peer review, a mistake-prevention workflow, not a security boundary. This mode does not authenticate agent or model identities and cannot contain a malicious process sharing the same OS user. Requests, reviews, execution records, and history keep their policy labels; local reviews never become independent authorization. See the identity boundary and deployment requirements.
Why This Exists
Coding agents can get tunnel vision, hallucinate, or misunderstand context. A second reviewer (ideally with a different model/tooling) catches mistakes before they become irreversible.
slb is built for multi-agent workflows where many agent terminals run in parallel and a single bad command could destroy work, data, or infrastructure.
Key Features
- Risk-Based Classification: Commands are automatically classified by risk level
- Client-Side Execution: Commands run in YOUR shell environment (inheriting AWS credentials, kubeconfig, virtualenvs, etc.)
- Command Hash Binding: Approvals bind to the exact command via SHA-256 hash
- SQLite Source of Truth: Project state lives in
.slb/state.db - Agent Mail Integration: Notify reviewers and track audit trails via MCP Agent Mail
- TUI Dashboard: Interactive terminal UI for human reviewers
Risk Tiers
| Tier | Approvals | Auto-approve | Examples |
|---|---|---|---|
| CRITICAL | 2+ | Never | rm -rf /, DROP DATABASE, terraform destroy, git push --force |
| DANGEROUS | 1 | Never | rm -rf ./build, git reset --hard, kubectl delete, DROP TABLE |
| CAUTION | 0 | After 30s | rm file.txt, git branch -d, npm uninstall |
| SAFE | 0 | Immediately | rm *.log, git stash, kubectl delete pod |
These counts are review requirements. With the default independent policy,
local sessions cannot satisfy a requirement for independent approval. With
same_host, they count cooperating local reviewers under the limitations above.
Quick Start
Installation
Recommended: Homebrew (macOS/Linux)
brew install dicklesworthstone/tap/slb
This method provides:
- Automatic updates via
brew upgrade - Dependency management
- Easy uninstall via
brew uninstall
Windows: Scoop
scoop bucket add dicklesworthstone https://github.com/Dicklesworthstone/scoop-bucket
scoop install dicklesworthstone/slb
Alternative: Direct Download
Choose the matching archive from the latest release:
- Linux x86_64:
slb_<version>_linux_amd64.tar.gz - Linux ARM64:
slb_<version>_linux_arm64.tar.gz - macOS Intel:
slb_<version>_darwin_amd64.tar.gz - macOS ARM64:
slb_<version>_darwin_arm64.tar.gz - Windows x86_64:
slb_<version>_windows_amd64.zip
Download checksums.txt from that same release and verify the archive's SHA-256
before extracting it. The archive contains slb on Linux/macOS or slb.exe on
Windows. Windows ARM64 archives are not currently published.
Alternative: Install Script
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/slb/main/scripts/install.sh?$(date +%s)" | bash
The installer verifies the archive against checksums.txt from the selected
release and reports the retained download workspace. Set INSTALL_DIR to choose
the destination. For qualification with a known scratch location,
SLB_RETAIN_WORK_ROOT may name an absolute path that does not exist yet and whose
parent already exists; it is created privately and retained on success or failure.
Build from Source
git clone https://github.com/Dicklesworthstone/slb.git
cd slb && make build
Go Install
go install github.com/Dicklesworthstone/slb/cmd/slb@latest
Initialize a Project
cd /path/to/your/project
slb init
This creates a .slb/ directory with:
state.db- SQLite database for requests, reviews, and sessionsconfig.toml- Project-specific configurationpending/- JSON files for pending requests (for watching/interop)
Working from Subdirectories
Ordinary commands discover the nearest initialized project from -C /
--project, then SLB_PROJECT, then the current directory. For example, a
session started at a repository root can submit a request from src/ and use
the root project's database, configuration, and custom patterns. Reviewers at
the root see that same request.
Project discovery does not change the command's working directory. -C changes
into the explicitly selected directory before discovery; SLB_PROJECT selects
the project without changing cwd. Requests bind the exact command directory,
and approved execution uses that recorded directory even when invoked elsewhere.
Default foreground, background, and emergency logs belong to the owning project's
.slb/logs/. An explicit --log-dir remains relative to the invoking directory.
slb init intentionally initializes the selected directory itself, allowing a
separate nested project. An existing nested .slb marks a project boundary;
malformed or inaccessible markers and missing policy databases cause an error,
instead of silently creating another database or using a home history store.
An explicit --db still selects the requested database. The exception is slb's
own user directory, ~/.slb (user config.toml, daemon.log, hooks and audit
records): it marks a project only when it holds a state.db, so until then
directories under your home are discovered as if it were absent.
Discovery follows filesystem aliases before selecting policy. Existing literal
project aliases are retained when they identify that same physical root; an alias
into another project's descendant uses the target project's policy. Stored
session, request, and review bindings are never rewritten by discovery.
When a selected root alias has its own recorded request identity, slb watch
uses local polling to retain that exact scope.
Same-Host Peer-Review Workflow
Enable this only when the agents cooperate and you want a second session to check for mistakes. From the initialized project, opt in before creating requests:
slb config set general.approval_policy same_host
This writes approval_policy = "same_host" under [general] in
.slb/config.toml. The default is "independent". Each request keeps the policy
under which it was created; changing configuration does not convert existing
requests or reviews. Both approving a request and executing it require the current
policy to match. Set it back to independent to disable local approval and
execution of outstanding same-host requests.
# 1. Each agent starts a session and retains its own returned ID and key.
slb session start --agent "Requester" --program "claude-code" --model "opus"
slb session start --agent "Reviewer" --program "codex-cli" --model "codex"
# 2. The requester explains the exact command and its intended effects.
slb request "rm -rf ./build" --session-id <requester-id> \
--reason "Clean generated artifacts before a fresh compile" \
--expected-effect "Remove only this project's build directory" \
--safety "Source files are outside build; all outputs can be regenerated"
# 3. A different agent inspects the command, working directory and explanation.
slb show <request-id>
slb approve <request-id> --session-id <reviewer-id> --session-key <reviewer-key> \
--comments "Checked the working directory and build contents"
# 4. The requester executes only after the required reviews are recorded.
slb execute <request-id> --session-id <requester-id>
The output identifies these as same-host reviews, not independently
authenticated authorization. Quorum, different-model checks when configured,
signatures, expiry, and command binding still apply. The requester cannot review
its own request, including through a restarted session with the same agent name
or the trusted_self_approve setting. Names and model labels remain self-reported.
slb run provides the same workflow while waiting and executing automatically
after review.
Another eligible reviewer can still record an objection after quorum is reached, until SLB commits the execution or authorization claim. The current conflict policy determines whether that objection rejects, escalates, or leaves the request approved. A negative vote never grants a new approval or renews an existing deadline.
Commands Reference
Session Management
slb session start --agent <name> --program <prog> --model <model>
slb session end --session-id <id>
slb session resume --agent <name> --session-id <id> --session-key <key> # Resume after crash
slb session list # Show active sessions
slb session heartbeat --session-id <id> # Keep session alive
Request & Run
# Primary command (atomic: check, request, wait, execute)
slb run "<command>" --reason "..." [--session-id <id>]
# Plumbing commands
slb request "<command>" --reason "..." # Create request only
slb status <request-id> [--wait] # Check status
slb pending [--all-projects] # List pending requests
slb cancel <request-id> # Cancel own request
Review & Approve
slb review <request-id> # Show full details
slb approve <request-id> --session-id <id> --session-key <key> # Review under the request's policy
slb reject <request-id> --session-id <id> --session-key <key> --reason "..."
Execution
slb execute <request-id> # Execute approved request
slb emergency-execute "<cmd>" --reason "..." # Unreviewed override (logged)
slb rollback <request-id> # Rollback if captured
Pattern Management
slb patterns list [--tier critical|dangerous|caution|safe]
slb patterns test "<command>" # Check what tier a command would be
slb patterns add --tier dangerous "<pattern>" # Agents can add patterns
Daemon & TUI
slb daemon start [--foreground] # Start background daemon
slb daemon stop # Stop daemon
slb daemon status # Check daemon status
slb tui # Launch interactive TUI
slb watch --session-id <id> --json # Stream events for agents
Configuration
Configuration is hierarchical (lowest to highest priority):
- Built-in defaults
- User config (
~/.slb/config.toml) - Project config (
.slb/config.toml) - Environment variables (
SLB_*) - Command-line flags
Example Configuration
[general]
approval_policy = "independent" # Default; "same_host" explicitly opts into local workflow review
min_approvals = 2
request_timeout = 1800 # 30 minutes
approval_ttl_minutes = 30
timeout_action = "escalate" # or "auto_reject", "auto_approve_warn"
[rate_limits]
max_pending_per_session = 5
max_requests_per_minute = 10
[notifications]
desktop_enabled = true
desktop_delay_seconds = 60
[daemon]
tcp_addr = "" # For Docker/remote agents
tcp_require_auth = true
Default Patterns
CRITICAL (2+ approvals)
| Pattern | Description |
|---|---|
rm -rf /... |
Recursive deletion of /, system paths, or home roots (/home, /home/user, /Users/user, /root, ~) |
DROP DATABASE/SCHEMA |
SQL database destruction |
TRUNCATE [TABLE] [ONLY] ... |
SQL data destruction (TABLE is optional) |
terraform destroy |
Infrastructure destruction |
kubectl delete node/namespace/pv/pvc |
Kubernetes critical resources |
git push --force, git push origin +main |
Force push (not with-lease), including + refspecs |
aws terminate-instances |
Cloud resource destruction |
dd ... of=/dev/ |
Direct disk writes |
DANGEROUS (1 approval)
| Pattern | Description |
|---|---|
rm -rf |
Recursive force delete |
git reset --hard |
Discard all changes |
git clean -fd |
Remove untracked files |
kubectl delete |
Delete Kubernetes resources |
curl ... | bash, bash <(curl ...) |
Downloaded script run by a shell |
terraform destroy -target |
Targeted destroy |
DROP TABLE |
SQL table destruction |
chmod -R, chown -R |
Recursive permission changes |
For deletion, -r, -R, and --recursive express recursive intent; -f
alone remains CAUTION. Project descendants such as /home/user/project/build
are DANGEROUS, while home roots and system directories remain CRITICAL.
build and ./build resolve against the same working directory, and any
protected operand makes a recursive deletion CRITICAL.
CAUTION (auto-approved after 30s)
| Pattern | Description |
|---|---|
rm <file> |
Single file deletion |
git stash drop |
Discard stashed changes |
git branch -d |
Delete local branch |
npm/pip uninstall |
Package removal |
SAFE (skip review)
| Pattern | Description |
|---|---|
rm *.log, rm *.tmp, rm *.bak |
Temporary file cleanup |
git stash |
Stash changes (not drop) |
kubectl delete pod |
Pod deletion (pods are ephemeral) |
npm cache clean |
Cache cleanup |
IDE Integration
Claude Code Hooks
Add to your AGENTS.md:
## SLB Integration
Before running any destructive command, use slb:
\`\`\`bash
# Instead of running directly:
rm -rf ./build
# Use slb:
slb run "rm -rf ./build" --reason "Clean build before fresh compile"
\`\`\`
All DANGEROUS and CRITICAL commands must go through slb review.
Install the Claude Code hook for the selected project:
slb init # Once per project, before project hook installation
slb hook install
slb hook status
# Or install for all projects in your user settings:
slb hook install --global
The slb integrations claude-hooks --install entry point uses the same installer.
Use slb integrations claude-hooks --preview to print a current PreToolUse
settings fragment without changing files. See hook installation and scopes.
Cursor Rules
Generate Cursor rules:
slb integrations cursor-rules > .cursorrules
Shell Completions
# zsh (~/.zshrc)
eval "$(slb completion zsh)"
# bash (~/.bashrc)
eval "$(slb completion bash)"
# fish (~/.config/fish/config.fish)
slb completion fish | source
Architecture
.slb/
├── state.db # SQLite database (source of truth)
├── config.toml # Project configuration
├── pending/ # JSON snapshots for watching
│ └── req-<uuid>.json
├── sessions/ # Session files
└── logs/ # Execution logs
└── req-<uuid>.log
Key Design Decision: Client-side execution. The daemon checks review evidence and policy; it is not an independently protected authorization service. Commands execute in the calling process's shell environment to inherit:
- AWS_PROFILE, AWS_ACCESS_KEY_ID
- KUBECONFIG
- Activated virtualenvs
- SSH_AUTH_SOCK
- Database connection strings
Troubleshooting
"Daemon not running" warning
This is expected - slb works without the daemon (file-based polling). Start the daemon for real-time updates:
slb daemon start
"Active session already exists"
Resume it with the session ID and key that slb session start printed:
slb session resume --agent "YourAgent" --session-id <id> --session-key <key>
slb stores only a digest of the key and cannot print it again. If the key is lost, end that session and start a new one:
slb session end --session-id <id>
slb session start --agent "YourAgent" --program <prog> --model <model>
Approval expired
Approvals have a TTL (30min default, 10min for CRITICAL). Re-request if expired:
slb run "<command>" --reason "..." # Creates new request
Command hash mismatch
The command was modified after review. Submit a new request for the modified command; changing its stored hash does not make the existing signed review valid.
Safety Note
slb adds friction and peer review for dangerous actions. It does NOT replace:
- Least-privilege credentials
- Environment safeguards
- Proper access controls
- Backup strategies
Use slb as defense in depth, not your only protection.
Claude Code Hook Integration
To integrate with Claude Code, slb provides a PreToolUse hook that intercepts Bash commands before execution.
Quick Setup
# Initialize the project's policy and state database first
slb init
# Install in the current project's .claude/settings.json
slb hook install
# Check that same project installation
slb hook status
# Test classification without executing
slb hook test "rm -rf ./build"
# Select a different project, or install for all projects
slb -C /path/to/project init
slb -C /path/to/project hook install
slb hook install --global
Installation Scope
install, status, and uninstall operate on the selected project by default.
Discovery starts at -C / --project, then SLB_PROJECT, then the current
working directory and selects the nearest initialized project. Installing from
src/ therefore updates that project's root settings and hook files. Add
--global to operate on your user installation instead.
Project installation requires a readable, initialized
<project>/.slb/state.db. Initialize a new project with slb init first; restore
or repair missing or unreadable state in an existing project before installing.
The installer checks this prerequisite before changing settings or creating a
fallback script. A global registration can be installed without initializing a
project; its native guard still applies the current policy checks for each tool call.
| Scope | Claude Code registration | Standalone fallback script |
|---|---|---|
| Project (default) | <project>/.claude/settings.json |
<project>/.slb/hooks/slb_guard.py |
Global (--global) |
~/.claude/settings.json |
~/.slb/hooks/slb_guard.py |
SLB updates its own PreToolUse hook and preserves other settings, other hook
events, and neighboring hooks in the same Bash entry. Reinstalling an unchanged
registration leaves the settings file unchanged. --force or --merge=false
resets only SLB's own hook options. Uninstalling removes the selected registration
and keeps the script file; it does not edit the other scope.
Earlier versions of slb hook install always wrote user settings. Inspect that
existing installation with slb hook status --global; use
slb hook uninstall --global if you choose to remove it. Project status reports
the project registration, even when a global registration is also present.
Generated registrations include onFailure: "block". Claude Code v2.1.295 or
later supports this setting to block an action when its hook cannot start,
fails, or times out. See the Claude Code hook failure documentation.
How It Works
- Native guard: Claude Code invokes the installed
slb hook guardcommand with the Bash tool input. The generated Python script is available separately as a standalone fallback. - Current policy: The guard uses the command's working directory to load project policy and find the project daemon.
- Review and execution: A reviewed command is handed to SLB's execution path, which rechecks its command binding, policy, review evidence, and expiration before claiming execution.
- Offline behavior: When the daemon is unavailable, the native guard classifies locally and blocks commands that require review.
Installing a hook leaves general.approval_policy unchanged. The optional
same_host policy remains a mistake-prevention workflow, with no independently
authenticated reviewer identity or operating-system security boundary.
Explicit Policy Files
When your workflow uses a separate configuration file, select the same file for installation, the daemon, requests, and reviews:
slb -C /path/to/project --config /path/to/review-policy.toml hook install
slb -C /path/to/project --config /path/to/review-policy.toml daemon start
slb -C /path/to/project --config /path/to/review-policy.toml request "<command>" --reason "..." --session-id <requester-id>
slb -C /path/to/project --config /path/to/review-policy.toml approve <request-id> --session-id <reviewer-id> --session-key <key>
The installed registration retains the absolute filename and its owning physical project. The hook reloads that file for offline classification and passes it to the executor when handing off a reviewed command. Equivalent file aliases may connect to the same daemon, but the handoff retains the hook's selected filename so later replacement of that file still takes effect. Missing or invalid selected files block the action. A daemon started with a different configuration must be restarted with the selected file; a hook query cannot change its policy.
The binding applies only to the owning project, including for a global hook. A separately initialized nested project uses its own policy. The generated Python artifact also retains the binding and uses the native guard to read current configuration; it blocks if that guard is unavailable. An artifact generated without an explicit file retains its embedded offline snapshot behavior.
Execution still reloads policy and verifies the exact request, signatures,
quorum and expiry. Opting out of same_host after a hook lookup prevents the
outstanding handoff from executing; it never upgrades the original reviews.
Hook Commands
slb hook generate # Generate standalone fallback script
slb hook install [--global] # Install in the selected scope
slb hook uninstall [--global] # Remove only that registration
slb hook status [--global] # Inspect that scope's installation
slb hook test "<command>" # Test classification without executing
slb integrations claude-hooks --preview # Print a PreToolUse settings fragment
slb integrations claude-hooks --install [--global] # Same scoped installer
The guard returns Claude Code's PreToolUse permission decision:
allow: The tool call may proceed, subject to Claude Code's other permissions.ask: Claude Code requests a decision, for example under a configured CAUTION policy.deny: The tool call is blocked with guidance for requesting SLB review.
Pattern Matching Engine
The pattern matching engine is the core of slb's command classification system.
Classification Algorithm
Normalization: Commands are parsed using shell-aware tokenization
- Strips wrapper prefixes:
sudo,doas,env,time,nohup, etc. - Extracts inner commands from
bash -c 'command'patterns - Resolves paths:
./foo→/absolute/path/foo
- Strips wrapper prefixes:
Compound Command Handling: Commands with
;,&&,||,|are split and each segment is classified independently. The highest risk segment determines the overall tier.echo "done" && rm -rf /etc → CRITICAL (rm -rf /etc wins) ls && git status → SAFE (no dangerous patterns)Shell-Aware Splitting: Separators inside quotes are preserved:
psql -c "DELETE FROM users; DROP TABLE x;" → Single segment (SQL) echo "foo" && rm -rf /tmp → Two segmentsPattern Precedence: Patterns are checked in order: SAFE → CRITICAL → DANGEROUS → CAUTION
- First match wins within each tier
- SAFE patterns skip review entirely
Fail-Safe Parse Handling: If command parsing fails (unbalanced quotes, complex escapes), the tier is upgraded by one level:
- SAFE → CAUTION
- CAUTION → DANGEROUS
- DANGEROUS → CRITICAL
Shell redirections that create, truncate or append to a file receive at least
CAUTION, even when the invoked program matches a SAFE rule. For example,
git stash > important.sql can truncate the file before Git starts. The current
CAUTION policy therefore applies; stronger DANGEROUS or CRITICAL matches remain.
This check also follows nested shell programs and considers every redirection,
including a file target later superseded by /dev/null. Literal /dev/null,
file-descriptor duplication/closing and input-only redirects retain their normal
classification. Classification never executes the command or inspects its files.
Fallback Detection
For commands that wrap SQL (e.g., psql -c "...", mysql -e "..."), pattern matching may not catch embedded statements. The engine includes fallback detection:
DELETE FROM ... (no WHERE clause) → CRITICAL
DELETE FROM ... WHERE ... → DANGEROUS
Runtime Pattern Management
Agents can add patterns at runtime:
slb patterns add --tier dangerous "docker system prune"
slb patterns list --tier critical
slb patterns test "kubectl delete deployment nginx"
Pattern changes are persisted to SQLite and take effect immediately.
Request Lifecycle
Requests follow a well-defined state machine with strict transition rules.
State Diagram
stateDiagram-v2
direction TB
PENDING --> APPROVED: Quorum satisfied
PENDING --> REJECTED: Rejected
PENDING --> CANCELLED: Requester withdraws
PENDING --> TIMEOUT: Review deadline
TIMEOUT --> ESCALATED: Escalation policy
ESCALATED --> APPROVED: Quorum satisfied
ESCALATED --> REJECTED: Rejected
ESCALATED --> CANCELLED: Requester withdraws
APPROVED --> CANCELLED: Requester withdraws
APPROVED --> REJECTED: Signed rejection blocks
APPROVED --> ESCALATED: Conflicting signed review
APPROVED --> EXECUTING: Verified atomic claim
EXECUTING --> EXECUTED: Success
EXECUTING --> EXECUTION_FAILED: Failure
EXECUTING --> TIMED_OUT: Execution deadline
Recovering Escalated Requests
slb pending and slb review list include both pending and escalated requests,
with an explicit status in each row. Their project and review-pool filters
apply to both states. Escalation keeps the original command binding, approval
policy, quorum and different-model requirement; another eligible reviewer can
inspect the request and complete its review.
The requester can withdraw a pending, approved or escalated request using
slb cancel <request-id> --session-id <requester-id>. Cancellation checks ownership
and current state in one transaction. It cannot overwrite an executing or terminal
request. Existing signed reviews remain available in history, and the durable
event journal records the transition with the original policy and review counts.
Withdrawing an escalated request also resolves that request's block on session
identity rotation. Escalated rows do not count as pending quota or re-enter pending
timeout scans merely because they remain visible in reviewer queues.
Terminal States
Once a request reaches a terminal state, no further transitions are allowed:
- EXECUTED: Command completed successfully
- EXECUTION_FAILED: Command returned non-zero exit code
- TIMED_OUT: Command exceeded execution timeout
- CANCELLED: Request was cancelled by the requester
- REJECTED: Request was rejected by a reviewer
Approval TTL
Approvals have a time-to-live to prevent stale approvals:
- Standard requests: 30 minutes (configurable)
- CRITICAL requests: 10 minutes (stricter by default)
If an approval expires before execution, submit a new request for review.
The decisive review loads the current TTL after obtaining the database writer reservation and reauthenticating any delegated reviewer. Execution also applies the current TTL to the original approval time: the effective deadline is the earlier of that result and the recorded approval expiry. Shortening the TTL can therefore revoke an outstanding approval; lengthening it cannot extend the recorded grant. SLB repeats this check during the atomic execution claim, even if a SAFE rule now matches the command. These checks do not rewrite review history. A reviewed request with a missing or invalid approval timestamp must be submitted again for fresh review.
Execution Verification
Before a reviewed command executes, its policy and supporting evidence must
verify in addition to these execution checks. same_host evidence is accepted
only for a same-host request while that policy remains enabled. The default
independent policy continues to refuse local approvals, including historical
ones. An approved status alone is never an execution permit.
Gate 1: Status Check
Request must be in APPROVED state.
Gate 2: Approval Expiry
Approval TTL must not have elapsed.
Gate 3: Command Hash
SHA-256 hash of the command must match. This ensures the exact approved command is executed, with no modifications allowed after approval.
Gate 4: Tier Consistency
Risk tier must still match (patterns may have changed since approval).
Gate 5: First-Executor-Wins
Only one executor can claim the request. Atomic database transition prevents race conditions when multiple agents try to execute.
Dry Run & Rollback
Dry Run Pre-flight
For supported literal command forms, slb gathers advisory evidence before
admitting the request:
| Requested command | Preview |
|---|---|
kubectl delete |
Forces --dry-run=client, retaining the selected resources and supported scope flags |
terraform destroy |
Runs plan -destroy -input=false, retaining variables, targets and supported -chdir selection |
rm |
Lists the literal target paths with ls -la -- |
git reset |
Inspects the requested ref, index and working-tree changes according to the reset mode |
helm uninstall |
Gets the selected release's manifest with its context and namespace |
A preview is evidence for the reviewer, not an approval or a sandbox. Use
--require-dry-run to refuse admission when a preview is unsupported, fails or
times out.
Git reset previews default to HEAD when no target is supplied. Soft resets
show the ref change; mixed/default resets also show the index changes. Hard
resets additionally show tracked working-tree changes and inventories of
untracked and ignored paths, including when invoked from a subdirectory.
Merge/keep previews label candidate differences and explain that Git's
preservation and abort rules still apply. Inventories do not simulate every
untracked-path collision, and the repository can change after inspection.
Pathspec, interactive, recursive-submodule and ambiguous reset forms are declined. Working-tree previews also decline configured clean/process filters and implicit recursive-submodule behavior. Preview diffs disable external diff, text conversion and filesystem-monitor programs, and preserve the normal time and output limits.
Enable in config:
[general]
enable_dry_run = true
Rollback State Capture
Before executing, slb can capture state for potential rollback:
[general]
enable_rollback_capture = true
max_rollback_size_mb = 100
Each execution attempt captures fresh state. Its recovery path is recorded atomically with the successful execution claim, so a refused or competing attempt cannot replace the winning execution's snapshot. Retrying after a failed preflight captures the current state again. An execution without capture does not inherit a snapshot left by an earlier preflight.
Captured state includes:
- Filesystem: Tar archive of affected paths
- Git: HEAD commit, branch, dirty state, untracked files
- Kubernetes: Private JSON manifests of explicitly named resources, bound to their context, namespace, API server and kubeconfig fingerprints
Rollback:
slb rollback <request-id> # Restore captured state
slb rollback <request-id> --force # Force overwrite
Request-based rollback requires the request's selected project (-C when
needed). Equivalent filesystem aliases are accepted without changing stored
project identity. Before restoration, the snapshot must match the request ID,
project, raw command and working directory. New metadata version 2 also binds
the complete command hash, including argv and shell mode. Historical version 1
metadata keeps its original identity checks and is not rewritten into stronger
evidence. --force does not bypass project, identity or version checks.
Both capture and recovery enforce a 4 MiB metadata limit. Recovery reads a
bounded regular metadata file and uses the selected snapshot directory for its
archive and patches; a copied metadata path cannot select another capture.
These are local consistency checks, not reviewer authentication or a new
execution authorization. A failed restoration does not mark the request as
rolled back. Explicit Git --from recovery retains its separate database-free
workflow and required --force acknowledgement.
Filesystem recovery validates the complete archive and all destination paths
before changing live files. Damaged gzip data, incomplete payloads, unsupported
entries, conflicting roots and symlinked destination ancestors are refused;
--force does not waive these checks. Valid version-1 snapshots, empty captures
and identical overlapping targets retain their existing format. Decoding is
bounded by the recorded payload size, 100,000 entries and 64 MiB of archive
metadata/padding. Stop concurrent writers before restoring: these checks are
preflight validation, not an atomic filesystem transaction, and a later I/O
failure or concurrent path change can still interrupt restoration.
Kubernetes capture supports literal kubectl delete commands with named
type/name or type name targets. Context, kubeconfig and namespace flags can
appear before or after delete; ordinary force, wait, grace-period, cascade and
timeout controls are supported. Selectors, --all, all namespaces, file/stdin
or kustomize selections, and unsupported connection/authentication overrides
are refused instead of producing a partial backup. A file-backed kubeconfig is
required, and kuberc preferences must be disabled when present.
Recovery validates the entire manifest set and the original configuration
before writing to the captured target. It creates resources by default;
--force permits applying over existing resources but does not bypass target
or integrity checks. Changed kubeconfig files, including context or credential
changes, require deliberate target reconciliation. Historical manifests without
target bindings remain available on disk for manual recovery; they are not
automatically applied to the current cluster.
Kubernetes snapshots cover at most 256 named objects, 16 MiB per object and
100 MiB total; a lower max_rollback_size_mb is honored. Resource manifests can
contain application secrets and are stored in private directories/files
(0700/0600). Kubeconfig contents and authentication material are not copied
into metadata. Recovery restores the named objects, not namespace contents,
cascaded dependents, ownership graphs or persistent-volume data. It is not an
atomic cluster-wide transaction: concurrent changes and partial API failures
still require inspection.
Daemon Architecture
The daemon provides real-time notifications and execution verification.
IPC Communication
Primary communication uses Unix domain sockets:
/tmp/slb-<hash>.sock
The socket and PID file (/tmp/slb-<hash>.pid) use the same hash of the discovered project root, allowing independent project daemons to coexist. Both files use the system temporary directory, including TMPDIR when set. Use -C /path/to/project with slb daemon start, stop, or status to select a project. Selection falls back to SLB_PROJECT, then the working directory; all three resolve symlink aliases and discover the nearest ancestor containing .slb, so commands also work from nested directories. If no ancestor contains .slb, the selected directory is the project root.
Lifecycle commands manage the local project daemon regardless of SLB_HOST. Companion .pid.lock and .sock.lock files remain after shutdown by design; leave them in place so daemon ownership remains coordinated across starts and stops.
When upgrading from a version with the shared per-user PID file, stop any existing daemon through its original foreground session or process manager, then restart it with the new version. The socket naming format and client IPC remain compatible. The new stop command requires project-aware shutdown support and deliberately refuses to signal a process from the old shared PID file.
JSON-RPC Protocol
All daemon communication uses JSON-RPC 2.0:
{"jsonrpc": "2.0", "method": "hook_query", "params": {"command": "rm -rf /"}, "id": 1}
Available methods:
hook_query- Classify command and check approvalshook_health- Health check with pattern hashverify_execution- Check execution gatessubscribe- Subscribe to request events
TCP Mode (Docker/Remote)
For agents in containers or remote machines:
[daemon]
tcp_addr = "0.0.0.0:9876"
tcp_require_auth = true
tcp_allowed_ips = ["192.168.1.0/24"]
Timeout Handling
When a request's approval window expires:
| Action | Behavior |
|---|---|
escalate |
Transition to ESCALATED, notify humans (default) |
auto_reject |
Automatically reject the request |
auto_approve_warn |
Auto-approve CAUTION tier with warning notification |
[general]
timeout_action = "escalate"
Desktop Notifications
Pending-request desktop alerts use macOS AppleScript or Linux notify-send:
[notifications]
desktop_enabled = true
desktop_delay_seconds = 60 # Wait before first notification
Desktop and webhook delivery are tracked independently. A failed destination is retried with backoff starting at 15 seconds and capped at five minutes, with a shared budget of 60 attempted sends per minute. Successful destinations are not resent while the daemon remains running, and pending-alert retries stop when the request leaves the pending state. This suppression state is in memory, so a daemon restart can repeat a still-pending alert.
Desktop helpers have a five-second deadline, bounded output capture and a one-second pipe-drain limit. Daemon cancellation interrupts ordinary helper process groups instead of waiting indefinitely. Pending desktop alerts are not implemented on Windows; timeout alerts retain their separate PowerShell path. Notifications remain advisory and never count as approval evidence.
Advanced Configuration
Cross-Project Reviews
Allow reviewers from other projects:
[general]
cross_project_reviews = true
review_pool = ["agent-a", "agent-b", "human-reviewer"]
Trusted Self-Approval
This legacy setting does not enable production self-approval: the default
independent policy still needs an external identity authority, and same_host
always requires another agent. A trusted name cannot supply a peer review.
[agents]
trusted_self_approve = ["senior-agent", "lead-developer"]
trusted_self_approve_delay_seconds = 300 # 5 minute delay
Conflict Resolution
When approvals and rejections conflict:
[general]
conflict_resolution = "any_rejection_blocks" # Default
# Options: any_rejection_blocks | first_wins | human_breaks_tie
Both approvals and rejections use the current conflict settings when the vote is committed. An eligible reviewer who has not already voted may reject an approved request until its execution or authorization claim is committed:
| Current policy | Effect of a later rejection on an approved request |
|---|---|
any_rejection_blocks |
Changes the request to rejected |
human_breaks_tie |
Changes the request to escalated |
first_wins |
Keeps an earlier affirmative decision approved, with its original approval timestamp and expiry |
The vote and claim compete for the same database writer reservation. A rejection that commits first is considered by the claim; once the claim commits, further review is refused. This boundary precedes process startup and also applies to native Git authorization. A reviewer cannot replace a previous vote, and further approvals cannot refresh an already approved request.
An expired approval may still receive an objection, but that objection cannot revive it. Changing conflict settings and then submitting a negative vote cannot newly approve a pending or escalated request; resolving it to approved requires a fresh affirmative review. Opting out of same-host approval does not prevent an otherwise eligible reviewer from rejecting an outstanding request, and the review retains the request's original assurance category.
CLI JSON always includes request_status alongside the vote's decision and
assurance. The change-only fields still indicate whether this vote changed the
request's state. CLI, TUI, and review notifications distinguish a recorded vote
from the resulting request status, including a nondecisive first_wins rejection.
Different Model Requirement
Require reviewers to report a different AI model:
[general]
require_different_model = true
different_model_timeout = 300 # Escalate to human after 5 min
In same_host mode this compares session model labels. It helps cooperating
agents choose another model, but does not authenticate which model produced a review.
Rate Limiting
Prevent request floods:
[rate_limits]
max_pending_per_session = 5 # Max concurrent pending requests
max_requests_per_minute = 10 # Rate limit per session
rate_limit_action = "reject" # reject | queue | warn
Queue mode waits for capacity before creating a request. Use --queue-timeout
to bound that wait separately from --timeout, which bounds waiting for a
review decision. general.request_timeout is measured in seconds without
rounding to minutes, and the new request receives its full lifetime after
admission preparation.
Admission also resolves the requester's expired pending work without requiring a daemon. It applies the current timeout policy and records the resulting timeout or escalation; it never treats expiration as a peer approval. Pending capacity can then be reused, while the per-minute request count remains intact. Other requesters' work is unaffected, and invalid policy stops admission.
Dynamic Quorum
Scale approval requirements based on active reviewers:
[patterns.critical]
dynamic_quorum = true
dynamic_quorum_floor = 2 # Minimum approvals even with few reviewers
Webhook Notifications
Send events to external systems:
[notifications]
webhook_url = "https://slack.com/webhook/..."
Payload includes request details, classification, and event type.
Security Design Principles
Defense in Depth
slb implements multiple checks for the selected review policy. In same_host
mode they prevent workflow mistakes, not malicious actions by the same OS user:
- Pattern-based classification - First line of defense
- Peer review requirement - Human/agent oversight
- Command hash binding - Tamper detection
- Approval TTL - Prevent stale approvals
- Execution verification gates - Pre-execution checks
- Audit logging - Full traceability
Cryptographic Guarantees
- Command binding: SHA-256 hash computed at request time, verified at execution
- Review signatures: HMAC-SHA256 keyed with the digest of the reviewer's session key. Same-host evidence uses a separate signature format that binds the decision to the exact command and review policy. Relabeling the policy or replacing the command invalidates that evidence. A holder of the signing material, including a process that can read the database, can still forge local reviews; a signature does not establish an independent principal.
- Session keys: Generated per session and shown once, by
slb session start(or byslb session resumewhen it creates or rotates a session). SQLite stores only a SHA-256 digest of each key, and supplied keys are compared with it in constant time, so a copy or backup ofstate.dbdoes not contain a key slb will accept. Upgrading converts existing keys in place and then rebuilds the file, so no old copy of a key is left in it; agents keep using the keys they hold. Hashing protects copies of the database. It does not separate agents that run as the same OS user: such a process can read another agent's key from its environment, files or terminal, write the database, or run commands directly. See the identity boundary. - Session recovery: Resuming an existing session requires its original
--session-idand--session-key, including with--force. Names, programs, and models are not recovery credentials. slb cannot return a lost key, because it does not have it; start a new session instead.
Fail-Closed Behavior
When components fail:
- Daemon unreachable, or no answer within
integrations.hook_query_timeout_ms(default 250ms) → the hook classifies locally: dangerous/critical commands are blocked and commands no pattern covers require confirmation. The audit source ishook_native_timeoutfor a deadline andhook_native_offlineotherwise - Parse error → Upgrade tier by one level
- Approval expired → Require new approval
- Hash mismatch → Reject execution
Audit Trail
Every action is logged to SQLite with:
- Timestamp
- Actor (session ID, agent name)
- Action type
- Request/review details
- Approval policy and review assurance (same-host workflow reviews remain labeled)
- Outcome
Query history:
slb history [--days 7] [--session <id>] [--status executed]
Environment Variables
All config options can be set via environment:
| Variable | Description |
|---|---|
SLB_APPROVAL_POLICY |
independent (default) or explicitly opt into low-assurance same_host review |
SLB_MIN_APPROVALS |
Minimum approval count |
SLB_REQUEST_TIMEOUT |
Request timeout in seconds |
SLB_TIMEOUT_ACTION |
What to do on timeout |
SLB_DESKTOP_NOTIFICATIONS |
Enable desktop notifications |
SLB_WEBHOOK_URL |
Webhook notification URL |
SLB_DAEMON_TCP_ADDR |
TCP listen address |
SLB_TRUSTED_SELF_APPROVE |
Comma-separated trusted agents |
SLB_HOOK_QUERY_TIMEOUT_MS |
Native hook daemon-query deadline in ms (10-10000, default 250) |
Agent Event Streaming
The slb watch command provides real-time event streaming for agent workflows.
Event Stream Format
Events are streamed as newline-delimited JSON (NDJSON) for easy programmatic consumption:
slb watch --session-id <id>
Selected fields from the event stream:
{"event":"request_pending","request_id":"abc123","status":"pending","risk_tier":"dangerous","approvals":0,"min_approvals":1,"approval_policy":"same_host","review_notice":"same-host peer review (low assurance; not independently authenticated authorization)"}
{"event":"request_approved","request_id":"abc123","status":"approved","approvals":1,"min_approvals":1,"approval_policy":"same_host"}
{"event":"request_executed","request_id":"abc123","status":"executed","exit_code":0,"approval_policy":"same_host","execution_assurance":"same_host"}
Request events also carry the project, redacted command display, command hash,
requester, rejection count, original created_at and any resolved_at timestamp.
observed_at records when the state was observed; it does not replace creation
time. The approval policy and its review_notice remain present throughout the
workflow, with stored execution assurance when available. Submitted vote counts
are display information, not verified execution permission. Use slb execute to
apply the current policy and verify the evidence.
Event Types
| Event | Description |
|---|---|
request_pending |
Request awaiting review, including changed vote counts |
request_approved |
Request was approved |
request_rejected |
Request was rejected |
request_executing |
Execution was claimed |
request_executed |
Command completed successfully |
request_execution_failed |
Execution failed |
request_timed_out |
Command execution timed out |
request_timeout |
Request timed out waiting for approval |
request_escalated |
Request was escalated |
request_cancelled |
Request was cancelled |
Transport Modes
Daemon IPC (preferred): Real-time streaming via Unix socket subscription when the daemon is running.
Polling fallback: If the daemon is unavailable, the command falls back to database polling at configurable intervals:
slb watch --poll-interval 5s
Both transports start with the selected project's unresolved requests and
completions from the last 15 minutes. A lost daemon connection emits
watch_degraded before polling resumes from the current database state.
Previously observed active requests remain tracked through their final state,
including an outcome older than the normal window after a long disconnect.
Old terminal requests then retire from the live watch state. Snapshots larger
than 10,000 requests fail explicitly instead of silently dropping records.
Default local discovery uses the nearest physical project containing .slb,
including from a subdirectory or symlink, and pins that project's database for
fallback. An explicit --db selects local polling and keeps that database and
the selected project's path spelling throughout the watch. It is never replaced
by a daemon's database. Missing databases produce an error rather than creating
an empty project. When SLB_HOST selects a remote daemon, a lost or unavailable
connection returns an error without switching to local records.
--auto-approve-caution requires a local database.
Consumers should replace state by request ID and tolerate repeated updates.
Rapid transitions can coalesce: this feed describes current state. For every
journaled lifecycle change and resumable replay, use slb events --follow and
save the returned cursors after processing.
Auto-Approve Mode
For reviewer agents, auto-approve CAUTION tier requests:
slb watch --session-id <id> --auto-approve-caution
Request Attachments
Requests can include attachments to provide context for reviewers.
Attachment Types
| Type | Description |
|---|---|
file |
Text file contents with source and size metadata |
git_diff |
Diff contents detected from a file |
screenshot |
Image data URI with validated dimensions |
context |
Output and result metadata from a policy-checked context command |
Adding Attachments
# Attach existing evidence without executing it
slb request "DROP TABLE users" --session-id "$REQUESTER_ID" \
--reason "..." --attach-file ./schema.sql
# Attach an image
slb request "kubectl delete deployment nginx" --session-id "$REQUESTER_ID" \
--reason "..." --attach-screenshot ./dashboard.png
# Capture context only when this exact command is allowed by current SAFE policy
slb request "terraform destroy" --session-id "$REQUESTER_ID" \
--reason "..." --attach-context "pwd"
The same flags work with slb run. File and screenshot attachments are validated
before context subprocesses start. Context collection validates the active
requester, project and selected --config, then requires every context command
to classify entirely as SAFE. Unknown commands, opaque shell execution and
commands requiring review are refused before the batch runs. A SAFE program
with a file-writing redirection also fails this check. pwd in the example
therefore needs an explicit matching SAFE rule in the selected project policy;
it is not a built-in exemption.
Each subprocess receives another fresh policy/session check and runs in the
request's working directory. Its attachment records that directory, the command
hash and policy_check: "safe". Request admission reloads configuration after
collection. Context output supplies evidence, not an approval or execution
assurance for the main command. SAFE rules are explicit operator policy, not a
sandbox or a guarantee that a program has no side effects.
For a context command that needs review, use the ordinary slb run workflow
first, save its output, and attach that file to a new request. A pending request
cannot authorize its own context-gathering commands.
Attachment Limits
The CLI uses a 1 MiB limit per file or image, a 100 KiB combined context-output
limit per command, a 10-second context runtime, and a 4096-pixel maximum image
dimension. These are the attachment collector's defaults; there is no
[attachments] configuration section.
Viewing Attachments
slb show <request-id> --with-attachments
Session Management
Sessions track agent identity and activity for audit and coordination.
Session Lifecycle
# Start session (prints session_key once; slb keeps only its digest)
slb session start --agent "GreenLake" --program "claude-code" --model "opus"
# Resume after a crash: present the original session ID and key
slb session resume --agent "GreenLake" --session-id <id> --session-key <key>
# Change model: returns a NEW session ID and key
slb session resume --agent "GreenLake" --session-id <current-id> --session-key <current-key> --model "codex"
# Change program: also rotates identity; an omitted model keeps its current value
slb session resume --agent "GreenLake" --session-id <current-id> --session-key <current-key> --program "codex-cli" --force
# Heartbeat (update last_active for GC)
slb session heartbeat --session-id <id>
# End session gracefully
slb session end --session-id <id>
Resuming an active session with omitted or unchanged program/model metadata
keeps its ID and key and refreshes its activity timestamp. Changing its model,
or making a permitted program change, atomically ends the old session and creates
a replacement with a fresh ID and key. Omitted metadata keeps its existing value.
Changing an established program requires --force; the original session ID and
key are still required. Always use the ID and key returned by resume for
subsequent work.
Identity rotation is refused while the session owns a pending, approved, or
escalated request. Finish that work with the original session, or cancel requests
that can be cancelled, before changing its program or model. The refused rotation
leaves the original session unchanged, and requests are never transferred to the
replacement identity. A command that has already claimed execution can finish
under its original session.
The old session's identity and signing-key digest remain available for historical review verification. Earlier reviews retain their original agent, model, signature, and approval policy; the normal expiration and execution checks still apply. A new session ID does not give the same agent another quorum vote or permission to review its own request.
Session Garbage Collection
Clean up stale sessions from crashed agents:
# Show what would be cleaned (dry run)
slb session gc --dry-run --threshold 30m
# Clean sessions inactive > 2 hours
slb session gc --threshold 2h --force
# Interactive cleanup (prompts for each)
slb session gc --threshold 1h
Cleanup rechecks that each candidate is still active and older than the same cutoff when it ends the session. A heartbeat or successful resume that makes a candidate current causes cleanup to skip it. Ended sessions retain their historical identity records and review evidence; cleanup does not delete them.
Rate Limit Reset
Reset rate limits for a session (admin use):
slb session reset-limits --session-id <id>
Emergency Override
emergency-execute bypasses peer review with acknowledgment and logging. A
same-UID agent can supply that acknowledgment too; it does not authenticate a
human or supply independent authorization. Use the explicit same-host workflow
when a second session should inspect the command before execution.
Usage
# Interactive (prompts for confirmation)
slb emergency-execute "rm -rf /tmp/broken" --reason "System emergency: disk full"
# Non-interactive (requires hash acknowledgment)
HASH=$(echo -n "rm -rf /tmp/broken" | sha256sum | cut -d' ' -f1)
slb emergency-execute "rm -rf /tmp/broken" --reason "Emergency" --yes --ack $HASH
Safeguards
- Mandatory reason: Must provide
--reasonexplaining the bypass - Hash acknowledgment: Non-interactive use requires command hash via
--ack - Extensive logging: Command, reason, timestamp, and operator identity logged
- Rollback capture: Optional state capture with
--capture-rollback
Audit Entry
Emergency executions create a permanent audit record:
{
"type": "emergency_execute",
"command": "rm -rf /tmp/broken",
"command_hash": "abc123...",
"reason": "System emergency: disk full",
"operator": "human",
"timestamp": "2026-01-03T10:30:00Z",
"exit_code": 0
}
Outcome Tracking
Record execution feedback to improve pattern classification over time.
Recording Outcomes
After execution, record whether the command caused problems:
# No problems
slb outcome record <request-id>
# Problems occurred
slb outcome record <request-id> --problems --description "Deleted wrong files"
# With rating and notes
slb outcome record <request-id> --rating 4 --notes "Worked as expected"
Viewing Outcomes
# List recent outcomes
slb outcome list
# Only problematic executions
slb outcome list --problems-only --limit 50
# Statistics summary
slb outcome stats
Statistics Output
{
"total_executions": 150,
"problematic": 3,
"success_rate": 0.98,
"by_tier": {
"critical": {"total": 10, "problems": 1},
"dangerous": {"total": 50, "problems": 2},
"caution": {"total": 90, "problems": 0}
}
}
This data enables:
- Identifying patterns that should be upgraded/downgraded
- Detecting agents that frequently cause problems
- Improving justification quality requirements
TUI Dashboard
The interactive terminal UI gives human reviewers an at-a-glance view of pending requests and agent activity.
Launching
slb tui
# Review the same alternate database selected by the requesting agents
slb --db /path/to/review-state.db tui \
--session-id "$REVIEWER_ID" --session-key "$REVIEWER_KEY"
The dashboard shows its selected database. --db applies to the dashboard,
request details, periodic refresh, history, reviews and pattern actions. Relative
database and config paths are resolved before the UI starts. A missing or
unreadable selected database causes an error before terminal startup; the TUI
does not create it or switch to the default store.
Project selection remains separate: request views and history searches include only that project's records, even when the database contains several projects. Pattern proposals belong to the selected database. Without reviewer credentials the request detail view is read-only; supplying credentials preserves the usual policy, quorum, signature and self-review checks. Submitting a review never executes the command.
Layout
┌─────────────────────────────────────────────────────────────────────┐
│ SLB Dashboard │
├─────────────────┬───────────────────────────────────────────────────┤
│ AGENTS │ PENDING REQUESTS │
│ ─────── │ ──────────────── │
│▸ GreenLake │▸ abc123 CRITICAL rm -rf /etc BlueLake 2m │
│ BlueLake │ def456 DANGEROUS git reset --hard GreenLake 5m │
│ RedStone │ ghi789 CAUTION npm uninstall RedStone 10m │
│ │ │
├─────────────────┴───────────────────────────────────────────────────┤
│ ACTIVITY │
│ ──────── │
│ 10:30:15 GreenLake approved abc123 │
│ 10:28:42 BlueLake requested def456 (DANGEROUS) │
│ 10:25:00 RedStone executed xyz999 (exit 0) │
└─────────────────────────────────────────────────────────────────────┘
Keyboard Navigation
| Key | Action |
|---|---|
Tab |
Cycle focus between panels |
↑/↓ |
Navigate within panel |
Enter |
View selected request details |
a / r |
Open approval / rejection form in request details |
Ctrl+S |
Submit the review form |
Esc |
Cancel a form, or leave request details |
F5 / Ctrl+R |
Refresh request details outside a form |
m |
Open pattern management |
H |
Open history view |
q |
Quit |
Panel Details
Agents Panel: Active sessions with last activity time and pending request count.
Pending Panel: Requests awaiting approval, sorted by urgency (CRITICAL first).
Activity Panel: Real-time feed of approvals, rejections, and executions.
History & Search
Browse and search the full audit history.
Full-Text Search
# Search commands
slb history -q "rm -rf"
# Search with filters
slb history -q "database" --tier critical --status executed
Filtering
# By status
slb history --status pending|approved|rejected|executed|cancelled
# By tier
slb history --tier critical|dangerous|caution|safe
# By agent
slb history --agent "GreenLake"
# By date
slb history --since 2026-01-01
slb history --since 2026-01-03T10:00:00Z
# Combined
slb history --tier critical --status executed --since 2026-01-01 --limit 100
Detailed View
# Show full request details
slb show <request-id>
# Include all information
slb show <request-id> --with-reviews --with-execution --with-attachments
Local Git Journal Export
SLB can copy the selected project's durable request-event metadata to a local Git repository. Configure the destination explicitly:
[history]
git_repo_path = "/path/to/slb-audit"
auto_git_commit = true
The default destination is empty, so no Git export runs by default. Relative destinations resolve from the owning project root. A new destination is initialized as its own repository; an existing destination must be an exact repository root, including a bare repository. SLB refuses to accidentally use an ancestor repository.
With a configured destination and auto_git_commit = true, the project daemon
replays committed events at startup and exports new events while running. It
also catches up after downtime. Start the daemon with the same --config when
using an explicit policy file. Restart it after changing the history destination
or automatic-export setting. Export failures are logged and retried; the source
journal remains available and request, review and execution outcomes are unchanged.
Manual export works without a daemon and with automatic export disabled:
slb history sync --limit 100 --timeout 30s --json
slb --config /path/to/policy.toml history sync --json
Each invocation exports one page of up to --limit events (1–1000; default 100).
Repeat while has_more is true. Output includes repository, ref, commit,
events, last_sequence, cursor and has_more. The default timeout is 30
seconds. The source database must already exist; synchronization does not create
or migrate it.
Each project uses a dedicated refs/slb/history/<project-hash> ref containing
events/<sequence>.json and checkpoint.json. Events and their resume checkpoint
are published in the same Git commit. Retries resume from that checkpoint, and
concurrent exporters compare the previous ref before publishing. A repeated sync
with no new events creates no additional commit. Use the returned ref with
git log or git show to inspect the export; HEAD, staged files, the working tree
and author configuration remain unchanged. SLB does not push to a remote.
The export preserves the journal's original policy, review/execution assurance, status, outcome and historical-baseline labels. It excludes raw commands, free-form evidence, attachments, session keys, signatures and execution receipts. SQLite remains the authoritative store: this metadata copy is neither a complete database backup nor independent authorization. Separate emergency and blocked-hook ledgers are outside this export. A corrupt checkpoint or a different source journal is refused instead of silently resetting the cursor; reconcile the source or select a fresh destination before resuming.
Agent Mail Integration
SLB can tell reviewers about requests through MCP Agent Mail. There are two routes, and there is no CLI command for sending a notification by hand: the notices follow request lifecycle changes.
Request lifecycle notifications (daemon, recommended)
Since v0.5.0 the project daemon can deliver committed request, review, status
and execution changes from its request journal to Agent Mail (HTTP MCP) and/or a
JSON webhook. Delivery survives daemon downtime and retries failed sends. It is
opt-in and configured in the project's .slb/config.toml or an explicitly selected
--config file:
[notifications.requests]
enabled = true
agent_mail_url = "http://127.0.0.1:8765/mcp/"
agent_mail_sender = "BlueLake" # an existing, registered identity
agent_mail_recipients = ["GreenCastle", "RedStone"] # identities the sender may contact
- The Agent Mail project key is the daemon's absolute project path, and the
thread is
SLB-Requests. - SLB does not register identities or change contact policies: the sender and recipients must already exist in that project and be allowed to message each other.
integrations.agent_mail_enableddoes not turn this worker on; only[notifications.requests] enableddoes.- Restart the project daemon after changing these settings.
- When using
--config /path/to/review-policy.toml, pass that file to requests, reviews, execution anddaemon start. The background daemon and execution workers retain the selected configuration. A running daemon is not reconfigured by another command's flag. - Notices carry request metadata (ID, risk tier, status, review counts, exit
code), never the command text or justification. Reviewers run
slb review <request-id>for the details.
docs/REQUEST_NOTIFICATIONS.md covers webhooks, templates, retry and replay semantics. Alerts for commands that a hook blocked are a separate opt-in route, described in docs/BLOCKED_NOTIFICATIONS.md.
Legacy CLI notifications
Without request-journal delivery, SLB sends a best-effort message when a request is created, approved, rejected or executed:
[integrations]
agent_mail_enabled = true # default
agent_mail_thread = "SLB-Reviews" # default
| Event | Importance |
|---|---|
| New CRITICAL request | urgent |
| New DANGEROUS request | normal |
| New CAUTION request | low |
| Request approved | normal |
| Request rejected | normal |
| Request executed | low |
These messages are sent with the mcp-agent-mail CLI to SLB-Broadcast (sender
SLB-System unless one is configured), with a two-second timeout. If that CLI is not installed they
are skipped silently, and a failed send never blocks the request. Enabling
[notifications.requests] turns this route off, so the journal worker is the
single source of request notices. Route selection uses the command's effective
configuration, including --config; a second project-config load cannot silently
restore legacy sends.
Output Formats
All commands support structured output for programmatic use.
JSON Mode
# Global flag
slb --output json pending
# Per-command
slb pending --json
slb history --json
slb session list --json
Output Examples
Pending requests (JSON):
{
"requests": [
{
"id": "abc123",
"status": "pending",
"tier": "critical",
"command": "rm -rf /etc",
"requestor": "GreenLake",
"created_at": "2026-01-03T10:00:00Z",
"approvals": 0,
"required_approvals": 2
}
],
"count": 1
}
Session start (JSON):
{
"session_id": "sess_abc123",
"session_key": "key_xyz789",
"agent_name": "GreenLake",
"program": "claude-code",
"model": "opus",
"project_path": "/home/user/myproject",
"started_at": "2026-01-03T10:00:00Z"
}
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Invalid arguments |
| 3 | Request not found |
| 4 | Permission denied |
| 5 | Timeout |
| 6 | Rate limited |
Planning & Development
- Design doc:
PLAN_TO_MAKE_SLB.md - Agent rules:
AGENTS.md - Task tracking:
bd ready(beads) - Prioritization:
bv --robot-priority
License
MIT License (with OpenAI/Anthropic Rider) — see LICENSE for details.