sterlai
Terminal AI agent for Stellar. Chat-driven payments, path payments, trustlines, and friendbot funding on testnet, with a separate signer daemon that prompts for explicit approval on every signature.
Package details
Install sterlai from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:sterlai- Package
sterlai- Version
0.1.14- Published
- Apr 21, 2026
- Downloads
- 1,705/mo · 230/wk
- Author
- ibrahim_1
- License
- MIT
- Types
- extension, skill
- Size
- 1.6 MB
- Dependencies
- 13 dependencies · 0 peers
Pi manifest JSON
{
"extensions": [
"./dist/extensions"
],
"skills": [
"./src/skills"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
Sterl is a terminal AI agent for the Stellar network. It runs the Pi coding agent runtime with a Stellar-specific extension that exposes accounts, balances, payments, path payments, and trustlines as LLM-callable tools - and routes every signature through a separate signer daemon that prompts the user for explicit approval.
The default target is Stellar Testnet. Mainnet is supported but the setup wizard, friendbot integration, and policy defaults assume you are starting on testnet.
Architecture
┌─────────────────┐ Unix socket NDJSON ┌────────────────────┐
│ sterl (CLI) │ ─────────────────────────────► │ sterl-signer │
│ Pi runtime │ │ daemon │
│ + extension │ ◄───────────────────────────── │ encrypted │
│ + tools │ signed XDR + sig │ Ed25519 keystore │
└────────┬────────┘ └────────┬───────────┘
│ │
│ Horizon (REST) + Soroban RPC │ PBKDF2 + AES-256-GCM
▼ ▼
stellar.org / your own Horizon ~/.sterl/keys/stellar.json
- The agent process never holds a key; it only ever holds the public address.
- The signer daemon decrypts the keystore in-memory at startup, then waits for sign requests on a Unix domain socket. Every request is summarised on stderr and requires
yto approve. - Spending is constrained by
~/.sterl/policy.yaml: per-asset per-tx and per-day caps, optional destination/issuer allowlists, and a security profile (locked/balanced/relaxed).
Install
From npm (recommended)
npm install -g sterlai # or: pnpm add -g sterlai@latest
sterl setup # one-time wizard (network, keypair, friendbot, LLM)
sterl # chat with the agent
That's it. The npm package id is sterlai (the unscoped name sterl was rejected by npm's anti-typosquatting filter), but the binary it installs is still sterl - every command in this README works as written.
The tarball ships with the prebuilt macOS Touch ID helper (universal binary covering both Apple Silicon and Intel) so per-transaction biometric approval works out of the box on macOS 12+ - no Xcode required on the install machine.
From source
If you are working from this repo (pnpm 9 is the supported package manager):
git clone https://github.com/fozagtx/sterl.git
cd sterl
pnpm install
pnpm run build
pnpm link --global
Don't have pnpm? Install with
npm install -g pnpmorcorepack enable pnpm.
Update to the latest version
npm update -g sterlai # or: pnpm up -g sterlai
npm ls -g sterlai # confirm the installed version
Quickstart (testnet)
sterl setup # network, keypair, friendbot, AND pick LLM provider in one go
sterl signer # Terminal 1: unlock keystore + run signer daemon
sterl # Terminal 2: open the chat UI
sterl setup walks you through five things on first run:
- Network (testnet/mainnet)
- Generate an Ed25519 keypair encrypted with your passphrase
- Friendbot funding (testnet only)
- macOS Keychain (Touch ID) - on macOS, optionally save your passphrase to the login keychain so the signer unlocks with a fingerprint instead of a typed passphrase. One Touch ID prompt per
sterl signerlaunch, not per transaction. Skip on Linux/Windows or decline to fall back to typed passphrase. - Model provider - pick one of:
- OAuth subscription (Claude Pro/Max, ChatGPT Plus/Pro, Copilot, Gemini) - finishes inside chat with
/login - API key - detects
ANTHROPIC_API_KEY/OPENAI_API_KEY/GROQ_API_KEY/ etc. in your env, or runsterl loginfor a guided wizard that pastes the key into~/.pi/agent/auth.jsonfor you - Local Ollama - probes
http://localhost:11434, lists your installed models, writes~/.pi/agent/models.jsonfor you - Skip - configure later with
sterl model
- OAuth subscription (Claude Pro/Max, ChatGPT Plus/Pro, Copilot, Gemini) - finishes inside chat with
You can re-run just the provider step any time: sterl model (or sterl model --ollama / --apikey / --oauth to jump straight to one path).
In the chat:
Check my balance. Send 5 XLM to alice*sterl.dev. Quote a path payment of 10 XLM into USDC. Add a trustline for USDC. Run doctor.
Other useful commands:
sterl signer status # daemon health
sterl signer # unlock keystore + run signer daemon (foreground)
sterl signer --no-keychain # force a typed passphrase even if Touch ID is enrolled
sterl signer keychain enroll # save the passphrase to macOS Keychain (Touch ID)
sterl signer keychain status # check whether the Keychain entry exists
sterl signer keychain clear # remove the passphrase from macOS Keychain
sterl pay <url> # pay an MPP-gated HTTP endpoint (Stellar Machine Payments Protocol)
sterl pay --quote <url> # show the 402 challenge without paying
sterl model # pick / re-pick LLM provider (OAuth, API key, local Ollama)
sterl model --ollama # auto-detect localhost:11434 and wire models into ~/.pi/agent/models.json
sterl login # guided wizard to save an API key (OpenRouter, Anthropic, Groq, Gemini, xAI, Mistral, Cerebras, ZAI)
sterl login openrouter # skip the picker, jump straight to OpenRouter
sterl login --list # list every supported API-key provider
sterl faucet # friendbot-fund the configured testnet wallet
sterl faucet <G…> # friendbot-fund any other testnet address
sterl fund <G…> # alias of `faucet`
sterl balance # quick balance check (configured wallet)
sterl balance <G…> # quick balance check (any account)
sterl address # print the configured public key (pipe-friendly)
sterl # plain agent launch
sterl doctor # one-shot health report
Inside the chat you also have slash commands:
/about show Sterl version & key paths
/balance balances for the configured wallet
/balance G… balances for any account
/faucet friendbot-fund the configured testnet wallet
/faucet G… friendbot-fund any testnet address
/doctor health report
/login OAuth into Claude Pro/Max, ChatGPT Plus/Pro, Copilot, Gemini, Antigravity
/model pick the active model from any provider you have credentials for
Adding LLM providers (sterl login)
sterl login is a guided wizard that saves an LLM provider API key to ~/.pi/agent/auth.json (perms 0600) without making you edit JSON or set environment variables. Pair it with the in-chat /login command (which only handles OAuth) and you can wire up every supported provider without touching a config file.
sterl login # numbered picker for every API-key provider
sterl login openrouter # skip the picker, jump to OpenRouter
sterl login anthropic # save a raw Anthropic API key (use /login for Claude Pro/Max OAuth)
sterl login --list # list every supported provider with a setup URL
sterl login --help # full usage
Supported providers (all stack into /model after login - mix freely):
| Provider | Best for | Get a key |
|---|---|---|
| OpenRouter | one key, hundreds of models (Claude, GPT, Llama, Qwen, Gemini, …) | https://openrouter.ai/keys |
| Anthropic (raw) | Claude API without subscription | https://console.anthropic.com/settings/keys |
| OpenAI | GPT models | https://platform.openai.com/api-keys |
| Groq | free tier; very fast Llama / Qwen / Kimi | https://console.groq.com/keys |
| Google Gemini | free tier; Gemini 2.x | https://aistudio.google.com/apikey |
| xAI (Grok) | Grok 4 family | https://console.x.ai |
| Mistral | Mistral Large, Codestral | https://console.mistral.ai/api-keys/ |
| Cerebras | free tier; very fast Llama 3.3 / Qwen 3 | https://cloud.cerebras.ai/ |
| ZAI (GLM) | GLM-4.6 / GLM-4.5 | https://z.ai |
The wizard validates the key prefix (sk-or- for OpenRouter, sk-ant- for Anthropic, gsk_ for Groq, etc.) and asks before overwriting an existing entry. Existing OAuth tokens from the in-chat /login are preserved - the wizard only edits the provider entry it touches.
After login, launch sterl and pick the model with /model (or cycle with Ctrl+P).
MCP server (Cursor, Claude Desktop, …)
Sterl ships an MCP (Model Context Protocol) server that exposes its Stellar toolkit to any MCP-compatible client over stdio.
sterl mcp # start the MCP server
sterl mcp --read-only # hide write tools (send_payment, swap, change_trust)
sterl mcp --help # full usage + example client config
Add this to your client config (Cursor: ~/.cursor/mcp.json, Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"sterl": {
"command": "sterl",
"args": ["mcp"]
}
}
}
What gets exposed (same set as the in-chat tools):
| Tool | Kind | Notes |
|---|---|---|
account_info |
read | Public key, network, sequence, thresholds. |
get_balances |
read | XLM + trustline balances for any account. |
fund_testnet_account |
write (faucet) | Friendbot - testnet only, no signer needed. |
send_payment |
write (signer) | Pays XLM or any asset; auto create_account if needed. |
change_trust |
write (signer) | Add or remove trustlines. |
swap |
write (signer) | Strict-send path payment with a slippage floor. |
quote_path_payment |
read | Best-path quote via Horizon. |
resolve_federation |
read | SEP-2 federation lookup. |
parse_sep7 |
read | Parse web+stellar:pay?… URIs. |
doctor |
read | Health check. |
show_policy |
read | Active Sterl policy (YAML). |
show_recent_activity |
read | Tail the audit log. |
quote_mpp_url |
read | Probe an MPP-gated URL for its 402 challenge (no payment). |
Write tools still go through the local sterl-signer daemon, which prompts for explicit y/N approval on its own stderr before any signature is produced - so the MCP client can request a transaction, but only the human at the daemon terminal can authorize it. Make sure the daemon is running first:
sterl signer
All MCP traffic is JSON-RPC over stdin/stdout; Sterl's own logs go to stderr so they never leak into the protocol stream.
Machine Payments Protocol (MPP)
Sterl ships first-class support for the Stellar Machine Payments Protocol, the open standard for HTTP-402-gated APIs that settle in SEP-41 token transfers on Soroban.
sterl pay --quote https://api.example.com/paid # see the 402 challenge
sterl pay https://api.example.com/paid # unlock keystore + pay + fetch
sterl pay -X POST -H "Content-Type: application/json" \
-d '{"prompt":"hello"}' https://api.example.com/llm
How it works:
- The first request returns
402 Payment Requiredwith a stellar-charge challenge (amount, currency SAC, recipient). sterl payprompts you for your keystore passphrase, decrypts the Ed25519 key in-process for this single request, signs the SEP-41transferper draft-stellar-charge-00, and retries withX-Payment: <signed-xdr>.- The server submits the transaction, returns
200 OKwith the resource and anX-Payment-Receiptheader. - The process exits; the key is gone from memory.
Why doesn't MPP go through the signer daemon? The official
@stellar/mppclient signs Soroban auth entries in its own process and has no hook for delegating to a remote signer. To preserve the "agent process never holds a key" rule,sterl payis intentionally a separate one-shot CLI command that you (the human) invoke directly. The chat agent has a read-onlyquote_mpp_urltool that previews the 402 challenge but tells you to runsterl payyourself to actually pay.
Inside chat, ask: "Quote the MPP price for https://...." The agent calls quote_mpp_url, shows you the cost, then you decide whether to run sterl pay in your terminal.
Faucet
sterl faucet taps Stellar's official testnet Friendbot and credits the target account with 10,000 XLM. It works in three places:
- Setup wizard - automatically offered on the first run (
sterl setup). - CLI -
sterl faucet(orsterl fund) - also reports the post-funding balance and a Stellar Expert link. - Inside the chat - ask the agent: "Top up my testnet wallet from the faucet." It calls the
fund_testnet_accounttool.
Friendbot is testnet-only; the command refuses to run on mainnet.
Configuration
| Path | Purpose |
|---|---|
~/.sterl/config.yaml |
Network, signer backend, public key, friendbot URL override, Keychain/Touch ID flag. |
~/.sterl/policy.yaml |
Security profile and spending caps. |
~/.sterl/keys/stellar.json |
Encrypted Ed25519 keystore (AES-256-GCM + PBKDF2). |
~/.sterl/signer.sock |
Unix domain socket for signer IPC. |
~/.sterl/db/sterl.db |
SQLite store for audit log + daily spending totals. |
~/.sterl/agent/ |
Pi runtime state (sessions, settings). |
Reset / start over
If you want to wipe Sterl's local state and re-run the setup wizard from scratch (new keypair, new passphrase, fresh config), paste this into a terminal that is not inside a sterl chat window:
pkill -f "sterl.*signer|cli\.js signer" 2>/dev/null # stop any running signer daemon
rm -f ~/.sterl/signer.sock # remove its unix socket
security delete-generic-password -a "$USER" -s sterl-signer 2>/dev/null # macOS only: drop the Keychain passphrase
rm -rf ~/.sterl # wipe config, keystore, policy, audit DB, pi runtime state
Then re-run:
sterl setup # or: node dist/cli.js if you're working from the repo
What each step clears:
| step | what lives there | consequence |
|---|---|---|
pkill + rm signer.sock |
running sterl-signer process + its IPC socket |
next launch creates a fresh socket. |
security delete-generic-password |
sterl-signer / $USER entry in the login keychain (biometric-gated passphrase) |
Touch ID can no longer unlock - the wizard will ask for a new passphrase and re-enroll if you opt in. |
rm -rf ~/.sterl |
config.yaml, keys/stellar.json, policy.yaml, db/sterl.db, agent/ (Pi sessions, chat history) |
irreversible - your old Ed25519 keypair is gone. On testnet this is fine; on mainnet you'd lose access to any funds tied to that address. |
What is not touched:
~/.pi/agent/auth.json- your LLM provider credentials (Anthropic key, Claude Pro OAuth token, etc.). Kept so you don't have to re-login every time. Remove it manually if you want to switch accounts.- Any global
sterlinstall or Pi cache.
If you're on mainnet or just want a safety net, snapshot first:
cp -R ~/.sterl ~/.sterl.bak-$(date +%Y%m%d-%H%M%S)
Tools the agent can call
| Tool | Purpose |
|---|---|
account_info |
Public key, network, sequence, thresholds, signers. |
get_balances |
XLM and trustline balances for any account. |
fund_testnet_account |
Friendbot faucet - funds an account with 10,000 XLM on testnet. |
send_payment |
Payment + auto-create_account for unfunded XLM destinations. |
change_trust |
Add or remove trustlines. |
quote_path_payment |
Strict-send path quote via Horizon. |
swap |
Strict-send path payment with a slippage floor. |
resolve_federation |
SEP-2 federation lookup (alice*example.com → G…). |
parse_sep7 |
Parse web+stellar:pay?… URIs (typically QR codes). |
show_policy |
Print active policy as YAML. |
show_recent_activity |
Tail the audit log. |
doctor |
One-shot health check. |
Security model
- Single-key software wallet. The keystore is encrypted at rest; the daemon decrypts it once at startup and keeps the secret in process memory only.
- The daemon is the only component that ever touches a private key. It is launched and managed independently from the agent process and you can run it under your preferred process supervisor.
- Every signing request is interactive by default - the daemon requires explicit approval on stderr before producing a signature.
- The policy engine is enforced in the agent process and double-checked by daily spending counts in SQLite. The signer is independent and will sign whatever the user approves; treat the policy as a guard against autonomous misuse, not as a hardware enforcement.
macOS Touch ID (optional)
On macOS, sterl setup offers to store your keystore passphrase in the user's login keychain. When that flag is on, the signer demands a real biometric approval at three distinct points:
- At daemon startup - before reading the Keychain, the signer pops a
sterl-signerTouch ID modal. Only after you authenticate does it run/usr/bin/security find-generic-passwordto fetch the passphrase and decrypt the keystore in-process. - At every
signTransactionrequest - instead of ay/Nstderr prompt, the daemon shows a Touch ID modal whose body contains the action summary, source account, and tx hash. Touch ID is the approval; cancelling rejects the transaction. - At every
sterl payMPP charge - before settling a Stellar Machine Payments transfer,sterl paypops a Touch ID modal whose body shows the URL, amount, and recipient. The same biometric prompt also reads the keystore passphrase out of the Keychain in one step, so a single fingerprint both approves the charge and unlocks the key for the one-shot payment.
Both prompts go through Apple's LocalAuthentication framework using LAPolicyDeviceOwnerAuthentication, so the fallback chain is:
Touch ID → Apple Watch tap → device password
How the prompt is wired
We ship a tiny ad-hoc-signed Swift helper at native/dist/sterl-biometric-arm64 (and -universal if you build it). The signer process spawns this helper with the reason text and a --tx-summary; the helper calls LAContext.evaluatePolicy and exits 0/2/3/4 depending on the outcome. Embedding a real Info.plist in the binary gives the system modal a sterl-signer title - without it, Touch ID prompts driven from JavaScript-for-Automation show "osascript".
If the prebuilt helper is missing, the wrong architecture, or fails to launch, the signer transparently falls back to a JXA-based code path that calls the same LAContext API. Functionality is identical, just the modal title is less polished. You can check which path your install is using with:
sterl doctor # reports biometric backend: native | jxa | unavailable
Notes
- The passphrase lives only in
~/Library/Keychains/login.keychain-db, scoped to your user account. - Cancel the Touch ID dialog at startup and the daemon falls back to typing your passphrase. Add
--no-keychainto skip the Keychain unlock path entirely for a single launch. - Cancel the per-transaction Touch ID dialog and the request is rejected immediately. If
LAContextitself hard-fails mid-session (e.g. biometry got removed), the daemon prints the error and drops back to a typedy/Nprompt for that one transaction. sterl signer keychain enroll | status | clearmanages the Keychain entry. Removing it leaves the encrypted keystore file intact - only the cached unlock secret is gone.- Linux and Windows fall through to the typed-passphrase + typed-
y/Nflow; nothing on those platforms touches the macOS keychain orLocalAuthentication. The fallback is automatic - no flag needed - and applies to both the long-running signer daemon (sterl signer) and the one-shot MPP path (sterl pay).
Rebuilding the native helper
Only needed if you are forking Sterl or want to ship a universal binary. Requires Xcode command-line tools (xcode-select --install):
pnpm run build:native # current host arch (arm64 or x86_64)
pnpm run build:native:universal # arm64 + x86_64 fat binary via lipo
The script ad-hoc-signs the binary (codesign -s -), so distributed installs don't need an Apple Developer ID. Gatekeeper stays quiet because we invoke the helper as a subprocess rather than via open(1).
A future release can swap the software keystore for Ledger or another hardware backend without changing the IPC protocol.
Releasing
Maintainers cut a release with one command:
pnpm release # patch: 0.1.0 -> 0.1.1
pnpm release:minor # minor: 0.1.x -> 0.2.0
pnpm release:major # major: 0.x.x -> 1.0.0
scripts/release.sh runs in this order so a failed publish never leaves a half-released state on the remote:
- preflight - must be on
main, clean working tree, in sync withorigin, logged in to npm typecheck+lint+test+buildpnpm version <bump>- bumpspackage.json, makes a commit and avX.Y.Ztag locallynpm publish- prompts for the npm 2FA OTP if you have it enabledgit push --follow-tags- only runs if publish succeeded
If publish fails (bad OTP, registry hiccup) the bump and tag stay local; rerun npm publish to retry, or git tag -d vX.Y.Z && git reset --hard HEAD~1 to undo.
Landing page hosting
The marketing site at https://sterlai.xyz is the static docs/ folder served by Vercel (Hobby tier, free). The deploy is fully managed by vercel.json at the repo root - every push to main triggers a fresh build within seconds, no GitHub Actions or gh-pages branch involved.
DNS lives at Spaceship (apex A 76.76.21.21, www CNAME cname.vercel-dns.com). The apex sterlai.xyz is canonical and www.sterlai.xyz 301-redirects to it. Nothing in docs/ is read by GitHub Pages anymore - the old Pages config has been disabled and docs/CNAME removed.
To preview the site locally without Vercel:
npx serve docs # or: python3 -m http.server -d docs
What's intentionally not here
- No mock signer. Sterl always uses the real keystore - there is no developer-mode bypass.
- No EVM. Sterl does not speak Ethereum, Base, or any EVM chain.
- No
manage_offer. Path payments cover the AI agent's needs; resting orders are out of scope. - No Soroban yet. Soroban RPC client is configured but no contract tools are wired through.