auramaxx 1.0.0-alpha.4

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 (363) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +112 -0
  3. package/bin/aurawallet.js +121 -0
  4. package/docs/ADAPTERS.md +467 -0
  5. package/docs/API.md +2679 -0
  6. package/docs/APPS.md +198 -0
  7. package/docs/ARCHITECTURE.md +350 -0
  8. package/docs/AUTH.md +698 -0
  9. package/docs/BEST-PRACTICES.md +121 -0
  10. package/docs/CLI.md +61 -0
  11. package/docs/DEVELOPING-APPS.md +452 -0
  12. package/docs/EXTENSION.md +97 -0
  13. package/docs/JOBS.md +33 -0
  14. package/docs/MCP.md +76 -0
  15. package/docs/PROTOCOL.md +142 -0
  16. package/docs/SETUP.md +219 -0
  17. package/docs/WORKSPACE.md +672 -0
  18. package/docs/agent-auth.md +63 -0
  19. package/docs/aura-file.md +48 -0
  20. package/docs/credentials.md +53 -0
  21. package/docs/external/getting-started.md +65 -0
  22. package/docs/external/overview.md +45 -0
  23. package/docs/external/use-cases.md +48 -0
  24. package/docs/external/why-aura.md +35 -0
  25. package/docs/jobs/connect-agent.md +77 -0
  26. package/docs/jobs/migrate-from-dotenv.md +79 -0
  27. package/docs/jobs/recover-from-lockout.md +72 -0
  28. package/docs/jobs/secure-ci.md +63 -0
  29. package/docs/oauth2.md +42 -0
  30. package/docs/passkeys.md +60 -0
  31. package/docs/security.md +540 -0
  32. package/docs/specs/aura-open-protocol.md +61 -0
  33. package/docs/specs/aura-provider-plugin.md +24 -0
  34. package/docs/specs/aura-registry-model.md +31 -0
  35. package/docs/specs/fixtures/invalid-bad-key.aura +1 -0
  36. package/docs/specs/fixtures/invalid-bad-unicode-escape.aura +1 -0
  37. package/docs/specs/fixtures/invalid-duplicate-key.aura +2 -0
  38. package/docs/specs/fixtures/valid-basic.aura +4 -0
  39. package/docs/specs/fixtures/valid-provider-ref.aura +1 -0
  40. package/docs/specs/fixtures/valid-quoted-escapes.aura +2 -0
  41. package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -0
  42. package/docs/totp.md +40 -0
  43. package/docs/wallet/AI.md +508 -0
  44. package/docs/wallet/DEVELOPING-STRATEGIES.md +713 -0
  45. package/docs/wallet/README.md +47 -0
  46. package/docs/wallet/STRATEGY.md +89 -0
  47. package/next.config.ts +21 -0
  48. package/package.json +151 -0
  49. package/postcss.config.mjs +8 -0
  50. package/prisma/migrations/20260214170000_baseline/migration.sql +511 -0
  51. package/prisma/migrations/20260216214537_add_passkey_model/migration.sql +18 -0
  52. package/prisma/migrations/20260217150500_add_credential_access_audit/migration.sql +31 -0
  53. package/prisma/migrations/migration_lock.toml +3 -0
  54. package/prisma/schema.prisma +447 -0
  55. package/public/logo-chevron.svg +31 -0
  56. package/public/logo-concentric.svg +31 -0
  57. package/public/logo-crosshatch.svg +39 -0
  58. package/public/logo-dashed.svg +39 -0
  59. package/public/logo-horizontal.svg +31 -0
  60. package/public/logo-m56.svg +64 -0
  61. package/public/logo.webp +0 -0
  62. package/scripts/add-app.js +245 -0
  63. package/scripts/init.sh +57 -0
  64. package/scripts/migrate-apikeys-to-credentials.ts +35 -0
  65. package/scripts/sandbox-agent-flow.sh +235 -0
  66. package/scripts/sandbox.sh +175 -0
  67. package/scripts/validate-job-docs.mjs +125 -0
  68. package/server/abi/SwapHelper.json +438 -0
  69. package/server/cli/approval.ts +447 -0
  70. package/server/cli/commands/app.ts +204 -0
  71. package/server/cli/commands/cron.ts +24 -0
  72. package/server/cli/commands/doctor.ts +1007 -0
  73. package/server/cli/commands/env.ts +456 -0
  74. package/server/cli/commands/init.ts +752 -0
  75. package/server/cli/commands/mcp.ts +125 -0
  76. package/server/cli/commands/restore.ts +314 -0
  77. package/server/cli/commands/shell-hook.ts +468 -0
  78. package/server/cli/commands/start.ts +62 -0
  79. package/server/cli/commands/status.ts +59 -0
  80. package/server/cli/commands/stop.ts +14 -0
  81. package/server/cli/commands/token.ts +180 -0
  82. package/server/cli/commands/unlock.ts +49 -0
  83. package/server/cli/commands/vault.ts +417 -0
  84. package/server/cli/index.ts +328 -0
  85. package/server/cli/lib/aura-parser.ts +64 -0
  86. package/server/cli/lib/credential-create.ts +74 -0
  87. package/server/cli/lib/credential-resolve.ts +254 -0
  88. package/server/cli/lib/dotenv-migrate.ts +116 -0
  89. package/server/cli/lib/dotenv-parser.ts +146 -0
  90. package/server/cli/lib/http.ts +91 -0
  91. package/server/cli/lib/init-steps.ts +76 -0
  92. package/server/cli/lib/local-agent-trust.ts +45 -0
  93. package/server/cli/lib/process.ts +136 -0
  94. package/server/cli/lib/prompt.ts +85 -0
  95. package/server/cli/lib/theme.ts +240 -0
  96. package/server/cli/socket.ts +570 -0
  97. package/server/cli/transport-client.ts +50 -0
  98. package/server/cron/index.ts +137 -0
  99. package/server/cron/job.ts +31 -0
  100. package/server/cron/jobs/balance-sync.ts +436 -0
  101. package/server/cron/jobs/incoming-scan.ts +506 -0
  102. package/server/cron/jobs/native-price.ts +70 -0
  103. package/server/cron/jobs/orphan-cleanup.ts +40 -0
  104. package/server/cron/jobs/strategy-runner.ts +175 -0
  105. package/server/cron/scheduler.ts +125 -0
  106. package/server/index.ts +406 -0
  107. package/server/lib/adapters/factory.ts +110 -0
  108. package/server/lib/adapters/index.ts +19 -0
  109. package/server/lib/adapters/router.ts +297 -0
  110. package/server/lib/adapters/telegram.ts +645 -0
  111. package/server/lib/adapters/types.ts +89 -0
  112. package/server/lib/adapters/webhook.ts +95 -0
  113. package/server/lib/address.ts +49 -0
  114. package/server/lib/agent-auth/contracts.ts +1194 -0
  115. package/server/lib/agent-profiles.ts +328 -0
  116. package/server/lib/ai.ts +285 -0
  117. package/server/lib/api-registry/contracts.ts +86 -0
  118. package/server/lib/api-registry/validation.ts +172 -0
  119. package/server/lib/apikey-migration.ts +189 -0
  120. package/server/lib/app-installer.ts +505 -0
  121. package/server/lib/app-tokens.ts +247 -0
  122. package/server/lib/auth.ts +314 -0
  123. package/server/lib/batch.ts +242 -0
  124. package/server/lib/cold.ts +874 -0
  125. package/server/lib/config.ts +381 -0
  126. package/server/lib/credential-access-audit.ts +85 -0
  127. package/server/lib/credential-access-policy.ts +110 -0
  128. package/server/lib/credential-health.ts +343 -0
  129. package/server/lib/credential-import.ts +487 -0
  130. package/server/lib/credential-scope.ts +87 -0
  131. package/server/lib/credential-shares.ts +190 -0
  132. package/server/lib/credential-transport.ts +342 -0
  133. package/server/lib/credential-vault.ts +77 -0
  134. package/server/lib/credentials.ts +333 -0
  135. package/server/lib/crypto.ts +8 -0
  136. package/server/lib/db.ts +15 -0
  137. package/server/lib/defaults.ts +366 -0
  138. package/server/lib/dex/index.ts +80 -0
  139. package/server/lib/dex/relay.ts +235 -0
  140. package/server/lib/dex/types.ts +59 -0
  141. package/server/lib/dex/uniswap.ts +370 -0
  142. package/server/lib/e2e-agent/artifacts.ts +36 -0
  143. package/server/lib/e2e-agent/contracts.ts +112 -0
  144. package/server/lib/e2e-agent/validation.ts +135 -0
  145. package/server/lib/encrypt.ts +128 -0
  146. package/server/lib/error.ts +20 -0
  147. package/server/lib/events.ts +205 -0
  148. package/server/lib/hot.ts +357 -0
  149. package/server/lib/key-fingerprint.ts +28 -0
  150. package/server/lib/logger.ts +331 -0
  151. package/server/lib/network.ts +137 -0
  152. package/server/lib/notifications.ts +219 -0
  153. package/server/lib/oauth2-refresh.ts +241 -0
  154. package/server/lib/oursecret.ts +54 -0
  155. package/server/lib/passkey-credential.ts +360 -0
  156. package/server/lib/passkey.ts +68 -0
  157. package/server/lib/permissions.ts +248 -0
  158. package/server/lib/pino.ts +24 -0
  159. package/server/lib/policy-preview.ts +138 -0
  160. package/server/lib/price.ts +338 -0
  161. package/server/lib/prices.ts +34 -0
  162. package/server/lib/project-scope.ts +239 -0
  163. package/server/lib/resolve-action.ts +427 -0
  164. package/server/lib/resolve.ts +36 -0
  165. package/server/lib/sessions.ts +632 -0
  166. package/server/lib/solana/connection.ts +26 -0
  167. package/server/lib/solana/jupiter.ts +128 -0
  168. package/server/lib/solana/transfer.ts +108 -0
  169. package/server/lib/solana/wallet.ts +136 -0
  170. package/server/lib/strategy/emits.ts +21 -0
  171. package/server/lib/strategy/engine.ts +1305 -0
  172. package/server/lib/strategy/executor.ts +115 -0
  173. package/server/lib/strategy/hook-context.ts +158 -0
  174. package/server/lib/strategy/hooks.ts +990 -0
  175. package/server/lib/strategy/index.ts +28 -0
  176. package/server/lib/strategy/installer.ts +305 -0
  177. package/server/lib/strategy/loader.ts +256 -0
  178. package/server/lib/strategy/message.ts +235 -0
  179. package/server/lib/strategy/repository.ts +218 -0
  180. package/server/lib/strategy/session-logger.ts +693 -0
  181. package/server/lib/strategy/sources.ts +288 -0
  182. package/server/lib/strategy/state.ts +189 -0
  183. package/server/lib/strategy/templates.ts +403 -0
  184. package/server/lib/strategy/tick.ts +404 -0
  185. package/server/lib/strategy/types.ts +230 -0
  186. package/server/lib/swap.ts +3 -0
  187. package/server/lib/temp.ts +86 -0
  188. package/server/lib/token-metadata.ts +86 -0
  189. package/server/lib/token-safety.ts +200 -0
  190. package/server/lib/token-search.ts +444 -0
  191. package/server/lib/totp.ts +194 -0
  192. package/server/lib/transactions.ts +123 -0
  193. package/server/lib/transport.ts +75 -0
  194. package/server/lib/txhistory/decoder.ts +262 -0
  195. package/server/lib/txhistory/enricher.ts +652 -0
  196. package/server/lib/txhistory/index.ts +391 -0
  197. package/server/lib/txhistory/signatures.ts +59 -0
  198. package/server/lib/verified-summary.ts +421 -0
  199. package/server/mcp/profile-policy.ts +30 -0
  200. package/server/mcp/server.ts +619 -0
  201. package/server/mcp/tools.ts +523 -0
  202. package/server/middleware/auth.ts +119 -0
  203. package/server/middleware/requestLogger.ts +84 -0
  204. package/server/routes/actions.ts +459 -0
  205. package/server/routes/adapters.ts +703 -0
  206. package/server/routes/addressbook.ts +113 -0
  207. package/server/routes/ai.ts +34 -0
  208. package/server/routes/apikeys.ts +295 -0
  209. package/server/routes/apps.ts +601 -0
  210. package/server/routes/auth.ts +457 -0
  211. package/server/routes/backup.ts +340 -0
  212. package/server/routes/batch.ts +270 -0
  213. package/server/routes/bookmarks.ts +162 -0
  214. package/server/routes/credential-shares.ts +198 -0
  215. package/server/routes/credential-vaults.ts +154 -0
  216. package/server/routes/credentials.ts +1290 -0
  217. package/server/routes/dashboard.ts +71 -0
  218. package/server/routes/defaults.ts +124 -0
  219. package/server/routes/fund.ts +229 -0
  220. package/server/routes/import.ts +352 -0
  221. package/server/routes/launch.ts +665 -0
  222. package/server/routes/lock.ts +54 -0
  223. package/server/routes/logs.ts +68 -0
  224. package/server/routes/nuke.ts +111 -0
  225. package/server/routes/passkey-credentials.ts +99 -0
  226. package/server/routes/passkey.ts +346 -0
  227. package/server/routes/portfolio.ts +217 -0
  228. package/server/routes/price.ts +63 -0
  229. package/server/routes/resolve.ts +31 -0
  230. package/server/routes/security.ts +45 -0
  231. package/server/routes/send-evm.ts +241 -0
  232. package/server/routes/send-solana.ts +281 -0
  233. package/server/routes/send.ts +178 -0
  234. package/server/routes/setup.ts +210 -0
  235. package/server/routes/strategy.ts +894 -0
  236. package/server/routes/swap-evm.ts +353 -0
  237. package/server/routes/swap-solana.ts +177 -0
  238. package/server/routes/swap.ts +356 -0
  239. package/server/routes/token.ts +247 -0
  240. package/server/routes/unlock.ts +403 -0
  241. package/server/routes/wallet-assets.ts +361 -0
  242. package/server/routes/wallet-transactions.ts +515 -0
  243. package/server/routes/wallet.ts +710 -0
  244. package/server/types.ts +146 -0
  245. package/skills/aurawallet/SKILL.md +739 -0
  246. package/skills/aurawallet-setup/SKILL.md +74 -0
  247. package/skills/security-review/SKILL.md +148 -0
  248. package/src/app/api/agent-requests/route.ts +30 -0
  249. package/src/app/api/apps/install/route.ts +126 -0
  250. package/src/app/api/apps/manifests/route.ts +16 -0
  251. package/src/app/api/apps/static/[...path]/route.ts +57 -0
  252. package/src/app/api/events/route.ts +92 -0
  253. package/src/app/api/page.tsx +212 -0
  254. package/src/app/api/workspace/[id]/apps/[wid]/route.ts +119 -0
  255. package/src/app/api/workspace/[id]/apps/route.ts +81 -0
  256. package/src/app/api/workspace/[id]/export/route.ts +67 -0
  257. package/src/app/api/workspace/[id]/route.ts +168 -0
  258. package/src/app/api/workspace/auth.ts +34 -0
  259. package/src/app/api/workspace/config/route.ts +106 -0
  260. package/src/app/api/workspace/import/route.ts +127 -0
  261. package/src/app/api/workspace/route.ts +116 -0
  262. package/src/app/app/page.tsx +2122 -0
  263. package/src/app/apple-icon.png +0 -0
  264. package/src/app/docs/page.tsx +178 -0
  265. package/src/app/favicon.ico +0 -0
  266. package/src/app/globals.css +572 -0
  267. package/src/app/health/page.tsx +5 -0
  268. package/src/app/hello/page.tsx +15 -0
  269. package/src/app/icon.png +0 -0
  270. package/src/app/layout.tsx +34 -0
  271. package/src/app/page.tsx +986 -0
  272. package/src/app/providers.tsx +90 -0
  273. package/src/app/share/[token]/page.tsx +295 -0
  274. package/src/components/ChainSelector.tsx +144 -0
  275. package/src/components/HumanActionBar.tsx +695 -0
  276. package/src/components/NotificationDrawer.tsx +129 -0
  277. package/src/components/apps/AgentKeysApp.tsx +490 -0
  278. package/src/components/apps/App.tsx +153 -0
  279. package/src/components/apps/AppGrid.tsx +15 -0
  280. package/src/components/apps/DetailedAddressDrawer.tsx +325 -0
  281. package/src/components/apps/DraggableApp.tsx +562 -0
  282. package/src/components/apps/IFrameApp.tsx +73 -0
  283. package/src/components/apps/LogsApp.tsx +360 -0
  284. package/src/components/apps/SendApp.tsx +394 -0
  285. package/src/components/apps/SetupWizardApp.tsx +1004 -0
  286. package/src/components/apps/SystemDefaultsApp.tsx +845 -0
  287. package/src/components/apps/ThirdPartyApp.tsx +428 -0
  288. package/src/components/apps/TokenApp.tsx +319 -0
  289. package/src/components/apps/TransactionsApp.tsx +438 -0
  290. package/src/components/apps/WalletDetailApp.tsx +1505 -0
  291. package/src/components/apps/index.ts +13 -0
  292. package/src/components/design-system/Button.tsx +53 -0
  293. package/src/components/design-system/ChainIndicator.tsx +65 -0
  294. package/src/components/design-system/ChainSelector.tsx +137 -0
  295. package/src/components/design-system/ConfirmationModal.tsx +106 -0
  296. package/src/components/design-system/ConfirmationPopover.tsx +81 -0
  297. package/src/components/design-system/Drawer.tsx +123 -0
  298. package/src/components/design-system/FilterDropdown.tsx +72 -0
  299. package/src/components/design-system/Modal.tsx +206 -0
  300. package/src/components/design-system/Popover.tsx +142 -0
  301. package/src/components/design-system/TextInput.tsx +85 -0
  302. package/src/components/design-system/Toggle.tsx +58 -0
  303. package/src/components/design-system/index.ts +11 -0
  304. package/src/components/docs/DocsThemeToggle.tsx +49 -0
  305. package/src/components/health/CredentialHealthDashboard.tsx +214 -0
  306. package/src/components/icons/ChainIcons.tsx +72 -0
  307. package/src/components/layout/AppStoreDrawer.tsx +369 -0
  308. package/src/components/layout/ContentArea.tsx +21 -0
  309. package/src/components/layout/TabBar.tsx +278 -0
  310. package/src/components/layout/WalletSidebar.tsx +1033 -0
  311. package/src/components/layout/index.ts +4 -0
  312. package/src/components/marketing/AuraWalletSpecOverlay.tsx +635 -0
  313. package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
  314. package/src/components/vault/ApiKeysConsole.tsx +1080 -0
  315. package/src/components/vault/AuditConsole.tsx +584 -0
  316. package/src/components/vault/CredentialDetail.tsx +455 -0
  317. package/src/components/vault/CredentialEmpty.tsx +55 -0
  318. package/src/components/vault/CredentialField.tsx +361 -0
  319. package/src/components/vault/CredentialForm.tsx +1212 -0
  320. package/src/components/vault/CredentialList.tsx +165 -0
  321. package/src/components/vault/CredentialRow.tsx +97 -0
  322. package/src/components/vault/CredentialShareModal.tsx +178 -0
  323. package/src/components/vault/CredentialVault.tsx +754 -0
  324. package/src/components/vault/CredentialWalletWidget.tsx +103 -0
  325. package/src/components/vault/ImportCredentialsModal.tsx +515 -0
  326. package/src/components/vault/LargeTypeModal.tsx +64 -0
  327. package/src/components/vault/PasswordGenerator.tsx +224 -0
  328. package/src/components/vault/TOTPDisplay.tsx +123 -0
  329. package/src/components/vault/VaultSidebar.tsx +413 -0
  330. package/src/components/vault/types.ts +54 -0
  331. package/src/context/AuthContext.tsx +337 -0
  332. package/src/context/PriceContext.tsx +113 -0
  333. package/src/context/ThemeContext.tsx +164 -0
  334. package/src/context/WebSocketContext.tsx +269 -0
  335. package/src/context/WorkspaceContext.tsx +668 -0
  336. package/src/hooks/index.ts +3 -0
  337. package/src/hooks/useAgentActions.ts +368 -0
  338. package/src/hooks/useBalance.ts +103 -0
  339. package/src/hooks/useBalances.ts +129 -0
  340. package/src/instrumentation.ts +12 -0
  341. package/src/lib/api.ts +449 -0
  342. package/src/lib/app-loader.ts +148 -0
  343. package/src/lib/app-registry.ts +178 -0
  344. package/src/lib/app-sdk.ts +157 -0
  345. package/src/lib/audit-console-adapter.ts +151 -0
  346. package/src/lib/auth-client.ts +75 -0
  347. package/src/lib/config.ts +74 -0
  348. package/src/lib/crypto.ts +112 -0
  349. package/src/lib/db.ts +21 -0
  350. package/src/lib/docs.ts +390 -0
  351. package/src/lib/events.ts +361 -0
  352. package/src/lib/pino.ts +24 -0
  353. package/src/lib/theme-handlers.ts +168 -0
  354. package/src/lib/theme.ts +351 -0
  355. package/src/lib/tokenData.ts +378 -0
  356. package/src/lib/vault-crypto.ts +129 -0
  357. package/src/lib/websocket-server.ts +302 -0
  358. package/src/lib/websocket-setup.ts +79 -0
  359. package/src/lib/wordlist.ts +2050 -0
  360. package/src/lib/workspace-handlers.ts +285 -0
  361. package/start.sh +80 -0
  362. package/tailwind.config.ts +99 -0
  363. package/tsconfig.json +42 -0
@@ -0,0 +1,71 @@
1
+ import { Router, Request, Response } from 'express';
2
+ import { prisma } from '../lib/db';
3
+ import { listTokensFromDb } from '../lib/sessions';
4
+ import { getAdminTokenHashes } from '../lib/auth';
5
+ import { getErrorMessage } from '../lib/error';
6
+
7
+ const router = Router();
8
+
9
+ // GET /dashboard - Combined view of pending requests and agent tokens
10
+ router.get('/', async (_req: Request, res: Response) => {
11
+ try {
12
+ // Get pending requests
13
+ const pendingActions = await prisma.humanAction.findMany({
14
+ where: {
15
+ status: 'pending',
16
+ NOT: { type: 'strategy:message' },
17
+ },
18
+ orderBy: { createdAt: 'desc' }
19
+ });
20
+
21
+ // Get all tokens from DB (with isActive flag from memory check)
22
+ const allTokens = await listTokensFromDb();
23
+
24
+ // Exclude admin tokens from DB list — they're added separately from getAdminTokenHashes()
25
+ const nonAdminTokens = allTokens.filter(t => t.agentId !== 'admin');
26
+
27
+ // Active = in memory + not expired + not revoked + (no fund limit OR has remaining)
28
+ const agentActiveTokens = nonAdminTokens.filter(t => t.isActive && (t.limit === 0 || t.remaining > 0));
29
+
30
+ // Inactive = not in memory (server restarted) OR expired OR revoked OR depleted (has limit but none remaining)
31
+ const inactiveTokens = nonAdminTokens.filter(t => !t.isActive || (t.limit > 0 && t.remaining <= 0));
32
+
33
+ // Get admin token hashes and create admin token entries
34
+ const adminHashes = getAdminTokenHashes();
35
+ const adminTokens = adminHashes.map(hash => ({
36
+ tokenHash: hash,
37
+ agentId: 'admin',
38
+ isAdmin: true,
39
+ limit: 0,
40
+ spent: 0,
41
+ remaining: Infinity,
42
+ permissions: ['admin:*'],
43
+ expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000, // 7 days
44
+ isExpired: false,
45
+ isRevoked: false,
46
+ isActive: true,
47
+ }));
48
+
49
+ // Combine: admin tokens first, then agent tokens
50
+ const activeTokens = [...adminTokens, ...agentActiveTokens];
51
+
52
+ res.json({
53
+ success: true,
54
+ requests: pendingActions,
55
+ tokens: {
56
+ active: activeTokens,
57
+ inactive: inactiveTokens
58
+ },
59
+ counts: {
60
+ pendingActions: pendingActions.length,
61
+ activeTokens: activeTokens.length,
62
+ inactiveTokens: inactiveTokens.length
63
+ }
64
+ });
65
+ } catch (error) {
66
+ const message = getErrorMessage(error);
67
+ res.status(500).json({ error: message });
68
+ }
69
+ });
70
+
71
+ export default router;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * System Defaults Routes
3
+ * ======================
4
+ * Admin-only endpoints for managing centralized system defaults.
5
+ *
6
+ * GET /defaults — All defaults grouped by type
7
+ * PATCH /defaults/:key — Update a single default
8
+ * POST /defaults/reset — Reset one or all defaults to seed values
9
+ */
10
+
11
+ import { Router, Request, Response } from 'express';
12
+ import { requireWalletAuth } from '../middleware/auth';
13
+ import { requireAdmin } from '../lib/permissions';
14
+ import { getAllDefaults, setDefault, resetDefault, SEED_DEFAULTS, getDefault } from '../lib/defaults';
15
+ import { events } from '../lib/events';
16
+ import { getErrorMessage } from '../lib/error';
17
+
18
+ const router = Router();
19
+
20
+ // All defaults routes require admin access
21
+ router.use(requireWalletAuth, requireAdmin);
22
+
23
+ /**
24
+ * GET /defaults — List all defaults grouped by type
25
+ */
26
+ router.get('/', async (_req: Request, res: Response) => {
27
+ try {
28
+ const grouped = await getAllDefaults();
29
+ res.json({ success: true, defaults: grouped });
30
+ } catch (error) {
31
+ const message = getErrorMessage(error);
32
+ res.status(500).json({ success: false, error: message });
33
+ }
34
+ });
35
+
36
+ /**
37
+ * PATCH /defaults/:key — Update a single default value
38
+ * Body: { value: any }
39
+ */
40
+ router.patch('/:key', async (req: Request<{ key: string }>, res: Response) => {
41
+ try {
42
+ const { key } = req.params;
43
+ const { value } = req.body;
44
+
45
+ if (value === undefined) {
46
+ res.status(400).json({ success: false, error: 'value is required' });
47
+ return;
48
+ }
49
+
50
+ // Validate the key exists in seeds (don't allow creating arbitrary keys via PATCH)
51
+ const seed = SEED_DEFAULTS.find(s => s.key === key);
52
+ if (!seed) {
53
+ res.status(404).json({ success: false, error: `Unknown default key: ${key}` });
54
+ return;
55
+ }
56
+
57
+ // Type-check: ensure value type matches seed type roughly
58
+ const seedType = typeof seed.value;
59
+ const valueType = typeof value;
60
+
61
+ if (seedType === 'number' && valueType !== 'number') {
62
+ res.status(400).json({ success: false, error: `Expected number for ${key}, got ${valueType}` });
63
+ return;
64
+ }
65
+
66
+ if (Array.isArray(seed.value) && !Array.isArray(value)) {
67
+ res.status(400).json({ success: false, error: `Expected array for ${key}` });
68
+ return;
69
+ }
70
+
71
+ const previousValue = key === 'trust.localProfile'
72
+ ? await getDefault<string>('trust.localProfile', 'dev')
73
+ : null;
74
+
75
+ await setDefault(key, value);
76
+
77
+ if (key === 'trust.localProfile') {
78
+ const previousProfile = typeof previousValue === 'string' ? previousValue.trim() : 'dev';
79
+ const nextProfile = typeof value === 'string' ? value.trim() : '';
80
+ const dangerousModeChanged = (previousProfile === 'admin') !== (nextProfile === 'admin');
81
+
82
+ if (dangerousModeChanged) {
83
+ events.custom('trust:local_dangerous_mode_changed', {
84
+ actorType: req.auth?.token?.agentId ? 'agent' : 'admin',
85
+ actorId: req.auth?.token?.agentId ?? 'admin',
86
+ tokenHash: req.auth?.tokenHash,
87
+ key,
88
+ previousValue: previousProfile,
89
+ nextValue: nextProfile,
90
+ timestamp: Date.now(),
91
+ });
92
+ }
93
+ }
94
+
95
+ res.json({ success: true, key, value });
96
+ } catch (error) {
97
+ const message = getErrorMessage(error);
98
+ res.status(500).json({ success: false, error: message });
99
+ }
100
+ });
101
+
102
+ /**
103
+ * POST /defaults/reset — Reset one or all defaults to seed values
104
+ * Body: { key: string } — use "*" to reset all
105
+ */
106
+ router.post('/reset', async (req: Request, res: Response) => {
107
+ try {
108
+ const { key } = req.body;
109
+
110
+ if (!key || typeof key !== 'string') {
111
+ res.status(400).json({ success: false, error: 'key is required (use "*" to reset all)' });
112
+ return;
113
+ }
114
+
115
+ await resetDefault(key);
116
+
117
+ res.json({ success: true, key, reset: true });
118
+ } catch (error) {
119
+ const message = getErrorMessage(error);
120
+ res.status(500).json({ success: false, error: message });
121
+ }
122
+ });
123
+
124
+ export default router;
@@ -0,0 +1,229 @@
1
+ import { Router, Request, Response } from 'express';
2
+ import { ethers } from 'ethers';
3
+ import { PublicKey } from '@solana/web3.js';
4
+ import { reserveSpend, releaseSpend, getRemainingByType } from '../lib/sessions';
5
+ import { tokenCanAccessWallet, getHotWallet } from '../lib/hot';
6
+ import {
7
+ isUnlocked, getColdWalletAddress, signWithColdWallet, getSolanaColdKeypair, getSolanaColdAddress,
8
+ isVaultUnlocked, getVaultAddress, getVaultSolanaAddress, getVaultSolanaKeypair, signWithVault, getPrimaryVaultId
9
+ } from '../lib/cold';
10
+ import { prisma } from '../lib/db';
11
+ import { getRpcUrl, resolveChain } from '../lib/config';
12
+ import { logger } from '../lib/logger';
13
+ import { requireWalletAuth } from '../middleware/auth';
14
+ import { hasAnyPermission, isAdmin } from '../lib/permissions';
15
+ import { isSolanaChain, normalizeAddress, getNativeAddress, getNativeCurrency } from '../lib/address';
16
+ import { getSolanaConnection } from '../lib/solana/connection';
17
+ import { buildSolTransfer, sendSolanaTransaction } from '../lib/solana/transfer';
18
+ import { getErrorMessage, HttpError } from '../lib/error';
19
+
20
+ const router = Router();
21
+
22
+ /**
23
+ * POST /fund - Agent transfers funds from cold wallet to their hot wallet
24
+ *
25
+ * This executes immediately if the cold wallet is unlocked.
26
+ * The agent's spending limit is checked and deducted.
27
+ *
28
+ * Security checks:
29
+ * 1. Valid bearer token (HMAC signature)
30
+ * 2. Token not revoked
31
+ * 3. Token can access the target hot wallet (owns or has walletAccess grant)
32
+ * 4. Token has fund permission
33
+ * 5. Amount within remaining spending limit (fund limit)
34
+ * 6. Cold wallet must be unlocked
35
+ */
36
+ router.post('/', requireWalletAuth, async (req: Request, res: Response) => {
37
+ let rollback = () => {};
38
+ try {
39
+ const { to, amount, chain } = req.body;
40
+ const auth = req.auth!;
41
+
42
+ // Validate required fields
43
+ if (!to || typeof to !== 'string') {
44
+ res.status(400).json({ error: 'to (hot wallet address) is required' });
45
+ return;
46
+ }
47
+
48
+ if (!amount || (typeof amount !== 'string' && typeof amount !== 'number')) {
49
+ res.status(400).json({ error: 'amount is required (in wei for EVM or lamports for Solana)' });
50
+ return;
51
+ }
52
+
53
+ // Amount is in wei (EVM) or lamports (Solana). Parse as BigInt.
54
+ const amountWei = BigInt(amount);
55
+ if (amountWei <= 0n) {
56
+ res.status(400).json({ error: 'amount must be a positive number' });
57
+ return;
58
+ }
59
+
60
+ // Determine chain first (needed to compute decimal amount for limit checks)
61
+ const { targetChain } = resolveChain(chain);
62
+
63
+ const currency = getNativeAddress(targetChain);
64
+ const nativeCurrency = getNativeCurrency(targetChain);
65
+
66
+ // Convert wei/lamports to decimal for limit checks
67
+ const amountNum = isSolanaChain(targetChain)
68
+ ? Number(amountWei) / 1e9 // lamports -> SOL
69
+ : parseFloat(ethers.formatEther(amountWei)); // wei -> ETH
70
+
71
+ // Admin bypasses permission checks
72
+ if (!isAdmin(auth)) {
73
+ // Check fund permission
74
+ if (!hasAnyPermission(auth.token.permissions, ['fund'])) {
75
+ logger.permissionDenied('fund', auth.token.agentId, '/fund');
76
+ res.status(403).json({ error: 'Token does not have fund permission' });
77
+ return;
78
+ }
79
+
80
+ // Check if token can access the target wallet
81
+ const canAccess = await tokenCanAccessWallet(auth.tokenHash, auth.token.walletAccess, to);
82
+ if (!canAccess) {
83
+ logger.permissionDenied('wallet_access', auth.token.agentId, '/fund');
84
+ res.status(403).json({ error: 'Token does not have access to this wallet' });
85
+ return;
86
+ }
87
+
88
+ // Reserve spending atomically (prevents TOCTOU race between concurrent requests)
89
+ const reserve = reserveSpend(auth.tokenHash, auth.token, 'fund', amountNum, currency);
90
+ if (!reserve.ok) {
91
+ logger.limitExceeded(auth.token.agentId, 'fund', amountNum, reserve.remaining);
92
+ res.status(403).json({
93
+ error: 'Amount exceeds remaining spending limit',
94
+ remaining: reserve.remaining,
95
+ requested: amountNum
96
+ });
97
+ return;
98
+ }
99
+ }
100
+
101
+ // Set rollback to release reserved spend on early exit or error
102
+ rollback = () => {
103
+ if (!isAdmin(auth)) releaseSpend(auth.tokenHash, 'fund', amountNum, currency);
104
+ };
105
+
106
+ // Look up which vault the target hot wallet belongs to
107
+ const hotWalletInfo = await getHotWallet(to);
108
+ const vaultId = hotWalletInfo?.coldWalletId || getPrimaryVaultId();
109
+
110
+ // The source vault must be unlocked
111
+ if (vaultId && !isVaultUnlocked(vaultId)) {
112
+ rollback();
113
+ logger.authFailed('Vault locked', '/fund');
114
+ res.status(401).json({ error: `Vault ${vaultId} is locked. Human must unlock it first.` });
115
+ return;
116
+ }
117
+ if (!isUnlocked()) {
118
+ rollback();
119
+ logger.authFailed('Vault locked', '/fund');
120
+ res.status(401).json({ error: 'Cold wallet is locked. Human must unlock it first.' });
121
+ return;
122
+ }
123
+
124
+ // --- Solana early branch ---
125
+ if (isSolanaChain(targetChain)) {
126
+ const coldKeypair = vaultId ? getVaultSolanaKeypair(vaultId) : getSolanaColdKeypair();
127
+ const coldAddress = vaultId ? getVaultSolanaAddress(vaultId) : getSolanaColdAddress();
128
+ if (!coldKeypair || !coldAddress) {
129
+ rollback();
130
+ res.status(400).json({ error: 'Solana cold wallet not available' });
131
+ return;
132
+ }
133
+
134
+ const connection = await getSolanaConnection(targetChain);
135
+ const toPubkey = new PublicKey(to);
136
+ // Pass lamports directly to buildSolTransfer
137
+ const tx = await buildSolTransfer(connection, coldKeypair.publicKey, toPubkey, Number(amountWei));
138
+ const txHash = await sendSolanaTransaction(connection, tx, coldKeypair);
139
+
140
+ // Spend already reserved atomically above
141
+
142
+ await prisma.log.create({
143
+ data: {
144
+ walletAddress: coldAddress,
145
+ title: 'Agent Fund Transfer',
146
+ description: `Transferred ${amountNum} ${nativeCurrency} to hot wallet ${to.slice(0, 10)}...`,
147
+ txHash
148
+ }
149
+ });
150
+
151
+ const agentId = !isAdmin(auth) ? auth.token.agentId : undefined;
152
+ logger.fund(to, amountNum.toString(), txHash, agentId);
153
+
154
+ const remaining = isAdmin(auth)
155
+ ? Infinity
156
+ : getRemainingByType(auth.tokenHash, auth.token, 'fund', currency);
157
+
158
+ res.json({
159
+ success: true,
160
+ txHash,
161
+ amount: amountNum.toString(),
162
+ from: coldAddress,
163
+ to,
164
+ chain: targetChain,
165
+ remaining
166
+ });
167
+ return;
168
+ }
169
+
170
+ // --- EVM path ---
171
+ const coldAddress = vaultId ? getVaultAddress(vaultId) : getColdWalletAddress();
172
+ if (!coldAddress) {
173
+ rollback();
174
+ res.status(400).json({ error: 'Cold wallet not available' });
175
+ return;
176
+ }
177
+
178
+ // Execute the transfer — amountWei is already in wei, pass directly
179
+ const provider = new ethers.JsonRpcProvider(await getRpcUrl(targetChain));
180
+ const txHash = vaultId
181
+ ? await signWithVault(vaultId, {
182
+ to: to.toLowerCase(),
183
+ value: amountWei,
184
+ from: coldAddress
185
+ }, provider)
186
+ : await signWithColdWallet({
187
+ to: to.toLowerCase(),
188
+ value: amountWei,
189
+ from: coldAddress
190
+ }, provider);
191
+
192
+ // Spend already reserved atomically above
193
+
194
+ // Log the transaction
195
+ await prisma.log.create({
196
+ data: {
197
+ walletAddress: coldAddress,
198
+ title: 'Agent Fund Transfer',
199
+ description: `Transferred ${amountNum} ETH to hot wallet ${to.slice(0, 10)}...`,
200
+ txHash
201
+ }
202
+ });
203
+
204
+ // Log fund event
205
+ const agentId = !isAdmin(auth) ? auth.token.agentId : undefined;
206
+ logger.fund(to, amountNum.toString(), txHash, agentId);
207
+
208
+ // Get remaining for response
209
+ const remaining = isAdmin(auth)
210
+ ? Infinity
211
+ : getRemainingByType(auth.tokenHash, auth.token, 'fund', currency);
212
+
213
+ res.json({
214
+ success: true,
215
+ txHash,
216
+ amount: amountNum.toString(),
217
+ from: coldAddress,
218
+ to: to.toLowerCase(),
219
+ chain: targetChain,
220
+ remaining
221
+ });
222
+ } catch (error) {
223
+ rollback();
224
+ if (error instanceof HttpError) { res.status(error.status).json({ error: error.message }); return; }
225
+ res.status(400).json({ error: getErrorMessage(error) });
226
+ }
227
+ });
228
+
229
+ export default router;