11 KiB
NIP-DBSYNC — Minimal Mutation Events over Nostr
draft optional
Defines a minimal event format for publishing immutable application mutation events over Nostr.
This draft intentionally standardizes only the wire format for mutation transport. It does not standardize database replication strategy, conflict resolution, relay retention, or key derivation.
Abstract
This NIP defines one regular event kind, 5000, for signed mutation events.
A mutation event identifies:
- the object namespace being mutated,
- the object identifier within that namespace,
- the mutation operation,
- an optional parent mutation event,
- an application-defined payload.
The purpose of this NIP is to make signed mutation logs portable across Nostr clients and relays without requiring relays to implement database-specific behavior.
Motivation
Many applications need a way to distribute signed state changes across multiple publishers, consumers, or services.
Today this can be done with private event kinds, but private schemas make cross-implementation interoperability harder than necessary. This NIP defines a small shared envelope for mutation events while leaving application-specific state semantics in the payload.
This NIP is intended for use cases such as:
- synchronizing object changes between cooperating services,
- publishing auditable mutation logs,
- replaying application events from ordinary Nostr relays,
- bridging non-Nostr systems into a Nostr-based event stream.
This NIP is not a consensus protocol. It does not provide:
- total ordering,
- transactional guarantees,
- global conflict resolution,
- authorization rules,
- guaranteed relay retention.
Applications that require those properties MUST define them separately.
Specification
Event Kind
| Kind | Category | Name |
|---|---|---|
| 5000 | Regular | Mutation |
Kind 5000 is a regular event. Relays that support this NIP MAY store it like any other regular event.
This NIP does not require relays to:
- retain all historical events,
- index any specific tag beyond normal NIP-01 behavior,
- deliver events in causal or chronological order,
- detect or resolve conflicts.
Applications that depend on durable replay or custom indexing MUST choose relays whose policies satisfy those needs.
Event Structure
{
"id": "<32-byte lowercase hex>",
"pubkey": "<32-byte lowercase hex>",
"created_at": "<unix timestamp, seconds>",
"kind": 5000,
"tags": [
["r", "<resource namespace>"],
["i", "<object identifier>"],
["op", "<mutation operation>"],
["e", "<parent mutation event id>"]
],
"content": "<JSON-encoded application payload>",
"sig": "<64-byte lowercase hex>"
}
The content field is a JSON-encoded string. Its structure is defined below.
Tags
| Tag | Required | Description |
|---|---|---|
r |
Yes | Stable resource namespace for the mutated object type. Reverse-DNS style names are RECOMMENDED, for example com.example.accounts.user. |
i |
Yes | Opaque object identifier, unique within the r namespace. Consumers MUST treat this as a string. |
op |
Yes | Mutation operation. This NIP defines only upsert and delete. |
e |
No | Parent mutation event id, if the publisher wants to express ancestry. At most one e tag SHOULD be included in this version of the protocol. |
v |
No | Application payload schema version as a string. RECOMMENDED when the payload format may evolve over time. |
Tag Rules
Publishers:
- MUST include exactly one
rtag. - MUST include exactly one
itag. - MUST include exactly one
optag. - MUST set
opto eitherupsertordelete. - SHOULD include at most one
etag. - MAY include one
vtag.
Consumers:
- MUST ignore unknown tags.
- MUST NOT assume tag ordering.
- MUST treat the
etag as an ancestry hint, not as proof of global ordering.
Resource Namespaces
The r tag identifies an application-level object type.
This NIP does not define a global registry of resource namespaces. To reduce collisions, publishers SHOULD use a stable namespace they control, such as reverse-DNS notation.
Examples:
com.example.accounts.userorg.example.inventory.itemnet.example.billing.invoice
Publishers MUST document the payload schema associated with each resource namespace they use.
Content Payload
The content field MUST be a JSON-encoded object.
{
"value": {},
"patch": "merge"
}
| Field | Required | Description |
|---|---|---|
value |
Yes | Application-defined mutation payload. For upsert, this is the state fragment or full post-mutation state being published. For delete, this MAY be an empty object or a small reason object. |
patch |
No | How value should be interpreted. This NIP defines merge and replace. If omitted, consumers MUST treat it as application-defined. |
Payload Rules
For op = upsert:
valueMUST be a JSON object.- Publishers SHOULD publish either:
- a partial object intended to be merged, or
- a full post-mutation object intended to replace prior state.
- If the interpretation is important for interoperability, publishers SHOULD set
patchtomergeorreplace.
For op = delete:
valueMAY be{}.- Consumers MUST treat
deleteas an application-level tombstone signal. - This NIP does not define whether deletion means hard delete, soft delete, archival, or hiding. Applications MUST define that separately.
Serialization
All payload values MUST be JSON-serializable.
The following representations are RECOMMENDED:
| Type | Representation |
|---|---|
| Timestamp / datetime | ISO 8601 string |
| Decimal | String |
| Binary | Base64 string |
| Null | JSON null |
Publishers MAY define additional type mappings, but those mappings are application-specific and MUST be documented outside this NIP.
Ancestry and Replay
The optional e tag allows a publisher to indicate which prior mutation event it considered the parent when creating a new mutation.
This supports applications that want ancestry hints for:
- local conflict detection,
- replay ordering,
- branch inspection,
- audit tooling.
However:
- the
etag does not create a global ordering guarantee, - relays are not required to deliver parents before children,
- consumers MUST be prepared to receive out-of-order events,
- consumers MAY buffer, defer, ignore, or immediately apply parent-missing events according to local policy.
This NIP does not define a merge event format.
This NIP does not define conflict resolution. If two valid mutation events for the same (r, i) object are concurrent or incompatible, consumers MUST resolve them using application-specific rules.
Authorization
This NIP does not define who is authorized to publish mutation events for a given resource or object.
Authorization is application-specific.
Consumers MUST NOT assume that a valid Nostr signature alone authorizes a mutation. Consumers MUST apply their own trust policy, which MAY include:
- explicit pubkey allowlists,
- per-resource ACLs,
- external capability documents,
- relay-level write restrictions,
- application-specific verification.
This NIP does not define custodial keys, deterministic key derivation, shared cluster secrets, or delegation schemes.
Relay Behavior
A relay implementing only NIP-01 remains compatible with this NIP.
No new relay messages are required beyond REQ, EVENT, and CLOSE.
Relays:
- MAY index the
randitags using existing single-letter tag indexing conventions. - MAY apply normal retention, rate-limit, and access-control policies.
- MAY reject events that are too large or otherwise violate local policy.
- MUST NOT be expected to validate application payload semantics.
Applications that require stronger guarantees, such as durable retention or strict admission control, MUST obtain those guarantees from relay policy or from a separate protocol profile.
Subscription Filters
This NIP works with ordinary NIP-01 filters.
All mutations for one resource
{
"kinds": [5000],
"#r": ["com.example.accounts.user"]
}
Mutation history for one object
{
"kinds": [5000],
"#r": ["com.example.accounts.user"],
"#i": ["550e8400-e29b-41d4-a716-446655440000"]
}
Mutations from trusted authors
{
"kinds": [5000],
"authors": [
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
]
}
Applications SHOULD prefer narrow subscriptions over broad network-wide firehoses.
Examples
Upsert with parent
{
"id": "1111111111111111111111111111111111111111111111111111111111111111",
"pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"created_at": 1710500300,
"kind": 5000,
"tags": [
["r", "com.example.accounts.user"],
["i", "550e8400-e29b-41d4-a716-446655440000"],
["op", "upsert"],
["e", "0000000000000000000000000000000000000000000000000000000000000000"],
["v", "1"]
],
"content": "{\"value\":{\"email\":\"jane.doe@newdomain.com\",\"updated_at\":\"2025-03-15T14:35:00Z\"},\"patch\":\"merge\"}",
"sig": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
}
Delete tombstone
{
"id": "2222222222222222222222222222222222222222222222222222222222222222",
"pubkey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"created_at": 1710500600,
"kind": 5000,
"tags": [
["r", "com.example.accounts.user"],
["i", "550e8400-e29b-41d4-a716-446655440000"],
["op", "delete"],
["e", "1111111111111111111111111111111111111111111111111111111111111111"],
["v", "1"]
],
"content": "{\"value\":{\"reason\":\"user_requested\"}}",
"sig": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
}
Security Considerations
- Unauthorized writes: A valid signature proves authorship, not authorization. Consumers MUST enforce their own trust policy.
- Replay: Old valid events may be redelivered by relays or attackers. Consumers SHOULD deduplicate by event id and apply local replay policy.
- Reordering: Events may arrive out of order. Consumers MUST NOT treat
created_atoreas a guaranteed total order. - Conflict flooding: Multiple valid mutations may target the same object. Consumers SHOULD rate-limit, bound buffering, and define local conflict policy.
- Sensitive data exposure: Nostr events are typically widely replicable. Publishers SHOULD NOT put secrets or regulated data in mutation payloads unless they provide application-layer encryption.
- Relay retention variance: Some relays will prune history. Applications that depend on full replay MUST choose relays accordingly or maintain an external archive.
Extension Points
Future drafts or companion NIPs may define:
- snapshot events for faster bootstrap,
- object-head or checkpoint events,
- capability or delegation profiles for authorized writers,
- standardized conflict-resolution profiles for specific application classes.
Such extensions SHOULD remain optional and MUST NOT change the meaning of kind 5000 mutation events defined here.
References
- NIP-01 — Basic protocol flow description