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.
- package/.trajectories/active/traj_3yx9dy148mge.json +42 -0
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.json +49 -0
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.md +31 -0
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.json +49 -0
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.md +31 -0
- package/.trajectories/completed/2026-01/traj_6unwwmgyj5sq.json +109 -0
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.json +49 -0
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.md +31 -0
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.json +66 -0
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.md +36 -0
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.json +49 -0
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.md +31 -0
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.json +65 -0
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.md +37 -0
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.json +36 -0
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.md +21 -0
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.json +101 -0
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.md +52 -0
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.json +61 -0
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.md +36 -0
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.json +73 -0
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.md +41 -0
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.json +77 -0
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.md +42 -0
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.json +109 -0
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.md +56 -0
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.json +113 -0
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.md +57 -0
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.json +61 -0
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.md +36 -0
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.json +49 -0
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.md +31 -0
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.json +49 -0
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.md +31 -0
- package/.trajectories/index.json +140 -1
- package/TRAIL_GIT_AUTH_FIX.md +113 -0
- package/deploy/workspace/codex.config.toml +1 -1
- package/deploy/workspace/entrypoint.sh +20 -79
- package/deploy/workspace/gh-relay +156 -0
- package/deploy/workspace/git-credential-relay +5 -1
- package/dist/bridge/multi-project-client.js +13 -10
- package/dist/bridge/spawner.d.ts +2 -0
- package/dist/bridge/spawner.js +19 -1
- package/dist/bridge/types.d.ts +2 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +115 -69
- package/dist/cloud/api/admin.js +16 -3
- package/dist/cloud/api/codex-auth-helper.js +28 -8
- package/dist/cloud/api/consensus.d.ts +13 -0
- package/dist/cloud/api/consensus.js +259 -0
- package/dist/cloud/api/daemons.js +205 -1
- package/dist/cloud/api/git.js +37 -7
- package/dist/cloud/api/onboarding.js +4 -1
- package/dist/cloud/api/provider-env.d.ts +5 -0
- package/dist/cloud/api/provider-env.js +27 -0
- package/dist/cloud/api/providers.js +2 -0
- package/dist/cloud/api/test-helpers.js +130 -0
- package/dist/cloud/api/workspaces.js +38 -3
- package/dist/cloud/db/bulk-ingest.d.ts +88 -0
- package/dist/cloud/db/bulk-ingest.js +268 -0
- package/dist/cloud/db/drizzle.d.ts +33 -0
- package/dist/cloud/db/drizzle.js +174 -2
- package/dist/cloud/db/index.d.ts +24 -5
- package/dist/cloud/db/index.js +19 -4
- package/dist/cloud/db/schema.d.ts +397 -3
- package/dist/cloud/db/schema.js +75 -1
- package/dist/cloud/provisioner/index.d.ts +8 -0
- package/dist/cloud/provisioner/index.js +256 -50
- package/dist/cloud/server.js +47 -3
- package/dist/cloud/services/index.d.ts +1 -0
- package/dist/cloud/services/index.js +2 -0
- package/dist/cloud/services/nango.d.ts +3 -4
- package/dist/cloud/services/nango.js +11 -33
- package/dist/cloud/services/workspace-keepalive.d.ts +76 -0
- package/dist/cloud/services/workspace-keepalive.js +234 -0
- package/dist/config/relay-config.d.ts +23 -0
- package/dist/config/relay-config.js +23 -0
- package/dist/daemon/agent-manager.d.ts +20 -1
- package/dist/daemon/agent-manager.js +47 -0
- package/dist/daemon/agent-registry.js +4 -4
- package/dist/daemon/agent-signing.d.ts +158 -0
- package/dist/daemon/agent-signing.js +523 -0
- package/dist/daemon/api.js +18 -1
- package/dist/daemon/cli-auth.d.ts +4 -1
- package/dist/daemon/cli-auth.js +55 -11
- package/dist/daemon/cloud-sync.d.ts +47 -1
- package/dist/daemon/cloud-sync.js +152 -3
- package/dist/daemon/connection.d.ts +28 -0
- package/dist/daemon/connection.js +98 -15
- package/dist/daemon/consensus-integration.d.ts +167 -0
- package/dist/daemon/consensus-integration.js +371 -0
- package/dist/daemon/consensus.d.ts +271 -0
- package/dist/daemon/consensus.js +632 -0
- package/dist/daemon/delivery-tracker.d.ts +34 -0
- package/dist/daemon/delivery-tracker.js +104 -0
- package/dist/daemon/enhanced-features.d.ts +118 -0
- package/dist/daemon/enhanced-features.js +178 -0
- package/dist/daemon/index.d.ts +4 -0
- package/dist/daemon/index.js +5 -0
- package/dist/daemon/rate-limiter.d.ts +68 -0
- package/dist/daemon/rate-limiter.js +130 -0
- package/dist/daemon/router.d.ts +18 -11
- package/dist/daemon/router.js +55 -111
- package/dist/daemon/server.d.ts +13 -1
- package/dist/daemon/server.js +71 -9
- package/dist/daemon/sync-queue.d.ts +116 -0
- package/dist/daemon/sync-queue.js +361 -0
- package/dist/health-worker-manager.d.ts +62 -0
- package/dist/health-worker-manager.js +144 -0
- package/dist/health-worker.d.ts +9 -0
- package/dist/health-worker.js +79 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -1
- package/dist/memory/context-compaction.d.ts +156 -0
- package/dist/memory/context-compaction.js +453 -0
- package/dist/memory/index.d.ts +1 -0
- package/dist/memory/index.js +1 -0
- package/dist/protocol/channels.js +4 -4
- package/dist/protocol/framing.d.ts +72 -10
- package/dist/protocol/framing.js +194 -25
- package/dist/storage/adapter.d.ts +8 -1
- package/dist/storage/adapter.js +11 -0
- package/dist/storage/batched-sqlite-adapter.d.ts +71 -0
- package/dist/storage/batched-sqlite-adapter.js +183 -0
- package/dist/storage/dead-letter-queue.d.ts +196 -0
- package/dist/storage/dead-letter-queue.js +427 -0
- package/dist/storage/dlq-adapter.d.ts +195 -0
- package/dist/storage/dlq-adapter.js +664 -0
- package/dist/trajectory/config.d.ts +32 -14
- package/dist/trajectory/config.js +38 -16
- package/dist/trajectory/integration.js +217 -64
- package/dist/utils/git-remote.d.ts +47 -0
- package/dist/utils/git-remote.js +125 -0
- package/dist/utils/id-generator.d.ts +35 -0
- package/dist/utils/id-generator.js +60 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/precompiled-patterns.d.ts +110 -0
- package/dist/utils/precompiled-patterns.js +322 -0
- package/dist/wrapper/auth-detection.js +1 -1
- package/dist/wrapper/base-wrapper.d.ts +36 -0
- package/dist/wrapper/base-wrapper.js +48 -2
- package/dist/wrapper/client.d.ts +14 -4
- package/dist/wrapper/client.js +84 -31
- package/dist/wrapper/idle-detector.d.ts +102 -0
- package/dist/wrapper/idle-detector.js +279 -0
- package/dist/wrapper/parser.d.ts +4 -0
- package/dist/wrapper/parser.js +19 -1
- package/dist/wrapper/pty-wrapper.d.ts +7 -1
- package/dist/wrapper/pty-wrapper.js +51 -27
- package/dist/wrapper/tmux-wrapper.d.ts +12 -1
- package/dist/wrapper/tmux-wrapper.js +65 -17
- package/package.json +5 -5
- package/scripts/run-migrations.js +43 -0
- package/scripts/verify-schema.js +134 -0
- package/tests/benchmarks/protocol.bench.ts +310 -0
- package/dist/dashboard/out/404.html +0 -1
- package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_buildManifest.js +0 -1
- package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_ssgManifest.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/116-2502180def231162.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/117-f7b8ab0809342e77.js +0 -2
- package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +0 -9
- package/dist/dashboard/out/_next/static/chunks/648-5cc6e1921389a58a.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/766-b54f0853794b78c3.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/83-b51836037078006c.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/891-6cd50de1224f70bb.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/899-bb19a9b3d9b39ea6.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/_not-found/page-53b8a69f76db17d0.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-8939b0fc700f7eca.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/page-5af1b6b439858aa6.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-f45ecbc3e06134fc.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/history/page-8c8bed33beb2bf1c.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/login/page-16f3b49e55b1e0ed.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-ac39dc0cc3c26fa7.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/page-4a5938c18a11a654.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/pricing/page-982a7000fee44014.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-ac3a6ac433fd6001.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-09f9caae98a18c09.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/signup/page-547dd0ca55ecd0ba.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +0 -18
- package/dist/dashboard/out/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/framework-f66176bb897dc684.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/main-2ee6beb2ae96d210.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/main-app-5d692157a8eb1fd9.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +0 -1
- package/dist/dashboard/out/_next/static/css/85d2af9c7ac74d62.css +0 -1
- package/dist/dashboard/out/_next/static/css/fe4b28883eeff359.css +0 -1
- package/dist/dashboard/out/alt-logos/agent-relay-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo.svg +0 -45
- package/dist/dashboard/out/alt-logos/logo.svg +0 -38
- package/dist/dashboard/out/alt-logos/monogram-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo.svg +0 -38
- package/dist/dashboard/out/app/onboarding.html +0 -1
- package/dist/dashboard/out/app/onboarding.txt +0 -7
- package/dist/dashboard/out/app.html +0 -1
- package/dist/dashboard/out/app.txt +0 -7
- package/dist/dashboard/out/apple-icon.png +0 -0
- package/dist/dashboard/out/connect-repos.html +0 -1
- package/dist/dashboard/out/connect-repos.txt +0 -7
- package/dist/dashboard/out/history.html +0 -1
- package/dist/dashboard/out/history.txt +0 -7
- package/dist/dashboard/out/index.html +0 -1
- package/dist/dashboard/out/index.txt +0 -7
- package/dist/dashboard/out/login.html +0 -6
- package/dist/dashboard/out/login.txt +0 -7
- package/dist/dashboard/out/metrics.html +0 -1
- package/dist/dashboard/out/metrics.txt +0 -7
- package/dist/dashboard/out/pricing.html +0 -13
- package/dist/dashboard/out/pricing.txt +0 -7
- package/dist/dashboard/out/providers/setup/claude.html +0 -1
- package/dist/dashboard/out/providers/setup/claude.txt +0 -8
- package/dist/dashboard/out/providers/setup/codex.html +0 -1
- package/dist/dashboard/out/providers/setup/codex.txt +0 -8
- package/dist/dashboard/out/providers.html +0 -1
- package/dist/dashboard/out/providers.txt +0 -7
- package/dist/dashboard/out/signup.html +0 -6
- package/dist/dashboard/out/signup.txt +0 -7
- package/dist/dashboard-server/metrics.d.ts +0 -105
- package/dist/dashboard-server/metrics.js +0 -193
- package/dist/dashboard-server/needs-attention.d.ts +0 -24
- package/dist/dashboard-server/needs-attention.js +0 -78
- package/dist/dashboard-server/server.d.ts +0 -15
- package/dist/dashboard-server/server.js +0 -3776
- package/dist/dashboard-server/start.d.ts +0 -6
- package/dist/dashboard-server/start.js +0 -13
- package/dist/dashboard-server/user-bridge.d.ts +0 -103
- 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
|