@forgesworn/fold-kit 0.0.0-seed.0 → 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/README.md +154 -4
- package/dist/access.d.ts +34 -0
- package/dist/access.js +102 -0
- package/dist/channel.d.ts +27 -0
- package/dist/channel.js +38 -0
- package/dist/credential.d.ts +58 -0
- package/dist/credential.js +145 -0
- package/dist/display-name.d.ts +38 -0
- package/dist/display-name.js +78 -0
- package/dist/epoch.d.ts +387 -0
- package/dist/epoch.js +665 -0
- package/dist/hex.d.ts +28 -0
- package/dist/hex.js +32 -0
- package/dist/identity.d.ts +49 -0
- package/dist/identity.js +17 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +17 -0
- package/dist/invitation.d.ts +217 -0
- package/dist/invitation.js +484 -0
- package/dist/kinds.d.ts +64 -0
- package/dist/kinds.js +64 -0
- package/dist/lane.d.ts +41 -0
- package/dist/lane.js +63 -0
- package/dist/link.d.ts +52 -0
- package/dist/link.js +107 -0
- package/dist/network-hints.d.ts +16 -0
- package/dist/network-hints.js +65 -0
- package/dist/persistent-invitation.d.ts +35 -0
- package/dist/persistent-invitation.js +118 -0
- package/dist/room.d.ts +53 -0
- package/dist/room.js +153 -0
- package/dist/transport.d.ts +24 -0
- package/dist/transport.js +1 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.js +1 -0
- package/dist/verify.d.ts +26 -0
- package/dist/verify.js +46 -0
- package/llms.txt +100 -0
- package/package.json +76 -6
- package/vectors/circle-vectors.json +2173 -0
- package/vectors/kithmoot-vectors.json +1550 -0
package/dist/hex.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Case-insensitive equality for hex identifiers.
|
|
3
|
+
*
|
|
4
|
+
* Nostr pubkeys, room ids, and every other hex field this protocol compares
|
|
5
|
+
* are canonically produced in lower case, but nothing on the wire enforces
|
|
6
|
+
* that: an allow-list entry typed or pasted by a person, in particular, may
|
|
7
|
+
* carry upper-case hex naming exactly the same key. Every place this
|
|
8
|
+
* protocol decides whether two hex identifiers name the same thing must go
|
|
9
|
+
* through this function rather than `===`/`!==`, so a case difference is
|
|
10
|
+
* never mistaken for a different identity - see `vectors/README.md`.
|
|
11
|
+
*/
|
|
12
|
+
export function hexEquals(a, b) {
|
|
13
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Canonicalise a hex identifier to lower case.
|
|
17
|
+
*
|
|
18
|
+
* `hexEquals` makes an equality check safe regardless of case, but nothing
|
|
19
|
+
* protects a *lexicographic* comparison the same way: `Peer`'s glare
|
|
20
|
+
* tiebreak and `resolveSingularRoles`' device tiebreak both order hex
|
|
21
|
+
* strings with `<`, and two implementations that disagree on which of two
|
|
22
|
+
* differently-cased spellings of the same identifier sorts first can reach
|
|
23
|
+
* opposite answers from identical input - the exact deadlock perfect
|
|
24
|
+
* negotiation exists to prevent. Call this once, at the point a hex
|
|
25
|
+
* identifier enters the system - a decoded event, a parsed credential or
|
|
26
|
+
* proof, a pubkey read from storage or a URL - rather than at each
|
|
27
|
+
* comparison site, so every later equality or ordering check on it is
|
|
28
|
+
* correct by construction. See `vectors/README.md`.
|
|
29
|
+
*/
|
|
30
|
+
export function normaliseHex(hex) {
|
|
31
|
+
return hex.toLowerCase();
|
|
32
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type Event } from 'nostr-tools/pure';
|
|
2
|
+
/**
|
|
3
|
+
* An event with everything filled in except the three fields a signature
|
|
4
|
+
* produces: `pubkey`, `id`, `sig`.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately the same shape an external signer expects - NIP-07's
|
|
7
|
+
* `window.nostr.signEvent`, NIP-46's `sign_event`, and signet-login's
|
|
8
|
+
* `SignetSigner.signEvent` all take exactly this - so a signer built for
|
|
9
|
+
* any of those satisfies the interface below without an adapter.
|
|
10
|
+
*/
|
|
11
|
+
export interface UnsignedEvent {
|
|
12
|
+
kind: number;
|
|
13
|
+
created_at: number;
|
|
14
|
+
tags: string[][];
|
|
15
|
+
content: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The person, as far as this protocol is concerned.
|
|
19
|
+
*
|
|
20
|
+
* The participant key is used for exactly one thing: signing a device
|
|
21
|
+
* credential, one small event per room. Everything else in a room already
|
|
22
|
+
* runs on other keys - the device key signs the roster and the gift-wrapped
|
|
23
|
+
* signalling, the room key encrypts the roster and the chat. So this is the
|
|
24
|
+
* whole surface an identity has to cover.
|
|
25
|
+
*
|
|
26
|
+
* That narrowness is what makes an external signer practical, and the
|
|
27
|
+
* reason to prefer one: a participant who signs in with a browser extension
|
|
28
|
+
* or a remote bunker never has a participant secret in this app's storage
|
|
29
|
+
* at all. `localIdentity` is the other implementation - a key generated and
|
|
30
|
+
* held here, which is the zero-friction default and is exactly as valid.
|
|
31
|
+
*
|
|
32
|
+
* `signEvent` is async because a remote signer is a round trip: an
|
|
33
|
+
* extension prompt, a relay hop to a bunker, a tap on a phone. Every caller
|
|
34
|
+
* in this library awaits it rather than assuming an answer is available in
|
|
35
|
+
* the same tick.
|
|
36
|
+
*/
|
|
37
|
+
export interface ParticipantIdentity {
|
|
38
|
+
/** Lower-case hex. See `normaliseHex`. */
|
|
39
|
+
readonly pubkey: string;
|
|
40
|
+
signEvent(unsigned: UnsignedEvent): Promise<Event>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* An identity backed by a secret key held in this process.
|
|
44
|
+
*
|
|
45
|
+
* The key never leaves the closure, which is the same guarantee the app
|
|
46
|
+
* gave before this seam existed - it is simply now one implementation of an
|
|
47
|
+
* interface rather than the only way to be a participant.
|
|
48
|
+
*/
|
|
49
|
+
export declare function localIdentity(participantSk: Uint8Array): ParticipantIdentity;
|
package/dist/identity.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { finalizeEvent, getPublicKey } from 'nostr-tools/pure';
|
|
2
|
+
import { normaliseHex } from './hex.js';
|
|
3
|
+
/**
|
|
4
|
+
* An identity backed by a secret key held in this process.
|
|
5
|
+
*
|
|
6
|
+
* The key never leaves the closure, which is the same guarantee the app
|
|
7
|
+
* gave before this seam existed - it is simply now one implementation of an
|
|
8
|
+
* interface rather than the only way to be a participant.
|
|
9
|
+
*/
|
|
10
|
+
export function localIdentity(participantSk) {
|
|
11
|
+
return {
|
|
12
|
+
pubkey: normaliseHex(getPublicKey(participantSk)),
|
|
13
|
+
async signEvent(unsigned) {
|
|
14
|
+
return finalizeEvent(unsigned, participantSk);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export { hexEquals, normaliseHex } from './hex.js';
|
|
2
|
+
export { verifyEventUncached, boundedEventVerifier } from './verify.js';
|
|
3
|
+
export type { UnsignedEvent, ParticipantIdentity } from './identity.js';
|
|
4
|
+
export { localIdentity } from './identity.js';
|
|
5
|
+
export { KINDS } from './kinds.js';
|
|
6
|
+
export type { DeviceCredential, AccessTier, AgentRule, RoomPolicy, KindredProof } from './types.js';
|
|
7
|
+
export type { RelayTransport } from './transport.js';
|
|
8
|
+
export { createDeviceCredential, verifyDeviceCredential, PERSON_CREDENTIAL_MAX_SECONDS, } from './credential.js';
|
|
9
|
+
export type { CreateCredentialOptions, VerifyResult } from './credential.js';
|
|
10
|
+
export { generateRoomSecret, deriveRoom, encodeJoinUrl, decodeJoinUrl, parseRoomPolicy, ROOM_LABELS, } from './room.js';
|
|
11
|
+
export { MAX_RELAY_HINTS, MAX_ICE_HINTS, MAX_NETWORK_HINT_LENGTH, isSafeRelayUrl, safeRelayUrls, isSafeIceUrl, safeIceUrls, assertNetworkHintBounds, } from './network-hints.js';
|
|
12
|
+
export { sanitiseDisplayName, MAX_DISPLAY_NAME_LENGTH } from './display-name.js';
|
|
13
|
+
export { issueKindredProof, evaluateAccess, ACCESS_LABELS, } from './access.js';
|
|
14
|
+
export type { IssueKindredProofOptions } from './access.js';
|
|
15
|
+
export { INVITATION_DELEGATION_TTL_SECONDS, MAX_INVITATION_DELEGATION_DEPTH, createRoomInvitation, roomInvitation, deriveInvitationId, encodeInvitationRequest, decodeInvitationRequest, verifyInvitationDelegation, encodeInvitationGrant, decodeRoomAdmissionGrant, decodeInvitationGrant, ROOM_ENDED_MESSAGE, encodeInvitationRetirement, decodeInvitationRetirement, decodeInvitationRetirementNotice, retirementError, hostRoomInvitation, requestRoomAdmissionCapability, requestRoomAdmission, INVITATION_LABELS, } from './invitation.js';
|
|
16
|
+
export type { RoomInvitation, RoomInvitationHost, InvitationDelegation, RoomInvitationDelegate, RoomAdmission, EncodeInvitationRequestOptions, InvitationRequest, DecodeInvitationRequestOptions, EncodeInvitationGrantOptions, DecodeInvitationGrantOptions, HostRoomInvitationOptions, EncodeInvitationRetirementOptions, RequestRoomAdmissionOptions, } from './invitation.js';
|
|
17
|
+
export { encodePersistentInvitation, decodePersistentInvitation, requestPersistentRoomAdmission, PERSISTENT_INVITATION_LABELS, } from './persistent-invitation.js';
|
|
18
|
+
export type { PersistentRoomAdmission } from './persistent-invitation.js';
|
|
19
|
+
export { MAX_ROOM_LINK_FRAGMENT_LENGTH, parseRoomLink, encodeRoomLink, } from './link.js';
|
|
20
|
+
export type { RoomLink } from './link.js';
|
|
21
|
+
export { EPOCH_ID_INFO, EPOCH_KEY_INFO, MAX_EPOCH, EPOCH_REQUEST_KEY_INFO, generateEpochSecret, deriveEpoch, encodeRekeyEvent, peekRekeyEvent, decodeRekeyEvent, deriveEpochRequestKey, epochRequestAdmission, encodeEpochRequest, decodeEpochRequest, encodeEpochGrant, decodeEpochGrant, hostRoomEpoch, EpochRefusedError, requestRoomEpoch, canonicalAdmins, CHANNEL_NAME, RESERVED_CHANNELS, canonicalChannels, signChannels, verifyChannels, signAdmins, verifyAdmins, EPOCH_LABELS, } from './epoch.js';
|
|
22
|
+
export type { RoomEpoch, EpochKeys, EncodeRekeyOptions, RekeyNotice, PeekRekeyOptions, DecodeRekeyOptions, EpochRequestAdmissionOptions, EncodeEpochRequestOptions, DecodeEpochRequestOptions, EpochRequest, EpochRefusal, EncodeEpochGrantOptions, DecodeEpochGrantOptions, EpochGrant, HostRoomEpochOptions, RequestRoomEpochOptions, SignAdminsOptions, SignChannelsOptions, VerifyChannelsOptions, VerifyAdminsOptions, } from './epoch.js';
|
|
23
|
+
export { deriveChannel, CHANNEL_ID_INFO, CHANNEL_KEY_INFO, MAX_CHANNEL_NAME_LENGTH, CHANNEL_LABELS } from './channel.js';
|
|
24
|
+
export { LANES, LANE_MEANING, LANE_LABEL, LANE_GLYPH, isLane, laneOfRelayUrl, laneOfRelays, weakestLane, isDowngrade, } from './lane.js';
|
|
25
|
+
export type { Lane } from './lane.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Public entry point. See README.md for an API overview and EXTRACTION.md
|
|
2
|
+
// for where each export came from.
|
|
3
|
+
export { hexEquals, normaliseHex } from './hex.js';
|
|
4
|
+
export { verifyEventUncached, boundedEventVerifier } from './verify.js';
|
|
5
|
+
export { localIdentity } from './identity.js';
|
|
6
|
+
export { KINDS } from './kinds.js';
|
|
7
|
+
export { createDeviceCredential, verifyDeviceCredential, PERSON_CREDENTIAL_MAX_SECONDS, } from './credential.js';
|
|
8
|
+
export { generateRoomSecret, deriveRoom, encodeJoinUrl, decodeJoinUrl, parseRoomPolicy, ROOM_LABELS, } from './room.js';
|
|
9
|
+
export { MAX_RELAY_HINTS, MAX_ICE_HINTS, MAX_NETWORK_HINT_LENGTH, isSafeRelayUrl, safeRelayUrls, isSafeIceUrl, safeIceUrls, assertNetworkHintBounds, } from './network-hints.js';
|
|
10
|
+
export { sanitiseDisplayName, MAX_DISPLAY_NAME_LENGTH } from './display-name.js';
|
|
11
|
+
export { issueKindredProof, evaluateAccess, ACCESS_LABELS, } from './access.js';
|
|
12
|
+
export { INVITATION_DELEGATION_TTL_SECONDS, MAX_INVITATION_DELEGATION_DEPTH, createRoomInvitation, roomInvitation, deriveInvitationId, encodeInvitationRequest, decodeInvitationRequest, verifyInvitationDelegation, encodeInvitationGrant, decodeRoomAdmissionGrant, decodeInvitationGrant, ROOM_ENDED_MESSAGE, encodeInvitationRetirement, decodeInvitationRetirement, decodeInvitationRetirementNotice, retirementError, hostRoomInvitation, requestRoomAdmissionCapability, requestRoomAdmission, INVITATION_LABELS, } from './invitation.js';
|
|
13
|
+
export { encodePersistentInvitation, decodePersistentInvitation, requestPersistentRoomAdmission, PERSISTENT_INVITATION_LABELS, } from './persistent-invitation.js';
|
|
14
|
+
export { MAX_ROOM_LINK_FRAGMENT_LENGTH, parseRoomLink, encodeRoomLink, } from './link.js';
|
|
15
|
+
export { EPOCH_ID_INFO, EPOCH_KEY_INFO, MAX_EPOCH, EPOCH_REQUEST_KEY_INFO, generateEpochSecret, deriveEpoch, encodeRekeyEvent, peekRekeyEvent, decodeRekeyEvent, deriveEpochRequestKey, epochRequestAdmission, encodeEpochRequest, decodeEpochRequest, encodeEpochGrant, decodeEpochGrant, hostRoomEpoch, EpochRefusedError, requestRoomEpoch, canonicalAdmins, CHANNEL_NAME, RESERVED_CHANNELS, canonicalChannels, signChannels, verifyChannels, signAdmins, verifyAdmins, EPOCH_LABELS, } from './epoch.js';
|
|
16
|
+
export { deriveChannel, CHANNEL_ID_INFO, CHANNEL_KEY_INFO, MAX_CHANNEL_NAME_LENGTH, CHANNEL_LABELS } from './channel.js';
|
|
17
|
+
export { LANES, LANE_MEANING, LANE_LABEL, LANE_GLYPH, isLane, laneOfRelayUrl, laneOfRelays, weakestLane, isDowngrade, } from './lane.js';
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { type Event } from 'nostr-tools/pure';
|
|
2
|
+
import type { RelayTransport } from './transport.js';
|
|
3
|
+
/** A delegated responder cannot silently make its authority permanent. */
|
|
4
|
+
export declare const INVITATION_DELEGATION_TTL_SECONDS: number;
|
|
5
|
+
/** Bounds both verification work and the size of a grant from a hostile peer. */
|
|
6
|
+
export declare const MAX_INVITATION_DELEGATION_DEPTH = 16;
|
|
7
|
+
/**
|
|
8
|
+
* What a share link grants.
|
|
9
|
+
*
|
|
10
|
+
* `bearer` proves that somebody received the link. It is not a traffic key.
|
|
11
|
+
* `inviter` pins the root pubkey allowed to establish a room-bound responder
|
|
12
|
+
* chain, so another bearer can ask to enter but cannot nominate an authority
|
|
13
|
+
* or substitute a room of their own.
|
|
14
|
+
*/
|
|
15
|
+
export interface RoomInvitation {
|
|
16
|
+
bearer: Uint8Array;
|
|
17
|
+
inviter: string;
|
|
18
|
+
/** Version 3 group link: admission is stored encrypted on the relays. */
|
|
19
|
+
persistent?: true;
|
|
20
|
+
}
|
|
21
|
+
/** The private half retained by the browser that created an invitation. */
|
|
22
|
+
export interface RoomInvitationHost {
|
|
23
|
+
invitation: RoomInvitation;
|
|
24
|
+
inviterSk: Uint8Array;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* One hop in the authority chain rooted at the inviter pubkey in the link.
|
|
28
|
+
*
|
|
29
|
+
* Every field that gives the certificate meaning is signed. In particular,
|
|
30
|
+
* `invitation` binds it to one bearer-derived rendezvous and `room` to one
|
|
31
|
+
* traffic-secret derivation, so a member delegated for an old link cannot
|
|
32
|
+
* answer a replacement or substitute another room.
|
|
33
|
+
*/
|
|
34
|
+
export interface InvitationDelegation {
|
|
35
|
+
invitation: string;
|
|
36
|
+
room: string;
|
|
37
|
+
issuer: string;
|
|
38
|
+
delegate: string;
|
|
39
|
+
expiresAt: number;
|
|
40
|
+
sig: string;
|
|
41
|
+
}
|
|
42
|
+
/** The capability an admitted browser retains so it can answer the next
|
|
43
|
+
* joiner even after the creator has left. */
|
|
44
|
+
export interface RoomInvitationDelegate {
|
|
45
|
+
delegateSk: Uint8Array;
|
|
46
|
+
chain: InvitationDelegation[];
|
|
47
|
+
}
|
|
48
|
+
/** The result of admission: room traffic capability plus bounded authority
|
|
49
|
+
* to keep this particular invitation available. */
|
|
50
|
+
export interface RoomAdmission {
|
|
51
|
+
secret: Uint8Array;
|
|
52
|
+
delegate: RoomInvitationDelegate;
|
|
53
|
+
/**
|
|
54
|
+
* The epoch the responder says the room is at. A hint, not a key: the
|
|
55
|
+
* secret above opens epoch 0, and a room that has been rekeyed is read
|
|
56
|
+
* only with the current epoch's secret, which the room's authority hands
|
|
57
|
+
* to a member on proof of who it is (see `epoch.ts`). A joiner told the
|
|
58
|
+
* room is ahead asks before it announces; one told nothing waits a
|
|
59
|
+
* moment for the rekey events themselves. Absent from a responder that
|
|
60
|
+
* predates epochs.
|
|
61
|
+
*/
|
|
62
|
+
epoch?: number;
|
|
63
|
+
}
|
|
64
|
+
/** Create a bearer plus a fresh, unlinkable inviter key for one share URL. */
|
|
65
|
+
export declare function createRoomInvitation(persistent?: boolean): RoomInvitationHost;
|
|
66
|
+
/** Validate and canonicalise an invitation crossing a URL/storage boundary. */
|
|
67
|
+
export declare function roomInvitation(bearer: Uint8Array, inviter: string, persistent?: boolean): RoomInvitation;
|
|
68
|
+
/** Public rendezvous id. Relays see this and timing, but cannot derive the bearer. */
|
|
69
|
+
export declare function deriveInvitationId(invitation: RoomInvitation): string;
|
|
70
|
+
export interface EncodeInvitationRequestOptions {
|
|
71
|
+
invitation: RoomInvitation;
|
|
72
|
+
requesterSk: Uint8Array;
|
|
73
|
+
now: number;
|
|
74
|
+
name?: string;
|
|
75
|
+
participant?: string;
|
|
76
|
+
}
|
|
77
|
+
/** Who is asking, as decoded from a request. */
|
|
78
|
+
export interface InvitationRequest {
|
|
79
|
+
device: string;
|
|
80
|
+
request: string;
|
|
81
|
+
name?: string;
|
|
82
|
+
participant?: string;
|
|
83
|
+
}
|
|
84
|
+
/** Prove possession of the bearer without putting it, or a traffic key, on a relay. */
|
|
85
|
+
export declare function encodeInvitationRequest(opts: EncodeInvitationRequestOptions): Event;
|
|
86
|
+
export interface DecodeInvitationRequestOptions {
|
|
87
|
+
invitation: RoomInvitation;
|
|
88
|
+
now: number;
|
|
89
|
+
maxAgeSeconds?: number;
|
|
90
|
+
}
|
|
91
|
+
/** Returns null for all malformed, stale, wrongly addressed, or unauthorised asks. */
|
|
92
|
+
export declare function decodeInvitationRequest(event: Event, opts: DecodeInvitationRequestOptions): InvitationRequest | null;
|
|
93
|
+
/**
|
|
94
|
+
* Verify a whole chain and return the pubkey authorised by its final hop.
|
|
95
|
+
* An empty chain names the root inviter itself.
|
|
96
|
+
*/
|
|
97
|
+
export declare function verifyInvitationDelegation(invitation: RoomInvitation, chain: InvitationDelegation[], now: number): string | null;
|
|
98
|
+
export interface EncodeInvitationGrantOptions {
|
|
99
|
+
invitation: RoomInvitation;
|
|
100
|
+
inviterSk: Uint8Array;
|
|
101
|
+
requester: string;
|
|
102
|
+
request: string;
|
|
103
|
+
roomSecret: Uint8Array;
|
|
104
|
+
now: number;
|
|
105
|
+
/** Empty for the creator; otherwise the root-to-signer chain received when
|
|
106
|
+
* this responder joined. */
|
|
107
|
+
delegation?: InvitationDelegation[];
|
|
108
|
+
delegationTtlSeconds?: number;
|
|
109
|
+
/** The epoch this responder is at, so the requester knows whether the
|
|
110
|
+
* secret it is being handed opens the live room or only its history. */
|
|
111
|
+
epoch?: number;
|
|
112
|
+
}
|
|
113
|
+
/** Encrypt a room secret only to the requester and authenticate its signer
|
|
114
|
+
* through the room-bound chain rooted at the inviter pinned in the link. */
|
|
115
|
+
export declare function encodeInvitationGrant(opts: EncodeInvitationGrantOptions): Event;
|
|
116
|
+
export interface DecodeInvitationGrantOptions {
|
|
117
|
+
invitation: RoomInvitation;
|
|
118
|
+
requesterSk: Uint8Array;
|
|
119
|
+
request: string;
|
|
120
|
+
now: number;
|
|
121
|
+
maxAgeSeconds?: number;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Accept a fresh response whose delegation chain terminates at its event
|
|
125
|
+
* signer, then retain the final requester hop so this member can become a
|
|
126
|
+
* responder in turn. The chain always roots at the pubkey pinned in the
|
|
127
|
+
* link; a bearer holder cannot nominate an authority of their own.
|
|
128
|
+
*/
|
|
129
|
+
export declare function decodeRoomAdmissionGrant(event: Event, opts: DecodeInvitationGrantOptions): RoomAdmission | null;
|
|
130
|
+
/** Backward-compatible convenience for callers that only need the traffic
|
|
131
|
+
* secret. New interactive clients should retain `decodeRoomAdmissionGrant`'s
|
|
132
|
+
* delegate capability so the room does not depend on its creator staying. */
|
|
133
|
+
export declare function decodeInvitationGrant(event: Event, opts: DecodeInvitationGrantOptions): Uint8Array | null;
|
|
134
|
+
export interface HostRoomInvitationOptions {
|
|
135
|
+
transport: RelayTransport;
|
|
136
|
+
invitation: RoomInvitation;
|
|
137
|
+
inviterSk: Uint8Array;
|
|
138
|
+
roomSecret: Uint8Array;
|
|
139
|
+
/** Root-to-this-responder chain. Empty/absent only on the creator. */
|
|
140
|
+
delegation?: InvitationDelegation[];
|
|
141
|
+
now?: () => number;
|
|
142
|
+
onAdmitted?: (device: string) => void;
|
|
143
|
+
/** Called when the creator's durable retirement tombstone is heard. */
|
|
144
|
+
onRetired?: () => void;
|
|
145
|
+
/** The epoch this responder is at, asked on every grant because it
|
|
146
|
+
* moves. Omit to say nothing, which a joiner treats as unknown. */
|
|
147
|
+
epoch?: () => number;
|
|
148
|
+
/**
|
|
149
|
+
* Asked before every grant, when present. Return true to let the
|
|
150
|
+
* request in, false to leave it unanswered: there is no refusal on the
|
|
151
|
+
* wire, so a declined person sees the room not answer, which is the same
|
|
152
|
+
* as nobody being home. A request is asked about once, whatever its
|
|
153
|
+
* retries; a person who tries again with a fresh request is asked about
|
|
154
|
+
* again. Absent, every request is granted, which is what a temporary
|
|
155
|
+
* room's link has always meant.
|
|
156
|
+
*/
|
|
157
|
+
admit?: (request: InvitationRequest) => boolean | Promise<boolean>;
|
|
158
|
+
}
|
|
159
|
+
export interface EncodeInvitationRetirementOptions {
|
|
160
|
+
invitation: RoomInvitation;
|
|
161
|
+
/** Only the root inviter may retire a link. Delegates never receive this
|
|
162
|
+
* key, which stops one room member disabling admission for everybody. */
|
|
163
|
+
inviterSk: Uint8Array;
|
|
164
|
+
now: number;
|
|
165
|
+
/** The room itself was ended, not just this link replaced. Additive: a
|
|
166
|
+
* reader that predates it still sees an ordinary retirement. */
|
|
167
|
+
ended?: boolean;
|
|
168
|
+
}
|
|
169
|
+
/** What a joiner is told when a link was retired because its room ended. */
|
|
170
|
+
export declare const ROOM_ENDED_MESSAGE = "this room was ended by the person who started it";
|
|
171
|
+
/** Make a permanent tombstone for one invitation rendezvous. */
|
|
172
|
+
export declare function encodeInvitationRetirement(opts: EncodeInvitationRetirementOptions): Event;
|
|
173
|
+
/** A valid tombstone never expires: invitation ids are random and unique,
|
|
174
|
+
* and a retired bearer must not become usable again after a timeout. */
|
|
175
|
+
export declare function decodeInvitationRetirement(event: Event, invitation: RoomInvitation): boolean;
|
|
176
|
+
/** A valid retirement, and whether it says the room was ended. Undefined
|
|
177
|
+
* for anything that is not a valid retirement of this invitation. */
|
|
178
|
+
export declare function decodeInvitationRetirementNotice(event: Event, invitation: RoomInvitation): {
|
|
179
|
+
ended: boolean;
|
|
180
|
+
} | undefined;
|
|
181
|
+
/** The error a joiner rejects with on a retirement. */
|
|
182
|
+
export declare function retirementError(notice: {
|
|
183
|
+
ended: boolean;
|
|
184
|
+
}): Error;
|
|
185
|
+
/**
|
|
186
|
+
* Auto-admit anybody holding this link while this admitted member is online.
|
|
187
|
+
*
|
|
188
|
+
* Closing the returned handle stops this responder. People
|
|
189
|
+
* already admitted necessarily retain the room secret they were given; link
|
|
190
|
+
* rotation is not member revocation and the UI must never claim that it is.
|
|
191
|
+
*/
|
|
192
|
+
export declare function hostRoomInvitation(opts: HostRoomInvitationOptions): {
|
|
193
|
+
close(): void;
|
|
194
|
+
};
|
|
195
|
+
export interface RequestRoomAdmissionOptions {
|
|
196
|
+
transport: RelayTransport;
|
|
197
|
+
invitation: RoomInvitation;
|
|
198
|
+
requesterSk?: Uint8Array;
|
|
199
|
+
now?: () => number;
|
|
200
|
+
timeoutMs?: number;
|
|
201
|
+
retryMs?: number;
|
|
202
|
+
/** Carried in the request for a host who asks before letting people in. */
|
|
203
|
+
name?: string;
|
|
204
|
+
participant?: string;
|
|
205
|
+
}
|
|
206
|
+
/** Resolve the room and a bounded responder delegation, with no account or
|
|
207
|
+
* prompt. Retaining the delegation is what removes the creator as an
|
|
208
|
+
* availability dependency for the next arrival. */
|
|
209
|
+
export declare function requestRoomAdmissionCapability(opts: RequestRoomAdmissionOptions): Promise<RoomAdmission>;
|
|
210
|
+
/** Compatibility wrapper for non-interactive consumers. It joins correctly,
|
|
211
|
+
* but discards the ability to keep the link available for somebody else. */
|
|
212
|
+
export declare function requestRoomAdmission(opts: RequestRoomAdmissionOptions): Promise<Uint8Array>;
|
|
213
|
+
/** Every wire-format literal this module owns (each one a kithmoot protocol string), frozen for
|
|
214
|
+
* `src/labels.test.ts`, which checks each module against its own exported
|
|
215
|
+
* list rather than scanning file text for matching comments. Pure data -
|
|
216
|
+
* adding this export changes no runtime behaviour. */
|
|
217
|
+
export declare const INVITATION_LABELS: readonly ["kithmoot/v2/invitation-delegation:", "kithmoot/v2/invitation-id", "kithmoot/v2/invitation-request-key"];
|