@toon-protocol/core 3.3.0 → 3.5.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/wire/oer.ts","../src/wire/envelope.ts","../src/wire/giftwrap.ts","../src/wire/sealed-exchange.ts"],"sourcesContent":["/**\n * Canonical OER length primitives (RFC-0030), ported byte-for-byte from\n * `connector_domain::oer` so the Rust and TypeScript fleets agree on the wire.\n *\n * \"Canonical\" is the whole point (ADR 0023): every value has exactly ONE\n * encoding, and a decoder that quietly accepts a second spelling of the same\n * value is a decoder two implementations can disagree through. So a\n * determinant is refused unless the bytes it introduces are exactly what\n * {@link encodeVarUint} would have produced:\n *\n * - `0x81 0x03` — a long form for `3`, which `0x03` already encodes — is\n * {@link OerErrorKind.NonCanonicalLength}, not `3`.\n * - `0x80` — a zero-byte long form aliasing `0x00` — is\n * {@link OerErrorKind.NonCanonicalLength}, not `0`.\n * - `0x89 …` — nine length bytes, which cannot fit a `u64` without silently\n * discarding the high-order ones — is\n * {@link OerErrorKind.LengthDeterminantOverflow}, not a truncated read.\n *\n * Values are `bigint` because the wire type is `u64` and `number` cannot hold\n * one; the caller narrows.\n *\n * Pure: no I/O, no keys, no clock.\n */\n\n/** The three, and only three, ways a length determinant can be refused. */\nexport enum OerErrorKind {\n /** The buffer ends before the field it declares does. */\n BufferUnderflow = 'buffer_underflow',\n /** A determinant that is not the minimal encoding of its own value. */\n NonCanonicalLength = 'non_canonical_length',\n /** A determinant wider than eight bytes (cannot be a `u64`). */\n LengthDeterminantOverflow = 'length_determinant_overflow',\n}\n\n/** A refusal from the length primitives, carrying which of the three it is. */\nexport class OerError extends Error {\n constructor(readonly kind: OerErrorKind) {\n super(OER_ERROR_MESSAGES[kind]);\n this.name = 'OerError';\n }\n}\n\nconst OER_ERROR_MESSAGES: Record<OerErrorKind, string> = {\n [OerErrorKind.BufferUnderflow]: 'buffer underflow: the value is truncated',\n [OerErrorKind.NonCanonicalLength]:\n 'non-canonical OER length determinant: not the minimal encoding of its value',\n [OerErrorKind.LengthDeterminantOverflow]:\n 'OER length determinant wider than 8 bytes',\n};\n\nconst MAX_DETERMINANT_BYTES = 8;\n\n/** Encode a VarUInt: `0..=127` as one byte, `128+` as a length-prefixed BE value. */\nexport function encodeVarUint(value: bigint): Uint8Array {\n if (value < 0n)\n throw new RangeError('VarUInt cannot encode a negative value');\n if (value <= 127n) return Uint8Array.of(Number(value));\n\n const body: number[] = [];\n let remaining = value;\n while (remaining > 0n) {\n body.unshift(Number(remaining & 0xffn));\n remaining >>= 8n;\n }\n if (body.length > MAX_DETERMINANT_BYTES) {\n throw new RangeError('VarUInt cannot encode a value wider than 8 bytes');\n }\n return Uint8Array.from([0x80 | body.length, ...body]);\n}\n\n/** A decoded field: its value, and how many bytes it consumed. */\nexport interface Decoded<T> {\n value: T;\n consumed: number;\n}\n\n/**\n * Decode a VarUInt at `offset`, refusing any non-canonical spelling.\n *\n * @throws {OerError} and nothing else — arbitrary bytes never produce a\n * `RangeError`, a `TypeError`, or a plausible-but-wrong value.\n */\nexport function decodeVarUint(\n buf: Uint8Array,\n offset: number\n): Decoded<bigint> {\n const first = buf[offset];\n if (first === undefined) throw new OerError(OerErrorKind.BufferUnderflow);\n if (first <= 127) return { value: BigInt(first), consumed: 1 };\n\n const length = first & 0x7f;\n if (length > MAX_DETERMINANT_BYTES) {\n throw new OerError(OerErrorKind.LengthDeterminantOverflow);\n }\n const start = offset + 1;\n const end = start + length;\n if (end > buf.length) throw new OerError(OerErrorKind.BufferUnderflow);\n\n let value = 0n;\n for (let i = start; i < end; i++) {\n value = (value << 8n) | BigInt(buf[i] as number);\n }\n const consumed = 1 + length;\n\n // The canonicality test, exactly as `connector_domain::oer` states it:\n // re-encode and demand the same bytes back. This is what rejects the\n // non-minimal long form and the zero-length alias in one check.\n const canonical = encodeVarUint(value);\n if (\n canonical.length !== consumed ||\n !canonical.every((byte, i) => byte === buf[offset + i])\n ) {\n throw new OerError(OerErrorKind.NonCanonicalLength);\n }\n return { value, consumed };\n}\n\n/** Encode a VarOctetString: a VarUInt length prefix followed by the bytes. */\nexport function encodeVarOctetString(data: Uint8Array): Uint8Array {\n const prefix = encodeVarUint(BigInt(data.length));\n const out = new Uint8Array(prefix.length + data.length);\n out.set(prefix, 0);\n out.set(data, prefix.length);\n return out;\n}\n\n/**\n * Decode a VarOctetString at `offset`.\n *\n * @throws {OerError} and nothing else.\n */\nexport function decodeVarOctetString(\n buf: Uint8Array,\n offset: number\n): Decoded<Uint8Array> {\n const { value: length, consumed: lengthBytes } = decodeVarUint(buf, offset);\n\n // A declared length larger than the whole buffer is underflow, checked in\n // `bigint` BEFORE any `Number` conversion so a `u64`-scale length can never\n // wrap into a plausible small one.\n if (length > BigInt(buf.length)) {\n throw new OerError(OerErrorKind.BufferUnderflow);\n }\n const start = offset + lengthBytes;\n const end = start + Number(length);\n if (end > buf.length) throw new OerError(OerErrorKind.BufferUnderflow);\n\n // A copy, not a view: a decoded body that aliases the input buffer would\n // change under a caller who reuses that buffer.\n return {\n value: buf.slice(start, end),\n consumed: lengthBytes + Number(length),\n };\n}\n","/**\n * The structured envelope (ADR 0018): what a packet carries between a\n * connector and the app behind it, encoded with the same OER primitives an ILP\n * packet already uses rather than as HTTP text.\n *\n * A faithful port of `connector_domain::envelope`\n * (`crates/connector-domain/src/envelope.rs`) — a method, target, ordered\n * headers and body going in; a two-byte big-endian status, headers and body\n * coming back. `vectors/wire-vectors.json` is the contract this is checked\n * against, and `wire-vectors.test.ts` is where that check happens; the prose\n * here describes the same thing but the bytes decide.\n *\n * ─── Why not HTTP text ─────────────────────────────────────────────────────\n * The hand-rolled HTTP text this replaces — building latin1 HTTP/1.1 request\n * text, then parsing a status line and scanning for `\\r\\n\\r\\n` — is how such\n * code acquires leniencies nobody chose: a missing header/body separator\n * yielding an empty body, blank header lines skipped, spaces tolerated inside\n * the target. A structured encoding has exactly one representation per message\n * and nothing to smuggle.\n *\n * ─── Headers are a list, not a map ─────────────────────────────────────────\n * A count prefix then `(name, value)` pairs in order. Both header ORDER and\n * DUPLICATE NAMES are meaningful on this wire and both survive a round trip; a\n * `Record<string, string>` would silently destroy each.\n *\n * Pure: no keys, no I/O, no clock. `@noble/*` is not involved.\n */\n\nimport {\n OerError,\n OerErrorKind,\n decodeVarOctetString,\n decodeVarUint,\n encodeVarOctetString,\n encodeVarUint,\n} from './oer.js';\n\nconst TYPE_ENVELOPE_REQUEST = 1;\nconst TYPE_ENVELOPE_RESPONSE = 2;\n\n/**\n * Every distinguishable way an envelope can fail to decode. These strings are\n * the vector file's `expected_error` values verbatim — the same names\n * `EnvelopeError` uses in `connector-domain` — so a replay compares reasons,\n * not just \"it threw\".\n *\n * There is deliberately no catch-all: a decoder with an `Invalid` bucket is a\n * decoder that can be lenient without anyone noticing.\n */\nexport enum EnvelopeErrorKind {\n /** The buffer ends before the envelope does. */\n BufferUnderflow = 'buffer_underflow',\n /** A length determinant that is not the minimal encoding of its value. */\n NonCanonicalLength = 'non_canonical_length',\n /** A length determinant wider than eight bytes. */\n LengthDeterminantOverflow = 'length_determinant_overflow',\n /** The type byte is not the one the requested direction requires. */\n InvalidType = 'invalid_type',\n /** A field that must be UTF-8 is not. */\n InvalidUtf8 = 'invalid_utf8',\n /** Bytes remain after a fully decoded envelope. */\n TrailingBytes = 'trailing_bytes',\n}\n\n/**\n * A refusal to decode. `kind` is the machine-readable reason; `field` names\n * the offending field for {@link EnvelopeErrorKind.InvalidUtf8}, matching\n * `EnvelopeError::InvalidUtf8(&'static str)` on the Rust side.\n */\nexport class EnvelopeError extends Error {\n constructor(\n readonly kind: EnvelopeErrorKind,\n readonly field?: string\n ) {\n super(\n field === undefined\n ? ENVELOPE_ERROR_MESSAGES[kind]\n : `${ENVELOPE_ERROR_MESSAGES[kind]} (field '${field}')`\n );\n this.name = 'EnvelopeError';\n }\n}\n\nconst ENVELOPE_ERROR_MESSAGES: Record<EnvelopeErrorKind, string> = {\n [EnvelopeErrorKind.BufferUnderflow]:\n 'buffer underflow: envelope is truncated',\n [EnvelopeErrorKind.NonCanonicalLength]:\n 'non-canonical OER length determinant: not the minimal encoding of its value',\n [EnvelopeErrorKind.LengthDeterminantOverflow]:\n 'OER length determinant wider than 8 bytes',\n [EnvelopeErrorKind.InvalidType]:\n 'invalid envelope type byte: expected 1 (REQUEST) or 2 (RESPONSE)',\n [EnvelopeErrorKind.InvalidUtf8]: 'invalid UTF-8 in envelope field',\n [EnvelopeErrorKind.TrailingBytes]:\n 'trailing bytes after a fully decoded envelope',\n};\n\n/** One `(name, value)` header, in wire order. Duplicates are legal. */\nexport type EnvelopeHeader = readonly [name: string, value: string];\n\n/** The request a connector is to make of the app behind a terminated route. */\nexport interface EnvelopeRequest {\n method: string;\n target: string;\n headers: readonly EnvelopeHeader[];\n body: Uint8Array;\n}\n\n/** The app's response back to the connector. */\nexport interface EnvelopeResponse {\n /** HTTP status, two bytes big-endian on the wire (`0..=65535`). */\n status: number;\n headers: readonly EnvelopeHeader[];\n body: Uint8Array;\n}\n\n/** Either direction, tagged — the shape the vector file's `decoded` carries. */\nexport type Envelope =\n | ({ direction: 'request' } & EnvelopeRequest)\n | ({ direction: 'response' } & EnvelopeResponse);\n\n// ─── Shared helpers ─────────────────────────────────────────────────────────\n\nconst UTF8_ENCODER = new TextEncoder();\n/** `fatal` is load-bearing: a lenient decoder would turn bad bytes into U+FFFD. */\nconst UTF8_DECODER = new TextDecoder('utf-8', { fatal: true });\n\n/** Map an {@link OerError} onto its envelope-level counterpart, 1:1. */\nfunction fromOerError(error: unknown): EnvelopeError {\n if (error instanceof OerError) {\n switch (error.kind) {\n case OerErrorKind.BufferUnderflow:\n return new EnvelopeError(EnvelopeErrorKind.BufferUnderflow);\n case OerErrorKind.NonCanonicalLength:\n return new EnvelopeError(EnvelopeErrorKind.NonCanonicalLength);\n case OerErrorKind.LengthDeterminantOverflow:\n return new EnvelopeError(EnvelopeErrorKind.LengthDeterminantOverflow);\n }\n }\n throw error;\n}\n\nfunction decodeOctets(\n buf: Uint8Array,\n offset: number\n): { value: Uint8Array; consumed: number } {\n try {\n return decodeVarOctetString(buf, offset);\n } catch (error) {\n throw fromOerError(error);\n }\n}\n\nfunction decodeCount(\n buf: Uint8Array,\n offset: number\n): {\n value: bigint;\n consumed: number;\n} {\n try {\n return decodeVarUint(buf, offset);\n } catch (error) {\n throw fromOerError(error);\n }\n}\n\n/** Decode a VarOctetString and validate it as UTF-8 in one step. */\nfunction decodeStringField(\n buf: Uint8Array,\n offset: number,\n field: string\n): { value: string; consumed: number } {\n const { value: bytes, consumed } = decodeOctets(buf, offset);\n try {\n return { value: UTF8_DECODER.decode(bytes), consumed };\n } catch {\n throw new EnvelopeError(EnvelopeErrorKind.InvalidUtf8, field);\n }\n}\n\nfunction checkTypeByte(buf: Uint8Array, expected: number): number {\n const typeByte = buf[0];\n if (typeByte === undefined) {\n throw new EnvelopeError(EnvelopeErrorKind.BufferUnderflow);\n }\n if (typeByte !== expected) {\n throw new EnvelopeError(EnvelopeErrorKind.InvalidType);\n }\n return 1;\n}\n\nfunction concat(parts: readonly Uint8Array[]): Uint8Array {\n const total = parts.reduce((sum, part) => sum + part.length, 0);\n const out = new Uint8Array(total);\n let offset = 0;\n for (const part of parts) {\n out.set(part, offset);\n offset += part.length;\n }\n return out;\n}\n\nfunction encodeHeaders(headers: readonly EnvelopeHeader[]): Uint8Array {\n const parts: Uint8Array[] = [encodeVarUint(BigInt(headers.length))];\n for (const [name, value] of headers) {\n parts.push(encodeVarOctetString(UTF8_ENCODER.encode(name)));\n parts.push(encodeVarOctetString(UTF8_ENCODER.encode(value)));\n }\n return concat(parts);\n}\n\nfunction decodeHeaders(\n buf: Uint8Array,\n offset: number\n): { value: EnvelopeHeader[]; consumed: number } {\n const { value: count, consumed: countBytes } = decodeCount(buf, offset);\n\n // Every header costs at least two bytes, so a count exceeding what remains\n // cannot be honoured. Checked in `bigint`, before any loop, so a `u64`-scale\n // count is refused as underflow instead of spinning.\n if (count * 2n > BigInt(buf.length - offset - countBytes)) {\n throw new EnvelopeError(EnvelopeErrorKind.BufferUnderflow);\n }\n\n let consumed = countBytes;\n const headers: EnvelopeHeader[] = [];\n for (let i = 0n; i < count; i++) {\n const name = decodeStringField(buf, offset + consumed, 'header name');\n consumed += name.consumed;\n const value = decodeStringField(buf, offset + consumed, 'header value');\n consumed += value.consumed;\n headers.push([name.value, value.value]);\n }\n return { value: headers, consumed };\n}\n\n// ─── Request ────────────────────────────────────────────────────────────────\n\n/** Encode a request envelope. The inverse of {@link decodeEnvelopeRequest}. */\nexport function encodeEnvelopeRequest(request: EnvelopeRequest): Uint8Array {\n return concat([\n Uint8Array.of(TYPE_ENVELOPE_REQUEST),\n encodeVarOctetString(UTF8_ENCODER.encode(request.method)),\n encodeVarOctetString(UTF8_ENCODER.encode(request.target)),\n encodeHeaders(request.headers),\n encodeVarOctetString(request.body),\n ]);\n}\n\n/**\n * Decode a request envelope, consuming the WHOLE buffer.\n *\n * @throws {EnvelopeError} and nothing else, for any input whatsoever.\n */\nexport function decodeEnvelopeRequest(buf: Uint8Array): EnvelopeRequest {\n let offset = checkTypeByte(buf, TYPE_ENVELOPE_REQUEST);\n\n const method = decodeStringField(buf, offset, 'method');\n offset += method.consumed;\n\n const target = decodeStringField(buf, offset, 'target');\n offset += target.consumed;\n\n const headers = decodeHeaders(buf, offset);\n offset += headers.consumed;\n\n const body = decodeOctets(buf, offset);\n offset += body.consumed;\n\n if (offset !== buf.length) {\n throw new EnvelopeError(EnvelopeErrorKind.TrailingBytes);\n }\n return {\n method: method.value,\n target: target.value,\n headers: headers.value,\n body: body.value,\n };\n}\n\n// ─── Response ───────────────────────────────────────────────────────────────\n\n/** Encode a response envelope. The inverse of {@link decodeEnvelopeResponse}. */\nexport function encodeEnvelopeResponse(response: EnvelopeResponse): Uint8Array {\n if (\n !Number.isInteger(response.status) ||\n response.status < 0 ||\n response.status > 0xffff\n ) {\n throw new RangeError(\n `envelope status must fit two bytes, got ${response.status}`\n );\n }\n return concat([\n Uint8Array.of(\n TYPE_ENVELOPE_RESPONSE,\n (response.status >> 8) & 0xff,\n response.status & 0xff\n ),\n encodeHeaders(response.headers),\n encodeVarOctetString(response.body),\n ]);\n}\n\n/**\n * Decode a response envelope, consuming the WHOLE buffer.\n *\n * @throws {EnvelopeError} and nothing else, for any input whatsoever.\n */\nexport function decodeEnvelopeResponse(buf: Uint8Array): EnvelopeResponse {\n let offset = checkTypeByte(buf, TYPE_ENVELOPE_RESPONSE);\n\n const high = buf[offset];\n const low = buf[offset + 1];\n if (high === undefined || low === undefined) {\n throw new EnvelopeError(EnvelopeErrorKind.BufferUnderflow);\n }\n const status = (high << 8) | low;\n offset += 2;\n\n const headers = decodeHeaders(buf, offset);\n offset += headers.consumed;\n\n const body = decodeOctets(buf, offset);\n offset += body.consumed;\n\n if (offset !== buf.length) {\n throw new EnvelopeError(EnvelopeErrorKind.TrailingBytes);\n }\n return { status, headers: headers.value, body: body.value };\n}\n\n// ─── Direction-tagged façade ────────────────────────────────────────────────\n\n/** Encode either direction from its tagged form. */\nexport function encodeEnvelope(envelope: Envelope): Uint8Array {\n return envelope.direction === 'request'\n ? encodeEnvelopeRequest(envelope)\n : encodeEnvelopeResponse(envelope);\n}\n\n/**\n * Decode as the named direction. The direction is an INPUT, never guessed from\n * the type byte: a caller that asked for a request and got a response has been\n * handed the wrong bytes, and `invalid_type` is the honest answer.\n */\nexport function decodeEnvelope(\n buf: Uint8Array,\n direction: 'request' | 'response'\n): Envelope {\n return direction === 'request'\n ? { direction, ...decodeEnvelopeRequest(buf) }\n : { direction, ...decodeEnvelopeResponse(buf) };\n}\n","/**\n * The gift wrap (ADR 0018) and the fulfilment a shared secret derives (ADR\n * 0019): the seal around the {@link ./envelope.js | envelope}, so a packet\n * payload is readable only by the connector that terminates it and the\n * preimage that pays for it needs no app participation.\n *\n * A faithful port of `connector_signer::giftwrap`\n * (`crates/connector-signer/src/giftwrap.rs` on toon-protocol/connector `main`)\n * and of `connector_domain::condition::derive_condition`\n * (`crates/connector-domain/src/condition.rs:27`). `vectors/wire-vectors.json`'s\n * `giftwrap` and `fulfilment` sections are the contract, replayed byte-for-byte\n * in `wire-vectors.test.ts`; the prose here describes the same thing but the\n * bytes decide.\n *\n * ─── The mechanism, as read off the Rust ────────────────────────────────────\n * Recorded here because it cannot be inferred from the vector file's field\n * names, and because the acceptance criterion for toon-client#449 asks for it\n * in writing. Every line below is cited to\n * `crates/connector-signer/src/giftwrap.rs` unless stated otherwise.\n *\n * - **Curve and ECDH.** The receiver's identity key is secp256k1; the value a\n * real connector reports from `GET /ilp/identity` is its 65-byte\n * uncompressed form (`0x04 ‖ X ‖ Y`). The sender ECDHs a fresh, per-packet\n * ephemeral key against it and uses the **raw X-coordinate** of the\n * resulting point — `crypto.rs`'s `ecdh_x_coordinate` does\n * `tweak_mul_assign` then `serialize_compressed()[1..]`, deliberately *not*\n * `libsecp256k1::SharedSecret`'s digest-mixing variant. So: not hashed, and\n * not the 33-byte compressed point.\n * - **Key derivation.** `HKDF-SHA256`, **no salt** (`Hkdf::new(None, ikm)`,\n * i.e. HKDF-Extract with an all-zero salt), expanded to 32 bytes under one\n * of three fixed ASCII `info` strings — `giftwrap.rs`'s `hkdf_key`:\n * - `toon-giftwrap-request` — the request AEAD key, over the ECDH X.\n * - `toon-giftwrap-response` — the response AEAD key, over the shared secret.\n * - `toon-giftwrap-fulfillment` — the fulfilment, over the shared secret.\n * (American spelling on the wire; the vector *section* is `fulfilment`.)\n * These three strings are the only domain separation between two AEAD keys\n * and a payment preimage all derived from the same input, so one wrong\n * character silently yields bytes that decrypt nothing and satisfy no\n * condition.\n * - **AEAD.** ChaCha20-Poly1305 (RFC 8439), 12-byte nonce, **no** additional\n * authenticated data.\n * - **Request framing.** `0x01 ‖ ephemeral_public(65) ‖ nonce(12) ‖\n * ciphertext`, where the encrypted plaintext is `shared_secret(32) ‖\n * encoded_envelope`. The secret rides *inside* the sealed request, which is\n * what lets the response be sealed with no second key exchange.\n * - **Response framing.** `0x02 ‖ nonce(12) ‖ ciphertext`, the plaintext being\n * just the encoded envelope. Sealed directly with the request's shared\n * secret.\n * - **Fulfilment and condition.** `fulfilment = HKDF-SHA256(shared_secret,\n * \"toon-giftwrap-fulfillment\")`; the condition a sender mints is\n * `sha256(fulfilment)` — `condition.rs`'s `derive_condition`. A sender knows\n * the secret before it seals, so it can mint the condition first; the\n * terminating connector recovers the secret by opening the wrap and derives\n * the preimage without asking the app anything.\n * - **Telling a sealed reject from a plaintext one.** The leading type byte,\n * and only that (`looks_like_sealed_response`). A reject raised short of the\n * termination shares no secret with the sender and carries empty `data`; an\n * empty `data` is never sealed, so a sealed reject is positive proof that\n * the destination itself said no.\n *\n * ─── Two failure modes, two types ───────────────────────────────────────────\n * A wrap that cannot be OPENED throws {@link GiftWrapError}. A wrap that opens\n * cleanly but whose recovered plaintext is not a valid envelope throws\n * `EnvelopeError`, from `envelope.ts` — a different class entirely, raised\n * above this module rather than by it. The Rust keeps the same separation\n * (`GiftWrapError` vs `connector_domain::EnvelopeError`) for the same reason:\n * \"I could not read this\" and \"I read this and it was malformed\" are different\n * facts about a peer, and collapsing them loses the distinction at every call\n * site.\n *\n * ─── Callers ────────────────────────────────────────────────────────────────\n * In this package, `BootstrapService.announceViaIlp` (toon#143) is the sending\n * direction: it seals to the connector that TERMINATES the destination and\n * lets that connector derive the fulfilment (ADR 0019), via\n * `wire/sealed-exchange.ts`. The receiving direction — a node that is itself\n * the BTP destination, opening a request addressed to its own\n * {@link giftWrapPublicKey} and sealing the answer back — is served by\n * {@link openRequest}/{@link sealResponse} and lives in the consumers of this\n * subpath rather than here.\n */\n\nimport { secp256k1 } from '@noble/curves/secp256k1.js';\nimport { chacha20poly1305 } from '@noble/ciphers/chacha.js';\nimport { hkdf } from '@noble/hashes/hkdf.js';\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { randomBytes } from '@noble/hashes/utils.js';\n\n// ─── Constants (giftwrap.rs:36-46) ──────────────────────────────────────────\n\n/** ChaCha20-Poly1305 nonce length. */\nexport const GIFTWRAP_NONCE_LENGTH = 12;\n/** The shared secret carried inside a sealed request, and the fulfilment. */\nexport const GIFTWRAP_SECRET_LENGTH = 32;\n/** The uncompressed secp256k1 public key a sealed request carries. */\nexport const GIFTWRAP_PUBLIC_KEY_LENGTH = 65;\n\nconst REQUEST_INFO = 'toon-giftwrap-request';\nconst RESPONSE_INFO = 'toon-giftwrap-response';\nconst FULFILLMENT_INFO = 'toon-giftwrap-fulfillment';\n\n/** `TYPE_GIFTWRAP_REQUEST` — the leading byte of a sealed request. */\nexport const GIFTWRAP_TYPE_REQUEST = 1;\n/** `TYPE_GIFTWRAP_RESPONSE` — the leading byte of a sealed response. */\nexport const GIFTWRAP_TYPE_RESPONSE = 2;\n\n/** Poly1305 tag length — the minimum a non-empty ciphertext can be. */\nconst TAG_LENGTH = 16;\n\n// ─── Errors ─────────────────────────────────────────────────────────────────\n\n/**\n * Every distinguishable way a wrap can fail to open — the four variants of\n * `GiftWrapError` in `giftwrap.rs:57-71`, named the same way. There is\n * deliberately no catch-all: this module must never be able to fail \"somehow\".\n *\n * None of these ever carries decrypted plaintext, matching the Rust's own\n * guarantee.\n */\nexport enum GiftWrapErrorKind {\n /** The leading byte is not the one this direction requires. */\n InvalidType = 'invalid_type',\n /** The bytes end before the framing does. */\n Truncated = 'truncated',\n /** An ephemeral or receiver public key that is not a usable curve point. */\n InvalidKey = 'invalid_key',\n /** The AEAD refused: wrong key, wrong nonce, or a tampered ciphertext. */\n OpenFailed = 'open_failed',\n}\n\nconst GIFTWRAP_ERROR_MESSAGES: Record<GiftWrapErrorKind, string> = {\n [GiftWrapErrorKind.InvalidType]: 'invalid gift wrap type byte',\n [GiftWrapErrorKind.Truncated]: 'gift wrap is truncated',\n [GiftWrapErrorKind.InvalidKey]: 'invalid ephemeral or peer public key',\n [GiftWrapErrorKind.OpenFailed]: 'gift wrap failed to decrypt',\n};\n\n/**\n * A refusal to open a wrap. `kind` is the machine-readable reason;\n * `expectedType` names the type byte that was required, for\n * {@link GiftWrapErrorKind.InvalidType} — mirroring\n * `GiftWrapError::InvalidType(u8)`.\n */\nexport class GiftWrapError extends Error {\n constructor(\n readonly kind: GiftWrapErrorKind,\n readonly expectedType?: number\n ) {\n super(\n expectedType === undefined\n ? GIFTWRAP_ERROR_MESSAGES[kind]\n : `${GIFTWRAP_ERROR_MESSAGES[kind]}: expected ${expectedType}`\n );\n this.name = 'GiftWrapError';\n }\n}\n\n// ─── Shapes ─────────────────────────────────────────────────────────────────\n\n/**\n * What sealing a request produces: the bytes to carry as `Prepare.data`, and\n * the shared secret that both derives this packet's fulfilment and opens the\n * answer. The sender must keep the secret — it is the only thing that opens\n * the response, and the only thing that proves the fulfilment it is paid\n * against was earned rather than guessed.\n */\nexport interface SealedRequest {\n /** `0x01 ‖ ephemeral_public(65) ‖ nonce(12) ‖ ciphertext`. */\n readonly wrapped: Uint8Array;\n /** The 32 random bytes sealed inside `wrapped`. */\n readonly sharedSecret: Uint8Array;\n}\n\n/** What opening a sealed request recovers. */\nexport interface OpenedRequest {\n /** The encoded envelope — feed to `decodeEnvelopeRequest`. */\n readonly envelopeBytes: Uint8Array;\n /** The 32-byte secret the sender chose, for the fulfilment and the answer. */\n readonly sharedSecret: Uint8Array;\n}\n\n/**\n * A key agreement that never hands out secret key material — the TypeScript\n * counterpart of `giftwrap.rs`'s dependency on `Signer::ecdh`, and the reason\n * {@link openRequest} takes this rather than a raw private key: a remote or\n * hardware-backed identity can open a wrap without its key leaving its own\n * boundary.\n *\n * Returns the raw 32-byte ECDH X-coordinate, or throws.\n */\nexport interface GiftWrapEcdh {\n ecdh(ephemeralPublicKey: Uint8Array): Uint8Array;\n}\n\n// ─── Primitives ─────────────────────────────────────────────────────────────\n\nfunction concat(parts: readonly Uint8Array[]): Uint8Array {\n const total = parts.reduce((sum, part) => sum + part.length, 0);\n const out = new Uint8Array(total);\n let offset = 0;\n for (const part of parts) {\n out.set(part, offset);\n offset += part.length;\n }\n return out;\n}\n\nconst UTF8_ENCODER = new TextEncoder();\n\n/**\n * `HKDF-SHA256(ikm, salt = none, info, L = 32)` — `giftwrap.rs`'s `hkdf_key`.\n * The absent salt is load-bearing: HKDF-Extract runs with an all-zero salt, so\n * passing a salt of any kind produces a different, silently-wrong key.\n *\n * `@noble/hashes`'s `hkdf` (v2) requires `info` as bytes rather than the\n * string `@noble/hashes` v1 accepted, so the fixed ASCII `info` strings are\n * UTF-8 encoded here, once per call — the encoded bytes are identical to the\n * ASCII source either way, so this changes no derived value.\n */\nfunction hkdfKey(ikm: Uint8Array, info: string): Uint8Array {\n return hkdf(sha256, ikm, undefined, UTF8_ENCODER.encode(info), 32);\n}\n\n/** `giftwrap.rs`'s `encrypt_with_nonce`: `nonce ‖ ciphertext`. */\nfunction encryptWithNonce(\n key: Uint8Array,\n plaintext: Uint8Array,\n nonce: Uint8Array\n): Uint8Array {\n return concat([nonce, chacha20poly1305(key, nonce).encrypt(plaintext)]);\n}\n\n/** `giftwrap.rs`'s `decrypt`: splits `nonce ‖ ciphertext` and opens it. */\nfunction decrypt(key: Uint8Array, nonceAndCiphertext: Uint8Array): Uint8Array {\n if (nonceAndCiphertext.length < GIFTWRAP_NONCE_LENGTH) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n const nonce = nonceAndCiphertext.subarray(0, GIFTWRAP_NONCE_LENGTH);\n const ciphertext = nonceAndCiphertext.subarray(GIFTWRAP_NONCE_LENGTH);\n // A ciphertext shorter than the Poly1305 tag cannot be an AEAD output at\n // all. `@noble/ciphers` throws a plain `Error` for that rather than failing\n // the tag check, so classify it here instead of letting a foreign error type\n // escape this module.\n if (ciphertext.length < TAG_LENGTH) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n try {\n return chacha20poly1305(key, nonce).decrypt(ciphertext);\n } catch {\n throw new GiftWrapError(GiftWrapErrorKind.OpenFailed);\n }\n}\n\n/**\n * The raw ECDH X-coordinate of `secret * public` — `crypto.rs`'s\n * `ecdh_x_coordinate`. `@noble/curves` returns the compressed point, whose\n * bytes `1..33` are exactly the `serialize_compressed()[1..]` the Rust takes.\n */\nfunction ecdhXCoordinate(\n secretKey: Uint8Array,\n publicKey: Uint8Array\n): Uint8Array {\n let compressed: Uint8Array;\n try {\n compressed = secp256k1.getSharedSecret(secretKey, publicKey, true);\n } catch {\n throw new GiftWrapError(GiftWrapErrorKind.InvalidKey);\n }\n return compressed.slice(1);\n}\n\n/**\n * The ordinary local case of {@link GiftWrapEcdh}: a 32-byte secp256k1 secret\n * key held in this process. `openRequest` accepts a bare key too and wraps it\n * in this.\n */\nexport function localGiftWrapEcdh(secretKey: Uint8Array): GiftWrapEcdh {\n return {\n ecdh: (ephemeralPublicKey) =>\n ecdhXCoordinate(secretKey, ephemeralPublicKey),\n };\n}\n\n/**\n * The 65-byte uncompressed secp256k1 public key a `secretKey` opens gift\n * wraps under — the counterpart {@link openRequest}/{@link localGiftWrapEcdh}\n * derive their ECDH from, and the same format a real connector reports from\n * `GET /ilp/identity` (`ConnectorIdentity.publicKey`). A caller publishes\n * this as a `kind:31990` advertisement's `seal_pubkey` tag (toon-meta#266\n * §3.1) so a buyer can seal a job's PREPARE `data` to it directly, without a\n * `GET /identity` this client cannot serve (ADR 0022).\n */\nexport function giftWrapPublicKey(secretKey: Uint8Array): Uint8Array {\n try {\n return secp256k1.getPublicKey(secretKey, false);\n } catch {\n throw new GiftWrapError(GiftWrapErrorKind.InvalidKey);\n }\n}\n\n// ─── Request direction ──────────────────────────────────────────────────────\n\n/**\n * The deterministic core of {@link sealRequest}, parameterized on every value\n * a real seal draws at random — the counterpart of the Rust's\n * `seal_request_with_randomness`, which that crate gates behind a `test-util`\n * feature for a reason worth repeating: **reusing a (key, nonce) pair under\n * ChaCha20-Poly1305 is catastrophic**, so caller-supplied randomness is a\n * footgun, not a convenience. Use {@link sealRequest} unless you are replaying\n * a fixed vector.\n *\n * `receiverPublicKey` may be the 65-byte uncompressed form a connector's\n * `GET /ilp/identity` reports, or the 33-byte compressed form; the ECDH result\n * is the same point either way. The ephemeral key is always emitted\n * uncompressed, because that is what the framing pins.\n */\nexport function sealRequestWithRandomness(\n plaintext: Uint8Array,\n receiverPublicKey: Uint8Array,\n ephemeralSecretKey: Uint8Array,\n sharedSecret: Uint8Array,\n nonce: Uint8Array\n): Uint8Array {\n if (sharedSecret.length !== GIFTWRAP_SECRET_LENGTH) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n if (nonce.length !== GIFTWRAP_NONCE_LENGTH) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n\n let ephemeralPublicKey: Uint8Array;\n try {\n ephemeralPublicKey = secp256k1.getPublicKey(ephemeralSecretKey, false);\n } catch {\n throw new GiftWrapError(GiftWrapErrorKind.InvalidKey);\n }\n\n const aeadKey = hkdfKey(\n ecdhXCoordinate(ephemeralSecretKey, receiverPublicKey),\n REQUEST_INFO\n );\n const inner = concat([sharedSecret, plaintext]);\n\n return concat([\n Uint8Array.of(GIFTWRAP_TYPE_REQUEST),\n ephemeralPublicKey,\n encryptWithNonce(aeadKey, inner, nonce),\n ]);\n}\n\n/**\n * Seal `plaintext` (an encoded envelope) to `receiverPublicKey`. A fresh\n * ephemeral key pair, shared secret and nonce are drawn for this call alone,\n * so no two sealed requests — even to the same receiver, even with the same\n * plaintext — share any of the three.\n *\n * Returns the wire bytes and the shared secret. Mint the packet's execution\n * condition as {@link deriveCondition}({@link deriveFulfillment}(secret))\n * before sending: the terminating connector recovers the same secret by\n * opening this wrap, so the preimage it is paid against is one it derived, not\n * one anybody handed it.\n */\nexport function sealRequest(\n plaintext: Uint8Array,\n receiverPublicKey: Uint8Array\n): SealedRequest {\n const ephemeralSecretKey = secp256k1.utils.randomSecretKey();\n const sharedSecret = randomBytes(GIFTWRAP_SECRET_LENGTH);\n const nonce = randomBytes(GIFTWRAP_NONCE_LENGTH);\n\n return {\n wrapped: sealRequestWithRandomness(\n plaintext,\n receiverPublicKey,\n ephemeralSecretKey,\n sharedSecret,\n nonce\n ),\n sharedSecret,\n };\n}\n\n/**\n * Open a sealed request addressed to `identity`, recovering the encoded\n * envelope and the shared secret carried alongside it.\n *\n * `identity` is either a 32-byte secp256k1 secret key or a {@link GiftWrapEcdh}\n * that never exposes one. A node that is itself the BTP destination is the\n * party that calls this for real. It also makes the seal testable from both\n * ends — and the vectors' \"opening it with the fixture's secret key must\n * recover the envelope and the secret exactly\" a check this repo can actually\n * run.\n */\nexport function openRequest(\n bytes: Uint8Array,\n identity: Uint8Array | GiftWrapEcdh\n): OpenedRequest {\n if (bytes.length === 0) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n if (bytes[0] !== GIFTWRAP_TYPE_REQUEST) {\n throw new GiftWrapError(\n GiftWrapErrorKind.InvalidType,\n GIFTWRAP_TYPE_REQUEST\n );\n }\n if (bytes.length < 1 + GIFTWRAP_PUBLIC_KEY_LENGTH) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n\n const ephemeralPublicKey = bytes.slice(1, 1 + GIFTWRAP_PUBLIC_KEY_LENGTH);\n const ciphertext = bytes.subarray(1 + GIFTWRAP_PUBLIC_KEY_LENGTH);\n\n const agreement =\n identity instanceof Uint8Array ? localGiftWrapEcdh(identity) : identity;\n let ecdhSecret: Uint8Array;\n try {\n ecdhSecret = agreement.ecdh(ephemeralPublicKey);\n } catch (error) {\n throw error instanceof GiftWrapError\n ? error\n : new GiftWrapError(GiftWrapErrorKind.InvalidKey);\n }\n\n const plaintext = decrypt(hkdfKey(ecdhSecret, REQUEST_INFO), ciphertext);\n if (plaintext.length < GIFTWRAP_SECRET_LENGTH) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n\n return {\n envelopeBytes: plaintext.slice(GIFTWRAP_SECRET_LENGTH),\n sharedSecret: plaintext.slice(0, GIFTWRAP_SECRET_LENGTH),\n };\n}\n\n// ─── Response direction ─────────────────────────────────────────────────────\n\n/**\n * The deterministic core of {@link sealResponse}, parameterized on the nonce.\n * Same footgun, same warning as {@link sealRequestWithRandomness}: a nonce\n * reused under one shared secret destroys the confidentiality of both\n * responses sealed with it.\n */\nexport function sealResponseWithRandomness(\n sharedSecret: Uint8Array,\n plaintext: Uint8Array,\n nonce: Uint8Array\n): Uint8Array {\n if (sharedSecret.length !== GIFTWRAP_SECRET_LENGTH) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n if (nonce.length !== GIFTWRAP_NONCE_LENGTH) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n return concat([\n Uint8Array.of(GIFTWRAP_TYPE_RESPONSE),\n encryptWithNonce(hkdfKey(sharedSecret, RESPONSE_INFO), plaintext, nonce),\n ]);\n}\n\n/**\n * Seal `plaintext` — an encoded response envelope, or a reject's diagnostic\n * bytes — with the request's own `sharedSecret`. No second key exchange: the\n * secret is bidirectional by construction, which is also why a sealed reject\n * can only have come from the termination.\n */\nexport function sealResponse(\n sharedSecret: Uint8Array,\n plaintext: Uint8Array\n): Uint8Array {\n return sealResponseWithRandomness(\n sharedSecret,\n plaintext,\n randomBytes(GIFTWRAP_NONCE_LENGTH)\n );\n}\n\n/**\n * Open a sealed response with the shared secret {@link sealRequest} returned\n * for the request it answers. No signer, no key exchange, no identity key.\n */\nexport function openResponse(\n sharedSecret: Uint8Array,\n bytes: Uint8Array\n): Uint8Array {\n if (bytes.length === 0) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n if (bytes[0] !== GIFTWRAP_TYPE_RESPONSE) {\n throw new GiftWrapError(\n GiftWrapErrorKind.InvalidType,\n GIFTWRAP_TYPE_RESPONSE\n );\n }\n if (sharedSecret.length !== GIFTWRAP_SECRET_LENGTH) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n return decrypt(hkdfKey(sharedSecret, RESPONSE_INFO), bytes.subarray(1));\n}\n\n/**\n * Whether `bytes` is shaped like a sealed response — the leading type byte and\n * nothing else, exactly as `looks_like_sealed_response` does it. This is how a\n * sender tells a reject raised BY the destination from one raised on the way\n * to it, without holding the secret to try opening it.\n *\n * Empty `data`, which every reject raised short of a termination carries, is\n * never sealed.\n */\nexport function looksLikeSealedResponse(bytes: Uint8Array): boolean {\n return bytes.length > 0 && bytes[0] === GIFTWRAP_TYPE_RESPONSE;\n}\n\n// ─── Fulfilment and condition ───────────────────────────────────────────────\n\n/**\n * The fulfilment a request's shared secret derives (ADR 0019):\n * `HKDF-SHA256(shared_secret, \"toon-giftwrap-fulfillment\")`, 32 bytes.\n * Domain-separated by its own `info` string from both AEAD keys the same\n * secret also derives, so it can never collide with either.\n */\nexport function deriveFulfillment(sharedSecret: Uint8Array): Uint8Array {\n if (sharedSecret.length !== GIFTWRAP_SECRET_LENGTH) {\n throw new GiftWrapError(GiftWrapErrorKind.Truncated);\n }\n return hkdfKey(sharedSecret, FULFILLMENT_INFO);\n}\n\n/**\n * The condition a `fulfillment` satisfies: `sha256(fulfillment)` —\n * `connector_domain::condition::derive_condition`. Hashing only ever runs in\n * this direction; there is deliberately no function here that goes from a\n * condition back to a fulfilment.\n *\n * The hash is the same one `wire/condition.ts` already uses; what ADR 0019\n * changes is where the preimage comes from. `mintExecutionCondition` there\n * draws a random preimage the sender must then carry to the FULFILL itself;\n * here the preimage is DERIVED from the secret sealed inside the packet, so\n * the terminating connector can produce it without the sender or the app\n * handing it over. Checking a returned fulfilment against a condition is\n * unchanged either way — use `fulfillmentMatchesCondition` from\n * `wire/condition.ts` for that rather than a second copy of it here.\n */\nexport function deriveCondition(fulfillment: Uint8Array): Uint8Array {\n return sha256(fulfillment);\n}\n","/**\n * One sealed request/answer exchange with a terminating connector (ADR 0018,\n * ADR 0019, ADR 0020) — the whole shape of a packet's payload, from the\n * envelope going out to the answer coming back, behind two functions.\n *\n * A sender's obligations on this wire are not independent of one another, and\n * getting any of them separately wrong is silent:\n *\n * - The `data` is a gift wrap addressed to the connector that TERMINATES the\n * route, around an OER envelope — never HTTP text, never a bare payload.\n * - The execution condition is `sha256` of the fulfilment DERIVED from the\n * very secret that wrap carries. A random condition would never be\n * fulfilled; an all-zero one is refused outright by the connector\n * (`connector-domain`'s `condition_is_present`), which is why the publish\n * path could not work against it at all before this module existed.\n * - The answer is sealed with that same secret, so opening it requires having\n * kept it from the seal.\n *\n * {@link sealExchange} produces all three from one call so they cannot drift\n * apart, and {@link readExchangeOutcome} is the only thing that reads the\n * answer. The secret is the token joining them.\n *\n * ─── Why the outcome is a tagged union ─────────────────────────────────────\n * ADR 0018 gives a sender something it never had: a REJECT sealed with the\n * request's own secret can ONLY have come from the termination, because only\n * the termination could recover that secret. A reject raised short of it is\n * necessarily plaintext — an intermediate hop shares no secret and cannot\n * seal. So \"the destination said no\" and \"somebody on the path said no\" are\n * now genuinely different facts, and flattening both into one error string\n * would throw away the distinction the seal was introduced to create.\n *\n * ADR 0020 draws the other line this union encodes: an HTTP status inside a\n * response envelope is envelope CONTENT, not a packet outcome. A 404 arrives\n * on a FULFILL and value moved. {@link ExchangeOutcome.Answered} therefore\n * carries any status at all, and it is the caller's business — not this\n * module's — whether a given status means its own operation succeeded.\n *\n * Pure: no I/O, no clock, no network. Randomness only through\n * {@link sealRequest}.\n */\n\nimport {\n decodeEnvelopeResponse,\n encodeEnvelopeRequest,\n EnvelopeError,\n type EnvelopeRequest,\n type EnvelopeResponse,\n} from './envelope.js';\nimport {\n deriveCondition,\n deriveFulfillment,\n GiftWrapError,\n looksLikeSealedResponse,\n openResponse,\n sealRequest,\n} from './giftwrap.js';\n\n/**\n * A sealed request, ready to become a PREPARE: what goes in `data`, what goes\n * on `executionCondition`, and the secret needed to read the answer.\n *\n * The three are produced together and belong together. Sending `data` under a\n * condition minted any other way produces a packet no honest connector can\n * fulfil; opening the answer with any other secret fails.\n */\nexport interface SealedExchange {\n /** The gift wrap to carry as the PREPARE's `data`. */\n readonly data: Uint8Array;\n /**\n * The PREPARE's `executionCondition`: `sha256(deriveFulfillment(secret))`.\n * Derived, never random and never caller-supplied — the terminating\n * connector recovers the same secret and derives the same fulfilment\n * without the app participating (ADR 0019).\n */\n readonly condition: Uint8Array;\n /**\n * The 32-byte secret sealed inside the wrap. Keep it until the answer is\n * read; it is the only thing that opens the response. Never send it\n * anywhere else, and never reuse it for a second packet.\n */\n readonly sharedSecret: Uint8Array;\n /**\n * The fulfilment the terminating connector will return. Held so a caller\n * can check the FULFILL preimage it actually got, rather than only that it\n * hashes to the condition.\n */\n readonly fulfillment: Uint8Array;\n}\n\n/**\n * How an exchange ended. Three outcomes, deliberately not two: a refusal's\n * ORIGIN is the fact ADR 0018 makes knowable for the first time.\n */\nexport type ExchangeOutcome =\n /**\n * The termination answered. `response` is whatever it said, at whatever\n * status — a non-2xx here is a real answer that was paid for (ADR 0020),\n * not a failure of the packet.\n */\n | { readonly kind: 'answered'; readonly response: EnvelopeResponse }\n /**\n * The DESTINATION refused, provably: the reject's `data` was sealed with\n * this exchange's own secret, which only the terminating connector could\n * hold. `detail` is whatever it sealed alongside (often empty).\n */\n | {\n readonly kind: 'destination-refused';\n readonly code: string;\n readonly message: string;\n readonly detail: Uint8Array;\n }\n /**\n * Somebody ON THE PATH refused — no route, expiry, a ceiling, a rejected\n * claim — short of the termination, so nothing was sealed. Unauthenticated\n * by construction: any hop can produce this, and it should be read as a\n * hint, not a verdict from the destination.\n */\n | {\n readonly kind: 'path-refused';\n readonly code: string;\n readonly message: string;\n };\n\n/**\n * Why an answer that arrived could not be read. Distinct from an\n * {@link ExchangeOutcome}: these are not the destination's answer at all, but\n * bytes that failed to be one.\n */\nexport type SealedResponseErrorKind =\n /** A FULFILL whose `data` is absent, or does not begin with the sealed-response type byte. */\n | 'not-sealed'\n /** The wrap is shaped right but does not open under this exchange's secret. */\n | 'unopenable'\n /** It opened, and what came out is not a response envelope. */\n | 'malformed-envelope';\n\n/** A refusal to read an answer. Never carries recovered plaintext. */\nexport class SealedResponseError extends Error {\n constructor(\n readonly kind: SealedResponseErrorKind,\n message: string,\n override readonly cause?: Error\n ) {\n super(message);\n this.name = 'SealedResponseError';\n }\n}\n\n/**\n * Seal `request` to `connectorPublicKey` — the identity of the connector that\n * TERMINATES the destination, as read from its own client edge\n * (`ConnectorEdgeClient`). Sealing to any other key is a confidentiality\n * failure that presents as an undeliverable packet.\n *\n * @param request the envelope to seal.\n * @param connectorPublicKey the terminating connector's 65-byte uncompressed\n * secp256k1 identity key.\n */\nexport function sealExchange(\n request: EnvelopeRequest,\n connectorPublicKey: Uint8Array\n): SealedExchange {\n const { wrapped, sharedSecret } = sealRequest(\n encodeEnvelopeRequest(request),\n connectorPublicKey\n );\n const fulfillment = deriveFulfillment(sharedSecret);\n return {\n data: wrapped,\n condition: deriveCondition(fulfillment),\n sharedSecret,\n fulfillment,\n };\n}\n\n/**\n * Read what came back.\n *\n * `accepted` distinguishes a FULFILL from a REJECT; `data` is the packet's\n * payload bytes, absent when it carried none. Both come straight off the\n * transport's result, so this stays testable without a socket.\n *\n * A FULFILL must be sealed and must decode: value moved, so bytes that are\n * neither are a broken counterparty, and this throws rather than inventing an\n * outcome. A REJECT is classified instead of thrown on — an unopenable one is\n * simply a path reject that happened to carry bytes.\n *\n * @throws {SealedResponseError} for a FULFILL that is not a readable sealed\n * response envelope.\n */\nexport function readExchangeOutcome(\n result: { accepted: boolean; code?: string; message?: string },\n data: Uint8Array | undefined,\n sharedSecret: Uint8Array\n): ExchangeOutcome {\n if (result.accepted) {\n return { kind: 'answered', response: openAnswer(data, sharedSecret) };\n }\n\n const code = result.code ?? 'F00';\n const message = result.message ?? '';\n\n // Only the termination could have sealed this, so only a wrap that actually\n // OPENS proves the destination refused. A leading type byte alone is a\n // claim, not evidence — an intermediary can write one byte.\n if (data !== undefined && looksLikeSealedResponse(data)) {\n try {\n return {\n kind: 'destination-refused',\n code,\n message,\n detail: openResponse(sharedSecret, data),\n };\n } catch (error) {\n if (!(error instanceof GiftWrapError)) throw error;\n // Fall through: bytes that pose as sealed but do not open are not the\n // destination's word, and must not be reported as if they were.\n }\n }\n\n return { kind: 'path-refused', code, message };\n}\n\nfunction openAnswer(\n data: Uint8Array | undefined,\n sharedSecret: Uint8Array\n): EnvelopeResponse {\n if (data === undefined || data.length === 0) {\n throw new SealedResponseError(\n 'not-sealed',\n 'FULFILL carried no data; a terminated route always seals a response envelope'\n );\n }\n if (!looksLikeSealedResponse(data)) {\n throw new SealedResponseError(\n 'not-sealed',\n `FULFILL data is not a sealed response (leading byte 0x${(data[0] ?? 0)\n .toString(16)\n .padStart(2, '0')}, expected 0x02)`\n );\n }\n\n let opened: Uint8Array;\n try {\n opened = openResponse(sharedSecret, data);\n } catch (error) {\n if (!(error instanceof GiftWrapError)) throw error;\n throw new SealedResponseError(\n 'unopenable',\n \"FULFILL data did not open under this packet's shared secret\",\n error\n );\n }\n\n try {\n return decodeEnvelopeResponse(opened);\n } catch (error) {\n if (!(error instanceof EnvelopeError)) throw error;\n throw new SealedResponseError(\n 'malformed-envelope',\n `sealed answer opened but did not decode as a response envelope: ${error.message}`,\n error\n );\n }\n}\n\n/** Read a response header, case-insensitively; the first match wins. */\nexport function envelopeHeader(\n response: EnvelopeResponse,\n name: string\n): string | undefined {\n const wanted = name.toLowerCase();\n for (const [header, value] of response.headers) {\n if (header.toLowerCase() === wanted) return value;\n }\n return undefined;\n}\n"],"mappings":"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","names":["OerErrorKind","EnvelopeErrorKind","GiftWrapErrorKind","concat","UTF8_ENCODER"]}
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { I as InvalidEventError, P as PeerDiscoveryError, a as ToonDecodeError,
3
3
  import { NostrEvent, Event } from 'nostr-tools/pure';
4
4
  import { SimplePool } from 'nostr-tools/pool';
5
5
  import { TurboAuthenticatedClient } from '@ardrive/turbo-sdk';
6
- export { balanceProofFieldsMina, balanceProofHashEvm, balanceProofHashSolana, bigintToBytes32BE, concatBytes, coopCloseHashEvm, eip712DomainSeparatorEvm, hexToBytes, minaHashToField } from '@toon-protocol/settlement-digest';
6
+ export { SOLANA_BALANCE_PROOF_MESSAGE_SIZE, balanceProofFieldsMina, balanceProofHashEvm, balanceProofHashSolana, balanceProofMessageSolana, bigintToBytes32BE, concatBytes, coopCloseHashEvm, eip712DomainSeparatorEvm, hexToBytes, minaHashToField } from '@toon-protocol/settlement-digest';
7
7
 
8
8
  /**
9
9
  * Nostr event kind constants for ILP-related events.
@@ -478,6 +478,21 @@ interface IlpPeerInfo {
478
478
  };
479
479
  /** Token pairs this peer can swap, with current rates. Absent = no swap support. */
480
480
  swapPairs?: SwapPair[];
481
+ /**
482
+ * Optional operator notice. A POINTER, not the payload: the durable text lives
483
+ * in the operator's repo and this carries only enough to decide whether to go
484
+ * read it. Absent on almost every announce. Parsed leniently — a malformed or
485
+ * partial notice is dropped rather than failing the whole announce (toon#183).
486
+ */
487
+ notice?: {
488
+ /** Stable per notice, so a consumer can show it once and never again. */
489
+ id: string;
490
+ severity: 'info' | 'action-required';
491
+ /** One line, plain. Not the notice — the reason to open it. */
492
+ summary: string;
493
+ /** Where the durable text lives. Carried as-is; core performs no I/O on it. */
494
+ url: string;
495
+ };
481
496
  }
482
497
  /**
483
498
  * Declarative advertisement of a token swap pair supported by a swap-capable peer (Swap).
@@ -2345,6 +2360,14 @@ interface IlpClient {
2345
2360
  * the transport applies its default (timeout-derived).
2346
2361
  */
2347
2362
  expiresAt?: string;
2363
+ /**
2364
+ * The PREPARE's execution condition, base64-encoded (32 bytes) — the
2365
+ * transport MUST forward it verbatim onto the outgoing packet. An absent
2366
+ * or all-zero condition is refused outright by a Rust connector (toon#143),
2367
+ * so a caller sending a sealed-wire packet (see `@toon-protocol/core/wire`)
2368
+ * always sets this.
2369
+ */
2370
+ executionCondition?: string;
2348
2371
  }): Promise<IlpSendResult>;
2349
2372
  /**
2350
2373
  * Optional: Send ILP packet with signed balance proof claim (BTP only).
@@ -2357,12 +2380,50 @@ interface IlpClient {
2357
2380
  timeout?: number;
2358
2381
  /** Per-packet expiry as an ISO 8601 string (see sendIlpPacket). */
2359
2382
  expiresAt?: string;
2383
+ /** The PREPARE's execution condition, base64-encoded (see sendIlpPacket). */
2384
+ executionCondition?: string;
2360
2385
  }, claim: unknown): Promise<IlpSendResult>;
2361
2386
  }
2362
2387
  /**
2363
2388
  * @deprecated Use IlpClient instead
2364
2389
  */
2365
2390
  type AgentRuntimeClient = IlpClient;
2391
+ /**
2392
+ * The terminating connector's own identity, as `GET /ilp/identity` reports it.
2393
+ */
2394
+ interface ConnectorEdgeIdentity {
2395
+ /** Uncompressed secp256k1 public key — exactly 65 raw bytes, leading 0x04. */
2396
+ publicKey: Uint8Array;
2397
+ }
2398
+ /**
2399
+ * What a locally-terminated route costs, as `GET /ilp/routes/price` reports it.
2400
+ */
2401
+ interface ConnectorEdgeRoutePrice {
2402
+ /** The price in ILP base units of the route asked about. */
2403
+ price: bigint;
2404
+ }
2405
+ /**
2406
+ * A sibling port to {@link IlpClient} (toon#143): who a destination's
2407
+ * terminating connector is, and what it charges. ADR 0018 makes holding that
2408
+ * connector's public key a precondition of forming a packet at all — a
2409
+ * sealed-wire payload (`@toon-protocol/core/wire`'s `sealExchange`) cannot be
2410
+ * built without it — and ADR 0020 makes the price something to ASK for, never
2411
+ * computed locally.
2412
+ *
2413
+ * Structural, like every other port in this file: a caller's own
2414
+ * `ConnectorEdgeClient` (resolving `destination` to the terminating
2415
+ * connector's client-edge endpoint first, per ADR 0022) satisfies this
2416
+ * without `core` importing it.
2417
+ */
2418
+ interface ConnectorEdgeLookup {
2419
+ /** The identity of the connector that terminates `destination`. */
2420
+ getIdentity(destination: string): Promise<ConnectorEdgeIdentity>;
2421
+ /**
2422
+ * What sending to `destination` costs, flat per handler (ADR 0020), or
2423
+ * `null` when nothing terminated here matches it.
2424
+ */
2425
+ getRoutePrice(destination: string): Promise<ConnectorEdgeRoutePrice | null>;
2426
+ }
2366
2427
  /**
2367
2428
  * Own settlement configuration for local chain selection during registration.
2368
2429
  */
@@ -2403,8 +2464,21 @@ interface BootstrapServiceConfig extends BootstrapConfig {
2403
2464
  toonDecoder?: (bytes: Uint8Array) => NostrEvent;
2404
2465
  /** Static BTP secret for initial peer registration (before settlement) */
2405
2466
  btpSecret?: string;
2406
- /** Base price per byte for ILP packet pricing (default: 10n) */
2467
+ /**
2468
+ * @deprecated No longer multiplied by the announce payload's byte length
2469
+ * (toon#143 — ADR 0020 makes the announce packet's amount something ASKED
2470
+ * for via {@link connectorEdgeLookup}, not computed locally). When set, it
2471
+ * is used verbatim as an explicit override of the announce amount instead
2472
+ * of the asked-for route price; leave unset to always ask.
2473
+ */
2407
2474
  basePricePerByte?: bigint;
2475
+ /**
2476
+ * Identity + price lookup for the connector terminating a peer's
2477
+ * `ilpAddress` (toon#143). Required for `announceViaIlp` to seal its
2478
+ * packet (ADR 0018/0019/0020); when absent, the announce phase is skipped
2479
+ * entirely rather than sending an unsealed packet with no condition.
2480
+ */
2481
+ connectorEdgeLookup?: ConnectorEdgeLookup;
2408
2482
  }
2409
2483
 
2410
2484
  /**
@@ -2591,7 +2665,9 @@ declare class BootstrapService {
2591
2665
  private readonly ownIlpAddress?;
2592
2666
  private readonly toonEncoder?;
2593
2667
  private readonly toonDecoder?;
2594
- private readonly basePricePerByte;
2668
+ /** @deprecated see {@link BootstrapServiceConfig.basePricePerByte}. */
2669
+ private readonly basePricePerByte?;
2670
+ private readonly connectorEdgeLookup?;
2595
2671
  private listeners;
2596
2672
  private phase;
2597
2673
  /**
@@ -2671,6 +2747,17 @@ declare class BootstrapService {
2671
2747
  * @throws BootstrapError if pubkey is invalid or peer info query fails
2672
2748
  */
2673
2749
  bootstrapWithPeer(knownPeer: KnownPeer): Promise<BootstrapResult>;
2750
+ /**
2751
+ * What the announce PREPARE should carry as its amount.
2752
+ *
2753
+ * ASKED for (ADR 0020, flat per handler), never computed as bytes × rate.
2754
+ * `basePricePerByte`, when a caller still sets it, is a deprecated explicit
2755
+ * override rather than a per-byte rate — see its doc comment on
2756
+ * {@link BootstrapServiceConfig}.
2757
+ *
2758
+ * @throws {BootstrapError} when nothing terminated here prices `destination`.
2759
+ */
2760
+ private resolveAnnounceAmount;
2674
2761
  /**
2675
2762
  * Announce own kind:10032 as paid ILP PREPARE (Phase 2).
2676
2763
  */
@@ -2833,6 +2920,8 @@ interface SendPacketParams {
2833
2920
  * if needed.
2834
2921
  */
2835
2922
  expiresAt?: Date;
2923
+ /** The PREPARE's execution condition, 32 bytes (not base64 string). */
2924
+ executionCondition?: Uint8Array;
2836
2925
  }
2837
2926
  /**
2838
2927
  * Result returned by ConnectorNode.sendPacket().
@@ -3360,8 +3449,17 @@ interface ToonNodeConfig {
3360
3449
  knownPeers?: KnownPeer[];
3361
3450
  /** Optional settlement preferences for peer registration */
3362
3451
  settlementInfo?: SettlementConfig;
3363
- /** Base price per byte for ILP packet pricing (default: 10n) */
3452
+ /**
3453
+ * @deprecated No longer multiplied by the announce payload's byte length
3454
+ * (toon#143). See {@link BootstrapServiceConfig.basePricePerByte}.
3455
+ */
3364
3456
  basePricePerByte?: bigint;
3457
+ /**
3458
+ * Identity + price lookup for the connector terminating a peer's
3459
+ * `ilpAddress` (toon#143). Required for the bootstrap announce phase to
3460
+ * seal its packet; when absent, the announce phase is skipped.
3461
+ */
3462
+ connectorEdgeLookup?: ConnectorEdgeLookup;
3365
3463
  /** Enable ArDrive peer lookup (default: true) */
3366
3464
  ardriveEnabled?: boolean;
3367
3465
  /** Default relay URL for ArDrive-sourced peers that lack relay URLs (default: '') */
@@ -3581,7 +3679,8 @@ declare const MOCK_USDC_CONFIG: MockUsdcConfig;
3581
3679
  *
3582
3680
  * Provides chain presets for three blockchain families:
3583
3681
  * - **EVM**: Anvil (local dev), Arbitrum Sepolia (staging), Arbitrum One (production)
3584
- * - **Solana**: solana-devnet (local dev)
3682
+ * - **Solana**: solana-devnet (local dev), solana-mainnet (production,
3683
+ * carries the deployed payment-channel program id)
3585
3684
  * - **Mina**: mina-devnet (local dev)
3586
3685
  *
3587
3686
  * Environment variable overrides:
@@ -3607,7 +3706,7 @@ type ChainName = 'anvil' | 'arbitrum-sepolia' | 'arbitrum-one' | 'base-sepolia'
3607
3706
  /**
3608
3707
  * Supported Solana chain preset names.
3609
3708
  */
3610
- type SolanaChainName = 'solana-devnet';
3709
+ type SolanaChainName = 'solana-devnet' | 'solana-mainnet';
3611
3710
  /**
3612
3711
  * Supported Mina chain preset names.
3613
3712
  */
@@ -3786,7 +3885,9 @@ declare const MINA_CHAIN_PRESETS: Record<MinaChainName, MinaChainPreset>;
3786
3885
  *
3787
3886
  * Environment variable overrides:
3788
3887
  * - `SOLANA_RPC_URL` overrides the preset's rpcUrl
3789
- * - `SOLANA_PROGRAM_ID` overrides the preset's programId
3888
+ * - `SOLANA_PROGRAM_ID` overrides the preset's programId. Required in practice
3889
+ * for `solana-devnet`, which ships an empty programId -- see the comment on
3890
+ * that preset for why no fixed default is meaningful there.
3790
3891
  *
3791
3892
  * @param name - Solana chain preset name
3792
3893
  * @returns Resolved Solana chain preset
@@ -4561,4 +4662,4 @@ declare function selectLatestAddressable<T extends Event>(events: T[]): T | unde
4561
4662
  */
4562
4663
  declare const VERSION = "0.1.0";
4563
4664
 
4564
- export { AddressRegistry, type AgentRuntimeClient, ArDrivePeerRegistry, AttestationBootstrap, type AttestationBootstrapConfig, type AttestationBootstrapEvent, type AttestationBootstrapEventListener, type AttestationBootstrapResult, type AttestationEventOptions, AttestationState, AttestationVerifier, type AttestationVerifierConfig, type AttestedResultVerificationOptions, type AttestedResultVerificationResult, AttestedResultVerifier, BLOB_STORAGE_REQUEST_KIND, BLOB_STORAGE_RESULT_KIND, type BlobStorageRequestParams, type BootstrapConfig, BootstrapError, type BootstrapEvent, type BootstrapEventListener, type BootstrapPhase, type BootstrapResult, BootstrapService, type BootstrapServiceConfig, type BtpHandshakeExtension, type BuildIlpPeerInfoOptions, type BuildIlpPrepareParams, CHAIN_PRESETS, type CalculateRouteAmountParams, type ChainName, type ChainPreset, type ChainProviderConfigEntry, type ChainType, type ChannelState, type ClientNetworkPresets, type ConnectorAdminClient, type ConnectorAdminLike, type ConnectorChannelClient, type ConnectorChannelLike, type ConnectorNodeLike, type CustomEndpoints, type DeriveFromKmsSeedOptions, type DeterminismReport, type DirectRuntimeClientConfig, type DiscoveredPeer, type DiscoveryTracker, type DiscoveryTrackerConfig, type DvmJobStatus, type EVMProviderConfigEntry, EXPIRATION_TAG, type EmbeddableConnectorLike, type ForbiddenPattern, type GenesisPeer, GenesisPeerLoader, type HandlePacketAcceptResponse, type HandlePacketRejectResponse, type HandlePacketRequest, type HandlePacketResponse, ILP_PEER_INFO_KIND, ILP_ROOT_PREFIX, ILP_TO_SEMANTIC, IMAGE_GENERATION_KIND, type IlpClient, type IlpPeerInfo, type IlpPreparePacket, type IlpSendResult, JOB_FEEDBACK_KIND, JOB_REQUEST_KIND_BASE, JOB_RESULT_KIND_BASE, JOB_REVIEW_KIND, type JobFeedbackParams, type JobRequestParams, type JobResultParams, type JobReviewParams, KmsIdentityError, type KmsKeypair, type KnownPeer, type LogEntry, type LogLevel, type Logger, type LoggerConfig, MINA_CHAIN_PRESETS, MOCK_USDC_ADDRESS, MOCK_USDC_CONFIG, type MinaChainName, type MinaChainPreset, type MinaProviderConfigEntry, type MockUsdcConfig, type MultiChainName, type NetworkFamilyStatus, type NetworkMode, type NetworkNodeEnv, type NetworkProfile, type NixBuildResult, NixBuilder, type NixBuilderConfig, NostrPeerDiscovery, type OpenChannelParams, type OpenChannelResult, PET_INTERACTION_EVENT_KIND, PET_INTERACTION_REQUEST_KIND, PET_INTERACTION_RESULT_KIND, PREFIX_CLAIM_KIND, PREFIX_GRANT_KIND, type PacketHandler, type ParsedAttestation, type ParsedBlobStorageRequest, type ParsedJobFeedback, type ParsedJobRequest, type ParsedJobResult, type ParsedJobReview, type ParsedSwarmRequest, type ParsedSwarmSelection, type ParsedWorkflowDefinition, type ParsedWotDeclaration, PcrReproducibilityError, type PcrReproducibilityResult, type PeerDescriptor, type PrefixClaimContent, type PrefixGrantContent, type PrefixValidationResult, type PublishSeedRelayConfig, RELAY_ONLY_CHAIN, type RegisterPeerParams, type ReputationScore, ReputationScoreCalculator, type ReputationSignals, type ResolveRouteFeesParams, type ResolveRouteFeesResult, SEED_RELAY_LIST_KIND, SERVICE_DISCOVERY_KIND, SOLANA_CHAIN_PRESETS, SeedRelayDiscovery, type SeedRelayDiscoveryConfig, type SeedRelayDiscoveryResult, type SeedRelayEntry, type SendPacketParams, type SendPacketResult, type ServiceDiscoveryContent, type SettlementConfig, type SkillDescriptor, type SocialDiscoveryEvent, type SocialDiscoveryEventListener, SocialPeerDiscovery, type SocialPeerDiscoveryConfig, type SolanaChainName, type SolanaChainPreset, type SolanaProviderConfigEntry, type Subscription, type SwapPair, type SwarmRequestParams, type SwarmSelectionParams, TEE_ATTESTATION_KIND, TEXT_GENERATION_KIND, TEXT_TO_SPEECH_KIND, TRANSLATION_KIND, type TeeAttestation, ToonError, type ToonNode, type ToonNodeConfig, type ToonNodeStartResult, UI_RENDERER_KIND, UI_TAG, USDC_DECIMALS, USDC_NAME, USDC_SYMBOL, type UiCoordinate, VERSION, type VerificationResult, type VerifyOptions, type Violation, WEB_OF_TRUST_KIND, WORKFLOW_CHAIN_KIND, type WorkflowDefinitionParams, type WorkflowStep, type WotDeclarationParams, analyzeDockerfileForNonDeterminism, assignAddressFromHandshake, base58Decode, base58Encode, buildAttestationEvent, buildBlobStorageRequest, buildEip712Domain, buildEvmProviderEntry, buildIlpPeerInfoEvent, buildIlpPrepare, buildJobFeedbackEvent, buildJobRequestEvent, buildJobResultEvent, buildJobReviewEvent, buildMinaProviderEntry, buildPrefixClaimEvent, buildPrefixGrantEvent, buildPrefixHandshakeData, buildSeedRelayListEvent, buildServiceDiscoveryEvent, buildSolanaProviderEntry, buildSwarmRequestEvent, buildSwarmSelectionEvent, buildUiCoordinate, buildWorkflowDefinitionEvent, buildWotDeclarationEvent, calculateRouteAmount, checkAddressCollision, createAgentRuntimeClient, createDirectChannelClient, createDirectConnectorAdmin, createDirectIlpClient, createDirectRuntimeClient, createDiscoveryTracker, createHttpChannelClient, createHttpConnectorAdmin, createHttpIlpClient$1 as createHttpIlpClient, createHttpRuntimeClient, createHttpIlpClient as createHttpRuntimeClientV2, createLogger, createToonNode, deriveChildAddress, deriveFromKmsSeed, deriveMinaPublicKeyBase58, extractPrefixFromHandshake, getEventExpiration, getUiCoordinate, hasMinReputation, hasRequireAttestation, hexToMinaBase58PrivateKey, ilpCodeToSemantic, isEventExpired, isGenesisNode, isValidIlpAddressStructure, negotiateSettlementChain, parseAttestation, parseBlobStorageRequest, parseIlpPeerInfo, parseJobFeedback, parseJobRequest, parseJobResult, parseJobReview, parsePrefixClaimEvent, parsePrefixGrantEvent, parseSeedRelayList, parseServiceDiscovery, parseSwarmRequest, parseSwarmSelection, parseUiCoordinate, parseWorkflowDefinition, parseWotDeclaration, publishSeedRelayEntry, readDockerfileNix, resolveChainConfig, resolveClientNetwork, resolveMinaChainConfig, resolveNetworkProfile, resolveRouteFees, resolveSolanaChainConfig, resolveTokenForChain, selectLatestAddressable, validateChainId, validateIlpAddress, validatePrefix, validatePrefixConsistency, verifyPcrReproducibility };
4665
+ export { AddressRegistry, type AgentRuntimeClient, ArDrivePeerRegistry, AttestationBootstrap, type AttestationBootstrapConfig, type AttestationBootstrapEvent, type AttestationBootstrapEventListener, type AttestationBootstrapResult, type AttestationEventOptions, AttestationState, AttestationVerifier, type AttestationVerifierConfig, type AttestedResultVerificationOptions, type AttestedResultVerificationResult, AttestedResultVerifier, BLOB_STORAGE_REQUEST_KIND, BLOB_STORAGE_RESULT_KIND, type BlobStorageRequestParams, type BootstrapConfig, BootstrapError, type BootstrapEvent, type BootstrapEventListener, type BootstrapPhase, type BootstrapResult, BootstrapService, type BootstrapServiceConfig, type BtpHandshakeExtension, type BuildIlpPeerInfoOptions, type BuildIlpPrepareParams, CHAIN_PRESETS, type CalculateRouteAmountParams, type ChainName, type ChainPreset, type ChainProviderConfigEntry, type ChainType, type ChannelState, type ClientNetworkPresets, type ConnectorAdminClient, type ConnectorAdminLike, type ConnectorChannelClient, type ConnectorChannelLike, type ConnectorEdgeIdentity, type ConnectorEdgeLookup, type ConnectorEdgeRoutePrice, type ConnectorNodeLike, type CustomEndpoints, type DeriveFromKmsSeedOptions, type DeterminismReport, type DirectRuntimeClientConfig, type DiscoveredPeer, type DiscoveryTracker, type DiscoveryTrackerConfig, type DvmJobStatus, type EVMProviderConfigEntry, EXPIRATION_TAG, type EmbeddableConnectorLike, type ForbiddenPattern, type GenesisPeer, GenesisPeerLoader, type HandlePacketAcceptResponse, type HandlePacketRejectResponse, type HandlePacketRequest, type HandlePacketResponse, ILP_PEER_INFO_KIND, ILP_ROOT_PREFIX, ILP_TO_SEMANTIC, IMAGE_GENERATION_KIND, type IlpClient, type IlpPeerInfo, type IlpPreparePacket, type IlpSendResult, JOB_FEEDBACK_KIND, JOB_REQUEST_KIND_BASE, JOB_RESULT_KIND_BASE, JOB_REVIEW_KIND, type JobFeedbackParams, type JobRequestParams, type JobResultParams, type JobReviewParams, KmsIdentityError, type KmsKeypair, type KnownPeer, type LogEntry, type LogLevel, type Logger, type LoggerConfig, MINA_CHAIN_PRESETS, MOCK_USDC_ADDRESS, MOCK_USDC_CONFIG, type MinaChainName, type MinaChainPreset, type MinaProviderConfigEntry, type MockUsdcConfig, type MultiChainName, type NetworkFamilyStatus, type NetworkMode, type NetworkNodeEnv, type NetworkProfile, type NixBuildResult, NixBuilder, type NixBuilderConfig, NostrPeerDiscovery, type OpenChannelParams, type OpenChannelResult, PET_INTERACTION_EVENT_KIND, PET_INTERACTION_REQUEST_KIND, PET_INTERACTION_RESULT_KIND, PREFIX_CLAIM_KIND, PREFIX_GRANT_KIND, type PacketHandler, type ParsedAttestation, type ParsedBlobStorageRequest, type ParsedJobFeedback, type ParsedJobRequest, type ParsedJobResult, type ParsedJobReview, type ParsedSwarmRequest, type ParsedSwarmSelection, type ParsedWorkflowDefinition, type ParsedWotDeclaration, PcrReproducibilityError, type PcrReproducibilityResult, type PeerDescriptor, type PrefixClaimContent, type PrefixGrantContent, type PrefixValidationResult, type PublishSeedRelayConfig, RELAY_ONLY_CHAIN, type RegisterPeerParams, type ReputationScore, ReputationScoreCalculator, type ReputationSignals, type ResolveRouteFeesParams, type ResolveRouteFeesResult, SEED_RELAY_LIST_KIND, SERVICE_DISCOVERY_KIND, SOLANA_CHAIN_PRESETS, SeedRelayDiscovery, type SeedRelayDiscoveryConfig, type SeedRelayDiscoveryResult, type SeedRelayEntry, type SendPacketParams, type SendPacketResult, type ServiceDiscoveryContent, type SettlementConfig, type SkillDescriptor, type SocialDiscoveryEvent, type SocialDiscoveryEventListener, SocialPeerDiscovery, type SocialPeerDiscoveryConfig, type SolanaChainName, type SolanaChainPreset, type SolanaProviderConfigEntry, type Subscription, type SwapPair, type SwarmRequestParams, type SwarmSelectionParams, TEE_ATTESTATION_KIND, TEXT_GENERATION_KIND, TEXT_TO_SPEECH_KIND, TRANSLATION_KIND, type TeeAttestation, ToonError, type ToonNode, type ToonNodeConfig, type ToonNodeStartResult, UI_RENDERER_KIND, UI_TAG, USDC_DECIMALS, USDC_NAME, USDC_SYMBOL, type UiCoordinate, VERSION, type VerificationResult, type VerifyOptions, type Violation, WEB_OF_TRUST_KIND, WORKFLOW_CHAIN_KIND, type WorkflowDefinitionParams, type WorkflowStep, type WotDeclarationParams, analyzeDockerfileForNonDeterminism, assignAddressFromHandshake, base58Decode, base58Encode, buildAttestationEvent, buildBlobStorageRequest, buildEip712Domain, buildEvmProviderEntry, buildIlpPeerInfoEvent, buildIlpPrepare, buildJobFeedbackEvent, buildJobRequestEvent, buildJobResultEvent, buildJobReviewEvent, buildMinaProviderEntry, buildPrefixClaimEvent, buildPrefixGrantEvent, buildPrefixHandshakeData, buildSeedRelayListEvent, buildServiceDiscoveryEvent, buildSolanaProviderEntry, buildSwarmRequestEvent, buildSwarmSelectionEvent, buildUiCoordinate, buildWorkflowDefinitionEvent, buildWotDeclarationEvent, calculateRouteAmount, checkAddressCollision, createAgentRuntimeClient, createDirectChannelClient, createDirectConnectorAdmin, createDirectIlpClient, createDirectRuntimeClient, createDiscoveryTracker, createHttpChannelClient, createHttpConnectorAdmin, createHttpIlpClient$1 as createHttpIlpClient, createHttpRuntimeClient, createHttpIlpClient as createHttpRuntimeClientV2, createLogger, createToonNode, deriveChildAddress, deriveFromKmsSeed, deriveMinaPublicKeyBase58, extractPrefixFromHandshake, getEventExpiration, getUiCoordinate, hasMinReputation, hasRequireAttestation, hexToMinaBase58PrivateKey, ilpCodeToSemantic, isEventExpired, isGenesisNode, isValidIlpAddressStructure, negotiateSettlementChain, parseAttestation, parseBlobStorageRequest, parseIlpPeerInfo, parseJobFeedback, parseJobRequest, parseJobResult, parseJobReview, parsePrefixClaimEvent, parsePrefixGrantEvent, parseSeedRelayList, parseServiceDiscovery, parseSwarmRequest, parseSwarmSelection, parseUiCoordinate, parseWorkflowDefinition, parseWotDeclaration, publishSeedRelayEntry, readDockerfileNix, resolveChainConfig, resolveClientNetwork, resolveMinaChainConfig, resolveNetworkProfile, resolveRouteFees, resolveSolanaChainConfig, resolveTokenForChain, selectLatestAddressable, validateChainId, validateIlpAddress, validatePrefix, validatePrefixConsistency, verifyPcrReproducibility };