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,172 @@
1
+ import { isIPv4, isIPv6 } from 'net';
2
+ import {
3
+ AdvisoryFinding,
4
+ API_AUDIT_EXIT_CODES,
5
+ API_REGISTRY_ERROR_CODES,
6
+ API_REGISTRY_IDENTITY_REGEX,
7
+ API_REGISTRY_RESERVED_NAMESPACES,
8
+ API_REGISTRY_RESERVED_PACKAGE_NAMES,
9
+ LocalPolicy,
10
+ PublisherKey,
11
+ SignatureEnvelope,
12
+ permissionSchema,
13
+ } from './contracts';
14
+
15
+ export class ApiRegistryValidationError extends Error {
16
+ constructor(public readonly code: string, message: string) {
17
+ super(message);
18
+ this.name = 'ApiRegistryValidationError';
19
+ }
20
+ }
21
+
22
+ export function validatePackageIdentity(identity: string): { namespace: string; name: string } {
23
+ const match = identity.match(API_REGISTRY_IDENTITY_REGEX);
24
+ if (!match) {
25
+ throw new ApiRegistryValidationError(
26
+ API_REGISTRY_ERROR_CODES.nameInvalid,
27
+ `Invalid package identity "${identity}". Expected @namespace/name`
28
+ );
29
+ }
30
+
31
+ const namespace = match[1];
32
+ const name = match[2];
33
+
34
+ if (API_REGISTRY_RESERVED_NAMESPACES.has(namespace) || API_REGISTRY_RESERVED_PACKAGE_NAMES.has(name)) {
35
+ throw new ApiRegistryValidationError(
36
+ API_REGISTRY_ERROR_CODES.nameInvalid,
37
+ `Package identity "${identity}" uses a reserved namespace or package name`
38
+ );
39
+ }
40
+
41
+ return { namespace, name };
42
+ }
43
+
44
+ function isFqdn(host: string): boolean {
45
+ // strict enough for MVP: labels with lowercase letters/digits/hyphens, at least one dot
46
+ if (!host.includes('.')) return false;
47
+ if (host.includes('*') || host.includes('/') || host.includes('?') || host.includes(':')) return false;
48
+ const labels = host.split('.');
49
+ return labels.every((label) => /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(label));
50
+ }
51
+
52
+ export function validateAllowedHosts(allowedHosts: string[]): void {
53
+ for (const host of allowedHosts) {
54
+ if (!isFqdn(host) || isIPv4(host) || isIPv6(host)) {
55
+ throw new ApiRegistryValidationError(
56
+ API_REGISTRY_ERROR_CODES.egressDenied,
57
+ `Host "${host}" is invalid. allowedHosts must contain FQDN values only`
58
+ );
59
+ }
60
+ }
61
+ }
62
+
63
+ export function enforceEgressPolicy(hostname: string, allowedHosts: readonly string[]): void {
64
+ if (!allowedHosts.includes(hostname)) {
65
+ throw new ApiRegistryValidationError(
66
+ API_REGISTRY_ERROR_CODES.egressDenied,
67
+ `Blocked outbound host: ${hostname}`
68
+ );
69
+ }
70
+ }
71
+
72
+ export function validateSignatureEnvelope(envelope: SignatureEnvelope): void {
73
+ if (envelope.algorithm !== 'ed25519') {
74
+ throw new ApiRegistryValidationError(
75
+ API_REGISTRY_ERROR_CODES.signatureAlgorithmUnsupported,
76
+ `Unsupported signature algorithm: ${envelope.algorithm}`
77
+ );
78
+ }
79
+
80
+ if (!envelope.keyId || !envelope.sig || !envelope.payloadHash || !envelope.createdAt) {
81
+ throw new ApiRegistryValidationError(
82
+ API_REGISTRY_ERROR_CODES.signatureAlgorithmUnsupported,
83
+ 'Signature envelope is missing required fields'
84
+ );
85
+ }
86
+ }
87
+
88
+ export function resolveEffectivePermissions(
89
+ requiredPermissions: string[],
90
+ localPolicy: LocalPolicy,
91
+ runtimeHardDeny: Set<string>
92
+ ): string[] {
93
+ for (const permission of requiredPermissions) {
94
+ const parsed = permissionSchema.safeParse(permission);
95
+ if (!parsed.success) {
96
+ throw new ApiRegistryValidationError(
97
+ API_REGISTRY_ERROR_CODES.permissionUnresolved,
98
+ `Required permission "${permission}" is invalid: ${parsed.error.issues.map((issue) => issue.message).join('; ')}`
99
+ );
100
+ }
101
+
102
+ const deniedByRuntime = runtimeHardDeny.has(permission);
103
+ const deniedByPolicy = (localPolicy.deny ?? []).includes(permission);
104
+ const allowedByPolicy = (localPolicy.allow ?? []).includes(permission);
105
+
106
+ if (deniedByRuntime || deniedByPolicy || !allowedByPolicy) {
107
+ throw new ApiRegistryValidationError(
108
+ API_REGISTRY_ERROR_CODES.permissionUnresolved,
109
+ `Required permission "${permission}" could not be resolved under local/runtime policy`
110
+ );
111
+ }
112
+ }
113
+
114
+ return [...new Set(requiredPermissions)].sort();
115
+ }
116
+
117
+ export function enforceHistoricalKeyTrust(key: PublisherKey, signatureCreatedAt: string): void {
118
+ const signatureTime = Date.parse(signatureCreatedAt);
119
+ const keyCreatedAt = Date.parse(key.createdAt);
120
+
121
+ if (Number.isNaN(signatureTime) || Number.isNaN(keyCreatedAt)) {
122
+ throw new ApiRegistryValidationError(
123
+ API_REGISTRY_ERROR_CODES.keyTrustInvalid,
124
+ 'Invalid timestamp in key trust evaluation'
125
+ );
126
+ }
127
+
128
+ if (signatureTime < keyCreatedAt) {
129
+ throw new ApiRegistryValidationError(
130
+ API_REGISTRY_ERROR_CODES.keyTrustInvalid,
131
+ 'Signature predates key creation'
132
+ );
133
+ }
134
+
135
+ if (key.status === 'compromised') {
136
+ const cutoff = Date.parse(key.compromiseDetectedAt ?? '');
137
+ if (Number.isNaN(cutoff) || signatureTime >= cutoff) {
138
+ throw new ApiRegistryValidationError(
139
+ API_REGISTRY_ERROR_CODES.keyTrustInvalid,
140
+ 'Signature is outside historical trust window for compromised key'
141
+ );
142
+ }
143
+ }
144
+ }
145
+
146
+ export function evaluateAuditExitCode(input: {
147
+ mode: 'ci' | 'local';
148
+ yanked: boolean;
149
+ integrityFailure: boolean;
150
+ findings: AdvisoryFinding[];
151
+ }): number {
152
+ if (input.integrityFailure) return API_AUDIT_EXIT_CODES.integrityFailure;
153
+ if (input.yanked) return API_AUDIT_EXIT_CODES.yankedBlocked;
154
+
155
+ const hasExploitKnown = input.findings.some((finding) => Boolean(finding.exploitKnown));
156
+ const hasCritical = input.findings.some((finding) => finding.severity === 'critical');
157
+ const hasHigh = input.findings.some((finding) => finding.severity === 'high');
158
+
159
+ if (hasExploitKnown || hasCritical) {
160
+ return API_AUDIT_EXIT_CODES.advisoryBlocked;
161
+ }
162
+
163
+ if (input.mode === 'ci' && hasHigh) {
164
+ return API_AUDIT_EXIT_CODES.advisoryBlocked;
165
+ }
166
+
167
+ if (input.mode === 'local' && hasHigh) {
168
+ return API_AUDIT_EXIT_CODES.warning;
169
+ }
170
+
171
+ return API_AUDIT_EXIT_CODES.ok;
172
+ }
@@ -0,0 +1,189 @@
1
+ import { prisma } from './db';
2
+ import { createCredential, deleteCredential, getCredential, listCredentials, readCredentialSecrets, updateCredential } from './credentials';
3
+ import { getPrimaryVaultId, isVaultUnlocked, listVaults } from './cold';
4
+ import { CredentialField, CredentialFile } from '../types';
5
+ import { normalizeScope } from './credential-scope';
6
+
7
+ const APIKEY_TYPE = 'apikey';
8
+
9
+ export interface ApiKeyCredentialRecord {
10
+ id: string;
11
+ service: string;
12
+ name: string;
13
+ keyMasked: string;
14
+ metadata: unknown;
15
+ createdAt: string;
16
+ updatedAt: string;
17
+ }
18
+
19
+ function getActiveVaultId(): string | null {
20
+ const primary = getPrimaryVaultId();
21
+ if (primary && isVaultUnlocked(primary)) {
22
+ return primary;
23
+ }
24
+ const unlocked = listVaults().find(vault => vault.isUnlocked);
25
+ return unlocked?.id || null;
26
+ }
27
+
28
+ function parseMetadata(metadata: string | null): unknown {
29
+ if (!metadata) return null;
30
+ try {
31
+ return JSON.parse(metadata);
32
+ } catch {
33
+ return metadata;
34
+ }
35
+ }
36
+
37
+ export function maskKey(key: string): string {
38
+ if (key.length <= 8) {
39
+ return '*'.repeat(key.length);
40
+ }
41
+ return `${key.slice(0, 4)}${'*'.repeat(key.length - 8)}${key.slice(-4)}`;
42
+ }
43
+
44
+ function listApiKeyCredentialsRaw(): CredentialFile[] {
45
+ return listCredentials({ type: APIKEY_TYPE });
46
+ }
47
+
48
+ function findApiKeyCredential(service: string, name: string): CredentialFile | null {
49
+ const normalizedService = normalizeScope(service);
50
+ const normalizedName = normalizeScope(name);
51
+
52
+ for (const credential of listApiKeyCredentialsRaw()) {
53
+ const metaService = typeof credential.meta.service === 'string' ? normalizeScope(credential.meta.service) : '';
54
+ const metaName = typeof credential.meta.name === 'string' ? normalizeScope(credential.meta.name) : '';
55
+ if (metaService === normalizedService && metaName === normalizedName) {
56
+ return credential;
57
+ }
58
+ }
59
+
60
+ return null;
61
+ }
62
+
63
+ function toApiKeyRecord(credential: CredentialFile): ApiKeyCredentialRecord {
64
+ const service = typeof credential.meta.service === 'string' ? credential.meta.service : '';
65
+ const name = typeof credential.meta.name === 'string' ? credential.meta.name : credential.name;
66
+ const keyMasked = typeof credential.meta.keyMasked === 'string' ? credential.meta.keyMasked : '********';
67
+
68
+ return {
69
+ id: credential.id,
70
+ service,
71
+ name,
72
+ keyMasked,
73
+ metadata: credential.meta.metadata ?? null,
74
+ createdAt: credential.createdAt,
75
+ updatedAt: credential.updatedAt,
76
+ };
77
+ }
78
+
79
+ function buildApiKeyMeta(service: string, name: string, key: string, metadata: unknown): Record<string, unknown> {
80
+ return {
81
+ service,
82
+ name,
83
+ tags: [normalizeScope(service)],
84
+ keyMasked: maskKey(key),
85
+ metadata: metadata ?? null,
86
+ };
87
+ }
88
+
89
+ function apiKeySecretField(key: string): CredentialField[] {
90
+ return [{ key: 'key', value: key, type: 'secret', sensitive: true }];
91
+ }
92
+
93
+ export async function migrateApiKeysFromDatabase(): Promise<{
94
+ migrated: number;
95
+ skipped: number;
96
+ reason?: string;
97
+ }> {
98
+ const rows = await prisma.apiKey.findMany({
99
+ where: { isActive: true },
100
+ orderBy: { createdAt: 'asc' },
101
+ });
102
+ if (rows.length === 0) {
103
+ return { migrated: 0, skipped: 0 };
104
+ }
105
+
106
+ const vaultId = getActiveVaultId();
107
+ if (!vaultId) {
108
+ return { migrated: 0, skipped: rows.length, reason: 'No unlocked vault available for credential migration' };
109
+ }
110
+
111
+ let migrated = 0;
112
+ for (const row of rows) {
113
+ const existing = findApiKeyCredential(row.service, row.name);
114
+ const meta = buildApiKeyMeta(row.service, row.name, row.key, parseMetadata(row.metadata));
115
+ if (existing) {
116
+ updateCredential(existing.id, {
117
+ meta,
118
+ sensitiveFields: apiKeySecretField(row.key),
119
+ });
120
+ } else {
121
+ createCredential(vaultId, APIKEY_TYPE, `${row.service}:${row.name}`, meta, apiKeySecretField(row.key));
122
+ }
123
+ migrated++;
124
+ }
125
+
126
+ return { migrated, skipped: rows.length - migrated };
127
+ }
128
+
129
+ export function listApiKeyCredentials(): ApiKeyCredentialRecord[] {
130
+ return listApiKeyCredentialsRaw().map(toApiKeyRecord);
131
+ }
132
+
133
+ export function upsertApiKeyCredential(
134
+ service: string,
135
+ name: string,
136
+ key: string,
137
+ metadata: unknown,
138
+ ): ApiKeyCredentialRecord {
139
+ const vaultId = getActiveVaultId();
140
+ if (!vaultId) {
141
+ throw new Error('No unlocked vault available for API key credential storage');
142
+ }
143
+
144
+ const existing = findApiKeyCredential(service, name);
145
+ const nextMeta = buildApiKeyMeta(service, name, key, metadata);
146
+
147
+ const credential = existing
148
+ ? updateCredential(existing.id, {
149
+ meta: nextMeta,
150
+ sensitiveFields: apiKeySecretField(key),
151
+ })
152
+ : createCredential(
153
+ vaultId,
154
+ APIKEY_TYPE,
155
+ `${service}:${name}`,
156
+ nextMeta,
157
+ apiKeySecretField(key),
158
+ );
159
+
160
+ return toApiKeyRecord(credential);
161
+ }
162
+
163
+ export function deleteApiKeyCredentialById(id: string): ApiKeyCredentialRecord | null {
164
+ const credential = getCredential(id);
165
+ if (!credential || credential.type !== APIKEY_TYPE) {
166
+ return null;
167
+ }
168
+ const record = toApiKeyRecord(credential);
169
+ deleteCredential(id);
170
+ return record;
171
+ }
172
+
173
+ export function deleteApiKeyCredentialByServiceName(service: string, name: string): ApiKeyCredentialRecord | null {
174
+ const credential = findApiKeyCredential(service, name);
175
+ if (!credential) {
176
+ return null;
177
+ }
178
+ const record = toApiKeyRecord(credential);
179
+ deleteCredential(credential.id);
180
+ return record;
181
+ }
182
+
183
+ export function readApiKeyValueByServiceName(service: string, name: string): string | null {
184
+ const credential = findApiKeyCredential(service, name);
185
+ if (!credential) return null;
186
+ const fields = readCredentialSecrets(credential.id);
187
+ const keyField = fields.find(field => normalizeScope(field.key) === 'key');
188
+ return keyField?.value || null;
189
+ }