sorokeep 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +296 -0
- package/LICENSE +21 -0
- package/README.md +677 -0
- package/dist/alerts/alerts.d.ts +21 -0
- package/dist/alerts/alerts.d.ts.map +1 -0
- package/dist/alerts/alerts.js +18 -0
- package/dist/alerts/alerts.js.map +1 -0
- package/dist/alerts/discord.d.ts +3 -0
- package/dist/alerts/discord.d.ts.map +1 -0
- package/dist/alerts/discord.js +199 -0
- package/dist/alerts/discord.js.map +1 -0
- package/dist/alerts/dispatcher.d.ts +13 -0
- package/dist/alerts/dispatcher.d.ts.map +1 -0
- package/dist/alerts/dispatcher.js +91 -0
- package/dist/alerts/dispatcher.js.map +1 -0
- package/dist/alerts/keys.d.ts +24 -0
- package/dist/alerts/keys.d.ts.map +1 -0
- package/dist/alerts/keys.js +44 -0
- package/dist/alerts/keys.js.map +1 -0
- package/dist/alerts/pagerduty.d.ts +8 -0
- package/dist/alerts/pagerduty.d.ts.map +1 -0
- package/dist/alerts/pagerduty.js +164 -0
- package/dist/alerts/pagerduty.js.map +1 -0
- package/dist/alerts/resource.d.ts +23 -0
- package/dist/alerts/resource.d.ts.map +1 -0
- package/dist/alerts/resource.js +167 -0
- package/dist/alerts/resource.js.map +1 -0
- package/dist/alerts/slack.d.ts +7 -0
- package/dist/alerts/slack.d.ts.map +1 -0
- package/dist/alerts/slack.js +184 -0
- package/dist/alerts/slack.js.map +1 -0
- package/dist/alerts/telegram.d.ts +9 -0
- package/dist/alerts/telegram.d.ts.map +1 -0
- package/dist/alerts/telegram.js +133 -0
- package/dist/alerts/telegram.js.map +1 -0
- package/dist/alerts/templates.d.ts +109 -0
- package/dist/alerts/templates.d.ts.map +1 -0
- package/dist/alerts/templates.js +139 -0
- package/dist/alerts/templates.js.map +1 -0
- package/dist/alerts/types.d.ts +113 -0
- package/dist/alerts/types.d.ts.map +1 -0
- package/dist/alerts/types.js +87 -0
- package/dist/alerts/types.js.map +1 -0
- package/dist/alerts/webhook.d.ts +13 -0
- package/dist/alerts/webhook.d.ts.map +1 -0
- package/dist/alerts/webhook.js +56 -0
- package/dist/alerts/webhook.js.map +1 -0
- package/dist/cli/schemaFormatter.d.ts +11 -0
- package/dist/cli/schemaFormatter.d.ts.map +1 -0
- package/dist/cli/schemaFormatter.js +74 -0
- package/dist/cli/schemaFormatter.js.map +1 -0
- package/dist/commands/alerts.d.ts +3 -0
- package/dist/commands/alerts.d.ts.map +1 -0
- package/dist/commands/alerts.js +256 -0
- package/dist/commands/alerts.js.map +1 -0
- package/dist/commands/budget.d.ts +3 -0
- package/dist/commands/budget.d.ts.map +1 -0
- package/dist/commands/budget.js +47 -0
- package/dist/commands/budget.js.map +1 -0
- package/dist/commands/channels.d.ts +3 -0
- package/dist/commands/channels.d.ts.map +1 -0
- package/dist/commands/channels.js +84 -0
- package/dist/commands/channels.js.map +1 -0
- package/dist/commands/check.d.ts +3 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +58 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/completion.d.ts +3 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +36 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/costs.d.ts +15 -0
- package/dist/commands/costs.d.ts.map +1 -0
- package/dist/commands/costs.js +280 -0
- package/dist/commands/costs.js.map +1 -0
- package/dist/commands/daemon.d.ts +3 -0
- package/dist/commands/daemon.d.ts.map +1 -0
- package/dist/commands/daemon.js +93 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/db.d.ts +3 -0
- package/dist/commands/db.d.ts.map +1 -0
- package/dist/commands/db.js +140 -0
- package/dist/commands/db.js.map +1 -0
- package/dist/commands/guard.d.ts +3 -0
- package/dist/commands/guard.d.ts.map +1 -0
- package/dist/commands/guard.js +181 -0
- package/dist/commands/guard.js.map +1 -0
- package/dist/commands/history.d.ts +3 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +57 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/inspect.d.ts +3 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +82 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/mcp.d.ts +3 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +20 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/pause.d.ts +3 -0
- package/dist/commands/pause.d.ts.map +1 -0
- package/dist/commands/pause.js +32 -0
- package/dist/commands/pause.js.map +1 -0
- package/dist/commands/resources.d.ts +3 -0
- package/dist/commands/resources.d.ts.map +1 -0
- package/dist/commands/resources.js +95 -0
- package/dist/commands/resources.js.map +1 -0
- package/dist/commands/restore.d.ts +3 -0
- package/dist/commands/restore.d.ts.map +1 -0
- package/dist/commands/restore.js +92 -0
- package/dist/commands/restore.js.map +1 -0
- package/dist/commands/resume.d.ts +3 -0
- package/dist/commands/resume.d.ts.map +1 -0
- package/dist/commands/resume.js +31 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +63 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/watch.d.ts +3 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +163 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/core/aws_secrets.d.ts +13 -0
- package/dist/core/aws_secrets.d.ts.map +1 -0
- package/dist/core/aws_secrets.js +34 -0
- package/dist/core/aws_secrets.js.map +1 -0
- package/dist/core/budget.d.ts +10 -0
- package/dist/core/budget.d.ts.map +1 -0
- package/dist/core/budget.js +31 -0
- package/dist/core/budget.js.map +1 -0
- package/dist/core/channels.d.ts +67 -0
- package/dist/core/channels.d.ts.map +1 -0
- package/dist/core/channels.js +136 -0
- package/dist/core/channels.js.map +1 -0
- package/dist/core/check.d.ts +25 -0
- package/dist/core/check.d.ts.map +1 -0
- package/dist/core/check.js +85 -0
- package/dist/core/check.js.map +1 -0
- package/dist/core/completion.d.ts +9 -0
- package/dist/core/completion.d.ts.map +1 -0
- package/dist/core/completion.js +68 -0
- package/dist/core/completion.js.map +1 -0
- package/dist/core/costs.d.ts +90 -0
- package/dist/core/costs.d.ts.map +1 -0
- package/dist/core/costs.js +147 -0
- package/dist/core/costs.js.map +1 -0
- package/dist/core/decoder.d.ts +7 -0
- package/dist/core/decoder.d.ts.map +1 -0
- package/dist/core/decoder.js +38 -0
- package/dist/core/decoder.js.map +1 -0
- package/dist/core/discovery.d.ts +38 -0
- package/dist/core/discovery.d.ts.map +1 -0
- package/dist/core/discovery.js +208 -0
- package/dist/core/discovery.js.map +1 -0
- package/dist/core/extension.d.ts +83 -0
- package/dist/core/extension.d.ts.map +1 -0
- package/dist/core/extension.js +429 -0
- package/dist/core/extension.js.map +1 -0
- package/dist/core/inspect.d.ts +62 -0
- package/dist/core/inspect.d.ts.map +1 -0
- package/dist/core/inspect.js +303 -0
- package/dist/core/inspect.js.map +1 -0
- package/dist/core/introspection.d.ts +8 -0
- package/dist/core/introspection.d.ts.map +1 -0
- package/dist/core/introspection.js +8 -0
- package/dist/core/introspection.js.map +1 -0
- package/dist/core/monitor.d.ts +42 -0
- package/dist/core/monitor.d.ts.map +1 -0
- package/dist/core/monitor.js +188 -0
- package/dist/core/monitor.js.map +1 -0
- package/dist/core/rent_projection.d.ts +153 -0
- package/dist/core/rent_projection.d.ts.map +1 -0
- package/dist/core/rent_projection.js +146 -0
- package/dist/core/rent_projection.js.map +1 -0
- package/dist/core/scvalTranslator.d.ts +11 -0
- package/dist/core/scvalTranslator.d.ts.map +1 -0
- package/dist/core/scvalTranslator.js +36 -0
- package/dist/core/scvalTranslator.js.map +1 -0
- package/dist/core/state_diff.d.ts +45 -0
- package/dist/core/state_diff.d.ts.map +1 -0
- package/dist/core/state_diff.js +112 -0
- package/dist/core/state_diff.js.map +1 -0
- package/dist/core/status.d.ts +24 -0
- package/dist/core/status.d.ts.map +1 -0
- package/dist/core/status.js +57 -0
- package/dist/core/status.js.map +1 -0
- package/dist/core/vault.d.ts +37 -0
- package/dist/core/vault.d.ts.map +1 -0
- package/dist/core/vault.js +179 -0
- package/dist/core/vault.js.map +1 -0
- package/dist/core/watch.d.ts +36 -0
- package/dist/core/watch.d.ts.map +1 -0
- package/dist/core/watch.js +176 -0
- package/dist/core/watch.js.map +1 -0
- package/dist/daemon/loop.d.ts +38 -0
- package/dist/daemon/loop.d.ts.map +1 -0
- package/dist/daemon/loop.js +169 -0
- package/dist/daemon/loop.js.map +1 -0
- package/dist/db/backup.d.ts +12 -0
- package/dist/db/backup.d.ts.map +1 -0
- package/dist/db/backup.js +132 -0
- package/dist/db/backup.js.map +1 -0
- package/dist/db/budget.d.ts +17 -0
- package/dist/db/budget.d.ts.map +1 -0
- package/dist/db/budget.js +31 -0
- package/dist/db/budget.js.map +1 -0
- package/dist/db/database.d.ts +6 -0
- package/dist/db/database.d.ts.map +1 -0
- package/dist/db/database.js +134 -0
- package/dist/db/database.js.map +1 -0
- package/dist/db/migrations/.gitkeep +1 -0
- package/dist/db/migrations/001_resource_usage_logs.sql +28 -0
- package/dist/db/migrator.d.ts +28 -0
- package/dist/db/migrator.d.ts.map +1 -0
- package/dist/db/migrator.js +75 -0
- package/dist/db/migrator.js.map +1 -0
- package/dist/db/repositories.d.ts +474 -0
- package/dist/db/repositories.d.ts.map +1 -0
- package/dist/db/repositories.js +809 -0
- package/dist/db/repositories.js.map +1 -0
- package/dist/db/schema.sql +207 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +20 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +16 -0
- package/dist/lib.js.map +1 -0
- package/dist/logging/index.d.ts +22 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +34 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/logger.d.ts +14 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +135 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/logging/types.d.ts +10 -0
- package/dist/logging/types.d.ts.map +1 -0
- package/dist/logging/types.js +2 -0
- package/dist/logging/types.js.map +1 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +14 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +51 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/get-extension-costs.d.ts +4 -0
- package/dist/mcp/tools/get-extension-costs.d.ts.map +1 -0
- package/dist/mcp/tools/get-extension-costs.js +33 -0
- package/dist/mcp/tools/get-extension-costs.js.map +1 -0
- package/dist/mcp/tools/get_contract_status.d.ts +8 -0
- package/dist/mcp/tools/get_contract_status.d.ts.map +1 -0
- package/dist/mcp/tools/get_contract_status.js +33 -0
- package/dist/mcp/tools/get_contract_status.js.map +1 -0
- package/dist/rpc/client.d.ts +154 -0
- package/dist/rpc/client.d.ts.map +1 -0
- package/dist/rpc/client.js +766 -0
- package/dist/rpc/client.js.map +1 -0
- package/dist/utils/config.d.ts +51 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +86 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/formatting.d.ts +10 -0
- package/dist/utils/formatting.d.ts.map +1 -0
- package/dist/utils/formatting.js +66 -0
- package/dist/utils/formatting.js.map +1 -0
- package/dist/utils/watch-config.d.ts +13 -0
- package/dist/utils/watch-config.d.ts.map +1 -0
- package/dist/utils/watch-config.js +32 -0
- package/dist/utils/watch-config.js.map +1 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">Sorokeep</h1>
|
|
3
|
+
<p align="center">
|
|
4
|
+
The missing operations layer for deployed Soroban smart contracts.
|
|
5
|
+
<br />
|
|
6
|
+
Monitor TTLs. Get alerted before expiration. Auto-extend storage. Restore archived entries.
|
|
7
|
+
<br />
|
|
8
|
+
<br />
|
|
9
|
+
<a href="#install">Install</a>
|
|
10
|
+
·
|
|
11
|
+
<a href="#quick-start">Quick Start</a>
|
|
12
|
+
·
|
|
13
|
+
<a href="#commands">Commands</a>
|
|
14
|
+
·
|
|
15
|
+
<a href="#alerting">Alerting</a>
|
|
16
|
+
·
|
|
17
|
+
<a href="#contributing">Contributing</a>
|
|
18
|
+
</p>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<br />
|
|
22
|
+
|
|
23
|
+
## Why This Exists
|
|
24
|
+
|
|
25
|
+
Soroban's storage model is uncommon among major smart contract platforms: **state expires.** Every ledger entry — contract instances, persistent storage, WASM code — has a Time-To-Live (TTL). When it runs out, the entry is archived. If a contract's instance entry expires, the entire contract stops working. If persistent storage entries expire, user data becomes inaccessible until someone pays to restore it.
|
|
26
|
+
|
|
27
|
+
This is by design — state archival keeps Stellar lean and scalable. But it means **you must actively manage the lifecycle of your contract's state, or it dies.**
|
|
28
|
+
|
|
29
|
+
There is currently no dedicated open-source tool that combines TTL monitoring, alerting, auto-extension, cost tracking, and restoration for Soroban contracts. Developers either use manual CLI commands, build ad-hoc scripts, or embed TTL extension logic directly in their contracts.
|
|
30
|
+
|
|
31
|
+
Sorokeep is the unified operations layer that handles all of this.
|
|
32
|
+
|
|
33
|
+
> Security auditors have started flagging TTL mismanagement as a risk area in Soroban contracts. [Veridise](https://veridise.com/audits/soroban/) includes TTL handling in their audit scope. The [LayerZero Stellar endpoint audit](https://code4rena.com/audits/2026-04-layerzero-stellar-endpoint) explicitly lists TTL expiration edge cases as a concern. [OpenZeppelin's Stellar contracts library](https://docs.openzeppelin.com/stellar-contracts) deliberately leaves instance storage TTL management to the application developer.
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Watch & Introspect** — Register contracts, footprint discovery from on-chain transactions, and introspection specs
|
|
38
|
+
- **Monitor** — Continuous TTL polling with configurable intervals via a long-running daemon
|
|
39
|
+
- **Alert** — Decoupled, queue-backed multi-channel notifications (Webhook with HMAC-SHA256, Slack Block Kit, Discord, Telegram, PagerDuty) with robust retry logic for low TTLs, resource usage spikes, and state changes
|
|
40
|
+
- **Auto-Extend** — Policy-based automatic TTL extension with transaction simulation before submission via `ExtendFootprintTTLOp`
|
|
41
|
+
- **Restore** — Recover archived entries via `RestoreFootprintOp` with pre-submission simulation
|
|
42
|
+
- **Cost & Resource Tracking** — Track extension history, XLM costs, 30-day projections, resource usage logs, and enforce configurable monthly budgets to prevent runaway spend
|
|
43
|
+
- **Inspect** — Inspect on-chain state, parse SAC token balances, and diff state changes
|
|
44
|
+
- **Channels** — Manage funded channel accounts for concurrent transaction submissions without sequence bottlenecks
|
|
45
|
+
- **Local-First** — All state stored in a SQLite database-backed queue. No external services beyond a Stellar RPC endpoint
|
|
46
|
+
- **AI-Ready** — Built-in Model Context Protocol (MCP) server exposing tools for AI agents to interact with Sorokeep's data natively
|
|
47
|
+
- **Advanced Security** — Integrates with AWS Secrets Manager & HashiCorp Vault for secure key resolution
|
|
48
|
+
- **Production-Ready deployments** — Includes Dockerfile, systemd service templates, and GitHub Actions for CI/CD integration
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
**Requirements:** Node.js 22+
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# From source
|
|
56
|
+
git clone https://github.com/AbdulmalikAlayande/sorokeep.git
|
|
57
|
+
cd sorokeep
|
|
58
|
+
npm install
|
|
59
|
+
npm run build
|
|
60
|
+
|
|
61
|
+
# Run directly
|
|
62
|
+
npx tsx src/index.ts --help
|
|
63
|
+
|
|
64
|
+
# Or link globally after building
|
|
65
|
+
npm link
|
|
66
|
+
sorokeep --help
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
<!--
|
|
70
|
+
# npm (coming soon)
|
|
71
|
+
npm install -g sorokeep
|
|
72
|
+
-->
|
|
73
|
+
|
|
74
|
+
## Quick Start
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# 1. Register a contract for monitoring
|
|
78
|
+
sorokeep watch CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC \
|
|
79
|
+
--network testnet \
|
|
80
|
+
--name "XLM Native Token"
|
|
81
|
+
|
|
82
|
+
# 2. Check its current TTL health
|
|
83
|
+
sorokeep status CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC
|
|
84
|
+
|
|
85
|
+
# 3. Set up a webhook alert (fires when TTL drops below 20,000 ledgers)
|
|
86
|
+
sorokeep alerts add \
|
|
87
|
+
--contract CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC \
|
|
88
|
+
--type webhook \
|
|
89
|
+
--url https://your-server.com/webhook \
|
|
90
|
+
--threshold 20000
|
|
91
|
+
|
|
92
|
+
# 4. Start the monitoring daemon
|
|
93
|
+
sorokeep daemon --network testnet
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The daemon will check TTLs every 5 minutes, fire alerts when thresholds are crossed, send resolution notifications when TTLs recover, and auto-extend entries if guard policies are configured.
|
|
97
|
+
|
|
98
|
+
## Commands
|
|
99
|
+
|
|
100
|
+
### `sorokeep watch <contract-id>`
|
|
101
|
+
|
|
102
|
+
Register a contract for monitoring. Connects to the Stellar RPC, discovers the contract's instance and WASM code entries, reads their TTLs, and stores everything locally.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
sorokeep watch <contract-id> [options]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
| Option | Description | Default |
|
|
109
|
+
|--------|-------------|---------|
|
|
110
|
+
| `-n, --name <name>` | Human-readable contract name | — |
|
|
111
|
+
| `--network <network>` | `testnet` or `mainnet` | `testnet` |
|
|
112
|
+
| `-r, --rpc-url <url>` | Custom Stellar RPC endpoint | Network default |
|
|
113
|
+
| `--storage-keys <keys>` | Comma-separated base64 XDR storage keys to track | — |
|
|
114
|
+
|
|
115
|
+
**Example output:**
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
$ sorokeep watch CDLZFC3S...CYSC --network testnet --name "XLM Native Token"
|
|
119
|
+
|
|
120
|
+
✔ Contract XLM Native Token registered successfully.
|
|
121
|
+
|
|
122
|
+
Contract: XLM Native Token (CDLZFC3S...CYSC)
|
|
123
|
+
Network: testnet
|
|
124
|
+
Entries: 1 discovered
|
|
125
|
+
Instance TTL: 113,918 ledgers (~7d 6h) OK
|
|
126
|
+
|
|
127
|
+
Run 'sorokeep status CDLZFC3S...CYSC' to check TTLs anytime.
|
|
128
|
+
Run 'sorokeep guard CDLZFC3S...CYSC' to enable auto-extension.
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Entry discovery happens in layers:
|
|
132
|
+
|
|
133
|
+
1. **Deterministic** (automatic) — Contract instance and WASM code entries, derived from the contract ID and WASM hash. Always tracked.
|
|
134
|
+
2. **Footprint-based** (daemon) — Discovered by scanning on-chain transaction events for storage keys your contract uses.
|
|
135
|
+
3. **Manual** (opt-in) — Specific storage keys declared via `--storage-keys`.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### `sorokeep status <contract-id>`
|
|
140
|
+
|
|
141
|
+
Display current TTL health for a watched contract. Reads from the local database — no RPC call.
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
sorokeep status <contract-id>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Shows contract name, network, last checked ledger, and a table of all tracked entries with remaining TTL in ledgers and human-readable time, plus a status indicator (OK / Warning / Critical).
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
### `sorokeep daemon`
|
|
152
|
+
|
|
153
|
+
Start the long-running monitoring process.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
sorokeep daemon [options]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
| Option | Description | Default |
|
|
160
|
+
|--------|-------------|---------|
|
|
161
|
+
| `--network <network>` | Network to monitor | `testnet` |
|
|
162
|
+
| `--interval <ms>` | Polling interval in milliseconds (min: 10,000) | `300000` (5 min) |
|
|
163
|
+
| `-r, --rpc-url <url>` | Custom RPC endpoint | Network default |
|
|
164
|
+
|
|
165
|
+
Each cycle performs three phases:
|
|
166
|
+
|
|
167
|
+
1. **Monitor** — Fetches fresh TTLs for all contracts, detects threshold crossings, resolves recovered alerts
|
|
168
|
+
2. **Deliver** — Dispatches pending alerts to configured webhook and Slack channels
|
|
169
|
+
3. **Auto-Extend** — Extends TTLs for contracts with active guard policies
|
|
170
|
+
|
|
171
|
+
The daemon handles graceful shutdown on `SIGINT`/`SIGTERM` and includes a re-entrance guard to prevent overlapping cycles.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
### `sorokeep alerts`
|
|
176
|
+
|
|
177
|
+
Manage alert configurations. Supports five subcommands.
|
|
178
|
+
|
|
179
|
+
#### `alerts add` — Create a new alert
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
sorokeep alerts add [options]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
| Option | Description |
|
|
186
|
+
|--------|-------------|
|
|
187
|
+
| `--contract <id>` | Contract ID to alert on (required) |
|
|
188
|
+
| `--type <type>` | `webhook` or `slack` (required) |
|
|
189
|
+
| `--url <url>` | Webhook POST URL (required for webhook) |
|
|
190
|
+
| `--channel <channel>` | Slack channel name or ID (required for slack) |
|
|
191
|
+
| `--threshold <ledgers>` | Fire when remaining TTL drops below this (required) |
|
|
192
|
+
| `--secret <secret>` | HMAC signing secret for webhooks (auto-generated if omitted) |
|
|
193
|
+
|
|
194
|
+
For webhook alerts, an HMAC signing secret is auto-generated (32-byte hex) if you don't provide one. The secret is displayed once at creation time — save it to verify webhook signatures on your server. See [Webhook Signing](#webhook-signing) for details.
|
|
195
|
+
|
|
196
|
+
#### `alerts list` — View configured alerts
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
sorokeep alerts list --contract <id>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### `alerts remove` — Delete an alert configuration
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
sorokeep alerts remove --id <config-id>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### `alerts test` — Send a test alert
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
sorokeep alerts test --id <config-id>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Fires a synthetic `threshold_crossed` event through the real delivery pipeline. Useful for verifying that your webhook endpoint or Slack channel is correctly configured before going live.
|
|
215
|
+
|
|
216
|
+
#### `alerts history` — View past alert activity
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
sorokeep alerts history --contract <id> [--limit 20]
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Shows a table of fired alerts: timestamp, entry label, TTL at fire, channel type, delivery status, retry count, and resolution time.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
### `sorokeep guard`
|
|
227
|
+
|
|
228
|
+
Configure auto-extension policies. When enabled, the daemon automatically extends TTLs by submitting `ExtendFootprintTTLOp` transactions using a funded Stellar keypair.
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
sorokeep guard <contract-id> [options]
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
| Option | Description | Default |
|
|
235
|
+
|--------|-------------|---------|
|
|
236
|
+
| `--target-ttl <ledgers>` | TTL to extend entries to | `100000` |
|
|
237
|
+
| `--threshold <ledgers>` | Extend when TTL drops below this | `20000` |
|
|
238
|
+
| `--keypair <secret>` | Stellar secret key (for one-time extension) | — |
|
|
239
|
+
| `--keypair-env <var>` | Env var name containing the secret key | — |
|
|
240
|
+
| `--auto-extend` | Enable daemon auto-extension (requires `--keypair-env`) | — |
|
|
241
|
+
| `--dry-run` | Simulate extension and show estimated fee | — |
|
|
242
|
+
| `--disable` | Disable auto-extension for this contract | — |
|
|
243
|
+
|
|
244
|
+
**Usage modes:**
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
# Check current policy
|
|
248
|
+
sorokeep guard <contract-id>
|
|
249
|
+
|
|
250
|
+
# Dry run — see estimated fee without submitting
|
|
251
|
+
sorokeep guard <contract-id> --keypair S... --dry-run
|
|
252
|
+
|
|
253
|
+
# One-time immediate extension
|
|
254
|
+
sorokeep guard <contract-id> --keypair S...
|
|
255
|
+
|
|
256
|
+
# Enable auto-extension for the daemon
|
|
257
|
+
sorokeep guard <contract-id> --keypair-env STELLAR_SECRET_KEY --auto-extend
|
|
258
|
+
|
|
259
|
+
# Disable auto-extension
|
|
260
|
+
sorokeep guard <contract-id> --disable
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Security:** Secret keys are never stored in the database. When using `--auto-extend`, only the public key and the environment variable name are persisted. The daemon resolves the actual secret key from the environment at runtime.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
### `sorokeep costs`
|
|
268
|
+
|
|
269
|
+
View extension history and rent spending for a contract.
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
sorokeep costs <contract-id> [options]
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
| Option | Description | Default |
|
|
276
|
+
|--------|-------------|---------|
|
|
277
|
+
| `--period <days>` | Show costs for the last N days | `30` |
|
|
278
|
+
| `--all` | Show all history | — |
|
|
279
|
+
|
|
280
|
+
**Output includes:**
|
|
281
|
+
|
|
282
|
+
- Total extensions and total cost in XLM
|
|
283
|
+
- Breakdown by entry type (instance, wasm, persistent) with count and cost
|
|
284
|
+
- 30-day cost projection extrapolated from the selected period
|
|
285
|
+
- Recent extensions table: timestamp, entry label, old TTL → new TTL, cost in XLM, transaction hash
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
### `sorokeep restore`
|
|
290
|
+
|
|
291
|
+
Recover archived ledger entries via `RestoreFootprintOp` transactions.
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
sorokeep restore <contract-id> [options]
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
| Option | Description |
|
|
298
|
+
|--------|-------------|
|
|
299
|
+
| `--keypair <secret>` | Stellar secret key |
|
|
300
|
+
| `--keypair-env <var>` | Env var containing secret key |
|
|
301
|
+
| `--entry <keyXdr>` | Specific entry key XDR to restore (repeatable) |
|
|
302
|
+
| `--all` | Restore all tracked entries for the contract |
|
|
303
|
+
|
|
304
|
+
One of `--keypair` or `--keypair-env` is required. One of `--entry` or `--all` is required (mutually exclusive).
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# Restore a specific entry
|
|
308
|
+
sorokeep restore <contract-id> --keypair-env STELLAR_SECRET_KEY --entry <base64-xdr>
|
|
309
|
+
|
|
310
|
+
# Restore all tracked entries
|
|
311
|
+
sorokeep restore <contract-id> --keypair-env STELLAR_SECRET_KEY --all
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
### `sorokeep resources`
|
|
317
|
+
|
|
318
|
+
View resource usage logs (CPU instructions, memory bytes, fee structures) for a contract to track execution efficiency over time.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
### `sorokeep budget`
|
|
323
|
+
|
|
324
|
+
Set and monitor a monthly XLM extension budget for a contract. Prevents runaway costs if a contract requires frequent extensions.
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
### `sorokeep channels`
|
|
329
|
+
|
|
330
|
+
Manage funded channel accounts used to submit extension and restoration transactions concurrently, avoiding sequence number bottlenecks.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
### `sorokeep inspect`
|
|
335
|
+
|
|
336
|
+
Inspect on-chain state directly. Can parse Stellar Asset Contract (SAC) token balances, diff state changes, and decode XDR without manual intervention.
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
### `sorokeep check`
|
|
341
|
+
|
|
342
|
+
Perform an ad-hoc, one-off execution of the monitoring cycle without starting the long-running daemon.
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
### `sorokeep db`
|
|
347
|
+
|
|
348
|
+
Database management tasks, including migrations, backups, and introspection cache management.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
### `sorokeep completion`
|
|
353
|
+
|
|
354
|
+
Generate shell autocomplete scripts for bash/zsh to enable tab completion for all Sorokeep commands.
|
|
355
|
+
|
|
356
|
+
## Alerting
|
|
357
|
+
|
|
358
|
+
Sorokeep delivers alerts through multiple channels: **webhooks**, **Slack**, **Discord**, **Telegram**, and **PagerDuty**. Each alert includes a severity level and rich context about the affected entry. Sorokeep uses a robust, decoupled detection and dispatch architecture with a database-backed queue.
|
|
359
|
+
|
|
360
|
+
### Alert Lifecycle
|
|
361
|
+
|
|
362
|
+
1. **Threshold Crossed** — During each monitoring cycle, if an entry's remaining TTL drops below a configured threshold, the monitor writes a `threshold_crossed` alert to the database queue.
|
|
363
|
+
2. **Delivery** — The dispatcher reads undelivered rows from the queue and routes the alert to the configured channel. Failed deliveries are retried on subsequent cycles, up to 5 attempts, and then gracefully abandoned. Success marks the row as delivered.
|
|
364
|
+
3. **Resolution** — When TTL recovers past the threshold (e.g., after an extension), Sorokeep fires an `alert_resolved` notification to all configured channels.
|
|
365
|
+
|
|
366
|
+
### Severity Levels
|
|
367
|
+
|
|
368
|
+
Severity is computed automatically based on how much TTL remains relative to the configured threshold:
|
|
369
|
+
|
|
370
|
+
| Severity | Condition | Description |
|
|
371
|
+
|----------|-----------|-------------|
|
|
372
|
+
| **critical** | Remaining TTL < 25% of threshold, or TTL = 0 | Entry is in immediate danger of archival |
|
|
373
|
+
| **warning** | Remaining TTL is below threshold but above 25% | Entry needs attention soon |
|
|
374
|
+
| **info** | Alert resolved (TTL recovered) | Entry is healthy again |
|
|
375
|
+
|
|
376
|
+
### Webhook Delivery
|
|
377
|
+
|
|
378
|
+
Webhook alerts are delivered as HTTP POST requests with a JSON body:
|
|
379
|
+
|
|
380
|
+
```json
|
|
381
|
+
{
|
|
382
|
+
"type": "threshold_crossed",
|
|
383
|
+
"severity": "warning",
|
|
384
|
+
"contractId": "CDLZFC3S...",
|
|
385
|
+
"contractName": "XLM Native Token",
|
|
386
|
+
"network": "testnet",
|
|
387
|
+
"entry": {
|
|
388
|
+
"keyXdr": "AAAA1234...",
|
|
389
|
+
"type": "instance",
|
|
390
|
+
"label": "Contract Instance"
|
|
391
|
+
},
|
|
392
|
+
"threshold": {
|
|
393
|
+
"configuredLedgers": 20000,
|
|
394
|
+
"currentRemainingLedgers": 8500,
|
|
395
|
+
"approximateTimeRemaining": "~13h 0m"
|
|
396
|
+
},
|
|
397
|
+
"firedAtLedger": 2500000,
|
|
398
|
+
"timestamp": "2026-06-13T12:00:00.000Z"
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Webhook Signing
|
|
403
|
+
|
|
404
|
+
Webhook requests include an HMAC-SHA256 signature in the `X-Sorokeep-Signature` header for payload verification:
|
|
405
|
+
|
|
406
|
+
```
|
|
407
|
+
X-Sorokeep-Signature: sha256=a1b2c3d4e5f6...
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
To verify on your server:
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
import { createHmac } from "node:crypto";
|
|
414
|
+
|
|
415
|
+
function verifySignature(payload, signature, secret) {
|
|
416
|
+
const expected = "sha256=" + createHmac("sha256", secret)
|
|
417
|
+
.update(payload)
|
|
418
|
+
.digest("hex");
|
|
419
|
+
return signature === expected;
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
The signing secret is auto-generated when you create a webhook alert (or you can provide your own with `--secret`). It is displayed once at creation time — store it securely.
|
|
424
|
+
|
|
425
|
+
### Slack Delivery
|
|
426
|
+
|
|
427
|
+
Slack alerts are sent via the [Slack Web API](https://api.slack.com/methods/chat.postMessage) using Block Kit for rich formatting. Messages include severity icons, contract details, remaining TTL, and actionable hints.
|
|
428
|
+
|
|
429
|
+
**Setup:**
|
|
430
|
+
|
|
431
|
+
1. Create a Slack app with `chat:write` scope at [api.slack.com/apps](https://api.slack.com/apps)
|
|
432
|
+
2. Install the app to your workspace and copy the Bot User OAuth Token (`xoxb-...`)
|
|
433
|
+
3. Provide the token via environment variable:
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
export SOROKEEP_SLACK_TOKEN=xoxb-your-bot-token
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Alternatively, store the token in your config file at `~/.sorokeep/config.yaml`:
|
|
440
|
+
|
|
441
|
+
```yaml
|
|
442
|
+
slackToken: "xoxb-your-bot-token"
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
The environment variable takes precedence over the config file.
|
|
446
|
+
|
|
447
|
+
### Retry Policy
|
|
448
|
+
|
|
449
|
+
Failed alert deliveries are automatically retried on subsequent daemon cycles. After **5 consecutive failures**, the alert is abandoned and no further delivery attempts are made. You can view delivery status and retry counts with `sorokeep alerts history`.
|
|
450
|
+
|
|
451
|
+
## How It Works
|
|
452
|
+
|
|
453
|
+
Sorokeep is an off-chain monitoring tool. It reads data from the Stellar RPC, stores it locally in SQLite, and acts on it (alerts, auto-extension, restoration). It does not run on-chain and does not require you to modify your contracts.
|
|
454
|
+
|
|
455
|
+
```
|
|
456
|
+
┌─────────────────────┐
|
|
457
|
+
│ Stellar Network │
|
|
458
|
+
│ (testnet / mainnet) │
|
|
459
|
+
└──────────┬───────────┘
|
|
460
|
+
│ RPC
|
|
461
|
+
┌──────────▼───────────┐
|
|
462
|
+
│ Sorokeep │
|
|
463
|
+
│ │
|
|
464
|
+
│ ┌─────────────────┐ │
|
|
465
|
+
│ │ Monitor Cycle │ │
|
|
466
|
+
│ │ (fetch TTLs, │ │
|
|
467
|
+
│ │ detect alerts, │ │
|
|
468
|
+
│ │ resolve) │ │
|
|
469
|
+
│ └────────┬────────┘ │
|
|
470
|
+
│ │ │
|
|
471
|
+
│ ┌────────▼────────┐ │
|
|
472
|
+
│ │ Dispatcher │ │
|
|
473
|
+
│ │ (webhook/slack) │ │
|
|
474
|
+
│ └────────┬────────┘ │
|
|
475
|
+
│ │ │
|
|
476
|
+
│ ┌────────▼────────┐ │
|
|
477
|
+
│ │ Auto-Extend │ │
|
|
478
|
+
│ │ (guard policy) │ │
|
|
479
|
+
│ └─────────────────┘ │
|
|
480
|
+
│ │
|
|
481
|
+
│ ┌─────────────────┐ │
|
|
482
|
+
│ │ SQLite DB │ │
|
|
483
|
+
│ │ ~/.soroban- │ │
|
|
484
|
+
│ │ sorokeep/ │ │
|
|
485
|
+
│ │ sorokeep.db │ │
|
|
486
|
+
│ └─────────────────┘ │
|
|
487
|
+
└───────────────────────┘
|
|
488
|
+
│
|
|
489
|
+
┌───────────────┼───────────────┐
|
|
490
|
+
▼ ▼ ▼
|
|
491
|
+
┌──────────┐ ┌────────────┐ ┌────────────┐
|
|
492
|
+
│ Webhooks │ │ Slack │ │ Terminal │
|
|
493
|
+
└──────────┘ └────────────┘ └────────────┘
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### The Daemon Cycle
|
|
497
|
+
|
|
498
|
+
Every polling interval (default: 5 minutes), the daemon runs three phases:
|
|
499
|
+
|
|
500
|
+
1. **Monitor** — For each registered contract, fetches fresh TTLs from the RPC, updates the database, checks each entry against every configured alert threshold. Fires `threshold_crossed` when TTL drops below a threshold; fires `alert_resolved` when TTL recovers.
|
|
501
|
+
|
|
502
|
+
2. **Deliver** — Processes all undelivered alerts from the database queue. Routes each to its configured channel (Webhook, Slack, Discord, Telegram, PagerDuty), marks successful deliveries, increments retry counters on failures, and abandons gracefully after 5 retries.
|
|
503
|
+
|
|
504
|
+
3. **Auto-Extend** — For contracts with an active guard policy, checks which entries have TTL below the policy threshold and simulates an `ExtendFootprintTTLOp` transaction via the Stellar RPC. If successful, it submits the transaction, records the exact XLM cost, and updates the contract's monthly budget usage to prevent runaway spend.
|
|
505
|
+
|
|
506
|
+
### Storage
|
|
507
|
+
|
|
508
|
+
All state is local. Sorokeep stores data in `~/.sorokeep/sorokeep.db` (SQLite with WAL mode). No external services required beyond a Stellar RPC endpoint.
|
|
509
|
+
|
|
510
|
+
**Database tables:**
|
|
511
|
+
|
|
512
|
+
| Table | Purpose |
|
|
513
|
+
|-------|---------|
|
|
514
|
+
| `contracts` | Registered contracts with network, name, WASM hash |
|
|
515
|
+
| `contract_entries` | Tracked ledger entries with TTLs and discovery source |
|
|
516
|
+
| `extension_policies` | Auto-extension rules per contract (threshold, target, keypair reference) |
|
|
517
|
+
| `alert_configs` | Alert channels, thresholds, and webhook secrets |
|
|
518
|
+
| `alerts_fired` | Fired alert records with delivery status, retry count, and resolution tracking |
|
|
519
|
+
| `extension_history` | Every TTL extension with transaction hash and XLM cost |
|
|
520
|
+
|
|
521
|
+
### Configuration
|
|
522
|
+
|
|
523
|
+
Sorokeep stores user configuration in `~/.sorokeep/config.yaml`:
|
|
524
|
+
|
|
525
|
+
```yaml
|
|
526
|
+
network: testnet
|
|
527
|
+
pollingIntervalSeconds: 300
|
|
528
|
+
slackToken: "xoxb-..." # Optional — can also use SOROKEEP_SLACK_TOKEN env var
|
|
529
|
+
rpcUrl: "https://..." # Optional — overrides network default
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
The config file is created with `0600` permissions (owner read/write only) to protect sensitive values like the Slack token.
|
|
533
|
+
|
|
534
|
+
## Project Structure
|
|
535
|
+
|
|
536
|
+
```
|
|
537
|
+
sorokeep/
|
|
538
|
+
├── src/
|
|
539
|
+
│ ├── index.ts # CLI entry point (Commander.js)
|
|
540
|
+
│ ├── commands/ # CLI command handlers (thin presentation layer)
|
|
541
|
+
│ │ ├── watch.ts # Contract registration
|
|
542
|
+
│ │ ├── status.ts # TTL health display
|
|
543
|
+
│ │ ├── daemon.ts # Long-running monitor
|
|
544
|
+
│ │ ├── alerts.ts # Alert CRUD + test + history
|
|
545
|
+
│ │ ├── guard.ts # Auto-extension policies
|
|
546
|
+
│ │ ├── costs.ts # Extension cost reporting
|
|
547
|
+
│ │ └── restore.ts # Archived entry recovery
|
|
548
|
+
│ ├── core/ # Business logic (no CLI dependencies)
|
|
549
|
+
│ │ ├── watch.ts # Contract registration and discovery
|
|
550
|
+
│ │ ├── monitor.ts # Polling cycle, threshold detection, resolution
|
|
551
|
+
│ │ ├── extension.ts # TTL extend, auto-extend, restore, cost recording
|
|
552
|
+
│ │ └── discovery.ts # Footprint-based storage key discovery
|
|
553
|
+
│ ├── alerts/ # Alert delivery pipeline
|
|
554
|
+
│ │ ├── types.ts # AlertEvent, AlertSeverity, buildAlertEvent
|
|
555
|
+
│ │ ├── dispatcher.ts # Routing, retry logic, delivery orchestration
|
|
556
|
+
│ │ ├── webhook.ts # HTTP POST with HMAC-SHA256 signing
|
|
557
|
+
│ │ └── slack.ts # Slack Web API + Block Kit formatting
|
|
558
|
+
│ ├── daemon/ # Daemon lifecycle
|
|
559
|
+
│ │ └── loop.ts # Start/stop, re-entrance guard, cycle orchestration
|
|
560
|
+
│ ├── rpc/ # Stellar RPC client wrapper
|
|
561
|
+
│ │ └── client.ts # Instance/WASM fetch, batch TTLs, extend, restore
|
|
562
|
+
│ ├── db/ # Database layer
|
|
563
|
+
│ │ ├── schema.sql # Full SQLite schema
|
|
564
|
+
│ │ ├── database.ts # Init, WAL mode, live migrations
|
|
565
|
+
│ │ └── repositories.ts # All query functions
|
|
566
|
+
│ ├── logging/ # Structured logging (pino)
|
|
567
|
+
│ └── utils/ # Config loader, TTL formatting
|
|
568
|
+
├── tests/ # Mirrors src/ structure — 891 tests across 66 files
|
|
569
|
+
├── .github/workflows/ # CI (test + type-check) and publish
|
|
570
|
+
├── package.json
|
|
571
|
+
├── tsconfig.json
|
|
572
|
+
├── vitest.config.ts
|
|
573
|
+
├── Dockerfile # Docker container definition
|
|
574
|
+
├── systemd/ # Systemd service templates for Linux deployments
|
|
575
|
+
├── LICENSE
|
|
576
|
+
└── CONTRIBUTING.md
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
**Architecture layers:**
|
|
580
|
+
|
|
581
|
+
- **Commands** (`src/commands/`) — Thin CLI layer. Parses arguments, calls core, formats terminal output. No business logic.
|
|
582
|
+
- **Core** (`src/core/`) — Pure business logic. Testable without network or CLI. The daemon reuses the same functions.
|
|
583
|
+
- **RPC** (`src/rpc/`) — Stellar SDK wrapper. All network calls go through here. Handles transaction building, simulation, signing, and submission.
|
|
584
|
+
- **Alerts** (`src/alerts/`) — Delivery pipeline. Channel-specific formatting and transport, routing, retry management.
|
|
585
|
+
- **DB** (`src/db/`) — SQLite repositories. All queries centralized here. In-memory mode for tests.
|
|
586
|
+
|
|
587
|
+
## Tech Stack
|
|
588
|
+
|
|
589
|
+
| Package | Purpose |
|
|
590
|
+
|---------|---------|
|
|
591
|
+
| [TypeScript](https://www.typescriptlang.org/) | Application language (ESM) |
|
|
592
|
+
| [@stellar/stellar-sdk](https://github.com/nicktomlin/js-stellar-sdk) | Stellar and Soroban RPC interactions |
|
|
593
|
+
| [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) | Local database (synchronous, zero external deps) |
|
|
594
|
+
| [Commander.js](https://github.com/tj/commander.js) | CLI framework |
|
|
595
|
+
| [pino](https://github.com/pinojs/pino) | Structured JSON logging |
|
|
596
|
+
| [chalk](https://github.com/chalk/chalk) / [ora](https://github.com/sindresorhus/ora) | Terminal formatting and spinners |
|
|
597
|
+
| [yaml](https://github.com/eemeli/yaml) | Config file parsing |
|
|
598
|
+
| [Vitest](https://vitest.dev/) | Test framework |
|
|
599
|
+
|
|
600
|
+
## Testing
|
|
601
|
+
|
|
602
|
+
```bash
|
|
603
|
+
# Run all tests
|
|
604
|
+
npm test
|
|
605
|
+
|
|
606
|
+
# Run a specific test file
|
|
607
|
+
npx vitest run tests/core/monitor.test.ts
|
|
608
|
+
|
|
609
|
+
# Watch mode
|
|
610
|
+
npx vitest
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
**891 tests** across **66 test files** covering:
|
|
614
|
+
|
|
615
|
+
- **Formatting** — TTL conversion, status classification, human-readable time
|
|
616
|
+
- **Database** — CRUD, cascades, upserts, deduplication, alert delivery queues
|
|
617
|
+
- **RPC Client** — Contract instance, WASM code, batch TTL queries, transaction simulation
|
|
618
|
+
- **Watch** — Registration, re-watch, SAC contracts, error handling, network isolation, introspection
|
|
619
|
+
- **Monitor Cycle** — TTL refresh, threshold detection, alert deduplication, resolution, fault isolation, multi-threshold escalation, partial RPC responses
|
|
620
|
+
- **Extension** — TTL extension, auto-extension policy evaluation, restore, cost recording, budget enforcement
|
|
621
|
+
- **Alert Dispatcher** — Channel routing, retry logic, max retry cap, abandoned alerts (Slack, Discord, Telegram, Webhook, PagerDuty)
|
|
622
|
+
- **Webhook** — HMAC-SHA256 signing, timeout handling, HTTP error responses
|
|
623
|
+
- **Slack** — Token resolution, Block Kit structure, `body.ok` validation
|
|
624
|
+
- **CLI Commands** — Alerts, budget, guard, costs, watch, status, daemon, check, restore, db, channels
|
|
625
|
+
- **Config** — Load/save, defaults, parse failure handling, file permissions
|
|
626
|
+
- **Daemon** — Start/stop, re-entrance guard, cycle error isolation
|
|
627
|
+
- **MCP Server** — Test coverage for all exposed MCP tools
|
|
628
|
+
|
|
629
|
+
All tests use in-memory SQLite databases and mocked RPC responses — no network calls, no filesystem side effects. TDD is practiced throughout.
|
|
630
|
+
|
|
631
|
+
## FAQ
|
|
632
|
+
|
|
633
|
+
### Why TypeScript, not Rust?
|
|
634
|
+
|
|
635
|
+
Sorokeep is an off-chain operational tool, not a smart contract. TypeScript was chosen because:
|
|
636
|
+
|
|
637
|
+
1. The Stellar JS SDK is the most complete client library for Soroban RPC interactions
|
|
638
|
+
2. Soroban developers already have Node.js in their toolchain
|
|
639
|
+
3. npm distribution means zero-friction installation
|
|
640
|
+
4. The performance requirements (periodic RPC polling) are well within Node.js capabilities
|
|
641
|
+
5. It maximizes the contributor pool — most Soroban developers know TypeScript
|
|
642
|
+
|
|
643
|
+
### Is my secret key stored anywhere?
|
|
644
|
+
|
|
645
|
+
No. When you configure auto-extension with `--keypair-env`, Sorokeep stores only the **public key** and the **environment variable name** in the database. The actual secret key is resolved from your environment at runtime. If you use `--keypair` for a one-time operation, the key is used in-memory and never persisted.
|
|
646
|
+
|
|
647
|
+
### What happens if the daemon crashes mid-cycle?
|
|
648
|
+
|
|
649
|
+
Each phase (monitor, deliver, auto-extend) is wrapped in isolated error handling. A failure in one phase doesn't prevent the others from running. Alert deliveries are idempotent — if a delivery was marked successful, it won't be re-sent. If the daemon restarts, undelivered alerts will be picked up on the next cycle.
|
|
650
|
+
|
|
651
|
+
### What networks are supported?
|
|
652
|
+
|
|
653
|
+
Testnet (`https://soroban-testnet.stellar.org`) and Mainnet (`https://mainnet.sorobanrpc.com`). You can also point Sorokeep at any custom RPC endpoint with `--rpc-url`.
|
|
654
|
+
|
|
655
|
+
### What about email alerts?
|
|
656
|
+
|
|
657
|
+
Email is not yet implemented. The CLI will reject `--type email` with a clear error message. Webhook and Slack are the supported channels today.
|
|
658
|
+
|
|
659
|
+
## Roadmap
|
|
660
|
+
|
|
661
|
+
- Web dashboard for visual TTL monitoring
|
|
662
|
+
- Multi-contract batch operations
|
|
663
|
+
|
|
664
|
+
## Contributing
|
|
665
|
+
|
|
666
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
667
|
+
|
|
668
|
+
## License
|
|
669
|
+
|
|
670
|
+
[MIT](LICENSE)
|
|
671
|
+
|
|
672
|
+
## Author
|
|
673
|
+
|
|
674
|
+
**Abdulmalik Alayande**
|
|
675
|
+
|
|
676
|
+
- GitHub: [@AbdulmalikAlayande](https://github.com/AbdulmalikAlayande)
|
|
677
|
+
- X: [@The_good_man02](https://twitter.com/The_good_man02)
|