Watches Soroban contracts on testnet and futurenet and fires a webhook alert when:
- a contract's storage is about to be archived (TTL/
liveUntilLedgerSeqdrops below a threshold you configure), or - a matching contract event is emitted (optionally topic-filtered).
No dashboard, no hosted service — a small CLI you run yourself, backed by a local SQLite file. See PRD.md for scope/target users and ARCHITECTURE.md for how it's built.
npm install
cp .env.example .env # adjust network/poll interval/default webhook as needed# Register a TTL watch — alerts once a contract's remaining ledgers drop below the threshold
npx sentinel watch add \
--contract C... \
--kind ttl \
--name my-contract-ttl \
--network testnet \
--ttl-threshold 17280 \
--webhook https://your-webhook-endpoint
# Register an event watch — alerts on new contract events, optionally topic-filtered
npx sentinel watch add \
--contract C... \
--kind event \
--name my-contract-events \
--network testnet \
--webhook https://your-webhook-endpoint
# List / remove watches
npx sentinel watch list
npx sentinel watch remove <id>
# Run the poll loop (Ctrl+C to stop), or a single cycle with --once
npx sentinel run
npx sentinel run --onceEvery alert is delivered as a single JSON POST, regardless of watch kind:
{
"kind": "ttl" | "event",
"watchName": "string",
"contractId": "C...",
"network": "testnet" | "futurenet",
"firedAt": "ISO-8601",
"detail": { "...": "kind-specific fields" }
}Discord and Slack both accept incoming webhooks directly, so pointing
--webhook at either works today without a dedicated integration.
npm run build # compile TypeScript
npm test # unit tests (mocked RPC, no network)
npm run test:integration # hits real testnet RPC — requires network access
npm run test:all # both
npm run lintscripts/demo-testnet.ts is not a mocked demo — it funds two throwaway
testnet accounts via Friendbot, submits a real payment, and runs Sentinel's
actual production code (WatchRepository + pollOnce, the same functions
the CLI uses) to detect the resulting TTL state and transfer event, then
delivers both as real webhook POSTs to a local receiver started by the
script:
npm run demo:testnetMIT