- Sashité for Developers
- Nostr
- NIPs
- Rule System
NIP-XX
Rule System
draft optional
This NIP defines a Nostr event kind by which a publisher names, signs and makes discoverable a rule system: the executable statement of the rules a turn-based, two-player abstract strategy board game of the chess family is played under. The event names an immutable program by its digest; sessions reference the event; and every consumer of a session — the players’ clients, observers, rating authorities — computes the same answers by running the same program, for as long as the program’s bytes exist.
Abstract
Defines kind: 3417, a regular event signed by the publisher of a rule system. The event carries the game the rule system implements, the SHA-256 digest of a WebAssembly module implementing it under a declared, versioned ABI, retrieval hints for the module, and documentary references to the specification and the sources the module was built from.
A rule system is identified by the event id, its behavior by the module’s digest, its provenance by the event’s pubkey. The founding events of a session reference the rule system by id (an e tag with the marker rules), and every rule question the session raises — what is legal, what a move does to the position, whether the position is terminal, how time is charged, which of a slot’s candidate moves is canonical, what the outcome is at a given instant — is by definition what the module answers. A revision of the rules is a new module, a new event, a new rule system; a session founded under a rule system keeps it for its whole life, whatever is published later.
Motivation
A session’s outcome is a function of its public events and of the rules it was founded under (kind 3425 §Natural state of events at the cutoff). For that function to be computable by anyone, forever, the rules must be named in a way that cannot drift and that does not depend on any party keeping software alive: a description of the rules is not enough — a description must be implemented, and an implementation that exists today may not exist in a year, while a session at three days per move may last one.
Naming an executable program by its digest closes the gap. The program is data: bytes that any party can store, mirror and verify without trusting anyone; running them takes a WebAssembly runtime — a browser, or a small interpreter — and nothing else. Two consumers holding the same bytes reach the same answer on the same events with no coordination, and a session founded under a given rule system remains playable, verifiable and ratable after that rule system has been superseded, because the bytes it names are the bytes it names.
Publishing the digest as a signed event rather than as a bare value in the founding events gives the rule system three things a digest alone lacks: an identity a relay can index (#e on the event id — every founding under these rules is one subscription), a provenance any consumer can check without a side channel (the publisher’s signature), and a place for the human-readable context of the rules (a label, the specification the module was built to, the sources it reproduces from) that does not enter the founding events.
This NIP fixes the format and the protocol-level guarantees of the event (immutability, identity, provenance, retrieval). It does not prescribe the module’s interface: the ABI the module implements is identified by name in the event, and its contract — the operations, their inputs and outputs, the encodings of positions and moves — is defined by the consuming application for each game, exactly as the encodings of positions (kind 3422 §Content) and moves (kind 3423 §Content) are.
Specification
Event kind
kind: 3417
Rule System events are regular events per NIP-01: immutable once signed and broadcast. A rule system is never edited in place: a revision is a new event (§Revision).
Signing party
The event is signed by the publisher of the rule system — any pubkey. The protocol imposes no trust on the publisher: the module’s bytes are verified against the digest the event carries, and what the module computes is the rule, whoever published it. What the signature establishes is provenance — who stands behind a rule system — on which consumers base their own policies (§Provenance).
Tags
| Tag | Cardinality | Value | Description |
|---|---|---|---|
game |
exactly one | game identifier | the game the rule system implements |
x |
exactly one | SHA-256 hex digest | the digest of the module’s bytes |
abi |
exactly one | ABI identifier | the interface the module implements |
url |
zero or more | URL | a retrieval hint for the module |
spec |
zero or one | SHA-256 hex digest + optional URL | the specification the module was built to |
source |
zero or one | URL + commit | the sources and build recipe the module reproduces from |
nonce |
exactly one | nonce + difficulty | NIP-13 proof of work |
Tags not listed here are ignored, as everywhere in Nostr; in particular NIP-94’s m (application/wasm) and size MAY be carried and are read by nobody but the consumer that chooses to.
The game
["game", "<game_id>"]
The game identifier names the game family the rule system implements, in the vocabulary of the founding kinds (kind 3420 §Match-terms tags): a non-empty ASCII string starting with a lowercase letter, followed by zero or more lowercase letters or digits, of at most 32 characters — ^[a-z][a-z0-9]{0,31}$. A founding event that references this rule system MUST carry the same game (§Referencing a rule system). Which variants of the game the rule system defines is not stated by the event: it is what the module reports, through its ABI.
The module
["x", "<sha256>"]
The second element is the SHA-256 digest, as 64 lowercase hexadecimal characters (^[0-9a-f]{64}$), of the bytes of a WebAssembly module — the rule system itself. The module is content-addressed: its digest is the identity of the rules’ behavior, and a module with a different digest is a different behavior, even for the same game and the same publisher. This kind defines the x tag as the digest of the module it names; the tag is single-letter, hence indexed by relays (#x finds every Rule System event naming a given module), and its use here is concordant with the idiom of NIP-94 and of Blossom, where x carries the hash of a file or a blob.
["abi", "<abi_id>"]
The second element is the identifier of the ABI the module implements — the contract through which a consumer asks the module its rule questions: the exported functions, the memory protocol, the operations and the encodings of their inputs and outputs. The identifier is a non-empty string of printable ASCII characters without whitespace, of at most 64 characters. Its vocabulary, and the contract each identifier names, are the consuming application’s for the game: this NIP does not prescribe them, as it does not prescribe the encodings of positions and moves. Every ABI contract MUST give the module a way to state its own identifier; a consumer reads it as the ABI prescribes and MUST refuse a module whose stated identifier differs from the tag’s (§Retrieval and verification).
["url", "<url>"]
Each url tag is a retrieval hint: a location from which the module can be fetched by its digest — a Blossom server (<server>/<sha256> or <server>/<sha256>.wasm), or any HTTP location the application understands. A hint is a convenience, never an identity: a consumer that retrieves the module MUST verify that its SHA-256 equals x and MUST NOT use a module whose digest differs, whatever the hint. Hints are not the only path to the bytes: the publisher’s Blossom server list (kind 10063) names servers from which any consumer can construct the module’s URL from its digest — a replaceable list, hence the publisher’s servers today — and any party may mirror the module, since its digest verifies it; a consumer’s own cache (§Retrieval and verification) is the durable path.
Documentary tags
["spec", "<sha256>", "<url>"]
The spec tag, which publishers SHOULD carry, pins, by SHA-256 digest with an optional retrieval hint, the specification the module was built to: the human-readable statement of the rules — the document a defect of the module would be judged against. It exists so that what a player commits to has a readable, content-addressed form beside the executable one. It is documentary: no consumer computes anything from it, and a disagreement between the specification and the module is a defect of the publication, repaired by a new rule system (§Revision), never by reading the specification over the module for an existing session.
["source", "<url>", "<commit>"]
The source tag, which publishers SHOULD carry, names the sources the module was built from — a repository URL and, as its third element, the commit or tag — for auditability: a build recipe published with the sources lets any party rebuild the module and compare digests. It is documentary in the same sense.
Proof of work
["nonce", "<nonce>", "<difficulty>"]
The nonce tag follows NIP-13. It is required, as on the suite’s open-authorship kinds: a Rule System event is a rare, deliberate publication, and the proof of work keeps the kind from being an avenue for spam on the relays that index it.
Retention
The event MUST NOT carry a NIP-40 expiration tag: NIP-40 authorizes relays to delete the event, and a Rule System referenced by a session must remain retrievable for as long as any session naming it may be evaluated — which is indefinitely. Relays serving the suite SHOULD retain Rule System events indefinitely and SHOULD NOT act on a NIP-09 deletion request against one; consumers MUST ignore such a request: a publisher who wishes to retract a rule system publishes a new one and stops founding under the old, and the sessions founded under the old are unaffected (§Revision).
Timing
A Rule System event is never attested and belongs to no session’s timing mode: its created_at is informational, and nothing in the suite is ordered by it. Which rule system a session is played under is fixed by the session’s founding events, not by when the rule system was published.
Content
The content field is a free-text, human-readable label for the rule system, with optional notes (a name, a version, what changed since the previous one). It MAY be empty. It is not normative: nothing about a session depends on it, and a consumer MUST NOT key any behavior on it. It MUST satisfy:
- Length: 0 to 4096 bytes inclusive (measured after NIP-01 JSON unescaping).
- Encoding: valid UTF-8.
- Forbidden characters: any code point in the
contentfield MUST NOT fall in the following ranges: - U+0000 to U+0008, U+000B, U+000C, U+000E to U+001F (C0 control characters, except line feed U+000A and horizontal tab U+0009).
- U+007F to U+009F (DEL and C1 control characters).
- U+202A to U+202E (bidirectional formatting and override characters).
- U+2066 to U+2069 (directional isolate characters).
Semantic constraints
A conforming Rule System event MUST satisfy all of the following:
- Exactly one
gametag is present, whose second element matches^[a-z][a-z0-9]{0,31}$. - Exactly one
xtag is present, whose second element matches^[0-9a-f]{64}$. - Exactly one
abitag is present, whose second element is a non-empty string of printable ASCII characters without whitespace, of at most 64 characters. - Every
urltag, if any, carries a non-empty URL as its second element. - At most one
spectag is present; when present, its second element matches^[0-9a-f]{64}$. - At most one
sourcetag is present; when present, its second element is a non-empty URL and its third element a non-empty commit or tag. - Exactly one
noncetag is present, carries its committed difficulty as its third element, and the event id satisfies NIP-13 against that committed difficulty — never against a relay’s advertised minimum (an ingestion policy, not conformance). - No
expirationtag is present. - The
contentfield satisfies §Content.
Every constraint is checkable from the event alone. Whether the module is retrievable, and whether it implements the ABI it claims, are not conformance matters of the event: they decide whether the rule system is usable (§Retrieval and verification). A non-conforming Rule System event MUST be ignored; no event referencing it is conforming (§Referencing a rule system).
Validation responsibility
Validation of a Rule System event against the semantic constraints (§Semantic constraints) is the responsibility of the client and the relay. A relay additionally enforces its advertised NIP-13 minimum difficulty at ingestion — a relay policy, distinct from conformance — and SHOULD retain the event indefinitely (§Retention). Whether the module the event names is retrievable and usable is the responsibility of every consumer that founds or evaluates a session under it (§Retrieval and verification); no relay checks it.
Retrieval and verification
A consumer establishes a rule system in two verifiable hops, neither of which trusts anything:
- The event, by id: retrieved from the relay hinted by the referencing event, or from any relay; its signature valid, its kind
3417, its semantic constraints satisfied. - The module, by digest: retrieved from a
urlhint, from a server in the publisher’s Blossom server list (kind10063), from a cache or from any mirror; its SHA-256 equal tox.
The consumer then instantiates the module and checks that the ABI identifier it states equals the abi tag. A rule system whose module cannot be retrieved, whose digest does not match, or whose stated ABI differs from the tag’s — or whose ABI the consumer does not implement — is unusable for that consumer: it MUST NOT found a session under it, and a session naming it is unverified for that consumer, never evaluated by other means. Likewise, a consumer that cannot retrieve the Rule System event a founding references treats that founding, and the session, as unverified, never as non-conforming: retrievability is a fact about relays, not about the event.
A consumer SHOULD cache both the event and the module of every rule system it has a session under, keyed by id and by digest, so that evaluating the session no longer depends on any network once the session has begun.
Provenance
The event’s pubkey is the rule system’s publisher. Because the module’s behavior is verified, not believed, the protocol needs no trust in the publisher; what a consumer may reasonably want is to know whose rules a session is played under, and the signature gives it that without a side channel. A client SHOULD present the publisher to the user before founding a session, and MAY restrict the rule systems it founds under, or evaluates, to publishers it recognizes — a policy of the client, not a rule of this NIP. Two Rule System events naming the same module (the same x) are two rule systems — same behavior, different identities, and, when the publishers differ, different provenance. A publisher SHOULD publish a given module once: a second event for the same x is a distinct rule system, not a re-announcement.
Revision
A rule system is never edited: the event is immutable, and so are the bytes it names. Any change to the rules — a rule, a table, a threshold, the ABI — is a new module with a new digest, published as a new event. The previous rule system remains exactly what it was: a session founded under it keeps it for its whole life (kind 3420 §Match-terms tags), a rematch of that session inherits it (kind 3420 §Rematch challenge), and every consumer keeps evaluating it under the same bytes. A publisher that adopts the new rule system founds under it from then on; nothing about the old one changes but the publisher’s use of it.
Referencing a rule system
A session’s founding events name the rule system by an e tag carrying the marker rules:
["e", "<rule_system_event_id>", "<relay_hint>", "rules"]
The Open Challenge (kind 3418), the Direct Challenge (kind 3420), the Pairing (kind 3419) and the Game Session (kind 3422) each carry exactly one such reference (the Game Session mirroring its founding’s), and so does a Composition (kind 3440), whose certificate holds under the rule system it names. The referenced event MUST be a conforming Rule System event whose game equals the referencing event’s game tag; a referencing event that names another kind, a non-conforming Rule System, or one of another game is non-conforming — and one whose reference cannot be retrieved is unverified (§Retrieval and verification). The relay hint (third element) is OPTIONAL but SHOULD be present; an empty string MAY stand in its position. The semantics of the reference — a matching term for a matchmaker, a commitment for the players, the one authority every Ply and Conclusion of the session is judged under — are those of kind 3420 §Match-terms tags.
Examples
Example 1 — A rule system for sanki
The publisher of a game platform signs the rule system its clients found under. The module implements the platform’s ABI for the game; the spec and source tags let anyone read the rules the module was built to and rebuild it. Ids, keys and digests below are placeholders; the event id shows the leading zeros its committed difficulty of 20 bits requires.
{
"kind": 3417,
"pubkey": "d0c1b2a3f4e5968778695a4b3c2d1e0f1a2b3c4d5e6f708192a3b4c5d6e7f809",
"created_at": 1700000000,
"tags": [
["game", "sanki"],
["x", "9f2c4b1e8d7a6053c2b1a09f8e7d6c5b4a392817f6e5d4c3b2a1908f7e6d5c4b"],
["abi", "example.sanki.kernel-abi/1"],
["url", "https://blobs.example.com/9f2c4b1e8d7a6053c2b1a09f8e7d6c5b4a392817f6e5d4c3b2a1908f7e6d5c4b.wasm"],
["spec", "3f6d1a0c9e4b2a7d5c8e1f0a9b3c7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c", "https://blobs.example.com/3f6d1a0c9e4b2a7d5c8e1f0a9b3c7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c.md"],
["source", "https://git.example.com/sanki-kernel-wasm", "v1.0.0"],
["nonce", "776412", "20"]
],
"content": "Sanki — reference rules, 2026-09",
"id": "000004c1a6e2b9d3f7e0a2c4b6d8f1e3a5c7e9b0d2f4a6c8e0b2d4f6a8c0e2b4",
"sig": "..."
}
A Direct Challenge (kind 3420) founding a session under it carries ["e", "000004c1a6e2b9d3f7e0a2c4b6d8f1e3a5c7e9b0d2f4a6c8e0b2d4f6a8c0e2b4", "wss://relay.example.com", "rules"] beside its ["game", "sanki"]; the Game Session that accepts it mirrors the reference.
Client guidelines
Clients implementing kind: 3417 SHOULD:
- Before founding, entering a pool, challenging or accepting: retrieve the Rule System event and the module, verify both (§Retrieval and verification), instantiate the module and check its ABI; do not publish a founding event under a rule system that is unusable (kinds
3418,3420,3422§Validation responsibility). - Cache the event and the module of every rule system the user has a session under, by id and by digest; evaluate the user’s sessions from the cache.
- Present the publisher (the event’s
pubkey) and the label (content) to the user; treat a rule system by an unrecognised publisher according to the client’s policy (§Provenance), and never run its module outside a sandbox. - Discovery: subscribe to kind
3417events by publisher (authors) to learn the rule systems a publisher offers; subscribe to founding kinds with#eon a Rule System id to follow every session under it. - Never key behavior on
content,specorsource: they are documentary. The rules are the module.
Security considerations
- The module is the rule. A rule system is exactly the bytes its
xnames; a defect of those bytes is the rule for every session founded under it, repaired only forward by a new rule system. Publishers SHOULD verify a module against its specification and its tests before publishing, and SHOULD publish the sources and the recipe (source) so that the build is auditable. - Untrusted code. A module is a program from a party the consumer may not know. Consumers MUST run it in a sandbox with no host capabilities (no imports, no clock, no network) and with a fixed memory — the WebAssembly model, applied strictly. A module that traps, or exhausts its memory, on a request yields no answer for that request, and the session is unverified for that consumer; a wall-clock watchdog a consumer places on a module from a publisher it does not recognize is a liveness guard, never a verdict.
- Publisher-controlled URLs. Fetching a
urlorspechint discloses the consumer to that host, and a hint may point at bytes of any size. Consumers SHOULD prefer their cache and known mirrors, SHOULD bound a download (NIP-94’ssize, when carried, states the expected length), and MUST verify the digest of anything they fetch before using it — which §The module already requires. - Content rendering.
contentis free text from an arbitrary publisher: clients MUST escape it for the rendering context and MUST NOT interpret it as markup, a link or an instruction. - Availability. A session is evaluable only while its rule system’s event and module are retrievable. Both are content-addressed and immutable, hence mirrorable by anyone; consumers cache them; publishers SHOULD keep them available indefinitely and SHOULD publish them on more than one server (§The module). An
expirationtag is forbidden, relays are asked to retain the event and to ignore deletion requests, and consumers ignore them in any case, for this reason (§Retention). - Provenance, not trust. The signature says who published; it does not vouch for the rules’ quality, and the protocol needs it to vouch for nothing — the rules are verified by running them. Clients decide, as policy, which publishers they found under.
- Same module, several events. Anyone can publish a Rule System event naming a module they did not build. Consumers identify rule systems by event id and provenance by
pubkey, so such an event is a distinct rule system, of that publisher’s, behaving like the original; it neither alters nor impersonates the original. - Proof of work protects the kind against spam on the relays that index it; it is a conformance condition against the committed difficulty (constraint 7), independent of any relay’s policy.
References
- NIP-01 — Basic protocol flow description
- NIP-09 — Event Deletion Request
- NIP-13 — Proof of Work
- NIP-40 — Expiration Timestamp
- NIP-94 — File Metadata (the
xfile-hash idiom) - Blossom — Blobs Stored Simply on Mediaservers (BUD-01 retrieval by digest, BUD-03 user server list)
- WebAssembly Core Specification — the execution model of the module
- Direct Challenge — the
rulesreference and the match terms of a session - Game Session — the founding that mirrors the reference
- Conclusion — the verdict the rule system yields, binding by correctness
