Skip to content

Latest commit

 

History

History
300 lines (216 loc) · 16.8 KB

File metadata and controls

300 lines (216 loc) · 16.8 KB

JSON Schema Validation Boundaries

JSON Schema draft-07 validates structure — field types, string patterns, array shapes, required properties. Some NIP and MIP requirements are semantic — they depend on decoded values, cross-field relationships, cryptographic correctness, or external state. These cannot be expressed in JSON Schema and must be enforced by runtime validators, codegen-generated validators, or application code.

This document catalogs known gaps. See #136 for the full audit.

Summary

A comprehensive audit of all ~300 schemas against their upstream NIP/MIP specs identified ~270 validation gaps across 65+ NIPs and 4 MIPs, organized into 8 categories:

Category Count Description
Inter-event references ~52 e/a-tag kind checks, pubkey matching across events
Decoded content ~51 JSON-in-string, Markdown, BOLT11, bech32 TLV, PGN
Cross-field constraints ~50 Tag-to-tag comparisons, content-to-tag matching
Protocol state ~45 Relay behavior, HTTP context, authorization
Encrypted content ~25 NIP-44/NIP-04 ciphertext hiding inner structure
Computed values ~21 SHA-256, Schnorr signatures, bech32 checksums
Temporal ~16 Wall-clock comparisons, freshness windows
Cryptographic ~10 ECDH, DLEQ, P2PK, Cashu proofs

Draft-07 Keyword Limitations

Several gap patterns recur because of specific draft-07 limitations:

  1. No maxContains (added in draft 2019-09): Cannot enforce "exactly one p-tag" or "at most one e-tag" in heterogeneous tag arrays. Affects NIP-57 (exactly 1 p-tag), NIP-88 (unique option IDs).

  2. No contentSchema/contentMediaType enforcement: Cannot validate that a string field contains valid JSON matching another schema. Affects ~20 NIPs with JSON-in-string content fields.

  3. No cross-property value comparison: Cannot express since <= until, start < end, or "tag A value equals tag B value". Affects NIP-01 filters, NIP-52/53 calendars, NIP-32 labels.

  4. No "last item matching" in arrays: contains checks existence anywhere; no way to constrain the last item matching a pattern. Affects NIP-25 (last e-tag is reaction target).

  5. No current-time reference: Cannot express "created_at must be within 10 minutes of now". Affects NIP-42, NIP-43, NIP-98 authentication events.

Known Gaps

Computed Values

NIP-01: id is SHA-256 of serialized event

  • Schema: nips/nip-01/note/schema.yaml
  • Spec: NIP-01id = SHA-256 of [0, pubkey, created_at, kind, tags, content]
  • What the schema checks: 64-char lowercase hex string
  • What it cannot check: the value is the correct hash of the serialized event
  • Where to enforce: application code, runtime validator

NIP-01: sig is a valid Schnorr signature

  • Schema: nips/nip-01/note/schema.yaml
  • Spec: NIP-01sig = Schnorr signature of id by pubkey
  • What the schema checks: 128-char lowercase hex string
  • What it cannot check: cryptographic signature validity
  • Where to enforce: application code, runtime validator

NIP-19: bech32 checksum validation

  • Schemas: nips/nip-19/npub/schema.yaml, nips/nip-19/note/schema.yaml, etc.
  • Spec: NIP-19 — bech32 encoding includes a 6-character BCH error-detecting checksum
  • What the schema checks: character set regex and prefix matching
  • What it cannot check: the checksum is valid
  • Where to enforce: application code

NIP-57: SHA256(description) must match bolt11 description hash

  • Schema: nips/nip-57/kind-9735/schema.yaml
  • Spec: NIP-57 — hash of zap request must match the bolt11 invoice description hash
  • What the schema checks: bolt11 superficial bech32 format, description tag is a string
  • What it cannot check: SHA-256 computation, BOLT11 internal field extraction
  • Where to enforce: application code

NIP-94: x tag must be SHA-256 of actual file content

  • Schema: nips/nip-94/kind-1063/schema.yaml
  • Spec: NIP-94x tag is the SHA-256 of the referenced file
  • What the schema checks: 64-char hex string format
  • What it cannot check: the hash matches the actual file at the URL
  • Where to enforce: application code

MIP-05: kind:446 content decoded length (multiple of 280)

  • Schema: mips/mip-05/kind-446/schema.yaml
  • Spec: MIP-05 — decoded byte length must be a multiple of 280
  • What the schema checks: base64 format, minLength: 376
  • What it cannot check: decoded length is exactly 280n bytes
  • Where to enforce: runtime validator or codegen (schemata-codegen#35)

Cross-Field Tag Constraints

MIP-00: i-tag KeyPackageRef length vs mls_ciphersuite

  • Schema: mips/mip-00/tag/i/schema.yaml, mips/mip-00/kind-30443/schema.yaml
  • Spec: MIP-00 — KeyPackageRef hash size depends on ciphersuite
  • What the schema checks: hex format, length is one of 64/96/128 chars
  • What it cannot check: the i-tag length matches the mls_ciphersuite tag value
  • Where to enforce: runtime validator or codegen (schemata-codegen#36)

NIP-01: filter since <= until

  • Schema: nips/nip-01/messages/filter/schema.yaml
  • Spec: NIP-01since <= created_at <= until
  • What the schema checks: both are integers >= 0
  • What it cannot check: since <= until cross-field comparison
  • Where to enforce: application code

NIP-25: last e-tag must be the reaction target

  • Schema: nips/nip-25/kind-7/schema.yaml
  • Spec: NIP-25 — "The last e-tag MUST be the id of the note that is being reacted to"
  • What the schema checks: at least one e-tag exists (via contains)
  • What it cannot check: it is the last e-tag (no "last matching item" in draft-07)
  • Where to enforce: application code, codegen validators

NIP-32: l-tag namespace must match an L-tag value

  • Schema: nips/nip-32/kind-1985/schema.yaml
  • Spec: NIP-32 — l-tag's third element must correspond to an L-tag with that namespace value
  • What the schema checks: at least one l-tag and one target tag exist
  • What it cannot check: cross-tag value equality between l namespace and L value
  • Where to enforce: codegen validators

NIP-52/53: start must be before end

  • Schemas: nips/nip-52/kind-31922/schema.yaml, nips/nip-53/kind-30311/schema.yaml
  • Spec: start date/timestamp must be less than end
  • What the schema checks: format validation independently
  • What it cannot check: cross-tag value comparison
  • Where to enforce: codegen validators

NIP-57: exactly one p-tag (no maxContains in draft-07)

  • Schema: nips/nip-57/kind-9734/schema.yaml
  • Spec: NIP-57 — "It MUST have only one p tag"
  • What the schema checks: at least one p-tag via contains
  • What it cannot check: at most one (needs maxContains from draft 2019-09)
  • Where to enforce: codegen validators

Decoded Content

NIP-01: kind:0 content is stringified JSON metadata

  • Schema: nips/nip-01/kind-0/schema.yaml
  • Spec: NIP-01 — content is {name, about, picture} stringified JSON
  • What the schema checks: content is a string; schema.content.yaml validates the parsed structure separately
  • What it cannot check: the string parses as valid JSON matching the content schema
  • Where to enforce: application code

NIP-57: description tag contains JSON-encoded zap request

  • Schema: nips/nip-57/kind-9735/schema.yaml
  • Spec: NIP-57 — description tag value is JSON-encoded kind:9734 event
  • What the schema checks: tag is ["description", <string>]
  • What it cannot check: the string parses as valid JSON matching kind:9734 schema
  • Where to enforce: application code

NIP-19: TLV structure in nprofile/nevent/naddr

  • Schemas: nips/nip-19/nprofile/schema.yaml, nips/nip-19/nevent/schema.yaml, nips/nip-19/naddr/schema.yaml
  • Spec: NIP-19 — bech32 data contains binary TLV with required fields
  • What the schema checks: prefix matching and character set
  • What it cannot check: decoded binary TLV structure and required fields
  • Where to enforce: application code

Encrypted Content

NIP-59: seal/wrap layer integrity

  • Schemas: nips/nip-59/kind-13/schema.yaml, nips/nip-59/kind-1059/schema.yaml
  • Spec: NIP-59 — seal content is NIP-44 encrypted rumor; wrap content is NIP-44 encrypted seal
  • What the schema checks: non-empty content string, correct kind
  • What it cannot check: decryption correctness, pubkey consistency across layers
  • Where to enforce: application code

NIP-17: DM layers must be sealed then gift-wrapped

  • Schemas: nips/nip-17/kind-14/schema.yaml, nips/nip-17/kind-15/schema.yaml
  • Spec: NIP-17 — unsigned DMs must be sealed (kind:13) then gift-wrapped (kind:1059)
  • What the schema checks: kind 14/15 structure in isolation
  • What it cannot check: multi-layer nesting, encryption correctness
  • Where to enforce: application code

NIP-46: kind:24133 encrypted JSON-RPC

  • Schema: nips/nip-46/kind-24133/schema.yaml
  • Spec: NIP-46 — content is NIP-44 encrypted JSON-RPC with method enum
  • What the schema checks: content is non-empty string
  • What it cannot check: inner JSON-RPC structure, method names, request/response correlation
  • Where to enforce: application code

NIP-47: encrypted wallet request/response payloads

  • Schemas: nips/nip-47/kind-23194/schema.yaml through kind-23197/schema.yaml
  • Spec: NIP-47 — content is NIP-44 encrypted JSON with method, params, result, error
  • What the schema checks: content is non-empty string
  • What it cannot check: inner payload structure, method enum, error codes
  • Where to enforce: application code

Inter-Event References

NIP-09: deletion must reference events with same pubkey

  • Schema: nips/nip-09/kind-5/schema.yaml
  • Spec: NIP-09 — referenced event pubkeys must match deletion event pubkey
  • What the schema checks: at least one e/a tag
  • What it cannot check: referenced events share the same pubkey
  • Where to enforce: application code

NIP-57: zap receipt must mirror tags from zap request

  • Schema: nips/nip-57/kind-9735/schema.yaml
  • Spec: NIP-57 — receipt must include p, e, a tags from the zap request embedded in description
  • What the schema checks: p, bolt11, description tags required
  • What it cannot check: tag mirroring from the JSON-encoded request
  • Where to enforce: application code

NIP-88: response option IDs must match poll options

  • Schema: nips/nip-88/kind-1018/schema.yaml
  • Spec: NIP-88 — response tag option ID must exist in the referenced poll's option tags
  • What the schema checks: response tag has alphanumeric ID
  • What it cannot check: ID exists in the referenced poll event
  • Where to enforce: application code

Temporal Constraints

NIP-42: auth event created_at freshness

  • Schema: nips/nip-42/kind-22242/schema.yaml
  • Spec: NIP-42created_at must be within ~10 minutes of current time
  • What the schema checks: integer >= 0
  • What it cannot check: comparison with wall-clock time
  • Where to enforce: application code

NIP-98: HTTP auth event freshness

  • Schema: nips/nip-98/kind-27235/schema.yaml
  • Spec: NIP-98created_at within ~60 seconds of current time
  • What the schema checks: integer >= 0
  • What it cannot check: comparison with wall-clock time
  • Where to enforce: application code

Protocol State

NIP-01: replaceable event semantics

  • Spec: NIP-01 — kinds 10000-19999 replaceable, 20000-29999 ephemeral, 30000-39999 addressable
  • What the schema checks: specific kind constants
  • What it cannot check: replacement policy, tiebreaker rules, storage behavior
  • Where to enforce: relay implementation

NIP-29: group events must be sent to the group's relay

  • Schemas: nips/nip-29/kind-9000/schema.yaml through kind-9022/schema.yaml
  • Spec: NIP-29 — group events are only valid on the hosting relay
  • What the schema checks: h-tag exists
  • What it cannot check: relay affinity
  • Where to enforce: relay implementation

Cryptographic Validation

NIP-60: Cashu proof validity

  • Schema: nips/nip-60/kind-7375/schema.yaml
  • Spec: NIP-60 — proofs must have valid blinded signatures, unspent status
  • What the schema checks: nothing (encrypted content)
  • What it cannot check: keyset existence, signature verification, unspent status
  • Where to enforce: application code, mint API

NIP-61: DLEQ proof verification

  • Schema: nips/nip-61/kind-9321/schema.yaml
  • Spec: NIP-61 — Cashu proofs must include verifiable DLEQ proofs
  • What the schema checks: proof tag is ["proof", <string>]
  • What it cannot check: DLEQ cryptographic validity
  • Where to enforce: application code, crypto libraries

Categories of Limitations

Category Example Enforceable by
Computed values id = SHA-256 of event, sig = Schnorr signature, file hashes Application code
Decoded-value constraints JSON-in-string, BOLT11, bech32 TLV, Markdown, PGN Codegen validators, application code
Cross-field tag constraints Tag A's value determines valid values for Tag B, start < end Codegen validators
Cryptographic validation NIP-44 encryption, ECDH, DLEQ, P2PK, Cashu proofs Application code, crypto libraries
Inter-event references e-tag must reference event of specific kind, pubkey matching Application code, relay queries
Encrypted content NIP-44/NIP-04 ciphertext hiding inner JSON structure Application code (decrypt first)
Protocol state Replaceable events, relay routing, HTTP context, authorization Relay implementation, client code
Temporal constraints Freshness windows, expiration, start < end timestamps Application code

Content Schema Boundaries

Content schemas (schema.content.yaml) close many of the ~51 decoded-content gaps listed above by validating the decoded content field. However, they have their own boundaries:

  • Base64 content: Content schemas validate the encoded string (pattern, length) but cannot validate decoded binary. MLS KeyPackageBundle and EncryptedToken structure requires protocol-specific parsers beyond JSON Schema.
  • Plaintext content: Content schemas provide format-level validation (e.g., space-separated tokens, non-empty strings) but cannot validate semantic correctness (e.g., valid PGN moves, valid NWC method names).
  • Decode step is external: JSON Schema cannot express "parse this string as JSON and validate the result". The decode step (JSON.parse, base64 decode) must happen in application code or a validator wrapper before the content schema is applied.

Contributing

When adding new schemas, if you identify a spec requirement that JSON Schema cannot express, add it to this document and consider filing a schemata-codegen issue if the gap is enforceable by generated validators.