@estiva-app/protocol 0.1.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.
- package/CHANGELOG.md +57 -0
- package/LICENSE +21 -0
- package/README.md +158 -0
- package/dist/bridge.d.ts +91 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +138 -0
- package/dist/bridge.js.map +1 -0
- package/dist/events.d.ts +432 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +616 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/live.d.ts +205 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +398 -0
- package/dist/live.js.map +1 -0
- package/dist/nip19.d.ts +98 -0
- package/dist/nip19.d.ts.map +1 -0
- package/dist/nip19.js +320 -0
- package/dist/nip19.js.map +1 -0
- package/dist/nip98.d.ts +61 -0
- package/dist/nip98.d.ts.map +1 -0
- package/dist/nip98.js +134 -0
- package/dist/nip98.js.map +1 -0
- package/dist/sign.d.ts +67 -0
- package/dist/sign.d.ts.map +1 -0
- package/dist/sign.js +58 -0
- package/dist/sign.js.map +1 -0
- package/dist/subscriptions.d.ts +120 -0
- package/dist/subscriptions.d.ts.map +1 -0
- package/dist/subscriptions.js +68 -0
- package/dist/subscriptions.js.map +1 -0
- package/package.json +59 -0
- package/src/bridge.ts +198 -0
- package/src/events.ts +821 -0
- package/src/index.ts +159 -0
- package/src/live.ts +536 -0
- package/src/nip19.ts +354 -0
- package/src/nip98.ts +164 -0
- package/src/sign.ts +113 -0
- package/src/subscriptions.ts +200 -0
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/** A Nostr tag: an array of strings whose first element is the tag name. */
|
|
2
|
+
export type NostrTag = string[];
|
|
3
|
+
/** An event before signing. `pubkey` is 64-char lowercase hex. */
|
|
4
|
+
export interface UnsignedEvent {
|
|
5
|
+
pubkey: string;
|
|
6
|
+
created_at: number;
|
|
7
|
+
kind: number;
|
|
8
|
+
tags: NostrTag[];
|
|
9
|
+
content: string;
|
|
10
|
+
}
|
|
11
|
+
/** A signed event, ready to submit to the relay. */
|
|
12
|
+
export interface SignedEvent extends UnsignedEvent {
|
|
13
|
+
id: string;
|
|
14
|
+
sig: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Buzz kind numbers used across the suite (crates/buzz-core/src/kind.rs).
|
|
18
|
+
*
|
|
19
|
+
* The union of what Peek and Ship each declared before SHA-3. A kind number is
|
|
20
|
+
* a fact about the relay, not about an app, so there is no reason for two apps
|
|
21
|
+
* to hold different subsets of it — and a subset is how an app ends up unable to
|
|
22
|
+
* *read* a kind its neighbour writes.
|
|
23
|
+
*/
|
|
24
|
+
export declare const KIND: {
|
|
25
|
+
readonly PROFILE: 0;
|
|
26
|
+
readonly DELETION: 5;
|
|
27
|
+
readonly REACTION: 7;
|
|
28
|
+
readonly STREAM_MESSAGE: 9;
|
|
29
|
+
readonly NIP29_PUT_USER: 9000;
|
|
30
|
+
readonly NIP29_EDIT_METADATA: 9002;
|
|
31
|
+
readonly NIP29_CREATE_GROUP: 9007;
|
|
32
|
+
readonly NIP29_DELETE_GROUP: 9008;
|
|
33
|
+
/**
|
|
34
|
+
* Estiva assertion — a statement *about* something in the channel, rather
|
|
35
|
+
* than a message in it (PEEK-128). `resolution` is the first and, for now,
|
|
36
|
+
* only subtype; the `t` tag names it so later subtypes can join without a
|
|
37
|
+
* new kind.
|
|
38
|
+
*
|
|
39
|
+
* The number sits in the **regular** range (1000–9999), which is what makes
|
|
40
|
+
* it stored and append-only. A replaceable kind would overwrite the previous
|
|
41
|
+
* assertion and destroy exactly the history this exists to keep. It clears
|
|
42
|
+
* NIP-29's 9000–9030 block on purpose.
|
|
43
|
+
*/
|
|
44
|
+
readonly ASSERTION: 9101;
|
|
45
|
+
/**
|
|
46
|
+
* NIP-22 comment (REW-10). Ship's comments are posted into the project's
|
|
47
|
+
* Folder — which *is* a Peek topic's channel — so they arrive in a channel as
|
|
48
|
+
* ordinary messages and must be read alongside `STREAM_MESSAGE`.
|
|
49
|
+
*
|
|
50
|
+
* Both kinds, permanently: a `kind:9` is not replaceable, so every comment
|
|
51
|
+
* written before Ship flipped stays `kind:9` forever and this pair can never
|
|
52
|
+
* shrink to one.
|
|
53
|
+
*/
|
|
54
|
+
readonly COMMENT: 1111;
|
|
55
|
+
/** NIP-84 highlight. */
|
|
56
|
+
readonly HIGHLIGHT: 9802;
|
|
57
|
+
/** NIP-FC File — see docs/buzz-compat/nips/NIP-FC.md in the Peek repo. */
|
|
58
|
+
readonly FILE: 30840;
|
|
59
|
+
/** NIP-FC Component. */
|
|
60
|
+
readonly COMPONENT: 30841;
|
|
61
|
+
/**
|
|
62
|
+
* NIP-42 relay auth — the challenge response that turns a connected socket
|
|
63
|
+
* into an authenticated one (`Kind::Authentication`, PEE-5).
|
|
64
|
+
*
|
|
65
|
+
* Unlike every other kind here, this one is **never published**: Buzz builds
|
|
66
|
+
* it into `buzz-auth/src/nip42.rs`, never stores it, and never logs it —
|
|
67
|
+
* "AUTH events are never stored or logged (may contain bearer tokens)" is a
|
|
68
|
+
* comment in that file. It goes over the socket and is gone.
|
|
69
|
+
*/
|
|
70
|
+
readonly RELAY_AUTH: 22242;
|
|
71
|
+
readonly HTTP_AUTH: 27235;
|
|
72
|
+
};
|
|
73
|
+
/** `build_reaction` (builders.rs:463) caps the emoji at 64 chars. */
|
|
74
|
+
export declare const MAX_EMOJI_CHARS = 64;
|
|
75
|
+
/** kind:9 content cap — `check_content(content, 64 * 1024)` in build_message. */
|
|
76
|
+
export declare const MAX_MESSAGE_BYTES: number;
|
|
77
|
+
/** `mention_tags` in builders.rs rejects more than this many mentions. */
|
|
78
|
+
export declare const MAX_MENTIONS = 50;
|
|
79
|
+
/**
|
|
80
|
+
* NIP-01 event id: sha256 over the canonical serialization
|
|
81
|
+
* `[0, pubkey, created_at, kind, tags, content]`.
|
|
82
|
+
*
|
|
83
|
+
* `JSON.stringify` produces exactly the escaping NIP-01 requires (`\n`, `\"`,
|
|
84
|
+
* `\\`, `\r`, `\t`, `\b`, `\f`, `\uXXXX` for other control chars) with no
|
|
85
|
+
* insignificant whitespace, so no custom serializer is needed.
|
|
86
|
+
*
|
|
87
|
+
* Cross-checked against `nostr-tools/pure`'s `getEventHash` over 103 events
|
|
88
|
+
* recorded from production: all 103 ids agree. See `test/oracle.test.ts` — the
|
|
89
|
+
* third-party implementation is a devDependency of this package and a dependency
|
|
90
|
+
* of nothing, so the check costs consumers no bytes.
|
|
91
|
+
*/
|
|
92
|
+
export declare function computeEventId(e: UnsignedEvent): string;
|
|
93
|
+
/** Apps store ms; Nostr `created_at` is seconds. */
|
|
94
|
+
export declare function toNostrSeconds(ms: number): number;
|
|
95
|
+
/**
|
|
96
|
+
* Buzz's `canonical_channel_name` (crates/buzz-core/src/channel.rs:15):
|
|
97
|
+
* strip leading '#' and whitespace, then trim the end. Applied by
|
|
98
|
+
* `build_create_channel` before the name reaches the tag, so we must match it or
|
|
99
|
+
* our ids diverge for any title with a leading '#' or space.
|
|
100
|
+
*/
|
|
101
|
+
export declare function canonicalChannelName(name: string): string;
|
|
102
|
+
/** NIP-01 addressable reference: `<kind>:<pubkey>:<d-tag>`. */
|
|
103
|
+
export declare function addr(kind: number, pubkey: string, dTag: string): string;
|
|
104
|
+
/**
|
|
105
|
+
* kind:0 profile — mirrors `build_profile` (builders.rs:537).
|
|
106
|
+
*
|
|
107
|
+
* Buzz builds the content with `serde_json::Map`, which is a `BTreeMap` unless
|
|
108
|
+
* the `preserve_order` feature is enabled. It is not enabled in Buzz's
|
|
109
|
+
* workspace, so **keys serialize in alphabetical order**: about, display_name,
|
|
110
|
+
* name, nip05, picture. We sort to match; getting this wrong changes the content
|
|
111
|
+
* string and therefore the event id.
|
|
112
|
+
*
|
|
113
|
+
* An app must not call this: the identity service is the sole publisher of
|
|
114
|
+
* `kind:0` and refuses it for every app before consulting any allowlist
|
|
115
|
+
* (SPEC §4.2). It is here because the *identity service* and the seed scripts
|
|
116
|
+
* need it, and because a second copy of this key ordering is the kind of thing
|
|
117
|
+
* that drifts.
|
|
118
|
+
*/
|
|
119
|
+
export declare function buildProfile(pubkey: string, createdAtMs: number, fields: {
|
|
120
|
+
display_name?: string;
|
|
121
|
+
name?: string;
|
|
122
|
+
picture?: string;
|
|
123
|
+
about?: string;
|
|
124
|
+
nip05?: string;
|
|
125
|
+
}): UnsignedEvent;
|
|
126
|
+
/** Who a pubkey belongs to, as far as the relay knows. */
|
|
127
|
+
export interface Profile {
|
|
128
|
+
/** kind:0 `display_name` or `name`. Absent when nobody has published one. */
|
|
129
|
+
displayName?: string;
|
|
130
|
+
/** kind:0 `picture` — an avatar URL chosen by whichever app published it. */
|
|
131
|
+
picture?: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The inverse of `buildProfile`: a kind:0 event to the fields an app shows.
|
|
135
|
+
*
|
|
136
|
+
* Both name keys are read because both are written: Peek publishes
|
|
137
|
+
* `display_name`, Ship publishes `name`, and a person who has used both apps
|
|
138
|
+
* should resolve either way rather than on a coin flip. Anything that will not
|
|
139
|
+
* parse costs a name, never the read that asked for it.
|
|
140
|
+
*
|
|
141
|
+
* One parser is what keeps a person from having two names depending on which
|
|
142
|
+
* app they appear in — which is the whole reason this is not left to each
|
|
143
|
+
* consumer.
|
|
144
|
+
*/
|
|
145
|
+
export declare function parseProfile(event: {
|
|
146
|
+
content: string;
|
|
147
|
+
} | undefined): Profile;
|
|
148
|
+
export type ChannelVisibility = 'open' | 'private';
|
|
149
|
+
export type ChannelKind = 'stream' | 'forum' | 'dm';
|
|
150
|
+
/**
|
|
151
|
+
* kind:9007 create channel — mirrors `build_create_channel` (builders.rs:674).
|
|
152
|
+
* Tag order is fixed: h, name, [visibility], [channel_type], [about], [ttl].
|
|
153
|
+
*/
|
|
154
|
+
export declare function buildCreateChannel(pubkey: string, createdAtMs: number, args: {
|
|
155
|
+
channelUuid: string;
|
|
156
|
+
name: string;
|
|
157
|
+
visibility?: ChannelVisibility;
|
|
158
|
+
channelType?: ChannelKind;
|
|
159
|
+
about?: string;
|
|
160
|
+
ttlSeconds?: number;
|
|
161
|
+
}): UnsignedEvent;
|
|
162
|
+
/**
|
|
163
|
+
* kind:9008 NIP-29 delete-group — what makes a deleted container actually gone.
|
|
164
|
+
*
|
|
165
|
+
* Deleting a topic in Peek used to remove it and its messages from Convex and
|
|
166
|
+
* publish nothing at all. Create propagated as a 9007; delete propagated
|
|
167
|
+
* nowhere. So a conversation somebody deleted, believing it gone, stayed
|
|
168
|
+
* readable to every admitted member indefinitely — measured on production at 145
|
|
169
|
+
* messages across 37 channels with zero deletion requests against any of them
|
|
170
|
+
* (PEEK-170).
|
|
171
|
+
*
|
|
172
|
+
* **9008 rather than a kind:5 per message**, and the difference is capability
|
|
173
|
+
* rather than efficiency. Buzz rejects a multi-target kind:5 outright, and
|
|
174
|
+
* NIP-09 is honoured only for the key that signed the original — so
|
|
175
|
+
* per-message deletion could never reach anybody else's messages in the
|
|
176
|
+
* container. Delete-group soft-deletes the channel, and Buzz's read guards
|
|
177
|
+
* (`c.deleted_at IS NULL` in both `get_accessible_channels` and `is_member`)
|
|
178
|
+
* then hide the whole container regardless of who wrote what is in it.
|
|
179
|
+
*
|
|
180
|
+
* Requires the channel's **owner** — Buzz enforces "only owner can delete
|
|
181
|
+
* group" in `validate_admin_event`, and a container's creator becomes its owner
|
|
182
|
+
* at create. Anybody else is refused, which the caller reports rather than hides.
|
|
183
|
+
*/
|
|
184
|
+
export declare function buildDeleteChannel(pubkey: string, createdAtMs: number, args: {
|
|
185
|
+
channelUuid: string;
|
|
186
|
+
}): UnsignedEvent;
|
|
187
|
+
/**
|
|
188
|
+
* kind:9002 edit metadata — renaming a channel (PEE-2).
|
|
189
|
+
*
|
|
190
|
+
* Buzz applies each recognised tag it finds (`name`, `about`, `archived`,
|
|
191
|
+
* `topic`, `purpose`, `visibility`, `ttl`) and refuses an event carrying none
|
|
192
|
+
* of them, so this builds exactly the fields asked for and nothing else.
|
|
193
|
+
*
|
|
194
|
+
* The name goes through `canonicalChannelName` for the same reason
|
|
195
|
+
* `buildCreateChannel` does: Buzz canonicalises before storing, so sending the
|
|
196
|
+
* raw string means the app and the relay disagree about what the container is
|
|
197
|
+
* called. A name that canonicalises away to nothing is not a rename, and
|
|
198
|
+
* throwing here is better than a round trip to be told so.
|
|
199
|
+
*
|
|
200
|
+
* Requires the channel's owner or admin — the relay refuses anyone else.
|
|
201
|
+
*/
|
|
202
|
+
export declare function buildEditChannelMetadata(pubkey: string, createdAtMs: number, args: {
|
|
203
|
+
channelUuid: string;
|
|
204
|
+
name?: string;
|
|
205
|
+
about?: string;
|
|
206
|
+
}): UnsignedEvent;
|
|
207
|
+
export type MemberRole = 'owner' | 'admin' | 'member';
|
|
208
|
+
/**
|
|
209
|
+
* kind:9000 add member — mirrors `build_add_member` (builders.rs:565).
|
|
210
|
+
* Tag order: h, p, [role]. The target pubkey is lowercased by Buzz.
|
|
211
|
+
*/
|
|
212
|
+
export declare function buildAddMember(pubkey: string, createdAtMs: number, args: {
|
|
213
|
+
channelUuid: string;
|
|
214
|
+
targetPubkey: string;
|
|
215
|
+
role?: MemberRole;
|
|
216
|
+
}): UnsignedEvent;
|
|
217
|
+
/**
|
|
218
|
+
* kind:7 reaction — mirrors `build_reaction` (builders.rs:463).
|
|
219
|
+
*
|
|
220
|
+
* Note there is **no `h` tag**: Buzz derives the channel from the *target's*
|
|
221
|
+
* `#e` tag and explicitly ignores a client-supplied `#h` (`NOSTR.md`). Adding one
|
|
222
|
+
* would change the event id for no benefit.
|
|
223
|
+
*/
|
|
224
|
+
export declare function buildReaction(pubkey: string, createdAtMs: number, args: {
|
|
225
|
+
targetEventId: string;
|
|
226
|
+
emoji: string;
|
|
227
|
+
}): UnsignedEvent;
|
|
228
|
+
/**
|
|
229
|
+
* kind:5 deletion — mirrors `build_remove_reaction` (builders.rs:495).
|
|
230
|
+
*
|
|
231
|
+
* Buzz accepts self-authored deletions (and an owner deleting their agent's).
|
|
232
|
+
* NIP-09 is a *request*, author-scoped, and it removes the record rather than
|
|
233
|
+
* the work — so an app must hide the control from non-authors rather than offer
|
|
234
|
+
* one that silently fails (SPEC §6.5).
|
|
235
|
+
*/
|
|
236
|
+
export declare function buildDeletion(pubkey: string, createdAtMs: number, args: {
|
|
237
|
+
targetEventId: string;
|
|
238
|
+
}): UnsignedEvent;
|
|
239
|
+
/**
|
|
240
|
+
* NIP-10 reply context.
|
|
241
|
+
*
|
|
242
|
+
* Mirrors Buzz's `ThreadRef` + `thread_tags` (builders.rs:173), which has a
|
|
243
|
+
* detail that is easy to get wrong: for a **direct** reply (parent is the root)
|
|
244
|
+
* Buzz emits a SINGLE tag marked `"reply"` — not a `"root"` tag. Only a nested
|
|
245
|
+
* reply emits both. Getting this wrong changes the event id and, worse, produces
|
|
246
|
+
* threads Buzz's clients read differently.
|
|
247
|
+
*/
|
|
248
|
+
export interface ThreadRef {
|
|
249
|
+
/** Thread root event id (64-hex). */
|
|
250
|
+
rootId: string;
|
|
251
|
+
/** Direct parent event id. Equal to `rootId` for a direct reply. */
|
|
252
|
+
parentId: string;
|
|
253
|
+
}
|
|
254
|
+
/** Exactly Buzz's `thread_tags` (builders.rs:173). */
|
|
255
|
+
export declare function threadTags(ref: ThreadRef): NostrTag[];
|
|
256
|
+
/**
|
|
257
|
+
* kind:9 stream message — mirrors `build_message` (builders.rs:219).
|
|
258
|
+
* Tag order: h, [thread tags], [a about], [p mentions], [broadcast], [imeta].
|
|
259
|
+
*
|
|
260
|
+
* **This is the shape verified byte-identical against Buzz's SDK** — see Peek's
|
|
261
|
+
* docs/buzz-compat/INTEROP_PROOF.md §4.
|
|
262
|
+
*
|
|
263
|
+
* **`about` is where the two copies had drifted.** Peek grew it so a pasted
|
|
264
|
+
* NIP-19 pointer could carry its address as an `a` tag and Ship could route the
|
|
265
|
+
* conversation to the issue directly; Ship's copy never received it and could
|
|
266
|
+
* not emit one at all. Peek's shape is what ships. Omitting `about` produces
|
|
267
|
+
* byte-identical output to Ship's old builder — pinned by
|
|
268
|
+
* `test/wire-vectors.test.ts`, because "the change is a no-op for Ship" is
|
|
269
|
+
* exactly the kind of claim that deserves a fixture rather than a sentence.
|
|
270
|
+
*/
|
|
271
|
+
export declare function buildMessage(pubkey: string, createdAtMs: number, args: {
|
|
272
|
+
channelUuid: string;
|
|
273
|
+
content: string;
|
|
274
|
+
threadRef?: ThreadRef;
|
|
275
|
+
/** Addressable objects this conversation concerns (for cross-app routing). */
|
|
276
|
+
about?: string[];
|
|
277
|
+
/** Mentioned pubkeys (64-hex). Deduplicated, lowercased, capped at 50. */
|
|
278
|
+
mentions?: string[];
|
|
279
|
+
broadcast?: boolean;
|
|
280
|
+
/** Raw `imeta` tag vectors for media attachments. */
|
|
281
|
+
mediaTags?: NostrTag[];
|
|
282
|
+
}): UnsignedEvent;
|
|
283
|
+
/** Assertion subtypes. Only `resolution` exists today (PEEK-128). */
|
|
284
|
+
export declare const ASSERTION_SUBTYPE: {
|
|
285
|
+
readonly RESOLUTION: "resolution";
|
|
286
|
+
};
|
|
287
|
+
/** What a resolution assertion says happened. */
|
|
288
|
+
export type ResolutionAction = 'resolved' | 'reopened';
|
|
289
|
+
/** Rationale cap — the same 64 KiB ceiling a message content carries. */
|
|
290
|
+
export declare const MAX_RATIONALE_BYTES: number;
|
|
291
|
+
/**
|
|
292
|
+
* kind:9101 resolution assertion — "this thread is resolved", said on the wire
|
|
293
|
+
* so any Estiva app reading the channel can see it (PEEK-128).
|
|
294
|
+
*
|
|
295
|
+
* **Append-only.** A reopen is another assertion, never a deletion of the
|
|
296
|
+
* resolve that preceded it; current state is folded from the ordered run, not
|
|
297
|
+
* read off a single canonical event. That is why this is a regular kind rather
|
|
298
|
+
* than a replaceable one — see `KIND.ASSERTION`.
|
|
299
|
+
*
|
|
300
|
+
* The event carries the claim and who made it. It does **not** carry authority:
|
|
301
|
+
* there is no "proposed" or "endorsed" mode here, because how much weight a
|
|
302
|
+
* given actor's assertion deserves is a policy question that belongs to the app
|
|
303
|
+
* reading it, not to the protocol. That is also why the *fold* of these
|
|
304
|
+
* assertions is not in this package — see the README on the line this package
|
|
305
|
+
* does not cross.
|
|
306
|
+
*
|
|
307
|
+
* Tag order is fixed so the event id is reproducible: h, e(target), t, action,
|
|
308
|
+
* [e(support)].
|
|
309
|
+
*/
|
|
310
|
+
export declare function buildResolution(pubkey: string, createdAtMs: number, args: {
|
|
311
|
+
channelUuid: string;
|
|
312
|
+
/** Event id of the message whose resolution state this asserts. */
|
|
313
|
+
targetEventId: string;
|
|
314
|
+
action: ResolutionAction;
|
|
315
|
+
/** Optional reply that carried the resolution, for readers that want it. */
|
|
316
|
+
supportingEventId?: string;
|
|
317
|
+
/** Optional free text. */
|
|
318
|
+
rationale?: string;
|
|
319
|
+
}): UnsignedEvent;
|
|
320
|
+
/** NIP-32 self-label: a namespace and a value, both indexable. */
|
|
321
|
+
export interface Label {
|
|
322
|
+
/** Namespace, e.g. "nfb.highlight". */
|
|
323
|
+
namespace: string;
|
|
324
|
+
/** Value within that namespace, e.g. "insight". */
|
|
325
|
+
value: string;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* kind:30840 File — NIP-FC.
|
|
329
|
+
*
|
|
330
|
+
* `componentDTags` are listed **in document order**; order lives on the File so
|
|
331
|
+
* reordering is a single edit in one place.
|
|
332
|
+
*/
|
|
333
|
+
export declare function buildFile(pubkey: string, createdAtMs: number, args: {
|
|
334
|
+
/** Stable File id. MUST NOT encode the relay or org — Files are portable. */
|
|
335
|
+
fileId: string;
|
|
336
|
+
title: string;
|
|
337
|
+
componentDTags: string[];
|
|
338
|
+
/** Channel scope. A File in a private channel inherits its access rules. */
|
|
339
|
+
channelUuid?: string;
|
|
340
|
+
/** File-level metadata; shape is the app's business. */
|
|
341
|
+
metadata?: Record<string, unknown>;
|
|
342
|
+
}): UnsignedEvent;
|
|
343
|
+
/**
|
|
344
|
+
* kind:30841 Component — NIP-FC.
|
|
345
|
+
*
|
|
346
|
+
* `type` must be namespaced `<namespace>/<name>`. The protocol defines the
|
|
347
|
+
* container; the payload shape belongs to the type.
|
|
348
|
+
*/
|
|
349
|
+
export declare function buildComponent(pubkey: string, createdAtMs: number, args: {
|
|
350
|
+
componentId: string;
|
|
351
|
+
/** Parent File's `d` tag. */
|
|
352
|
+
fileId: string;
|
|
353
|
+
/** Namespaced, e.g. `nfb/todo`. */
|
|
354
|
+
type: string;
|
|
355
|
+
payload: Record<string, unknown>;
|
|
356
|
+
channelUuid?: string;
|
|
357
|
+
labels?: Label[];
|
|
358
|
+
}): UnsignedEvent;
|
|
359
|
+
/**
|
|
360
|
+
* kind:9802 highlight — NIP-84.
|
|
361
|
+
*
|
|
362
|
+
* `.content` is the excerpt itself. `e`/`a` tags point at a source event, `r` at
|
|
363
|
+
* a URL; `p` attributes the original author.
|
|
364
|
+
*
|
|
365
|
+
* Optional NIP-32 `L`/`l` self-labels categorise the highlight. NIP-32 §52
|
|
366
|
+
* allows those tags on non-1985 events for exactly this ("self-reporting"), so
|
|
367
|
+
* a highlight can say *what kind* of highlight it is without a bespoke kind.
|
|
368
|
+
*/
|
|
369
|
+
export declare function buildHighlight(pubkey: string, createdAtMs: number, args: {
|
|
370
|
+
/** The excerpt. */
|
|
371
|
+
content: string;
|
|
372
|
+
/** Channel to post into (Buzz scopes by `h`). */
|
|
373
|
+
channelUuid?: string;
|
|
374
|
+
/** Source event being highlighted. */
|
|
375
|
+
sourceEventId?: string;
|
|
376
|
+
/** Source URL, when the highlight came from outside Nostr. */
|
|
377
|
+
sourceUrl?: string;
|
|
378
|
+
/** Original author(s) of the highlighted material. */
|
|
379
|
+
attribution?: string[];
|
|
380
|
+
labels?: Label[];
|
|
381
|
+
}): UnsignedEvent;
|
|
382
|
+
/**
|
|
383
|
+
* The relay's clock tolerance for a NIP-42 AUTH event, in seconds.
|
|
384
|
+
*
|
|
385
|
+
* `TIMESTAMP_TOLERANCE_SECS` in `crates/buzz-auth/src/nip42.rs` — the same ±60s
|
|
386
|
+
* NIP-98 uses, checked against the *relay's* clock. It is the one failure here
|
|
387
|
+
* a correct client can still hit: a browser whose clock is more than a minute
|
|
388
|
+
* out signs a perfectly valid event that is refused every time, and the socket
|
|
389
|
+
* is left open and permanently unauthenticated rather than closed.
|
|
390
|
+
*/
|
|
391
|
+
export declare const RELAY_AUTH_TOLERANCE_SECS = 60;
|
|
392
|
+
/**
|
|
393
|
+
* The relay URL a NIP-42 `relay` tag must carry, as Buzz computes it.
|
|
394
|
+
*
|
|
395
|
+
* `nip42_expected_relay_url` (`buzz-relay/src/api/bridge.rs:225`) is literally
|
|
396
|
+
* `format!("{scheme}://{}", tenant.host())` — scheme from the deployment, host
|
|
397
|
+
* from **the tenant the connection arrived on**, never the deployment-wide
|
|
398
|
+
* `config.relay_url`. Buzz has a test asserting exactly that
|
|
399
|
+
* (`nip42_expected_relay_url_uses_tenant_host_not_config_host`), because it
|
|
400
|
+
* regressed once.
|
|
401
|
+
*
|
|
402
|
+
* So: an origin, with no path and no trailing slash, derived from the URL we
|
|
403
|
+
* actually connected to. `normalize_relay_url` on the relay side would forgive
|
|
404
|
+
* a trailing slash, but it would not forgive a path or a different host.
|
|
405
|
+
*/
|
|
406
|
+
export declare function relayAuthUrl(connectUrl: string): string;
|
|
407
|
+
/**
|
|
408
|
+
* An unsigned kind:22242 answering a relay's AUTH challenge.
|
|
409
|
+
*
|
|
410
|
+
* Built here rather than at a call site because the tag layout is part of the
|
|
411
|
+
* event id preimage, and a second copy would be a silent divergence the relay
|
|
412
|
+
* notices and we do not.
|
|
413
|
+
*
|
|
414
|
+
* **Tag order is not load-bearing for this one kind**, unusually for this file.
|
|
415
|
+
* Buzz looks both tags up by name — `tags.find(TagKind::Challenge)` and
|
|
416
|
+
* `tags.find(TagKind::Relay)` in `verify_nip42_event` — and the two reference
|
|
417
|
+
* clients disagree anyway: rust-nostr's `EventBuilder::auth` emits challenge
|
|
418
|
+
* first, nostr-tools' `makeAuthEvent` emits relay first. NIP-42's own example
|
|
419
|
+
* uses relay-then-challenge, which is what this follows. Nothing downstream
|
|
420
|
+
* compares this event's id to anything, because nothing ever stores it.
|
|
421
|
+
*/
|
|
422
|
+
export declare function buildUnsignedRelayAuthEvent(args: {
|
|
423
|
+
/** Left empty for `/sign`, which overwrites it with the token's subject. */
|
|
424
|
+
pubkey: string;
|
|
425
|
+
/** The URL the socket connected to. Reduced to an origin — see above. */
|
|
426
|
+
relayUrl: string;
|
|
427
|
+
/** The challenge exactly as the relay sent it. Compared byte for byte. */
|
|
428
|
+
challenge: string;
|
|
429
|
+
/** Override for tests; defaults to now. `Date.now` is in `lib.es2022`. */
|
|
430
|
+
nowMs?: number;
|
|
431
|
+
}): UnsignedEvent;
|
|
432
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
|