canary-kit 0.9.0 → 0.11.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/CANARY.md +38 -0
- package/NIP-CANARY.md +26 -0
- package/README.md +1 -1
- package/dist/counter.d.ts +1 -36
- package/dist/counter.d.ts.map +1 -1
- package/dist/counter.js +1 -61
- package/dist/counter.js.map +1 -1
- package/dist/crypto.d.ts +1 -110
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +2 -308
- package/dist/crypto.js.map +1 -1
- package/dist/encoding.d.ts +1 -55
- package/dist/encoding.d.ts.map +1 -1
- package/dist/encoding.js +1 -97
- package/dist/encoding.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/nostr.d.ts +104 -88
- package/dist/nostr.d.ts.map +1 -1
- package/dist/nostr.js +123 -111
- package/dist/nostr.js.map +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +13 -2
- package/dist/sync.js.map +1 -1
- package/dist/token.d.ts +4 -71
- package/dist/token.d.ts.map +1 -1
- package/dist/token.js +17 -111
- package/dist/token.js.map +1 -1
- package/dist/wordlist.d.ts +1 -27
- package/dist/wordlist.d.ts.map +1 -1
- package/dist/wordlist.js +1 -296
- package/dist/wordlist.js.map +1 -1
- package/llms-full.txt +1 -1
- package/llms.txt +1 -1
- package/package.json +3 -2
package/dist/crypto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,OAAO,EACL,MAAM,EAAE,UAAU,EAAE,UAAU,EAC9B,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EACjD,aAAa,EAAE,aAAa,EAC5B,eAAe,EAAE,qBAAqB,GACvC,MAAM,cAAc,CAAA"}
|
package/dist/encoding.d.ts
CHANGED
|
@@ -1,56 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export type TokenEncoding = {
|
|
3
|
-
format: 'words';
|
|
4
|
-
count?: number;
|
|
5
|
-
wordlist?: readonly string[];
|
|
6
|
-
} | {
|
|
7
|
-
format: 'pin';
|
|
8
|
-
digits?: number;
|
|
9
|
-
} | {
|
|
10
|
-
format: 'hex';
|
|
11
|
-
length?: number;
|
|
12
|
-
};
|
|
13
|
-
/** Default encoding: single word from en-v1 wordlist. */
|
|
14
|
-
export declare const DEFAULT_ENCODING: TokenEncoding;
|
|
15
|
-
/**
|
|
16
|
-
* Encode raw bytes as words using 11-bit indices into a wordlist.
|
|
17
|
-
* Each word uses a consecutive 2-byte slice: readUint16BE(bytes, i*2) % wordlistSize.
|
|
18
|
-
*
|
|
19
|
-
* @param bytes - Raw bytes to encode (must have at least `count * 2` bytes).
|
|
20
|
-
* @param count - Number of words to produce (integer 1–16, default: 1).
|
|
21
|
-
* @param wordlist - Custom wordlist (must be exactly 2048 entries, default: en-v1).
|
|
22
|
-
* @returns Array of lowercase word strings.
|
|
23
|
-
* @throws {RangeError} If count is not an integer 1–16, wordlist is wrong size, or insufficient bytes.
|
|
24
|
-
*/
|
|
25
|
-
export declare function encodeAsWords(bytes: Uint8Array, count?: number, wordlist?: readonly string[]): string[];
|
|
26
|
-
/**
|
|
27
|
-
* Encode raw bytes as a numeric PIN with leading zeros.
|
|
28
|
-
* Uses the first ceil(digits * 0.415) bytes, interpreted as a big-endian
|
|
29
|
-
* integer, reduced modulo 10^digits.
|
|
30
|
-
*
|
|
31
|
-
* @param bytes - Raw bytes to encode (must be non-empty).
|
|
32
|
-
* @param digits - Number of PIN digits to produce (integer 1-10, default: 4).
|
|
33
|
-
* @returns Zero-padded numeric string of the specified length.
|
|
34
|
-
* @throws {RangeError} If digits is not an integer 1-10 or bytes is empty.
|
|
35
|
-
*/
|
|
36
|
-
export declare function encodeAsPin(bytes: Uint8Array, digits?: number): string;
|
|
37
|
-
/**
|
|
38
|
-
* Encode raw bytes as a lowercase hex string.
|
|
39
|
-
*
|
|
40
|
-
* @param bytes - Raw bytes to encode.
|
|
41
|
-
* @param length - Number of hex characters to produce (integer 1-64, default: 8).
|
|
42
|
-
* @returns Lowercase hex string of the specified length.
|
|
43
|
-
* @throws {RangeError} If length is not an integer 1-64 or insufficient bytes are provided.
|
|
44
|
-
*/
|
|
45
|
-
export declare function encodeAsHex(bytes: Uint8Array, length?: number): string;
|
|
46
|
-
/**
|
|
47
|
-
* Encode raw bytes using the specified encoding format.
|
|
48
|
-
* Returns a single string (words are space-joined).
|
|
49
|
-
*
|
|
50
|
-
* @param bytes - Raw bytes to encode.
|
|
51
|
-
* @param encoding - Encoding format: words, pin, or hex (default: single word).
|
|
52
|
-
* @returns Encoded token string (space-joined words, zero-padded PIN, or hex).
|
|
53
|
-
* @throws {RangeError} If encoding parameters are out of range or bytes are insufficient.
|
|
54
|
-
*/
|
|
55
|
-
export declare function encodeToken(bytes: Uint8Array, encoding?: TokenEncoding): string;
|
|
1
|
+
export { encodeAsWords, encodeAsPin, encodeAsHex, encodeToken, type TokenEncoding, DEFAULT_ENCODING, } from 'spoken-token';
|
|
56
2
|
//# sourceMappingURL=encoding.d.ts.map
|
package/dist/encoding.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../src/encoding.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../src/encoding.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EACpD,KAAK,aAAa,EAAE,gBAAgB,GACrC,MAAM,cAAc,CAAA"}
|
package/dist/encoding.js
CHANGED
|
@@ -1,98 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import { WORDLIST } from './wordlist.js';
|
|
3
|
-
/** Default encoding: single word from en-v1 wordlist. */
|
|
4
|
-
export const DEFAULT_ENCODING = { format: 'words', count: 1 };
|
|
5
|
-
/**
|
|
6
|
-
* Encode raw bytes as words using 11-bit indices into a wordlist.
|
|
7
|
-
* Each word uses a consecutive 2-byte slice: readUint16BE(bytes, i*2) % wordlistSize.
|
|
8
|
-
*
|
|
9
|
-
* @param bytes - Raw bytes to encode (must have at least `count * 2` bytes).
|
|
10
|
-
* @param count - Number of words to produce (integer 1–16, default: 1).
|
|
11
|
-
* @param wordlist - Custom wordlist (must be exactly 2048 entries, default: en-v1).
|
|
12
|
-
* @returns Array of lowercase word strings.
|
|
13
|
-
* @throws {RangeError} If count is not an integer 1–16, wordlist is wrong size, or insufficient bytes.
|
|
14
|
-
*/
|
|
15
|
-
export function encodeAsWords(bytes, count = 1, wordlist = WORDLIST) {
|
|
16
|
-
if (wordlist.length !== 2048)
|
|
17
|
-
throw new RangeError('Wordlist must contain exactly 2048 entries');
|
|
18
|
-
if (!Number.isInteger(count) || count < 1 || count > 16)
|
|
19
|
-
throw new RangeError('Word count must be an integer 1–16');
|
|
20
|
-
if (bytes.length < count * 2)
|
|
21
|
-
throw new RangeError('Not enough bytes for requested word count');
|
|
22
|
-
const words = [];
|
|
23
|
-
for (let i = 0; i < count; i++) {
|
|
24
|
-
const index = readUint16BE(bytes, i * 2) % wordlist.length;
|
|
25
|
-
words.push(wordlist[index]);
|
|
26
|
-
}
|
|
27
|
-
return words;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Encode raw bytes as a numeric PIN with leading zeros.
|
|
31
|
-
* Uses the first ceil(digits * 0.415) bytes, interpreted as a big-endian
|
|
32
|
-
* integer, reduced modulo 10^digits.
|
|
33
|
-
*
|
|
34
|
-
* @param bytes - Raw bytes to encode (must be non-empty).
|
|
35
|
-
* @param digits - Number of PIN digits to produce (integer 1-10, default: 4).
|
|
36
|
-
* @returns Zero-padded numeric string of the specified length.
|
|
37
|
-
* @throws {RangeError} If digits is not an integer 1-10 or bytes is empty.
|
|
38
|
-
*/
|
|
39
|
-
export function encodeAsPin(bytes, digits = 4) {
|
|
40
|
-
if (!Number.isInteger(digits) || digits < 1 || digits > 10)
|
|
41
|
-
throw new RangeError('PIN digits must be an integer 1–10');
|
|
42
|
-
if (bytes.length === 0)
|
|
43
|
-
throw new RangeError('Cannot encode empty byte array as PIN');
|
|
44
|
-
const needed = Math.min(Math.ceil(digits * 0.415), bytes.length);
|
|
45
|
-
const mod = Math.pow(10, digits);
|
|
46
|
-
// Use BigInt accumulation for 9–10 digits to avoid 32-bit overflow in >>> 0
|
|
47
|
-
if (digits >= 9) {
|
|
48
|
-
let bigVal = 0n;
|
|
49
|
-
for (let i = 0; i < needed; i++)
|
|
50
|
-
bigVal = bigVal * 256n + BigInt(bytes[i]);
|
|
51
|
-
return (Number(bigVal % BigInt(mod))).toString().padStart(digits, '0');
|
|
52
|
-
}
|
|
53
|
-
let value = 0;
|
|
54
|
-
for (let i = 0; i < needed; i++) {
|
|
55
|
-
value = (value * 256 + bytes[i]) >>> 0;
|
|
56
|
-
}
|
|
57
|
-
return (value % mod).toString().padStart(digits, '0');
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Encode raw bytes as a lowercase hex string.
|
|
61
|
-
*
|
|
62
|
-
* @param bytes - Raw bytes to encode.
|
|
63
|
-
* @param length - Number of hex characters to produce (integer 1-64, default: 8).
|
|
64
|
-
* @returns Lowercase hex string of the specified length.
|
|
65
|
-
* @throws {RangeError} If length is not an integer 1-64 or insufficient bytes are provided.
|
|
66
|
-
*/
|
|
67
|
-
export function encodeAsHex(bytes, length = 8) {
|
|
68
|
-
if (!Number.isInteger(length) || length < 1 || length > 64)
|
|
69
|
-
throw new RangeError('Hex length must be an integer 1–64');
|
|
70
|
-
const needed = Math.ceil(length / 2);
|
|
71
|
-
if (bytes.length < needed)
|
|
72
|
-
throw new RangeError(`Not enough bytes: need ${needed}, got ${bytes.length}`);
|
|
73
|
-
let hex = '';
|
|
74
|
-
for (let i = 0; i < needed && i < bytes.length; i++) {
|
|
75
|
-
hex += bytes[i].toString(16).padStart(2, '0');
|
|
76
|
-
}
|
|
77
|
-
return hex.slice(0, length);
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Encode raw bytes using the specified encoding format.
|
|
81
|
-
* Returns a single string (words are space-joined).
|
|
82
|
-
*
|
|
83
|
-
* @param bytes - Raw bytes to encode.
|
|
84
|
-
* @param encoding - Encoding format: words, pin, or hex (default: single word).
|
|
85
|
-
* @returns Encoded token string (space-joined words, zero-padded PIN, or hex).
|
|
86
|
-
* @throws {RangeError} If encoding parameters are out of range or bytes are insufficient.
|
|
87
|
-
*/
|
|
88
|
-
export function encodeToken(bytes, encoding = DEFAULT_ENCODING) {
|
|
89
|
-
switch (encoding.format) {
|
|
90
|
-
case 'words':
|
|
91
|
-
return encodeAsWords(bytes, encoding.count ?? 1, encoding.wordlist).join(' ');
|
|
92
|
-
case 'pin':
|
|
93
|
-
return encodeAsPin(bytes, encoding.digits ?? 4);
|
|
94
|
-
case 'hex':
|
|
95
|
-
return encodeAsHex(bytes, encoding.length ?? 8);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
1
|
+
export { encodeAsWords, encodeAsPin, encodeAsHex, encodeToken, DEFAULT_ENCODING, } from 'spoken-token';
|
|
98
2
|
//# sourceMappingURL=encoding.js.map
|
package/dist/encoding.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encoding.js","sourceRoot":"","sources":["../src/encoding.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"encoding.js","sourceRoot":"","sources":["../src/encoding.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAChC,gBAAgB,GACrC,MAAM,cAAc,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export { WORDLIST, WORDLIST_SIZE, getWord, indexOf, } from './wordlist.js';
|
|
2
2
|
export { getCounter, counterToBytes, counterFromEventId, DEFAULT_ROTATION_INTERVAL, MAX_COUNTER_OFFSET, } from './counter.js';
|
|
3
|
+
export { encodeAsWords, encodeAsPin, encodeAsHex, encodeToken, type TokenEncoding, DEFAULT_ENCODING, } from './encoding.js';
|
|
4
|
+
export { MAX_TOLERANCE, deriveTokenBytes, deriveToken, deriveDuressTokenBytes, deriveDuressToken, verifyToken, deriveLivenessToken, deriveDirectionalPair, type DirectionalPair, type TokenVerifyResult, type VerifyOptions, } from './token.js';
|
|
3
5
|
export { GROUP_CONTEXT, deriveVerificationWord, deriveVerificationPhrase, deriveDuressWord, deriveDuressPhrase, deriveCurrentWord, } from './derive.js';
|
|
4
6
|
export { verifyWord, type VerifyResult, type VerifyStatus, } from './verify.js';
|
|
5
7
|
export { createGroup, getCurrentWord, getCurrentDuressWord, advanceCounter, reseed, addMember, removeMember, removeMemberAndReseed, syncCounter, dissolveGroup, type GroupConfig, type GroupState, } from './group.js';
|
|
6
8
|
export { PRESETS, type PresetName, type GroupPreset, } from './presets.js';
|
|
7
9
|
export { deriveBeaconKey, deriveDuressKey, encryptBeacon, decryptBeacon, buildDuressAlert, encryptDuressAlert, decryptDuressAlert, type BeaconPayload, type DuressAlert, type DuressLocation, } from './beacon.js';
|
|
8
|
-
export { MAX_TOLERANCE, deriveTokenBytes, deriveToken, deriveDuressTokenBytes, deriveDuressToken, verifyToken, deriveLivenessToken, type TokenVerifyResult, type VerifyOptions, } from './token.js';
|
|
9
|
-
export { encodeAsWords, encodeAsPin, encodeAsHex, encodeToken, type TokenEncoding, DEFAULT_ENCODING, } from './encoding.js';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,GAC1C,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAC9C,yBAAyB,EAAE,kBAAkB,GAC9C,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EACpD,KAAK,aAAa,EAAE,gBAAgB,GACrC,MAAM,eAAe,CAAA;AAGtB,OAAO,EACL,aAAa,EACb,gBAAgB,EAAE,WAAW,EAC7B,sBAAsB,EAAE,iBAAiB,EACzC,WAAW,EAAE,mBAAmB,EAChC,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EAAE,KAAK,aAAa,GAC3C,MAAM,YAAY,CAAA;AAGnB,OAAO,EACL,aAAa,EACb,sBAAsB,EAAE,wBAAwB,EAChD,gBAAgB,EAAE,kBAAkB,EACpC,iBAAiB,GAClB,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,UAAU,EACV,KAAK,YAAY,EAAE,KAAK,YAAY,GACrC,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,WAAW,EAAE,cAAc,EAAE,oBAAoB,EACjD,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAC/C,qBAAqB,EAAE,WAAW,EAAE,aAAa,EACjD,KAAK,WAAW,EAAE,KAAK,UAAU,GAClC,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,GAC3C,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EACxD,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,GAC1D,MAAM,aAAa,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
// --- Re-exported from spoken-token (backwards compatibility) ---
|
|
2
3
|
export { WORDLIST, WORDLIST_SIZE, getWord, indexOf, } from './wordlist.js';
|
|
3
4
|
export { getCounter, counterToBytes, counterFromEventId, DEFAULT_ROTATION_INTERVAL, MAX_COUNTER_OFFSET, } from './counter.js';
|
|
5
|
+
export { encodeAsWords, encodeAsPin, encodeAsHex, encodeToken, DEFAULT_ENCODING, } from './encoding.js';
|
|
6
|
+
// --- CANARY Protocol (universal + duress-aware) ---
|
|
7
|
+
export { MAX_TOLERANCE, deriveTokenBytes, deriveToken, deriveDuressTokenBytes, deriveDuressToken, verifyToken, deriveLivenessToken, deriveDirectionalPair, } from './token.js';
|
|
8
|
+
// --- Group layer ---
|
|
4
9
|
export { GROUP_CONTEXT, deriveVerificationWord, deriveVerificationPhrase, deriveDuressWord, deriveDuressPhrase, deriveCurrentWord, } from './derive.js';
|
|
5
10
|
export { verifyWord, } from './verify.js';
|
|
6
11
|
export { createGroup, getCurrentWord, getCurrentDuressWord, advanceCounter, reseed, addMember, removeMember, removeMemberAndReseed, syncCounter, dissolveGroup, } from './group.js';
|
|
7
12
|
export { PRESETS, } from './presets.js';
|
|
13
|
+
// --- Beacon ---
|
|
8
14
|
export { deriveBeaconKey, deriveDuressKey, encryptBeacon, decryptBeacon, buildDuressAlert, encryptDuressAlert, decryptDuressAlert, } from './beacon.js';
|
|
9
|
-
// --- CANARY Protocol (universal API) ---
|
|
10
|
-
export { MAX_TOLERANCE, deriveTokenBytes, deriveToken, deriveDuressTokenBytes, deriveDuressToken, verifyToken, deriveLivenessToken, } from './token.js';
|
|
11
|
-
export { encodeAsWords, encodeAsPin, encodeAsHex, encodeToken, DEFAULT_ENCODING, } from './encoding.js';
|
|
12
15
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;AAEf,kEAAkE;AAClE,OAAO,EACL,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,GAC1C,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAC9C,yBAAyB,EAAE,kBAAkB,GAC9C,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAChC,gBAAgB,GACrC,MAAM,eAAe,CAAA;AAEtB,qDAAqD;AACrD,OAAO,EACL,aAAa,EACb,gBAAgB,EAAE,WAAW,EAC7B,sBAAsB,EAAE,iBAAiB,EACzC,WAAW,EAAE,mBAAmB,EAChC,qBAAqB,GAGtB,MAAM,YAAY,CAAA;AAEnB,sBAAsB;AACtB,OAAO,EACL,aAAa,EACb,sBAAsB,EAAE,wBAAwB,EAChD,gBAAgB,EAAE,kBAAkB,EACpC,iBAAiB,GAClB,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,UAAU,GAEX,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,WAAW,EAAE,cAAc,EAAE,oBAAoB,EACjD,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAC/C,qBAAqB,EAAE,WAAW,EAAE,aAAa,GAElD,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,OAAO,GACR,MAAM,cAAc,CAAA;AAErB,iBAAiB;AACjB,OAAO,EACL,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,GAEzD,MAAM,aAAa,CAAA"}
|
package/dist/nostr.d.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* NIP-XX Nostr transport builders for Simple Shared Secret (SSG) groups.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
4
|
+
* Three event kinds are used:
|
|
5
|
+
*
|
|
6
|
+
* - **kind 30078** (parameterised replaceable) — group state and stored signals.
|
|
7
|
+
* The d-tag uses the `ssg/` namespace prefix.
|
|
8
|
+
* - **kind 20078** (ephemeral) — real-time signals between group members.
|
|
9
|
+
* - **kind 14** (rumour / unsigned inner event) — NIP-17 gift-wrapped DMs carrying
|
|
10
|
+
* seed distribution, reseed, and other private payloads.
|
|
11
|
+
*
|
|
12
|
+
* Builders return unsigned events (`UnsignedEvent`). The consumer is responsible for
|
|
13
|
+
* signing (NIP-01), encrypting content (NIP-44), and wrapping (NIP-59) as needed.
|
|
7
14
|
*/
|
|
15
|
+
/** NIP-XX event kinds used by SSG groups. */
|
|
8
16
|
export declare const KINDS: {
|
|
9
|
-
readonly
|
|
10
|
-
readonly
|
|
11
|
-
readonly
|
|
12
|
-
readonly reseed: 28801;
|
|
13
|
-
readonly wordUsed: 28802;
|
|
14
|
-
readonly beacon: 20800;
|
|
17
|
+
readonly groupState: 30078;
|
|
18
|
+
readonly signal: 20078;
|
|
19
|
+
readonly giftWrap: 1059;
|
|
15
20
|
};
|
|
16
21
|
/** Unsigned Nostr event (consumer signs with their own library). */
|
|
17
22
|
export interface UnsignedEvent {
|
|
@@ -20,83 +25,39 @@ export interface UnsignedEvent {
|
|
|
20
25
|
tags: string[][];
|
|
21
26
|
created_at: number;
|
|
22
27
|
}
|
|
23
|
-
export interface
|
|
28
|
+
export interface GroupStateEventParams {
|
|
24
29
|
groupId: string;
|
|
25
|
-
name: string;
|
|
26
30
|
members: string[];
|
|
27
|
-
rotationInterval: number;
|
|
28
|
-
wordCount: 1 | 2 | 3;
|
|
29
|
-
wordlist: string;
|
|
30
31
|
encryptedContent: string;
|
|
32
|
+
rotationInterval?: number;
|
|
33
|
+
tolerance?: number;
|
|
31
34
|
expiration?: number;
|
|
32
35
|
}
|
|
33
|
-
|
|
34
|
-
* Build an unsigned kind 38800 group definition event.
|
|
35
|
-
*
|
|
36
|
-
* @param params - Group event parameters including groupId, name, members, and encrypted content.
|
|
37
|
-
* @returns An {@link UnsignedEvent} ready to be signed and published.
|
|
38
|
-
* @throws {Error} If groupId/name are invalid, member pubkeys are malformed, rotationInterval is non-positive, or wordCount is not 1/2/3.
|
|
39
|
-
*/
|
|
40
|
-
export declare function buildGroupEvent(params: GroupEventParams): UnsignedEvent;
|
|
41
|
-
export interface SeedDistributionParams {
|
|
42
|
-
recipientPubkey: string;
|
|
43
|
-
groupEventId: string;
|
|
44
|
-
encryptedContent: string;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Build an unsigned kind 28800 seed distribution event.
|
|
48
|
-
*
|
|
49
|
-
* @param params - Seed distribution parameters including recipient pubkey, group event ID, and encrypted seed.
|
|
50
|
-
* @returns An {@link UnsignedEvent} ready to be signed and published.
|
|
51
|
-
* @throws {Error} If recipientPubkey or groupEventId are not valid 64-character hex strings.
|
|
52
|
-
*/
|
|
53
|
-
export declare function buildSeedDistributionEvent(params: SeedDistributionParams): UnsignedEvent;
|
|
54
|
-
export interface MemberUpdateParams {
|
|
36
|
+
export interface StoredSignalEventParams {
|
|
55
37
|
groupId: string;
|
|
56
|
-
|
|
57
|
-
memberPubkey: string;
|
|
58
|
-
reseed: boolean;
|
|
38
|
+
signalType: string;
|
|
59
39
|
encryptedContent: string;
|
|
60
40
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
* @param params - Member update parameters including groupId, action ('add' | 'remove'), member pubkey, and reseed flag.
|
|
65
|
-
* @returns An {@link UnsignedEvent} ready to be signed and published.
|
|
66
|
-
* @throws {Error} If action is not 'add' or 'remove', or pubkey/groupId are invalid.
|
|
67
|
-
*/
|
|
68
|
-
export declare function buildMemberUpdateEvent(params: MemberUpdateParams): UnsignedEvent;
|
|
69
|
-
export interface ReseedParams {
|
|
70
|
-
groupEventId: string;
|
|
71
|
-
reason: 'member_removed' | 'compromise' | 'scheduled' | 'duress';
|
|
41
|
+
export interface SignalEventParams {
|
|
42
|
+
groupId: string;
|
|
43
|
+
signalType: string;
|
|
72
44
|
encryptedContent: string;
|
|
73
45
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
* @param params - Reseed parameters including group event ID, reason, and encrypted new seed.
|
|
78
|
-
* @returns An {@link UnsignedEvent} ready to be signed and published.
|
|
79
|
-
* @throws {Error} If groupEventId is not a valid 64-character hex string or reason is invalid.
|
|
80
|
-
*/
|
|
81
|
-
export declare function buildReseedEvent(params: ReseedParams): UnsignedEvent;
|
|
82
|
-
export interface WordUsedParams {
|
|
83
|
-
groupEventId: string;
|
|
46
|
+
export interface RumourEventParams {
|
|
47
|
+
recipientPubkey: string;
|
|
48
|
+
subject: string;
|
|
84
49
|
encryptedContent: string;
|
|
50
|
+
groupEventId?: string;
|
|
85
51
|
}
|
|
86
|
-
/**
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
export declare function buildWordUsedEvent(params: WordUsedParams): UnsignedEvent;
|
|
94
|
-
export interface BeaconEventParams {
|
|
95
|
-
groupId: string;
|
|
96
|
-
encryptedContent: string;
|
|
97
|
-
expiration?: number;
|
|
52
|
+
/** Plaintext payload for group configuration (kind 30078 encrypted content). */
|
|
53
|
+
export interface GroupConfigPayload {
|
|
54
|
+
description: string;
|
|
55
|
+
policies: {
|
|
56
|
+
invite_by: 'admin' | 'any_member';
|
|
57
|
+
reseed_by: 'admin' | 'any_admin';
|
|
58
|
+
};
|
|
98
59
|
}
|
|
99
|
-
/** Plaintext payload for
|
|
60
|
+
/** Plaintext payload for seed distribution (kind 14 rumour). */
|
|
100
61
|
export interface SeedDistributionPayload {
|
|
101
62
|
seed: string;
|
|
102
63
|
/** Allows re-seeding mid-window without waiting for the next natural time rotation. */
|
|
@@ -104,31 +65,86 @@ export interface SeedDistributionPayload {
|
|
|
104
65
|
/** The group's d-tag value (group identifier). */
|
|
105
66
|
group_d: string;
|
|
106
67
|
}
|
|
107
|
-
/** Plaintext payload for
|
|
108
|
-
export interface GroupEventPayload {
|
|
109
|
-
description: string;
|
|
110
|
-
policies: {
|
|
111
|
-
invite_by: 'creator' | 'any_member';
|
|
112
|
-
reseed_by: 'creator' | 'any_admin';
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
/** Plaintext payload for kind 28801 (reseed notification). */
|
|
68
|
+
/** Plaintext payload for reseed notification (kind 14 rumour). */
|
|
116
69
|
export interface ReseedPayload {
|
|
117
70
|
seed: string;
|
|
118
71
|
reason: 'member_removed' | 'compromise' | 'scheduled' | 'duress';
|
|
119
72
|
}
|
|
120
|
-
/** Plaintext payload for
|
|
73
|
+
/** Plaintext payload for member update (kind 14 rumour). */
|
|
74
|
+
export interface MemberUpdatePayload {
|
|
75
|
+
action: 'add' | 'remove';
|
|
76
|
+
member: string;
|
|
77
|
+
reseed: boolean;
|
|
78
|
+
}
|
|
79
|
+
/** Plaintext payload for counter advance (kind 20078 signal). */
|
|
80
|
+
export interface CounterAdvancePayload {
|
|
81
|
+
new_counter: number;
|
|
82
|
+
}
|
|
83
|
+
/** Plaintext payload for word-used / burn-after-use (kind 20078 signal). */
|
|
121
84
|
export interface WordUsedPayload {
|
|
122
85
|
new_counter: number;
|
|
123
86
|
used_by: string;
|
|
124
87
|
duress: boolean;
|
|
125
88
|
}
|
|
89
|
+
export declare function validatePubkey(pubkey: string, label: string): void;
|
|
90
|
+
export declare function validateTagString(value: string, label: string): void;
|
|
91
|
+
export declare function validateExpiration(expiration: number): void;
|
|
92
|
+
export declare function validateEventId(eventId: string, label: string): void;
|
|
93
|
+
/**
|
|
94
|
+
* SHA-256 hash a group ID for use in d-tags where privacy matters.
|
|
95
|
+
*
|
|
96
|
+
* Duplicates hashGroupTag from sync-crypto.ts — kept independent to avoid
|
|
97
|
+
* coupling builders to sync layer.
|
|
98
|
+
*/
|
|
99
|
+
export declare function hashGroupId(groupId: string): string;
|
|
100
|
+
/**
|
|
101
|
+
* Build an unsigned kind 30078 group state event.
|
|
102
|
+
*
|
|
103
|
+
* The d-tag uses plaintext `ssg/<groupId>` since content is NIP-44 encrypted.
|
|
104
|
+
* Includes NIP-32 labels for SSG namespace and p-tags for each member.
|
|
105
|
+
*
|
|
106
|
+
* @param params - Group state parameters.
|
|
107
|
+
* @returns An {@link UnsignedEvent} ready to be signed and published.
|
|
108
|
+
* @throws {Error} If groupId is invalid, member pubkeys are malformed, or optional
|
|
109
|
+
* numeric fields are out of range.
|
|
110
|
+
*/
|
|
111
|
+
export declare function buildGroupStateEvent(params: GroupStateEventParams): UnsignedEvent;
|
|
112
|
+
/**
|
|
113
|
+
* Build an unsigned kind 30078 stored signal event.
|
|
114
|
+
*
|
|
115
|
+
* The d-tag uses a SHA-256 hash of the group ID (for privacy) scoped by signal type:
|
|
116
|
+
* `ssg/<SHA256(groupId)>:<signalType>`.
|
|
117
|
+
*
|
|
118
|
+
* Includes a 7-day NIP-40 expiration tag.
|
|
119
|
+
*
|
|
120
|
+
* @param params - Stored signal parameters.
|
|
121
|
+
* @returns An {@link UnsignedEvent} ready to be signed and published.
|
|
122
|
+
* @throws {Error} If groupId or signalType are empty.
|
|
123
|
+
*/
|
|
124
|
+
export declare function buildStoredSignalEvent(params: StoredSignalEventParams): UnsignedEvent;
|
|
126
125
|
/**
|
|
127
|
-
* Build an unsigned kind
|
|
126
|
+
* Build an unsigned kind 20078 ephemeral signal event.
|
|
127
|
+
*
|
|
128
|
+
* The d-tag uses a SHA-256 hash of the group ID (for privacy):
|
|
129
|
+
* `ssg/<SHA256(groupId)>`. A t-tag carries the signal type.
|
|
130
|
+
*
|
|
131
|
+
* No expiration tag — ephemeral events are not stored by relays.
|
|
128
132
|
*
|
|
129
|
-
* @param params -
|
|
133
|
+
* @param params - Signal parameters.
|
|
130
134
|
* @returns An {@link UnsignedEvent} ready to be signed and published.
|
|
131
|
-
* @throws {Error} If groupId
|
|
135
|
+
* @throws {Error} If groupId or signalType are empty.
|
|
136
|
+
*/
|
|
137
|
+
export declare function buildSignalEvent(params: SignalEventParams): UnsignedEvent;
|
|
138
|
+
/**
|
|
139
|
+
* Build an unsigned kind 14 rumour event for NIP-17 gift wrapping.
|
|
140
|
+
*
|
|
141
|
+
* The consumer must set the `pubkey` field before computing the event ID.
|
|
142
|
+
* The rumour is then sealed (NIP-44 encrypt + kind 13) and gift-wrapped
|
|
143
|
+
* (kind 1059) by the caller.
|
|
144
|
+
*
|
|
145
|
+
* @param params - Rumour parameters including recipient and subject.
|
|
146
|
+
* @returns An {@link UnsignedEvent} ready for NIP-59 wrapping.
|
|
147
|
+
* @throws {Error} If recipientPubkey is invalid, subject is empty, or groupEventId is invalid.
|
|
132
148
|
*/
|
|
133
|
-
export declare function
|
|
149
|
+
export declare function buildRumourEvent(params: RumourEventParams): UnsignedEvent;
|
|
134
150
|
//# sourceMappingURL=nostr.d.ts.map
|
package/dist/nostr.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nostr.d.ts","sourceRoot":"","sources":["../src/nostr.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"nostr.d.ts","sourceRoot":"","sources":["../src/nostr.ts"],"names":[],"mappings":"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"}
|