@agirails/sdk 4.4.8 → 4.5.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 (99) hide show
  1. package/dist/builders/DeliveryProofBuilder.d.ts +224 -13
  2. package/dist/builders/DeliveryProofBuilder.d.ts.map +1 -1
  3. package/dist/builders/DeliveryProofBuilder.js +247 -13
  4. package/dist/builders/DeliveryProofBuilder.js.map +1 -1
  5. package/dist/cli/agirails.d.ts +85 -1
  6. package/dist/cli/agirails.d.ts.map +1 -1
  7. package/dist/cli/agirails.js +429 -154
  8. package/dist/cli/agirails.js.map +1 -1
  9. package/dist/cli/commands/init.d.ts +54 -0
  10. package/dist/cli/commands/init.d.ts.map +1 -1
  11. package/dist/cli/commands/init.js +193 -1
  12. package/dist/cli/commands/init.js.map +1 -1
  13. package/dist/cli/commands/receipt.d.ts +70 -2
  14. package/dist/cli/commands/receipt.d.ts.map +1 -1
  15. package/dist/cli/commands/receipt.js +218 -3
  16. package/dist/cli/commands/receipt.js.map +1 -1
  17. package/dist/cli/commands/test.d.ts +77 -1
  18. package/dist/cli/commands/test.d.ts.map +1 -1
  19. package/dist/cli/commands/test.js +264 -2
  20. package/dist/cli/commands/test.js.map +1 -1
  21. package/dist/cli/lib/runRequest.d.ts +90 -0
  22. package/dist/cli/lib/runRequest.d.ts.map +1 -1
  23. package/dist/cli/lib/runRequest.js +300 -9
  24. package/dist/cli/lib/runRequest.js.map +1 -1
  25. package/dist/cli/lib/sentinelReflections.d.ts +111 -0
  26. package/dist/cli/lib/sentinelReflections.d.ts.map +1 -0
  27. package/dist/cli/lib/sentinelReflections.js +193 -0
  28. package/dist/cli/lib/sentinelReflections.js.map +1 -0
  29. package/dist/delivery/MockDeliveryChannel.d.ts +208 -0
  30. package/dist/delivery/MockDeliveryChannel.d.ts.map +1 -0
  31. package/dist/delivery/MockDeliveryChannel.js +445 -0
  32. package/dist/delivery/MockDeliveryChannel.js.map +1 -0
  33. package/dist/delivery/RelayDeliveryChannel.d.ts +176 -0
  34. package/dist/delivery/RelayDeliveryChannel.d.ts.map +1 -0
  35. package/dist/delivery/RelayDeliveryChannel.js +377 -0
  36. package/dist/delivery/RelayDeliveryChannel.js.map +1 -0
  37. package/dist/delivery/channel.d.ts +282 -0
  38. package/dist/delivery/channel.d.ts.map +1 -0
  39. package/dist/delivery/channel.js +76 -0
  40. package/dist/delivery/channel.js.map +1 -0
  41. package/dist/delivery/channelLog.d.ts +115 -0
  42. package/dist/delivery/channelLog.d.ts.map +1 -0
  43. package/dist/delivery/channelLog.js +94 -0
  44. package/dist/delivery/channelLog.js.map +1 -0
  45. package/dist/delivery/crypto.d.ts +312 -0
  46. package/dist/delivery/crypto.d.ts.map +1 -0
  47. package/dist/delivery/crypto.js +495 -0
  48. package/dist/delivery/crypto.js.map +1 -0
  49. package/dist/delivery/eip712.d.ts +248 -0
  50. package/dist/delivery/eip712.d.ts.map +1 -0
  51. package/dist/delivery/eip712.js +397 -0
  52. package/dist/delivery/eip712.js.map +1 -0
  53. package/dist/delivery/envelopeBuilder.d.ts +531 -0
  54. package/dist/delivery/envelopeBuilder.d.ts.map +1 -0
  55. package/dist/delivery/envelopeBuilder.js +832 -0
  56. package/dist/delivery/envelopeBuilder.js.map +1 -0
  57. package/dist/delivery/index.d.ts +53 -0
  58. package/dist/delivery/index.d.ts.map +1 -0
  59. package/dist/delivery/index.js +143 -0
  60. package/dist/delivery/index.js.map +1 -0
  61. package/dist/delivery/keys.d.ts +344 -0
  62. package/dist/delivery/keys.d.ts.map +1 -0
  63. package/dist/delivery/keys.js +513 -0
  64. package/dist/delivery/keys.js.map +1 -0
  65. package/dist/delivery/nonce-keys.d.ts +93 -0
  66. package/dist/delivery/nonce-keys.d.ts.map +1 -0
  67. package/dist/delivery/nonce-keys.js +88 -0
  68. package/dist/delivery/nonce-keys.js.map +1 -0
  69. package/dist/delivery/setupBuilder.d.ts +403 -0
  70. package/dist/delivery/setupBuilder.d.ts.map +1 -0
  71. package/dist/delivery/setupBuilder.js +554 -0
  72. package/dist/delivery/setupBuilder.js.map +1 -0
  73. package/dist/delivery/types.d.ts +722 -0
  74. package/dist/delivery/types.d.ts.map +1 -0
  75. package/dist/delivery/types.js +150 -0
  76. package/dist/delivery/types.js.map +1 -0
  77. package/dist/delivery/validate.d.ts +288 -0
  78. package/dist/delivery/validate.d.ts.map +1 -0
  79. package/dist/delivery/validate.js +648 -0
  80. package/dist/delivery/validate.js.map +1 -0
  81. package/dist/level1/Agent.d.ts +130 -0
  82. package/dist/level1/Agent.d.ts.map +1 -1
  83. package/dist/level1/Agent.js +248 -0
  84. package/dist/level1/Agent.js.map +1 -1
  85. package/dist/level1/types/Options.d.ts +62 -0
  86. package/dist/level1/types/Options.d.ts.map +1 -1
  87. package/dist/level1/types/Options.js +22 -0
  88. package/dist/level1/types/Options.js.map +1 -1
  89. package/dist/runtime/MockRuntime.d.ts +32 -0
  90. package/dist/runtime/MockRuntime.d.ts.map +1 -1
  91. package/dist/runtime/MockRuntime.js +44 -0
  92. package/dist/runtime/MockRuntime.js.map +1 -1
  93. package/dist/wallet/aa/BundlerClient.d.ts.map +1 -1
  94. package/dist/wallet/aa/BundlerClient.js +18 -3
  95. package/dist/wallet/aa/BundlerClient.js.map +1 -1
  96. package/dist/wallet/aa/PaymasterClient.d.ts.map +1 -1
  97. package/dist/wallet/aa/PaymasterClient.js +4 -1
  98. package/dist/wallet/aa/PaymasterClient.js.map +1 -1
  99. package/package.json +6 -1
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ /**
3
+ * AIP-16 Delivery Surface — Channel Abstraction (Phase 2b)
4
+ * =========================================================
5
+ *
6
+ * Transport-agnostic interface for posting and observing delivery
7
+ * setup + envelope wire objects between requester and provider.
8
+ *
9
+ * The delivery channel is the wire-level boundary between the two
10
+ * participants of an AIP-16 delivery exchange. It does NOT participate
11
+ * in any cryptographic verification — its only job is to transport
12
+ * already-signed {@link DeliverySetupWireV1} and {@link DeliveryEnvelopeWireV1}
13
+ * objects between participants.
14
+ *
15
+ * ## Concrete implementations
16
+ *
17
+ * - **MockDeliveryChannel** — in-process loopback used by tests and
18
+ * MockRuntime flows. Implements dedup-after-verify on the read side
19
+ * (see {@link MockChannel} in `src/negotiation/` for the reference
20
+ * pattern). Useful for end-to-end tests without network setup.
21
+ * - **RelayDeliveryChannel** — HTTP client against the AGIRAILS relay
22
+ * (or any compatible relay implementing the same REST surface). Uses
23
+ * cursor pagination, polling at `POLL_INTERVAL_MS = 1000`, and an
24
+ * SSRF guard on the relay URL (see {@link RelayChannel} in
25
+ * `src/negotiation/` for the reference pattern).
26
+ *
27
+ * ## Security invariants (binding on ALL implementations)
28
+ *
29
+ * 1. **Dedup AFTER verify.** Implementations that deduplicate
30
+ * incoming setups/envelopes MUST verify signatures BEFORE adding any
31
+ * identifier (txId, sig hash, etc.) to a dedup set. Otherwise an
32
+ * attacker can post malformed signatures to permanently suppress
33
+ * the legitimate signed object that arrives later.
34
+ *
35
+ * 2. **Subscriber error isolation.** Subscriber callbacks may throw
36
+ * arbitrary errors (consumer code is not the channel's problem).
37
+ * Implementations MUST catch and silently swallow callback errors —
38
+ * NEVER propagate them into the channel's read loop, since one bad
39
+ * subscriber would otherwise halt delivery for all other subscribers
40
+ * sharing the same channel.
41
+ *
42
+ * 3. **No verification at the channel layer.** Channels do NOT call
43
+ * `recoverSetupSigner` / `recoverEnvelopeSigner` or any other
44
+ * cryptographic check. Verification is the consumer's responsibility
45
+ * (typically via {@link DeliverySetupBuilder.verify} /
46
+ * {@link DeliveryEnvelopeBuilder.verify}). The channel is a dumb pipe.
47
+ *
48
+ * 4. **Address comparison case-insensitivity.** Where addresses appear
49
+ * in dedup keys (e.g. `signerAddress`), implementations MUST normalize
50
+ * to lowercase on both sides of any comparison.
51
+ *
52
+ * ## Subscription semantics
53
+ *
54
+ * - `subscribeSetups` / `subscribeEnvelopes` are scoped to a single
55
+ * `txId`. A subscription begins emitting from "now" — that is, from
56
+ * objects that are NOT already known to the channel at subscribe time
57
+ * are guaranteed to be delivered; objects that ARE already known MAY
58
+ * or MAY NOT be re-delivered depending on the implementation. Code
59
+ * that needs the full historical set should call the optional
60
+ * `getSetups` / `getEnvelopes` methods.
61
+ *
62
+ * - Returning a {@link DeliverySubscription} whose `close()` is invoked
63
+ * MUST stop further callbacks from firing on that subscription. It
64
+ * SHOULD also free any underlying transport (polling timer, HTTP
65
+ * connection, etc.).
66
+ *
67
+ * - Multiple subscribers on the same `txId` MUST each receive all
68
+ * matching objects (fan-out, not steal). One subscriber's `close()`
69
+ * MUST NOT affect other subscribers.
70
+ *
71
+ * @module delivery/channel
72
+ * @see {@link DeliverySetupWireV1}
73
+ * @see {@link DeliveryEnvelopeWireV1}
74
+ */
75
+ Object.defineProperty(exports, "__esModule", { value: true });
76
+ //# sourceMappingURL=channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/delivery/channel.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * AIP-16 Delivery Surface — Pluggable Channel Logger (Phase 2b)
3
+ * ==============================================================
4
+ *
5
+ * Minimal logger contract used by delivery channel implementations
6
+ * (`MockDeliveryChannel`, `RelayDeliveryChannel`) to surface
7
+ * operational events — transport errors, dedup hits, subscriber-callback
8
+ * errors that were swallowed per security invariant #2 — without
9
+ * coupling those implementations to any specific logging framework.
10
+ *
11
+ * ## Design intent
12
+ *
13
+ * Channels are dumb pipes: they verify nothing, decrypt nothing, and
14
+ * make no business decisions. They DO, however, encounter transport
15
+ * failures, malformed payloads, and (per the subscriber-error-isolation
16
+ * invariant) consumer callbacks that throw. Operators need visibility
17
+ * into these events; tests need to be able to assert on or silence
18
+ * them. A pluggable {@link LogFn} satisfies both needs without
19
+ * pulling pino/winston/console into the SDK runtime.
20
+ *
21
+ * The shape is intentionally minimal — three levels, a message string,
22
+ * and an optional structured `details` bag. Implementers can adapt
23
+ * this to any backend (console, pino, OpenTelemetry, in-memory buffer
24
+ * for tests, etc.).
25
+ *
26
+ * ## Levels
27
+ *
28
+ * - `"info"` — Normal lifecycle events (subscription opened/closed,
29
+ * publish accepted). Quiet by default in production.
30
+ * - `"warn"` — Recoverable anomalies (dedup hit on poisoned signature,
31
+ * subscriber callback threw and was swallowed, optional `getSetups`
32
+ * not supported by the underlying transport). Worth surfacing in
33
+ * dashboards but not pageable.
34
+ * - `"error"` — Transport failures (relay returned 5xx, network
35
+ * unreachable, malformed wire object rejected before publish).
36
+ * May trigger retries upstream.
37
+ *
38
+ * ## Default
39
+ *
40
+ * The exported {@link noopLog} is a silent default suitable for
41
+ * library consumers who do not want any logging output. Tests can
42
+ * provide a capturing logger; production deployments can adapt the
43
+ * SDK's events to their preferred observability stack.
44
+ *
45
+ * @example Console logger
46
+ * ```typescript
47
+ * import type { LogFn } from '@agirails/sdk/delivery';
48
+ *
49
+ * const consoleLog: LogFn = (level, msg, details) => {
50
+ * // eslint-disable-next-line no-console
51
+ * console[level](`[delivery] ${msg}`, details ?? {});
52
+ * };
53
+ * ```
54
+ *
55
+ * @example Capturing logger for tests
56
+ * ```typescript
57
+ * const events: Array<{ level: string; msg: string; details?: unknown }> = [];
58
+ * const captureLog: LogFn = (level, msg, details) => {
59
+ * events.push({ level, msg, details });
60
+ * };
61
+ * // …run channel under test, then assert on `events`.
62
+ * ```
63
+ *
64
+ * @module delivery/channelLog
65
+ */
66
+ /**
67
+ * Pluggable logger function used by delivery channel implementations.
68
+ *
69
+ * Implementations of this signature receive a level, a human-readable
70
+ * message string, and an optional structured `details` bag. They MAY
71
+ * route the event to any backend (console, pino, OTel, in-memory
72
+ * capture, /dev/null) — the channel does not care.
73
+ *
74
+ * The function MUST be synchronous from the channel's point of view
75
+ * (the channel never `await`s it). Implementations that need to batch
76
+ * or ship logs asynchronously SHOULD do so via internal queues, NOT
77
+ * by returning a Promise.
78
+ *
79
+ * Implementations MUST NOT throw. If an underlying logging backend
80
+ * fails (e.g. write to disk failed), implementations SHOULD catch
81
+ * and discard — a channel cannot let a logging failure interrupt
82
+ * delivery transport.
83
+ *
84
+ * @param level The severity of the event. See module JSDoc for level
85
+ * semantics.
86
+ * @param msg A human-readable description of the event. Stable enough
87
+ * to be greppable across log volumes, but not promised as a
88
+ * machine-actionable identifier — use `details.code` (when present)
89
+ * for that.
90
+ * @param details Optional structured context (txId, signer address,
91
+ * HTTP status, error code, etc.). Implementations SHOULD attach
92
+ * any details bag to the underlying log record verbatim; loggers
93
+ * that flatten structured data SHOULD preserve key/value pairs.
94
+ */
95
+ export type LogFn = (level: 'info' | 'warn' | 'error', msg: string, details?: Record<string, unknown>) => void;
96
+ /**
97
+ * Silent default {@link LogFn}: discards every event.
98
+ *
99
+ * Suitable as the default `log` parameter on channel constructors so
100
+ * that consumers who do not opt into logging see no output. Tests and
101
+ * production deployments that want visibility SHOULD pass their own
102
+ * {@link LogFn} implementation.
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * import { noopLog } from '@agirails/sdk/delivery';
107
+ *
108
+ * const channel = new RelayDeliveryChannel({
109
+ * baseUrl: 'https://relay.agirails.io',
110
+ * log: noopLog, // explicit silence; same as omitting the param
111
+ * });
112
+ * ```
113
+ */
114
+ export declare const noopLog: LogFn;
115
+ //# sourceMappingURL=channelLog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channelLog.d.ts","sourceRoot":"","sources":["../../src/delivery/channelLog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAMH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,MAAM,KAAK,GAAG,CAClB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAChC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC9B,IAAI,CAAC;AAMV;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ /**
3
+ * AIP-16 Delivery Surface — Pluggable Channel Logger (Phase 2b)
4
+ * ==============================================================
5
+ *
6
+ * Minimal logger contract used by delivery channel implementations
7
+ * (`MockDeliveryChannel`, `RelayDeliveryChannel`) to surface
8
+ * operational events — transport errors, dedup hits, subscriber-callback
9
+ * errors that were swallowed per security invariant #2 — without
10
+ * coupling those implementations to any specific logging framework.
11
+ *
12
+ * ## Design intent
13
+ *
14
+ * Channels are dumb pipes: they verify nothing, decrypt nothing, and
15
+ * make no business decisions. They DO, however, encounter transport
16
+ * failures, malformed payloads, and (per the subscriber-error-isolation
17
+ * invariant) consumer callbacks that throw. Operators need visibility
18
+ * into these events; tests need to be able to assert on or silence
19
+ * them. A pluggable {@link LogFn} satisfies both needs without
20
+ * pulling pino/winston/console into the SDK runtime.
21
+ *
22
+ * The shape is intentionally minimal — three levels, a message string,
23
+ * and an optional structured `details` bag. Implementers can adapt
24
+ * this to any backend (console, pino, OpenTelemetry, in-memory buffer
25
+ * for tests, etc.).
26
+ *
27
+ * ## Levels
28
+ *
29
+ * - `"info"` — Normal lifecycle events (subscription opened/closed,
30
+ * publish accepted). Quiet by default in production.
31
+ * - `"warn"` — Recoverable anomalies (dedup hit on poisoned signature,
32
+ * subscriber callback threw and was swallowed, optional `getSetups`
33
+ * not supported by the underlying transport). Worth surfacing in
34
+ * dashboards but not pageable.
35
+ * - `"error"` — Transport failures (relay returned 5xx, network
36
+ * unreachable, malformed wire object rejected before publish).
37
+ * May trigger retries upstream.
38
+ *
39
+ * ## Default
40
+ *
41
+ * The exported {@link noopLog} is a silent default suitable for
42
+ * library consumers who do not want any logging output. Tests can
43
+ * provide a capturing logger; production deployments can adapt the
44
+ * SDK's events to their preferred observability stack.
45
+ *
46
+ * @example Console logger
47
+ * ```typescript
48
+ * import type { LogFn } from '@agirails/sdk/delivery';
49
+ *
50
+ * const consoleLog: LogFn = (level, msg, details) => {
51
+ * // eslint-disable-next-line no-console
52
+ * console[level](`[delivery] ${msg}`, details ?? {});
53
+ * };
54
+ * ```
55
+ *
56
+ * @example Capturing logger for tests
57
+ * ```typescript
58
+ * const events: Array<{ level: string; msg: string; details?: unknown }> = [];
59
+ * const captureLog: LogFn = (level, msg, details) => {
60
+ * events.push({ level, msg, details });
61
+ * };
62
+ * // …run channel under test, then assert on `events`.
63
+ * ```
64
+ *
65
+ * @module delivery/channelLog
66
+ */
67
+ Object.defineProperty(exports, "__esModule", { value: true });
68
+ exports.noopLog = void 0;
69
+ // ============================================================================
70
+ // Default silent logger
71
+ // ============================================================================
72
+ /**
73
+ * Silent default {@link LogFn}: discards every event.
74
+ *
75
+ * Suitable as the default `log` parameter on channel constructors so
76
+ * that consumers who do not opt into logging see no output. Tests and
77
+ * production deployments that want visibility SHOULD pass their own
78
+ * {@link LogFn} implementation.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * import { noopLog } from '@agirails/sdk/delivery';
83
+ *
84
+ * const channel = new RelayDeliveryChannel({
85
+ * baseUrl: 'https://relay.agirails.io',
86
+ * log: noopLog, // explicit silence; same as omitting the param
87
+ * });
88
+ * ```
89
+ */
90
+ const noopLog = () => {
91
+ // Intentional no-op. See JSDoc for rationale.
92
+ };
93
+ exports.noopLog = noopLog;
94
+ //# sourceMappingURL=channelLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channelLog.js","sourceRoot":"","sources":["../../src/delivery/channelLog.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;;;AAyCH,+EAA+E;AAC/E,wBAAwB;AACxB,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;GAiBG;AACI,MAAM,OAAO,GAAU,GAAG,EAAE;IACjC,8CAA8C;AAChD,CAAC,CAAC;AAFW,QAAA,OAAO,WAElB"}
@@ -0,0 +1,312 @@
1
+ /**
2
+ * AIP-16 Delivery Surface — AES-256-GCM AEAD + Body Hashing (Phase 2a Foundation)
3
+ * ==============================================================================
4
+ *
5
+ * Authenticated encryption primitives for the `x25519-aes256gcm-v1`
6
+ * delivery scheme of AIP-16 Rev 5.
7
+ *
8
+ * This module sits one layer above `./keys.ts` (which produces the
9
+ * 32-byte session key via X25519 ECDH + HKDF-SHA256) and one layer
10
+ * below the envelope builders (`./envelopeBuilder.ts`, later phase),
11
+ * which assemble the ciphertext + nonce + tag + signature into the
12
+ * wire envelope.
13
+ *
14
+ * ## Scope of this file
15
+ *
16
+ * - {@link encryptBody}: seal a UTF-8 string or raw byte body with
17
+ * AES-256-GCM, returning ciphertext + 12-byte nonce + 16-byte tag.
18
+ * - {@link decryptBody}: inverse — verify the GCM tag and return the
19
+ * plaintext.
20
+ * - {@link bodyHash}: keccak256 of the UTF-8 / raw bytes, in the exact
21
+ * form used for the `payloadHash` field of the signed EIP-712
22
+ * projection.
23
+ * - Hex format helpers ({@link bytesToHex}, {@link bytesFromHex}) for
24
+ * ciphertext / nonce / tag serialization at the wire boundary.
25
+ *
26
+ * ## Library choice: `node:crypto`
27
+ *
28
+ * We use Node's built-in AES-GCM (`createCipheriv('aes-256-gcm', …)`)
29
+ * rather than `@noble/ciphers`. Reasons:
30
+ *
31
+ * 1. AES-256-GCM is a *standard* AEAD; the implementation surface is
32
+ * small and audited as part of OpenSSL (the engine behind
33
+ * `node:crypto`).
34
+ * 2. Zero new dependencies — matches the design constraint in the
35
+ * Phase 2a task brief.
36
+ * 3. The `setAuthTag` / `getAuthTag` pattern is mechanical and easy
37
+ * to audit for "did we actually verify the tag before returning
38
+ * plaintext?" (`decipher.final()` throws on tag mismatch).
39
+ *
40
+ * ## AAD (Additional Authenticated Data) — H5 defense-in-depth
41
+ *
42
+ * Both {@link encryptBody} and {@link decryptBody} accept an OPTIONAL
43
+ * `aad` byte buffer. When supplied, the bytes are fed into GCM via
44
+ * `cipher.setAAD(aad)` / `decipher.setAAD(aad)` — they are *not*
45
+ * encrypted, but the GCM authentication tag commits to them, so a
46
+ * decryption with the wrong AAD fails closed with a tag mismatch.
47
+ *
48
+ * For the `x25519-aes256gcm-v1` scheme the envelope builder constructs
49
+ *
50
+ * aad = txId_bytes (32) || signerAddress_bytes (20) // 52 bytes
51
+ *
52
+ * and passes it to both encrypt and decrypt. This binds the GCM
53
+ * authentication to the on-chain transaction id and the EOA that
54
+ * signed the envelope, so a misrouted envelope (correct ciphertext +
55
+ * nonce + tag + sessionKey, but delivered to a different `txId` or
56
+ * `signerAddress`) cannot be opened — defense-in-depth on top of the
57
+ * EIP-712 signature over `txId` and `payloadHash`.
58
+ *
59
+ * Because the channel is gated behind the Phase 2f feature flag and
60
+ * no in-flight `x25519-aes256gcm-v1` envelopes exist in production, we
61
+ * change the AAD inline within the existing scheme tag rather than
62
+ * minting a `-v2` suffix; the scheme name stays `x25519-aes256gcm-v1`.
63
+ *
64
+ * AAD is OPTIONAL at the primitive level. Callers that omit it get
65
+ * the legacy "AAD = empty" behavior (still interoperable with prior
66
+ * versions of the function). The envelope builder always supplies AAD.
67
+ *
68
+ * ## Memory hygiene
69
+ *
70
+ * `sessionKey` and plaintext are held in plain `Uint8Array`s. Like
71
+ * `./keys.ts`, this module does not attempt to zero memory — V8 makes
72
+ * no such guarantees, and the SDK target environments (Node + Bun)
73
+ * inherit that limitation. Callers SHOULD let secrets go out of scope
74
+ * promptly after use.
75
+ *
76
+ * @module delivery/crypto
77
+ * @see ./keys — produces the 32-byte session key consumed here
78
+ * @see ./eip712 — signs the `payloadHash` produced by {@link bodyHash}
79
+ * @see {@link https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf NIST SP 800-38D — GCM mode}
80
+ * @see {@link https://www.rfc-editor.org/rfc/rfc5116 RFC 5116 — AEAD interface}
81
+ */
82
+ /**
83
+ * Length (in bytes) of the AES-256-GCM nonce / IV.
84
+ *
85
+ * Twelve bytes is the GCM-standard length (NIST SP 800-38D §5.2.1.1).
86
+ * Any other length is technically permitted by the spec but triggers
87
+ * an additional GHASH step and is interoperable only by convention;
88
+ * AIP-16 v1 mandates 12 bytes to keep the wire format unambiguous.
89
+ *
90
+ * We sample the nonce from `randomBytes(12)` per-encryption. The
91
+ * 2^96 nonce space is more than adequate for the AIP-16 use case
92
+ * (one nonce per delivery envelope per session key, with session keys
93
+ * themselves bound to a single transaction via HKDF).
94
+ */
95
+ export declare const AES_GCM_NONCE_LENGTH: 12;
96
+ /**
97
+ * Length (in bytes) of the AES-256-GCM authentication tag.
98
+ *
99
+ * Sixteen bytes is the maximum (full-strength) tag length permitted
100
+ * by GCM; NIST SP 800-38D recommends this size when the application
101
+ * cannot tolerate any forgery probability. The wire format reserves
102
+ * a fixed 16-byte slot for the tag.
103
+ */
104
+ export declare const AES_GCM_TAG_LENGTH: 16;
105
+ /**
106
+ * Length (in bytes) of an AES-256 key. Equal to
107
+ * {@link DELIVERY_SESSION_KEY_LENGTH} (re-exported from `./keys`) so
108
+ * callers do not have to import both files just for the length check.
109
+ */
110
+ export declare const AES_KEY_LENGTH: 32;
111
+ /**
112
+ * Result of {@link encryptBody}.
113
+ *
114
+ * All three fields are raw bytes (not hex). The envelope builder will
115
+ * apply {@link bytesToHex} just before serializing to the wire
116
+ * envelope's `bodyCiphertextHex`, `nonceHex`, and `tagHex` fields.
117
+ *
118
+ * The separation of `ciphertext` and `tag` matches the `node:crypto`
119
+ * GCM API surface (`getAuthTag` is a distinct call from the cipher's
120
+ * `update` / `final` output). It also makes the wire format
121
+ * unambiguous: there is no need to peel the last 16 bytes off the
122
+ * ciphertext, which is a common parsing bug.
123
+ */
124
+ export interface EncryptResult {
125
+ /**
126
+ * The AES-256-GCM ciphertext. Length is exactly equal to the
127
+ * plaintext length (GCM is a stream-cipher mode, no padding).
128
+ */
129
+ ciphertext: Uint8Array;
130
+ /**
131
+ * The 12-byte random nonce / IV used for this encryption. Embedded
132
+ * verbatim in the wire envelope so the recipient can pass it to
133
+ * {@link decryptBody}.
134
+ */
135
+ nonce: Uint8Array;
136
+ /**
137
+ * The 16-byte GCM authentication tag. The recipient MUST present
138
+ * this tag to AES-GCM during decryption; `decipher.final()` will
139
+ * throw if it does not match, which {@link decryptBody} catches
140
+ * and re-raises as `DeliveryCryptoError('crypto_decrypt_failed')`.
141
+ */
142
+ tag: Uint8Array;
143
+ }
144
+ /**
145
+ * Encrypt a delivery envelope body with AES-256-GCM using the supplied
146
+ * session key.
147
+ *
148
+ * The session key MUST have been produced by
149
+ * {@link import('./keys').deriveSessionKey} (32 bytes, HKDF-SHA256
150
+ * output bound to the on-chain `txId`).
151
+ *
152
+ * A fresh 12-byte nonce is sampled from `randomBytes` per call.
153
+ * Re-using a (key, nonce) pair is *catastrophic* for GCM — it leaks
154
+ * the GHASH key, allowing both decryption of past messages and
155
+ * forgery of future ones. With session keys scoped to one transaction
156
+ * and nonces randomized per envelope, the probability of collision
157
+ * over realistic envelope counts is negligible.
158
+ *
159
+ * AAD is OPTIONAL. When supplied, the bytes are committed by the GCM
160
+ * tag (defense-in-depth — see the H5 section of the module-level docs).
161
+ * Decryption with a different AAD (or with no AAD at all when the
162
+ * encrypt side used one) will fail with `crypto_decrypt_failed`.
163
+ *
164
+ * @param plaintext - Body bytes to encrypt. A `string` is interpreted
165
+ * as UTF-8; pre-encoded `Uint8Array` is passed through verbatim.
166
+ * @param sessionKey - 32-byte AES-256-GCM key from `deriveSessionKey`.
167
+ * @param aad - Optional Additional Authenticated Data. When omitted,
168
+ * no AAD is fed into GCM (legacy behavior). When supplied, MUST be a
169
+ * `Uint8Array`; mismatched AAD at decrypt time fails tag verification.
170
+ * @returns {@link EncryptResult} carrying ciphertext, nonce, and tag.
171
+ * @throws {DeliveryCryptoError} `crypto_encrypt_failed` if
172
+ * `sessionKey` is malformed (not a `Uint8Array` of length 32), if
173
+ * `plaintext` is the wrong type, if `aad` is supplied as a non-bytes
174
+ * value, or if `node:crypto` raises.
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * const key = deriveSessionKey(shared, txId);
179
+ * // AAD = txId_bytes || signerAddress_bytes (H5 binding)
180
+ * const aad = new Uint8Array(52);
181
+ * aad.set(bytesFromHex(txId), 0);
182
+ * aad.set(bytesFromHex(signerAddress), 32);
183
+ * const { ciphertext, nonce, tag } = encryptBody('{"result":"ok"}', key, aad);
184
+ * ```
185
+ */
186
+ export declare function encryptBody(plaintext: Uint8Array | string, sessionKey: Uint8Array, aad?: Uint8Array): EncryptResult;
187
+ /**
188
+ * Decrypt and authenticate an AES-256-GCM ciphertext produced by
189
+ * {@link encryptBody} (or an interoperable peer).
190
+ *
191
+ * The GCM authentication tag is verified inside `decipher.final()` —
192
+ * if the tag does not match, `node:crypto` throws and we re-raise as
193
+ * `DeliveryCryptoError('crypto_decrypt_failed')`. This is the *only*
194
+ * integrity check on the envelope body bytes; pair it with EIP-712
195
+ * signature verification one layer up to also bind the body to a
196
+ * specific signer.
197
+ *
198
+ * AAD is OPTIONAL but MUST match what the encrypt side used. The GCM
199
+ * tag commits to the AAD bytes; supplying the wrong AAD (or omitting
200
+ * AAD when the encrypt side supplied one, or vice versa) produces a
201
+ * tag-mismatch error, surfaced here as `crypto_decrypt_failed`. This
202
+ * is the H5 misrouting defense — see the module-level AAD section.
203
+ *
204
+ * @param ciphertext - GCM ciphertext as raw bytes.
205
+ * @param sessionKey - 32-byte AES-256-GCM key (same as encrypt side).
206
+ * @param nonce - 12-byte nonce that was used at encrypt time.
207
+ * @param tag - 16-byte GCM authentication tag.
208
+ * @param aad - Optional Additional Authenticated Data. MUST exactly
209
+ * match the AAD passed to {@link encryptBody}; mismatch (including
210
+ * AAD/no-AAD asymmetry) fails the tag check and throws
211
+ * `crypto_decrypt_failed`.
212
+ * @returns The decrypted plaintext as raw bytes. If the original
213
+ * plaintext was a UTF-8 string, the caller can recover it via
214
+ * `Buffer.from(plaintext).toString('utf8')`.
215
+ * @throws {DeliveryCryptoError} `crypto_decrypt_failed` on any of:
216
+ * malformed lengths, wrong session key, tampered ciphertext / tag /
217
+ * nonce, AAD mismatch, malformed AAD type, or underlying
218
+ * `node:crypto` failure.
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * const aad = new Uint8Array(52);
223
+ * aad.set(bytesFromHex(txId), 0);
224
+ * aad.set(bytesFromHex(signerAddress), 32);
225
+ * const plaintextBytes = decryptBody(ciphertext, key, nonce, tag, aad);
226
+ * const json = Buffer.from(plaintextBytes).toString('utf8');
227
+ * ```
228
+ */
229
+ export declare function decryptBody(ciphertext: Uint8Array, sessionKey: Uint8Array, nonce: Uint8Array, tag: Uint8Array, aad?: Uint8Array): Uint8Array;
230
+ /**
231
+ * Compute the keccak256 hash of an envelope body, in the exact form
232
+ * embedded into the `payloadHash` field of the signed EIP-712
233
+ * projection.
234
+ *
235
+ * Per AIP-16 Rev 5 §6.2, `payloadHash = keccak256(bodyBytes)` where
236
+ * `bodyBytes` is:
237
+ * - For `scheme: "public-v1"`: the UTF-8 bytes of the plaintext body
238
+ * string (or the raw `Uint8Array` if the caller pre-encoded).
239
+ * - For `scheme: "x25519-aes256gcm-v1"`: the *ciphertext* bytes (the
240
+ * output of {@link encryptBody}.ciphertext), not the plaintext.
241
+ * This commits the signer to the exact bytes that travel on the
242
+ * wire, preventing a malicious relay from substituting alternative
243
+ * ciphertext with the same plaintext (which GCM nonces would
244
+ * actually preclude, but the EIP-712 commitment is independent of
245
+ * that).
246
+ *
247
+ * The output is a 0x-prefixed lowercase 66-char hex string (32-byte
248
+ * keccak256 digest), exactly matching the `bytes32` field shape in
249
+ * the EIP-712 types.
250
+ *
251
+ * @param body - The bytes to hash. A `string` is interpreted as UTF-8.
252
+ * @returns The keccak256 digest as `0x` + 64 lowercase hex chars.
253
+ *
254
+ * @example
255
+ * ```typescript
256
+ * // Public scheme:
257
+ * const h1 = bodyHash('{"result":"ok"}');
258
+ *
259
+ * // Encrypted scheme — hash the CIPHERTEXT, not the plaintext:
260
+ * const { ciphertext } = encryptBody(plaintext, key);
261
+ * const h2 = bodyHash(ciphertext);
262
+ * ```
263
+ */
264
+ export declare function bodyHash(body: string | Uint8Array): `0x${string}`;
265
+ /**
266
+ * Encode raw bytes to a lowercase 0x-prefixed hex string.
267
+ *
268
+ * Intentionally byte-for-byte equivalent to the equivalent helper in
269
+ * `./keys.ts` (which is `internal` there). Exposed here at the
270
+ * module surface so envelope-builder callers can serialize their
271
+ * ciphertext / nonce / tag without reaching into `./keys.ts` internals.
272
+ *
273
+ * Hand-rolled rather than `Buffer.from(b).toString('hex')` so the
274
+ * implementation is portable to non-Node runtimes (Bun, web) without
275
+ * polyfills, and to avoid Buffer-vs-Uint8Array subclass confusion.
276
+ *
277
+ * @param b - Raw bytes to encode.
278
+ * @returns A `0x`-prefixed lowercase hex string of length
279
+ * `2 + 2 * b.length`.
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * bytesToHex(new Uint8Array([0xab, 0xcd])); // "0xabcd"
284
+ * ```
285
+ */
286
+ export declare function bytesToHex(b: Uint8Array): `0x${string}`;
287
+ /**
288
+ * Decode a 0x-prefixed hex string to raw bytes.
289
+ *
290
+ * Strict on shape: the string MUST start with `0x` (case-insensitive
291
+ * prefix), MUST contain only hex digits after the prefix, and MUST
292
+ * have an even number of hex digits. Returns a fresh `Uint8Array`.
293
+ *
294
+ * Used at the wire boundary to decode `bodyCiphertextHex`, `nonceHex`,
295
+ * `tagHex`, and any other hex-serialized byte field into the byte
296
+ * forms expected by {@link decryptBody}.
297
+ *
298
+ * @param hex - 0x-prefixed hex string, even number of hex digits.
299
+ * @returns A fresh `Uint8Array` of length `(hex.length - 2) / 2`.
300
+ * @throws {DeliveryCryptoError} `crypto_decrypt_failed` if `hex` is
301
+ * missing the `0x` prefix, has odd length, or contains non-hex
302
+ * characters. (`crypto_decrypt_failed` is the most likely site of
303
+ * failure for this helper; encrypt-side callers will instead get
304
+ * the structural validation in {@link encryptBody}.)
305
+ *
306
+ * @example
307
+ * ```typescript
308
+ * bytesFromHex('0xabcd'); // Uint8Array(2) [0xab, 0xcd]
309
+ * ```
310
+ */
311
+ export declare function bytesFromHex(hex: string): Uint8Array;
312
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/delivery/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AAYH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,IAAc,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,IAAc,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,cAAc,IAA8B,CAAC;AAM1D;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,UAAU,EAAE,UAAU,CAAC;IAEvB;;;;OAIG;IACH,KAAK,EAAE,UAAU,CAAC;IAElB;;;;;OAKG;IACH,GAAG,EAAE,UAAU,CAAC;CACjB;AAwCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,UAAU,GAAG,MAAM,EAC9B,UAAU,EAAE,UAAU,EACtB,GAAG,CAAC,EAAE,UAAU,GACf,aAAa,CA2Gf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,UAAU,EACf,GAAG,CAAC,EAAE,UAAU,GACf,UAAU,CAsFZ;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,KAAK,MAAM,EAAE,CAOjE;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,GAAG,KAAK,MAAM,EAAE,CAevD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAyCpD"}