~/api ❯ git status # bash, runs now
On branch main · nothing to commit
~/api ❯ /schedule "every night at 2am, back up postgres"
✦ Nightly backup of the app database
when: every day at 02:00 (0 2 * * *)
1. [allow] pg_dump app | gzip > /backups/app.sql.gz
2. [confirm] rm -rf /backups/old/* waits in /inbox↵ approve q cancel ›scheduled #3, runs while you're logged out
02:00 phone ·Approve a12? scheduled job 3 $ rm -rf /backups/old/*[Approve][Reject]
The problem
You know the machine. Not every flag.
So you leave the terminal, search, and paste back a command you haven't fully read, onto production. Sable removes that round trip, keeps routine work running on its own, and keeps you in command.
Without Sable
Try to recall the flags
Search the web
Paste a command you haven't read
Hope, then watch it for twenty minutes
Set a cron job and forget what it does
Relearn it next month
With Sable
State the goal in plain English
Read and edit the command before it runs
Destructive ones need the word YES
Nightly jobs and watchers run while you're away
Risky steps wait for your tap, on your phone
Next time, it already knows where the logs are
>.
You stay in command. Every command is shown before it runs. Anything risky waits for your approval, in the shell, in /inbox or from your phone, and every action lands in an audit log. scp, rsync and git push never touch the model.
What it does
An operator that lives on the box.
Sable runs where the work happens, keeps working after you log off, and owns the safety layer between the model and your server.
Sable is an AI operator for Linux servers. It sits where /bin/bash used to: every line you type goes to bash or to a language model, a background daemon keeps working after you log off, and it remembers what it learns about the machine.
When a line goes to the model, the model proposes a shell command. Sable shows you that command, lets you edit it, and runs it only when you say so. Commands that match a destructive pattern need the literal word YES. Everything that runs is written to an audit log.
Why it lives on the server
Most AI terminal tools are clients on your laptop. Sable runs on the server. It sees the real filesystem, the real services and the real logs, and it writes down what it learns about that machine where the machine can reach it. The tenth deploy is one sentence.
You stay in control
No command runs unseen. The preview is an edit box you can change before pressing Enter.
Nothing breaks
scp, rsync and git push bypass the model entirely.
It gets better
Work you repeat becomes a skill, and skills that fail lose rank.
Alpha software
Replacing a login shell is a serious change. Read SECURITY.md first, and keep a second root session open the first time you install.
Installation
Install on a Linux server, or try it in a Docker playground on any machine.
Linux server
Ubuntu 22.04 or newer and Debian are tested. You need Python 3.11+ and tmux.
git clone https://github.com/Parthkomalwad/sable ~/sable
cd ~/sable && bash install.sh
Creates a virtualenv
Dependencies stay out of your system Python.
Registers the shell
Adds sable to /etc/shells and runs chsh for your user.
Sets up state
Creates the audit log and the tmux layout.
Log out and back in
A setup wizard asks which model backend to use.
To go back to bash at any time, run bash uninstall.sh.
Windows and macOS
The playground is a full Ubuntu container with tmux, the bubblewrap sandbox and a local sshd. Your checkout is mounted as the source.
.\scripts\playground.ps1 -Rebuild # Windows
bash scripts/playground.sh # macOS / Linux
In VS Code you can also open the folder and choose Reopen in Container.
Without an API key
ollama pull llama3.1 # local model, the default backend
SABLE_MOCK_LLM=1 sable # scripted replies, no API calls
Mock mode runs routing, the confirm step, sub-agents and /tour end to end with no key and no local model. It's the quickest way to see Sable work.
Your first session
Five things to try in your first ten minutes.
Run something you know
Type ls -la. It runs straight away, exactly like bash.
Ask in plain English
Type show me the biggest files here. Read the proposed command, then press Enter.
Edit a proposal
Ask for something, press e at the preview, change a flag, and run your version.
Open the sidebar
Press Ctrl+T to see what that cost and what's running.
Take the tour
Type /tour for a guided walkthrough of the rest.
Need plain bash for a while? Type /bash. Type exit to come back to Sable.
Routing
Every line is scored before anything else happens. You don't need a prefix or a mode switch.
If SSH_ORIGINAL_COMMAND is set, Sable hands the command to bash before anything else loads.
Classifier
A heuristic scores the line as bash or language. Known commands run immediately.
Ambiguity
If the score is unclear, Sable asks rather than guessing.
Orchestrator
Language goes to the agent, which returns a command or a multi-step plan as strict JSON.
Forcing a route
Input
Effect
>> text
Always sends the line to the agent
Ctrl+B
Sends the next line straight to bash
/route why "line"
Explains how a line was routed
/bash
Plain bash until you type exit
Safety model
The preview is an edit box, not a dialog. You can change a command before it runs.
edit before run
~/api ❯ clean up old docker images and free some disk
✦ Removing dangling images and stopped containers
$ docker system prune -af↵ run e edit q cancel › e
edit> docker system prune -f
✓ 0 · 1.8s
Here the model proposed -af, which also removes unused images. The user cut it to -f and ran that instead.
Policy tiers
Rules live in policy.toml as data, so you can read and audit them without reading code. If the file can't be read, Sable refuses to start rather than run unguarded.
allowRuns after preview
You still see it and can edit or cancel it.
confirmNeeds the word YES
All eleven shipped rules, each with a reason shown to you.
denyNever runs
Three shipped rules, not even with approval. Rules you add can deny more, and an admin file in /etc/sable sets a floor users can't loosen.
Shipped rules
Rule
Why it's gated (confirm unless marked)
rm-rootdeny
rm -rf / or /*, in any flag order, destroys the whole system
fork-bombdeny
Exhausts the process table until a reboot
chmod-root-777deny
Makes every file world-writable and breaks ssh and sudo
recursive-delete
Recursive delete, and the path may expand to more than intended
dd-to-device
Overwrites a disk or partition table with no undo
mkfs · fdisk-device
Formats or repartitions a device
redirect-to-sd/nvme-device
Writing into a raw disk corrupts it from the first byte
curl-pipe-shell · wget-pipe-shell
Runs whatever the server returns, unreviewed
shutdown · reboot
Over SSH, this ends your own session
iptables-flush
Can expose the host or lock you out
Other protections
SSH bypass
The first executable line of main.py. Non-interactive SSH never reaches the model.
Audit log
Every executed command is appended, with the agent and model that proposed it.
Privacy mode
Known key formats and high-entropy strings are redacted before anything is sent to a model.
Your policy & hooks
Tighten what Sable allows on your machine with a rules file and scripts that run before each command.
Your own rules
Add rules to ~/.sable/policy.toml. Each one has a regex and a tier.
Eleven rules that need YES and three that never run, in policy.toml inside Sable.
Admin floor
/etc/sable/policy.toml. Your rules can make a command stricter than this floor, never looser.
Your rules
~/.sable/policy.toml. A broken file warns and is ignored; a broken admin file stops Sable from starting.
sudo always needs YES, whatever any file says. Sable does not run its agents as root.
Hooks
Put executables in ~/.sable/hooks/. They receive JSON on stdin, the same contract as Claude Code hooks. Exit 2 blocks; any other failure is reported and never blocks.
#!/usr/bin/env python3
import json, sys
if "curl" in json.load(sys.stdin)["command"]:
print("curl is not allowed on this host"); sys.exit(2)
Hook
Runs
Exit 2
pre_command
After policy allows a command, before it runs
Blocks it
post_command
After a command runs
n/a
pre_spawn
Before a sub-agent starts
Blocks the spawn
on_skill_use
When skills are used
n/a
A hook only sees commands policy already allowed, so a hook can block a command but never unblock one.
Trust controls
What Sable does when a model reads something hostile, runs away, or needs your approval.
Untrusted output
Command output reaches the model framed as data. Reading a web page, a log or a file outside your project taints the agent: every later command needs one more step of approval.
Blast radius
The preview is coloured: green read-only, amber writes, red deletes or worse, dim when unknown.
Approvals
A sub-agent can't type YES. It queues the command and you run /approve <id>. A denied command can never be approved.
Circuit breaker
Per-job limits on tokens, cost, turns, time and repeated failures. Other sub-agents pause until /breaker reset.
$SECRET:db_pass is swapped in only when the command runs. The model and the logs see the placeholder. Needs a desktop keyring.
Limits
No layer is a guarantee. The threat model lists ten known limits, such as the orchestrator not being sandboxed and secrets only being redacted as exact matches. Read it before relying on any one control.
Agent tools
Besides shell commands, agents can call typed tools. Every call goes through the same policy, preview, hooks and audit as a command.
a tool call
~/api ❯ add a healthcheck to docker-compose.yml
⚙ Adding a healthcheck to the web service
fs.patch docker-compose.yml
- "8080:80"
+ healthcheck: + test: curl --fail http://localhost/ || exit 1↵ run q cancel ›
type YES to confirm: YES
[verify] passed
Tool
Tier
What it does
web.search
allow
Searches the web (DuckDuckGo by default, no key). Taints the agent.
web.fetch
allow
Reads a public page as text. Private, local and cloud-metadata addresses are refused, per redirect too. Taints.
fs.read · fs.search · fs.tree
allow
Read and find files inside the working directory.
fs.patch · fs.write
confirm
Edit files. You see a diff first; the write is all or nothing.
docs.man · docs.help · docs.tldr · docs.pkg
allow
Local documentation, so the model checks a flag instead of guessing it.
Check and recover
Any action that changes something must carry a verify, such as docker compose config -q. When a step fails, you see it and what happens next:
↻ step failed: {"file_exists": "docker-compose.yml"} (got: missing)
next: retry 1 of 2
↻ reflection: the file is in ./deploy, not here; using that path
After two tries it asks you (a sub-agent queues the question), and the runtime refuses a third identical command.
Going over a budget trips the circuit breaker for that job. /tools lists every tool each agent may call and at which tier.
After a web tool the agent is tainted, so each further web page or command asks for YES. That keeps a hostile page from steering what runs next.
Sub-agents
When a goal outgrows one command, the orchestrator hands it to a sub-agent. You keep your prompt.
delegation
~/proj ❯ set up a python 3.12 venv, install requirements, run tests
$ python3.12 -m venv .venv && .venv/bin/pip install -r requirements.txt… timeout after 120s[orchestrator] delegating the rest to a sub-agent
◈ spawning set-up-python-venv → tmux window
~/proj ❯ /task list
NAME STATUS STEPS CREATED
set-up-python-venv running 4 2 min ago
Sandbox
Each sub-agent runs in its own tmux window inside a bwrap sandbox. Its workspace is writable and the rest of the filesystem is read-only. Without bubblewrap installed, Sable falls back to a bash wrapper and says so.
Managing tasks
Command
What it does
/task list
Running and finished sub-agents
/task <name> attach
Jump into its tmux window
/task <name> pause · resume · kill
Control a running agent
/task guide <name> <text> · Ctrl+G
Steer it without stopping it
/task <name> replay
What the model saw on each turn, redacted
/task <name> checkpoint · revert
Save and roll back its workspace
Autonomy & phone
A background daemon, sabled, runs schedules and watchers while you're logged out, and your phone is the approve button.
sable daemon install # writes a systemd user unit
systemctl --user enable --now sabled
sudo loginctl enable-linger $USER # keep it running after logout
Schedules
/schedule "every night at 2am, back up postgres" asks the model once, now, for a cron expression and a plan. You see every step with its policy tier and approve once. At 2am the daemon runs exactly those commands. No model is called at run time, so a job can't drift or be steered by output it reads.
allowRuns
Audited like anything you run yourself.
confirmWaits in /inbox
The run pauses at that step until you approve it. A rejection fails the run.
denyFails the run
Nothing after it runs.
Watchers
/watch add disk / 90, file, log <path> <regex> and http on localhost are checked every 30 seconds and fire once per change, not every tick. A watcher can notify you, run an approved plan, or wait in the inbox before its first step.
Your phone
/notify setup connects ntfy. You get a push when a job finishes, a watcher fires, or something waits for you. An approval push has Approve and Reject buttons. Each carries a random token that works once and expires in an hour; a replayed, expired or mismatched reply is refused and audited. The daemon polls for replies, so no port is opened on the server.
Each tool becomes mcp.<server>.<tool> and goes through the same preview, policy, audit and budget as any command. Tools start at confirm; /mcp trust lowers one tool to allow. MCP output is untrusted, so after an MCP result the rest of the goal runs one tier stricter. A stdio server gets a minimal environment and never inherits your API keys.
When a server asks you something mid-call, the shell shows [mcp files] asks: ... and prompts for each field. q declines.
Sable as a server
ssh you@host sable --mcp-serve # stdio only, nothing listens on the network
Policy says
run_command does
allow
Runs it, audited under mcp:<client>
confirm
Queues it to /inbox; after you approve, the same call runs once
deny
Refuses it
Also exposed: list_tasks, spawn_task, get_skill, search_memory.
Memory
Sable remembers what it learns about a server, shows where each fact came from, and forgets on request.
two sessions
~ ❯ where does myapp write its logs? its config is under /etc/myapp
$ cat /etc/myapp/app.confremembered: myapp logs are written to /var/log/myapp (f45a3ba393d78)# next day, a fresh login~ ❯ where does myapp write its logs on this server?
✦ The logs for myapp are located at /var/log/myapp, based on memory notes.
How facts get in
You add one with /remember deploys go out on Tuesdays. Agents add one when a goal discovers something durable, such as a path, a port or where a service logs, and you see a remembered: line each time, so nothing is saved silently. Secrets are redacted before anything is written.
Each fact is a markdown file under ~/.sable/palace/<room>/ with its sources: the session, the goal and the commands that ran. Rooms are server, user, incidents and repos/<name>.
How facts are used
At the start of a goal, matching facts are shown to the agent as notes marked as data that may be stale. A question they answer is answered from memory with no commands run. A note never grants permission, and a fact learned after reading untrusted content such as a web page is stored and shown as untrusted.
Command
What it does
/palace
Rooms and how many facts each holds
/palace server · /palace find nginx
List a room, or search every room
/palace why <id>
The fact, its tier and every source
/remember <text> [--room R] · /forget <id>
Add or delete a fact
/palace consolidate
Tidy now instead of waiting for the night
Nightly tidy-up
At maintenance_time (default 02:30) the daemon merges near-duplicate facts and keeps every source, promotes a fact seen in two separate sessions from episodic to semantic, and removes old episodic facts and anything past its expiry. It uses rules, not a model.
Doctor & moving
Check an install, upgrade an old one, and carry skills and memory to another server.
sable doctor # tools, database, config version, memory index
sable doctor --fix # migrate config (with a backup), fix permissions, reindex
sable export # skills, memory, your policy and hooks
sable import sable-export.tar.gz
sable sync git@github.com:you/sable-state.git
Doctor
Reports first. --fix backs up config.json before migrating it and never deletes your data.
What travels
Skills, memory, your policy and hooks. Never the keyring, the database, logs or config.json. Export refuses if a file holds a secret.
Safe import
Absolute paths, .., links, oversized archives and files that don't match the manifest are refused. Overwritten files are backed up first.
Rehearsal & undo
Risky changes run on a copy first, every change leaves an undo point, and a second model checks the work before it is called done.
a config change, rehearsed
~ ❯ change nginx to listen on 8080, add a header comment, keep a backup
rehearsal (on a copy; nothing real was changed)ok sed -i 's/listen 80;/listen 8080;/' /srv/site/nginx.conf
ok sed -i '1i # managed by sable' /srv/site/nginx.conf
ok cp /srv/site/nginx.conf /srv/site/nginx.conf.bak
changed /srv/site/nginx.conf added /srv/site/nginx.conf.bak
a apply q abort: a
undo point s3
reviewer: pass All three changes are in place.
Rehearsal
A change of two or more steps, whether a plan or one command chained with &&, runs first on a copy of the files it touches, inside a sandbox where the copy sits at the real path. You see each step's result and the diff, then choose apply or abort. Steps that act outside files, such as systemctl or a package install, are listed as not rehearsed rather than faked.
Undo
Command
What it does
/undo
Undo the last change in this session
/undo list · /undo 12
Every snapshot, and undo any of them, from any session
/task diff NAME · /task undo NAME
A sub-agent's last step
Undo shows the diff and asks first. Files come back byte for byte, files the step created are removed, and nothing else is touched.
Plan graphs and the reviewer
A goal with parts that can run at once becomes a graph of sub-agents with a join, shown in /dash. Before a goal that changed something is reported done, a reviewer model answers pass, concerns or fail, with a reason you see.
Step-up approval
Commands that never run, such as rm -rf /, can be allowed once from the interactive shell with a code from your authenticator app or a tap on your phone (/stepup setup). A code works once, three wrong codes lock it for ten minutes, and sub-agents and the daemon never get the offer.
Limits and signed skills
Sub-agents can run with no network and with memory, CPU and process caps. Skills you write or approve are signed; an imported or hand-edited skill makes commands one tier stricter, and a skill changed after signing is not used.
Skills
Do something three times and it becomes a skill. You approve it before it's used.
skills
~/api ❯ /skill list
deploy-api draft 0.50 build → migrate → restart
rotate-logs active 0.85
~/api ❯ /skill approve deploy-api
~/api ❯ deploy the api
◈ using skill deploy-api (0.50)
How a skill is born
Pattern
Sable watches the audit log for repeated sequences, and notices when a multi-step goal succeeds.
Draft
The model writes the skill as Markdown. It starts as a pending draft.
Approve
/skill show it, then approve or reject. Drafts are never injected.
Learn
Each run nudges confidence: +0.05 on success, −0.10 on failure. Retrieval ranks by it.
Format
A skill is a folder: ~/skills/<name>/SKILL.md plus any scripts, in the agentskills.io layout. It's plain text you can read and edit. A skill can carry a validator, such as a health check, that grades each run automatically.
Command
What it does
/skill list
Every skill, with status and confidence
/skill show · approve · reject
Review drafts
/skill disable
Stop a skill from being used
/skill stats
Success and failure history
Sidebar & snippets
You always know what an agent is doing and what it has cost.
Ctrl+T toggles a live tmux pane beside your prompt.
Session
Tokens and cost for this session, with daily and session budgets.
Run a saved snippet from the sidebar with the arrow keys and Enter, or open the full-screen picker with /clip and filter with /.
Plugins, hosts & sharing
Measure Sable, extend it, watch it, point it at more servers, and let a teammate approve from their phone.
another server
~ ❯ /host add b ops@10.0.0.5
~ ❯ @b df -h
on b: $ df -h↵ run q cancel
Filesystem Size Used Avail Use% Mounted on ...
~ ❯ @b rm -rf /tmp/scratch
queued on b as a1; approve it on that host
Command
What it does
sable eval
25 server tasks, each in a fresh home, checked by a script. Runs on the mock model by default; a paid model only if you name it
/skill doctor
Failing, stale, duplicate, unsigned or tampered skills, with what to do; changes nothing
/skill publish · install
Share one skill as an archive; installed skills arrive pending and unsigned
/plugin add · list · remove
A plugin is an MCP server plus optional skills and hooks; each part is shown and asked about
/host add · test, @host cmd, @all cmd
Commands on other servers over SSH, under each server's own policy
sable selfcheck
The week from Sable's own records: goals, steps and spend with and without skills; no model calls
sable share --approve-only --name alice
A teammate approves inbox items from their phone; each link works once, and the audit names them
Runbooks from incidents
When a goal that started from a problem ends with a passing check, Sable drafts a runbook into memory. The next time the same alert fires, /inbox and the phone push offer to run it, rehearsed and under policy.
OpenTelemetry
Set otel.endpoint and spans for each goal, turn, command, tool call and model call, with tokens and cost, are posted as OTLP/HTTP JSON to your collector. Off by default; text is redacted.
Commands & keys
Type /help in the shell for the full list.
Builtins
Command
What it does
/help
List every builtin
/task …
Manage sub-agents
/skill …
List, review and edit skills
/clip …
Saved snippets
/why [agent]
What the model saw when it last decided
/route why "line"
Explain a routing decision
/stats
Seven-day token table
/memory
View or clear session context
/config · /model · /mode
Settings, backend, routing mode
/bash · sable on|off|status
Drop to plain Linux and back
/tour
Guided walkthrough
/tools
Tools each agent may call, and their tier
/approve [id]
Allow what a sub-agent is waiting on
/audit [--since 1h]
Every decision: who, what, tier, outcome
/secret add · list · rm
Named secrets for $SECRET:name
/breaker [reset]
Circuit-breaker trips
/inbox [show · approve · reject KEY]
Everything waiting on you, in one list
/schedule "…" · list · pause · run-now · rm
Recurring jobs for the daemon
/watch add · list · rm
Disk, file, log and http watchers
/notify setup · test
Phone pushes and approvals over ntfy
/mcp add · list · trust · search
MCP servers and their tools
/dash
Full-screen view of agents, inbox and cost
/palace … · /remember · /forget
What Sable has learned, and from where
sable doctor [--fix]
Check and repair the install
/undo [list | id]
Put files back the way they were before a step
/stepup setup · status · off
Allow a blocked command once with a code or your phone
sable export · import · sync
Move skills and memory between servers
/budget reset · /new · /exit
Reset budget, new session, quit
Keys
Key
What it does
Ctrl+B
Next line runs as raw bash
Ctrl+T
Toggle the sidebar
Ctrl+G
Steer a running agent
Ctrl+X
Settings overlay
Ctrl+R
Reverse history search
Ctrl+\
Plain bash subshell
Tab · →
Complete; accept the inline suggestion
>>
Force agent mode for this line
Configuration
Edit settings live with /config or Ctrl+X.
Key
Default
Meaning
backend
ollama
ollama, openai or anthropic
model
llama3.1
Model name for that backend
api_base
http://localhost:11434
Ollama URL; ignored for cloud backends
routing_mode
auto
auto heuristic, or prefix for >> only
daily_token_budget
null
Warns at 80 %, stops at 100 %
session_token_budget
null
Same, per session
privacy_mode
false
Redact keys and high-entropy strings before sending
tasks_base_dir
~/tasks
Sub-agent workspaces
API keys
Set OPENAI_API_KEY or ANTHROPIC_API_KEY, or store the key in the Linux keyring. Keys are never written to the config file.
Architecture
One Python package with a layering rule CI enforces: lower layers never import higher ones.
Layer
Modules
Role
app
mainreplbuiltins/
SSH bypass, the read-route-dispatch loop, slash commands
agents
routerorchestratorworkersandbox
Routing, the multi-turn agent loop, sandboxed sub-agents
skills
watchercrystalliserindex
Pattern detection, drafting, ranked retrieval
policy
enginerulestiers
Rules as data; refuses to start if they can't be read
llm
ollamaopenaianthropic
One backend interface, streaming, JSON fallback chain
core
executordbauditevents/
pty runner, SQLite WAL, audit ledger, event bus
ui
prompt/sidebar/tmux/
Prompt, live sidebar, tmux layout
Design rules
Every command runs in a pty, so interactive programs just work.
cd is intercepted and changes the shell's own directory.
Model calls use httpx directly. There is no LLM gateway.
Model output always goes through a JSON fallback chain and never crashes the shell.
MCP, memory, doctor and portability, plan graphs, reviewer, rehearsal and undo, step-up approval, signed skills, evals, plugins, OpenTelemetry, multiple servers, runbooks and sharing