auramaxx 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +26 -0
- package/README.md +77 -0
- package/apps/desktop-electron/main.js +428 -0
- package/bin/auramaxx.js +1063 -0
- package/docs/ADAPTERS.md +466 -0
- package/docs/AGENT_SETUP.md +159 -0
- package/docs/API.md +127 -0
- package/docs/APPS.md +199 -0
- package/docs/ARCHITECTURE.md +235 -0
- package/docs/AUTH.md +318 -0
- package/docs/BEST-PRACTICES.md +82 -0
- package/docs/CLI.md +141 -0
- package/docs/DESKTOP_ELECTRON.md +26 -0
- package/docs/DEVELOPING-APPS.md +453 -0
- package/docs/MCP.md +122 -0
- package/docs/PACKAGING_POLICY.md +19 -0
- package/docs/PERMISSION.md +137 -0
- package/docs/PROTOCOL.md +142 -0
- package/docs/README.md +50 -0
- package/docs/SKILLS.md +132 -0
- package/docs/TROUBLESHOOTING.md +376 -0
- package/docs/WORKSPACE.md +673 -0
- package/docs/agent-auth.md +14 -0
- package/docs/api/authentication.md +79 -0
- package/docs/api/secrets/api-keys.md +28 -0
- package/docs/api/secrets/credentials.md +80 -0
- package/docs/api/secrets/sharing.md +48 -0
- package/docs/api/system.md +41 -0
- package/docs/api/wallets/apps-strategies.md +66 -0
- package/docs/api/wallets/core.md +46 -0
- package/docs/api/wallets/data-portfolio.md +42 -0
- package/docs/aura-file.md +48 -0
- package/docs/core-concepts/FEATURES.md +114 -0
- package/docs/credentials.md +120 -0
- package/docs/external/HOW_TO_AURAMAXX/GETTING_SECRETS.md +33 -0
- package/docs/external/HOW_TO_AURAMAXX/README.md +45 -0
- package/docs/external/getting-started.md +10 -0
- package/docs/external/overview.md +19 -0
- package/docs/external/persona-paths.md +7 -0
- package/docs/external/share-secret.md +76 -0
- package/docs/external/why-aura.md +7 -0
- package/docs/security.md +227 -0
- package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -0
- package/docs/wallet/AI.md +508 -0
- package/docs/wallet/DEVELOPING-STRATEGIES.md +713 -0
- package/docs/wallet/README.md +47 -0
- package/docs/wallet/STRATEGY.md +89 -0
- package/next.config.ts +28 -0
- package/package.json +167 -0
- package/postcss.config.mjs +8 -0
- package/prisma/migrations/20260214170000_baseline/migration.sql +511 -0
- package/prisma/migrations/20260216214537_add_passkey_model/migration.sql +18 -0
- package/prisma/migrations/20260217150500_add_credential_access_audit/migration.sql +31 -0
- package/prisma/migrations/20260222090000_update_admin_ttl_default/migration.sql +10 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +447 -0
- package/public/logo.webp +0 -0
- package/scripts/add-app.js +245 -0
- package/server/abi/SwapHelper.json +438 -0
- package/server/cli/approval.ts +447 -0
- package/server/cli/commands/actions.ts +474 -0
- package/server/cli/commands/api.ts +220 -0
- package/server/cli/commands/apikey.ts +277 -0
- package/server/cli/commands/app.ts +204 -0
- package/server/cli/commands/auth.ts +464 -0
- package/server/cli/commands/cron.ts +24 -0
- package/server/cli/commands/diary.ts +274 -0
- package/server/cli/commands/doctor.ts +1247 -0
- package/server/cli/commands/env.ts +476 -0
- package/server/cli/commands/experimental.ts +69 -0
- package/server/cli/commands/init.ts +798 -0
- package/server/cli/commands/lock.ts +157 -0
- package/server/cli/commands/mcp.ts +285 -0
- package/server/cli/commands/quickhack.ts +86 -0
- package/server/cli/commands/release-check.ts +231 -0
- package/server/cli/commands/restore.ts +314 -0
- package/server/cli/commands/service.ts +320 -0
- package/server/cli/commands/shell-hook.ts +512 -0
- package/server/cli/commands/skill.ts +216 -0
- package/server/cli/commands/start.ts +139 -0
- package/server/cli/commands/status.ts +59 -0
- package/server/cli/commands/stop.ts +36 -0
- package/server/cli/commands/token.ts +180 -0
- package/server/cli/commands/unlock.ts +50 -0
- package/server/cli/commands/vault.ts +1323 -0
- package/server/cli/commands/wallet.ts +209 -0
- package/server/cli/index.ts +280 -0
- package/server/cli/lib/approval-poll.ts +94 -0
- package/server/cli/lib/aura-parser.ts +64 -0
- package/server/cli/lib/credential-create.ts +74 -0
- package/server/cli/lib/credential-resolve.ts +280 -0
- package/server/cli/lib/dotenv-migrate.ts +116 -0
- package/server/cli/lib/dotenv-parser.ts +146 -0
- package/server/cli/lib/escalation.ts +57 -0
- package/server/cli/lib/http.ts +91 -0
- package/server/cli/lib/init-steps.ts +76 -0
- package/server/cli/lib/local-agent-trust.ts +45 -0
- package/server/cli/lib/lock-unlock-helper.ts +71 -0
- package/server/cli/lib/process.ts +162 -0
- package/server/cli/lib/prompt.ts +294 -0
- package/server/cli/lib/theme.ts +240 -0
- package/server/cli/socket.ts +579 -0
- package/server/cli/transport-client.ts +50 -0
- package/server/cron/index.ts +137 -0
- package/server/cron/job.ts +31 -0
- package/server/cron/jobs/balance-sync.ts +436 -0
- package/server/cron/jobs/incoming-scan.ts +506 -0
- package/server/cron/jobs/native-price.ts +70 -0
- package/server/cron/jobs/orphan-cleanup.ts +40 -0
- package/server/cron/jobs/strategy-runner.ts +175 -0
- package/server/cron/scheduler.ts +125 -0
- package/server/index.ts +420 -0
- package/server/lib/adapters/factory.ts +119 -0
- package/server/lib/adapters/index.ts +19 -0
- package/server/lib/adapters/router.ts +297 -0
- package/server/lib/adapters/telegram.ts +645 -0
- package/server/lib/adapters/types.ts +89 -0
- package/server/lib/adapters/webhook.ts +95 -0
- package/server/lib/address.ts +49 -0
- package/server/lib/agent-auth/contracts.ts +1194 -0
- package/server/lib/agent-profiles.ts +419 -0
- package/server/lib/ai.ts +285 -0
- package/server/lib/api-registry/contracts.ts +86 -0
- package/server/lib/api-registry/validation.ts +172 -0
- package/server/lib/apikey-migration.ts +258 -0
- package/server/lib/app-installer.ts +505 -0
- package/server/lib/app-tokens.ts +247 -0
- package/server/lib/approval-link.ts +27 -0
- package/server/lib/auth.ts +314 -0
- package/server/lib/auto-execute.ts +160 -0
- package/server/lib/batch.ts +242 -0
- package/server/lib/cold.ts +1048 -0
- package/server/lib/config.ts +408 -0
- package/server/lib/credential-access-audit.ts +85 -0
- package/server/lib/credential-access-policy.ts +111 -0
- package/server/lib/credential-health.ts +343 -0
- package/server/lib/credential-import.ts +608 -0
- package/server/lib/credential-scope.ts +102 -0
- package/server/lib/credential-shares.ts +190 -0
- package/server/lib/credential-transport.ts +533 -0
- package/server/lib/credential-vault.ts +77 -0
- package/server/lib/credentials.ts +422 -0
- package/server/lib/crypto.ts +8 -0
- package/server/lib/db.ts +58 -0
- package/server/lib/defaults.ts +386 -0
- package/server/lib/dex/index.ts +80 -0
- package/server/lib/dex/relay.ts +235 -0
- package/server/lib/dex/types.ts +59 -0
- package/server/lib/dex/uniswap.ts +370 -0
- package/server/lib/diary.ts +34 -0
- package/server/lib/dont-ask-again-policy.ts +41 -0
- package/server/lib/e2e-agent/artifacts.ts +36 -0
- package/server/lib/e2e-agent/contracts.ts +112 -0
- package/server/lib/e2e-agent/validation.ts +135 -0
- package/server/lib/encrypt.ts +114 -0
- package/server/lib/error.ts +20 -0
- package/server/lib/events.ts +217 -0
- package/server/lib/feature-flags.ts +93 -0
- package/server/lib/hot.ts +357 -0
- package/server/lib/human-action-summary.ts +80 -0
- package/server/lib/key-fingerprint.ts +28 -0
- package/server/lib/logger.ts +340 -0
- package/server/lib/network.ts +137 -0
- package/server/lib/notifications.ts +230 -0
- package/server/lib/oauth2-refresh.ts +241 -0
- package/server/lib/oursecret.ts +71 -0
- package/server/lib/passkey-credential.ts +360 -0
- package/server/lib/passkey.ts +68 -0
- package/server/lib/permissions.ts +299 -0
- package/server/lib/pino.ts +24 -0
- package/server/lib/policy-preview.ts +138 -0
- package/server/lib/price.ts +338 -0
- package/server/lib/prices.ts +34 -0
- package/server/lib/project-scope.ts +297 -0
- package/server/lib/resolve-action.ts +328 -0
- package/server/lib/resolve.ts +36 -0
- package/server/lib/secret-gist-share.ts +296 -0
- package/server/lib/sessions.ts +634 -0
- package/server/lib/socket-path.ts +56 -0
- package/server/lib/solana/connection.ts +26 -0
- package/server/lib/solana/jupiter.ts +128 -0
- package/server/lib/solana/transfer.ts +108 -0
- package/server/lib/solana/wallet.ts +136 -0
- package/server/lib/strategy/emits.ts +21 -0
- package/server/lib/strategy/engine.ts +1305 -0
- package/server/lib/strategy/executor.ts +115 -0
- package/server/lib/strategy/hook-context.ts +159 -0
- package/server/lib/strategy/hooks.ts +990 -0
- package/server/lib/strategy/index.ts +28 -0
- package/server/lib/strategy/installer.ts +305 -0
- package/server/lib/strategy/loader.ts +256 -0
- package/server/lib/strategy/message.ts +237 -0
- package/server/lib/strategy/repository.ts +218 -0
- package/server/lib/strategy/session-logger.ts +693 -0
- package/server/lib/strategy/sources.ts +288 -0
- package/server/lib/strategy/state.ts +189 -0
- package/server/lib/strategy/templates.ts +403 -0
- package/server/lib/strategy/tick.ts +404 -0
- package/server/lib/strategy/types.ts +230 -0
- package/server/lib/swap.ts +3 -0
- package/server/lib/temp.ts +86 -0
- package/server/lib/token-metadata.ts +86 -0
- package/server/lib/token-safety.ts +200 -0
- package/server/lib/token-search.ts +444 -0
- package/server/lib/totp.ts +194 -0
- package/server/lib/transactions.ts +123 -0
- package/server/lib/transport.ts +84 -0
- package/server/lib/txhistory/decoder.ts +262 -0
- package/server/lib/txhistory/enricher.ts +652 -0
- package/server/lib/txhistory/index.ts +391 -0
- package/server/lib/txhistory/signatures.ts +59 -0
- package/server/lib/update-check.ts +35 -0
- package/server/lib/verified-summary.ts +414 -0
- package/server/lib/view-registry.ts +80 -0
- package/server/mcp/profile-policy.ts +30 -0
- package/server/mcp/server.ts +1589 -0
- package/server/mcp/tools.ts +276 -0
- package/server/middleware/auth.ts +119 -0
- package/server/middleware/requestLogger.ts +84 -0
- package/server/routes/actions.ts +539 -0
- package/server/routes/adapters.ts +711 -0
- package/server/routes/addressbook.ts +113 -0
- package/server/routes/ai.ts +34 -0
- package/server/routes/apikeys.ts +343 -0
- package/server/routes/apps.ts +601 -0
- package/server/routes/auth.ts +406 -0
- package/server/routes/backup.ts +404 -0
- package/server/routes/batch.ts +270 -0
- package/server/routes/bookmarks.ts +162 -0
- package/server/routes/credential-shares.ts +380 -0
- package/server/routes/credential-vaults.ts +159 -0
- package/server/routes/credentials.ts +1782 -0
- package/server/routes/dashboard.ts +97 -0
- package/server/routes/defaults.ts +124 -0
- package/server/routes/flags.ts +11 -0
- package/server/routes/fund.ts +225 -0
- package/server/routes/heartbeat.ts +375 -0
- package/server/routes/import.ts +364 -0
- package/server/routes/launch.ts +665 -0
- package/server/routes/lock.ts +54 -0
- package/server/routes/logs.ts +68 -0
- package/server/routes/nuke.ts +111 -0
- package/server/routes/passkey-credentials.ts +99 -0
- package/server/routes/passkey.ts +366 -0
- package/server/routes/portfolio.ts +217 -0
- package/server/routes/price.ts +63 -0
- package/server/routes/resolve.ts +31 -0
- package/server/routes/security.ts +45 -0
- package/server/routes/send-evm.ts +241 -0
- package/server/routes/send-solana.ts +281 -0
- package/server/routes/send.ts +178 -0
- package/server/routes/setup.ts +210 -0
- package/server/routes/strategy.ts +894 -0
- package/server/routes/swap-evm.ts +352 -0
- package/server/routes/swap-solana.ts +176 -0
- package/server/routes/swap.ts +356 -0
- package/server/routes/token.ts +247 -0
- package/server/routes/unlock.ts +467 -0
- package/server/routes/views.ts +41 -0
- package/server/routes/wallet-assets.ts +361 -0
- package/server/routes/wallet-transactions.ts +515 -0
- package/server/routes/wallet.ts +709 -0
- package/server/types.ts +146 -0
- package/shared/credential-field-schema.ts +248 -0
- package/skills/auramaxx/HEARTBEAT.md +78 -0
- package/skills/auramaxx/SKILL.md +745 -0
- package/skills/auramaxx/docs/AGENT_SETUP.md +155 -0
- package/skills/auramaxx/docs/API.md +127 -0
- package/skills/auramaxx/docs/AUTH.md +318 -0
- package/skills/auramaxx/docs/CLI.md +130 -0
- package/skills/auramaxx/docs/MCP.md +122 -0
- package/skills/auramaxx/docs/TROUBLESHOOTING.md +357 -0
- package/skills/auramaxx/docs/WORKSPACE.md +673 -0
- package/skills/auramaxx/docs/security.md +227 -0
- package/skills/task-lifecycle/SKILL.md +378 -0
- package/src/app/api/[...doc]/page.tsx +36 -0
- package/src/app/api/agent-requests/route.ts +30 -0
- package/src/app/api/apps/install/route.ts +132 -0
- package/src/app/api/apps/manifests/route.ts +16 -0
- package/src/app/api/apps/static/[...path]/route.ts +57 -0
- package/src/app/api/docs/plain/route.ts +74 -0
- package/src/app/api/events/route.ts +92 -0
- package/src/app/api/page.tsx +290 -0
- package/src/app/api/workspace/[id]/apps/[wid]/route.ts +119 -0
- package/src/app/api/workspace/[id]/apps/route.ts +81 -0
- package/src/app/api/workspace/[id]/export/route.ts +67 -0
- package/src/app/api/workspace/[id]/route.ts +168 -0
- package/src/app/api/workspace/auth.ts +40 -0
- package/src/app/api/workspace/config/route.ts +121 -0
- package/src/app/api/workspace/import/route.ts +127 -0
- package/src/app/api/workspace/route.ts +116 -0
- package/src/app/app-legacy-do-not-use/page.tsx +2245 -0
- package/src/app/apple-icon.png +0 -0
- package/src/app/approve/[actionId]/page.tsx +409 -0
- package/src/app/docs/DocsPageContent.tsx +269 -0
- package/src/app/docs/[...doc]/page.tsx +41 -0
- package/src/app/docs/page.tsx +38 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +819 -0
- package/src/app/health/page.tsx +5 -0
- package/src/app/hello/page.tsx +102 -0
- package/src/app/icon.png +0 -0
- package/src/app/layout.tsx +39 -0
- package/src/app/page.tsx +1964 -0
- package/src/app/privacy/page.tsx +63 -0
- package/src/app/providers.tsx +87 -0
- package/src/app/share/[token]/page.tsx +295 -0
- package/src/app/terms/page.tsx +80 -0
- package/src/components/ChainSelector.tsx +44 -0
- package/src/components/HumanActionBar.tsx +697 -0
- package/src/components/NotificationDrawer.tsx +387 -0
- package/src/components/PasskeyEnrollmentPrompt.tsx +235 -0
- package/src/components/apps/AgentKeysApp.tsx +490 -0
- package/src/components/apps/App.tsx +153 -0
- package/src/components/apps/AppGrid.tsx +15 -0
- package/src/components/apps/DetailedAddressDrawer.tsx +325 -0
- package/src/components/apps/DraggableApp.tsx +562 -0
- package/src/components/apps/IFrameApp.tsx +73 -0
- package/src/components/apps/LogsApp.tsx +360 -0
- package/src/components/apps/SendApp.tsx +394 -0
- package/src/components/apps/SetupWizardApp.tsx +1004 -0
- package/src/components/apps/SystemDefaultsApp.tsx +845 -0
- package/src/components/apps/ThirdPartyApp.tsx +428 -0
- package/src/components/apps/TokenApp.tsx +319 -0
- package/src/components/apps/TransactionsApp.tsx +438 -0
- package/src/components/apps/WalletDetailApp.tsx +1505 -0
- package/src/components/apps/index.ts +13 -0
- package/src/components/design-system/Button.tsx +88 -0
- package/src/components/design-system/ChainIndicator.tsx +65 -0
- package/src/components/design-system/ChainSelector.tsx +147 -0
- package/src/components/design-system/ConfirmationModal.tsx +107 -0
- package/src/components/design-system/ConfirmationPopover.tsx +81 -0
- package/src/components/design-system/DownloadButton.tsx +149 -0
- package/src/components/design-system/Drawer.tsx +133 -0
- package/src/components/design-system/FilterDropdown.tsx +183 -0
- package/src/components/design-system/ItemPicker.tsx +157 -0
- package/src/components/design-system/Modal.tsx +296 -0
- package/src/components/design-system/Popover.tsx +142 -0
- package/src/components/design-system/TextInput.tsx +85 -0
- package/src/components/design-system/Toggle.tsx +65 -0
- package/src/components/design-system/TyvekCollapsibleSection.tsx +55 -0
- package/src/components/design-system/index.ts +14 -0
- package/src/components/docs/ClientSideMarkdown.tsx +51 -0
- package/src/components/docs/DocsSearchBar.tsx +118 -0
- package/src/components/docs/DocsThemeToggle.tsx +38 -0
- package/src/components/docs/PersistentDocGroup.tsx +91 -0
- package/src/components/docs/ShareUrlButton.tsx +33 -0
- package/src/components/docs/SidebarScrollMemory.tsx +56 -0
- package/src/components/health/CredentialHealthDashboard.tsx +214 -0
- package/src/components/icons/ChainIcons.tsx +72 -0
- package/src/components/layout/AppStoreDrawer.tsx +369 -0
- package/src/components/layout/ContentArea.tsx +21 -0
- package/src/components/layout/CreateViewModal.tsx +88 -0
- package/src/components/layout/LeftRail.tsx +114 -0
- package/src/components/layout/TabBar.tsx +284 -0
- package/src/components/layout/WalletSidebar.tsx +1030 -0
- package/src/components/layout/index.ts +6 -0
- package/src/components/marketing/AuraMaxxSpecOverlay.tsx +653 -0
- package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
- package/src/components/vault/ApiKeysConsole.tsx +1272 -0
- package/src/components/vault/AuditConsole.tsx +600 -0
- package/src/components/vault/CredentialDetail.tsx +625 -0
- package/src/components/vault/CredentialEmpty.tsx +55 -0
- package/src/components/vault/CredentialField.tsx +583 -0
- package/src/components/vault/CredentialForm.tsx +1484 -0
- package/src/components/vault/CredentialList.tsx +265 -0
- package/src/components/vault/CredentialRow.tsx +130 -0
- package/src/components/vault/CredentialShareModal.tsx +273 -0
- package/src/components/vault/CredentialVault.tsx +1662 -0
- package/src/components/vault/CredentialWalletWidget.tsx +103 -0
- package/src/components/vault/DocsConsole.tsx +113 -0
- package/src/components/vault/ImportCredentialsModal.tsx +578 -0
- package/src/components/vault/LargeTypeModal.tsx +88 -0
- package/src/components/vault/PasswordGenerator.tsx +232 -0
- package/src/components/vault/TOTPDisplay.tsx +108 -0
- package/src/components/vault/TotpSetupPanel.tsx +198 -0
- package/src/components/vault/VaultSidebar.tsx +881 -0
- package/src/components/vault/credentialFormName.ts +91 -0
- package/src/components/vault/hooks/useVaultKeyboardShortcuts.ts +69 -0
- package/src/components/vault/types.ts +56 -0
- package/src/context/AuthContext.tsx +365 -0
- package/src/context/PriceContext.tsx +113 -0
- package/src/context/ThemeContext.tsx +164 -0
- package/src/context/WebSocketContext.tsx +269 -0
- package/src/context/WorkspaceContext.tsx +668 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useAgentActions.ts +552 -0
- package/src/hooks/useBalance.ts +103 -0
- package/src/hooks/useBalances.ts +129 -0
- package/src/hooks/useTheme.ts +156 -0
- package/src/instrumentation.ts +12 -0
- package/src/lib/api-docs.ts +154 -0
- package/src/lib/api.ts +474 -0
- package/src/lib/app-loader.ts +148 -0
- package/src/lib/app-registry.ts +178 -0
- package/src/lib/app-sdk.ts +157 -0
- package/src/lib/audit-console-adapter.ts +151 -0
- package/src/lib/auth-client.ts +75 -0
- package/src/lib/config.ts +74 -0
- package/src/lib/credential-field-schema.ts +11 -0
- package/src/lib/crypto.ts +112 -0
- package/src/lib/db.ts +21 -0
- package/src/lib/docs.ts +544 -0
- package/src/lib/events.ts +363 -0
- package/src/lib/pino.ts +24 -0
- package/src/lib/theme-handlers.ts +168 -0
- package/src/lib/theme.ts +351 -0
- package/src/lib/tokenData.ts +378 -0
- package/src/lib/totp-import.ts +57 -0
- package/src/lib/vault-crypto.ts +129 -0
- package/src/lib/view-registry.ts +57 -0
- package/src/lib/websocket-server.ts +302 -0
- package/src/lib/websocket-setup.ts +79 -0
- package/src/lib/wordlist.ts +2050 -0
- package/src/lib/workspace-handlers.ts +285 -0
- package/start.sh +170 -0
- package/tailwind.config.ts +99 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
import {
|
|
2
|
+
constants,
|
|
3
|
+
createCipheriv,
|
|
4
|
+
createDecipheriv,
|
|
5
|
+
createPublicKey,
|
|
6
|
+
generateKeyPairSync,
|
|
7
|
+
KeyObject,
|
|
8
|
+
privateDecrypt,
|
|
9
|
+
publicEncrypt,
|
|
10
|
+
randomBytes,
|
|
11
|
+
} from 'crypto';
|
|
12
|
+
import * as net from 'net';
|
|
13
|
+
import { resolveAuraSocketCandidates } from './socket-path';
|
|
14
|
+
|
|
15
|
+
interface HybridEnvelope {
|
|
16
|
+
v: 1;
|
|
17
|
+
alg: 'RSA-OAEP/AES-256-GCM';
|
|
18
|
+
key: string;
|
|
19
|
+
iv: string;
|
|
20
|
+
tag: string;
|
|
21
|
+
data: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const OAEP_HASH = 'sha256';
|
|
25
|
+
|
|
26
|
+
function parseAgentPubkey(pubkey: string): KeyObject {
|
|
27
|
+
const value = pubkey.trim();
|
|
28
|
+
if (!value) {
|
|
29
|
+
throw new Error('Public key is required');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// PEM directly
|
|
33
|
+
if (value.includes('BEGIN PUBLIC KEY')) {
|
|
34
|
+
return createPublicKey(value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Base64(PEM) or DER/SPKI
|
|
38
|
+
const decoded = Buffer.from(value, 'base64');
|
|
39
|
+
if (decoded.length === 0) {
|
|
40
|
+
throw new Error('Invalid public key encoding');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const decodedText = decoded.toString('utf8');
|
|
44
|
+
if (decodedText.includes('BEGIN PUBLIC KEY')) {
|
|
45
|
+
return createPublicKey(decodedText);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return createPublicKey({
|
|
49
|
+
key: decoded,
|
|
50
|
+
format: 'der',
|
|
51
|
+
type: 'spki',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function rsaEncrypt(data: Buffer, key: KeyObject): Buffer {
|
|
56
|
+
return publicEncrypt(
|
|
57
|
+
{
|
|
58
|
+
key,
|
|
59
|
+
padding: constants.RSA_PKCS1_OAEP_PADDING,
|
|
60
|
+
oaepHash: OAEP_HASH,
|
|
61
|
+
},
|
|
62
|
+
data,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function isValidAgentPubkey(pubkey: string): boolean {
|
|
67
|
+
try {
|
|
68
|
+
const key = parseAgentPubkey(pubkey);
|
|
69
|
+
return key.asymmetricKeyType === 'rsa';
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function normalizeAgentPubkey(pubkey: string): string {
|
|
76
|
+
const key = parseAgentPubkey(pubkey);
|
|
77
|
+
return key.export({ type: 'spki', format: 'pem' }).toString();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Encrypt credential data to an agent's RSA-OAEP public key.
|
|
82
|
+
*
|
|
83
|
+
* Always uses hybrid RSA-OAEP + AES-256-GCM envelope so that clients
|
|
84
|
+
* only need a single decryption code path.
|
|
85
|
+
*/
|
|
86
|
+
export function encryptToAgentPubkey(data: string, pubkeyBase64: string): string {
|
|
87
|
+
const key = parseAgentPubkey(pubkeyBase64);
|
|
88
|
+
if (key.asymmetricKeyType !== 'rsa') {
|
|
89
|
+
throw new Error('Public key must be RSA');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const payload = Buffer.from(data, 'utf8');
|
|
93
|
+
|
|
94
|
+
// Always hybrid RSA + AES-GCM
|
|
95
|
+
const sessionKey = randomBytes(32); // AES-256
|
|
96
|
+
const iv = randomBytes(12); // GCM nonce
|
|
97
|
+
const cipher = createCipheriv('aes-256-gcm', sessionKey, iv);
|
|
98
|
+
const encryptedData = Buffer.concat([cipher.update(payload), cipher.final()]);
|
|
99
|
+
const tag = cipher.getAuthTag();
|
|
100
|
+
const wrappedKey = rsaEncrypt(sessionKey, key);
|
|
101
|
+
|
|
102
|
+
const envelope: HybridEnvelope = {
|
|
103
|
+
v: 1,
|
|
104
|
+
alg: 'RSA-OAEP/AES-256-GCM',
|
|
105
|
+
key: wrappedKey.toString('base64'),
|
|
106
|
+
iv: iv.toString('base64'),
|
|
107
|
+
tag: tag.toString('base64'),
|
|
108
|
+
data: encryptedData.toString('base64'),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return Buffer.from(JSON.stringify(envelope), 'utf8').toString('base64');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── Client-side decryption ──
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Decrypt a hybrid RSA-OAEP/AES-256-GCM envelope (or raw RSA ciphertext).
|
|
118
|
+
* Used by CLI tools to decrypt credentials returned from the server.
|
|
119
|
+
*/
|
|
120
|
+
export function decryptWithPrivateKey(encryptedBase64: string, privateKeyPem: string): string {
|
|
121
|
+
const decoded = Buffer.from(encryptedBase64, 'base64');
|
|
122
|
+
let envelope: HybridEnvelope;
|
|
123
|
+
try {
|
|
124
|
+
envelope = JSON.parse(decoded.toString('utf8')) as HybridEnvelope;
|
|
125
|
+
} catch {
|
|
126
|
+
return privateDecrypt(
|
|
127
|
+
{ key: privateKeyPem, padding: constants.RSA_PKCS1_OAEP_PADDING, oaepHash: OAEP_HASH },
|
|
128
|
+
decoded,
|
|
129
|
+
).toString('utf8');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (envelope.v !== 1 || envelope.alg !== 'RSA-OAEP/AES-256-GCM') {
|
|
133
|
+
throw new Error(`Unexpected envelope: v=${envelope.v} alg=${envelope.alg}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const sessionKey = privateDecrypt(
|
|
137
|
+
{ key: privateKeyPem, padding: constants.RSA_PKCS1_OAEP_PADDING, oaepHash: OAEP_HASH },
|
|
138
|
+
Buffer.from(envelope.key, 'base64'),
|
|
139
|
+
);
|
|
140
|
+
const decipher = createDecipheriv('aes-256-gcm', sessionKey, Buffer.from(envelope.iv, 'base64'));
|
|
141
|
+
decipher.setAuthTag(Buffer.from(envelope.tag, 'base64'));
|
|
142
|
+
return Buffer.concat([
|
|
143
|
+
decipher.update(Buffer.from(envelope.data, 'base64')),
|
|
144
|
+
decipher.final(),
|
|
145
|
+
]).toString('utf8');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ── Ephemeral keypair generation ──
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
export interface ProfileIssuanceOverrides {
|
|
153
|
+
ttlSeconds?: number;
|
|
154
|
+
maxReads?: number;
|
|
155
|
+
readScopes?: string[];
|
|
156
|
+
writeScopes?: string[];
|
|
157
|
+
excludeFields?: string[];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface ProfileIssuanceSelection {
|
|
161
|
+
profile?: string;
|
|
162
|
+
profileVersion?: string;
|
|
163
|
+
profileOverrides?: ProfileIssuanceOverrides;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function buildScopedReadTokenIssueRequest(input: {
|
|
167
|
+
agentId?: string;
|
|
168
|
+
pubkey: string;
|
|
169
|
+
} & ProfileIssuanceSelection): Record<string, unknown> {
|
|
170
|
+
if (input.profile && input.profile.trim()) {
|
|
171
|
+
return {
|
|
172
|
+
agentId: input.agentId || 'cli-reader',
|
|
173
|
+
profile: input.profile,
|
|
174
|
+
...(input.profileVersion ? { profileVersion: input.profileVersion } : {}),
|
|
175
|
+
...(input.profileOverrides ? { profileOverrides: input.profileOverrides } : {}),
|
|
176
|
+
pubkey: input.pubkey,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
agentId: input.agentId || 'cli-reader',
|
|
182
|
+
permissions: ['secret:read'],
|
|
183
|
+
credentialAccess: { read: ['vault:*'], excludeFields: [] },
|
|
184
|
+
pubkey: input.pubkey,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function buildScopedWriteTokenIssueRequest(input: {
|
|
189
|
+
agentId?: string;
|
|
190
|
+
pubkey: string;
|
|
191
|
+
vaultId: string;
|
|
192
|
+
} & ProfileIssuanceSelection): Record<string, unknown> {
|
|
193
|
+
if (input.profile && input.profile.trim()) {
|
|
194
|
+
return {
|
|
195
|
+
agentId: input.agentId || 'cli-writer',
|
|
196
|
+
profile: input.profile,
|
|
197
|
+
...(input.profileVersion ? { profileVersion: input.profileVersion } : {}),
|
|
198
|
+
...(input.profileOverrides ? { profileOverrides: input.profileOverrides } : {}),
|
|
199
|
+
pubkey: input.pubkey,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
agentId: input.agentId || 'cli-writer',
|
|
205
|
+
permissions: ['secret:write'],
|
|
206
|
+
credentialAccess: { write: [`vault:${input.vaultId}`] },
|
|
207
|
+
pubkey: input.pubkey,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
export interface EphemeralKeypair {
|
|
211
|
+
publicKeyPem: string;
|
|
212
|
+
privateKeyPem: string;
|
|
213
|
+
publicKeyBase64: string;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
interface AuthRequestCreateResponse {
|
|
217
|
+
success?: boolean;
|
|
218
|
+
requestId?: string;
|
|
219
|
+
secret?: string;
|
|
220
|
+
approveUrl?: string;
|
|
221
|
+
error?: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
interface AuthRequestPollResponse {
|
|
225
|
+
success?: boolean;
|
|
226
|
+
status?: 'pending' | 'approved' | 'rejected';
|
|
227
|
+
encryptedToken?: string;
|
|
228
|
+
error?: string;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface AuthRequestBootstrapOptions extends ProfileIssuanceSelection {
|
|
232
|
+
timeoutMs?: number;
|
|
233
|
+
pollIntervalMs?: number;
|
|
234
|
+
onStatus?: (message: string) => void;
|
|
235
|
+
/** If true, create the auth request and return immediately without polling. */
|
|
236
|
+
noWait?: boolean;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface AuthRequestCreated {
|
|
240
|
+
requestId: string;
|
|
241
|
+
secret: string;
|
|
242
|
+
approveUrl?: string;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function generateEphemeralKeypair(): EphemeralKeypair {
|
|
246
|
+
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
|
|
247
|
+
modulusLength: 2048,
|
|
248
|
+
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
249
|
+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
publicKeyPem: publicKey,
|
|
253
|
+
privateKeyPem: privateKey,
|
|
254
|
+
publicKeyBase64: Buffer.from(publicKey, 'utf8').toString('base64'),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function sleep(ms: number): Promise<void> {
|
|
259
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function parseProfileOverridesFromEnv(): ProfileIssuanceOverrides | undefined {
|
|
263
|
+
const raw = process.env.AURA_AUTH_PROFILE_OVERRIDES;
|
|
264
|
+
if (!raw) return undefined;
|
|
265
|
+
try {
|
|
266
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
267
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
268
|
+
throw new Error('must be a JSON object');
|
|
269
|
+
}
|
|
270
|
+
return parsed as ProfileIssuanceOverrides;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
const message = error instanceof Error ? error.message : 'invalid JSON';
|
|
273
|
+
throw new Error(`Invalid AURA_AUTH_PROFILE_OVERRIDES: ${message}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ── Socket bootstrap (CLI auth via Unix socket) ──
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Authenticate with the AuraMaxx server via Unix socket.
|
|
281
|
+
* Returns a bearer token for API access.
|
|
282
|
+
*/
|
|
283
|
+
export function bootstrapViaSocket(
|
|
284
|
+
agentId: string,
|
|
285
|
+
keypair: EphemeralKeypair,
|
|
286
|
+
): Promise<string> {
|
|
287
|
+
const socketPaths = resolveAuraSocketCandidates();
|
|
288
|
+
|
|
289
|
+
const connectToSocket = (socketPath: string): Promise<string> => new Promise((resolve, reject) => {
|
|
290
|
+
const socket = net.createConnection(socketPath);
|
|
291
|
+
let buffer = '';
|
|
292
|
+
let resolved = false;
|
|
293
|
+
|
|
294
|
+
const timeout = setTimeout(() => {
|
|
295
|
+
if (!resolved) {
|
|
296
|
+
resolved = true;
|
|
297
|
+
socket.destroy();
|
|
298
|
+
reject(new Error('Socket auth timed out. Is AuraMaxx running? (npx auramaxx start)'));
|
|
299
|
+
}
|
|
300
|
+
}, 5000);
|
|
301
|
+
|
|
302
|
+
socket.on('error', (err) => {
|
|
303
|
+
if (!resolved) {
|
|
304
|
+
resolved = true;
|
|
305
|
+
clearTimeout(timeout);
|
|
306
|
+
reject(new Error(`Cannot connect to AuraMaxx: ${err.message}\nRun 'npx auramaxx start' first.`));
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
socket.on('connect', () => {
|
|
311
|
+
socket.write(JSON.stringify({
|
|
312
|
+
type: 'auth',
|
|
313
|
+
agentId,
|
|
314
|
+
autoApprove: true,
|
|
315
|
+
pubkey: keypair.publicKeyPem,
|
|
316
|
+
}) + '\n');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
socket.on('data', (data) => {
|
|
320
|
+
buffer += data.toString();
|
|
321
|
+
let idx;
|
|
322
|
+
while ((idx = buffer.indexOf('\n')) !== -1) {
|
|
323
|
+
const line = buffer.substring(0, idx);
|
|
324
|
+
buffer = buffer.substring(idx + 1);
|
|
325
|
+
if (!line.trim()) continue;
|
|
326
|
+
try {
|
|
327
|
+
const msg = JSON.parse(line.trim()) as {
|
|
328
|
+
type: string;
|
|
329
|
+
encryptedToken?: string;
|
|
330
|
+
message?: string;
|
|
331
|
+
};
|
|
332
|
+
if (msg.type === 'auth_approved' && msg.encryptedToken) {
|
|
333
|
+
if (!resolved) {
|
|
334
|
+
resolved = true;
|
|
335
|
+
clearTimeout(timeout);
|
|
336
|
+
socket.destroy();
|
|
337
|
+
resolve(decryptWithPrivateKey(msg.encryptedToken, keypair.privateKeyPem));
|
|
338
|
+
}
|
|
339
|
+
} else if (msg.type === 'error') {
|
|
340
|
+
if (!resolved) {
|
|
341
|
+
resolved = true;
|
|
342
|
+
clearTimeout(timeout);
|
|
343
|
+
socket.destroy();
|
|
344
|
+
reject(new Error(`Auth error: ${msg.message}`));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} catch { /* ignore parse errors */ }
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
return (async () => {
|
|
353
|
+
let lastError: Error | null = null;
|
|
354
|
+
for (const socketPath of socketPaths) {
|
|
355
|
+
try {
|
|
356
|
+
return await connectToSocket(socketPath);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
const current = error instanceof Error ? error : new Error(String(error));
|
|
359
|
+
lastError = current;
|
|
360
|
+
// Only fall through to secondary path for connection-level failures.
|
|
361
|
+
if (!current.message.startsWith('Cannot connect to AuraMaxx')) {
|
|
362
|
+
throw current;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
throw lastError || new Error('Cannot connect to AuraMaxx.');
|
|
367
|
+
})();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Fallback auth flow for strict/disabled local auto-approve mode:
|
|
372
|
+
* create /auth request, then poll until human approval resolves.
|
|
373
|
+
*
|
|
374
|
+
* With `noWait: true`, creates the request and returns immediately
|
|
375
|
+
* with the approval URL (no polling). Callers can present the URL
|
|
376
|
+
* and exit, matching the MCP `auth` tool behavior.
|
|
377
|
+
*/
|
|
378
|
+
export async function bootstrapViaAuthRequest(
|
|
379
|
+
baseUrl: string,
|
|
380
|
+
agentId: string,
|
|
381
|
+
keypair: EphemeralKeypair,
|
|
382
|
+
options: AuthRequestBootstrapOptions & { noWait: true },
|
|
383
|
+
): Promise<AuthRequestCreated>;
|
|
384
|
+
export async function bootstrapViaAuthRequest(
|
|
385
|
+
baseUrl: string,
|
|
386
|
+
agentId: string,
|
|
387
|
+
keypair: EphemeralKeypair,
|
|
388
|
+
options?: AuthRequestBootstrapOptions,
|
|
389
|
+
): Promise<string>;
|
|
390
|
+
export async function bootstrapViaAuthRequest(
|
|
391
|
+
baseUrl: string,
|
|
392
|
+
agentId: string,
|
|
393
|
+
keypair: EphemeralKeypair,
|
|
394
|
+
options: AuthRequestBootstrapOptions = {},
|
|
395
|
+
): Promise<string | AuthRequestCreated> {
|
|
396
|
+
const profile = (
|
|
397
|
+
options.profile ||
|
|
398
|
+
process.env.AURA_AUTH_PROFILE ||
|
|
399
|
+
process.env.AURA_AGENT_PROFILE ||
|
|
400
|
+
'strict'
|
|
401
|
+
).trim();
|
|
402
|
+
if (!profile) {
|
|
403
|
+
throw new Error('No profile configured for /auth fallback');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const profileVersion = (
|
|
407
|
+
options.profileVersion ||
|
|
408
|
+
process.env.AURA_AUTH_PROFILE_VERSION ||
|
|
409
|
+
'v1'
|
|
410
|
+
).trim();
|
|
411
|
+
const profileOverrides = options.profileOverrides || parseProfileOverridesFromEnv();
|
|
412
|
+
const timeoutMs = options.timeoutMs ?? 120_000;
|
|
413
|
+
const pollIntervalMs = options.pollIntervalMs ?? 3_000;
|
|
414
|
+
|
|
415
|
+
options.onStatus?.(`Socket auth unavailable; requesting /auth approval with profile '${profile}'.`);
|
|
416
|
+
|
|
417
|
+
const createResponse = await fetch(`${baseUrl}/auth`, {
|
|
418
|
+
method: 'POST',
|
|
419
|
+
headers: { 'Content-Type': 'application/json' },
|
|
420
|
+
body: JSON.stringify({
|
|
421
|
+
agentId,
|
|
422
|
+
profile,
|
|
423
|
+
...(profileVersion ? { profileVersion } : {}),
|
|
424
|
+
...(profileOverrides ? { profileOverrides } : {}),
|
|
425
|
+
pubkey: keypair.publicKeyPem,
|
|
426
|
+
}),
|
|
427
|
+
signal: AbortSignal.timeout(8_000),
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
const createData = await createResponse.json().catch(() => ({})) as AuthRequestCreateResponse;
|
|
431
|
+
if (!createResponse.ok || !createData.success || !createData.requestId || !createData.secret) {
|
|
432
|
+
const reason = createData.error || `HTTP ${createResponse.status}`;
|
|
433
|
+
throw new Error(`Failed to create /auth request: ${reason}`);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const requestId = createData.requestId;
|
|
437
|
+
const secret = createData.secret;
|
|
438
|
+
const approveUrl = typeof createData.approveUrl === 'string' ? createData.approveUrl : undefined;
|
|
439
|
+
|
|
440
|
+
// Non-blocking mode: return immediately with the approval info
|
|
441
|
+
if (options.noWait) {
|
|
442
|
+
if (approveUrl) {
|
|
443
|
+
options.onStatus?.(`Auth request created (${requestId}). Approve at:\n ${approveUrl}`);
|
|
444
|
+
} else {
|
|
445
|
+
options.onStatus?.(`Auth request created (${requestId}). Awaiting approval.`);
|
|
446
|
+
}
|
|
447
|
+
return { requestId, secret, approveUrl };
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const startedAt = Date.now();
|
|
451
|
+
let announcedPending = false;
|
|
452
|
+
|
|
453
|
+
if (approveUrl) {
|
|
454
|
+
options.onStatus?.(`Auth request created (${requestId}). Approve at:\n ${approveUrl}\nWaiting for human approval...`);
|
|
455
|
+
} else {
|
|
456
|
+
options.onStatus?.(`Auth request created (${requestId}). Waiting for human approval...`);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
while (Date.now() - startedAt <= timeoutMs) {
|
|
460
|
+
const pollResponse = await fetch(
|
|
461
|
+
`${baseUrl}/auth/${encodeURIComponent(requestId)}?secret=${encodeURIComponent(secret)}`,
|
|
462
|
+
{ signal: AbortSignal.timeout(8_000) },
|
|
463
|
+
);
|
|
464
|
+
const pollData = await pollResponse.json().catch(() => ({})) as AuthRequestPollResponse;
|
|
465
|
+
|
|
466
|
+
if (pollResponse.ok && pollData.success) {
|
|
467
|
+
if (pollData.status === 'approved') {
|
|
468
|
+
if (!pollData.encryptedToken) {
|
|
469
|
+
throw new Error('Approval resolved but encrypted token is missing');
|
|
470
|
+
}
|
|
471
|
+
options.onStatus?.('Approval received. Continuing...');
|
|
472
|
+
return decryptWithPrivateKey(pollData.encryptedToken, keypair.privateKeyPem);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (pollData.status === 'rejected') {
|
|
476
|
+
throw new Error(`Auth request rejected (${requestId})`);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (pollData.status === 'pending' && !announcedPending) {
|
|
480
|
+
options.onStatus?.('Still pending approval in dashboard...');
|
|
481
|
+
announcedPending = true;
|
|
482
|
+
}
|
|
483
|
+
} else if (pollResponse.status === 410) {
|
|
484
|
+
throw new Error(`Auth token claim expired or already claimed (${requestId})`);
|
|
485
|
+
} else if (pollResponse.status >= 400 && pollResponse.status < 500 && pollResponse.status !== 404) {
|
|
486
|
+
const reason = pollData.error || `HTTP ${pollResponse.status}`;
|
|
487
|
+
throw new Error(`Auth polling failed: ${reason}`);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
await sleep(pollIntervalMs);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
throw new Error(`Timed out waiting for approval after ${Math.round(timeoutMs / 1000)}s (${requestId})`);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Create a scoped credential-read token via the server API.
|
|
498
|
+
*/
|
|
499
|
+
export async function createReadToken(
|
|
500
|
+
baseUrl: string,
|
|
501
|
+
token: string,
|
|
502
|
+
keypair: EphemeralKeypair,
|
|
503
|
+
agentId: string = 'cli-reader',
|
|
504
|
+
profile?: ProfileIssuanceSelection,
|
|
505
|
+
): Promise<string> {
|
|
506
|
+
const res = await fetch(`${baseUrl}/actions/token`, {
|
|
507
|
+
method: 'POST',
|
|
508
|
+
headers: {
|
|
509
|
+
'Content-Type': 'application/json',
|
|
510
|
+
'Authorization': `Bearer ${token}`,
|
|
511
|
+
},
|
|
512
|
+
body: JSON.stringify(buildScopedReadTokenIssueRequest({
|
|
513
|
+
agentId,
|
|
514
|
+
pubkey: keypair.publicKeyBase64,
|
|
515
|
+
profile: profile?.profile,
|
|
516
|
+
profileVersion: profile?.profileVersion,
|
|
517
|
+
profileOverrides: profile?.profileOverrides,
|
|
518
|
+
})),
|
|
519
|
+
signal: AbortSignal.timeout(5000),
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
if (!res.ok) {
|
|
523
|
+
const text = await res.text();
|
|
524
|
+
throw new Error(`Failed to create read token (${res.status}): ${text}`);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const data = await res.json() as { encryptedToken?: string };
|
|
528
|
+
if (!data.encryptedToken) {
|
|
529
|
+
throw new Error('No encryptedToken in response');
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return decryptWithPrivateKey(data.encryptedToken, keypair.privateKeyPem);
|
|
533
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential Vault — Subkey Derivation & Session Management
|
|
3
|
+
* =========================================================
|
|
4
|
+
*
|
|
5
|
+
* Derives a credential-specific encryption key from the wallet mnemonic.
|
|
6
|
+
* The derived key is held in memory while the vault is unlocked and used
|
|
7
|
+
* to encrypt/decrypt credential files. Hooks into the vault lifecycle
|
|
8
|
+
* (unlock/lock) in cold.ts.
|
|
9
|
+
*
|
|
10
|
+
* Key derivation: SHA256("credential-v1:" + vaultId + ":" + mnemonic)
|
|
11
|
+
* This produces a deterministic 256-bit key unique to each vault.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import CryptoJS from 'crypto-js';
|
|
15
|
+
import { getVaultMnemonic } from './cold';
|
|
16
|
+
|
|
17
|
+
// In-memory storage of derived credential keys (vaultId → hex key)
|
|
18
|
+
const credentialVaultSessions = new Map<string, string>();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Derive a credential encryption key from a vault's mnemonic.
|
|
22
|
+
* Uses SHA-256 of a domain-separated string for deterministic derivation.
|
|
23
|
+
*/
|
|
24
|
+
export function deriveCredentialKey(vaultId: string, mnemonic: string): string {
|
|
25
|
+
const input = `credential-v1:${vaultId}:${mnemonic}`;
|
|
26
|
+
return CryptoJS.SHA256(input).toString(CryptoJS.enc.Hex);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Unlock the credential vault for a given vault ID.
|
|
31
|
+
* Reads the mnemonic from the already-unlocked wallet vault session
|
|
32
|
+
* and derives + stores the credential subkey.
|
|
33
|
+
*/
|
|
34
|
+
export function unlockCredentialVault(vaultId: string): boolean {
|
|
35
|
+
const mnemonic = getVaultMnemonic(vaultId);
|
|
36
|
+
if (!mnemonic) return false;
|
|
37
|
+
|
|
38
|
+
const key = deriveCredentialKey(vaultId, mnemonic);
|
|
39
|
+
credentialVaultSessions.set(vaultId, key);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Lock the credential vault for a given vault ID.
|
|
45
|
+
* Removes the derived key from memory.
|
|
46
|
+
*/
|
|
47
|
+
export function lockCredentialVault(vaultId: string): void {
|
|
48
|
+
credentialVaultSessions.delete(vaultId);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Lock all credential vaults.
|
|
53
|
+
*/
|
|
54
|
+
export function lockAllCredentialVaults(): void {
|
|
55
|
+
credentialVaultSessions.clear();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the derived credential key for a vault (or null if locked).
|
|
60
|
+
*/
|
|
61
|
+
export function getCredentialVaultKey(vaultId: string): string | null {
|
|
62
|
+
return credentialVaultSessions.get(vaultId) ?? null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Check if a credential vault is unlocked.
|
|
67
|
+
*/
|
|
68
|
+
export function isCredentialVaultUnlocked(vaultId: string): boolean {
|
|
69
|
+
return credentialVaultSessions.has(vaultId);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Reset all credential vault state (for testing only).
|
|
74
|
+
*/
|
|
75
|
+
export function _resetCredentialVaultForTesting(): void {
|
|
76
|
+
credentialVaultSessions.clear();
|
|
77
|
+
}
|