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,457 @@
1
+ import { Router, Request, Response } from 'express';
2
+ import { randomBytes, timingSafeEqual } from 'crypto';
3
+ import { prisma } from '../lib/db';
4
+ import { createHumanActionNotification } from '../lib/notifications';
5
+ import { events } from '../lib/events';
6
+ import { requireWalletAuth } from '../middleware/auth';
7
+ import { isAdmin } from '../lib/permissions';
8
+ import { getPublicKey } from '../lib/transport';
9
+ import { claimEscrowedToken } from '../lib/auth';
10
+ import { isValidAgentPubkey, normalizeAgentPubkey, encryptToAgentPubkey } from '../lib/credential-transport';
11
+ import { hashSecret } from '../lib/crypto';
12
+ import { getDefault } from '../lib/defaults';
13
+ import { logger } from '../lib/logger';
14
+ import { getErrorMessage } from '../lib/error';
15
+ import { AgentProfileError, resolveProfileToEffectivePolicy } from '../lib/agent-profiles';
16
+
17
+ const router = Router();
18
+
19
+ // GET /auth/connect - Get server's public key for encrypting passwords
20
+ // This is a public endpoint - no authentication required
21
+ router.get('/connect', (_req: Request, res: Response) => {
22
+ res.json({ publicKey: getPublicKey() });
23
+ });
24
+
25
+ // POST /auth - Agent requests access token (queued for human approval)
26
+ // Returns a requestId and secret that can be used to retrieve the token later
27
+ router.post('/', async (req: Request, res: Response) => {
28
+ try {
29
+ const {
30
+ agentId,
31
+ limit,
32
+ permissions,
33
+ ttl,
34
+ limits,
35
+ walletAccess,
36
+ pubkey,
37
+ credentialAccess,
38
+ profile,
39
+ profileVersion,
40
+ profileOverrides,
41
+ } = req.body;
42
+
43
+ if (!agentId || typeof agentId !== 'string') {
44
+ res.status(400).json({ error: 'agentId is required' });
45
+ return;
46
+ }
47
+
48
+ if (typeof profile !== 'string' || profile.trim().length === 0) {
49
+ res.status(400).json({
50
+ error: 'profile is required for agent issuance',
51
+ code: 'AGENT_PROFILE_REQUIRED',
52
+ });
53
+ return;
54
+ }
55
+
56
+ const hasRawIssuancePayload = (
57
+ permissions !== undefined ||
58
+ ttl !== undefined ||
59
+ credentialAccess !== undefined
60
+ );
61
+ if (hasRawIssuancePayload) {
62
+ res.status(400).json({
63
+ error: 'Raw permission issuance is disabled on /auth. Use profile + optional profileOverrides.',
64
+ code: 'AGENT_PROFILE_ONLY',
65
+ });
66
+ return;
67
+ }
68
+
69
+ const defaultFundLimit = await getDefault<number>('limits.fund', 0.1);
70
+ const requestedLimit = typeof limit === 'number' ? limit : defaultFundLimit;
71
+ const resolvedProfile = resolveProfileToEffectivePolicy({
72
+ profileId: profile,
73
+ profileVersion: typeof profileVersion === 'string' ? profileVersion : undefined,
74
+ overrides: profileOverrides,
75
+ });
76
+ const requestedPermissions = [...resolvedProfile.permissions];
77
+ const requestedTtl = resolvedProfile.ttlSeconds;
78
+ const requestedLimits = limits || { fund: requestedLimit };
79
+ const requestedWalletAccess = Array.isArray(walletAccess)
80
+ ? walletAccess.map((addr: string) => addr.toLowerCase())
81
+ : undefined;
82
+ const requestedCredentialAccess = resolvedProfile.credentialAccess;
83
+
84
+ if (typeof pubkey !== 'string' || !pubkey.trim()) {
85
+ res.status(400).json({ error: 'pubkey is required' });
86
+ return;
87
+ }
88
+ if (!isValidAgentPubkey(pubkey)) {
89
+ res.status(400).json({ error: 'pubkey must be a valid RSA public key (PEM or base64)' });
90
+ return;
91
+ }
92
+ const normalizedPubkey = normalizeAgentPubkey(pubkey);
93
+
94
+ // Generate a secret for retrieving the token later
95
+ const secret = randomBytes(32).toString('hex');
96
+ const secretHash = hashSecret(secret);
97
+
98
+ // Create pending request with secretHash in metadata
99
+ const request = await prisma.humanAction.create({
100
+ data: {
101
+ type: 'auth',
102
+ fromTier: 'system',
103
+ toAddress: null,
104
+ amount: null,
105
+ chain: 'base',
106
+ status: 'pending',
107
+ metadata: JSON.stringify({
108
+ agentId,
109
+ limit: requestedLimit,
110
+ permissions: requestedPermissions,
111
+ ttl: requestedTtl,
112
+ limits: requestedLimits,
113
+ walletAccess: requestedWalletAccess,
114
+ credentialAccess: requestedCredentialAccess,
115
+ profile: resolvedProfile.profile,
116
+ effectivePolicyHash: resolvedProfile.effectivePolicyHash,
117
+ overrideDelta: resolvedProfile.overrideDelta,
118
+ warnings: resolvedProfile.warnings,
119
+ pubkey: normalizedPubkey,
120
+ secretHash,
121
+ // tokenHash will be added here when approved (raw token stays in memory escrow)
122
+ })
123
+ }
124
+ });
125
+
126
+ // Create notification for human approval
127
+ await createHumanActionNotification(request);
128
+
129
+ // Emit WebSocket event
130
+ events.actionCreated({
131
+ id: request.id,
132
+ type: 'agent_access',
133
+ source: `agent:${agentId}`,
134
+ summary: `${agentId} requesting ${requestedLimit} ETH access`,
135
+ expiresAt: null,
136
+ metadata: {
137
+ agentId,
138
+ limit: requestedLimit,
139
+ permissions: requestedPermissions,
140
+ profile: resolvedProfile.profile,
141
+ effectivePolicyHash: resolvedProfile.effectivePolicyHash,
142
+ },
143
+ });
144
+
145
+ logger.agentRequested(agentId, request.id, requestedLimit);
146
+
147
+ res.json({
148
+ success: true,
149
+ requestId: request.id,
150
+ secret, // Only returned once - agent must store this!
151
+ message: 'Waiting for human approval',
152
+ agentId,
153
+ limit: requestedLimit,
154
+ permissions: requestedPermissions,
155
+ limits: requestedLimits,
156
+ credentialAccess: requestedCredentialAccess,
157
+ profile: resolvedProfile.profile,
158
+ effectivePolicyHash: resolvedProfile.effectivePolicyHash,
159
+ overrideDelta: resolvedProfile.overrideDelta,
160
+ warnings: resolvedProfile.warnings,
161
+ ttl: requestedTtl
162
+ });
163
+ } catch (error) {
164
+ if (error instanceof AgentProfileError) {
165
+ res.status(400).json({ error: error.message, code: error.code });
166
+ return;
167
+ }
168
+ const message = getErrorMessage(error);
169
+ res.status(400).json({ error: message });
170
+ }
171
+ });
172
+
173
+ // GET /auth/pending - List pending auth requests (public, shows sanitized info)
174
+ router.get('/pending', async (_req: Request, res: Response) => {
175
+ try {
176
+ const requests = await prisma.humanAction.findMany({
177
+ where: {
178
+ type: 'auth',
179
+ status: 'pending'
180
+ },
181
+ orderBy: { createdAt: 'desc' }
182
+ });
183
+
184
+ res.json({
185
+ success: true,
186
+ requests: requests.map(r => {
187
+ const metadata = r.metadata ? JSON.parse(r.metadata) : {};
188
+ // Don't expose secretHash
189
+ const { secretHash, ...safeMetadata } = metadata;
190
+ return {
191
+ ...r,
192
+ metadata: safeMetadata
193
+ };
194
+ })
195
+ });
196
+ } catch (error) {
197
+ const message = getErrorMessage(error);
198
+ res.status(400).json({ error: message });
199
+ }
200
+ });
201
+
202
+ // POST /auth/request-permissions - Agent requests additional permissions or wallet access
203
+ // Requires existing valid token
204
+ router.post('/request-permissions', requireWalletAuth, async (req: Request, res: Response) => {
205
+ try {
206
+ const auth = req.auth!;
207
+ const { requestedPermissions, requestedWalletAccess, requestedLimits, requestedPubkey } = req.body;
208
+
209
+ // Don't allow admin to request more permissions
210
+ if (isAdmin(auth)) {
211
+ res.status(400).json({ error: 'Admin already has all permissions' });
212
+ return;
213
+ }
214
+
215
+ // Validate input
216
+ if (!requestedPermissions && !requestedWalletAccess && !requestedLimits) {
217
+ res.status(400).json({ error: 'Must request at least one of: permissions, walletAccess, or limits' });
218
+ return;
219
+ }
220
+
221
+ // Normalize wallet access
222
+ const normalizedWalletAccess = requestedWalletAccess && Array.isArray(requestedWalletAccess)
223
+ ? requestedWalletAccess.map((addr: string) => addr.toLowerCase())
224
+ : undefined;
225
+ if (typeof requestedPubkey !== 'string' || !requestedPubkey.trim()) {
226
+ res.status(400).json({ error: 'requestedPubkey is required' });
227
+ return;
228
+ }
229
+ if (!isValidAgentPubkey(requestedPubkey)) {
230
+ res.status(400).json({ error: 'requestedPubkey must be a valid RSA public key (PEM or base64)' });
231
+ return;
232
+ }
233
+ const normalizedPubkey = normalizeAgentPubkey(requestedPubkey);
234
+
235
+ // Generate a secret for retrieving the new token
236
+ const secret = randomBytes(32).toString('hex');
237
+ const secretHash = hashSecret(secret);
238
+
239
+ // Create pending request
240
+ const request = await prisma.humanAction.create({
241
+ data: {
242
+ type: 'permission_update',
243
+ fromTier: 'system',
244
+ toAddress: null,
245
+ amount: null,
246
+ chain: 'base',
247
+ status: 'pending',
248
+ metadata: JSON.stringify({
249
+ agentId: auth.token.agentId,
250
+ tokenHash: auth.tokenHash,
251
+ currentPermissions: auth.token.permissions,
252
+ currentLimits: auth.token.limits,
253
+ currentWalletAccess: auth.token.walletAccess,
254
+ requestedPermissions: requestedPermissions || [],
255
+ requestedWalletAccess: normalizedWalletAccess,
256
+ requestedLimits,
257
+ requestedPubkey: normalizedPubkey,
258
+ secretHash,
259
+ })
260
+ }
261
+ });
262
+
263
+ // Create notification for human approval
264
+ await createHumanActionNotification(request);
265
+
266
+ // Emit WebSocket event
267
+ events.actionCreated({
268
+ id: request.id,
269
+ type: 'permission_update',
270
+ source: `agent:${auth.token.agentId}`,
271
+ summary: `${auth.token.agentId} requesting permission update`,
272
+ expiresAt: null,
273
+ metadata: { agentId: auth.token.agentId, requestedPermissions, requestedLimits },
274
+ });
275
+
276
+ logger.permissionRequested(auth.token.agentId, request.id, requestedPermissions || []);
277
+
278
+ res.json({
279
+ success: true,
280
+ requestId: request.id,
281
+ secret, // Only returned once - agent must store this!
282
+ message: 'Permission update request created, waiting for human approval',
283
+ currentPermissions: auth.token.permissions,
284
+ requestedPermissions: requestedPermissions || [],
285
+ requestedWalletAccess: normalizedWalletAccess,
286
+ requestedLimits
287
+ });
288
+ } catch (error) {
289
+ const message = getErrorMessage(error);
290
+ res.status(400).json({ error: message });
291
+ }
292
+ });
293
+
294
+ // POST /auth/validate - Validate a token and return its info
295
+ // Used by Next.js WebSocket server to authenticate connections
296
+ router.post('/validate', async (req: Request, res: Response) => {
297
+ try {
298
+ const { token } = req.body;
299
+
300
+ if (!token || typeof token !== 'string') {
301
+ res.status(400).json({ valid: false, error: 'token is required' });
302
+ return;
303
+ }
304
+
305
+ // Import validation functions
306
+ const { validateToken, getTokenHash } = await import('../lib/auth');
307
+ const { isRevoked } = await import('../lib/sessions');
308
+
309
+ // Validate token (admin tokens are just tokens with admin:* permission)
310
+ const payload = validateToken(token);
311
+ if (!payload) {
312
+ res.json({ valid: false, error: 'Invalid or expired token' });
313
+ return;
314
+ }
315
+
316
+ const tokenHash = getTokenHash(token);
317
+
318
+ // Check if revoked
319
+ if (isRevoked(tokenHash)) {
320
+ res.json({ valid: false, error: 'Token has been revoked' });
321
+ return;
322
+ }
323
+
324
+ logger.tokenValidated(payload.agentId, tokenHash);
325
+
326
+ res.json({
327
+ valid: true,
328
+ isAdmin: payload.permissions.includes('admin:*'),
329
+ tokenHash,
330
+ payload: {
331
+ agentId: payload.agentId,
332
+ permissions: payload.permissions,
333
+ limits: payload.limits,
334
+ walletAccess: payload.walletAccess,
335
+ exp: payload.exp
336
+ }
337
+ });
338
+ } catch (error) {
339
+ const message = getErrorMessage(error);
340
+ res.status(400).json({ valid: false, error: message });
341
+ }
342
+ });
343
+
344
+ // GET /auth/:requestId - Agent polls for token (requires secret)
345
+ router.get('/:requestId', async (req: Request<{ requestId: string }>, res: Response) => {
346
+ try {
347
+ const { requestId } = req.params;
348
+ const { secret } = req.query;
349
+
350
+ if (!secret || typeof secret !== 'string') {
351
+ res.status(400).json({ error: 'secret query parameter is required' });
352
+ return;
353
+ }
354
+
355
+ const request = await prisma.humanAction.findUnique({
356
+ where: { id: requestId }
357
+ });
358
+
359
+ if (!request) {
360
+ res.status(404).json({ error: 'Request not found' });
361
+ return;
362
+ }
363
+
364
+ // Parse metadata
365
+ let metadata: {
366
+ agentId?: string;
367
+ limit?: number;
368
+ permissions?: string[];
369
+ ttl?: number;
370
+ secretHash?: string;
371
+ tokenHash?: string;
372
+ limits?: { fund?: number; send?: number; swap?: number };
373
+ walletAccess?: string[];
374
+ profile?: { id: string; version: string; displayName?: string; rationale?: string };
375
+ effectivePolicyHash?: string;
376
+ overrideDelta?: string[];
377
+ warnings?: string[];
378
+ } = {};
379
+ if (request.metadata) {
380
+ try {
381
+ metadata = JSON.parse(request.metadata);
382
+ } catch {
383
+ // ignore parse errors
384
+ }
385
+ }
386
+
387
+ // Verify secret (timing-safe comparison)
388
+ const providedHash = hashSecret(secret);
389
+ if (!metadata.secretHash) {
390
+ res.status(403).json({ error: 'Invalid secret' });
391
+ return;
392
+ }
393
+ const providedBuf = Buffer.from(providedHash, 'hex');
394
+ const expectedBuf = Buffer.from(metadata.secretHash, 'hex');
395
+ if (providedBuf.length !== expectedBuf.length || !timingSafeEqual(providedBuf, expectedBuf)) {
396
+ res.status(403).json({ error: 'Invalid secret' });
397
+ return;
398
+ }
399
+
400
+ // Return status based on request state
401
+ if (request.status === 'pending') {
402
+ res.json({
403
+ success: true,
404
+ status: 'pending',
405
+ message: 'Waiting for human approval'
406
+ });
407
+ return;
408
+ }
409
+
410
+ if (request.status === 'rejected') {
411
+ res.json({
412
+ success: true,
413
+ status: 'rejected',
414
+ message: 'Request was rejected'
415
+ });
416
+ return;
417
+ }
418
+
419
+ if (request.status === 'approved') {
420
+ const tokenToReturn = claimEscrowedToken(requestId);
421
+ if (!tokenToReturn) {
422
+ res.status(410).json({ error: 'Token already claimed or expired', status: 'approved' });
423
+ return;
424
+ }
425
+
426
+ // Encrypt token to agent pubkey for transport (plaintext is no longer supported)
427
+ const pubkey = (metadata as { pubkey?: string }).pubkey;
428
+ if (!pubkey) {
429
+ res.status(500).json({ error: 'No pubkey available to return encrypted token' });
430
+ return;
431
+ }
432
+
433
+ res.json({
434
+ success: true,
435
+ status: 'approved',
436
+ encryptedToken: encryptToAgentPubkey(tokenToReturn, pubkey),
437
+ agentId: metadata.agentId,
438
+ limit: metadata.limit,
439
+ limits: metadata.limits,
440
+ permissions: metadata.permissions,
441
+ walletAccess: metadata.walletAccess,
442
+ profile: metadata.profile,
443
+ effectivePolicyHash: metadata.effectivePolicyHash,
444
+ overrideDelta: metadata.overrideDelta,
445
+ warnings: metadata.warnings,
446
+ });
447
+ return;
448
+ }
449
+
450
+ res.status(400).json({ error: `Unknown status: ${request.status}` });
451
+ } catch (error) {
452
+ const message = getErrorMessage(error);
453
+ res.status(400).json({ error: message });
454
+ }
455
+ });
456
+
457
+ export default router;