springtale — command-line interface for managing connectors, rules, events, vault, formations, and the daemon.
springtale
│
├── init create vault + DB, optional onboarding
├── server start run springtaled inline
├── run alias for `server start`
├── healthcheck [--url] probe /health, exit 0 on healthy
├── doctor diagnostic checks
├── fix ERROR_ID apply an auto-repair for E001..E009
├── trace [--connector --rule] real-time execution trace
├── panic emergency wipe (no confirm)
│
├── connector { list, install PATH, sign PATH, enable NAME, disable NAME, remove NAME }
├── author { add [NAME] [PUBKEY] --self, list, remove NAME }
├── rule { list, add FILE, toggle ID, run ID, update ID FILE, delete ID }
├── events [--limit N --connector NAME]
├── agent set-autonomy RULE LEVEL
├── config ai { get --scope, set --scope --type }
│
├── vault duress-setup
├── crypto rotate-vault-key
├── bot { pair-init, panic-unpair }
│
├── travel { prepare --backup-to, restore --from }
├── memory { audit, compact --max-entries N }
└── data { export [--output --encrypt], import --input, purge }
Fig. 1. CLI surface at a glance. --json, --passphrase-file, and --passphrase-command are global flags on every subcommand.
| Flag | Description |
|---|---|
--json |
Output as JSON instead of formatted tables. Declared global = true, so it parses on every subcommand — but see the note below. |
--passphrase-file <path> |
Read the vault passphrase from a file. The file must be mode 0600; anything more permissive is refused. |
--passphrase-command <cmd> |
Run <cmd> and use its stdout as the vault passphrase (for OS keychains, pass, secret managers). |
The store is always encrypted, so every store-backed command (connector, rule,
events, agent, author, config, memory, data, …) plus doctor and fix needs
the vault passphrase. With neither flag set the CLI prompts interactively on the
terminal. The passphrase never goes through argv or the environment.
springtale has 29 top-level command families
(apps/springtale-cli/src/cli.rs). They split three ways.
Daemon clients. These build an HTTP client from the config and send a
bearer token; if springtaled is not reachable they fail with
UNREACHABLE and do not fall back to the local store:
connector (except sign), rule, events, trace, memory, data,
agent, config, formation, recipe, approval, chat, session,
safety, canvas, bot settings.
Offline. These use the vault, the local encrypted store, or local files only, and work with the daemon stopped:
init, new, doctor, fix, panic, travel, vault, crypto,
cooperation, author, connector sign, bot pair-init,
bot panic-unpair.
bot panic-unpair opens the encrypted database directly on purpose, so it
still works when the daemon is down.
Neither. server start and run are the daemon — they boot
springtaled in-process. healthcheck is an unauthenticated GET /health
probe: it is not offline, it needs a running daemon, it just needs no token.
--json is a single global flag (cli.rs), rendered through
output::emit. It is threaded into: connector, rule, events,
memory, agent, config, formation, recipe, approval, chat,
session, safety, canvas, author, bot settings.
It parses but is ignored everywhere else. data always writes JSON to
stdout for export and human text to stderr for import/purge regardless;
trace always prints its own event lines; canvas --stream prints raw SSE
data: payloads and only honours --json on the non-streaming path; and
every offline / daemon-boot command (init, new, doctor, fix,
panic, travel, vault, crypto, cooperation, server, run,
healthcheck, bot pair-init, bot panic-unpair, connector sign)
ignores it.
Bot persona, context window and tool policy are settings with a route and a
command — not a TOML file. springtale bot settings get and
springtale bot settings set read and write GET/PUT /bot/settings.
set takes --name, --tone, --prefix (persona), --context_window,
and a repeatable --allow (tool policy; passing any --allow replaces the
whole allow-list). It is a read-modify-write against the endpoint on
purpose: a direct store write would not be seen until the daemon restarted.
Create the data directory, vault, and database. Prompts for a passphrase interactively.
$ springtale init
Enter vault passphrase: ********
Confirm passphrase: ********
Vault created at ~/.local/share/springtale/vault.bin
Database created at ~/.local/share/springtale/springtale.db
Start the springtaled daemon inline (foreground). Useful for development.
$ springtale server start
INFO springtaled: listening on 127.0.0.1:8080
READY 8080
springtaled itself takes two flags used by the desktop shell, which runs
it as a sidecar rather than embedding a second copy of the runtime:
| Flag | Effect |
|---|---|
--bind ADDR |
Override [api] bind. 127.0.0.1:0 binds a free port; the port actually bound is what the READY {port} line reports. |
--passphrase-stdin |
Read the vault passphrase as exactly one line from stdin, ahead of SPRINGTALE_PASSPHRASE_FILE, SPRINGTALE_PASSPHRASE and the TTY prompt. Keeps it out of argv and the environment, both readable by other local processes. |
springtale run is a plain-English alias for the same thing —
springtale init cli-runner && springtale run is the documented
zero-to-running path.
Probe the daemon's /health endpoint and exit 0 on a 2xx response,
non-zero otherwise (3-second timeout). --url defaults to
http://127.0.0.1:8080, matching the springtaled default bind.
Built for container HEALTHCHECK — the distroless final image has no
wget or curl:
HEALTHCHECK --interval=30s --timeout=5s CMD ["/usr/local/bin/springtale", "healthcheck"]Run the same diagnostic checks exposed by GET /diagnostics. Reports
configuration, connectivity, capability, and schema issues with a
stable error id (E001 through E009).
$ springtale doctor
✓ vault reachable
✗ E003: connector-telegram missing bot token
✓ rule engine loaded
Apply the bundled auto-repair for a diagnostic error id. Same logic as
POST /fixes/{id}/apply.
$ springtale fix E003
Fixed E003 — connector-telegram config stub written.
Real-time execution trace — tails rule triggers, action dispatches, and sentinel verdicts. Filter by connector or rule.
$ springtale trace --connector connector-telegram
TRIG connector-telegram.message_received → rule: weather-command
ACT connector-presearch.search ok (410ms)
OUT connector-telegram.send_message ok
Install a connector from a TOML manifest file. Verifies the Ed25519 signature before registering.
$ springtale connector install ./connector-kick.toml
Installed: connector-kick v0.1.0
List all installed connectors.
$ springtale connector list
┌──────────────────────┬─────────┬─────────┐
│ NAME │ VERSION │ ENABLED │
├──────────────────────┼─────────┼─────────┤
│ connector-kick │ 0.1.0 │ true │
│ connector-telegram │ 0.1.0 │ true │
│ connector-github │ 0.1.0 │ false │
└──────────────────────┴─────────┴─────────┘
$ springtale connector enable connector-github
Enabled: connector-github
$ springtale connector disable connector-github
Disabled: connector-github
$ springtale connector remove connector-github
Removed: connector-github
Sign a connector manifest with the local vault identity. Manifest signatures are
required — connector install rejects an unsigned manifest or one signed by an
author that is not registered (see §4.5). Run author add --self once so your
own key is trusted, then sign every manifest you build.
$ springtale connector sign ./connector-kick.toml
Signed: ./connector-kick.toml (author: local)
Trusted authors are the Ed25519 public keys a manifest signature may come from.
Register a trusted author. With --self, registers the local vault identity —
the key connector sign uses — under name (default local). Without
--self, name and pubkey are both required and register a third-party key.
$ springtale author add --self
Added author: local
$ springtale author add alice <ed25519-public-key>
Added author: alice
$ springtale author list
┌───────┬──────────────────────────────────────────────┐
│ NAME │ PUBLIC KEY │
├───────┼──────────────────────────────────────────────┤
│ local │ … │
└───────┴──────────────────────────────────────────────┘
Manifests signed by a removed author fail verification on the next load.
Add a rule from a TOML or JSON file.
$ springtale rule add ./rules/stream-announce.toml
Added: stream-announce (id: a1b2c3d4-...)
$ springtale rule list
┌──────────────────┬──────────┬─────────────────────┐
│ NAME │ STATUS │ TRIGGER │
├──────────────────┼──────────┼─────────────────────┤
│ stream-announce │ enabled │ ConnectorEvent │
│ daily-backup │ enabled │ Cron │
│ pr-announce │ disabled │ ConnectorEvent │
└──────────────────┴──────────┴─────────────────────┘
$ springtale rule toggle a1b2c3d4-...
Toggled: stream-announce → disabled
Replace a rule definition from a file.
Delete a rule permanently.
Manually evaluate a rule against a synthetic trigger (dry-run).
$ springtale rule run a1b2c3d4-...
Rule: stream-announce
Matched: true
Actions: 1
Query the event log.
| Flag | Type | Default | Description |
|---|---|---|---|
--limit |
u32 |
50 | Number of events to return |
--connector |
String |
(all) | Filter by connector name |
$ springtale events --limit 10 --connector connector-kick
┌─────────────────────┬──────────────────┬──────────────────┐
│ TIMESTAMP │ CONNECTOR │ TRIGGER │
├─────────────────────┼──────────────────┼──────────────────┤
│ 2026-04-10 14:22:01 │ connector-kick │ stream_live │
│ 2026-04-10 12:05:33 │ connector-kick │ chat_message │
└─────────────────────┴──────────────────┴──────────────────┘
<rule> is a rule name or a rule id; the level is stored against the rule id.
Autonomy levels: observe, suggest, act-with-approval, act-autonomously.
A rule with no explicit setting runs at act-autonomously.
$ springtale agent set-autonomy watcher observe
Set autonomy: watcher → observe
Configure a secondary duress passphrase that unlocks a decoy vault under coercion. Both passphrases produce valid decryption paths; the vault file size is constant (131,152 bytes) regardless of which is in use.
$ springtale vault duress-setup
Enter real passphrase: ********
Enter duress passphrase: ********
Duress vault configured.
Re-encrypt the vault with a new passphrase. It opens the vault with the old passphrase, copies every entry into a fresh vault created under the new one, and rewrites the vault file. Nothing outside the vault file is touched.
$ springtale crypto rotate-vault-key
Enter current passphrase: ********
Enter new passphrase: ********
Vault re-encrypted.
No API token is rotated by this. Bearer tokens are minted from the OS
CSPRNG at login, not derived from the passphrase (see
api.md §2). What the new passphrase changes:
- The login verifier.
POST /auth/logincompares the presented passphrase against a hash the daemon computed once, at boot, from the passphrase it started with. A daemon that is already running keeps that old value in memory, so it keeps accepting the old passphrase at/auth/loginuntil it is restarted (or the vault locked and unlocked) with the new one. Restart the daemon after rotating. - Live sessions are unaffected while the daemon runs — they are keyed
by
sha256(token)in process memory, with no link to the passphrase. Locking the vault or restarting the daemon drops all of them, so every client will have to log in again with the new passphrase. - Long-lived named tokens (
POST /auth/tokens) survive rotation entirely: their hashes live in theapi_tokenstable, which this command never touches. If you are rotating in response to a compromise, revoke them explicitly withDELETE /auth/tokens/{id}.
Bot pairing management. Covers code generation and emergency revocation
for users paired through chat connectors. No chat access is required to
revoke — panic-unpair works from a recovered terminal.
Generate a pairing code for a new user. The code is displayed on the terminal only — never in chat — so the operator must convey it out of band.
$ springtale bot pair-init
Pairing code: 847-291-530 (valid 10 min)
Revoke ALL paired users and invalidate every outstanding pairing code. For emergencies — no chat access needed.
$ springtale bot panic-unpair
Revoked 4 paired users, invalidated 1 outstanding code.
Travel mode prepares Springtale for a border crossing or device inspection: encrypt a backup, wipe the local install, then restore at destination.
$ springtale travel prepare --backup-to ~/secure-backup.enc
Backup written: ~/secure-backup.enc (encrypted with vault passphrase)
Local vault + database wiped.
$ springtale travel restore --from ~/secure-backup.enc
Enter passphrase: ********
Restored: vault, database, config.
Emergency wipe. Vault key material is zeroed in memory, then the vault file is overwritten with random bytes, fsync'd, and unlinked. SQLite databases are VACUUM'd and overwritten. Completes in under 3 seconds on a 1 MB vault.
$ springtale panic
Wiping... done in 0.8s.
Limitation: on SSDs with wear levelling, residual ciphertext may survive in the flash translation layer. Full-disk encryption is the only complete mitigation — Springtale's panic wipe destroys the key, which is sufficient to make any residual data unreadable, but not to physically remove it from all blocks.
List memory sessions and their entry counts.
$ springtale memory audit
┌──────────────────┬─────────┬─────────┐
│ SESSION │ ENTRIES │ BYTES │
├──────────────────┼─────────┼─────────┤
│ telegram:alice │ 124 │ 56K │
│ telegram:bob │ 43 │ 12K │
└──────────────────┴─────────┴─────────┘
Delete oldest entries beyond the per-session cap (default 100).
Export all user data as JSON. With --encrypt, the output is encrypted with the vault passphrase.
Re-import a previously exported JSON archive. Replays rules, connector configs, and event history into the current store. The vault is untouched.
$ springtale data import --input backup.json
Imported: 12 rules, 4 connectors, 8421 events
If the input was produced with data export --encrypt, decrypt first or use springtale travel restore --from <path> instead.
CLI-only operation. Import opens the SQLite backend directly via the local
SqliteBackendand runs the runtime'simport_data()function offline. There is no corresponding HTTP API endpoint — by design, because import is a destructive write to the store that must not race with the daemon's other writers. Stop the daemon (or use--ephemeralfor the import session) before running this command.
Delete all user data (rules, events, memory, formations) without touching the vault.
Show the AI adapter configured at one level. --scope is colony,
formation <id>, or agent <rule-id>. AI is per level — there is no global
adapter and no [ai_*] TOML section; the same settings are reachable from the
dashboard and POST /config/ai/configure.
$ springtale config ai get --scope agent 7c1e…
type: ollama
model: llama3.1:8b
base_url: http://127.0.0.1:11434
--type is noop, ollama, openai, or anthropic. --api-key-stdin reads
the API key from stdin (never argv) and stores it in the vault.
$ springtale config ai set --scope colony --type ollama --model llama3.1:8b
AI adapter (colony): ollama / llama3.1:8b
$ springtale config ai set --scope formation 3 --type anthropic --model claude-sonnet-4-6 --api-key-stdin < key.txt
- [1] Configuration: configuration.md
- [2] API endpoints: api.md
- [3] Rule authoring: ../guide/rules.md
- [4] Security model: ../arch/SECURITY.md