@byollm/protocol 0.1.0-alpha.93 → 0.1.0-alpha.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { z } from 'zod';
2
+ import { S as StoredKeys, P as PublicIdentity } from './portable-D2mu8USa.js';
3
+ export { C as CONSOLE_FRAME_VERSION, a as CONSOLE_MAX_DATA_BYTES, b as ConsoleBye, c as ConsoleFrame, d as ConsoleHello, e as ConsoleOrder, f as ConsoleOrderFault, g as ConsoleOrderResult, h as ConsoleResize, i as ConsoleStdin, j as ConsoleStdout, E as ENCRYPTION_KEY_CONTEXT, k as ENVELOPE_BODY_VERSION, l as ENVELOPE_MAX_AGE_MS, m as EnvelopeBodyContext, n as EnvelopeContext, o as EnvelopeDirection, p as EnvelopeFailure, q as EnvelopeInner, O as OpenResult, r as SealedEnvelope, s as consoleEnvelope, t as consoleOrder, u as cryptoReady, v as decodeEnvelopeInner, w as encodeEnvelopeInner, x as envelopeSignedBody, y as fingerprint, z as fromBase64Url, A as generateKeys, B as keyId, D as open, F as publicIdentityOf, G as seal, H as signWith, I as toBase64Url, J as verifyPublicIdentity, K as verifyWith } from './portable-D2mu8USa.js';
2
4
 
3
5
  /**
4
6
  * Protocol version carried on every request; servers refuse what they can't
@@ -2073,174 +2075,6 @@ declare const ClaimedStub: z.ZodObject<{
2073
2075
  }, z.core.$strict>;
2074
2076
  type ClaimedStub = z.infer<typeof ClaimedStub>;
2075
2077
 
2076
- /**
2077
- * Device and site keys — byollm_009 §3.
2078
- *
2079
- * **Two keypairs per party, and the split is load-bearing.** An Ed25519
2080
- * *identity* key signs; an X25519 *encryption* key receives sealed envelopes.
2081
- * The encryption key is signed by the identity key, and **the identity key is
2082
- * what gets pinned**. So "who sent this" and "who can read this" are answered
2083
- * by different keys — which is what lets an encryption key rotate without
2084
- * re-establishing trust, and what byollm_009 §6's signed-then-sealed envelope
2085
- * depends on.
2086
- *
2087
- * **No new dependency.** byollm_009 §2 says established primitives only, via
2088
- * libsodium. Everything *this* module needs — Ed25519 signing, X25519 key
2089
- * generation — Node provides natively, and using it costs nothing and adds no
2090
- * install weight to a daemon that must land fast on a stranger's laptop.
2091
- *
2092
- * libsodium becomes necessary at envelope v2, where sealing does. That is a
2093
- * real dependency decision and it belongs in the change that needs it: a
2094
- * sealed box is a specific reviewed construction, and rebuilding it out of
2095
- * Node primitives is exactly the "novel construction" §2 rules out. Deferring
2096
- * the dependency is not the same as deferring the rule.
2097
- */
2098
- /** A public identity, as it travels on the wire. All values base64url. */
2099
- declare const PublicIdentity: z.ZodObject<{
2100
- identity: z.ZodString;
2101
- encryption: z.ZodString;
2102
- encryptionSig: z.ZodString;
2103
- }, z.core.$strict>;
2104
- type PublicIdentity = z.infer<typeof PublicIdentity>;
2105
- /** Private key material, as stored on disk. Never leaves the machine. */
2106
- declare const StoredKeys: z.ZodObject<{
2107
- version: z.ZodLiteral<1>;
2108
- identityPublic: z.ZodString;
2109
- identityPrivate: z.ZodString;
2110
- encryptionPublic: z.ZodString;
2111
- encryptionPrivate: z.ZodString;
2112
- encryptionSig: z.ZodString;
2113
- createdAt: z.ZodNumber;
2114
- }, z.core.$strict>;
2115
- type StoredKeys = z.infer<typeof StoredKeys>;
2116
- /** Domain separator, so a signature over an encryption key cannot be
2117
- * replayed as a signature over anything else. */
2118
- /**
2119
- * What an encryption key's signature covers.
2120
- *
2121
- * Exported because a rotation is a real event this protocol has to be able to
2122
- * *test* — a record whose encryption key moved under an identity that signed
2123
- * the move is the one case pinning must refuse loudly, and building one
2124
- * outside this file otherwise means re-typing this string, which is how two
2125
- * copies of a constant start disagreeing.
2126
- */
2127
- declare const ENCRYPTION_KEY_CONTEXT = "byollm/v1/encryption-key";
2128
- /** Generate a fresh pair of keypairs and bind them together. */
2129
- declare function generateKeys(now: number): StoredKeys;
2130
- /** The public half, for the wire. */
2131
- declare function publicIdentityOf(keys: StoredKeys): PublicIdentity;
2132
- /**
2133
- * Check that an encryption key really belongs to the identity presenting it.
2134
- *
2135
- * Called on everything received, including from an upstream we otherwise
2136
- * trust — the point of pinning the identity is that nothing else needs to be
2137
- * trusted, and that only holds if this is checked every time rather than at
2138
- * first sight.
2139
- */
2140
- declare function verifyPublicIdentity(identity: PublicIdentity): boolean;
2141
- /** Sign arbitrary bytes with an identity key. */
2142
- /**
2143
- * Sign bytes with an identity key.
2144
- *
2145
- * Takes only the private half it uses. A signer that demanded a whole
2146
- * {@link StoredKeys} would make every caller hold an encryption keypair for a
2147
- * job that has no encryption in it — and the control plane, which signs
2148
- * rosters and opens nothing, would be generating and storing secret material
2149
- * it can never need. Every existing caller passes a full `StoredKeys`, which
2150
- * satisfies this.
2151
- */
2152
- declare function signWith(keys: Pick<StoredKeys, "identityPrivate">, data: Uint8Array): string;
2153
- /** Verify bytes against a raw Ed25519 public key. */
2154
- declare function verifyWith(identityPublic: string, data: Uint8Array, signature: string): boolean;
2155
- /**
2156
- * A fingerprint a human can compare out loud.
2157
- *
2158
- * 120 bits of SHA-256 over the raw identity key, as six groups of four. Long
2159
- * enough that grinding a colliding key is not worth anyone's afternoon, short
2160
- * enough to read down a phone line — which is the whole point. A fingerprint
2161
- * nobody can be bothered to compare provides no security at all, so
2162
- * legibility is a security property here, not a nicety.
2163
- *
2164
- * Formatted with a `BYOLLM-` prefix so a pasted fingerprint is recognisable
2165
- * out of context, in a support thread or a screenshot.
2166
- */
2167
- declare function fingerprint(identityPublic: string): string;
2168
- /** The short id used in envelopes and provenance. Stable, and comparable. */
2169
- declare const keyId: (identityPublic: string) => string;
2170
-
2171
- declare function cryptoReady(): Promise<void>;
2172
- /**
2173
- * How long a sealed payload is worth keeping, from creation.
2174
- *
2175
- * Bound into every envelope and recomputed when one is opened, so it lives
2176
- * here rather than in the two places that need it. Two copies of a value the
2177
- * signature depends on is the same bug as two clock readings: it works until
2178
- * they disagree, and then nothing can be opened.
2179
- *
2180
- * Not a job's TTL. That answers how long the *work* is worth doing, belongs
2181
- * to the app and the store, and may legitimately differ per deployment.
2182
- */
2183
- declare const ENVELOPE_MAX_AGE_MS: number;
2184
- /** Which leg an envelope belongs to. Bound into the signature. */
2185
- declare const EnvelopeDirection: z.ZodEnum<{
2186
- payload: "payload";
2187
- result: "result";
2188
- }>;
2189
- type EnvelopeDirection = z.infer<typeof EnvelopeDirection>;
2190
- declare const SealedEnvelope: z.ZodObject<{
2191
- ciphertext: z.ZodString;
2192
- recipientKeyId: z.ZodString;
2193
- senderKeyId: z.ZodString;
2194
- direction: z.ZodEnum<{
2195
- payload: "payload";
2196
- result: "result";
2197
- }>;
2198
- deadlineAt: z.ZodNumber;
2199
- }, z.core.$strict>;
2200
- type SealedEnvelope = z.infer<typeof SealedEnvelope>;
2201
- /** Everything the signature covers besides the plaintext itself. */
2202
- interface EnvelopeContext {
2203
- readonly jobId: string;
2204
- readonly senderKeyId: string;
2205
- readonly recipientKeyId: string;
2206
- readonly deadlineAt: number;
2207
- readonly direction: EnvelopeDirection;
2208
- }
2209
- /** Seal a plaintext to a recipient, signed by the sender's identity. */
2210
- declare function seal(input: {
2211
- plaintext: string;
2212
- senderKeys: StoredKeys;
2213
- recipientEncryptionPublic: string;
2214
- context: EnvelopeContext;
2215
- }): Promise<SealedEnvelope>;
2216
- /** Why an envelope was refused. Never distinguished to a remote caller. */
2217
- type EnvelopeFailure = "not-for-us" | "unopenable" | "malformed" | "bad-signature" | "context-mismatch";
2218
- type OpenResult = {
2219
- readonly ok: true;
2220
- readonly plaintext: string;
2221
- } | {
2222
- readonly ok: false;
2223
- readonly reason: EnvelopeFailure;
2224
- };
2225
- /**
2226
- * Open an envelope and verify it came from the pinned sender.
2227
- *
2228
- * Every failure returns rather than throws: this runs on input from the
2229
- * network, and a crash here is a denial of service on the delivery path.
2230
- *
2231
- * The context is checked against the signature, not merely read from the
2232
- * envelope. An envelope carries its own claims about who sent it and to
2233
- * whom — believing those would authenticate the attacker's assertion rather
2234
- * than the sender's key.
2235
- */
2236
- declare function open(input: {
2237
- envelope: SealedEnvelope;
2238
- recipientKeys: StoredKeys;
2239
- senderIdentityPublic: string;
2240
- /** The deadline is taken from the envelope and checked against its signature. */
2241
- expected: Omit<EnvelopeContext, "deadlineAt">;
2242
- }): Promise<OpenResult>;
2243
-
2244
2078
  /**
2245
2079
  * Request signing — byollm_009 §4.2.
2246
2080
  *
@@ -2972,227 +2806,4 @@ declare const MUST_IDS: readonly ("PAIR_ONE_USER" | "PAIR_INTERACTIVE" | "PAIR_C
2972
2806
  /** Every MUST verified a particular way. */
2973
2807
  declare function mustsVerifiedBy(kind: MustVerification): MustId[];
2974
2808
 
2975
- /**
2976
- * The terminal stream a hosted box's console runs over — byollm_018 §"Console
2977
- * redesign: brokered E2E replaces SSH-first (RULED, Todd 2026-09-03)".
2978
- *
2979
- * The ruling's item 3 is the whole reason this file exists: *"The terminal
2980
- * stream is E2E encrypted browser↔agent with the sealed-channel pattern jobs
2981
- * use; the hub routes ciphertext."* So the bytes are carried by
2982
- * {@link seal}/{@link open} exactly as a job's payload is, and what is defined
2983
- * here is the thing a sealed envelope does not carry: **what a frame means,
2984
- * and where it sits in the stream.**
2985
- *
2986
- * ## Why an ordering rule is protocol and not an implementation detail
2987
- *
2988
- * Sealing makes the hub unable to READ the stream. It does nothing to make
2989
- * the hub unable to reshape it. A router that holds ciphertext can drop a
2990
- * frame, deliver two out of order, or send the same frame twice, and every
2991
- * envelope still opens, still verifies against the pinned identity, and still
2992
- * looks perfect to the receiver — because each envelope is authenticated on
2993
- * its own and says nothing about the ones around it.
2994
- *
2995
- * On a terminal that is not a theoretical loss of tidiness. A dropped
2996
- * keystroke is a different command; two frames swapped is a different command;
2997
- * a replayed frame is a command run twice. The console carries vendor
2998
- * sign-in flows (byollm_018 item 5), so "a different command" can mean an auth
2999
- * code going somewhere it was not typed.
3000
- *
3001
- * Hence {@link consoleOrder}: a sequence inside the ciphertext, checked by
3002
- * both ends. Inside, because a counter the hub could see is a counter the hub
3003
- * could rewrite.
3004
- *
3005
- * ## One definition, both ends, and there are three ends
3006
- *
3007
- * The browser, the box's agent, and the hub broker all handle these frames,
3008
- * and the first two must agree byte for byte or the stream silently corrupts.
3009
- * The hub must NOT be able to agree — it only ever sees the sealed envelope.
3010
- * This module is the single definition the two real ends share; neither
3011
- * retypes it.
3012
- *
3013
- * ## The pinned box key is the PAIRING key (CCB's call, 09-16)
3014
- *
3015
- * The ruling says "box key pinned at first session", and the backlog left one
3016
- * delta open: whether the identity the owner already approved at pairing can
3017
- * serve as that pinned key. It can, and it should, for three reasons.
3018
- *
3019
- * 1. **The pairing key is the only one a human ever chose.** `PairStartRequest`
3020
- * carries the device's {@link PublicIdentity} and says why: *"Pairing is
3021
- * where the two parties learn each other's identities, because it is the
3022
- * one moment a human is already deciding to trust: the approval click. A
3023
- * key exchanged anywhere else would be a key nobody chose."* A fresh
3024
- * trust-on-first-use pin at the first console session is precisely a key
3025
- * nobody chose — it trusts whoever answers first, which on a brokered
3026
- * channel is whoever the broker points at.
3027
- * 2. **Two pins of one box can disagree, and silently.** A console pin
3028
- * separate from the pairing pin is a second record of the same fact, which
3029
- * is this project's most-repeated defect. When they diverge, nothing reads
3030
- * both, so nothing notices.
3031
- * 3. **Alarm-on-change comes free from the split already in `keys.ts`.** The
3032
- * Ed25519 identity is the pinned half and the X25519 encryption key is
3033
- * signed by it, so {@link verifyPublicIdentity} lets the box rotate its
3034
- * encryption key without a new ceremony while an IDENTITY change fails to
3035
- * verify and alarms — which is what the ruling asks for, and it is already
3036
- * written.
3037
- *
3038
- * **What this does not claim.** Pinning the pairing key does not make the hub
3039
- * unable to lie about which key that was; the hub is the one telling the
3040
- * browser. The ruling is explicit that we build the operator-ACCOUNTABLE
3041
- * version, not the operator-incapable one, and item 4's session feed is where
3042
- * that accountability is paid. What the pin buys is that a CHANGE is
3043
- * detectable, and that the key being pinned is one an owner approved rather
3044
- * than one a broker supplied.
3045
- */
3046
- /** Domain tag for the plaintext inside a console envelope. */
3047
- declare const CONSOLE_FRAME_VERSION = "byollm/v1/console";
3048
- /**
3049
- * The largest `data` payload one frame may carry, before base64.
3050
- *
3051
- * Far below {@link MAX_ENVELOPE_BYTES}, and deliberately: an envelope cap
3052
- * stops a memory attack, while this stops a latency one. A console is
3053
- * interactive, so a peer that batches a megabyte into one frame has made the
3054
- * stream unusable without ever exceeding a limit.
3055
- */
3056
- declare const CONSOLE_MAX_DATA_BYTES: number;
3057
- declare const ConsoleHello: z.ZodObject<{
3058
- v: z.ZodLiteral<"byollm/v1/console">;
3059
- kind: z.ZodLiteral<"hello">;
3060
- seq: z.ZodLiteral<1>;
3061
- browser: z.ZodObject<{
3062
- identity: z.ZodString;
3063
- encryption: z.ZodString;
3064
- encryptionSig: z.ZodString;
3065
- }, z.core.$strict>;
3066
- cols: z.ZodNumber;
3067
- rows: z.ZodNumber;
3068
- }, z.core.$strict>;
3069
- type ConsoleHello = z.infer<typeof ConsoleHello>;
3070
- /**
3071
- * Two schemas rather than one with a `kind: z.enum([...])`, because the union
3072
- * below discriminates on `kind` and a discriminator that is itself a set is
3073
- * how a union quietly stops discriminating.
3074
- */
3075
- declare const ConsoleStdin: z.ZodObject<{
3076
- v: z.ZodLiteral<"byollm/v1/console">;
3077
- kind: z.ZodLiteral<"stdin">;
3078
- seq: z.ZodNumber;
3079
- data: z.ZodString;
3080
- }, z.core.$strict>;
3081
- type ConsoleStdin = z.infer<typeof ConsoleStdin>;
3082
- declare const ConsoleStdout: z.ZodObject<{
3083
- v: z.ZodLiteral<"byollm/v1/console">;
3084
- kind: z.ZodLiteral<"stdout">;
3085
- seq: z.ZodNumber;
3086
- data: z.ZodString;
3087
- }, z.core.$strict>;
3088
- type ConsoleStdout = z.infer<typeof ConsoleStdout>;
3089
- declare const ConsoleResize: z.ZodObject<{
3090
- v: z.ZodLiteral<"byollm/v1/console">;
3091
- kind: z.ZodLiteral<"resize">;
3092
- seq: z.ZodNumber;
3093
- cols: z.ZodNumber;
3094
- rows: z.ZodNumber;
3095
- }, z.core.$strict>;
3096
- type ConsoleResize = z.infer<typeof ConsoleResize>;
3097
- declare const ConsoleBye: z.ZodObject<{
3098
- v: z.ZodLiteral<"byollm/v1/console">;
3099
- kind: z.ZodLiteral<"bye">;
3100
- seq: z.ZodNumber;
3101
- reason: z.ZodString;
3102
- }, z.core.$strict>;
3103
- type ConsoleBye = z.infer<typeof ConsoleBye>;
3104
- declare const ConsoleFrame: z.ZodDiscriminatedUnion<[z.ZodObject<{
3105
- v: z.ZodLiteral<"byollm/v1/console">;
3106
- kind: z.ZodLiteral<"hello">;
3107
- seq: z.ZodLiteral<1>;
3108
- browser: z.ZodObject<{
3109
- identity: z.ZodString;
3110
- encryption: z.ZodString;
3111
- encryptionSig: z.ZodString;
3112
- }, z.core.$strict>;
3113
- cols: z.ZodNumber;
3114
- rows: z.ZodNumber;
3115
- }, z.core.$strict>, z.ZodObject<{
3116
- v: z.ZodLiteral<"byollm/v1/console">;
3117
- kind: z.ZodLiteral<"stdin">;
3118
- seq: z.ZodNumber;
3119
- data: z.ZodString;
3120
- }, z.core.$strict>, z.ZodObject<{
3121
- v: z.ZodLiteral<"byollm/v1/console">;
3122
- kind: z.ZodLiteral<"stdout">;
3123
- seq: z.ZodNumber;
3124
- data: z.ZodString;
3125
- }, z.core.$strict>, z.ZodObject<{
3126
- v: z.ZodLiteral<"byollm/v1/console">;
3127
- kind: z.ZodLiteral<"resize">;
3128
- seq: z.ZodNumber;
3129
- cols: z.ZodNumber;
3130
- rows: z.ZodNumber;
3131
- }, z.core.$strict>, z.ZodObject<{
3132
- v: z.ZodLiteral<"byollm/v1/console">;
3133
- kind: z.ZodLiteral<"bye">;
3134
- seq: z.ZodNumber;
3135
- reason: z.ZodString;
3136
- }, z.core.$strict>], "kind">;
3137
- type ConsoleFrame = z.infer<typeof ConsoleFrame>;
3138
- /**
3139
- * The envelope context for a console frame.
3140
- *
3141
- * Exported so that neither end computes it: the session id goes in `jobId`
3142
- * and the direction is derived, and if the two ends disagreed about either,
3143
- * every envelope would fail to open with `not-for-us` — a failure that looks
3144
- * exactly like an attack.
3145
- */
3146
- declare function consoleEnvelope(input: {
3147
- sessionId: string;
3148
- from: "browser" | "box";
3149
- senderKeyId: string;
3150
- recipientKeyId: string;
3151
- deadlineAt: number;
3152
- }): EnvelopeContext;
3153
- /** Why a frame was refused. Each one is a distinct thing a router can do. */
3154
- type ConsoleOrderFault =
3155
- /** Seq went backwards or repeated — the router sent it twice. */
3156
- "replayed"
3157
- /** Seq skipped — the router dropped what was between. */
3158
- | "gap"
3159
- /** A `hello` that was not the first frame, or a first frame that was not `hello`. */
3160
- | "out-of-turn"
3161
- /** A `stdin` from the box, or a `stdout` from the browser. */
3162
- | "wrong-way"
3163
- /** Anything after `bye`. The stream is over. */
3164
- | "closed";
3165
- type ConsoleOrderResult = {
3166
- readonly ok: true;
3167
- } | {
3168
- readonly ok: false;
3169
- readonly fault: ConsoleOrderFault;
3170
- };
3171
- /**
3172
- * One direction's ordering rule, which both ends run over what they receive.
3173
- *
3174
- * **Strictly +1, not merely increasing.** Increasing would catch replay and
3175
- * reorder while letting a DROP through silently, and a dropped frame on a
3176
- * terminal is a truncated command that still runs. There is no benign gap
3177
- * here to tolerate: the transport underneath is an ordered, reliable stream,
3178
- * so a gap means something between the ends removed a frame.
3179
- *
3180
- * Every fault is fatal to the session by design. A console has no
3181
- * resynchronisation story that is safe — you cannot ask "what did I miss" of
3182
- * a party that may be the one who took it.
3183
- */
3184
- /**
3185
- * One direction's receiver-side ordering state.
3186
- *
3187
- * Named as an interface rather than inferred, so the box's agent and the
3188
- * browser hold the SAME type — an inferred one would let the two ends drift
3189
- * apart without a compiler ever objecting.
3190
- */
3191
- interface ConsoleOrder {
3192
- /** The last sequence accepted. 0 before anything has been. */
3193
- readonly seen: number;
3194
- accept(frame: ConsoleFrame): ConsoleOrderResult;
3195
- }
3196
- declare function consoleOrder(from: "browser" | "box"): ConsoleOrder;
3197
-
3198
- export { ABOUT, ABOUT_SHORT, ABOUT_SHORT_LEDE, ABOUT_SHORT_TAIL, AUDIENCES, Audience, BACKENDS, BACKEND_CLASSES, BACKEND_IDS, BackendClass, BackendCost, type BackendDescriptor, type BackendId, BackendIdSchema, CLOCK_ATTRIBUTION_MS, CLOCK_SKEW_WARN_MS, CONSOLE_FRAME_VERSION, CONSOLE_MAX_DATA_BYTES, Capability, CapabilityMatrix, ChatMessage, ChatPayload, ClaimRequest, ClaimResponse, ClaimedJob, ClaimedStub, ConsoleBye, ConsoleFrame, ConsoleHello, type ConsoleOrder, type ConsoleOrderFault, type ConsoleOrderResult, ConsoleResize, ConsoleStdin, ConsoleStdout, DeliveredResult, ENCRYPTION_KEY_CONTEXT, ENDPOINTS, ENVELOPE_MAX_AGE_MS, ERROR_STATUS, type Endpoint, type EnvelopeContext, EnvelopeDirection, type EnvelopeFailure, FetchRequest, FetchResponse, type FloorRefusal, GRANT_CONTEXT, GRANT_MAX_AGE_MS, GRANT_SIGNED_FIELDS, GeneratePayload, type GrantClaims, GrantRef, type GrantRefusal, HeartbeatRequest, HeartbeatResponse, JOB_KINDS, JobKind, JobOutcome, JobPayload, JobRefused, JobResultCanceled, JobResultError, JobResultOk, JobState, JobStub, KindedPayload, Lease, MAX_CLOCK_SKEW_MS, MAX_ENVELOPE_BYTES, MAX_PURPOSES, MAX_SUCCESSION_CHAIN, MIN_PROTOCOL_VERSION, MUSTS, MUST_IDS, Manifest, type MatchDaemon, type MatchJob, MatchRefusal, type MatchResult, type Must, type MustEnforcer, type MustId, type MustVerification, type MustVerifiedBy, OFFER_SCOPES, OfferScope, type OpenResult, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, Purpose, REFUSAL_MESSAGES, REFUSAL_TEXT, RESERVED_PURPOSE, RETIREMENT_WINDOW_MS, RefusalReason, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, RunMetadata, SIZE_CLASSES, SIZE_CLASS_LIMITS, SUCCESSION_CONTEXT, SUPPORTED_PROTOCOL_VERSIONS, SealedEnvelope, SealedOutcome, type SignatureFailure, SignedGrant, SizeClass, type SpendConsent, type StopReason, StopReasonSchema, StoredKeys, Succession, type SuccessionFailure, type SuccessionWalk, TERMINAL_STATES, UPDATE_OFFER_SINCE, UPGRADE_COMMAND, type VersionRefusal, WireError, WireErrorCode, WithheldKind, backendDescriptor, backendName, canTransition, canonicalRequest, checkDaemonFloor, checkProtocolVersion, classifyCost, compareVersions, consoleEnvelope, consoleOrder, cryptoReady, declaredVersion, describeBytes, effectiveOfferScope, envelopeBytes, fingerprint, generateKeys, grantStatement, isBackendId, isCloudTaggedModel, isJobKind, isLocalHost, isTerminal, keyId, kindsOf, matchAudience, mayOfferUpdate, mentionsWireField, mustsVerifiedBy, open, payloadTextLength, provenanceFor, publicIdentityOf, resolveCost, seal, signGrant, signRequest, signSiteRequest, signSuccession, signWith, singlePurposeManifest, sizeClassCeiling, sizeClassOf, successionStatement, tooLargeMessage, updateOfferFor, verifyGrant, verifyLink, verifyPublicIdentity, verifyRequest, verifySiteRequest, verifyWith, walkSuccession, withoutComments };
2809
+ export { ABOUT, ABOUT_SHORT, ABOUT_SHORT_LEDE, ABOUT_SHORT_TAIL, AUDIENCES, Audience, BACKENDS, BACKEND_CLASSES, BACKEND_IDS, BackendClass, BackendCost, type BackendDescriptor, type BackendId, BackendIdSchema, CLOCK_ATTRIBUTION_MS, CLOCK_SKEW_WARN_MS, Capability, CapabilityMatrix, ChatMessage, ChatPayload, ClaimRequest, ClaimResponse, ClaimedJob, ClaimedStub, DeliveredResult, ENDPOINTS, ERROR_STATUS, type Endpoint, FetchRequest, FetchResponse, type FloorRefusal, GRANT_CONTEXT, GRANT_MAX_AGE_MS, GRANT_SIGNED_FIELDS, GeneratePayload, type GrantClaims, GrantRef, type GrantRefusal, HeartbeatRequest, HeartbeatResponse, JOB_KINDS, JobKind, JobOutcome, JobPayload, JobRefused, JobResultCanceled, JobResultError, JobResultOk, JobState, JobStub, KindedPayload, Lease, MAX_CLOCK_SKEW_MS, MAX_ENVELOPE_BYTES, MAX_PURPOSES, MAX_SUCCESSION_CHAIN, MIN_PROTOCOL_VERSION, MUSTS, MUST_IDS, Manifest, type MatchDaemon, type MatchJob, MatchRefusal, type MatchResult, type Must, type MustEnforcer, type MustId, type MustVerification, type MustVerifiedBy, OFFER_SCOPES, OfferScope, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, Purpose, REFUSAL_MESSAGES, REFUSAL_TEXT, RESERVED_PURPOSE, RETIREMENT_WINDOW_MS, RefusalReason, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, RunMetadata, SIZE_CLASSES, SIZE_CLASS_LIMITS, SUCCESSION_CONTEXT, SUPPORTED_PROTOCOL_VERSIONS, SealedOutcome, type SignatureFailure, SignedGrant, SizeClass, type SpendConsent, type StopReason, StopReasonSchema, StoredKeys, Succession, type SuccessionFailure, type SuccessionWalk, TERMINAL_STATES, UPDATE_OFFER_SINCE, UPGRADE_COMMAND, type VersionRefusal, WireError, WireErrorCode, WithheldKind, backendDescriptor, backendName, canTransition, canonicalRequest, checkDaemonFloor, checkProtocolVersion, classifyCost, compareVersions, declaredVersion, describeBytes, effectiveOfferScope, envelopeBytes, grantStatement, isBackendId, isCloudTaggedModel, isJobKind, isLocalHost, isTerminal, kindsOf, matchAudience, mayOfferUpdate, mentionsWireField, mustsVerifiedBy, payloadTextLength, provenanceFor, resolveCost, signGrant, signRequest, signSiteRequest, signSuccession, singlePurposeManifest, sizeClassCeiling, sizeClassOf, successionStatement, tooLargeMessage, updateOfferFor, verifyGrant, verifyLink, verifyRequest, verifySiteRequest, walkSuccession, withoutComments };
package/dist/index.js CHANGED
@@ -1,3 +1,23 @@
1
+ import {
2
+ CONSOLE_FRAME_VERSION,
3
+ CONSOLE_MAX_DATA_BYTES,
4
+ ConsoleBye,
5
+ ConsoleFrame,
6
+ ConsoleHello,
7
+ ConsoleResize,
8
+ ConsoleStdin,
9
+ ConsoleStdout,
10
+ ENVELOPE_BODY_VERSION,
11
+ PublicIdentity,
12
+ consoleEnvelope,
13
+ consoleOrder,
14
+ decodeEnvelopeInner,
15
+ encodeEnvelopeInner,
16
+ envelopeSignedBody,
17
+ fromBase64Url,
18
+ toBase64Url
19
+ } from "./chunk-W7Q7DKQ6.js";
20
+
1
21
  // src/wire.ts
2
22
  import { z as z9 } from "zod";
3
23
 
@@ -11,20 +31,6 @@ import {
11
31
  verify
12
32
  } from "crypto";
13
33
  import { z } from "zod";
14
- var PublicIdentity = z.object({
15
- /** Raw Ed25519 public key. The pinned one. */
16
- identity: z.string().min(1),
17
- /** Raw X25519 public key, for sealing to this party. */
18
- encryption: z.string().min(1),
19
- /**
20
- * Ed25519 signature over the encryption key, by the identity key.
21
- *
22
- * This is what stops an upstream substituting an encryption key of its
23
- * own while relaying a genuine identity: the receiver pins the identity
24
- * and refuses any encryption key not signed by it.
25
- */
26
- encryptionSig: z.string().min(1)
27
- }).strict();
28
34
  var StoredKeys = z.object({
29
35
  version: z.literal(1),
30
36
  identityPublic: z.string().min(1),
@@ -89,16 +95,16 @@ function verifyPublicIdentity(identity) {
89
95
  return false;
90
96
  }
91
97
  }
92
- function signWith(keys, data2) {
93
- return sign(null, data2, importPrivate(keys.identityPrivate)).toString(
98
+ function signWith(keys, data) {
99
+ return sign(null, data, importPrivate(keys.identityPrivate)).toString(
94
100
  "base64url"
95
101
  );
96
102
  }
97
- function verifyWith(identityPublic, data2, signature) {
103
+ function verifyWith(identityPublic, data, signature) {
98
104
  try {
99
105
  return verify(
100
106
  null,
101
- data2,
107
+ data,
102
108
  importPublic(identityPublic, "Ed25519"),
103
109
  Buffer.from(signature, "base64url")
104
110
  );
@@ -1192,20 +1198,7 @@ var SealedEnvelope = z8.object({
1192
1198
  */
1193
1199
  deadlineAt: z8.number().int().positive()
1194
1200
  }).strict();
1195
- function signedBody(context, plaintext) {
1196
- return Buffer.from(
1197
- JSON.stringify({
1198
- v: "byollm/v1/envelope",
1199
- jobId: context.jobId,
1200
- senderKeyId: context.senderKeyId,
1201
- recipientKeyId: context.recipientKeyId,
1202
- deadlineAt: context.deadlineAt,
1203
- direction: context.direction,
1204
- plaintext
1205
- }),
1206
- "utf8"
1207
- );
1208
- }
1201
+ var signedBody = (context, plaintext) => envelopeSignedBody(context, plaintext);
1209
1202
  var rawX25519 = (key, part) => {
1210
1203
  const jwk = key.export({ format: "jwk" });
1211
1204
  const value = part === "x" ? jwk.x : jwk.d;
@@ -1216,7 +1209,7 @@ async function seal(input) {
1216
1209
  await cryptoReady();
1217
1210
  const body = signedBody(input.context, input.plaintext);
1218
1211
  const signature = signWith(input.senderKeys, body);
1219
- const inner = JSON.stringify({ body: body.toString("base64url"), signature });
1212
+ const inner = encodeEnvelopeInner(body, signature);
1220
1213
  const recipient = new Uint8Array(
1221
1214
  Buffer.from(input.recipientEncryptionPublic, "base64url")
1222
1215
  );
@@ -1255,22 +1248,15 @@ async function open(input) {
1255
1248
  } catch {
1256
1249
  return { ok: false, reason: "unopenable" };
1257
1250
  }
1258
- let parsed;
1259
- try {
1260
- parsed = JSON.parse(inner);
1261
- } catch {
1262
- return { ok: false, reason: "malformed" };
1263
- }
1264
- if (typeof parsed.body !== "string" || typeof parsed.signature !== "string") {
1265
- return { ok: false, reason: "malformed" };
1266
- }
1267
- const body = Buffer.from(parsed.body, "base64url");
1251
+ const parsed = decodeEnvelopeInner(inner);
1252
+ if (parsed === void 0) return { ok: false, reason: "malformed" };
1253
+ const body = parsed.body;
1268
1254
  if (!verifyWith(input.senderIdentityPublic, body, parsed.signature)) {
1269
1255
  return { ok: false, reason: "bad-signature" };
1270
1256
  }
1271
1257
  let claims;
1272
1258
  try {
1273
- claims = JSON.parse(body.toString("utf8"));
1259
+ claims = JSON.parse(new TextDecoder().decode(body));
1274
1260
  } catch {
1275
1261
  return { ok: false, reason: "malformed" };
1276
1262
  }
@@ -2663,118 +2649,6 @@ var MUST_IDS = Object.freeze(Object.keys(MUSTS));
2663
2649
  function mustsVerifiedBy(kind) {
2664
2650
  return MUST_IDS.filter((id) => kindsOf(MUSTS[id]).includes(kind));
2665
2651
  }
2666
-
2667
- // src/console.ts
2668
- import { z as z12 } from "zod";
2669
- var CONSOLE_FRAME_VERSION = "byollm/v1/console";
2670
- var CONSOLE_MAX_DATA_BYTES = 64 * 1024;
2671
- var data = z12.string().max(Math.ceil(CONSOLE_MAX_DATA_BYTES * 4 / 3) + 4).describe("base64");
2672
- var cols = z12.number().int().min(1).max(1e4);
2673
- var rows = z12.number().int().min(1).max(1e4);
2674
- var seq = z12.number().int().positive();
2675
- var ConsoleHello = z12.object({
2676
- v: z12.literal(CONSOLE_FRAME_VERSION),
2677
- kind: z12.literal("hello"),
2678
- seq: z12.literal(1),
2679
- /**
2680
- * The browser's EPHEMERAL identity for this session, and the reason hello
2681
- * exists as its own frame.
2682
- *
2683
- * A browser has no long-term key — it is a tab, not a machine — so it
2684
- * generates one per session and the box seals its output to it. That key
2685
- * has to reach the box without the hub being able to substitute one of its
2686
- * own, which is the classic broker attack. So it travels SEALED TO THE
2687
- * BOX'S PINNED IDENTITY like every other frame: the hub cannot read it and
2688
- * cannot replace it, because replacing it means producing a sealed box
2689
- * the box's key opens and the browser's key signed.
2690
- *
2691
- * The box does not pin this. It cannot — it is new every session, and
2692
- * nothing authorises it cryptographically. What authorises the browser is
2693
- * the hub's per-session grant (ruling item 2); what this key does is keep
2694
- * the hub from READING what it authorised.
2695
- */
2696
- browser: PublicIdentity,
2697
- cols,
2698
- rows
2699
- }).strict();
2700
- var ConsoleStdin = z12.object({
2701
- v: z12.literal(CONSOLE_FRAME_VERSION),
2702
- /** Browser→box only. A `stdin` arriving from the box is `wrong-way`. */
2703
- kind: z12.literal("stdin"),
2704
- seq,
2705
- data
2706
- }).strict();
2707
- var ConsoleStdout = z12.object({
2708
- v: z12.literal(CONSOLE_FRAME_VERSION),
2709
- /** Box→browser only. */
2710
- kind: z12.literal("stdout"),
2711
- seq,
2712
- data
2713
- }).strict();
2714
- var ConsoleResize = z12.object({
2715
- v: z12.literal(CONSOLE_FRAME_VERSION),
2716
- kind: z12.literal("resize"),
2717
- seq,
2718
- cols,
2719
- rows
2720
- }).strict();
2721
- var ConsoleBye = z12.object({
2722
- v: z12.literal(CONSOLE_FRAME_VERSION),
2723
- kind: z12.literal("bye"),
2724
- seq,
2725
- /** Shown to the owner in the session feed, so it is a sentence. */
2726
- reason: z12.string().min(1).max(200)
2727
- }).strict();
2728
- var ConsoleFrame = z12.discriminatedUnion("kind", [
2729
- ConsoleHello,
2730
- ConsoleStdin,
2731
- ConsoleStdout,
2732
- ConsoleResize,
2733
- ConsoleBye
2734
- ]);
2735
- function consoleEnvelope(input) {
2736
- return {
2737
- jobId: input.sessionId,
2738
- senderKeyId: input.senderKeyId,
2739
- recipientKeyId: input.recipientKeyId,
2740
- deadlineAt: input.deadlineAt,
2741
- direction: input.from === "browser" ? "payload" : "result"
2742
- };
2743
- }
2744
- function consoleOrder(from) {
2745
- const maySend = {
2746
- // The browser drives: it opens, it types, it sets the size, it leaves.
2747
- browser: ["hello", "stdin", "resize", "bye"],
2748
- // The box answers, and can end a session. Nothing else.
2749
- box: ["stdout", "bye"]
2750
- };
2751
- const allowed = maySend[from];
2752
- const opensWithHello = from === "browser";
2753
- let last = 0;
2754
- let closed = false;
2755
- return {
2756
- /** The last sequence accepted. 0 before anything has been. */
2757
- get seen() {
2758
- return last;
2759
- },
2760
- accept(frame) {
2761
- if (closed) return { ok: false, fault: "closed" };
2762
- if (!allowed.includes(frame.kind)) {
2763
- return { ok: false, fault: "wrong-way" };
2764
- }
2765
- const isFirst = last === 0;
2766
- const wantsHello = isFirst && opensWithHello;
2767
- if (wantsHello !== (frame.kind === "hello")) {
2768
- return { ok: false, fault: "out-of-turn" };
2769
- }
2770
- if (frame.seq <= last) return { ok: false, fault: "replayed" };
2771
- if (frame.seq !== last + 1) return { ok: false, fault: "gap" };
2772
- last = frame.seq;
2773
- if (frame.kind === "bye") closed = true;
2774
- return { ok: true };
2775
- }
2776
- };
2777
- }
2778
2652
  export {
2779
2653
  ABOUT,
2780
2654
  ABOUT_SHORT,
@@ -2809,6 +2683,7 @@ export {
2809
2683
  DeliveredResult,
2810
2684
  ENCRYPTION_KEY_CONTEXT,
2811
2685
  ENDPOINTS,
2686
+ ENVELOPE_BODY_VERSION,
2812
2687
  ENVELOPE_MAX_AGE_MS,
2813
2688
  ERROR_STATUS,
2814
2689
  EnvelopeDirection,
@@ -2896,10 +2771,14 @@ export {
2896
2771
  consoleOrder,
2897
2772
  cryptoReady,
2898
2773
  declaredVersion,
2774
+ decodeEnvelopeInner,
2899
2775
  describeBytes,
2900
2776
  effectiveOfferScope,
2777
+ encodeEnvelopeInner,
2901
2778
  envelopeBytes,
2779
+ envelopeSignedBody,
2902
2780
  fingerprint,
2781
+ fromBase64Url,
2903
2782
  generateKeys,
2904
2783
  grantStatement,
2905
2784
  isBackendId,
@@ -2928,6 +2807,7 @@ export {
2928
2807
  sizeClassCeiling,
2929
2808
  sizeClassOf,
2930
2809
  successionStatement,
2810
+ toBase64Url,
2931
2811
  tooLargeMessage,
2932
2812
  updateOfferFor,
2933
2813
  verifyGrant,