@agenticprimitives/key-custody 0.1.0-alpha.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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/dist/aad.d.ts +2 -0
  4. package/dist/aad.d.ts.map +1 -0
  5. package/dist/aad.js +19 -0
  6. package/dist/aad.js.map +1 -0
  7. package/dist/account.d.ts +23 -0
  8. package/dist/account.d.ts.map +1 -0
  9. package/dist/account.js +54 -0
  10. package/dist/account.js.map +1 -0
  11. package/dist/derive-subject.d.ts +38 -0
  12. package/dist/derive-subject.d.ts.map +1 -0
  13. package/dist/derive-subject.js +137 -0
  14. package/dist/derive-subject.js.map +1 -0
  15. package/dist/factories.d.ts +30 -0
  16. package/dist/factories.d.ts.map +1 -0
  17. package/dist/factories.js +149 -0
  18. package/dist/factories.js.map +1 -0
  19. package/dist/index.d.ts +13 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +17 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/kms-viem-account.d.ts +4 -0
  24. package/dist/kms-viem-account.d.ts.map +1 -0
  25. package/dist/kms-viem-account.js +72 -0
  26. package/dist/kms-viem-account.js.map +1 -0
  27. package/dist/providers/aws.d.ts +13 -0
  28. package/dist/providers/aws.d.ts.map +1 -0
  29. package/dist/providers/aws.js +14 -0
  30. package/dist/providers/aws.js.map +1 -0
  31. package/dist/providers/gcp.d.ts +103 -0
  32. package/dist/providers/gcp.d.ts.map +1 -0
  33. package/dist/providers/gcp.js +490 -0
  34. package/dist/providers/gcp.js.map +1 -0
  35. package/dist/providers/local.d.ts +60 -0
  36. package/dist/providers/local.d.ts.map +1 -0
  37. package/dist/providers/local.js +246 -0
  38. package/dist/providers/local.js.map +1 -0
  39. package/dist/relay-only.d.ts +3 -0
  40. package/dist/relay-only.d.ts.map +1 -0
  41. package/dist/relay-only.js +19 -0
  42. package/dist/relay-only.js.map +1 -0
  43. package/dist/types.d.ts +134 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +70 -0
  46. package/dist/types.js.map +1 -0
  47. package/package.json +84 -0
  48. package/spec.md +6 -0
@@ -0,0 +1,246 @@
1
+ // LocalAesProvider / LocalSecp256k1Signer — DEV ONLY backends.
2
+ //
3
+ // Refuses to instantiate when NODE_ENV=production. Production guard per spec.
4
+ //
5
+ // LocalAesProvider:
6
+ // - "Wraps" data keys via HKDF derivation. encryptedDataKey is a random salt;
7
+ // the data key is HKDF(masterSecret, salt, infoBytes(aadContext)).
8
+ // - To "decrypt", re-derives from masterSecret + salt + AAD. Tampering with
9
+ // AAD changes the derived key, which surfaces as a tag mismatch when the
10
+ // caller AES-GCM-decrypts the payload.
11
+ //
12
+ // LocalSecp256k1Signer:
13
+ // - Holds a hardcoded private key from env (A2A_MASTER_PRIVATE_KEY).
14
+ // - secp256k1 signs a 32-byte digest; returns 65-byte (r,s,v) signature.
15
+ import { hmac } from '@noble/hashes/hmac';
16
+ import { sha256 } from '@noble/hashes/sha256';
17
+ import { hkdf } from '@noble/hashes/hkdf';
18
+ import { secp256k1 } from '@noble/curves/secp256k1';
19
+ import { keccak_256 } from '@noble/hashes/sha3';
20
+ import { hexToBytes, bytesToHex, toHex } from 'viem';
21
+ import { canonicalContextBytes } from '../aad';
22
+ import { buildEvent } from '@agenticprimitives/audit';
23
+ const HKDF_INFO = 'agenticprimitives/local-aes:v1';
24
+ const SALT_BYTES = 16;
25
+ /**
26
+ * Fail-closed default: refuse to start when NODE_ENV=production, but let a
27
+ * deliberate deployment opt into a local in-memory primitive with an explicit
28
+ * acknowledgement env var. Use case: demo / staging stacks that want to
29
+ * run the full A2A flow without standing up a real KMS first.
30
+ *
31
+ * Each override emits a loud one-time warning at boot. Production preflight
32
+ * (`scripts/check-production-deploy.ts`) is the load-bearing check that
33
+ * these opt-ins do not coexist with real-value keys.
34
+ *
35
+ * Two opt-ins exist because the threat models differ:
36
+ * - `A2A_ALLOW_LOCAL_MASTER_KEY` — in-memory secp256k1 signer (relayer,
37
+ * bundler, paymaster envelope). Compromise = forge bundler txs.
38
+ * - `A2A_ALLOW_LOCAL_ENVELOPE_KEY` — HKDF-derived AES-GCM session-data-key
39
+ * wrap. Compromise = decrypt session keypairs at rest = forge delegations.
40
+ */
41
+ const warnedOptIns = new Set();
42
+ /**
43
+ * H7-B.9 / XPKG-005 — production guard. Previously keyed off
44
+ * `process.env.NODE_ENV !== 'production'` to SKIP — which silently
45
+ * allowed local providers on runtimes that don't set NODE_ENV
46
+ * (Cloudflare Workers, Deno, SES). Fix: inferred environment defaults
47
+ * to `'production'` when NODE_ENV is unreadable or missing, matching
48
+ * `inferEnvironment` in `factories.ts` / sibling-package inferEnvironment functions.
49
+ */
50
+ function isProductionEnvironment() {
51
+ try {
52
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV) {
53
+ return process.env.NODE_ENV === 'production';
54
+ }
55
+ }
56
+ catch {
57
+ /* SES / Workers may throw on process access */
58
+ }
59
+ // Ambiguous runtime → assume production (fail-closed).
60
+ return true;
61
+ }
62
+ function assertLocalProviderAllowedInProduction(label, optInEnvVar) {
63
+ if (!isProductionEnvironment())
64
+ return;
65
+ let optIn = false;
66
+ try {
67
+ optIn = process.env?.[optInEnvVar] === 'true';
68
+ }
69
+ catch {
70
+ /* SES / Workers — opt-in not readable, treat as not-set */
71
+ }
72
+ if (optIn) {
73
+ if (!warnedOptIns.has(optInEnvVar)) {
74
+ warnedOptIns.add(optInEnvVar);
75
+ // eslint-disable-next-line no-console
76
+ console.warn(`[key-custody] ${label}: running in production via ${optInEnvVar}=true. ` +
77
+ `A managed KMS backend (gcp-kms / aws-kms) MUST replace this before any real-value keys land.`);
78
+ }
79
+ return;
80
+ }
81
+ throw new Error(`${label} refuses to start in production (or in a runtime where NODE_ENV is unset). ` +
82
+ `Configure a managed KMS backend (gcp-kms / aws-kms), or set ` +
83
+ `${optInEnvVar}=true to acknowledge running with local key material (demo / staging only).`);
84
+ }
85
+ function loadMasterSecret(envOverride) {
86
+ const hex = envOverride ?? process.env.A2A_SESSION_SECRET;
87
+ if (!hex) {
88
+ throw new Error('LocalAesProvider: A2A_SESSION_SECRET (hex) is required. Generate one for dev: openssl rand -hex 32');
89
+ }
90
+ const bytes = hexToBytes(hex.startsWith('0x') ? hex : `0x${hex}`);
91
+ if (bytes.length < 32) {
92
+ throw new Error(`A2A_SESSION_SECRET must be at least 32 bytes (64 hex chars); got ${bytes.length}.`);
93
+ }
94
+ return bytes;
95
+ }
96
+ function loadPrivateKey(envOverride) {
97
+ const hex = envOverride ?? process.env.A2A_MASTER_PRIVATE_KEY;
98
+ if (!hex) {
99
+ throw new Error('LocalSecp256k1Signer: A2A_MASTER_PRIVATE_KEY (0x-prefixed hex) is required.');
100
+ }
101
+ const cleaned = hex.startsWith('0x') ? hex : `0x${hex}`;
102
+ const bytes = hexToBytes(cleaned);
103
+ if (bytes.length !== 32) {
104
+ throw new Error(`A2A_MASTER_PRIVATE_KEY must be 32 bytes; got ${bytes.length}.`);
105
+ }
106
+ return bytes;
107
+ }
108
+ function randomBytes(n) {
109
+ const out = new Uint8Array(n);
110
+ // Node 20+ and modern browsers both expose globalThis.crypto.getRandomValues
111
+ globalThis.crypto.getRandomValues(out);
112
+ return out;
113
+ }
114
+ function publicKeyToAddress(pubKey) {
115
+ // pubKey: 64-byte uncompressed (no 0x04 prefix)
116
+ const raw = pubKey.length === 65 ? pubKey.slice(1) : pubKey;
117
+ const hash = keccak_256(raw);
118
+ // viem's bytesToHex already prefixes with 0x — don't double it.
119
+ return bytesToHex(hash.slice(12));
120
+ }
121
+ export class LocalAesProvider {
122
+ keyVersion = 'local-v1';
123
+ master;
124
+ constructor(opts) {
125
+ // Production guard moved from the constructor to the
126
+ // envelope-encryption methods (generateSessionDataKey /
127
+ // decryptSessionDataKey). Rationale: the production posture
128
+ // requires a real KMS for SESSION DATA KEY material (encrypt /
129
+ // decrypt of session payloads) — HKDF-from-a-local-secret is the
130
+ // dev-only path that gets refused in prod. The MAC primitive
131
+ // (generateMac, below) is just HMAC-SHA256 over a wrangler-secret-
132
+ // loaded value, which is a legitimate production pattern for
133
+ // service-to-service auth (audit C1). Splitting the guard lets
134
+ // generateMac work in production without weakening the encryption
135
+ // posture.
136
+ this.master = loadMasterSecret(opts?.sessionSecretHex);
137
+ }
138
+ async generateSessionDataKey(input) {
139
+ assertLocalProviderAllowedInProduction('LocalAesProvider.generateSessionDataKey', 'A2A_ALLOW_LOCAL_ENVELOPE_KEY');
140
+ const salt = randomBytes(SALT_BYTES);
141
+ const info = canonicalContextBytes(input.aadContext);
142
+ // Derive 32-byte data key. encryptedDataKey == salt (the master is held in process memory).
143
+ const dk = hkdf(sha256, this.master, salt, new Uint8Array([...new TextEncoder().encode(HKDF_INFO + '|'), ...info]), 32);
144
+ return {
145
+ plaintextDataKey: dk,
146
+ encryptedDataKey: salt,
147
+ keyId: 'local-master',
148
+ keyVersion: this.keyVersion,
149
+ };
150
+ }
151
+ async decryptSessionDataKey(input) {
152
+ assertLocalProviderAllowedInProduction('LocalAesProvider.decryptSessionDataKey', 'A2A_ALLOW_LOCAL_ENVELOPE_KEY');
153
+ if (input.keyVersion !== this.keyVersion) {
154
+ throw new Error(`LocalAesProvider: keyVersion mismatch (got "${input.keyVersion}", expected "${this.keyVersion}").`);
155
+ }
156
+ const info = canonicalContextBytes(input.aadContext);
157
+ const dk = hkdf(sha256, this.master, input.encryptedDataKey, new Uint8Array([...new TextEncoder().encode(HKDF_INFO + '|'), ...info]), 32);
158
+ return dk;
159
+ }
160
+ async generateMac(input) {
161
+ // Permitted in production: this is HMAC-SHA256 with a wrangler-
162
+ // secret-loaded shared key, which is a legitimate production
163
+ // pattern for service-to-service auth. Production should migrate
164
+ // to a managed KMS HMAC key for key rotation + IAM scoping, but
165
+ // the LocalAesProvider MAC path is structurally safe.
166
+ const ctx = `mac|${input.service}|${input.audience}`;
167
+ const subkey = hmac(sha256, this.master, new TextEncoder().encode(ctx));
168
+ const mac = hmac(sha256, subkey, input.canonicalMessage);
169
+ return { mac, keyId: `local-mac:${input.service}:${input.audience}` };
170
+ }
171
+ }
172
+ export class LocalSecp256k1Signer {
173
+ provider = 'local-aes';
174
+ priv;
175
+ addr;
176
+ auditSink;
177
+ constructor(opts) {
178
+ assertLocalProviderAllowedInProduction('LocalSecp256k1Signer', 'A2A_ALLOW_LOCAL_MASTER_KEY');
179
+ this.priv = loadPrivateKey(opts?.privateKeyHex);
180
+ const pub = secp256k1.getPublicKey(this.priv, false); // uncompressed (65 bytes)
181
+ this.addr = publicKeyToAddress(pub);
182
+ this.auditSink = opts?.auditSink;
183
+ }
184
+ async signA2AAction(input) {
185
+ if (input.digest.length !== 32) {
186
+ throw new Error(`signA2AAction expects a 32-byte digest; got ${input.digest.length}.`);
187
+ }
188
+ const sig = secp256k1.sign(input.digest, this.priv);
189
+ // viem-compatible signature: r (32) | s (32) | v (1, 27 or 28)
190
+ const out = new Uint8Array(65);
191
+ out.set(numberTo32Bytes(sig.r), 0);
192
+ out.set(numberTo32Bytes(sig.s), 32);
193
+ out[64] = (sig.recovery ?? 0) + 27;
194
+ // Audit emit (C3 pass 3c). Per CLAUDE.md security invariant:
195
+ // "Every Decrypt and signing op emits an audit row with
196
+ // keyVersion, hashed sessionId, optional toolId/actionId.
197
+ // Raw sessionId MUST NEVER be logged."
198
+ if (this.auditSink) {
199
+ const ctx = input.auditContext ?? {};
200
+ try {
201
+ await this.auditSink.write(buildEvent({
202
+ action: 'key-custody.sign',
203
+ outcome: 'success',
204
+ actor: { type: 'system', id: 'local-secp256k1-signer' },
205
+ subject: { type: 'sign-digest', id: bytesToHex(input.digest) },
206
+ context: {
207
+ keyId: 'local-master-secp256k1',
208
+ signerAddress: this.addr,
209
+ toolId: ctx.toolId ?? null,
210
+ actionId: ctx.actionId ?? null,
211
+ // Hash sessionId — never log raw.
212
+ sessionHash: ctx.sessionId
213
+ ? toHex(keccak_256(new TextEncoder().encode(ctx.sessionId))).slice(0, 18)
214
+ : null,
215
+ },
216
+ }));
217
+ }
218
+ catch {
219
+ /* fail-soft */
220
+ }
221
+ }
222
+ return {
223
+ signature: out,
224
+ keyId: 'local-master-secp256k1',
225
+ signerAddress: this.addr,
226
+ };
227
+ }
228
+ async getSignerAddress() {
229
+ return this.addr;
230
+ }
231
+ /** Internal: returns the address as a hex string for use by adapters. */
232
+ addressHex() {
233
+ return this.addr;
234
+ }
235
+ }
236
+ function numberTo32Bytes(n) {
237
+ const out = new Uint8Array(32);
238
+ let value = n;
239
+ for (let i = 31; i >= 0; i--) {
240
+ out[i] = Number(value & 0xffn);
241
+ value >>= 8n;
242
+ }
243
+ return out;
244
+ }
245
+ export { hexToBytes, bytesToHex, toHex };
246
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/providers/local.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { BuildOpts, KmsAccountBackend } from './types';
2
+ export declare function getRelayOnlySigner(opts: BuildOpts): KmsAccountBackend;
3
+ //# sourceMappingURL=relay-only.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-only.d.ts","sourceRoot":"","sources":["../src/relay-only.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAI5D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,GAAG,iBAAiB,CAerE"}
@@ -0,0 +1,19 @@
1
+ // getRelayOnlySigner — wraps a KmsAccountBackend so any sign call throws.
2
+ // Used in Phase B to guarantee the master signer can only broadcast, never
3
+ // sign on the user's authority.
4
+ import { buildSignerBackend } from './factories';
5
+ export function getRelayOnlySigner(opts) {
6
+ const inner = buildSignerBackend(opts);
7
+ return {
8
+ // Report the wrapped backend's real kind (audit F-6) — the relay-only
9
+ // shim changes capability, not provenance.
10
+ provider: inner.provider,
11
+ async getSignerAddress() {
12
+ return inner.getSignerAddress();
13
+ },
14
+ async signA2AAction() {
15
+ throw new Error('getRelayOnlySigner: signA2AAction called on relay-only signer. The master key is restricted to broadcast operations only.');
16
+ },
17
+ };
18
+ }
19
+ //# sourceMappingURL=relay-only.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-only.js","sourceRoot":"","sources":["../src/relay-only.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,2EAA2E;AAC3E,gCAAgC;AAGhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,MAAM,UAAU,kBAAkB,CAAC,IAAe;IAChD,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO;QACL,sEAAsE;QACtE,2CAA2C;QAC3C,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,CAAC,gBAAgB;YACpB,OAAO,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAClC,CAAC;QACD,KAAK,CAAC,aAAa;YACjB,MAAM,IAAI,KAAK,CACb,2HAA2H,CAC5H,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,134 @@
1
+ import type { Address } from '@agenticprimitives/types';
2
+ import type { AuditSink } from '@agenticprimitives/audit';
3
+ export type KmsBackend = 'local-aes' | 'aws-kms' | 'gcp-kms';
4
+ /**
5
+ * H7-F.5 / PKG-KEY-CUSTODY-005 closure — opaque branded type for
6
+ * sensitive config values (raw private keys, KMS service-account JSON,
7
+ * session secrets, derivation masters, etc.).
8
+ *
9
+ * Previously `BuildOpts.config: Record<string, string>` shipped raw
10
+ * private keys + KMS service-account JSON as plain strings. A consumer
11
+ * who logged `opts.config` for debugging silently dumped the master
12
+ * private key to logs (and worse, to any structured-log backend that
13
+ * indexed it).
14
+ *
15
+ * `Secret<T>` is a `BrandedSecret` wrapper that:
16
+ * - Cannot be `JSON.stringify`d to its underlying value (the brand
17
+ * wins; the value field is non-enumerable + has a custom toJSON
18
+ * that returns `'[redacted secret]'`).
19
+ * - Cannot be `console.log`'d in a useful way (custom `inspect`
20
+ * symbol + `Symbol.toPrimitive` return the redaction marker).
21
+ * - Exposes the underlying value ONLY through {@link unwrapSecret}.
22
+ *
23
+ * Loaders (`loadSecret`, `loadSecretFromEnv`) are the only constructors.
24
+ * Existing `Record<string, string>` config still works (back-compat);
25
+ * new code should use the branded shape via `secretConfig`.
26
+ */
27
+ declare const SECRET_BRAND: unique symbol;
28
+ export interface Secret<T extends string = string> {
29
+ readonly [SECRET_BRAND]: true;
30
+ /** Phantom — for compile-time discrimination of value shapes. */
31
+ readonly _kind?: T;
32
+ }
33
+ /**
34
+ * Wrap a plain string as an opaque secret. The returned object will
35
+ * NOT survive `JSON.stringify`, `console.log`, or `util.inspect`.
36
+ */
37
+ export declare function loadSecret<T extends string = string>(value: string): Secret<T>;
38
+ /** Load a secret from a process env var. Throws if the var is missing or empty. */
39
+ export declare function loadSecretFromEnv<T extends string = string>(name: string): Secret<T>;
40
+ /** Unwrap a secret to its underlying string. Use AT THE LAST POSSIBLE MOMENT. */
41
+ export declare function unwrapSecret<T extends string>(s: Secret<T>): string;
42
+ /** Type guard. */
43
+ export declare function isSecret<T extends string = string>(v: unknown): v is Secret<T>;
44
+ export interface BuildOpts {
45
+ /**
46
+ * Backend selection. Recommended explicit value. When omitted, the
47
+ * factory falls back to `A2A_KMS_BACKEND` env, then to `local-aes` in
48
+ * development. In production with neither set, the factory THROWS at
49
+ * construction time (audit H1: no silent local-aes default).
50
+ */
51
+ backend?: KmsBackend;
52
+ /**
53
+ * Plain config bag (back-compat). H7-F.5 callers should prefer
54
+ * {@link secretConfig} for any value that's a private key, session
55
+ * secret, KMS service-account JSON, or derivation master.
56
+ */
57
+ config?: Record<string, string>;
58
+ /**
59
+ * H7-F.5 / PKG-KEY-CUSTODY-005 — sensitive config values wrapped in
60
+ * `Secret<T>` so they don't survive logging / JSON.stringify.
61
+ * Factories that need to unwrap call {@link unwrapSecret} at the
62
+ * latest possible moment + never store the unwrapped value.
63
+ */
64
+ secretConfig?: Record<string, Secret<string>>;
65
+ /**
66
+ * Optional audit sink threaded into signers so every signing op emits
67
+ * `key-custody.sign`. Consumers share one sink across all primitives
68
+ * so rows land in one trail. Fail-soft if the sink throws.
69
+ */
70
+ auditSink?: AuditSink;
71
+ /**
72
+ * Production-readiness gate (audit H1). Inverted default: factories
73
+ * treat the runtime as `'production'` unless either:
74
+ * - `developmentMode: true` is set explicitly, or
75
+ * - `process.env.NODE_ENV !== 'production'`.
76
+ * In production with no explicit backend AND no `A2A_KMS_BACKEND`
77
+ * env, the factory throws. Pass `environment: 'production'` to force
78
+ * production semantics in tests; pass `'development'` (or
79
+ * `developmentMode: true`) to opt into the dev fallback.
80
+ */
81
+ environment?: 'production' | 'development';
82
+ /** Shorthand for `environment: 'development'`. */
83
+ developmentMode?: boolean;
84
+ }
85
+ export interface A2AKeyProvider {
86
+ readonly keyVersion: string;
87
+ generateSessionDataKey(input: {
88
+ aadContext: Record<string, string>;
89
+ }): Promise<{
90
+ plaintextDataKey: Uint8Array;
91
+ encryptedDataKey: Uint8Array;
92
+ keyId: string;
93
+ keyVersion: string;
94
+ }>;
95
+ decryptSessionDataKey(input: {
96
+ encryptedDataKey: Uint8Array;
97
+ aadContext: Record<string, string>;
98
+ keyId: string;
99
+ keyVersion: string;
100
+ }): Promise<Uint8Array>;
101
+ signA2AAction?(input: {
102
+ digest: Uint8Array;
103
+ auditContext?: {
104
+ toolId?: string;
105
+ sessionId?: string;
106
+ actionId?: string;
107
+ };
108
+ }): Promise<{
109
+ signature: Uint8Array;
110
+ keyId: string;
111
+ signerAddress: Address;
112
+ }>;
113
+ generateMac?(input: {
114
+ canonicalMessage: Uint8Array;
115
+ service: string;
116
+ audience: string;
117
+ }): Promise<{
118
+ mac: Uint8Array;
119
+ keyId: string;
120
+ }>;
121
+ }
122
+ export interface KmsAccountBackend {
123
+ /**
124
+ * The concrete backend kind. `createKmsAccount` reads this so the
125
+ * emitted `provider` / `keyId` reflect the REAL backend (audit
126
+ * provenance), instead of a defaulted `'local-aes'` label that could
127
+ * mislabel a production GCP signer (audit F-6).
128
+ */
129
+ readonly provider: 'local-aes' | 'aws-kms' | 'gcp-kms';
130
+ signA2AAction: NonNullable<A2AKeyProvider['signA2AAction']>;
131
+ getSignerAddress(): Promise<Address>;
132
+ }
133
+ export {};
134
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * H7-F.5 / PKG-KEY-CUSTODY-005 closure — opaque branded type for
3
+ * sensitive config values (raw private keys, KMS service-account JSON,
4
+ * session secrets, derivation masters, etc.).
5
+ *
6
+ * Previously `BuildOpts.config: Record<string, string>` shipped raw
7
+ * private keys + KMS service-account JSON as plain strings. A consumer
8
+ * who logged `opts.config` for debugging silently dumped the master
9
+ * private key to logs (and worse, to any structured-log backend that
10
+ * indexed it).
11
+ *
12
+ * `Secret<T>` is a `BrandedSecret` wrapper that:
13
+ * - Cannot be `JSON.stringify`d to its underlying value (the brand
14
+ * wins; the value field is non-enumerable + has a custom toJSON
15
+ * that returns `'[redacted secret]'`).
16
+ * - Cannot be `console.log`'d in a useful way (custom `inspect`
17
+ * symbol + `Symbol.toPrimitive` return the redaction marker).
18
+ * - Exposes the underlying value ONLY through {@link unwrapSecret}.
19
+ *
20
+ * Loaders (`loadSecret`, `loadSecretFromEnv`) are the only constructors.
21
+ * Existing `Record<string, string>` config still works (back-compat);
22
+ * new code should use the branded shape via `secretConfig`.
23
+ */
24
+ const SECRET_BRAND = Symbol.for('agenticprimitives.secret');
25
+ const REDACTED = '[redacted secret]';
26
+ /**
27
+ * Wrap a plain string as an opaque secret. The returned object will
28
+ * NOT survive `JSON.stringify`, `console.log`, or `util.inspect`.
29
+ */
30
+ export function loadSecret(value) {
31
+ const inner = {
32
+ [SECRET_BRAND]: true,
33
+ __value: value,
34
+ toJSON: () => REDACTED,
35
+ toString: () => REDACTED,
36
+ [Symbol.toPrimitive]: () => REDACTED,
37
+ };
38
+ // Hide `__value` from enumeration so naive iteration (Object.keys,
39
+ // spread, JSON.stringify) cannot reach it.
40
+ Object.defineProperty(inner, '__value', { enumerable: false, writable: false });
41
+ // Node's util.inspect honors this symbol.
42
+ Object.defineProperty(inner, Symbol.for('nodejs.util.inspect.custom'), {
43
+ enumerable: false,
44
+ value: () => REDACTED,
45
+ });
46
+ return inner;
47
+ }
48
+ /** Load a secret from a process env var. Throws if the var is missing or empty. */
49
+ export function loadSecretFromEnv(name) {
50
+ let value;
51
+ try {
52
+ value = process.env?.[name];
53
+ }
54
+ catch {
55
+ /* SES / Workers may throw on process access */
56
+ }
57
+ if (typeof value !== 'string' || value.length === 0) {
58
+ throw new Error(`[key-custody] loadSecretFromEnv: env var ${name} is missing or empty`);
59
+ }
60
+ return loadSecret(value);
61
+ }
62
+ /** Unwrap a secret to its underlying string. Use AT THE LAST POSSIBLE MOMENT. */
63
+ export function unwrapSecret(s) {
64
+ return s.__value;
65
+ }
66
+ /** Type guard. */
67
+ export function isSecret(v) {
68
+ return typeof v === 'object' && v !== null && v[SECRET_BRAND] === true;
69
+ }
70
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,YAAY,GAAkB,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;AAY3E,MAAM,QAAQ,GAAG,mBAA4B,CAAC;AAE9C;;;GAGG;AACH,MAAM,UAAU,UAAU,CAA4B,KAAa;IACjE,MAAM,KAAK,GAAsB;QAC/B,CAAC,YAAY,CAAC,EAAE,IAAI;QACpB,OAAO,EAAE,KAAK;QACd,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ;QACtB,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ;QACxB,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,CAAC,QAAQ;KAKrC,CAAC;IACF,mEAAmE;IACnE,2CAA2C;IAC3C,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAChF,0CAA0C;IAC1C,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,EAAE;QACrE,UAAU,EAAE,KAAK;QACjB,KAAK,EAAE,GAAG,EAAE,CAAC,QAAQ;KACtB,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,iBAAiB,CAA4B,IAAY;IACvE,IAAI,KAAyB,CAAC;IAC9B,IAAI,CAAC;QACH,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,+CAA+C;IACjD,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,4CAA4C,IAAI,sBAAsB,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,UAAU,CAAI,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAAmB,CAAY;IACzD,OAAQ,CAAuB,CAAC,OAAO,CAAC;AAC1C,CAAC;AAED,kBAAkB;AAClB,MAAM,UAAU,QAAQ,CAA4B,CAAU;IAC5D,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAK,CAAkC,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;AAC3G,CAAC"}
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@agenticprimitives/key-custody",
3
+ "version": "0.1.0-alpha.2",
4
+ "description": "Pluggable envelope encryption + signers + HMAC providers (local-AES / AWS KMS / GCP KMS). Pure crypto primitives; no session lifecycle (that's in delegation).",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/agentictrustlabs/agenticprimitives.git",
9
+ "directory": "packages/key-custody"
10
+ },
11
+ "homepage": "https://github.com/agentictrustlabs/agenticprimitives/tree/master/packages/key-custody",
12
+ "bugs": {
13
+ "url": "https://github.com/agentictrustlabs/agenticprimitives/issues"
14
+ },
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js"
22
+ },
23
+ "./local": {
24
+ "types": "./dist/providers/local.d.ts",
25
+ "import": "./dist/providers/local.js"
26
+ },
27
+ "./aws": {
28
+ "types": "./dist/providers/aws.d.ts",
29
+ "import": "./dist/providers/aws.js"
30
+ },
31
+ "./gcp": {
32
+ "types": "./dist/providers/gcp.d.ts",
33
+ "import": "./dist/providers/gcp.js"
34
+ },
35
+ "./mac": {
36
+ "types": "./dist/mac.d.ts",
37
+ "import": "./dist/mac.js"
38
+ },
39
+ "./kms-viem": {
40
+ "types": "./dist/kms-viem-account.d.ts",
41
+ "import": "./dist/kms-viem-account.js"
42
+ }
43
+ },
44
+ "files": [
45
+ "LICENSE",
46
+ "dist",
47
+ "spec.md",
48
+ "README.md"
49
+ ],
50
+ "scripts": {
51
+ "build": "tsc -p tsconfig.build.json",
52
+ "typecheck": "tsc -p tsconfig.json --noEmit",
53
+ "test": "vitest run",
54
+ "test:unit": "vitest run test/unit",
55
+ "test:integration": "vitest run test/integration --passWithNoTests",
56
+ "test:watch": "vitest",
57
+ "clean": "rm -rf dist"
58
+ },
59
+ "publishConfig": {
60
+ "access": "public"
61
+ },
62
+ "dependencies": {
63
+ "@noble/curves": "^1.6.0",
64
+ "@noble/hashes": "^1.5.0"
65
+ },
66
+ "peerDependencies": {
67
+ "@agenticprimitives/audit": "workspace:*",
68
+ "@agenticprimitives/connect-auth": "workspace:*",
69
+ "@agenticprimitives/types": "workspace:*",
70
+ "viem": "^2.50.0"
71
+ },
72
+ "devDependencies": {
73
+ "vitest": "^2.1.0"
74
+ },
75
+ "keywords": [
76
+ "kms",
77
+ "aws-kms",
78
+ "gcp-kms",
79
+ "envelope-encryption",
80
+ "secp256k1",
81
+ "hmac",
82
+ "agentic"
83
+ ]
84
+ }
package/spec.md ADDED
@@ -0,0 +1,6 @@
1
+ # @agenticprimitives/key-custody — spec
2
+
3
+ The authoritative specification lives at:
4
+ **[`../../specs/203-key-custody.md`](../../specs/203-key-custody.md)**
5
+
6
+ Do not edit a divergent copy here.