Configuration and Storage
Spettro uses both project-local and user-global storage.
Global (~/.spettro/)
| Path | Purpose |
|---|---|
config.json |
Active provider/model, permission, token budget, auto-compact, favorites, UI state, local endpoints, thinking level. |
keys.enc |
Encrypted API keys map by provider ID. |
trusted.json |
Permanently trusted project paths. |
models.json |
Cached models.dev catalog. |
hooks.json |
Global runtime hooks fallback/default. |
lsp.json |
Optional LSP overrides; servers are auto-detected on PATH with zero config. |
memory.md |
Persistent memory: user-scope facts loaded into agent context each session. |
memory-inbox.json |
Drafted memory candidates awaiting /memory review approval (never loaded into context). |
commands/ |
Global custom slash commands (.toml / .md prompt files). |
history/<project-hash>/ |
Checkpointing shadow git repo and conversation snapshots (auto-created; reclaimable via /storage clean). |
sessions/<session-id>/ |
Session metadata, messages, tasks/todos, and agent events. |
conversations/<project-slug>/ |
Legacy conversation storage path kept for compatibility tooling. |
Project-local (<repo>/.spettro/)
| Path | Purpose |
|---|---|
PLAN.md |
Last generated implementation plan. |
allowed_commands.json |
Commands approved with “allow always” for this project. |
hooks.json |
Project runtime hooks (overrides global by (event, matcher, id)). |
lsp.json |
Optional project LSP overrides (wins over the global file per server key). |
memory.md |
Persistent memory: project-scope facts loaded into agent context each session. |
commands/ |
Project custom slash commands; override global commands on name conflict. |
index.json |
Optional project snapshot when indexer-style flow is used. |
Project root
| Path | Purpose |
|---|---|
spettro.agents.toml |
Project-specific agent manifest (fallback is built-in default). |
Security model
- API keys are not stored in plaintext in
config.json. - Keys are encrypted with AES-GCM using a derived machine/user secret.
- Override key derivation input with
SPETTRO_MASTER_KEY. - First run in a folder requires explicit trust confirmation.
Permission levels
| Level | Behavior |
|---|---|
ask-first |
Strictest flow; approval-first execution model. |
restricted |
Allows execution with policy checks and approval gating where required. |
yolo |
Least restrictive execution policy. |
Notifications
When the terminal is unfocused (or a run took more than 10 s), Spettro alerts
you on run/goal completion, on errors, and when the agent is waiting for a
command approval or an answer. Alerts go out on two channels at once: an OSC 9
terminal escape sequence — rendered as a system notification by iTerm2,
WezTerm, Ghostty, and Kitty; degraded to a terminal bell (BEL) elsewhere — and
a best-effort desktop notification (notify-send on Linux, osascript on
macOS).
config.json key |
Default | Meaning |
|---|---|---|
notifications_disabled |
false |
Set true to turn all notifications off. |
notify_quiet_sec |
5 |
Minimum seconds between notifications; events inside the window are dropped so bursts don't spam. |
Checkpointing storage
Shadow-git snapshot storage for /rewind; see Checkpointing
for how each key behaves.
config.json key |
Default | Meaning |
|---|---|---|
checkpointing_disabled |
false |
Set true to turn checkpointing (and /rewind) off entirely. |
checkpoint_max_file_mb |
20 |
Files larger than this are excluded from snapshots (recorded and surfaced on rewind). |
checkpoint_retention_days |
14 |
Checkpoints older than this are pruned when the shadow repo is opened. |
checkpoint_max_gb |
5 |
If the shadow store still exceeds this after retention, the oldest half of the remaining checkpoints is dropped. |
checkpoint_warn_gb |
2 |
One-time warning threshold for projects without their own .git (where snapshots must copy the tree). |
Storage cleanup
Session policy for /storage clean and spettro clean; see
Storage for the full artifact inventory.
config.json key |
Default | Meaning |
|---|---|---|
clean_session_age_days |
30 |
Sessions not updated within this many days become clean candidates. |
clean_keep_sessions |
5 |
The most recent K sessions per project always survive cleanup, regardless of age. |
Shell command approvals
- Shell tools run via
bash -lc(shell-exec/bash). - Some safe read-only commands are always allowed.
- In non-
yolomodes, non-default commands require approval. - Choosing "allow always" stores normalized command approvals in
.spettro/allowed_commands.json.
Web access (web-search / web-fetch / download)
web-search,web-fetch, anddownloadare built-in network tools; see Web Tools for behavior, limits, and the HTML-to-markdown engine.- In non-
yolomodes each network target requires approval; "allow always" persists targets in.spettro/allowed_network.json. - All three go through the SSRF-hardened HTTP client: only http/https, non-public IPs (loopback, private ranges, cloud metadata) blocked at dial time, max 5 redirects.
downloadadditionally honors file-write approval and OS sandbox write roots; it never leaves partial files.
Commit co-authoring (mandatory)
- Every commit Spettro produces — directly via the built-in committer or indirectly when an agent runs
git committhroughshell-exec/bash— carries the trailerCo-Authored-By: Spettro <spettro@eyed.to>. - The trailer is auto-injected by the runtime when missing. It is idempotent: if you (or the agent) already supplied the trailer, no second copy is added.
- Only the porcelain
git commitis rewritten; plumbing such asgit commit-treeis left untouched.
Media generation (xAI Grok Imagine)
grok-imageandgrok-videoare built-in tools that callhttps://api.x.ai/v1/images/generationsandhttps://api.x.ai/v1/videos/generationsrespectively.- Both look up the xAI key from the encrypted store (
x-ai/xai) or$XAI_API_KEY; configure it once via/connect x-aior by exporting the env var. - Outputs are written into the workspace. When no
pathis given, Spettro pickspublic/for Next.js projects andassets/everywhere else, slugging the prompt for the filename. - These tools are listed in
coding/codeagents by default; add them to other agents inspettro.agents.tomlif you want broader access. - When the Telegram relay is running and at least one chat is bound, every successful
grok-image/grok-videocall is also broadcast to those chats: images viasendPhoto, videos viasendVideo, falling back tosendDocumentfor files that exceed Telegram's inline-media caps (10 MB for photos, 50 MB for videos). The originating prompt becomes the Telegram caption (truncated). Upload errors surface through/telegram status.
Runtime hooks
- Hook files are JSON and can be configured globally (
~/.spettro/hooks.json) and per-project (.spettro/hooks.json). - Supported events:
PreToolUse,PostToolUse,PermissionRequest,SessionStart. - Merge precedence: global rules load first; project rules override by
(event, matcher, id). - Hooks run via
bash -lcand may emit a JSON decision line:{"decision":"allow"}{"decision":"deny","reason":"..."}
updated_argsis honored forPreToolUseshell tools.
Agent manifest
Spettro loads spettro.agents.toml from the project root if present; otherwise it falls back to built-ins.
See AGENTS.md for full schema and validation.
version = 3
default_agent = "plan"
[runtime]
default_permission = "ask-first"
default_timeout_sec = 120
sandbox_mode = "full-access" # off | read-only | workspace-write | full-access
log_tool_calls = true
# sandbox_net = "none" # all | localhost | none | ports:443,8080
# sandbox_allow_dirs = ["/data"] # extra writable roots inside the sandbox
# sandbox_allow_read_dirs = ["/opt/sdk"] # extra readable roots (e.g. toolchain caches)
OS sandbox
Spettro can confine the agent at the kernel level — Seatbelt (sandbox-exec) on
macOS, Landlock on Linux. It is opt-in and off by default; it complements
(never replaces) the approval gates. The boundary is invisible to the model:
there is no sandbox tool and no prompt advertisement, blocked operations look
like ordinary failures, and the policy is set only by the operator (CLI flag or
spettro.agents.toml) — the model cannot inspect it or request its way out.
Activation precedence: CLI flags > spettro.agents.toml > off.
spettro --sandbox workspace-write # writes -> workspace + temp; reads confined
spettro --sandbox read-only --sandbox-net none # no project/user writes, no network
spettro --sandbox-net ports:443 # TCP confined to port 443 (any host)
spettro --sandbox workspace-write --sandbox-allow-dir /data # extra writable root (repeatable)
spettro --sandbox read-only --sandbox-allow-read-dir ~/go/pkg/mod # extra readable root (repeatable)
What is guarded:
- Writes — confined identically for shell commands (kernel) and the
file-write/file-edittools (in-process check), soread-onlyis truly read-only and cannot be bypassed by writing through a file tool. Writable set: temp dirs, any--sandbox-allow-dirroots, and — inworkspace-writeonly — the workspace. - Reads — system paths stay readable so programs run, but the home tree
(other projects,
~/.ssh, credentials,~/.spettrokeys) is blocked except the workspace and any allowed roots. Toolchain caches that live in$HOME(e.g.~/go/pkg/mod,~/.cache) are blocked too, so add them with--sandbox-allow-read-dirwhen building under the sandbox. - Network —
nonedenies it,localhostallows loopback only,ports:443,8080allows those TCP ports (any host). The spettro process keeps network access for the LLM API, so the agent always reaches its model even undernet=none. - The spettro process itself — write-confined as defense-in-depth (Landlock
in-process on Linux; a one-time
sandbox-execre-exec on macOS). Its reads stay open so the in-process git committer and skill discovery keep working; the model's own read surface is confined at the shell and file-tool layers.
Platform caveats:
- Linux network confinement needs Landlock ABI v4 (kernel 6.7+) and governs TCP
only (UDP/unix sockets pass);
localhostdegrades to deny-all TCP because Landlock cannot scope rules to loopback. If the kernel cannot enforce a requested policy, sandboxed commands fail closed (exit 126). - macOS network filters are ip/port based (no hostname allowlists). Under
none, DNS and unix sockets are blocked too. - macOS parent confinement uses a
sandbox-execre-exec rather than the deprecated in-processsandbox_init, to keep theCGO_ENABLED=0cross- compiled release builds working.
