ncc-02-js 0.2.2 → 0.2.4

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 CHANGED
@@ -25,12 +25,16 @@ npm install ncc-02-js
25
25
  ```javascript
26
26
  import { NCC02Resolver } from 'ncc-02-js';
27
27
 
28
- const resolver = new NCC02Resolver(relay, [trustedCAPubkey]);
28
+ // Initialize with relay URLs and optional trusted CA pubkeys
29
+ const resolver = new NCC02Resolver(['wss://relay.damus.io'], {
30
+ trustedCAPubkeys: ['npub1...'] // Trusted third-party certifiers
31
+ });
29
32
 
30
33
  try {
31
- const service = await resolver.resolve(ownerPubkey, 'api', {
34
+ // ownerPubkey can be hex or npub
35
+ const service = await resolver.resolve(ownerPubkey, 'media', {
32
36
  requireAttestation: true,
33
- minLevel: 'verified'
37
+ minLevel: 'verified' // 'self', 'verified', 'hardened'
34
38
  });
35
39
  console.log('Resolved endpoint:', service.endpoint);
36
40
  } catch (err) {
@@ -43,21 +47,71 @@ try {
43
47
  ```javascript
44
48
  import { NCC02Builder } from 'ncc-02-js';
45
49
 
50
+ // Initialize with private key (hex)
46
51
  const builder = new NCC02Builder(privateKey);
47
- const event = builder.createServiceRecord('api', 'https://api.example.com', 'sha256:fingerprint');
48
- // publish event to relays...
52
+
53
+ // Example 1: IP-based Service
54
+ const event = builder.createServiceRecord({
55
+ serviceId: 'media',
56
+ endpoint: 'https://203.0.113.45:8443',
57
+ fingerprint: 'sha256:fingerprint',
58
+ expiryDays: 14
59
+ });
60
+
61
+ // Example 2: Tor Onion Service
62
+ const onionEvent = builder.createServiceRecord({
63
+ serviceId: 'wallet',
64
+ endpoint: 'tcp://vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion:80',
65
+ fingerprint: 'sha256:fingerprint',
66
+ expiryDays: 7
67
+ });
68
+ // publish events to relays...
69
+ ```
70
+
71
+ ### 3. Issue an Attestation (CA)
72
+
73
+ ```javascript
74
+ const caBuilder = new NCC02Builder(caPrivateKey);
75
+ const attestation = caBuilder.createAttestation({
76
+ subjectPubkey: 'npub1...', // The service owner being certified
77
+ serviceId: 'media',
78
+ serviceEventId: serviceRecordEventId,
79
+ level: 'verified',
80
+ validDays: 30
81
+ });
49
82
  ```
50
83
 
51
- ## API
84
+ ## Trust Model & Security
85
+
86
+ ### Trust Levels
87
+ - `self`: Asserted by the service owner (default if no attestation).
88
+ - `verified`: Attested by a trusted third party.
89
+ - `hardened`: Attested by a third party with stricter verification (e.g., physical proof or long-term history).
90
+
91
+ ### Threat Model
92
+ - **Endpoint Impersonation**: Prevented by binding the endpoint URI to a public key fingerprint (`k` tag).
93
+ - **Man-in-the-Middle (MITM)**: Mitigated via cryptographic pinning of transport-level keys.
94
+ - **Stale Records**: Limited by required expiry (`exp`) and support for revocations.
95
+ - **Relay Censorship**: Mitigated by querying multiple relays (implemented via `SimplePool`).
96
+
97
+ ### Fail-Closed Design
98
+ The library follows a fail-closed principle. If a policy requirement is not met (e.g., `requireAttestation: true` but no valid attestation is found), it throws an `NCC02Error` rather than returning a partially verified record.
99
+
100
+ ## API Reference
101
+
102
+ ### `NCC02Resolver(relays, options)`
103
+ - `relays`: Array of relay URLs.
104
+ - `options.pool`: (Optional) Existing `nostr-tools` SimplePool.
105
+ - `options.trustedCAPubkeys`: (Optional) Array of pubkeys trusted to issue attestations.
52
106
 
53
- ### `NCC02Resolver`
54
- - `resolve(pubkey, serviceId, options)`: Resolves and verifies a service record.
55
- - `verifyEndpoint(resolved, actualFingerprint)`: Helper to check if a connected endpoint matches the record.
107
+ #### `resolve(pubkey, serviceId, options)`
108
+ - `options.requireAttestation`: Fails if no trusted attestation is found.
109
+ - `options.minLevel`: Minimum trust level required.
56
110
 
57
- ### `NCC02Builder`
58
- - `createServiceRecord(id, uri, fingerprint, expiryDays)`
59
- - `createAttestation(subject, srv, eventId, level, validDays)`
60
- - `createRevocation(attestationId, reason)`
111
+ ### `NCC02Builder(privateKey)`
112
+ - `createServiceRecord({ serviceId, endpoint, fingerprint, expiryDays })`
113
+ - `createAttestation({ subjectPubkey, serviceId, serviceEventId, level, validDays })`
114
+ - `createRevocation({ attestationId, reason })`
61
115
 
62
116
  ## License
63
117
 
package/dist/index.cjs CHANGED
@@ -2535,13 +2535,15 @@ var NCC02Builder = class {
2535
2535
  }
2536
2536
  /**
2537
2537
  * Creates a signed Certificate Attestation (Kind 30060).
2538
- * @param {string} subjectPubkey
2539
- * @param {string} serviceId
2540
- * @param {string} serviceEventId
2541
- * @param {string} [level='verified']
2542
- * @param {number} [validDays=30]
2538
+ * @param {Object} options
2539
+ * @param {string} options.subjectPubkey - The 'subj' tag pubkey.
2540
+ * @param {string} options.serviceId - The 'srv' tag identifier.
2541
+ * @param {string} options.serviceEventId - The 'e' tag referencing the Service Record.
2542
+ * @param {string} [options.level='verified'] - The 'lvl' tag level.
2543
+ * @param {number} [options.validDays=30] - Validity in days.
2543
2544
  */
2544
- createAttestation(subjectPubkey, serviceId, serviceEventId, level = "verified", validDays = 30) {
2545
+ createAttestation(options) {
2546
+ const { subjectPubkey, serviceId, serviceEventId, level = "verified", validDays = 30 } = options;
2545
2547
  if (!subjectPubkey) throw new Error("subjectPubkey is required");
2546
2548
  if (!serviceId) throw new Error("serviceId is required");
2547
2549
  if (!serviceEventId) throw new Error("serviceEventId (e tag) is required");
@@ -2566,10 +2568,12 @@ var NCC02Builder = class {
2566
2568
  }
2567
2569
  /**
2568
2570
  * Creates a signed Revocation (Kind 30061).
2569
- * @param {string} attestationId
2570
- * @param {string} [reason='']
2571
+ * @param {Object} options
2572
+ * @param {string} options.attestationId - The 'e' tag referencing the attestation.
2573
+ * @param {string} [options.reason=''] - Optional reason.
2571
2574
  */
2572
- createRevocation(attestationId, reason = "") {
2575
+ createRevocation(options) {
2576
+ const { attestationId, reason = "" } = options;
2573
2577
  if (!attestationId) throw new Error("attestationId (e tag) is required");
2574
2578
  const tags = [["e", attestationId]];
2575
2579
  if (reason) tags.push(["reason", reason]);
@@ -7688,8 +7692,8 @@ var NCC02Resolver = class {
7688
7692
  }
7689
7693
  /**
7690
7694
  * Internal query helper using SimplePool.subscribeMany (since list() is deprecated).
7691
- * @param {Object} filter
7692
- * @returns {Promise<any[]>}
7695
+ * @param {import('nostr-tools').Filter} filter
7696
+ * @returns {Promise<import('nostr-tools').Event[]>}
7693
7697
  */
7694
7698
  async _query(filter) {
7695
7699
  return new Promise((resolve) => {
package/dist/index.mjs CHANGED
@@ -2501,13 +2501,15 @@ var NCC02Builder = class {
2501
2501
  }
2502
2502
  /**
2503
2503
  * Creates a signed Certificate Attestation (Kind 30060).
2504
- * @param {string} subjectPubkey
2505
- * @param {string} serviceId
2506
- * @param {string} serviceEventId
2507
- * @param {string} [level='verified']
2508
- * @param {number} [validDays=30]
2504
+ * @param {Object} options
2505
+ * @param {string} options.subjectPubkey - The 'subj' tag pubkey.
2506
+ * @param {string} options.serviceId - The 'srv' tag identifier.
2507
+ * @param {string} options.serviceEventId - The 'e' tag referencing the Service Record.
2508
+ * @param {string} [options.level='verified'] - The 'lvl' tag level.
2509
+ * @param {number} [options.validDays=30] - Validity in days.
2509
2510
  */
2510
- createAttestation(subjectPubkey, serviceId, serviceEventId, level = "verified", validDays = 30) {
2511
+ createAttestation(options) {
2512
+ const { subjectPubkey, serviceId, serviceEventId, level = "verified", validDays = 30 } = options;
2511
2513
  if (!subjectPubkey) throw new Error("subjectPubkey is required");
2512
2514
  if (!serviceId) throw new Error("serviceId is required");
2513
2515
  if (!serviceEventId) throw new Error("serviceEventId (e tag) is required");
@@ -2532,10 +2534,12 @@ var NCC02Builder = class {
2532
2534
  }
2533
2535
  /**
2534
2536
  * Creates a signed Revocation (Kind 30061).
2535
- * @param {string} attestationId
2536
- * @param {string} [reason='']
2537
+ * @param {Object} options
2538
+ * @param {string} options.attestationId - The 'e' tag referencing the attestation.
2539
+ * @param {string} [options.reason=''] - Optional reason.
2537
2540
  */
2538
- createRevocation(attestationId, reason = "") {
2541
+ createRevocation(options) {
2542
+ const { attestationId, reason = "" } = options;
2539
2543
  if (!attestationId) throw new Error("attestationId (e tag) is required");
2540
2544
  const tags = [["e", attestationId]];
2541
2545
  if (reason) tags.push(["reason", reason]);
@@ -7654,8 +7658,8 @@ var NCC02Resolver = class {
7654
7658
  }
7655
7659
  /**
7656
7660
  * Internal query helper using SimplePool.subscribeMany (since list() is deprecated).
7657
- * @param {Object} filter
7658
- * @returns {Promise<any[]>}
7661
+ * @param {import('nostr-tools').Filter} filter
7662
+ * @returns {Promise<import('nostr-tools').Event[]>}
7659
7663
  */
7660
7664
  async _query(filter) {
7661
7665
  return new Promise((resolve) => {
package/dist/models.d.ts CHANGED
@@ -34,17 +34,28 @@ export class NCC02Builder {
34
34
  }): import("nostr-tools/core").VerifiedEvent;
35
35
  /**
36
36
  * Creates a signed Certificate Attestation (Kind 30060).
37
- * @param {string} subjectPubkey
38
- * @param {string} serviceId
39
- * @param {string} serviceEventId
40
- * @param {string} [level='verified']
41
- * @param {number} [validDays=30]
37
+ * @param {Object} options
38
+ * @param {string} options.subjectPubkey - The 'subj' tag pubkey.
39
+ * @param {string} options.serviceId - The 'srv' tag identifier.
40
+ * @param {string} options.serviceEventId - The 'e' tag referencing the Service Record.
41
+ * @param {string} [options.level='verified'] - The 'lvl' tag level.
42
+ * @param {number} [options.validDays=30] - Validity in days.
42
43
  */
43
- createAttestation(subjectPubkey: string, serviceId: string, serviceEventId: string, level?: string, validDays?: number): import("nostr-tools/core").VerifiedEvent;
44
+ createAttestation(options: {
45
+ subjectPubkey: string;
46
+ serviceId: string;
47
+ serviceEventId: string;
48
+ level?: string;
49
+ validDays?: number;
50
+ }): import("nostr-tools/core").VerifiedEvent;
44
51
  /**
45
52
  * Creates a signed Revocation (Kind 30061).
46
- * @param {string} attestationId
47
- * @param {string} [reason='']
53
+ * @param {Object} options
54
+ * @param {string} options.attestationId - The 'e' tag referencing the attestation.
55
+ * @param {string} [options.reason=''] - Optional reason.
48
56
  */
49
- createRevocation(attestationId: string, reason?: string): import("nostr-tools/core").VerifiedEvent;
57
+ createRevocation(options: {
58
+ attestationId: string;
59
+ reason?: string;
60
+ }): import("nostr-tools/core").VerifiedEvent;
50
61
  }
@@ -41,10 +41,10 @@ export class NCC02Resolver {
41
41
  trustedCAPubkeys: Set<string>;
42
42
  /**
43
43
  * Internal query helper using SimplePool.subscribeMany (since list() is deprecated).
44
- * @param {Object} filter
45
- * @returns {Promise<any[]>}
44
+ * @param {import('nostr-tools').Filter} filter
45
+ * @returns {Promise<import('nostr-tools').Event[]>}
46
46
  */
47
- _query(filter: any): Promise<any[]>;
47
+ _query(filter: import("nostr-tools").Filter): Promise<import("nostr-tools").Event[]>;
48
48
  /**
49
49
  * Resolves a service for a given pubkey and service identifier.
50
50
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ncc-02-js",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Nostr-native service discovery and trust implementation (NCC-02)",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -8,9 +8,9 @@
8
8
  "type": "module",
9
9
  "exports": {
10
10
  ".": {
11
+ "types": "./dist/index.d.ts",
11
12
  "import": "./dist/index.mjs",
12
- "require": "./dist/index.cjs",
13
- "types": "./dist/index.d.ts"
13
+ "require": "./dist/index.cjs"
14
14
  }
15
15
  },
16
16
  "sideEffects": false,
package/src/models.js CHANGED
@@ -55,17 +55,15 @@ export class NCC02Builder {
55
55
 
56
56
  /**
57
57
  * Creates a signed Certificate Attestation (Kind 30060).
58
- * @param {string} subjectPubkey
59
- * @param {string} serviceId
60
- * @param {string} serviceEventId
61
- * @param {string} [level='verified']
62
- * @param {number} [validDays=30]
58
+ * @param {Object} options
59
+ * @param {string} options.subjectPubkey - The 'subj' tag pubkey.
60
+ * @param {string} options.serviceId - The 'srv' tag identifier.
61
+ * @param {string} options.serviceEventId - The 'e' tag referencing the Service Record.
62
+ * @param {string} [options.level='verified'] - The 'lvl' tag level.
63
+ * @param {number} [options.validDays=30] - Validity in days.
63
64
  */
64
- createAttestation(subjectPubkey, serviceId, serviceEventId, level = 'verified', validDays = 30) {
65
- // Keeping signature backward compatible for now unless requested,
66
- // but improving internal logic slightly if needed.
67
- // Ideally this should also be options object but user only asked for createServiceRecord fix.
68
- // I'll leave it to minimize breaking changes scope creep.
65
+ createAttestation(options) {
66
+ const { subjectPubkey, serviceId, serviceEventId, level = 'verified', validDays = 30 } = options;
69
67
  if (!subjectPubkey) throw new Error('subjectPubkey is required');
70
68
  if (!serviceId) throw new Error('serviceId is required');
71
69
  if (!serviceEventId) throw new Error('serviceEventId (e tag) is required');
@@ -92,10 +90,12 @@ export class NCC02Builder {
92
90
 
93
91
  /**
94
92
  * Creates a signed Revocation (Kind 30061).
95
- * @param {string} attestationId
96
- * @param {string} [reason='']
93
+ * @param {Object} options
94
+ * @param {string} options.attestationId - The 'e' tag referencing the attestation.
95
+ * @param {string} [options.reason=''] - Optional reason.
97
96
  */
98
- createRevocation(attestationId, reason = '') {
97
+ createRevocation(options) {
98
+ const { attestationId, reason = '' } = options;
99
99
  if (!attestationId) throw new Error('attestationId (e tag) is required');
100
100
 
101
101
  const tags = [['e', attestationId]];
package/src/resolver.js CHANGED
@@ -50,13 +50,15 @@ export class NCC02Resolver {
50
50
 
51
51
  /**
52
52
  * Internal query helper using SimplePool.subscribeMany (since list() is deprecated).
53
- * @param {Object} filter
54
- * @returns {Promise<any[]>}
53
+ * @param {import('nostr-tools').Filter} filter
54
+ * @returns {Promise<import('nostr-tools').Event[]>}
55
55
  */
56
56
  async _query(filter) {
57
57
  return new Promise((resolve) => {
58
+ /** @type {import('nostr-tools').Event[]} */
58
59
  const events = [];
59
60
  // subscribeMany(relays, filters, callbacks)
61
+ // @ts-ignore - subscribeMany filters parameter type mismatch with simple Object
60
62
  const sub = this.pool.subscribeMany(this.relays, [filter], {
61
63
  onevent(e) { events.push(e); },
62
64
  oneose() { sub.close(); resolve(events); }