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.
Files changed (275) hide show
  1. package/CONTRIBUTING.md +296 -0
  2. package/LICENSE +21 -0
  3. package/README.md +677 -0
  4. package/dist/alerts/alerts.d.ts +21 -0
  5. package/dist/alerts/alerts.d.ts.map +1 -0
  6. package/dist/alerts/alerts.js +18 -0
  7. package/dist/alerts/alerts.js.map +1 -0
  8. package/dist/alerts/discord.d.ts +3 -0
  9. package/dist/alerts/discord.d.ts.map +1 -0
  10. package/dist/alerts/discord.js +199 -0
  11. package/dist/alerts/discord.js.map +1 -0
  12. package/dist/alerts/dispatcher.d.ts +13 -0
  13. package/dist/alerts/dispatcher.d.ts.map +1 -0
  14. package/dist/alerts/dispatcher.js +91 -0
  15. package/dist/alerts/dispatcher.js.map +1 -0
  16. package/dist/alerts/keys.d.ts +24 -0
  17. package/dist/alerts/keys.d.ts.map +1 -0
  18. package/dist/alerts/keys.js +44 -0
  19. package/dist/alerts/keys.js.map +1 -0
  20. package/dist/alerts/pagerduty.d.ts +8 -0
  21. package/dist/alerts/pagerduty.d.ts.map +1 -0
  22. package/dist/alerts/pagerduty.js +164 -0
  23. package/dist/alerts/pagerduty.js.map +1 -0
  24. package/dist/alerts/resource.d.ts +23 -0
  25. package/dist/alerts/resource.d.ts.map +1 -0
  26. package/dist/alerts/resource.js +167 -0
  27. package/dist/alerts/resource.js.map +1 -0
  28. package/dist/alerts/slack.d.ts +7 -0
  29. package/dist/alerts/slack.d.ts.map +1 -0
  30. package/dist/alerts/slack.js +184 -0
  31. package/dist/alerts/slack.js.map +1 -0
  32. package/dist/alerts/telegram.d.ts +9 -0
  33. package/dist/alerts/telegram.d.ts.map +1 -0
  34. package/dist/alerts/telegram.js +133 -0
  35. package/dist/alerts/telegram.js.map +1 -0
  36. package/dist/alerts/templates.d.ts +109 -0
  37. package/dist/alerts/templates.d.ts.map +1 -0
  38. package/dist/alerts/templates.js +139 -0
  39. package/dist/alerts/templates.js.map +1 -0
  40. package/dist/alerts/types.d.ts +113 -0
  41. package/dist/alerts/types.d.ts.map +1 -0
  42. package/dist/alerts/types.js +87 -0
  43. package/dist/alerts/types.js.map +1 -0
  44. package/dist/alerts/webhook.d.ts +13 -0
  45. package/dist/alerts/webhook.d.ts.map +1 -0
  46. package/dist/alerts/webhook.js +56 -0
  47. package/dist/alerts/webhook.js.map +1 -0
  48. package/dist/cli/schemaFormatter.d.ts +11 -0
  49. package/dist/cli/schemaFormatter.d.ts.map +1 -0
  50. package/dist/cli/schemaFormatter.js +74 -0
  51. package/dist/cli/schemaFormatter.js.map +1 -0
  52. package/dist/commands/alerts.d.ts +3 -0
  53. package/dist/commands/alerts.d.ts.map +1 -0
  54. package/dist/commands/alerts.js +256 -0
  55. package/dist/commands/alerts.js.map +1 -0
  56. package/dist/commands/budget.d.ts +3 -0
  57. package/dist/commands/budget.d.ts.map +1 -0
  58. package/dist/commands/budget.js +47 -0
  59. package/dist/commands/budget.js.map +1 -0
  60. package/dist/commands/channels.d.ts +3 -0
  61. package/dist/commands/channels.d.ts.map +1 -0
  62. package/dist/commands/channels.js +84 -0
  63. package/dist/commands/channels.js.map +1 -0
  64. package/dist/commands/check.d.ts +3 -0
  65. package/dist/commands/check.d.ts.map +1 -0
  66. package/dist/commands/check.js +58 -0
  67. package/dist/commands/check.js.map +1 -0
  68. package/dist/commands/completion.d.ts +3 -0
  69. package/dist/commands/completion.d.ts.map +1 -0
  70. package/dist/commands/completion.js +36 -0
  71. package/dist/commands/completion.js.map +1 -0
  72. package/dist/commands/costs.d.ts +15 -0
  73. package/dist/commands/costs.d.ts.map +1 -0
  74. package/dist/commands/costs.js +280 -0
  75. package/dist/commands/costs.js.map +1 -0
  76. package/dist/commands/daemon.d.ts +3 -0
  77. package/dist/commands/daemon.d.ts.map +1 -0
  78. package/dist/commands/daemon.js +93 -0
  79. package/dist/commands/daemon.js.map +1 -0
  80. package/dist/commands/db.d.ts +3 -0
  81. package/dist/commands/db.d.ts.map +1 -0
  82. package/dist/commands/db.js +140 -0
  83. package/dist/commands/db.js.map +1 -0
  84. package/dist/commands/guard.d.ts +3 -0
  85. package/dist/commands/guard.d.ts.map +1 -0
  86. package/dist/commands/guard.js +181 -0
  87. package/dist/commands/guard.js.map +1 -0
  88. package/dist/commands/history.d.ts +3 -0
  89. package/dist/commands/history.d.ts.map +1 -0
  90. package/dist/commands/history.js +57 -0
  91. package/dist/commands/history.js.map +1 -0
  92. package/dist/commands/inspect.d.ts +3 -0
  93. package/dist/commands/inspect.d.ts.map +1 -0
  94. package/dist/commands/inspect.js +82 -0
  95. package/dist/commands/inspect.js.map +1 -0
  96. package/dist/commands/mcp.d.ts +3 -0
  97. package/dist/commands/mcp.d.ts.map +1 -0
  98. package/dist/commands/mcp.js +20 -0
  99. package/dist/commands/mcp.js.map +1 -0
  100. package/dist/commands/pause.d.ts +3 -0
  101. package/dist/commands/pause.d.ts.map +1 -0
  102. package/dist/commands/pause.js +32 -0
  103. package/dist/commands/pause.js.map +1 -0
  104. package/dist/commands/resources.d.ts +3 -0
  105. package/dist/commands/resources.d.ts.map +1 -0
  106. package/dist/commands/resources.js +95 -0
  107. package/dist/commands/resources.js.map +1 -0
  108. package/dist/commands/restore.d.ts +3 -0
  109. package/dist/commands/restore.d.ts.map +1 -0
  110. package/dist/commands/restore.js +92 -0
  111. package/dist/commands/restore.js.map +1 -0
  112. package/dist/commands/resume.d.ts +3 -0
  113. package/dist/commands/resume.d.ts.map +1 -0
  114. package/dist/commands/resume.js +31 -0
  115. package/dist/commands/resume.js.map +1 -0
  116. package/dist/commands/status.d.ts +3 -0
  117. package/dist/commands/status.d.ts.map +1 -0
  118. package/dist/commands/status.js +63 -0
  119. package/dist/commands/status.js.map +1 -0
  120. package/dist/commands/watch.d.ts +3 -0
  121. package/dist/commands/watch.d.ts.map +1 -0
  122. package/dist/commands/watch.js +163 -0
  123. package/dist/commands/watch.js.map +1 -0
  124. package/dist/core/aws_secrets.d.ts +13 -0
  125. package/dist/core/aws_secrets.d.ts.map +1 -0
  126. package/dist/core/aws_secrets.js +34 -0
  127. package/dist/core/aws_secrets.js.map +1 -0
  128. package/dist/core/budget.d.ts +10 -0
  129. package/dist/core/budget.d.ts.map +1 -0
  130. package/dist/core/budget.js +31 -0
  131. package/dist/core/budget.js.map +1 -0
  132. package/dist/core/channels.d.ts +67 -0
  133. package/dist/core/channels.d.ts.map +1 -0
  134. package/dist/core/channels.js +136 -0
  135. package/dist/core/channels.js.map +1 -0
  136. package/dist/core/check.d.ts +25 -0
  137. package/dist/core/check.d.ts.map +1 -0
  138. package/dist/core/check.js +85 -0
  139. package/dist/core/check.js.map +1 -0
  140. package/dist/core/completion.d.ts +9 -0
  141. package/dist/core/completion.d.ts.map +1 -0
  142. package/dist/core/completion.js +68 -0
  143. package/dist/core/completion.js.map +1 -0
  144. package/dist/core/costs.d.ts +90 -0
  145. package/dist/core/costs.d.ts.map +1 -0
  146. package/dist/core/costs.js +147 -0
  147. package/dist/core/costs.js.map +1 -0
  148. package/dist/core/decoder.d.ts +7 -0
  149. package/dist/core/decoder.d.ts.map +1 -0
  150. package/dist/core/decoder.js +38 -0
  151. package/dist/core/decoder.js.map +1 -0
  152. package/dist/core/discovery.d.ts +38 -0
  153. package/dist/core/discovery.d.ts.map +1 -0
  154. package/dist/core/discovery.js +208 -0
  155. package/dist/core/discovery.js.map +1 -0
  156. package/dist/core/extension.d.ts +83 -0
  157. package/dist/core/extension.d.ts.map +1 -0
  158. package/dist/core/extension.js +429 -0
  159. package/dist/core/extension.js.map +1 -0
  160. package/dist/core/inspect.d.ts +62 -0
  161. package/dist/core/inspect.d.ts.map +1 -0
  162. package/dist/core/inspect.js +303 -0
  163. package/dist/core/inspect.js.map +1 -0
  164. package/dist/core/introspection.d.ts +8 -0
  165. package/dist/core/introspection.d.ts.map +1 -0
  166. package/dist/core/introspection.js +8 -0
  167. package/dist/core/introspection.js.map +1 -0
  168. package/dist/core/monitor.d.ts +42 -0
  169. package/dist/core/monitor.d.ts.map +1 -0
  170. package/dist/core/monitor.js +188 -0
  171. package/dist/core/monitor.js.map +1 -0
  172. package/dist/core/rent_projection.d.ts +153 -0
  173. package/dist/core/rent_projection.d.ts.map +1 -0
  174. package/dist/core/rent_projection.js +146 -0
  175. package/dist/core/rent_projection.js.map +1 -0
  176. package/dist/core/scvalTranslator.d.ts +11 -0
  177. package/dist/core/scvalTranslator.d.ts.map +1 -0
  178. package/dist/core/scvalTranslator.js +36 -0
  179. package/dist/core/scvalTranslator.js.map +1 -0
  180. package/dist/core/state_diff.d.ts +45 -0
  181. package/dist/core/state_diff.d.ts.map +1 -0
  182. package/dist/core/state_diff.js +112 -0
  183. package/dist/core/state_diff.js.map +1 -0
  184. package/dist/core/status.d.ts +24 -0
  185. package/dist/core/status.d.ts.map +1 -0
  186. package/dist/core/status.js +57 -0
  187. package/dist/core/status.js.map +1 -0
  188. package/dist/core/vault.d.ts +37 -0
  189. package/dist/core/vault.d.ts.map +1 -0
  190. package/dist/core/vault.js +179 -0
  191. package/dist/core/vault.js.map +1 -0
  192. package/dist/core/watch.d.ts +36 -0
  193. package/dist/core/watch.d.ts.map +1 -0
  194. package/dist/core/watch.js +176 -0
  195. package/dist/core/watch.js.map +1 -0
  196. package/dist/daemon/loop.d.ts +38 -0
  197. package/dist/daemon/loop.d.ts.map +1 -0
  198. package/dist/daemon/loop.js +169 -0
  199. package/dist/daemon/loop.js.map +1 -0
  200. package/dist/db/backup.d.ts +12 -0
  201. package/dist/db/backup.d.ts.map +1 -0
  202. package/dist/db/backup.js +132 -0
  203. package/dist/db/backup.js.map +1 -0
  204. package/dist/db/budget.d.ts +17 -0
  205. package/dist/db/budget.d.ts.map +1 -0
  206. package/dist/db/budget.js +31 -0
  207. package/dist/db/budget.js.map +1 -0
  208. package/dist/db/database.d.ts +6 -0
  209. package/dist/db/database.d.ts.map +1 -0
  210. package/dist/db/database.js +134 -0
  211. package/dist/db/database.js.map +1 -0
  212. package/dist/db/migrations/.gitkeep +1 -0
  213. package/dist/db/migrations/001_resource_usage_logs.sql +28 -0
  214. package/dist/db/migrator.d.ts +28 -0
  215. package/dist/db/migrator.d.ts.map +1 -0
  216. package/dist/db/migrator.js +75 -0
  217. package/dist/db/migrator.js.map +1 -0
  218. package/dist/db/repositories.d.ts +474 -0
  219. package/dist/db/repositories.d.ts.map +1 -0
  220. package/dist/db/repositories.js +809 -0
  221. package/dist/db/repositories.js.map +1 -0
  222. package/dist/db/schema.sql +207 -0
  223. package/dist/index.d.ts +3 -0
  224. package/dist/index.d.ts.map +1 -0
  225. package/dist/index.js +47 -0
  226. package/dist/index.js.map +1 -0
  227. package/dist/lib.d.ts +20 -0
  228. package/dist/lib.d.ts.map +1 -0
  229. package/dist/lib.js +16 -0
  230. package/dist/lib.js.map +1 -0
  231. package/dist/logging/index.d.ts +22 -0
  232. package/dist/logging/index.d.ts.map +1 -0
  233. package/dist/logging/index.js +34 -0
  234. package/dist/logging/index.js.map +1 -0
  235. package/dist/logging/logger.d.ts +14 -0
  236. package/dist/logging/logger.d.ts.map +1 -0
  237. package/dist/logging/logger.js +135 -0
  238. package/dist/logging/logger.js.map +1 -0
  239. package/dist/logging/types.d.ts +10 -0
  240. package/dist/logging/types.d.ts.map +1 -0
  241. package/dist/logging/types.js +2 -0
  242. package/dist/logging/types.js.map +1 -0
  243. package/dist/mcp/index.d.ts +3 -0
  244. package/dist/mcp/index.d.ts.map +1 -0
  245. package/dist/mcp/index.js +14 -0
  246. package/dist/mcp/index.js.map +1 -0
  247. package/dist/mcp/server.d.ts +10 -0
  248. package/dist/mcp/server.d.ts.map +1 -0
  249. package/dist/mcp/server.js +51 -0
  250. package/dist/mcp/server.js.map +1 -0
  251. package/dist/mcp/tools/get-extension-costs.d.ts +4 -0
  252. package/dist/mcp/tools/get-extension-costs.d.ts.map +1 -0
  253. package/dist/mcp/tools/get-extension-costs.js +33 -0
  254. package/dist/mcp/tools/get-extension-costs.js.map +1 -0
  255. package/dist/mcp/tools/get_contract_status.d.ts +8 -0
  256. package/dist/mcp/tools/get_contract_status.d.ts.map +1 -0
  257. package/dist/mcp/tools/get_contract_status.js +33 -0
  258. package/dist/mcp/tools/get_contract_status.js.map +1 -0
  259. package/dist/rpc/client.d.ts +154 -0
  260. package/dist/rpc/client.d.ts.map +1 -0
  261. package/dist/rpc/client.js +766 -0
  262. package/dist/rpc/client.js.map +1 -0
  263. package/dist/utils/config.d.ts +51 -0
  264. package/dist/utils/config.d.ts.map +1 -0
  265. package/dist/utils/config.js +86 -0
  266. package/dist/utils/config.js.map +1 -0
  267. package/dist/utils/formatting.d.ts +10 -0
  268. package/dist/utils/formatting.d.ts.map +1 -0
  269. package/dist/utils/formatting.js +66 -0
  270. package/dist/utils/formatting.js.map +1 -0
  271. package/dist/utils/watch-config.d.ts +13 -0
  272. package/dist/utils/watch-config.d.ts.map +1 -0
  273. package/dist/utils/watch-config.js +32 -0
  274. package/dist/utils/watch-config.js.map +1 -0
  275. 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
+ &middot;
11
+ <a href="#quick-start">Quick Start</a>
12
+ &middot;
13
+ <a href="#commands">Commands</a>
14
+ &middot;
15
+ <a href="#alerting">Alerting</a>
16
+ &middot;
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)