@cardanowall/poe-standard 0.0.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,128 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+
5
+ // src/schema.ts
6
+ var ChunkedBytesArraySchema = zod.z.array(
7
+ zod.z.instanceof(Uint8Array).refine((b) => b.length >= 1 && b.length <= 64, {
8
+ params: { code: "CHUNK_TOO_LARGE" }
9
+ })
10
+ ).min(1);
11
+ var UTF8_ENCODER = new TextEncoder();
12
+ var UriChunkArraySchema = zod.z.array(
13
+ zod.z.string().refine(
14
+ (s) => {
15
+ const n = UTF8_ENCODER.encode(s).length;
16
+ return n >= 1 && n <= 64;
17
+ },
18
+ { params: { code: "CHUNK_TOO_LARGE" } }
19
+ )
20
+ ).min(1);
21
+ var HashDigestSchema = zod.z.instanceof(Uint8Array);
22
+ var HashesMapSchema = zod.z.record(zod.z.string(), HashDigestSchema);
23
+ var MerkleCommitSchema = zod.z.object({
24
+ alg: zod.z.string(),
25
+ root: zod.z.instanceof(Uint8Array),
26
+ leaf_count: zod.z.number().int().min(1),
27
+ uris: zod.z.array(UriChunkArraySchema).min(1).optional()
28
+ }).strict();
29
+ var SlotSchema = zod.z.object({
30
+ epk: zod.z.instanceof(Uint8Array).optional(),
31
+ kem_ct: ChunkedBytesArraySchema.optional(),
32
+ wrap: zod.z.instanceof(Uint8Array).optional()
33
+ });
34
+ var Argon2idParamsSchema = zod.z.object({
35
+ m: zod.z.number().int(),
36
+ t: zod.z.number().int(),
37
+ p: zod.z.number().int()
38
+ }).strict();
39
+ var PassphraseBlockSchema = zod.z.object({
40
+ alg: zod.z.string(),
41
+ salt: zod.z.instanceof(Uint8Array).superRefine((bytes, ctx) => {
42
+ if (bytes.length < 16) {
43
+ ctx.addIssue({
44
+ code: "custom",
45
+ path: [],
46
+ message: `passphrase.salt length ${bytes.length} < 16`,
47
+ params: { code: "ENC_PASSPHRASE_SALT_TOO_SHORT" }
48
+ });
49
+ } else if (bytes.length > 64) {
50
+ ctx.addIssue({
51
+ code: "custom",
52
+ path: [],
53
+ message: `passphrase.salt length ${bytes.length} > 64`,
54
+ params: { code: "ENC_PASSPHRASE_SALT_TOO_LONG" }
55
+ });
56
+ }
57
+ }),
58
+ params: zod.z.record(zod.z.string(), zod.z.unknown())
59
+ }).strict();
60
+ var EncryptionEnvelopeSchema = zod.z.object({
61
+ scheme: zod.z.unknown(),
62
+ aead: zod.z.string(),
63
+ kem: zod.z.string().optional(),
64
+ nonce: zod.z.instanceof(Uint8Array),
65
+ slots: zod.z.array(SlotSchema).optional(),
66
+ slots_mac: zod.z.instanceof(Uint8Array).refine((b) => b.length === 32, {
67
+ params: { code: "ENC_SLOTS_MAC_INVALID_LENGTH" }
68
+ }).optional(),
69
+ passphrase: PassphraseBlockSchema.optional()
70
+ }).strict();
71
+ var ItemEntrySchema = zod.z.object({
72
+ hashes: HashesMapSchema,
73
+ uris: zod.z.array(UriChunkArraySchema).min(1).optional(),
74
+ // Captured as `unknown` so the validator can run the
75
+ // `ENC_REQUIRES_CONTENT_HASH` pre-check ahead of any inner-shape errors
76
+ // and surface the most informative code first.
77
+ enc: zod.z.unknown().optional()
78
+ }).strict();
79
+ var SigEntrySchema = zod.z.object({
80
+ cose_key: ChunkedBytesArraySchema.optional(),
81
+ cose_sign1: ChunkedBytesArraySchema
82
+ }).strict();
83
+ var SupersedesSchema = zod.z.instanceof(Uint8Array).refine((b) => b.length === 32, {
84
+ params: { code: "SUPERSEDES_TX_INVALID_LENGTH" }
85
+ });
86
+ var VersionLiteralSchema = zod.z.literal(1);
87
+ var PoeRecordSchema = zod.z.looseObject({
88
+ v: VersionLiteralSchema,
89
+ items: zod.z.array(ItemEntrySchema).optional(),
90
+ merkle: zod.z.array(MerkleCommitSchema).optional(),
91
+ supersedes: SupersedesSchema.optional(),
92
+ sigs: zod.z.array(SigEntrySchema).optional(),
93
+ crit: zod.z.array(zod.z.string()).optional()
94
+ });
95
+ var TOP_LEVEL_BASE_KEYS = /* @__PURE__ */ new Set([
96
+ "v",
97
+ "items",
98
+ "merkle",
99
+ "supersedes",
100
+ "sigs",
101
+ "crit"
102
+ ]);
103
+ var EXTENSION_KEY_VENDOR_RE = /^x-.+\n?$/;
104
+ var EXTENSION_KEY_COMPANION_RE = /^[a-z]+-.+\n?$/;
105
+ function isExtensionKey(k) {
106
+ return EXTENSION_KEY_VENDOR_RE.test(k) || EXTENSION_KEY_COMPANION_RE.test(k);
107
+ }
108
+
109
+ exports.Argon2idParamsSchema = Argon2idParamsSchema;
110
+ exports.ChunkedBytesArraySchema = ChunkedBytesArraySchema;
111
+ exports.EXTENSION_KEY_COMPANION_RE = EXTENSION_KEY_COMPANION_RE;
112
+ exports.EXTENSION_KEY_VENDOR_RE = EXTENSION_KEY_VENDOR_RE;
113
+ exports.EncryptionEnvelopeSchema = EncryptionEnvelopeSchema;
114
+ exports.HashDigestSchema = HashDigestSchema;
115
+ exports.HashesMapSchema = HashesMapSchema;
116
+ exports.ItemEntrySchema = ItemEntrySchema;
117
+ exports.MerkleCommitSchema = MerkleCommitSchema;
118
+ exports.PassphraseBlockSchema = PassphraseBlockSchema;
119
+ exports.PoeRecordSchema = PoeRecordSchema;
120
+ exports.SigEntrySchema = SigEntrySchema;
121
+ exports.SlotSchema = SlotSchema;
122
+ exports.SupersedesSchema = SupersedesSchema;
123
+ exports.TOP_LEVEL_BASE_KEYS = TOP_LEVEL_BASE_KEYS;
124
+ exports.UriChunkArraySchema = UriChunkArraySchema;
125
+ exports.VersionLiteralSchema = VersionLiteralSchema;
126
+ exports.isExtensionKey = isExtensionKey;
127
+ //# sourceMappingURL=schema.cjs.map
128
+ //# sourceMappingURL=schema.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema.ts"],"names":["z"],"mappings":"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","file":"schema.cjs","sourcesContent":["// CIP-309 v1 PoE record Zod schemas.\n//\n// Scope: structural shape gate. The schema enforces per-field types, length\n// bounds (chunk size, digest length, supersedes length, nonce length,\n// passphrase salt length), closed-map invariants (`sigs[i]`, `slot`,\n// `passphrase`, `merkle[i]`), and the `v == 1` literal. Cross-field rules\n// (item.hashes content-hash binding when `enc` present, slots/passphrase\n// exclusivity, `crit[]` shape, registry membership of algorithm\n// identifiers, COSE_Sign1 structural decode, URI per-scheme shape rules)\n// fire in `validator.ts` so the validator can emit the precise structural\n// codes (`UNSUPPORTED_*_ALG`, `ENC_*`, `SIG_*`, `INVALID_URI`,\n// `CRIT_SHAPE_INVALID`, …) rather than a generic schema-mismatch.\n//\n// Refinements that DO live in the schema (because the validator's domain\n// pass lifts these as `SCHEMA_*` / `*_LENGTH_MISMATCH` codes directly):\n// - chunk size `[1, 64]` → `CHUNK_TOO_LARGE`\n// - 32-byte digest / 32-byte root / 32-byte supersedes → `HASH_DIGEST_LENGTH_MISMATCH`\n// / `SUPERSEDES_TX_INVALID_LENGTH`\n// - 24-byte nonce / 32-byte slots_mac →\n// `NONCE_LENGTH_MISMATCH` / `ENC_SLOTS_MAC_INVALID_LENGTH`\n// - passphrase salt 16..64 bytes → `ENC_PASSPHRASE_SALT_TOO_SHORT` /\n// `ENC_PASSPHRASE_SALT_TOO_LONG`\n//\n// Per-slot recipient lengths (`epk`, `kem_ct`, `wrap`) are NOT enforced here:\n// the required slot shape depends on the envelope-level `kem`, which a slot\n// cannot see in isolation. The KEM-driven slot descriptor in `validator.ts`\n// emits the precise `KEM_EPK_LENGTH_MISMATCH` / `KEM_CT_LENGTH_MISMATCH` /\n// `WRAP_LENGTH_MISMATCH` / `ENC_SLOT_INVALID_SHAPE` codes instead.\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Chunked-bytes / chunked-text arrays\n// =============================================================================\n\n// `[1* bstr .size (1..64)]`. A zero-length chunk (0 < 1) is rejected with the\n// SAME `CHUNK_TOO_LARGE` code as oversized chunks (any length outside\n// `[1, 64]`).\nexport const ChunkedBytesArraySchema = z\n .array(\n z.instanceof(Uint8Array).refine((b) => b.length >= 1 && b.length <= 64, {\n params: { code: 'CHUNK_TOO_LARGE' },\n }),\n )\n .min(1);\nexport type ChunkedBytesArray = z.infer<typeof ChunkedBytesArraySchema>;\n\n// `[1* tstr .size (1..64)]` — chunk byte length is the UTF-8-encoded length\n// (each `tstr` is wire-encoded as UTF-8). The `tstr .size (1..64)` pin is a\n// byte-count constraint, not a code-unit constraint.\nconst UTF8_ENCODER = new TextEncoder();\nexport const UriChunkArraySchema = z\n .array(\n z.string().refine(\n (s) => {\n const n = UTF8_ENCODER.encode(s).length;\n return n >= 1 && n <= 64;\n },\n { params: { code: 'CHUNK_TOO_LARGE' } },\n ),\n )\n .min(1);\nexport type UriChunkArray = z.infer<typeof UriChunkArraySchema>;\n\n// =============================================================================\n// Hashes map\n// =============================================================================\n//\n// `hashes` is a non-empty CBOR map keyed by content-hash algorithm identifier\n// (a CBOR text string from the content-hash registry) with the 32-byte digest\n// as value. cbor2 surfaces a text-keyed CBOR map as a plain JS object — z.record\n// admits any string key here. Both the registry-membership check\n// (`UNSUPPORTED_HASH_ALG`) and the per-algorithm digest-length check\n// (`HASH_DIGEST_LENGTH_MISMATCH`) live in the validator's domain pass so\n// each violation emits its precise code; the schema only enforces the\n// value is a CBOR byte string.\n\nexport const HashDigestSchema = z.instanceof(Uint8Array);\n\nexport const HashesMapSchema = z.record(z.string(), HashDigestSchema);\nexport type HashesMap = z.infer<typeof HashesMapSchema>;\n\n// =============================================================================\n// Top-level `merkle[]`\n// =============================================================================\n//\n// Each commit is a closed map `{alg, root, leaf_count, ? uris}`. `alg` is open\n// (registry membership is enforced in the validator's domain pass — unknown\n// identifiers emit `UNSUPPORTED_MERKLE_COMMIT_ALG`).\n\nexport const MerkleCommitSchema = z\n .object({\n alg: z.string(),\n root: z.instanceof(Uint8Array),\n leaf_count: z.number().int().min(1),\n uris: z.array(UriChunkArraySchema).min(1).optional(),\n })\n .strict();\nexport type MerkleCommit = z.infer<typeof MerkleCommitSchema>;\n\n// =============================================================================\n// Encryption envelope\n// =============================================================================\n\n// Per-slot recipient entry. The slot shape is KEM-driven:\n//\n// - x25519: `{ epk: bstr(32), wrap: bstr(48) }` — `epk` is the\n// ephemeral X25519 public key, `wrap` is the 32-byte CEK + 16-byte\n// ChaCha20-Poly1305 tag.\n// - mlkem768x25519: `{ kem_ct: [ bstr .size (1..64) ], wrap: bstr(48) }` —\n// `kem_ct` is the 1120-byte X-Wing `enc` carried as a chunked byte-string\n// array (the same `bytes-chunk-array` shape `sigs[i].cose_sign1` uses);\n// there is NO per-slot `epk` on the hybrid path.\n//\n// The `kem` identifier is hoisted to envelope scope (a per-slot `kem` would\n// be wire-bloat). The schema is deliberately PERMISSIVE:\n// `epk`, `kem_ct`, and `wrap` are all optional and `.strict()` is NOT applied.\n// Both the per-field length checks (`KEM_EPK_LENGTH_MISMATCH`,\n// `KEM_CT_LENGTH_MISMATCH`, `WRAP_LENGTH_MISMATCH`) and the KEM-driven\n// shape gate (which field MUST/MUST NOT be present for the declared `kem`,\n// emitting `ENC_SLOT_INVALID_SHAPE`) live in the validator's domain pass —\n// the structural schema cannot know the envelope `kem` from a slot in\n// isolation, and we want the precise KEM-aware code rather than a generic\n// schema mismatch. Because `.strict()` is dropped, the domain pass MUST\n// explicitly reject cross-KEM contamination (an x25519 slot carrying\n// `kem_ct`, or a hybrid slot carrying `epk`).\nexport const SlotSchema = z.object({\n epk: z.instanceof(Uint8Array).optional(),\n kem_ct: ChunkedBytesArraySchema.optional(),\n wrap: z.instanceof(Uint8Array).optional(),\n});\nexport type Slot = z.infer<typeof SlotSchema>;\n\n// Argon2id params `{m, t, p}` are a closed map. Each value MUST be a CBOR\n// unsigned integer; the FLOOR check (`m ≥ 65536`,\n// `t ≥ 3`, `p ≥ 1`) emits `ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW` in the\n// validator's domain pass — keeping it out of the schema lets us emit the\n// distinct salt-length code when salt itself is malformed too.\nexport const Argon2idParamsSchema = z\n .object({\n m: z.number().int(),\n t: z.number().int(),\n p: z.number().int(),\n })\n .strict();\nexport type Argon2idParams = z.infer<typeof Argon2idParamsSchema>;\n\n// Passphrase block. `alg` is open (registry membership checked in the\n// validator's domain pass → `ENC_PASSPHRASE_ALG_UNSUPPORTED`);\n// `params` is open here (validator narrows on the registered `alg` value and\n// emits `SCHEMA_UNKNOWN_FIELD` for extra keys, `ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW`\n// for sub-floor values). `salt` length floor/ceiling are schema-layer\n// refinements with the dedicated `ENC_PASSPHRASE_SALT_TOO_SHORT/TOO_LONG`\n// codes — they belong at the schema layer because a slot cannot otherwise\n// see the salt length.\nexport const PassphraseBlockSchema = z\n .object({\n alg: z.string(),\n salt: z.instanceof(Uint8Array).superRefine((bytes, ctx) => {\n if (bytes.length < 16) {\n ctx.addIssue({\n code: 'custom',\n path: [],\n message: `passphrase.salt length ${bytes.length} < 16`,\n params: { code: 'ENC_PASSPHRASE_SALT_TOO_SHORT' },\n });\n } else if (bytes.length > 64) {\n ctx.addIssue({\n code: 'custom',\n path: [],\n message: `passphrase.salt length ${bytes.length} > 64`,\n params: { code: 'ENC_PASSPHRASE_SALT_TOO_LONG' },\n });\n }\n }),\n params: z.record(z.string(), z.unknown()),\n })\n .strict();\nexport type PassphraseBlock = z.infer<typeof PassphraseBlockSchema>;\n\n// Sealed-PoE envelope. The wire format admits any combination of\n// `kem` / `slots` / `slots_mac` / `passphrase` keys (permissive superset);\n// cross-field invariants (slots ⊕ passphrase, slots ↔ slots_mac, slots\n// requires kem, content-hash binding, slots non-empty) are enforced in the\n// validator's domain pass so each violation emits its typed code rather\n// than a generic shape mismatch.\nexport const EncryptionEnvelopeSchema = z\n .object({\n scheme: z.unknown(),\n aead: z.string(),\n kem: z.string().optional(),\n nonce: z.instanceof(Uint8Array),\n slots: z.array(SlotSchema).optional(),\n slots_mac: z\n .instanceof(Uint8Array)\n .refine((b) => b.length === 32, {\n params: { code: 'ENC_SLOTS_MAC_INVALID_LENGTH' },\n })\n .optional(),\n passphrase: PassphraseBlockSchema.optional(),\n })\n .strict();\nexport type EncryptionEnvelope = z.infer<typeof EncryptionEnvelopeSchema>;\n\n// =============================================================================\n// Item entry\n// =============================================================================\n\nexport const ItemEntrySchema = z\n .object({\n hashes: HashesMapSchema,\n uris: z.array(UriChunkArraySchema).min(1).optional(),\n // Captured as `unknown` so the validator can run the\n // `ENC_REQUIRES_CONTENT_HASH` pre-check ahead of any inner-shape errors\n // and surface the most informative code first.\n enc: z.unknown().optional(),\n })\n .strict();\nexport type ItemEntry = z.infer<typeof ItemEntrySchema>;\n\n// =============================================================================\n// Sig entry\n// =============================================================================\n//\n// Closed CBOR map `{cose_sign1, ? cose_key}`. Canonical CBOR map-key sort\n// (RFC 8949 §4.2.1, bytewise lex on encoded keys) places `cose_key`\n// (length-8 tstr, `0x68`) BEFORE `cose_sign1` (length-10 tstr, `0x6a`); the\n// schema property-order is irrelevant — the canonical encoder handles it.\nexport const SigEntrySchema = z\n .object({\n cose_key: ChunkedBytesArraySchema.optional(),\n cose_sign1: ChunkedBytesArraySchema,\n })\n .strict();\nexport type SigEntry = z.infer<typeof SigEntrySchema>;\n\n// =============================================================================\n// Supersedence\n// =============================================================================\n\nexport const SupersedesSchema = z.instanceof(Uint8Array).refine((b) => b.length === 32, {\n params: { code: 'SUPERSEDES_TX_INVALID_LENGTH' },\n});\nexport type Supersedes = z.infer<typeof SupersedesSchema>;\n\n// =============================================================================\n// Top-level record\n// =============================================================================\n//\n// `v == 1` is a literal — a future major (`v: 2`) MUST be rejected with\n// `SCHEMA_INVALID_LITERAL`. `z.literal(1)` preserves the narrow `1` type for\n// the inferred `PoeRecord[\"v\"]` (so consumers can dispatch on it) and emits\n// Zod's `invalid_value` code which the validator's mapper lifts to\n// `SCHEMA_INVALID_LITERAL`.\n//\n// `looseObject` admits extension keys (matching `^x-.+` or `^[a-z]+-.+`); the\n// validator's domain pass rejects unknown keys that match neither pattern with\n// `SCHEMA_UNKNOWN_FIELD`.\nexport const VersionLiteralSchema = z.literal(1);\n\nexport const PoeRecordSchema = z.looseObject({\n v: VersionLiteralSchema,\n items: z.array(ItemEntrySchema).optional(),\n merkle: z.array(MerkleCommitSchema).optional(),\n supersedes: SupersedesSchema.optional(),\n sigs: z.array(SigEntrySchema).optional(),\n crit: z.array(z.string()).optional(),\n});\nexport type PoeRecord = z.infer<typeof PoeRecordSchema>;\n\n// =============================================================================\n// Closed top-level base-key registry\n// =============================================================================\n//\n// Used by the validator's domain pass to distinguish unknown-typo keys from\n// well-formed extension keys (`^x-.+` / `^[a-z]+-.+`).\nexport const TOP_LEVEL_BASE_KEYS: ReadonlySet<string> = new Set([\n 'v',\n 'items',\n 'merkle',\n 'supersedes',\n 'sigs',\n 'crit',\n]);\n\n// Extension-key namespaces. Anchored at both ends so an\n// embedded newline cannot smuggle a multi-segment key past the check: `.`\n// excludes `\\n` in JS, and the `\\n?$` tail tolerates exactly ONE trailing\n// newline (matching the Python validator's `re.fullmatch(r'^(x-.+|[a-z]+-.+)$')`\n// semantics, where `$` likewise admits a single trailing `\\n`). So `x-note\\n`\n// is an extension key, but `x-a\\nb`, `x-note\\n\\n`, and `x-\\n` are not.\nexport const EXTENSION_KEY_VENDOR_RE = /^x-.+\\n?$/;\nexport const EXTENSION_KEY_COMPANION_RE = /^[a-z]+-.+\\n?$/;\n\nexport function isExtensionKey(k: string): boolean {\n return EXTENSION_KEY_VENDOR_RE.test(k) || EXTENSION_KEY_COMPANION_RE.test(k);\n}\n"]}
@@ -0,0 +1,93 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const ChunkedBytesArraySchema: z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
4
+ type ChunkedBytesArray = z.infer<typeof ChunkedBytesArraySchema>;
5
+ declare const UriChunkArraySchema: z.ZodArray<z.ZodString>;
6
+ type UriChunkArray = z.infer<typeof UriChunkArraySchema>;
7
+ declare const HashDigestSchema: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
8
+ declare const HashesMapSchema: z.ZodRecord<z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
9
+ type HashesMap = z.infer<typeof HashesMapSchema>;
10
+ declare const MerkleCommitSchema: z.ZodObject<{
11
+ alg: z.ZodString;
12
+ root: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
13
+ leaf_count: z.ZodNumber;
14
+ uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
15
+ }, z.core.$strict>;
16
+ type MerkleCommit = z.infer<typeof MerkleCommitSchema>;
17
+ declare const SlotSchema: z.ZodObject<{
18
+ epk: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
19
+ kem_ct: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
20
+ wrap: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
21
+ }, z.core.$strip>;
22
+ type Slot = z.infer<typeof SlotSchema>;
23
+ declare const Argon2idParamsSchema: z.ZodObject<{
24
+ m: z.ZodNumber;
25
+ t: z.ZodNumber;
26
+ p: z.ZodNumber;
27
+ }, z.core.$strict>;
28
+ type Argon2idParams = z.infer<typeof Argon2idParamsSchema>;
29
+ declare const PassphraseBlockSchema: z.ZodObject<{
30
+ alg: z.ZodString;
31
+ salt: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
32
+ params: z.ZodRecord<z.ZodString, z.ZodUnknown>;
33
+ }, z.core.$strict>;
34
+ type PassphraseBlock = z.infer<typeof PassphraseBlockSchema>;
35
+ declare const EncryptionEnvelopeSchema: z.ZodObject<{
36
+ scheme: z.ZodUnknown;
37
+ aead: z.ZodString;
38
+ kem: z.ZodOptional<z.ZodString>;
39
+ nonce: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
40
+ slots: z.ZodOptional<z.ZodArray<z.ZodObject<{
41
+ epk: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
42
+ kem_ct: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
43
+ wrap: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
44
+ }, z.core.$strip>>>;
45
+ slots_mac: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
46
+ passphrase: z.ZodOptional<z.ZodObject<{
47
+ alg: z.ZodString;
48
+ salt: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
49
+ params: z.ZodRecord<z.ZodString, z.ZodUnknown>;
50
+ }, z.core.$strict>>;
51
+ }, z.core.$strict>;
52
+ type EncryptionEnvelope = z.infer<typeof EncryptionEnvelopeSchema>;
53
+ declare const ItemEntrySchema: z.ZodObject<{
54
+ hashes: z.ZodRecord<z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
55
+ uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
56
+ enc: z.ZodOptional<z.ZodUnknown>;
57
+ }, z.core.$strict>;
58
+ type ItemEntry = z.infer<typeof ItemEntrySchema>;
59
+ declare const SigEntrySchema: z.ZodObject<{
60
+ cose_key: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
61
+ cose_sign1: z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
62
+ }, z.core.$strict>;
63
+ type SigEntry = z.infer<typeof SigEntrySchema>;
64
+ declare const SupersedesSchema: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
65
+ type Supersedes = z.infer<typeof SupersedesSchema>;
66
+ declare const VersionLiteralSchema: z.ZodLiteral<1>;
67
+ declare const PoeRecordSchema: z.ZodObject<{
68
+ v: z.ZodLiteral<1>;
69
+ items: z.ZodOptional<z.ZodArray<z.ZodObject<{
70
+ hashes: z.ZodRecord<z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
71
+ uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
72
+ enc: z.ZodOptional<z.ZodUnknown>;
73
+ }, z.core.$strict>>>;
74
+ merkle: z.ZodOptional<z.ZodArray<z.ZodObject<{
75
+ alg: z.ZodString;
76
+ root: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
77
+ leaf_count: z.ZodNumber;
78
+ uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
79
+ }, z.core.$strict>>>;
80
+ supersedes: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
81
+ sigs: z.ZodOptional<z.ZodArray<z.ZodObject<{
82
+ cose_key: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
83
+ cose_sign1: z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
84
+ }, z.core.$strict>>>;
85
+ crit: z.ZodOptional<z.ZodArray<z.ZodString>>;
86
+ }, z.core.$loose>;
87
+ type PoeRecord = z.infer<typeof PoeRecordSchema>;
88
+ declare const TOP_LEVEL_BASE_KEYS: ReadonlySet<string>;
89
+ declare const EXTENSION_KEY_VENDOR_RE: RegExp;
90
+ declare const EXTENSION_KEY_COMPANION_RE: RegExp;
91
+ declare function isExtensionKey(k: string): boolean;
92
+
93
+ export { type Argon2idParams, Argon2idParamsSchema, type ChunkedBytesArray, ChunkedBytesArraySchema, EXTENSION_KEY_COMPANION_RE, EXTENSION_KEY_VENDOR_RE, type EncryptionEnvelope, EncryptionEnvelopeSchema, HashDigestSchema, type HashesMap, HashesMapSchema, type ItemEntry, ItemEntrySchema, type MerkleCommit, MerkleCommitSchema, type PassphraseBlock, PassphraseBlockSchema, type PoeRecord, PoeRecordSchema, type SigEntry, SigEntrySchema, type Slot, SlotSchema, type Supersedes, SupersedesSchema, TOP_LEVEL_BASE_KEYS, type UriChunkArray, UriChunkArraySchema, VersionLiteralSchema, isExtensionKey };
@@ -0,0 +1,93 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const ChunkedBytesArraySchema: z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
4
+ type ChunkedBytesArray = z.infer<typeof ChunkedBytesArraySchema>;
5
+ declare const UriChunkArraySchema: z.ZodArray<z.ZodString>;
6
+ type UriChunkArray = z.infer<typeof UriChunkArraySchema>;
7
+ declare const HashDigestSchema: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
8
+ declare const HashesMapSchema: z.ZodRecord<z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
9
+ type HashesMap = z.infer<typeof HashesMapSchema>;
10
+ declare const MerkleCommitSchema: z.ZodObject<{
11
+ alg: z.ZodString;
12
+ root: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
13
+ leaf_count: z.ZodNumber;
14
+ uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
15
+ }, z.core.$strict>;
16
+ type MerkleCommit = z.infer<typeof MerkleCommitSchema>;
17
+ declare const SlotSchema: z.ZodObject<{
18
+ epk: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
19
+ kem_ct: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
20
+ wrap: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
21
+ }, z.core.$strip>;
22
+ type Slot = z.infer<typeof SlotSchema>;
23
+ declare const Argon2idParamsSchema: z.ZodObject<{
24
+ m: z.ZodNumber;
25
+ t: z.ZodNumber;
26
+ p: z.ZodNumber;
27
+ }, z.core.$strict>;
28
+ type Argon2idParams = z.infer<typeof Argon2idParamsSchema>;
29
+ declare const PassphraseBlockSchema: z.ZodObject<{
30
+ alg: z.ZodString;
31
+ salt: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
32
+ params: z.ZodRecord<z.ZodString, z.ZodUnknown>;
33
+ }, z.core.$strict>;
34
+ type PassphraseBlock = z.infer<typeof PassphraseBlockSchema>;
35
+ declare const EncryptionEnvelopeSchema: z.ZodObject<{
36
+ scheme: z.ZodUnknown;
37
+ aead: z.ZodString;
38
+ kem: z.ZodOptional<z.ZodString>;
39
+ nonce: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
40
+ slots: z.ZodOptional<z.ZodArray<z.ZodObject<{
41
+ epk: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
42
+ kem_ct: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
43
+ wrap: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
44
+ }, z.core.$strip>>>;
45
+ slots_mac: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
46
+ passphrase: z.ZodOptional<z.ZodObject<{
47
+ alg: z.ZodString;
48
+ salt: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
49
+ params: z.ZodRecord<z.ZodString, z.ZodUnknown>;
50
+ }, z.core.$strict>>;
51
+ }, z.core.$strict>;
52
+ type EncryptionEnvelope = z.infer<typeof EncryptionEnvelopeSchema>;
53
+ declare const ItemEntrySchema: z.ZodObject<{
54
+ hashes: z.ZodRecord<z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
55
+ uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
56
+ enc: z.ZodOptional<z.ZodUnknown>;
57
+ }, z.core.$strict>;
58
+ type ItemEntry = z.infer<typeof ItemEntrySchema>;
59
+ declare const SigEntrySchema: z.ZodObject<{
60
+ cose_key: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
61
+ cose_sign1: z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
62
+ }, z.core.$strict>;
63
+ type SigEntry = z.infer<typeof SigEntrySchema>;
64
+ declare const SupersedesSchema: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
65
+ type Supersedes = z.infer<typeof SupersedesSchema>;
66
+ declare const VersionLiteralSchema: z.ZodLiteral<1>;
67
+ declare const PoeRecordSchema: z.ZodObject<{
68
+ v: z.ZodLiteral<1>;
69
+ items: z.ZodOptional<z.ZodArray<z.ZodObject<{
70
+ hashes: z.ZodRecord<z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
71
+ uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
72
+ enc: z.ZodOptional<z.ZodUnknown>;
73
+ }, z.core.$strict>>>;
74
+ merkle: z.ZodOptional<z.ZodArray<z.ZodObject<{
75
+ alg: z.ZodString;
76
+ root: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
77
+ leaf_count: z.ZodNumber;
78
+ uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
79
+ }, z.core.$strict>>>;
80
+ supersedes: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
81
+ sigs: z.ZodOptional<z.ZodArray<z.ZodObject<{
82
+ cose_key: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
83
+ cose_sign1: z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
84
+ }, z.core.$strict>>>;
85
+ crit: z.ZodOptional<z.ZodArray<z.ZodString>>;
86
+ }, z.core.$loose>;
87
+ type PoeRecord = z.infer<typeof PoeRecordSchema>;
88
+ declare const TOP_LEVEL_BASE_KEYS: ReadonlySet<string>;
89
+ declare const EXTENSION_KEY_VENDOR_RE: RegExp;
90
+ declare const EXTENSION_KEY_COMPANION_RE: RegExp;
91
+ declare function isExtensionKey(k: string): boolean;
92
+
93
+ export { type Argon2idParams, Argon2idParamsSchema, type ChunkedBytesArray, ChunkedBytesArraySchema, EXTENSION_KEY_COMPANION_RE, EXTENSION_KEY_VENDOR_RE, type EncryptionEnvelope, EncryptionEnvelopeSchema, HashDigestSchema, type HashesMap, HashesMapSchema, type ItemEntry, ItemEntrySchema, type MerkleCommit, MerkleCommitSchema, type PassphraseBlock, PassphraseBlockSchema, type PoeRecord, PoeRecordSchema, type SigEntry, SigEntrySchema, type Slot, SlotSchema, type Supersedes, SupersedesSchema, TOP_LEVEL_BASE_KEYS, type UriChunkArray, UriChunkArraySchema, VersionLiteralSchema, isExtensionKey };
package/dist/schema.js ADDED
@@ -0,0 +1,109 @@
1
+ import { z } from 'zod';
2
+
3
+ // src/schema.ts
4
+ var ChunkedBytesArraySchema = z.array(
5
+ z.instanceof(Uint8Array).refine((b) => b.length >= 1 && b.length <= 64, {
6
+ params: { code: "CHUNK_TOO_LARGE" }
7
+ })
8
+ ).min(1);
9
+ var UTF8_ENCODER = new TextEncoder();
10
+ var UriChunkArraySchema = z.array(
11
+ z.string().refine(
12
+ (s) => {
13
+ const n = UTF8_ENCODER.encode(s).length;
14
+ return n >= 1 && n <= 64;
15
+ },
16
+ { params: { code: "CHUNK_TOO_LARGE" } }
17
+ )
18
+ ).min(1);
19
+ var HashDigestSchema = z.instanceof(Uint8Array);
20
+ var HashesMapSchema = z.record(z.string(), HashDigestSchema);
21
+ var MerkleCommitSchema = z.object({
22
+ alg: z.string(),
23
+ root: z.instanceof(Uint8Array),
24
+ leaf_count: z.number().int().min(1),
25
+ uris: z.array(UriChunkArraySchema).min(1).optional()
26
+ }).strict();
27
+ var SlotSchema = z.object({
28
+ epk: z.instanceof(Uint8Array).optional(),
29
+ kem_ct: ChunkedBytesArraySchema.optional(),
30
+ wrap: z.instanceof(Uint8Array).optional()
31
+ });
32
+ var Argon2idParamsSchema = z.object({
33
+ m: z.number().int(),
34
+ t: z.number().int(),
35
+ p: z.number().int()
36
+ }).strict();
37
+ var PassphraseBlockSchema = z.object({
38
+ alg: z.string(),
39
+ salt: z.instanceof(Uint8Array).superRefine((bytes, ctx) => {
40
+ if (bytes.length < 16) {
41
+ ctx.addIssue({
42
+ code: "custom",
43
+ path: [],
44
+ message: `passphrase.salt length ${bytes.length} < 16`,
45
+ params: { code: "ENC_PASSPHRASE_SALT_TOO_SHORT" }
46
+ });
47
+ } else if (bytes.length > 64) {
48
+ ctx.addIssue({
49
+ code: "custom",
50
+ path: [],
51
+ message: `passphrase.salt length ${bytes.length} > 64`,
52
+ params: { code: "ENC_PASSPHRASE_SALT_TOO_LONG" }
53
+ });
54
+ }
55
+ }),
56
+ params: z.record(z.string(), z.unknown())
57
+ }).strict();
58
+ var EncryptionEnvelopeSchema = z.object({
59
+ scheme: z.unknown(),
60
+ aead: z.string(),
61
+ kem: z.string().optional(),
62
+ nonce: z.instanceof(Uint8Array),
63
+ slots: z.array(SlotSchema).optional(),
64
+ slots_mac: z.instanceof(Uint8Array).refine((b) => b.length === 32, {
65
+ params: { code: "ENC_SLOTS_MAC_INVALID_LENGTH" }
66
+ }).optional(),
67
+ passphrase: PassphraseBlockSchema.optional()
68
+ }).strict();
69
+ var ItemEntrySchema = z.object({
70
+ hashes: HashesMapSchema,
71
+ uris: z.array(UriChunkArraySchema).min(1).optional(),
72
+ // Captured as `unknown` so the validator can run the
73
+ // `ENC_REQUIRES_CONTENT_HASH` pre-check ahead of any inner-shape errors
74
+ // and surface the most informative code first.
75
+ enc: z.unknown().optional()
76
+ }).strict();
77
+ var SigEntrySchema = z.object({
78
+ cose_key: ChunkedBytesArraySchema.optional(),
79
+ cose_sign1: ChunkedBytesArraySchema
80
+ }).strict();
81
+ var SupersedesSchema = z.instanceof(Uint8Array).refine((b) => b.length === 32, {
82
+ params: { code: "SUPERSEDES_TX_INVALID_LENGTH" }
83
+ });
84
+ var VersionLiteralSchema = z.literal(1);
85
+ var PoeRecordSchema = z.looseObject({
86
+ v: VersionLiteralSchema,
87
+ items: z.array(ItemEntrySchema).optional(),
88
+ merkle: z.array(MerkleCommitSchema).optional(),
89
+ supersedes: SupersedesSchema.optional(),
90
+ sigs: z.array(SigEntrySchema).optional(),
91
+ crit: z.array(z.string()).optional()
92
+ });
93
+ var TOP_LEVEL_BASE_KEYS = /* @__PURE__ */ new Set([
94
+ "v",
95
+ "items",
96
+ "merkle",
97
+ "supersedes",
98
+ "sigs",
99
+ "crit"
100
+ ]);
101
+ var EXTENSION_KEY_VENDOR_RE = /^x-.+\n?$/;
102
+ var EXTENSION_KEY_COMPANION_RE = /^[a-z]+-.+\n?$/;
103
+ function isExtensionKey(k) {
104
+ return EXTENSION_KEY_VENDOR_RE.test(k) || EXTENSION_KEY_COMPANION_RE.test(k);
105
+ }
106
+
107
+ export { Argon2idParamsSchema, ChunkedBytesArraySchema, EXTENSION_KEY_COMPANION_RE, EXTENSION_KEY_VENDOR_RE, EncryptionEnvelopeSchema, HashDigestSchema, HashesMapSchema, ItemEntrySchema, MerkleCommitSchema, PassphraseBlockSchema, PoeRecordSchema, SigEntrySchema, SlotSchema, SupersedesSchema, TOP_LEVEL_BASE_KEYS, UriChunkArraySchema, VersionLiteralSchema, isExtensionKey };
108
+ //# sourceMappingURL=schema.js.map
109
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema.ts"],"names":[],"mappings":"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","file":"schema.js","sourcesContent":["// CIP-309 v1 PoE record Zod schemas.\n//\n// Scope: structural shape gate. The schema enforces per-field types, length\n// bounds (chunk size, digest length, supersedes length, nonce length,\n// passphrase salt length), closed-map invariants (`sigs[i]`, `slot`,\n// `passphrase`, `merkle[i]`), and the `v == 1` literal. Cross-field rules\n// (item.hashes content-hash binding when `enc` present, slots/passphrase\n// exclusivity, `crit[]` shape, registry membership of algorithm\n// identifiers, COSE_Sign1 structural decode, URI per-scheme shape rules)\n// fire in `validator.ts` so the validator can emit the precise structural\n// codes (`UNSUPPORTED_*_ALG`, `ENC_*`, `SIG_*`, `INVALID_URI`,\n// `CRIT_SHAPE_INVALID`, …) rather than a generic schema-mismatch.\n//\n// Refinements that DO live in the schema (because the validator's domain\n// pass lifts these as `SCHEMA_*` / `*_LENGTH_MISMATCH` codes directly):\n// - chunk size `[1, 64]` → `CHUNK_TOO_LARGE`\n// - 32-byte digest / 32-byte root / 32-byte supersedes → `HASH_DIGEST_LENGTH_MISMATCH`\n// / `SUPERSEDES_TX_INVALID_LENGTH`\n// - 24-byte nonce / 32-byte slots_mac →\n// `NONCE_LENGTH_MISMATCH` / `ENC_SLOTS_MAC_INVALID_LENGTH`\n// - passphrase salt 16..64 bytes → `ENC_PASSPHRASE_SALT_TOO_SHORT` /\n// `ENC_PASSPHRASE_SALT_TOO_LONG`\n//\n// Per-slot recipient lengths (`epk`, `kem_ct`, `wrap`) are NOT enforced here:\n// the required slot shape depends on the envelope-level `kem`, which a slot\n// cannot see in isolation. The KEM-driven slot descriptor in `validator.ts`\n// emits the precise `KEM_EPK_LENGTH_MISMATCH` / `KEM_CT_LENGTH_MISMATCH` /\n// `WRAP_LENGTH_MISMATCH` / `ENC_SLOT_INVALID_SHAPE` codes instead.\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Chunked-bytes / chunked-text arrays\n// =============================================================================\n\n// `[1* bstr .size (1..64)]`. A zero-length chunk (0 < 1) is rejected with the\n// SAME `CHUNK_TOO_LARGE` code as oversized chunks (any length outside\n// `[1, 64]`).\nexport const ChunkedBytesArraySchema = z\n .array(\n z.instanceof(Uint8Array).refine((b) => b.length >= 1 && b.length <= 64, {\n params: { code: 'CHUNK_TOO_LARGE' },\n }),\n )\n .min(1);\nexport type ChunkedBytesArray = z.infer<typeof ChunkedBytesArraySchema>;\n\n// `[1* tstr .size (1..64)]` — chunk byte length is the UTF-8-encoded length\n// (each `tstr` is wire-encoded as UTF-8). The `tstr .size (1..64)` pin is a\n// byte-count constraint, not a code-unit constraint.\nconst UTF8_ENCODER = new TextEncoder();\nexport const UriChunkArraySchema = z\n .array(\n z.string().refine(\n (s) => {\n const n = UTF8_ENCODER.encode(s).length;\n return n >= 1 && n <= 64;\n },\n { params: { code: 'CHUNK_TOO_LARGE' } },\n ),\n )\n .min(1);\nexport type UriChunkArray = z.infer<typeof UriChunkArraySchema>;\n\n// =============================================================================\n// Hashes map\n// =============================================================================\n//\n// `hashes` is a non-empty CBOR map keyed by content-hash algorithm identifier\n// (a CBOR text string from the content-hash registry) with the 32-byte digest\n// as value. cbor2 surfaces a text-keyed CBOR map as a plain JS object — z.record\n// admits any string key here. Both the registry-membership check\n// (`UNSUPPORTED_HASH_ALG`) and the per-algorithm digest-length check\n// (`HASH_DIGEST_LENGTH_MISMATCH`) live in the validator's domain pass so\n// each violation emits its precise code; the schema only enforces the\n// value is a CBOR byte string.\n\nexport const HashDigestSchema = z.instanceof(Uint8Array);\n\nexport const HashesMapSchema = z.record(z.string(), HashDigestSchema);\nexport type HashesMap = z.infer<typeof HashesMapSchema>;\n\n// =============================================================================\n// Top-level `merkle[]`\n// =============================================================================\n//\n// Each commit is a closed map `{alg, root, leaf_count, ? uris}`. `alg` is open\n// (registry membership is enforced in the validator's domain pass — unknown\n// identifiers emit `UNSUPPORTED_MERKLE_COMMIT_ALG`).\n\nexport const MerkleCommitSchema = z\n .object({\n alg: z.string(),\n root: z.instanceof(Uint8Array),\n leaf_count: z.number().int().min(1),\n uris: z.array(UriChunkArraySchema).min(1).optional(),\n })\n .strict();\nexport type MerkleCommit = z.infer<typeof MerkleCommitSchema>;\n\n// =============================================================================\n// Encryption envelope\n// =============================================================================\n\n// Per-slot recipient entry. The slot shape is KEM-driven:\n//\n// - x25519: `{ epk: bstr(32), wrap: bstr(48) }` — `epk` is the\n// ephemeral X25519 public key, `wrap` is the 32-byte CEK + 16-byte\n// ChaCha20-Poly1305 tag.\n// - mlkem768x25519: `{ kem_ct: [ bstr .size (1..64) ], wrap: bstr(48) }` —\n// `kem_ct` is the 1120-byte X-Wing `enc` carried as a chunked byte-string\n// array (the same `bytes-chunk-array` shape `sigs[i].cose_sign1` uses);\n// there is NO per-slot `epk` on the hybrid path.\n//\n// The `kem` identifier is hoisted to envelope scope (a per-slot `kem` would\n// be wire-bloat). The schema is deliberately PERMISSIVE:\n// `epk`, `kem_ct`, and `wrap` are all optional and `.strict()` is NOT applied.\n// Both the per-field length checks (`KEM_EPK_LENGTH_MISMATCH`,\n// `KEM_CT_LENGTH_MISMATCH`, `WRAP_LENGTH_MISMATCH`) and the KEM-driven\n// shape gate (which field MUST/MUST NOT be present for the declared `kem`,\n// emitting `ENC_SLOT_INVALID_SHAPE`) live in the validator's domain pass —\n// the structural schema cannot know the envelope `kem` from a slot in\n// isolation, and we want the precise KEM-aware code rather than a generic\n// schema mismatch. Because `.strict()` is dropped, the domain pass MUST\n// explicitly reject cross-KEM contamination (an x25519 slot carrying\n// `kem_ct`, or a hybrid slot carrying `epk`).\nexport const SlotSchema = z.object({\n epk: z.instanceof(Uint8Array).optional(),\n kem_ct: ChunkedBytesArraySchema.optional(),\n wrap: z.instanceof(Uint8Array).optional(),\n});\nexport type Slot = z.infer<typeof SlotSchema>;\n\n// Argon2id params `{m, t, p}` are a closed map. Each value MUST be a CBOR\n// unsigned integer; the FLOOR check (`m ≥ 65536`,\n// `t ≥ 3`, `p ≥ 1`) emits `ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW` in the\n// validator's domain pass — keeping it out of the schema lets us emit the\n// distinct salt-length code when salt itself is malformed too.\nexport const Argon2idParamsSchema = z\n .object({\n m: z.number().int(),\n t: z.number().int(),\n p: z.number().int(),\n })\n .strict();\nexport type Argon2idParams = z.infer<typeof Argon2idParamsSchema>;\n\n// Passphrase block. `alg` is open (registry membership checked in the\n// validator's domain pass → `ENC_PASSPHRASE_ALG_UNSUPPORTED`);\n// `params` is open here (validator narrows on the registered `alg` value and\n// emits `SCHEMA_UNKNOWN_FIELD` for extra keys, `ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW`\n// for sub-floor values). `salt` length floor/ceiling are schema-layer\n// refinements with the dedicated `ENC_PASSPHRASE_SALT_TOO_SHORT/TOO_LONG`\n// codes — they belong at the schema layer because a slot cannot otherwise\n// see the salt length.\nexport const PassphraseBlockSchema = z\n .object({\n alg: z.string(),\n salt: z.instanceof(Uint8Array).superRefine((bytes, ctx) => {\n if (bytes.length < 16) {\n ctx.addIssue({\n code: 'custom',\n path: [],\n message: `passphrase.salt length ${bytes.length} < 16`,\n params: { code: 'ENC_PASSPHRASE_SALT_TOO_SHORT' },\n });\n } else if (bytes.length > 64) {\n ctx.addIssue({\n code: 'custom',\n path: [],\n message: `passphrase.salt length ${bytes.length} > 64`,\n params: { code: 'ENC_PASSPHRASE_SALT_TOO_LONG' },\n });\n }\n }),\n params: z.record(z.string(), z.unknown()),\n })\n .strict();\nexport type PassphraseBlock = z.infer<typeof PassphraseBlockSchema>;\n\n// Sealed-PoE envelope. The wire format admits any combination of\n// `kem` / `slots` / `slots_mac` / `passphrase` keys (permissive superset);\n// cross-field invariants (slots ⊕ passphrase, slots ↔ slots_mac, slots\n// requires kem, content-hash binding, slots non-empty) are enforced in the\n// validator's domain pass so each violation emits its typed code rather\n// than a generic shape mismatch.\nexport const EncryptionEnvelopeSchema = z\n .object({\n scheme: z.unknown(),\n aead: z.string(),\n kem: z.string().optional(),\n nonce: z.instanceof(Uint8Array),\n slots: z.array(SlotSchema).optional(),\n slots_mac: z\n .instanceof(Uint8Array)\n .refine((b) => b.length === 32, {\n params: { code: 'ENC_SLOTS_MAC_INVALID_LENGTH' },\n })\n .optional(),\n passphrase: PassphraseBlockSchema.optional(),\n })\n .strict();\nexport type EncryptionEnvelope = z.infer<typeof EncryptionEnvelopeSchema>;\n\n// =============================================================================\n// Item entry\n// =============================================================================\n\nexport const ItemEntrySchema = z\n .object({\n hashes: HashesMapSchema,\n uris: z.array(UriChunkArraySchema).min(1).optional(),\n // Captured as `unknown` so the validator can run the\n // `ENC_REQUIRES_CONTENT_HASH` pre-check ahead of any inner-shape errors\n // and surface the most informative code first.\n enc: z.unknown().optional(),\n })\n .strict();\nexport type ItemEntry = z.infer<typeof ItemEntrySchema>;\n\n// =============================================================================\n// Sig entry\n// =============================================================================\n//\n// Closed CBOR map `{cose_sign1, ? cose_key}`. Canonical CBOR map-key sort\n// (RFC 8949 §4.2.1, bytewise lex on encoded keys) places `cose_key`\n// (length-8 tstr, `0x68`) BEFORE `cose_sign1` (length-10 tstr, `0x6a`); the\n// schema property-order is irrelevant — the canonical encoder handles it.\nexport const SigEntrySchema = z\n .object({\n cose_key: ChunkedBytesArraySchema.optional(),\n cose_sign1: ChunkedBytesArraySchema,\n })\n .strict();\nexport type SigEntry = z.infer<typeof SigEntrySchema>;\n\n// =============================================================================\n// Supersedence\n// =============================================================================\n\nexport const SupersedesSchema = z.instanceof(Uint8Array).refine((b) => b.length === 32, {\n params: { code: 'SUPERSEDES_TX_INVALID_LENGTH' },\n});\nexport type Supersedes = z.infer<typeof SupersedesSchema>;\n\n// =============================================================================\n// Top-level record\n// =============================================================================\n//\n// `v == 1` is a literal — a future major (`v: 2`) MUST be rejected with\n// `SCHEMA_INVALID_LITERAL`. `z.literal(1)` preserves the narrow `1` type for\n// the inferred `PoeRecord[\"v\"]` (so consumers can dispatch on it) and emits\n// Zod's `invalid_value` code which the validator's mapper lifts to\n// `SCHEMA_INVALID_LITERAL`.\n//\n// `looseObject` admits extension keys (matching `^x-.+` or `^[a-z]+-.+`); the\n// validator's domain pass rejects unknown keys that match neither pattern with\n// `SCHEMA_UNKNOWN_FIELD`.\nexport const VersionLiteralSchema = z.literal(1);\n\nexport const PoeRecordSchema = z.looseObject({\n v: VersionLiteralSchema,\n items: z.array(ItemEntrySchema).optional(),\n merkle: z.array(MerkleCommitSchema).optional(),\n supersedes: SupersedesSchema.optional(),\n sigs: z.array(SigEntrySchema).optional(),\n crit: z.array(z.string()).optional(),\n});\nexport type PoeRecord = z.infer<typeof PoeRecordSchema>;\n\n// =============================================================================\n// Closed top-level base-key registry\n// =============================================================================\n//\n// Used by the validator's domain pass to distinguish unknown-typo keys from\n// well-formed extension keys (`^x-.+` / `^[a-z]+-.+`).\nexport const TOP_LEVEL_BASE_KEYS: ReadonlySet<string> = new Set([\n 'v',\n 'items',\n 'merkle',\n 'supersedes',\n 'sigs',\n 'crit',\n]);\n\n// Extension-key namespaces. Anchored at both ends so an\n// embedded newline cannot smuggle a multi-segment key past the check: `.`\n// excludes `\\n` in JS, and the `\\n?$` tail tolerates exactly ONE trailing\n// newline (matching the Python validator's `re.fullmatch(r'^(x-.+|[a-z]+-.+)$')`\n// semantics, where `$` likewise admits a single trailing `\\n`). So `x-note\\n`\n// is an extension key, but `x-a\\nb`, `x-note\\n\\n`, and `x-\\n` are not.\nexport const EXTENSION_KEY_VENDOR_RE = /^x-.+\\n?$/;\nexport const EXTENSION_KEY_COMPANION_RE = /^[a-z]+-.+\\n?$/;\n\nexport function isExtensionKey(k: string): boolean {\n return EXTENSION_KEY_VENDOR_RE.test(k) || EXTENSION_KEY_COMPANION_RE.test(k);\n}\n"]}