Skip to content

Commit 71ffba4

Browse files
authored
Add NIP-XX: Time Capsules specification
This NIP defines time-locked capsules: encrypted Nostr events that become readable only at/after a target timestamp or when a threshold of designated witnesses publish unlock shares. It includes specifications for event kinds, unlock modes, protocol flow, client behavior, security considerations, and examples.
1 parent fd9c627 commit 71ffba4

File tree

1 file changed

+317
-0
lines changed

1 file changed

+317
-0
lines changed

xx.md

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
# NIP-XX: Time Capsules
2+
3+
`draft` `optional`
4+
5+
This NIP defines time-locked capsules: encrypted Nostr events that become readable only at/after a target timestamp or when a threshold of designated witnesses publish unlock shares. This enables delayed revelation, threshold cryptography, digital inheritance, and whistleblowing protection.
6+
7+
Time-locked capsules allow content to be:
8+
9+
- Released automatically after a specific timestamp
10+
- Unlocked when multiple witnesses collaborate
11+
- Made accessible after long periods for digital inheritance
12+
- Protected with built-in delays for sensitive material
13+
14+
## Event Kinds
15+
Permalink: Event Kinds
16+
17+
- `1990`: Time Capsule (regular)
18+
- `30095`: Time Capsule (parameterized replaceable; keyed by `d` tag)
19+
- `1991`: Time Capsule Unlock Share
20+
- `1992`: Time Capsule Share Distribution
21+
22+
## Specification
23+
Permalink: Specification
24+
25+
### Time Capsule Events (kinds `1990` and `30095`)
26+
Permalink: Time Capsule Events
27+
28+
A time capsule event contains encrypted content and unlock conditions.
29+
30+
#### Required tags
31+
32+
- `u`: Unlock configuration in format `["u","<mode>","<param1>","<value1>",...]`
33+
- `p`: Witness pubkeys (one or more) - `["p","<witness_pubkey_hex>"]`
34+
- `w-commit`: Merkle root commitment - `["w-commit","<hex_merkle_root>"]`
35+
- `enc`: Encryption method - `["enc","nip44:v2"]`
36+
- `loc`: Storage location - `["loc","inline"|"https"|"blossom"|"ipfs"]`
37+
38+
#### Optional tags
39+
40+
- `d`: Identifier (required for kind `30095`) - `["d","<capsule-id>"]`
41+
- `uri`: External content URI (required when `loc != "inline"`) - `["uri","<url>"]`
42+
- `sha256`: Content integrity hash - `["sha256","<hex_hash>"]`
43+
- `expiration`: Expiration timestamp per NIP-40 - `["expiration","<unix>"]`
44+
- `alt`: Human-readable description - `["alt","<description>"]`
45+
46+
#### Content
47+
48+
The `content` field MUST contain a base64-encoded NIP-44 v2 encrypted payload. When `loc` is `"inline"`, the entire encrypted content is in this field. When `loc` is external, this field MAY be empty and the `uri` tag points to the encrypted content.
49+
50+
### Unlock Modes
51+
Permalink: Unlock Modes
52+
53+
#### Threshold Mode
54+
55+
```plaintext
56+
["u","threshold","t","<t>","n","<n>","T","<unix_unlock_time>"]
57+
```
58+
59+
- **t**-of-**n** witnesses must provide shares at/after timestamp `T`
60+
- Prevents unilateral early disclosure but not collusion of any `t` witnesses
61+
62+
#### Scheduled Mode
63+
64+
```plaintext
65+
["u","scheduled","T","<unix_unlock_time>"]
66+
```
67+
68+
- Indicates time-based operational release where witnesses or services intend to post shares after `T`
69+
- This mode is not a cryptographic timelock; a future revision may define a VDF-based trustless mode
70+
71+
Implementations MUST parse unknown `u` modes conservatively and treat them as unsupported.
72+
73+
### Unlock Share Events (kind `1991`)
74+
Permalink: Unlock Share Events
75+
76+
A witness posts one share after the unlock timestamp (with optional skew tolerance).
77+
78+
#### Required tags
79+
80+
- `e`: Capsule event reference - `["e","<capsule_event_id>"]`
81+
- `a`: Addressable reference (if capsule is parameterized replaceable) - `["a","30095:<pubkey_hex>:<d>"]`
82+
- `p`: Witness pubkey - `["p","<witness_pubkey_hex>"]`
83+
- `T`: Unlock time from capsule - `["T","<unix_timestamp>"]`
84+
85+
#### Content
86+
87+
- Base64 Shamir share for threshold mode
88+
- MAY be gift-wrapped (per NIP-59) to reduce metadata leakage
89+
- Clients MUST access the plaintext share after timestamp `T`
90+
91+
### Share Distribution Events (kind `1992`)
92+
Permalink: Share Distribution Events
93+
94+
Automates delivery of per-witness shares immediately after capsule creation.
95+
96+
#### Required tags
97+
98+
- `e`: Capsule event reference - `["e","<capsule_event_id>"]`
99+
- `a`: Addressable reference (if capsule is parameterized replaceable) - `["a","30095:<pubkey_hex>:<d>"]`
100+
- `p`: Recipient witness - `["p","<witness_pubkey_hex>"]`
101+
- `share-idx`: Share index - `["share-idx","<0..n-1>"]`
102+
- `enc`: Encryption method - `["enc","nip44:v2"]`
103+
104+
#### Content
105+
106+
NIP-44 v2 ciphertext containing the Shamir share destined for the witness. Only the intended witness can decrypt.
107+
108+
#### Validation Rules
109+
110+
- Event MUST be authored by the same pubkey as the capsule
111+
- The target `p` MUST appear in the capsule's witness list
112+
- `share-idx` MUST be within `[0, n-1]`
113+
114+
## Protocol Flow
115+
Permalink: Protocol Flow
116+
117+
1. **Create Capsule** (kind `1990` or `30095`)
118+
- Author generates random key `K` and encrypts payload with NIP-44 v2 → `C`
119+
- Selects witnesses (p tags), sets threshold `t`, witness count `n`, unlock time `T`
120+
- Computes `w-commit` over ordered witnesses
121+
- Publishes capsule with `content=C`, unlock config, witness list, commitment, storage location
122+
123+
2. **Distribute Shares** (kind `1992`) *(recommended)*
124+
- Split `K` using Shamir's Secret Sharing (t, n)
125+
- For each witness, publish `1992` with NIP-44 encrypted share for that witness
126+
- Include `share-idx` to maintain ordering
127+
128+
3. **Unlock** (kind `1991`)
129+
- At/after timestamp `T` (± skew tolerance), witnesses publish `1991` with plaintext shares
130+
- Clients collect any `t` valid shares, reconstruct `K`, and decrypt `C`
131+
132+
## Relay Behavior
133+
Permalink: Relay Behavior
134+
135+
### Validation
136+
137+
Relays MUST:
138+
139+
- Ensure required tags exist and are well-formed
140+
- For `1991`, reject shares where `now < T - skew` (recommended skew = 300 seconds)
141+
- For `1992`, validate author matches capsule author and recipient witness is in capsule's witness list
142+
143+
### Indexing
144+
145+
Relays SHOULD:
146+
147+
- Index `p` tags (witnesses) and `e` tags (capsule references) for discovery
148+
- Not rely on custom tag filters beyond NIP-01
149+
150+
### NIP-11 Capability Advertisement
151+
Permalink: NIP-11 Capability Advertisement
152+
153+
Relays implementing this NIP SHOULD advertise their support in their NIP-11 document:
154+
155+
```json
156+
{
157+
"supported_nips": [1, 11, ...],
158+
"software": "...",
159+
"version": "...",
160+
"capsules": {
161+
"v": "1",
162+
"modes": ["threshold","scheduled"],
163+
"max_inline_bytes": 131072
164+
}
165+
}
166+
```
167+
168+
### Error Handling
169+
170+
Early share rejection SHOULD use clear error messages per NIP-01 (e.g., `["OK", <event_id>, false, "invalid: too early"]`).
171+
172+
## Client Behavior
173+
Permalink: Client Behavior
174+
175+
- **Creation**: Generate `K`, encrypt payload with NIP-44 v2, produce capsule event, compute `w-commit`, publish
176+
- **Distribution**: Publish `1992` per witness with NIP-44 encrypted share; store local copy
177+
- **Monitoring**: Track timestamp `T`, watch for `1991` from witnesses; tolerate skew ±300s
178+
- **Reconstruction**: Verify witness membership via `w-commit`, collect any `t` valid shares, reconstruct `K`, decrypt content
179+
- **Integrity**: When `loc != inline`, fetch `uri`, verify `sha256` hash before decryption
180+
- **Discovery**: Use standard filters, e.g., witnesses look up:
181+
182+
```json
183+
{ "kinds": [1992], "#p": ["<witness_pubkey_hex>"] }
184+
```
185+
186+
## Security Considerations
187+
Permalink: Security Considerations
188+
189+
- **Witness Collusion**: Threshold prevents unilateral early disclosure but not collusion of any `t` witnesses. Choose diverse witnesses and set `t` accordingly.
190+
- **Early Disclosure**: Enforce timestamp `T` at relays (reject pre-`T - skew`) and at clients (ignore early shares).
191+
- **Time Manipulation**: Use trusted time sources where possible; keep small skew windows.
192+
- **External Storage Integrity**: Include `sha256` for any `uri` content.
193+
- **Spam/DoS**: Rate-limit `1991/1992` per capsule and per witness.
194+
195+
## Examples
196+
Permalink: Examples
197+
198+
### Time Capsule (kind 1990, threshold 2/3)
199+
200+
```json
201+
{
202+
"kind": 1990,
203+
"pubkey": "a2b3c4d5...",
204+
"created_at": 1735689600,
205+
"content": "base64_encoded_nip44v2_ciphertext",
206+
"tags": [
207+
["u","threshold","t","2","n","3","T","1735776000"],
208+
["p","f7234bd4..."],
209+
["p","a1a2a3a4..."],
210+
["p","b1b2b3b4..."],
211+
["w-commit","3a5f...c9"],
212+
["enc","nip44:v2"],
213+
["loc","inline"],
214+
["alt","Secret message requiring 2 of 3 witnesses"]
215+
]
216+
}
217+
```
218+
219+
### Time Capsule (kind 30095, external storage)
220+
221+
```json
222+
{
223+
"kind": 30095,
224+
"pubkey": "a2b3c4d5...",
225+
"created_at": 1735689600,
226+
"content": "",
227+
"tags": [
228+
["d","capsule-2025-07"],
229+
["u","threshold","t","3","n","5","T","1736000000"],
230+
["p","w1..."],
231+
["p","w2..."],
232+
["p","w3..."],
233+
["p","w4..."],
234+
["p","w5..."],
235+
["w-commit","9c01...ab"],
236+
["enc","nip44:v2"],
237+
["loc","https"],
238+
["uri","https://media.example/caps/abc"],
239+
["sha256","c0ffee..."],
240+
["alt","External ciphertext with integrity hash"]
241+
]
242+
}
243+
```
244+
245+
### Unlock Share (kind 1991)
246+
247+
```json
248+
{
249+
"kind": 1991,
250+
"pubkey": "a1a2a3a4...",
251+
"created_at": 1735776100,
252+
"content": "base64_shamir_share",
253+
"tags": [
254+
["e","...capsule_event_id..."],
255+
["a","30095:a2b3c4d5...:capsule-2025-07"],
256+
["p","a1a2a3a4..."],
257+
["T","1735776000"]
258+
]
259+
}
260+
```
261+
262+
### Share Distribution (kind 1992)
263+
264+
```json
265+
{
266+
"kind": 1992,
267+
"pubkey": "a2b3c4d5...",
268+
"created_at": 1735689700,
269+
"content": "base64_nip44v2_encrypted_share_for_witness",
270+
"tags": [
271+
["e","...capsule_event_id..."],
272+
["a","30095:a2b3c4d5...:capsule-2025-07"],
273+
["p","a1a2a3a4..."],
274+
["share-idx","1"],
275+
["enc","nip44:v2"]
276+
]
277+
}
278+
```
279+
280+
## Test Vectors
281+
Permalink: Test Vectors
282+
283+
### Test Vector A: Threshold 2-of-3
284+
285+
- Witnesses (ordered pubkeys): `hex_pubkey_A`, `hex_pubkey_B`, `hex_pubkey_C`
286+
- `w-commit` = MerkleRoot([(0, `hex_pubkey_A`), (1, `hex_pubkey_B`), (2, `hex_pubkey_C`)])
287+
- `T` = `1735776000`
288+
- Shares: `S0,S1,S2`; any two reconstruct `K`
289+
- Ciphertext: `C = NIP44v2_Encrypt(K, "hello world")``content = base64(C)`
290+
291+
Expected flow:
292+
293+
- `1990` event as shown above
294+
- `1992` to `hex_pubkey_B` with `share-idx=1` (content = NIP-44 encrypted `S1` to `hex_pubkey_B`)
295+
- `1991` from `hex_pubkey_B` and `hex_pubkey_C` after `T` (plaintext shares)
296+
- Client reconstructs `K` and decrypts `C``"hello world"`
297+
298+
## Rationale
299+
Permalink: Rationale
300+
301+
- Uses new kinds to avoid overloading existing semantics; unaware nodes ignore unknown kinds
302+
- Leverages standard `p`/`e` tags for discovery; avoids non-standard tag filtering
303+
- `w-commit` binds the witness set to prevent tampering
304+
- Parameterized replaceable variant (`30095`) supports pre-`T` fixes via the `d` tag and `a` addressing
305+
306+
## Backwards Compatibility
307+
Permalink: Backwards Compatibility
308+
309+
New kinds are ignored by unaware relays/clients. The `alt` tag provides a human-readable hint for unknown kinds. Use of standard `p` and `e` tags preserves discoverability via existing filters.
310+
311+
## Reference Implementation
312+
Permalink: Reference Implementation
313+
314+
A reference implementation is provided in [Shugur Relay](https://github.com/Shugur-Network/relay) project:
315+
316+
- Relay validation: `internal/relay/nips/nip_time_capsules.go`
317+
- Test suite: `tests/nips/test_time_capsules_comprehensive.sh`

0 commit comments

Comments
 (0)