@unicitylabs/nostr-js-sdk 0.0.1
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 +290 -0
- package/dist/browser/index.js +8444 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/index.min.js +12 -0
- package/dist/browser/index.min.js.map +1 -0
- package/dist/browser/index.umd.js +8524 -0
- package/dist/browser/index.umd.js.map +1 -0
- package/dist/browser/index.umd.min.js +12 -0
- package/dist/browser/index.umd.min.js.map +1 -0
- package/dist/cjs/NostrKeyManager.js +265 -0
- package/dist/cjs/NostrKeyManager.js.map +1 -0
- package/dist/cjs/client/NostrClient.js +518 -0
- package/dist/cjs/client/NostrClient.js.map +1 -0
- package/dist/cjs/client/NostrEventListener.js +31 -0
- package/dist/cjs/client/NostrEventListener.js.map +1 -0
- package/dist/cjs/client/WebSocketAdapter.js +90 -0
- package/dist/cjs/client/WebSocketAdapter.js.map +1 -0
- package/dist/cjs/client/index.js +18 -0
- package/dist/cjs/client/index.js.map +1 -0
- package/dist/cjs/crypto/bech32.js +201 -0
- package/dist/cjs/crypto/bech32.js.map +1 -0
- package/dist/cjs/crypto/index.js +49 -0
- package/dist/cjs/crypto/index.js.map +1 -0
- package/dist/cjs/crypto/nip04.js +327 -0
- package/dist/cjs/crypto/nip04.js.map +1 -0
- package/dist/cjs/crypto/schnorr.js +101 -0
- package/dist/cjs/crypto/schnorr.js.map +1 -0
- package/dist/cjs/index.js +57 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/nametag/NametagBinding.js +196 -0
- package/dist/cjs/nametag/NametagBinding.js.map +1 -0
- package/dist/cjs/nametag/NametagUtils.js +156 -0
- package/dist/cjs/nametag/NametagUtils.js.map +1 -0
- package/dist/cjs/nametag/index.js +47 -0
- package/dist/cjs/nametag/index.js.map +1 -0
- package/dist/cjs/protocol/Event.js +209 -0
- package/dist/cjs/protocol/Event.js.map +1 -0
- package/dist/cjs/protocol/EventKinds.js +126 -0
- package/dist/cjs/protocol/EventKinds.js.map +1 -0
- package/dist/cjs/protocol/Filter.js +202 -0
- package/dist/cjs/protocol/Filter.js.map +1 -0
- package/dist/cjs/protocol/index.js +50 -0
- package/dist/cjs/protocol/index.js.map +1 -0
- package/dist/cjs/token/TokenTransferProtocol.js +196 -0
- package/dist/cjs/token/TokenTransferProtocol.js.map +1 -0
- package/dist/cjs/token/index.js +45 -0
- package/dist/cjs/token/index.js.map +1 -0
- package/dist/esm/NostrKeyManager.js +228 -0
- package/dist/esm/NostrKeyManager.js.map +1 -0
- package/dist/esm/client/NostrClient.js +481 -0
- package/dist/esm/client/NostrClient.js.map +1 -0
- package/dist/esm/client/NostrEventListener.js +27 -0
- package/dist/esm/client/NostrEventListener.js.map +1 -0
- package/dist/esm/client/WebSocketAdapter.js +52 -0
- package/dist/esm/client/WebSocketAdapter.js.map +1 -0
- package/dist/esm/client/index.js +7 -0
- package/dist/esm/client/index.js.map +1 -0
- package/dist/esm/crypto/bech32.js +193 -0
- package/dist/esm/crypto/bech32.js.map +1 -0
- package/dist/esm/crypto/index.js +10 -0
- package/dist/esm/crypto/index.js.map +1 -0
- package/dist/esm/crypto/nip04.js +286 -0
- package/dist/esm/crypto/nip04.js.map +1 -0
- package/dist/esm/crypto/schnorr.js +93 -0
- package/dist/esm/crypto/schnorr.js.map +1 -0
- package/dist/esm/index.js +32 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/nametag/NametagBinding.js +155 -0
- package/dist/esm/nametag/NametagBinding.js.map +1 -0
- package/dist/esm/nametag/NametagUtils.js +149 -0
- package/dist/esm/nametag/NametagUtils.js.map +1 -0
- package/dist/esm/nametag/index.js +8 -0
- package/dist/esm/nametag/index.js.map +1 -0
- package/dist/esm/protocol/Event.js +172 -0
- package/dist/esm/protocol/Event.js.map +1 -0
- package/dist/esm/protocol/EventKinds.js +119 -0
- package/dist/esm/protocol/EventKinds.js.map +1 -0
- package/dist/esm/protocol/Filter.js +197 -0
- package/dist/esm/protocol/Filter.js.map +1 -0
- package/dist/esm/protocol/index.js +8 -0
- package/dist/esm/protocol/index.js.map +1 -0
- package/dist/esm/token/TokenTransferProtocol.js +154 -0
- package/dist/esm/token/TokenTransferProtocol.js.map +1 -0
- package/dist/esm/token/index.js +6 -0
- package/dist/esm/token/index.js.map +1 -0
- package/dist/types/NostrKeyManager.d.ts +150 -0
- package/dist/types/NostrKeyManager.d.ts.map +1 -0
- package/dist/types/client/NostrClient.d.ts +154 -0
- package/dist/types/client/NostrClient.d.ts.map +1 -0
- package/dist/types/client/NostrEventListener.d.ts +40 -0
- package/dist/types/client/NostrEventListener.d.ts.map +1 -0
- package/dist/types/client/WebSocketAdapter.d.ts +55 -0
- package/dist/types/client/WebSocketAdapter.d.ts.map +1 -0
- package/dist/types/client/index.d.ts +9 -0
- package/dist/types/client/index.d.ts.map +1 -0
- package/dist/types/crypto/bech32.d.ts +51 -0
- package/dist/types/crypto/bech32.d.ts.map +1 -0
- package/dist/types/crypto/index.d.ts +10 -0
- package/dist/types/crypto/index.d.ts.map +1 -0
- package/dist/types/crypto/nip04.d.ts +56 -0
- package/dist/types/crypto/nip04.d.ts.map +1 -0
- package/dist/types/crypto/schnorr.d.ts +47 -0
- package/dist/types/crypto/schnorr.d.ts.map +1 -0
- package/dist/types/index.d.ts +31 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/nametag/NametagBinding.d.ts +68 -0
- package/dist/types/nametag/NametagBinding.d.ts.map +1 -0
- package/dist/types/nametag/NametagUtils.d.ts +44 -0
- package/dist/types/nametag/NametagUtils.d.ts.map +1 -0
- package/dist/types/nametag/index.d.ts +8 -0
- package/dist/types/nametag/index.d.ts.map +1 -0
- package/dist/types/protocol/Event.d.ts +112 -0
- package/dist/types/protocol/Event.d.ts.map +1 -0
- package/dist/types/protocol/EventKinds.d.ts +62 -0
- package/dist/types/protocol/EventKinds.d.ts.map +1 -0
- package/dist/types/protocol/Filter.d.ts +146 -0
- package/dist/types/protocol/Filter.d.ts.map +1 -0
- package/dist/types/protocol/index.d.ts +10 -0
- package/dist/types/protocol/index.d.ts.map +1 -0
- package/dist/types/token/TokenTransferProtocol.d.ts +67 -0
- package/dist/types/token/TokenTransferProtocol.d.ts.map +1 -0
- package/dist/types/token/index.d.ts +6 -0
- package/dist/types/token/index.d.ts.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NametagBinding - Create and parse nametag binding events.
|
|
3
|
+
* Uses kind 30078 (APP_DATA) for parameterized replaceable events.
|
|
4
|
+
*/
|
|
5
|
+
import { Event } from '../protocol/Event.js';
|
|
6
|
+
import { Filter } from '../protocol/Filter.js';
|
|
7
|
+
import * as EventKinds from '../protocol/EventKinds.js';
|
|
8
|
+
import * as NametagUtils from './NametagUtils.js';
|
|
9
|
+
/** Default country code for phone number normalization */
|
|
10
|
+
const DEFAULT_COUNTRY = 'US';
|
|
11
|
+
/**
|
|
12
|
+
* Create a nametag binding event.
|
|
13
|
+
*
|
|
14
|
+
* Event structure:
|
|
15
|
+
* - Kind: 30078 (APP_DATA - parameterized replaceable)
|
|
16
|
+
* - Tags:
|
|
17
|
+
* - ["d", "<hashed_nametag>"] - Required for parameterized replaceable
|
|
18
|
+
* - ["nametag", "<hashed_nametag>"] - Hashed for privacy
|
|
19
|
+
* - ["t", "<hashed_nametag>"] - Indexed tag for relay search
|
|
20
|
+
* - ["address", "<unicity_addr>"] - Unicity blockchain address
|
|
21
|
+
* - Content: JSON with nametag_hash, address, verified timestamp
|
|
22
|
+
*
|
|
23
|
+
* @param keyManager Key manager with signing keys
|
|
24
|
+
* @param nametagId Nametag identifier (phone number or username)
|
|
25
|
+
* @param unicityAddress Unicity blockchain address
|
|
26
|
+
* @param defaultCountry Default country code for phone normalization
|
|
27
|
+
* @returns Signed event
|
|
28
|
+
*/
|
|
29
|
+
export async function createBindingEvent(keyManager, nametagId, unicityAddress, defaultCountry = DEFAULT_COUNTRY) {
|
|
30
|
+
const hashedNametag = NametagUtils.hashNametag(nametagId, defaultCountry);
|
|
31
|
+
const content = {
|
|
32
|
+
nametag_hash: hashedNametag,
|
|
33
|
+
address: unicityAddress,
|
|
34
|
+
verified: Date.now(),
|
|
35
|
+
};
|
|
36
|
+
const event = Event.create(keyManager, {
|
|
37
|
+
kind: EventKinds.APP_DATA,
|
|
38
|
+
tags: [
|
|
39
|
+
['d', hashedNametag],
|
|
40
|
+
['nametag', hashedNametag],
|
|
41
|
+
['t', hashedNametag],
|
|
42
|
+
['address', unicityAddress],
|
|
43
|
+
],
|
|
44
|
+
content: JSON.stringify(content),
|
|
45
|
+
});
|
|
46
|
+
return event;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create a filter to query pubkey by nametag.
|
|
50
|
+
* Query direction: nametag → pubkey
|
|
51
|
+
*
|
|
52
|
+
* @param nametagId Nametag identifier
|
|
53
|
+
* @param defaultCountry Default country code for phone normalization
|
|
54
|
+
* @returns Filter for nametag binding events
|
|
55
|
+
*/
|
|
56
|
+
export function createNametagToPubkeyFilter(nametagId, defaultCountry = DEFAULT_COUNTRY) {
|
|
57
|
+
const hashedNametag = NametagUtils.hashNametag(nametagId, defaultCountry);
|
|
58
|
+
return Filter.builder()
|
|
59
|
+
.kinds(EventKinds.APP_DATA)
|
|
60
|
+
.tTags(hashedNametag)
|
|
61
|
+
.build();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Create a filter to query nametags by pubkey.
|
|
65
|
+
* Query direction: pubkey → nametags
|
|
66
|
+
*
|
|
67
|
+
* @param nostrPubkey Nostr public key (hex)
|
|
68
|
+
* @returns Filter for nametag binding events
|
|
69
|
+
*/
|
|
70
|
+
export function createPubkeyToNametagFilter(nostrPubkey) {
|
|
71
|
+
return Filter.builder()
|
|
72
|
+
.kinds(EventKinds.APP_DATA)
|
|
73
|
+
.authors(nostrPubkey)
|
|
74
|
+
.limit(10)
|
|
75
|
+
.build();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Parse the hashed nametag from a binding event.
|
|
79
|
+
* Tries tags first, then content JSON.
|
|
80
|
+
*
|
|
81
|
+
* @param event Binding event
|
|
82
|
+
* @returns Hashed nametag, or undefined if not found
|
|
83
|
+
*/
|
|
84
|
+
export function parseNametagHashFromEvent(event) {
|
|
85
|
+
// Try "nametag" tag first
|
|
86
|
+
const fromTag = event.getTagValue('nametag');
|
|
87
|
+
if (fromTag) {
|
|
88
|
+
return fromTag;
|
|
89
|
+
}
|
|
90
|
+
// Try "d" tag
|
|
91
|
+
const fromDTag = event.getTagValue('d');
|
|
92
|
+
if (fromDTag) {
|
|
93
|
+
return fromDTag;
|
|
94
|
+
}
|
|
95
|
+
// Try content JSON
|
|
96
|
+
try {
|
|
97
|
+
const content = JSON.parse(event.content);
|
|
98
|
+
return content.nametag_hash;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Parse the Unicity address from a binding event.
|
|
106
|
+
* Tries tags first, then content JSON.
|
|
107
|
+
*
|
|
108
|
+
* @param event Binding event
|
|
109
|
+
* @returns Unicity address, or undefined if not found
|
|
110
|
+
*/
|
|
111
|
+
export function parseAddressFromEvent(event) {
|
|
112
|
+
// Try "address" tag first
|
|
113
|
+
const fromTag = event.getTagValue('address');
|
|
114
|
+
if (fromTag) {
|
|
115
|
+
return fromTag;
|
|
116
|
+
}
|
|
117
|
+
// Try content JSON
|
|
118
|
+
try {
|
|
119
|
+
const content = JSON.parse(event.content);
|
|
120
|
+
return content.address;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Verify that a binding event is valid.
|
|
128
|
+
* Checks signature and structure.
|
|
129
|
+
*
|
|
130
|
+
* @param event Event to verify
|
|
131
|
+
* @returns true if the binding event is valid
|
|
132
|
+
*/
|
|
133
|
+
export function isValidBindingEvent(event) {
|
|
134
|
+
// Check event kind
|
|
135
|
+
if (event.kind !== EventKinds.APP_DATA) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
// Check required tags
|
|
139
|
+
if (!event.hasTag('d')) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
// Check content structure
|
|
143
|
+
try {
|
|
144
|
+
const content = JSON.parse(event.content);
|
|
145
|
+
if (!content.nametag_hash || !content.address) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
// Verify signature
|
|
153
|
+
return event.verify();
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=NametagBinding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NametagBinding.js","sourceRoot":"","sources":["../../../src/nametag/NametagBinding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NametagUtils - Privacy-preserving nametag normalization and hashing.
|
|
3
|
+
* Supports phone number normalization to E.164 format.
|
|
4
|
+
*/
|
|
5
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
6
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
7
|
+
import { parsePhoneNumber, isValidPhoneNumber } from 'libphonenumber-js';
|
|
8
|
+
/** Salt prefix for nametag hashing */
|
|
9
|
+
const NAMETAG_SALT = 'unicity:nametag:';
|
|
10
|
+
/** Default country code for phone number normalization */
|
|
11
|
+
const DEFAULT_COUNTRY = 'US';
|
|
12
|
+
/**
|
|
13
|
+
* Compute SHA-256 hash of a string.
|
|
14
|
+
* @param input String to hash
|
|
15
|
+
* @returns Hex-encoded hash
|
|
16
|
+
*/
|
|
17
|
+
function sha256Hex(input) {
|
|
18
|
+
const bytes = new TextEncoder().encode(input);
|
|
19
|
+
return bytesToHex(sha256(bytes));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Check if a string looks like a phone number.
|
|
23
|
+
* Heuristic: starts with + OR has >50% digits AND >= 7 digits total.
|
|
24
|
+
* @param str String to check
|
|
25
|
+
* @returns true if the string looks like a phone number
|
|
26
|
+
*/
|
|
27
|
+
function isLikelyPhoneNumber(str) {
|
|
28
|
+
if (str.startsWith('+')) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
const digitsOnly = str.replace(/\D/g, '');
|
|
32
|
+
const digitCount = digitsOnly.length;
|
|
33
|
+
if (digitCount < 7) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// Count non-digit characters (excluding common phone number chars)
|
|
37
|
+
const cleanedLength = str.replace(/[\s\-\(\)\.]/g, '').length;
|
|
38
|
+
const digitRatio = digitCount / cleanedLength;
|
|
39
|
+
return digitRatio > 0.5;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Normalize a phone number to E.164 format.
|
|
43
|
+
* @param phoneNumber Phone number string
|
|
44
|
+
* @param defaultCountry Default country code
|
|
45
|
+
* @returns E.164 formatted phone number, or null if invalid
|
|
46
|
+
*/
|
|
47
|
+
function normalizePhoneNumber(phoneNumber, defaultCountry) {
|
|
48
|
+
try {
|
|
49
|
+
// Try to parse with default country
|
|
50
|
+
if (isValidPhoneNumber(phoneNumber, defaultCountry)) {
|
|
51
|
+
const parsed = parsePhoneNumber(phoneNumber, defaultCountry);
|
|
52
|
+
return parsed.format('E.164');
|
|
53
|
+
}
|
|
54
|
+
// Try without default country (for numbers with country code)
|
|
55
|
+
if (isValidPhoneNumber(phoneNumber)) {
|
|
56
|
+
const parsed = parsePhoneNumber(phoneNumber);
|
|
57
|
+
return parsed.format('E.164');
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Normalize a nametag for hashing.
|
|
67
|
+
* - If it looks like a phone number, normalize to E.164
|
|
68
|
+
* - Otherwise, lowercase and remove @unicity suffix
|
|
69
|
+
* @param nametag Nametag to normalize
|
|
70
|
+
* @param defaultCountry Default country code for phone normalization
|
|
71
|
+
* @returns Normalized nametag
|
|
72
|
+
*/
|
|
73
|
+
export function normalizeNametag(nametag, defaultCountry = DEFAULT_COUNTRY) {
|
|
74
|
+
const trimmed = nametag.trim();
|
|
75
|
+
if (isLikelyPhoneNumber(trimmed)) {
|
|
76
|
+
const normalized = normalizePhoneNumber(trimmed, defaultCountry);
|
|
77
|
+
if (normalized) {
|
|
78
|
+
return normalized;
|
|
79
|
+
}
|
|
80
|
+
// If phone normalization fails, fall through to standard normalization
|
|
81
|
+
}
|
|
82
|
+
// Standard normalization: lowercase, remove @unicity suffix
|
|
83
|
+
let normalized = trimmed.toLowerCase();
|
|
84
|
+
if (normalized.endsWith('@unicity')) {
|
|
85
|
+
normalized = normalized.slice(0, -8);
|
|
86
|
+
}
|
|
87
|
+
return normalized;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Hash a nametag with the standard salt.
|
|
91
|
+
* @param nametag Nametag to hash
|
|
92
|
+
* @param defaultCountry Default country code for phone normalization
|
|
93
|
+
* @returns Hex-encoded SHA-256 hash
|
|
94
|
+
*/
|
|
95
|
+
export function hashNametag(nametag, defaultCountry = DEFAULT_COUNTRY) {
|
|
96
|
+
const normalized = normalizeNametag(nametag, defaultCountry);
|
|
97
|
+
return sha256Hex(NAMETAG_SALT + normalized);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Compare two nametags for equality (handling format variations).
|
|
101
|
+
* @param tag1 First nametag
|
|
102
|
+
* @param tag2 Second nametag
|
|
103
|
+
* @param defaultCountry Default country code for phone normalization
|
|
104
|
+
* @returns true if the nametags represent the same identity
|
|
105
|
+
*/
|
|
106
|
+
export function areSameNametag(tag1, tag2, defaultCountry = DEFAULT_COUNTRY) {
|
|
107
|
+
const normalized1 = normalizeNametag(tag1, defaultCountry);
|
|
108
|
+
const normalized2 = normalizeNametag(tag2, defaultCountry);
|
|
109
|
+
return normalized1 === normalized2;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Format a nametag for display (privacy-preserving).
|
|
113
|
+
* For phone numbers, hides middle digits.
|
|
114
|
+
* @param nametag Nametag to format
|
|
115
|
+
* @param defaultCountry Default country code for phone normalization
|
|
116
|
+
* @returns Display-safe formatted nametag
|
|
117
|
+
*/
|
|
118
|
+
export function formatForDisplay(nametag, defaultCountry = DEFAULT_COUNTRY) {
|
|
119
|
+
const trimmed = nametag.trim();
|
|
120
|
+
if (isLikelyPhoneNumber(trimmed)) {
|
|
121
|
+
const normalized = normalizePhoneNumber(trimmed, defaultCountry);
|
|
122
|
+
if (normalized) {
|
|
123
|
+
// Hide middle digits: +1415***2671
|
|
124
|
+
const digits = normalized.slice(1); // Remove +
|
|
125
|
+
if (digits.length > 6) {
|
|
126
|
+
const start = digits.slice(0, 4);
|
|
127
|
+
const end = digits.slice(-4);
|
|
128
|
+
return '+' + start + '***' + end;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return normalizeNametag(nametag, defaultCountry);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if a string is a valid phone number.
|
|
136
|
+
* @param str String to check
|
|
137
|
+
* @param defaultCountry Default country code
|
|
138
|
+
* @returns true if the string is a valid phone number
|
|
139
|
+
*/
|
|
140
|
+
export function isPhoneNumber(str, defaultCountry = DEFAULT_COUNTRY) {
|
|
141
|
+
try {
|
|
142
|
+
return isValidPhoneNumber(str, defaultCountry) ||
|
|
143
|
+
isValidPhoneNumber(str);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=NametagUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NametagUtils.js","sourceRoot":"","sources":["../../../src/nametag/NametagUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nametag module - Privacy-preserving identity bindings
|
|
3
|
+
*/
|
|
4
|
+
export * as NametagUtils from './NametagUtils.js';
|
|
5
|
+
export * from './NametagUtils.js';
|
|
6
|
+
export * as NametagBinding from './NametagBinding.js';
|
|
7
|
+
export * from './NametagBinding.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/nametag/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAC;AAClD,cAAc,mBAAmB,CAAC;AAClC,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event - Represents a Nostr event (NIP-01).
|
|
3
|
+
* Provides serialization, signing, and verification functionality.
|
|
4
|
+
*/
|
|
5
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
6
|
+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
|
|
7
|
+
import * as Schnorr from '../crypto/schnorr.js';
|
|
8
|
+
/**
|
|
9
|
+
* Event class representing a Nostr event (NIP-01).
|
|
10
|
+
*/
|
|
11
|
+
export class Event {
|
|
12
|
+
/** Event ID - SHA-256 hash of the serialized event data (hex) */
|
|
13
|
+
id;
|
|
14
|
+
/** Creator's x-only public key (hex) */
|
|
15
|
+
pubkey;
|
|
16
|
+
/** Unix timestamp in seconds */
|
|
17
|
+
created_at;
|
|
18
|
+
/** Event kind (type) */
|
|
19
|
+
kind;
|
|
20
|
+
/** Event tags */
|
|
21
|
+
tags;
|
|
22
|
+
/** Event content */
|
|
23
|
+
content;
|
|
24
|
+
/** Schnorr signature (hex) */
|
|
25
|
+
sig;
|
|
26
|
+
/**
|
|
27
|
+
* Create an Event instance.
|
|
28
|
+
* @param data Signed event data
|
|
29
|
+
*/
|
|
30
|
+
constructor(data) {
|
|
31
|
+
this.id = data.id;
|
|
32
|
+
this.pubkey = data.pubkey;
|
|
33
|
+
this.created_at = data.created_at;
|
|
34
|
+
this.kind = data.kind;
|
|
35
|
+
this.tags = data.tags;
|
|
36
|
+
this.content = data.content;
|
|
37
|
+
this.sig = data.sig;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create and sign a new event.
|
|
41
|
+
* @param keyManager Key manager with signing key
|
|
42
|
+
* @param data Unsigned event data
|
|
43
|
+
* @returns Signed Event instance
|
|
44
|
+
*/
|
|
45
|
+
static create(keyManager, data) {
|
|
46
|
+
const pubkey = keyManager.getPublicKeyHex();
|
|
47
|
+
const created_at = data.created_at ?? Math.floor(Date.now() / 1000);
|
|
48
|
+
// Calculate event ID
|
|
49
|
+
const id = Event.calculateId(pubkey, created_at, data.kind, data.tags, data.content);
|
|
50
|
+
// Sign the event ID
|
|
51
|
+
const idBytes = hexToBytes(id);
|
|
52
|
+
const sig = keyManager.signHex(idBytes);
|
|
53
|
+
return new Event({
|
|
54
|
+
id,
|
|
55
|
+
pubkey,
|
|
56
|
+
created_at,
|
|
57
|
+
kind: data.kind,
|
|
58
|
+
tags: data.tags,
|
|
59
|
+
content: data.content,
|
|
60
|
+
sig,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Calculate the event ID from event data.
|
|
65
|
+
* ID = SHA-256(serialized event data)
|
|
66
|
+
* Serialized format: [0, pubkey, created_at, kind, tags, content]
|
|
67
|
+
*/
|
|
68
|
+
static calculateId(pubkey, created_at, kind, tags, content) {
|
|
69
|
+
const serialized = JSON.stringify([0, pubkey, created_at, kind, tags, content]);
|
|
70
|
+
const hash = sha256(new TextEncoder().encode(serialized));
|
|
71
|
+
return bytesToHex(hash);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Verify the event signature.
|
|
75
|
+
* @returns true if the signature is valid
|
|
76
|
+
*/
|
|
77
|
+
verify() {
|
|
78
|
+
try {
|
|
79
|
+
// Verify the ID
|
|
80
|
+
const calculatedId = Event.calculateId(this.pubkey, this.created_at, this.kind, this.tags, this.content);
|
|
81
|
+
if (calculatedId !== this.id) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
// Verify the signature
|
|
85
|
+
const idBytes = hexToBytes(this.id);
|
|
86
|
+
const sigBytes = hexToBytes(this.sig);
|
|
87
|
+
const pubkeyBytes = hexToBytes(this.pubkey);
|
|
88
|
+
return Schnorr.verify(sigBytes, idBytes, pubkeyBytes);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Parse an event from JSON data.
|
|
96
|
+
* @param json JSON object or string
|
|
97
|
+
* @returns Event instance
|
|
98
|
+
*/
|
|
99
|
+
static fromJSON(json) {
|
|
100
|
+
const data = typeof json === 'string' ? JSON.parse(json) : json;
|
|
101
|
+
if (!Event.isValidEventData(data)) {
|
|
102
|
+
throw new Error('Invalid event data');
|
|
103
|
+
}
|
|
104
|
+
return new Event(data);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if data has valid event structure.
|
|
108
|
+
*/
|
|
109
|
+
static isValidEventData(data) {
|
|
110
|
+
if (typeof data !== 'object' || data === null) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
const obj = data;
|
|
114
|
+
return (typeof obj.id === 'string' &&
|
|
115
|
+
typeof obj.pubkey === 'string' &&
|
|
116
|
+
typeof obj.created_at === 'number' &&
|
|
117
|
+
typeof obj.kind === 'number' &&
|
|
118
|
+
Array.isArray(obj.tags) &&
|
|
119
|
+
typeof obj.content === 'string' &&
|
|
120
|
+
typeof obj.sig === 'string');
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Convert the event to a plain object.
|
|
124
|
+
* @returns Plain object representation
|
|
125
|
+
*/
|
|
126
|
+
toJSON() {
|
|
127
|
+
return {
|
|
128
|
+
id: this.id,
|
|
129
|
+
pubkey: this.pubkey,
|
|
130
|
+
created_at: this.created_at,
|
|
131
|
+
kind: this.kind,
|
|
132
|
+
tags: this.tags,
|
|
133
|
+
content: this.content,
|
|
134
|
+
sig: this.sig,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get the first value of a tag by name.
|
|
139
|
+
* @param tagName Tag name to find
|
|
140
|
+
* @returns First value of the tag, or undefined if not found
|
|
141
|
+
*/
|
|
142
|
+
getTagValue(tagName) {
|
|
143
|
+
const tag = this.tags.find((t) => t[0] === tagName);
|
|
144
|
+
return tag?.[1];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get all values of a tag by name.
|
|
148
|
+
* @param tagName Tag name to find
|
|
149
|
+
* @returns Array of tag values
|
|
150
|
+
*/
|
|
151
|
+
getTagValues(tagName) {
|
|
152
|
+
return this.tags.filter((t) => t[0] === tagName).map((t) => t[1] ?? '');
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Check if a tag exists.
|
|
156
|
+
* @param tagName Tag name to check
|
|
157
|
+
* @returns true if the tag exists
|
|
158
|
+
*/
|
|
159
|
+
hasTag(tagName) {
|
|
160
|
+
return this.tags.some((t) => t[0] === tagName);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get all values from a single tag entry (excluding the tag name).
|
|
164
|
+
* @param tagName Tag name to find
|
|
165
|
+
* @returns Array of values from the first matching tag, or empty array
|
|
166
|
+
*/
|
|
167
|
+
getTagEntryValues(tagName) {
|
|
168
|
+
const tag = this.tags.find((t) => t[0] === tagName);
|
|
169
|
+
return tag ? tag.slice(1) : [];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=Event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Event.js","sourceRoot":"","sources":["../../../src/protocol/Event.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EventKinds - Standardized Nostr event kind definitions.
|
|
3
|
+
* Includes both standard NIP kinds and Unicity custom kinds.
|
|
4
|
+
*/
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Standard NIP Event Kinds
|
|
7
|
+
// ============================================================================
|
|
8
|
+
/** NIP-01: User profile metadata */
|
|
9
|
+
export const PROFILE = 0;
|
|
10
|
+
/** NIP-01: Short text note */
|
|
11
|
+
export const TEXT_NOTE = 1;
|
|
12
|
+
/** NIP-01: Recommend relay to followers */
|
|
13
|
+
export const RECOMMEND_RELAY = 2;
|
|
14
|
+
/** NIP-02: Contact list (follows) */
|
|
15
|
+
export const CONTACTS = 3;
|
|
16
|
+
/** NIP-04: Encrypted direct messages */
|
|
17
|
+
export const ENCRYPTED_DM = 4;
|
|
18
|
+
/** NIP-09: Event deletion */
|
|
19
|
+
export const DELETION = 5;
|
|
20
|
+
/** NIP-25: Reactions (likes, etc.) */
|
|
21
|
+
export const REACTION = 7;
|
|
22
|
+
/** NIP-59: Gift wrap for private events */
|
|
23
|
+
export const GIFT_WRAP = 1059;
|
|
24
|
+
/** NIP-65: Relay list metadata */
|
|
25
|
+
export const RELAY_LIST = 10002;
|
|
26
|
+
/** NIP-78: Application-specific data (parameterized replaceable) */
|
|
27
|
+
export const APP_DATA = 30078;
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Unicity Custom Event Kinds
|
|
30
|
+
// ============================================================================
|
|
31
|
+
/** Unicity: Agent profile information */
|
|
32
|
+
export const AGENT_PROFILE = 31111;
|
|
33
|
+
/** Unicity: Agent GPS location */
|
|
34
|
+
export const AGENT_LOCATION = 31112;
|
|
35
|
+
/** Unicity: Token transfer event */
|
|
36
|
+
export const TOKEN_TRANSFER = 31113;
|
|
37
|
+
/** Unicity: File metadata */
|
|
38
|
+
export const FILE_METADATA = 31114;
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// Event Kind Classification Functions
|
|
41
|
+
// ============================================================================
|
|
42
|
+
/**
|
|
43
|
+
* Check if an event kind is replaceable.
|
|
44
|
+
* Replaceable events (kinds 0, 3, or 10000-19999) are replaced when a new
|
|
45
|
+
* event with the same kind and pubkey is published.
|
|
46
|
+
* @param kind Event kind number
|
|
47
|
+
* @returns true if the event kind is replaceable
|
|
48
|
+
*/
|
|
49
|
+
export function isReplaceable(kind) {
|
|
50
|
+
return kind === 0 || kind === 3 || (kind >= 10000 && kind < 20000);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if an event kind is ephemeral.
|
|
54
|
+
* Ephemeral events (kinds 20000-29999) are not stored by relays.
|
|
55
|
+
* @param kind Event kind number
|
|
56
|
+
* @returns true if the event kind is ephemeral
|
|
57
|
+
*/
|
|
58
|
+
export function isEphemeral(kind) {
|
|
59
|
+
return kind >= 20000 && kind < 30000;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if an event kind is parameterized replaceable.
|
|
63
|
+
* Parameterized replaceable events (kinds 30000-39999) are replaced when a
|
|
64
|
+
* new event with the same kind, pubkey, and "d" tag value is published.
|
|
65
|
+
* @param kind Event kind number
|
|
66
|
+
* @returns true if the event kind is parameterized replaceable
|
|
67
|
+
*/
|
|
68
|
+
export function isParameterizedReplaceable(kind) {
|
|
69
|
+
return kind >= 30000 && kind < 40000;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get a human-readable name for an event kind.
|
|
73
|
+
* @param kind Event kind number
|
|
74
|
+
* @returns Human-readable name for the event kind
|
|
75
|
+
*/
|
|
76
|
+
export function getName(kind) {
|
|
77
|
+
switch (kind) {
|
|
78
|
+
case PROFILE:
|
|
79
|
+
return 'Profile';
|
|
80
|
+
case TEXT_NOTE:
|
|
81
|
+
return 'Text Note';
|
|
82
|
+
case RECOMMEND_RELAY:
|
|
83
|
+
return 'Recommend Relay';
|
|
84
|
+
case CONTACTS:
|
|
85
|
+
return 'Contacts';
|
|
86
|
+
case ENCRYPTED_DM:
|
|
87
|
+
return 'Encrypted DM';
|
|
88
|
+
case DELETION:
|
|
89
|
+
return 'Deletion';
|
|
90
|
+
case REACTION:
|
|
91
|
+
return 'Reaction';
|
|
92
|
+
case GIFT_WRAP:
|
|
93
|
+
return 'Gift Wrap';
|
|
94
|
+
case RELAY_LIST:
|
|
95
|
+
return 'Relay List';
|
|
96
|
+
case APP_DATA:
|
|
97
|
+
return 'App Data';
|
|
98
|
+
case AGENT_PROFILE:
|
|
99
|
+
return 'Agent Profile';
|
|
100
|
+
case AGENT_LOCATION:
|
|
101
|
+
return 'Agent Location';
|
|
102
|
+
case TOKEN_TRANSFER:
|
|
103
|
+
return 'Token Transfer';
|
|
104
|
+
case FILE_METADATA:
|
|
105
|
+
return 'File Metadata';
|
|
106
|
+
default:
|
|
107
|
+
if (isReplaceable(kind)) {
|
|
108
|
+
return `Replaceable (${kind})`;
|
|
109
|
+
}
|
|
110
|
+
if (isEphemeral(kind)) {
|
|
111
|
+
return `Ephemeral (${kind})`;
|
|
112
|
+
}
|
|
113
|
+
if (isParameterizedReplaceable(kind)) {
|
|
114
|
+
return `Parameterized Replaceable (${kind})`;
|
|
115
|
+
}
|
|
116
|
+
return `Unknown (${kind})`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=EventKinds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventKinds.js","sourceRoot":"","sources":["../../../src/protocol/EventKinds.ts"],"names":[],"mappings":"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"}
|