agent-relay 1.3.0 → 1.3.2

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 (240) hide show
  1. package/.trajectories/active/traj_3yx9dy148mge.json +42 -0
  2. package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.json +49 -0
  3. package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.md +31 -0
  4. package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.json +49 -0
  5. package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.md +31 -0
  6. package/.trajectories/completed/2026-01/traj_6unwwmgyj5sq.json +109 -0
  7. package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.json +49 -0
  8. package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.md +31 -0
  9. package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.json +66 -0
  10. package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.md +36 -0
  11. package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.json +49 -0
  12. package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.md +31 -0
  13. package/.trajectories/completed/2026-01/traj_cpn70dw066nt.json +65 -0
  14. package/.trajectories/completed/2026-01/traj_cpn70dw066nt.md +37 -0
  15. package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.json +36 -0
  16. package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.md +21 -0
  17. package/.trajectories/completed/2026-01/traj_he75f24d1xfm.json +101 -0
  18. package/.trajectories/completed/2026-01/traj_he75f24d1xfm.md +52 -0
  19. package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.json +61 -0
  20. package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.md +36 -0
  21. package/.trajectories/completed/2026-01/traj_oszg9flv74pk.json +73 -0
  22. package/.trajectories/completed/2026-01/traj_oszg9flv74pk.md +41 -0
  23. package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.json +77 -0
  24. package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.md +42 -0
  25. package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.json +109 -0
  26. package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.md +56 -0
  27. package/.trajectories/completed/2026-01/traj_x721m1j9rzup.json +113 -0
  28. package/.trajectories/completed/2026-01/traj_x721m1j9rzup.md +57 -0
  29. package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.json +61 -0
  30. package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.md +36 -0
  31. package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.json +49 -0
  32. package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.md +31 -0
  33. package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.json +49 -0
  34. package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.md +31 -0
  35. package/.trajectories/index.json +140 -1
  36. package/TRAIL_GIT_AUTH_FIX.md +113 -0
  37. package/deploy/workspace/codex.config.toml +1 -1
  38. package/deploy/workspace/entrypoint.sh +20 -79
  39. package/deploy/workspace/gh-relay +156 -0
  40. package/deploy/workspace/git-credential-relay +5 -1
  41. package/dist/bridge/multi-project-client.js +13 -10
  42. package/dist/bridge/spawner.d.ts +2 -0
  43. package/dist/bridge/spawner.js +19 -1
  44. package/dist/bridge/types.d.ts +2 -0
  45. package/dist/cli/index.d.ts +1 -1
  46. package/dist/cli/index.js +115 -69
  47. package/dist/cloud/api/admin.js +16 -3
  48. package/dist/cloud/api/codex-auth-helper.js +28 -8
  49. package/dist/cloud/api/consensus.d.ts +13 -0
  50. package/dist/cloud/api/consensus.js +259 -0
  51. package/dist/cloud/api/daemons.js +205 -1
  52. package/dist/cloud/api/git.js +37 -7
  53. package/dist/cloud/api/onboarding.js +4 -1
  54. package/dist/cloud/api/provider-env.d.ts +5 -0
  55. package/dist/cloud/api/provider-env.js +27 -0
  56. package/dist/cloud/api/providers.js +2 -0
  57. package/dist/cloud/api/test-helpers.js +130 -0
  58. package/dist/cloud/api/workspaces.js +38 -3
  59. package/dist/cloud/db/bulk-ingest.d.ts +88 -0
  60. package/dist/cloud/db/bulk-ingest.js +268 -0
  61. package/dist/cloud/db/drizzle.d.ts +33 -0
  62. package/dist/cloud/db/drizzle.js +174 -2
  63. package/dist/cloud/db/index.d.ts +24 -5
  64. package/dist/cloud/db/index.js +19 -4
  65. package/dist/cloud/db/schema.d.ts +397 -3
  66. package/dist/cloud/db/schema.js +75 -1
  67. package/dist/cloud/provisioner/index.d.ts +8 -0
  68. package/dist/cloud/provisioner/index.js +256 -50
  69. package/dist/cloud/server.js +47 -3
  70. package/dist/cloud/services/index.d.ts +1 -0
  71. package/dist/cloud/services/index.js +2 -0
  72. package/dist/cloud/services/nango.d.ts +3 -4
  73. package/dist/cloud/services/nango.js +11 -33
  74. package/dist/cloud/services/workspace-keepalive.d.ts +76 -0
  75. package/dist/cloud/services/workspace-keepalive.js +234 -0
  76. package/dist/config/relay-config.d.ts +23 -0
  77. package/dist/config/relay-config.js +23 -0
  78. package/dist/daemon/agent-manager.d.ts +20 -1
  79. package/dist/daemon/agent-manager.js +47 -0
  80. package/dist/daemon/agent-registry.js +4 -4
  81. package/dist/daemon/agent-signing.d.ts +158 -0
  82. package/dist/daemon/agent-signing.js +523 -0
  83. package/dist/daemon/api.js +18 -1
  84. package/dist/daemon/cli-auth.d.ts +4 -1
  85. package/dist/daemon/cli-auth.js +55 -11
  86. package/dist/daemon/cloud-sync.d.ts +47 -1
  87. package/dist/daemon/cloud-sync.js +152 -3
  88. package/dist/daemon/connection.d.ts +28 -0
  89. package/dist/daemon/connection.js +98 -15
  90. package/dist/daemon/consensus-integration.d.ts +167 -0
  91. package/dist/daemon/consensus-integration.js +371 -0
  92. package/dist/daemon/consensus.d.ts +271 -0
  93. package/dist/daemon/consensus.js +632 -0
  94. package/dist/daemon/delivery-tracker.d.ts +34 -0
  95. package/dist/daemon/delivery-tracker.js +104 -0
  96. package/dist/daemon/enhanced-features.d.ts +118 -0
  97. package/dist/daemon/enhanced-features.js +178 -0
  98. package/dist/daemon/index.d.ts +4 -0
  99. package/dist/daemon/index.js +5 -0
  100. package/dist/daemon/rate-limiter.d.ts +68 -0
  101. package/dist/daemon/rate-limiter.js +130 -0
  102. package/dist/daemon/router.d.ts +18 -11
  103. package/dist/daemon/router.js +55 -111
  104. package/dist/daemon/server.d.ts +13 -1
  105. package/dist/daemon/server.js +71 -9
  106. package/dist/daemon/sync-queue.d.ts +116 -0
  107. package/dist/daemon/sync-queue.js +361 -0
  108. package/dist/health-worker-manager.d.ts +62 -0
  109. package/dist/health-worker-manager.js +144 -0
  110. package/dist/health-worker.d.ts +9 -0
  111. package/dist/health-worker.js +79 -0
  112. package/dist/index.d.ts +2 -1
  113. package/dist/index.js +5 -1
  114. package/dist/memory/context-compaction.d.ts +156 -0
  115. package/dist/memory/context-compaction.js +453 -0
  116. package/dist/memory/index.d.ts +1 -0
  117. package/dist/memory/index.js +1 -0
  118. package/dist/protocol/channels.js +4 -4
  119. package/dist/protocol/framing.d.ts +72 -10
  120. package/dist/protocol/framing.js +194 -25
  121. package/dist/storage/adapter.d.ts +8 -1
  122. package/dist/storage/adapter.js +11 -0
  123. package/dist/storage/batched-sqlite-adapter.d.ts +71 -0
  124. package/dist/storage/batched-sqlite-adapter.js +183 -0
  125. package/dist/storage/dead-letter-queue.d.ts +196 -0
  126. package/dist/storage/dead-letter-queue.js +427 -0
  127. package/dist/storage/dlq-adapter.d.ts +195 -0
  128. package/dist/storage/dlq-adapter.js +664 -0
  129. package/dist/trajectory/config.d.ts +32 -14
  130. package/dist/trajectory/config.js +38 -16
  131. package/dist/trajectory/integration.js +217 -64
  132. package/dist/utils/git-remote.d.ts +47 -0
  133. package/dist/utils/git-remote.js +125 -0
  134. package/dist/utils/id-generator.d.ts +35 -0
  135. package/dist/utils/id-generator.js +60 -0
  136. package/dist/utils/index.d.ts +1 -0
  137. package/dist/utils/index.js +1 -0
  138. package/dist/utils/precompiled-patterns.d.ts +110 -0
  139. package/dist/utils/precompiled-patterns.js +322 -0
  140. package/dist/wrapper/auth-detection.js +1 -1
  141. package/dist/wrapper/base-wrapper.d.ts +36 -0
  142. package/dist/wrapper/base-wrapper.js +48 -2
  143. package/dist/wrapper/client.d.ts +14 -4
  144. package/dist/wrapper/client.js +84 -31
  145. package/dist/wrapper/idle-detector.d.ts +102 -0
  146. package/dist/wrapper/idle-detector.js +279 -0
  147. package/dist/wrapper/parser.d.ts +4 -0
  148. package/dist/wrapper/parser.js +19 -1
  149. package/dist/wrapper/pty-wrapper.d.ts +7 -1
  150. package/dist/wrapper/pty-wrapper.js +51 -27
  151. package/dist/wrapper/tmux-wrapper.d.ts +12 -1
  152. package/dist/wrapper/tmux-wrapper.js +65 -17
  153. package/package.json +5 -5
  154. package/scripts/run-migrations.js +43 -0
  155. package/scripts/verify-schema.js +134 -0
  156. package/tests/benchmarks/protocol.bench.ts +310 -0
  157. package/dist/dashboard/out/404.html +0 -1
  158. package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_buildManifest.js +0 -1
  159. package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_ssgManifest.js +0 -1
  160. package/dist/dashboard/out/_next/static/chunks/116-2502180def231162.js +0 -1
  161. package/dist/dashboard/out/_next/static/chunks/117-f7b8ab0809342e77.js +0 -2
  162. package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +0 -1
  163. package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +0 -9
  164. package/dist/dashboard/out/_next/static/chunks/648-5cc6e1921389a58a.js +0 -1
  165. package/dist/dashboard/out/_next/static/chunks/766-b54f0853794b78c3.js +0 -1
  166. package/dist/dashboard/out/_next/static/chunks/83-b51836037078006c.js +0 -1
  167. package/dist/dashboard/out/_next/static/chunks/891-6cd50de1224f70bb.js +0 -1
  168. package/dist/dashboard/out/_next/static/chunks/899-bb19a9b3d9b39ea6.js +0 -1
  169. package/dist/dashboard/out/_next/static/chunks/app/_not-found/page-53b8a69f76db17d0.js +0 -1
  170. package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-8939b0fc700f7eca.js +0 -1
  171. package/dist/dashboard/out/_next/static/chunks/app/app/page-5af1b6b439858aa6.js +0 -1
  172. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-f45ecbc3e06134fc.js +0 -1
  173. package/dist/dashboard/out/_next/static/chunks/app/history/page-8c8bed33beb2bf1c.js +0 -1
  174. package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +0 -1
  175. package/dist/dashboard/out/_next/static/chunks/app/login/page-16f3b49e55b1e0ed.js +0 -1
  176. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-ac39dc0cc3c26fa7.js +0 -1
  177. package/dist/dashboard/out/_next/static/chunks/app/page-4a5938c18a11a654.js +0 -1
  178. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-982a7000fee44014.js +0 -1
  179. package/dist/dashboard/out/_next/static/chunks/app/providers/page-ac3a6ac433fd6001.js +0 -1
  180. package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-09f9caae98a18c09.js +0 -1
  181. package/dist/dashboard/out/_next/static/chunks/app/signup/page-547dd0ca55ecd0ba.js +0 -1
  182. package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +0 -18
  183. package/dist/dashboard/out/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +0 -1
  184. package/dist/dashboard/out/_next/static/chunks/framework-f66176bb897dc684.js +0 -1
  185. package/dist/dashboard/out/_next/static/chunks/main-2ee6beb2ae96d210.js +0 -1
  186. package/dist/dashboard/out/_next/static/chunks/main-app-5d692157a8eb1fd9.js +0 -1
  187. package/dist/dashboard/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +0 -1
  188. package/dist/dashboard/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +0 -1
  189. package/dist/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  190. package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +0 -1
  191. package/dist/dashboard/out/_next/static/css/85d2af9c7ac74d62.css +0 -1
  192. package/dist/dashboard/out/_next/static/css/fe4b28883eeff359.css +0 -1
  193. package/dist/dashboard/out/alt-logos/agent-relay-logo-128.png +0 -0
  194. package/dist/dashboard/out/alt-logos/agent-relay-logo-256.png +0 -0
  195. package/dist/dashboard/out/alt-logos/agent-relay-logo-32.png +0 -0
  196. package/dist/dashboard/out/alt-logos/agent-relay-logo-512.png +0 -0
  197. package/dist/dashboard/out/alt-logos/agent-relay-logo-64.png +0 -0
  198. package/dist/dashboard/out/alt-logos/agent-relay-logo.svg +0 -45
  199. package/dist/dashboard/out/alt-logos/logo.svg +0 -38
  200. package/dist/dashboard/out/alt-logos/monogram-logo-128.png +0 -0
  201. package/dist/dashboard/out/alt-logos/monogram-logo-256.png +0 -0
  202. package/dist/dashboard/out/alt-logos/monogram-logo-32.png +0 -0
  203. package/dist/dashboard/out/alt-logos/monogram-logo-512.png +0 -0
  204. package/dist/dashboard/out/alt-logos/monogram-logo-64.png +0 -0
  205. package/dist/dashboard/out/alt-logos/monogram-logo.svg +0 -38
  206. package/dist/dashboard/out/app/onboarding.html +0 -1
  207. package/dist/dashboard/out/app/onboarding.txt +0 -7
  208. package/dist/dashboard/out/app.html +0 -1
  209. package/dist/dashboard/out/app.txt +0 -7
  210. package/dist/dashboard/out/apple-icon.png +0 -0
  211. package/dist/dashboard/out/connect-repos.html +0 -1
  212. package/dist/dashboard/out/connect-repos.txt +0 -7
  213. package/dist/dashboard/out/history.html +0 -1
  214. package/dist/dashboard/out/history.txt +0 -7
  215. package/dist/dashboard/out/index.html +0 -1
  216. package/dist/dashboard/out/index.txt +0 -7
  217. package/dist/dashboard/out/login.html +0 -6
  218. package/dist/dashboard/out/login.txt +0 -7
  219. package/dist/dashboard/out/metrics.html +0 -1
  220. package/dist/dashboard/out/metrics.txt +0 -7
  221. package/dist/dashboard/out/pricing.html +0 -13
  222. package/dist/dashboard/out/pricing.txt +0 -7
  223. package/dist/dashboard/out/providers/setup/claude.html +0 -1
  224. package/dist/dashboard/out/providers/setup/claude.txt +0 -8
  225. package/dist/dashboard/out/providers/setup/codex.html +0 -1
  226. package/dist/dashboard/out/providers/setup/codex.txt +0 -8
  227. package/dist/dashboard/out/providers.html +0 -1
  228. package/dist/dashboard/out/providers.txt +0 -7
  229. package/dist/dashboard/out/signup.html +0 -6
  230. package/dist/dashboard/out/signup.txt +0 -7
  231. package/dist/dashboard-server/metrics.d.ts +0 -105
  232. package/dist/dashboard-server/metrics.js +0 -193
  233. package/dist/dashboard-server/needs-attention.d.ts +0 -24
  234. package/dist/dashboard-server/needs-attention.js +0 -78
  235. package/dist/dashboard-server/server.d.ts +0 -15
  236. package/dist/dashboard-server/server.js +0 -3776
  237. package/dist/dashboard-server/start.d.ts +0 -6
  238. package/dist/dashboard-server/start.js +0 -13
  239. package/dist/dashboard-server/user-bridge.d.ts +0 -103
  240. package/dist/dashboard-server/user-bridge.js +0 -189
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Agent Authentication via Cryptographic Signing
3
+ *
4
+ * Provides agent identity verification through message signing.
5
+ * Extends the existing UID/GID-based auth with cryptographic guarantees.
6
+ *
7
+ * Features:
8
+ * - HMAC-SHA256 for shared-secret signing (simpler deployment)
9
+ * - Ed25519 for asymmetric signing (zero-trust mode)
10
+ * - Message signature verification
11
+ * - Key rotation support
12
+ * - Agent identity attestation
13
+ */
14
+ export interface AgentKeyPair {
15
+ /** Agent identifier */
16
+ agentName: string;
17
+ /** Public key (hex) for asymmetric, or key ID for HMAC */
18
+ publicKey: string;
19
+ /** Private/secret key (hex) - never transmitted */
20
+ privateKey: string;
21
+ /** Key creation timestamp */
22
+ createdAt: number;
23
+ /** Optional expiry timestamp */
24
+ expiresAt?: number;
25
+ /** Signing algorithm */
26
+ algorithm: 'hmac-sha256' | 'ed25519';
27
+ }
28
+ export interface SignedMessage {
29
+ /** Original message content */
30
+ content: string;
31
+ /** Signature (hex) */
32
+ signature: string;
33
+ /** Signing agent */
34
+ signer: string;
35
+ /** Timestamp of signing */
36
+ signedAt: number;
37
+ /** Key ID used (for rotation support) */
38
+ keyId: string;
39
+ /** Algorithm used */
40
+ algorithm: 'hmac-sha256' | 'ed25519';
41
+ }
42
+ export interface VerificationResult {
43
+ valid: boolean;
44
+ error?: string;
45
+ signer?: string;
46
+ signedAt?: number;
47
+ }
48
+ export interface AgentSigningConfig {
49
+ /** Enable message signing (default: false) */
50
+ enabled: boolean;
51
+ /** Signing algorithm */
52
+ algorithm: 'hmac-sha256' | 'ed25519';
53
+ /** Require signatures on all messages */
54
+ requireSignatures: boolean;
55
+ /** Allow unsigned messages from specific agents */
56
+ allowUnsignedFrom?: string[];
57
+ /** Key directory path */
58
+ keyDir?: string;
59
+ /** Shared secret for HMAC mode (all agents share) */
60
+ sharedSecret?: string;
61
+ /** Key rotation interval in hours (0 = no rotation) */
62
+ keyRotationHours?: number;
63
+ }
64
+ /**
65
+ * Generate a new agent key pair.
66
+ */
67
+ export declare function generateAgentKey(agentName: string, algorithm?: 'hmac-sha256' | 'ed25519', expiresInHours?: number): AgentKeyPair;
68
+ /**
69
+ * Save agent key to disk (private key file).
70
+ */
71
+ export declare function saveAgentKey(key: AgentKeyPair, keyDir?: string): void;
72
+ /**
73
+ * Load agent key from disk.
74
+ */
75
+ export declare function loadAgentKey(agentName: string, keyDir?: string): AgentKeyPair | null;
76
+ /**
77
+ * Load or generate agent key.
78
+ */
79
+ export declare function getOrCreateAgentKey(agentName: string, config: AgentSigningConfig, keyDir?: string): AgentKeyPair;
80
+ /**
81
+ * Sign a message using the agent's private key.
82
+ */
83
+ export declare function signMessage(content: string, key: AgentKeyPair): SignedMessage;
84
+ /**
85
+ * Sign with shared secret (HMAC mode where all agents share a secret).
86
+ */
87
+ export declare function signWithSharedSecret(content: string, agentName: string, sharedSecret: string): SignedMessage;
88
+ /**
89
+ * Verify a signed message using the agent's public key.
90
+ */
91
+ export declare function verifyMessage(signed: SignedMessage, key: AgentKeyPair): VerificationResult;
92
+ /**
93
+ * Verify an Ed25519 signed message using only the public key.
94
+ * This is the key advantage of asymmetric signing - verifiers don't need the private key.
95
+ */
96
+ export declare function verifyEd25519WithPublicKey(signed: SignedMessage, publicKeyPem: string, expectedSigner: string): VerificationResult;
97
+ /**
98
+ * Verify with shared secret.
99
+ */
100
+ export declare function verifyWithSharedSecret(signed: SignedMessage, sharedSecret: string): VerificationResult;
101
+ /**
102
+ * Manages agent signing keys and verification.
103
+ */
104
+ export declare class AgentSigningManager {
105
+ private config;
106
+ private keyDir;
107
+ private keys;
108
+ constructor(config?: Partial<AgentSigningConfig>, keyDir?: string);
109
+ /**
110
+ * Check if signing is enabled.
111
+ */
112
+ get enabled(): boolean;
113
+ /**
114
+ * Get or load key for an agent.
115
+ */
116
+ getKey(agentName: string): AgentKeyPair | null;
117
+ /**
118
+ * Register a new agent (generate and save key).
119
+ */
120
+ registerAgent(agentName: string): AgentKeyPair;
121
+ /**
122
+ * Sign a message for an agent.
123
+ */
124
+ sign(agentName: string, content: string): SignedMessage | null;
125
+ /**
126
+ * Verify a signed message.
127
+ */
128
+ verify(signed: SignedMessage): VerificationResult;
129
+ /**
130
+ * Check if a message requires verification.
131
+ */
132
+ requiresVerification(agentName: string): boolean;
133
+ /**
134
+ * Rotate key for an agent.
135
+ */
136
+ rotateKey(agentName: string): AgentKeyPair;
137
+ /**
138
+ * Export public key for an agent (for sharing with other systems).
139
+ */
140
+ exportPublicKey(agentName: string): {
141
+ agentName: string;
142
+ publicKey: string;
143
+ algorithm: string;
144
+ } | null;
145
+ }
146
+ /**
147
+ * Attach signature to protocol envelope.
148
+ */
149
+ export declare function attachSignature(envelope: Record<string, unknown>, signed: SignedMessage): Record<string, unknown>;
150
+ /**
151
+ * Extract signature from protocol envelope.
152
+ */
153
+ export declare function extractSignature(envelope: Record<string, unknown>): SignedMessage | null;
154
+ /**
155
+ * Load signing configuration from file.
156
+ */
157
+ export declare function loadSigningConfig(configPath?: string): AgentSigningConfig;
158
+ //# sourceMappingURL=agent-signing.d.ts.map
@@ -0,0 +1,523 @@
1
+ /**
2
+ * Agent Authentication via Cryptographic Signing
3
+ *
4
+ * Provides agent identity verification through message signing.
5
+ * Extends the existing UID/GID-based auth with cryptographic guarantees.
6
+ *
7
+ * Features:
8
+ * - HMAC-SHA256 for shared-secret signing (simpler deployment)
9
+ * - Ed25519 for asymmetric signing (zero-trust mode)
10
+ * - Message signature verification
11
+ * - Key rotation support
12
+ * - Agent identity attestation
13
+ */
14
+ import { createHmac, randomBytes, createHash, generateKeyPairSync, sign, verify, createPrivateKey, createPublicKey, } from 'node:crypto';
15
+ import fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import os from 'node:os';
18
+ // =============================================================================
19
+ // Default Configuration
20
+ // =============================================================================
21
+ const DEFAULT_CONFIG = {
22
+ enabled: false,
23
+ algorithm: 'hmac-sha256',
24
+ requireSignatures: false,
25
+ };
26
+ const DEFAULT_KEY_DIR = path.join(os.homedir(), '.agent-relay', 'keys');
27
+ // =============================================================================
28
+ // Key Management
29
+ // =============================================================================
30
+ /**
31
+ * Generate a new agent key pair.
32
+ */
33
+ export function generateAgentKey(agentName, algorithm = 'hmac-sha256', expiresInHours) {
34
+ const now = Date.now();
35
+ if (algorithm === 'hmac-sha256') {
36
+ // For HMAC, we generate a random secret
37
+ const secret = randomBytes(32).toString('hex');
38
+ const keyId = createHash('sha256')
39
+ .update(`${agentName}:${secret}:${now}`)
40
+ .digest('hex')
41
+ .substring(0, 16);
42
+ return {
43
+ agentName,
44
+ publicKey: keyId, // Key ID serves as public identifier
45
+ privateKey: secret,
46
+ createdAt: now,
47
+ expiresAt: expiresInHours ? now + expiresInHours * 3600000 : undefined,
48
+ algorithm,
49
+ };
50
+ }
51
+ // Ed25519 asymmetric key generation
52
+ const { publicKey: pubKeyObj, privateKey: privKeyObj } = generateKeyPairSync('ed25519');
53
+ // Export keys in PEM format for storage
54
+ const privateKeyPem = privKeyObj.export({ type: 'pkcs8', format: 'pem' });
55
+ const publicKeyPem = pubKeyObj.export({ type: 'spki', format: 'pem' });
56
+ // Create a key ID from the public key hash (for rotation tracking)
57
+ const _keyId = createHash('sha256')
58
+ .update(publicKeyPem)
59
+ .digest('hex')
60
+ .substring(0, 16);
61
+ return {
62
+ agentName,
63
+ publicKey: publicKeyPem,
64
+ privateKey: privateKeyPem,
65
+ createdAt: now,
66
+ expiresAt: expiresInHours ? now + expiresInHours * 3600000 : undefined,
67
+ algorithm,
68
+ };
69
+ }
70
+ /**
71
+ * Save agent key to disk (private key file).
72
+ */
73
+ export function saveAgentKey(key, keyDir = DEFAULT_KEY_DIR) {
74
+ if (!fs.existsSync(keyDir)) {
75
+ fs.mkdirSync(keyDir, { recursive: true, mode: 0o700 });
76
+ }
77
+ const keyPath = path.join(keyDir, `${key.agentName}.key.json`);
78
+ fs.writeFileSync(keyPath, JSON.stringify(key, null, 2), {
79
+ mode: 0o600, // Owner read/write only
80
+ });
81
+ }
82
+ /**
83
+ * Load agent key from disk.
84
+ */
85
+ export function loadAgentKey(agentName, keyDir = DEFAULT_KEY_DIR) {
86
+ const keyPath = path.join(keyDir, `${agentName}.key.json`);
87
+ if (!fs.existsSync(keyPath)) {
88
+ return null;
89
+ }
90
+ try {
91
+ const content = fs.readFileSync(keyPath, 'utf-8');
92
+ const key = JSON.parse(content);
93
+ // Check expiry
94
+ if (key.expiresAt && Date.now() > key.expiresAt) {
95
+ console.warn(`[signing] Key for ${agentName} has expired`);
96
+ return null;
97
+ }
98
+ return key;
99
+ }
100
+ catch (err) {
101
+ console.error(`[signing] Failed to load key for ${agentName}:`, err);
102
+ return null;
103
+ }
104
+ }
105
+ /**
106
+ * Load or generate agent key.
107
+ */
108
+ export function getOrCreateAgentKey(agentName, config, keyDir = DEFAULT_KEY_DIR) {
109
+ let key = loadAgentKey(agentName, keyDir);
110
+ if (!key) {
111
+ key = generateAgentKey(agentName, config.algorithm, config.keyRotationHours);
112
+ saveAgentKey(key, keyDir);
113
+ console.log(`[signing] Generated new key for ${agentName}`);
114
+ }
115
+ return key;
116
+ }
117
+ // =============================================================================
118
+ // Message Signing
119
+ // =============================================================================
120
+ /**
121
+ * Sign a message using the agent's private key.
122
+ */
123
+ export function signMessage(content, key) {
124
+ const signedAt = Date.now();
125
+ const dataToSign = `${key.agentName}:${signedAt}:${content}`;
126
+ let signature;
127
+ let keyId;
128
+ if (key.algorithm === 'hmac-sha256') {
129
+ signature = createHmac('sha256', key.privateKey)
130
+ .update(dataToSign)
131
+ .digest('hex');
132
+ keyId = key.publicKey; // For HMAC, publicKey is the key ID
133
+ }
134
+ else {
135
+ // Ed25519 signing using Node.js native crypto
136
+ const privateKeyObj = createPrivateKey(key.privateKey);
137
+ const signatureBuffer = sign(null, Buffer.from(dataToSign), privateKeyObj);
138
+ signature = signatureBuffer.toString('hex');
139
+ // For Ed25519, derive key ID from public key hash
140
+ keyId = createHash('sha256')
141
+ .update(key.publicKey)
142
+ .digest('hex')
143
+ .substring(0, 16);
144
+ }
145
+ return {
146
+ content,
147
+ signature,
148
+ signer: key.agentName,
149
+ signedAt,
150
+ keyId,
151
+ algorithm: key.algorithm,
152
+ };
153
+ }
154
+ /**
155
+ * Sign with shared secret (HMAC mode where all agents share a secret).
156
+ */
157
+ export function signWithSharedSecret(content, agentName, sharedSecret) {
158
+ const signedAt = Date.now();
159
+ const dataToSign = `${agentName}:${signedAt}:${content}`;
160
+ const signature = createHmac('sha256', sharedSecret)
161
+ .update(dataToSign)
162
+ .digest('hex');
163
+ const keyId = createHash('sha256')
164
+ .update(sharedSecret)
165
+ .digest('hex')
166
+ .substring(0, 16);
167
+ return {
168
+ content,
169
+ signature,
170
+ signer: agentName,
171
+ signedAt,
172
+ keyId,
173
+ algorithm: 'hmac-sha256',
174
+ };
175
+ }
176
+ // =============================================================================
177
+ // Message Verification
178
+ // =============================================================================
179
+ /**
180
+ * Verify a signed message using the agent's public key.
181
+ */
182
+ export function verifyMessage(signed, key) {
183
+ // Check signer matches key
184
+ if (signed.signer !== key.agentName) {
185
+ return {
186
+ valid: false,
187
+ error: `Signer mismatch: expected ${key.agentName}, got ${signed.signer}`,
188
+ };
189
+ }
190
+ // Check key ID for HMAC, or derive it for Ed25519
191
+ const expectedKeyId = key.algorithm === 'hmac-sha256'
192
+ ? key.publicKey
193
+ : createHash('sha256').update(key.publicKey).digest('hex').substring(0, 16);
194
+ if (signed.keyId !== expectedKeyId) {
195
+ return {
196
+ valid: false,
197
+ error: `Key ID mismatch: expected ${expectedKeyId}, got ${signed.keyId}`,
198
+ };
199
+ }
200
+ // Check expiry
201
+ if (key.expiresAt && Date.now() > key.expiresAt) {
202
+ return {
203
+ valid: false,
204
+ error: 'Signing key has expired',
205
+ };
206
+ }
207
+ // Verify signature
208
+ const dataToVerify = `${signed.signer}:${signed.signedAt}:${signed.content}`;
209
+ if (key.algorithm === 'hmac-sha256') {
210
+ // HMAC verification: recompute and compare
211
+ const expectedSignature = createHmac('sha256', key.privateKey)
212
+ .update(dataToVerify)
213
+ .digest('hex');
214
+ if (signed.signature !== expectedSignature) {
215
+ return {
216
+ valid: false,
217
+ error: 'Invalid signature',
218
+ };
219
+ }
220
+ }
221
+ else {
222
+ // Ed25519 verification using public key only (true asymmetric verification)
223
+ try {
224
+ const publicKeyObj = createPublicKey(key.publicKey);
225
+ const signatureBuffer = Buffer.from(signed.signature, 'hex');
226
+ const isValid = verify(null, Buffer.from(dataToVerify), publicKeyObj, signatureBuffer);
227
+ if (!isValid) {
228
+ return {
229
+ valid: false,
230
+ error: 'Invalid signature',
231
+ };
232
+ }
233
+ }
234
+ catch (err) {
235
+ return {
236
+ valid: false,
237
+ error: `Signature verification failed: ${err instanceof Error ? err.message : 'unknown error'}`,
238
+ };
239
+ }
240
+ }
241
+ return {
242
+ valid: true,
243
+ signer: signed.signer,
244
+ signedAt: signed.signedAt,
245
+ };
246
+ }
247
+ /**
248
+ * Verify an Ed25519 signed message using only the public key.
249
+ * This is the key advantage of asymmetric signing - verifiers don't need the private key.
250
+ */
251
+ export function verifyEd25519WithPublicKey(signed, publicKeyPem, expectedSigner) {
252
+ if (signed.algorithm !== 'ed25519') {
253
+ return {
254
+ valid: false,
255
+ error: `Algorithm mismatch: expected ed25519, got ${signed.algorithm}`,
256
+ };
257
+ }
258
+ if (signed.signer !== expectedSigner) {
259
+ return {
260
+ valid: false,
261
+ error: `Signer mismatch: expected ${expectedSigner}, got ${signed.signer}`,
262
+ };
263
+ }
264
+ const expectedKeyId = createHash('sha256')
265
+ .update(publicKeyPem)
266
+ .digest('hex')
267
+ .substring(0, 16);
268
+ if (signed.keyId !== expectedKeyId) {
269
+ return {
270
+ valid: false,
271
+ error: `Key ID mismatch: expected ${expectedKeyId}, got ${signed.keyId}`,
272
+ };
273
+ }
274
+ const dataToVerify = `${signed.signer}:${signed.signedAt}:${signed.content}`;
275
+ try {
276
+ const publicKeyObj = createPublicKey(publicKeyPem);
277
+ const signatureBuffer = Buffer.from(signed.signature, 'hex');
278
+ const isValid = verify(null, Buffer.from(dataToVerify), publicKeyObj, signatureBuffer);
279
+ if (!isValid) {
280
+ return {
281
+ valid: false,
282
+ error: 'Invalid signature',
283
+ };
284
+ }
285
+ }
286
+ catch (err) {
287
+ return {
288
+ valid: false,
289
+ error: `Signature verification failed: ${err instanceof Error ? err.message : 'unknown error'}`,
290
+ };
291
+ }
292
+ return {
293
+ valid: true,
294
+ signer: signed.signer,
295
+ signedAt: signed.signedAt,
296
+ };
297
+ }
298
+ /**
299
+ * Verify with shared secret.
300
+ */
301
+ export function verifyWithSharedSecret(signed, sharedSecret) {
302
+ const dataToSign = `${signed.signer}:${signed.signedAt}:${signed.content}`;
303
+ const expectedSignature = createHmac('sha256', sharedSecret)
304
+ .update(dataToSign)
305
+ .digest('hex');
306
+ if (signed.signature !== expectedSignature) {
307
+ return {
308
+ valid: false,
309
+ error: 'Invalid signature',
310
+ };
311
+ }
312
+ return {
313
+ valid: true,
314
+ signer: signed.signer,
315
+ signedAt: signed.signedAt,
316
+ };
317
+ }
318
+ // =============================================================================
319
+ // Agent Signing Manager
320
+ // =============================================================================
321
+ /**
322
+ * Manages agent signing keys and verification.
323
+ */
324
+ export class AgentSigningManager {
325
+ config;
326
+ keyDir;
327
+ keys = new Map();
328
+ constructor(config = {}, keyDir) {
329
+ this.config = { ...DEFAULT_CONFIG, ...config };
330
+ this.keyDir = keyDir ?? DEFAULT_KEY_DIR;
331
+ }
332
+ /**
333
+ * Check if signing is enabled.
334
+ */
335
+ get enabled() {
336
+ return this.config.enabled;
337
+ }
338
+ /**
339
+ * Get or load key for an agent.
340
+ */
341
+ getKey(agentName) {
342
+ // Check cache
343
+ const cached = this.keys.get(agentName);
344
+ if (cached) {
345
+ // Check expiry
346
+ if (cached.expiresAt && Date.now() > cached.expiresAt) {
347
+ this.keys.delete(agentName);
348
+ }
349
+ else {
350
+ return cached;
351
+ }
352
+ }
353
+ // Load from disk
354
+ const key = loadAgentKey(agentName, this.keyDir);
355
+ if (key) {
356
+ this.keys.set(agentName, key);
357
+ }
358
+ return key;
359
+ }
360
+ /**
361
+ * Register a new agent (generate and save key).
362
+ */
363
+ registerAgent(agentName) {
364
+ const key = getOrCreateAgentKey(agentName, this.config, this.keyDir);
365
+ this.keys.set(agentName, key);
366
+ return key;
367
+ }
368
+ /**
369
+ * Sign a message for an agent.
370
+ */
371
+ sign(agentName, content) {
372
+ if (!this.config.enabled) {
373
+ return null;
374
+ }
375
+ // Shared secret mode
376
+ if (this.config.sharedSecret) {
377
+ return signWithSharedSecret(content, agentName, this.config.sharedSecret);
378
+ }
379
+ // Per-agent key mode
380
+ const key = this.getKey(agentName);
381
+ if (!key) {
382
+ console.warn(`[signing] No key found for ${agentName}, cannot sign`);
383
+ return null;
384
+ }
385
+ return signMessage(content, key);
386
+ }
387
+ /**
388
+ * Verify a signed message.
389
+ */
390
+ verify(signed) {
391
+ if (!this.config.enabled) {
392
+ return { valid: true }; // Signing disabled, accept all
393
+ }
394
+ // Check if unsigned messages are allowed from this agent
395
+ if (this.config.allowUnsignedFrom?.includes(signed.signer)) {
396
+ return { valid: true, signer: signed.signer };
397
+ }
398
+ // Shared secret mode
399
+ if (this.config.sharedSecret) {
400
+ return verifyWithSharedSecret(signed, this.config.sharedSecret);
401
+ }
402
+ // Per-agent key mode
403
+ const key = this.getKey(signed.signer);
404
+ if (!key) {
405
+ if (this.config.requireSignatures) {
406
+ return {
407
+ valid: false,
408
+ error: `No key found for signer ${signed.signer}`,
409
+ };
410
+ }
411
+ // Key not found but signatures not required
412
+ return { valid: true, signer: signed.signer };
413
+ }
414
+ return verifyMessage(signed, key);
415
+ }
416
+ /**
417
+ * Check if a message requires verification.
418
+ */
419
+ requiresVerification(agentName) {
420
+ if (!this.config.enabled)
421
+ return false;
422
+ if (!this.config.requireSignatures)
423
+ return false;
424
+ if (this.config.allowUnsignedFrom?.includes(agentName))
425
+ return false;
426
+ return true;
427
+ }
428
+ /**
429
+ * Rotate key for an agent.
430
+ */
431
+ rotateKey(agentName) {
432
+ // Generate new key
433
+ const newKey = generateAgentKey(agentName, this.config.algorithm, this.config.keyRotationHours);
434
+ // Save and cache
435
+ saveAgentKey(newKey, this.keyDir);
436
+ this.keys.set(agentName, newKey);
437
+ console.log(`[signing] Rotated key for ${agentName}`);
438
+ return newKey;
439
+ }
440
+ /**
441
+ * Export public key for an agent (for sharing with other systems).
442
+ */
443
+ exportPublicKey(agentName) {
444
+ const key = this.getKey(agentName);
445
+ if (!key)
446
+ return null;
447
+ return {
448
+ agentName: key.agentName,
449
+ publicKey: key.publicKey,
450
+ algorithm: key.algorithm,
451
+ };
452
+ }
453
+ }
454
+ // =============================================================================
455
+ // Integration Helpers
456
+ // =============================================================================
457
+ /**
458
+ * Attach signature to protocol envelope.
459
+ */
460
+ export function attachSignature(envelope, signed) {
461
+ return {
462
+ ...envelope,
463
+ _sig: {
464
+ s: signed.signature,
465
+ k: signed.keyId,
466
+ t: signed.signedAt,
467
+ a: signed.algorithm,
468
+ },
469
+ };
470
+ }
471
+ /**
472
+ * Extract signature from protocol envelope.
473
+ */
474
+ export function extractSignature(envelope) {
475
+ const sig = envelope._sig;
476
+ if (!sig || !sig.s || !sig.k || !sig.t) {
477
+ return null;
478
+ }
479
+ // Reconstruct the signed content (envelope without _sig)
480
+ const { _sig, ...rest } = envelope;
481
+ const content = JSON.stringify(rest);
482
+ // Safely extract signer from envelope
483
+ const signer = typeof envelope.from === 'string' ? envelope.from : 'unknown';
484
+ // Validate algorithm value
485
+ const algorithm = sig.a === 'ed25519' ? 'ed25519' : 'hmac-sha256';
486
+ return {
487
+ content,
488
+ signature: sig.s,
489
+ signer,
490
+ signedAt: sig.t,
491
+ keyId: sig.k,
492
+ algorithm,
493
+ };
494
+ }
495
+ // =============================================================================
496
+ // Configuration Loading
497
+ // =============================================================================
498
+ const SIGNING_CONFIG_PATHS = [
499
+ path.join(os.homedir(), '.agent-relay', 'signing.json'),
500
+ path.join(os.homedir(), '.config', 'agent-relay', 'signing.json'),
501
+ '/etc/agent-relay/signing.json',
502
+ ];
503
+ /**
504
+ * Load signing configuration from file.
505
+ */
506
+ export function loadSigningConfig(configPath) {
507
+ const paths = configPath ? [configPath] : SIGNING_CONFIG_PATHS;
508
+ for (const p of paths) {
509
+ if (fs.existsSync(p)) {
510
+ try {
511
+ const content = fs.readFileSync(p, 'utf-8');
512
+ const config = JSON.parse(content);
513
+ console.log(`[signing] Loaded config from ${p}`);
514
+ return { ...DEFAULT_CONFIG, ...config };
515
+ }
516
+ catch (err) {
517
+ console.error(`[signing] Failed to parse ${p}:`, err);
518
+ }
519
+ }
520
+ }
521
+ return DEFAULT_CONFIG;
522
+ }
523
+ //# sourceMappingURL=agent-signing.js.map