dignity.js 0.3.0 → 0.4.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/docs/index.html CHANGED
@@ -3,27 +3,107 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>dignity.js v0.1.0 docs</title>
6
+ <meta name="description" content="dignity.js v0.4.0 — REST-like P2P object API for decentralized JavaScript applications." />
7
+ <title>dignity.js · Documentation</title>
8
+ <link rel="stylesheet" href="./assets/highlight/github.min.css" media="(prefers-color-scheme: light)" />
9
+ <link rel="stylesheet" href="./assets/highlight/github-dark.min.css" media="(prefers-color-scheme: dark)" />
7
10
  <link rel="stylesheet" href="./assets/styles.css" />
8
11
  </head>
9
12
  <body>
10
- <main>
11
- <h1>dignity.js documentation</h1>
12
- <p class="subtitle">Version 0.1.0 - P2P object API for decentralized JavaScript applications</p>
13
-
14
- <section>
15
- <h2>Professional docs layout (recommended)</h2>
16
- <ul>
17
- <li><strong>Monorepo style:</strong> <code>apps/docs</code> (Next.js/Nextra or Docusaurus) + versioned routes.</li>
18
- <li><strong>Library style:</strong> top-level <code>docs/</code> for public docs and examples.</li>
19
- <li><strong>API reference:</strong> generated from source comments and published online.</li>
20
- <li><strong>Examples:</strong> runnable samples under <code>examples/</code> or linked demos.</li>
21
- </ul>
22
- </section>
23
-
24
- <section>
25
- <h2>Quick start</h2>
26
- <pre><code>const {
13
+ <header class="site-header">
14
+ <a class="site-header__brand" href="#overview">
15
+ <img src="./assets/dignity-logo.svg" alt="" width="344" height="80" />
16
+ <!-- <span>dignity.js</span> -->
17
+ <span class="site-header__version">v0.4.0</span>
18
+ </a>
19
+ <div class="site-header__links">
20
+ <a href="https://www.npmjs.com/package/dignity.js" target="_blank" rel="noopener noreferrer">npm</a>
21
+ <a href="https://github.com/jose-compu/dignity.js" target="_blank" rel="noopener noreferrer">GitHub</a>
22
+ <a href="./openapi-like.json">API JSON</a>
23
+ </div>
24
+ <button class="menu-toggle" type="button" aria-label="Toggle navigation">Menu</button>
25
+ </header>
26
+
27
+ <div class="layout">
28
+ <aside class="sidebar">
29
+ <div class="sidebar__group">
30
+ <div class="sidebar__label">Getting started</div>
31
+ <nav>
32
+ <a href="#overview">Overview</a>
33
+ <a href="#installation">Installation</a>
34
+ <a href="#quick-start">Quick start</a>
35
+ <a href="#browser">Browser usage</a>
36
+ </nav>
37
+ </div>
38
+ <div class="sidebar__group">
39
+ <div class="sidebar__label">Guide</div>
40
+ <nav>
41
+ <a href="#object-api">Object API</a>
42
+ <a href="#discovery">Room discovery</a>
43
+ <a href="#messaging">Direct messaging</a>
44
+ <a href="#concurrency">Concurrency</a>
45
+ <a href="#security">Security model</a>
46
+ <a href="#persistence">IndexedDB persistence</a>
47
+ <a href="#react">React hooks</a>
48
+ <a href="#signaling">Signaling</a>
49
+ </nav>
50
+ </div>
51
+ <div class="sidebar__group">
52
+ <div class="sidebar__label">Reference</div>
53
+ <nav>
54
+ <a href="#api-reference">API reference</a>
55
+ <a href="#events">Events</a>
56
+ <a href="#exports">Package exports</a>
57
+ <a href="#examples">Examples</a>
58
+ <a href="#development">Development</a>
59
+ </nav>
60
+ </div>
61
+ </aside>
62
+
63
+ <main class="content">
64
+ <div class="content__inner">
65
+ <section id="overview" class="section">
66
+ <h1 class="page-title">Documentation</h1>
67
+ <p class="lead">
68
+ <strong>dignity.js</strong> is a REST-like P2P object API for decentralized JavaScript
69
+ applications. Synchronize shared objects across browsers with owner authorization,
70
+ scoped broadcast encryption, and built-in anti-abuse controls.
71
+ </p>
72
+
73
+ <div class="card-grid">
74
+ <article class="card">
75
+ <h3>Object CRUD</h3>
76
+ <p><code>create</code>, <code>read</code>, <code>list</code>, <code>update</code>, <code>remove</code> over P2P replication.</p>
77
+ </article>
78
+ <article class="card">
79
+ <h3>Security default-on</h3>
80
+ <p>Signing, encryption, Sloth VDF proof-of-work, and automatic peer bans.</p>
81
+ </article>
82
+ <article class="card">
83
+ <h3>Browser-first</h3>
84
+ <p>IIFE, ESM, and CJS bundles. Optional IndexedDB persistence and React hooks.</p>
85
+ </article>
86
+ </div>
87
+ </section>
88
+
89
+ <section id="installation" class="section">
90
+ <h2>Installation</h2>
91
+ <div class="code-block">
92
+ <span class="code-block__label">npm</span>
93
+ <pre><code class="language-bash">npm install dignity.js</code></pre>
94
+ </div>
95
+ <p>For React hooks, install React 18+ as a peer dependency:</p>
96
+ <div class="code-block">
97
+ <pre><code class="language-bash">npm install dignity.js react react-dom</code></pre>
98
+ </div>
99
+ </section>
100
+
101
+ <section id="quick-start" class="section">
102
+ <h2>Quick start</h2>
103
+ <p>Create a node, join a room, and replicate an object between two peers:</p>
104
+ <div class="code-block">
105
+ <span class="code-block__label">Node.js / ESM</span>
106
+ <pre><code class="language-javascript">const {
27
107
  DignityP2P,
28
108
  InMemoryNetworkHub,
29
109
  InMemoryNetworkAdapter
@@ -33,52 +113,342 @@ const hub = new InMemoryNetworkHub();
33
113
 
34
114
  const alice = new DignityP2P({
35
115
  nodeId: 'alice',
36
- networkAdapter: new InMemoryNetworkAdapter(hub)
116
+ networkAdapter: new InMemoryNetworkAdapter(hub),
117
+ security: {
118
+ appPassword: 'shared-out-of-band-password',
119
+ powSteps: 22
120
+ }
121
+ });
122
+
123
+ const bob = new DignityP2P({
124
+ nodeId: 'bob',
125
+ networkAdapter: new InMemoryNetworkAdapter(hub),
126
+ security: {
127
+ appPassword: 'shared-out-of-band-password',
128
+ powSteps: 22
129
+ }
37
130
  });
38
131
 
39
132
  await alice.start();
40
- await alice.create('notes', { title: 'hello world' }, { id: 'note-1' });
41
- const note = alice.read('notes', 'note-1');</code></pre>
42
- </section>
43
-
44
- <section>
45
- <h2>REST-like object API</h2>
46
- <ul>
47
- <li><code>create(collection, data, options)</code> -> owner is the creator.</li>
48
- <li><code>read(collection, id)</code> -> returns object or <code>null</code>.</li>
49
- <li><code>list(collection, options)</code> -> list active objects (or include deleted markers).</li>
50
- <li><code>update(collection, id, patch)</code> -> only object owner can update.</li>
51
- <li><code>remove(collection, id)</code> -> only object owner can delete.</li>
52
- </ul>
53
- </section>
54
-
55
- <section>
56
- <h2>Room / team discovery</h2>
57
- <pre><code>await node.joinDiscovery('team:red', {\n metadata: { nickname: 'alice' },\n heartbeatIntervalMs: 15000,\n ttlMs: 45000\n});\n\nconst peers = node.listPeers('team:red', { includeSelf: false });\nawait node.leaveDiscovery('team:red');</code></pre>
58
- </section>
59
-
60
- <section>
61
- <h2>Security defaults (enabled)</h2>
62
- <ul>
63
- <li><strong>Signing:</strong> every message is signed by sender keypair.</li>
64
- <li><strong>Broadcast encryption:</strong> encrypted using shared <code>appPassword</code>.</li>
65
- <li><strong>Direct encryption:</strong> encrypted with recipient public key.</li>
66
- <li><strong>PoW:</strong> Sloth VDF proof per message, default <code>powSteps: 22</code> (~1000ms on this machine).</li>
67
- <li><strong>Peer bans:</strong> invalid signature or invalid PoW bans sender for 48h by default.</li>
68
- </ul>
69
- <pre><code>const node = new DignityP2P({\n nodeId: 'alice',\n networkAdapter,\n security: {\n appPassword: 'shared-out-of-band-password',\n powTargetMs: 1000,\n signingEnabled: true,\n encryptionEnabled: true,\n powEnabled: true\n }\n});</code></pre>
70
- <pre><code>const coopNode = new DignityP2P({\n nodeId: 'alice',\n networkAdapter,\n security: {\n appPassword: 'global-fallback-password',\n broadcastPasswords: {\n 'coop:red': 'red-team-password',\n 'coop:blue': 'blue-team-password'\n }\n }\n});\n\nawait coopNode.create('matches', { mode: 'coop' }, {\n id: 'match-1',\n broadcastScope: 'coop:red'\n});</code></pre>
71
- </section>
72
-
73
- <section>
74
- <h2>Signaling defaults and customization</h2>
75
- <pre><code>const {
133
+ await bob.start();
134
+
135
+ await alice.joinDiscovery('main', { metadata: { nickname: 'alice' } });
136
+ await bob.joinDiscovery('main', { metadata: { nickname: 'bob' } });
137
+
138
+ await alice.create('notes', { title: 'hello decentralized world' }, {
139
+ id: 'note-1',
140
+ broadcastScope: 'main'
141
+ });
142
+
143
+ console.log(bob.read('notes', 'note-1'));
144
+
145
+ await alice.leaveDiscovery('main');
146
+ await bob.leaveDiscovery('main');
147
+ await alice.stop();
148
+ await bob.stop();</code></pre>
149
+ </div>
150
+ <div class="callout">
151
+ <strong>In-memory adapter</strong>
152
+ <code>InMemoryNetworkAdapter</code> is for tests and local demos. Production browser apps use a WebRTC network adapter (see project issues for the browser transport roadmap).
153
+ </div>
154
+ </section>
155
+
156
+ <section id="browser" class="section">
157
+ <h2>Browser usage</h2>
158
+ <p>Pre-built bundles are published to npm and available via CDN:</p>
159
+ <div class="code-block">
160
+ <span class="code-block__label">IIFE (global <code>DignityJS</code>)</span>
161
+ <pre><code class="language-xml">&lt;script src="https://unpkg.com/dignity.js/dist/dignity.min.js"&gt;&lt;/script&gt;
162
+ &lt;script&gt;
163
+ const { DignityP2P } = DignityJS;
164
+ &lt;/script&gt;</code></pre>
165
+ </div>
166
+ <div class="table-wrap">
167
+ <table>
168
+ <thead>
169
+ <tr>
170
+ <th>Bundle</th>
171
+ <th>Path</th>
172
+ <th>Format</th>
173
+ </tr>
174
+ </thead>
175
+ <tbody>
176
+ <tr>
177
+ <td>Minified browser</td>
178
+ <td><code>dist/dignity.min.js</code></td>
179
+ <td>IIFE</td>
180
+ </tr>
181
+ <tr>
182
+ <td>ES modules</td>
183
+ <td><code>dist/dignity.esm.js</code></td>
184
+ <td>ESM</td>
185
+ </tr>
186
+ <tr>
187
+ <td>Node / CommonJS</td>
188
+ <td><code>dist/dignity.cjs.js</code></td>
189
+ <td>CJS</td>
190
+ </tr>
191
+ </tbody>
192
+ </table>
193
+ </div>
194
+ </section>
195
+
196
+ <section id="object-api" class="section">
197
+ <h2>Object API</h2>
198
+ <p>
199
+ The core API mirrors REST semantics. Each object belongs to a <strong>collection</strong>.
200
+ The peer that creates an object becomes its <strong>owner</strong> — only the owner may update or delete it.
201
+ </p>
202
+
203
+ <div class="table-wrap">
204
+ <table>
205
+ <thead>
206
+ <tr>
207
+ <th>Method</th>
208
+ <th>Description</th>
209
+ <th>Authorization</th>
210
+ </tr>
211
+ </thead>
212
+ <tbody>
213
+ <tr>
214
+ <td><code>create(collection, data, options?)</code></td>
215
+ <td>Create a new object. Returns the normalized record.</td>
216
+ <td>Creator becomes owner</td>
217
+ </tr>
218
+ <tr>
219
+ <td><code>read(collection, id)</code></td>
220
+ <td>Read one object, or <code>null</code> if missing/deleted.</td>
221
+ <td>—</td>
222
+ </tr>
223
+ <tr>
224
+ <td><code>list(collection, options?)</code></td>
225
+ <td>List objects. Set <code>includeDeleted: true</code> for tombstones.</td>
226
+ <td>—</td>
227
+ </tr>
228
+ <tr>
229
+ <td><code>update(collection, id, patch, options?)</code></td>
230
+ <td>Merge <code>patch</code> into object data and increment version.</td>
231
+ <td>Owner only</td>
232
+ </tr>
233
+ <tr>
234
+ <td><code>updateWithRetry(collection, id, patchFn, options?)</code></td>
235
+ <td>Read-modify-write with automatic retry on version conflicts.</td>
236
+ <td>Owner only</td>
237
+ </tr>
238
+ <tr>
239
+ <td><code>remove(collection, id, options?)</code></td>
240
+ <td>Soft-delete the object (tombstone).</td>
241
+ <td>Owner only</td>
242
+ </tr>
243
+ </tbody>
244
+ </table>
245
+ </div>
246
+
247
+ <h3>Scoped broadcast</h3>
248
+ <p>Pass <code>broadcastScope</code> on create/update to select a team or room password namespace:</p>
249
+ <div class="code-block">
250
+ <pre><code class="language-javascript">await node.create('matches', { mode: 'coop' }, {
251
+ id: 'm-1',
252
+ broadcastScope: 'coop:red'
253
+ });</code></pre>
254
+ </div>
255
+ <p>Configure per-scope passwords via <code>security.broadcastPasswords</code>.</p>
256
+ </section>
257
+
258
+ <section id="discovery" class="section">
259
+ <h2>Room / team discovery</h2>
260
+ <p>Discover active peers in a named scope (room, team, raid, etc.):</p>
261
+ <div class="code-block">
262
+ <pre><code class="language-javascript">await node.joinDiscovery('team:red', {
263
+ metadata: { nickname: 'alice' },
264
+ heartbeatIntervalMs: 15000,
265
+ ttlMs: 45000
266
+ });
267
+
268
+ const peers = node.listPeers('team:red', { includeSelf: false });
269
+
270
+ await node.leaveDiscovery('team:red');</code></pre>
271
+ </div>
272
+ </section>
273
+
274
+ <section id="messaging" class="section">
275
+ <h2>Direct secure messaging</h2>
276
+ <p>Send end-to-end encrypted messages to a specific peer:</p>
277
+ <div class="code-block">
278
+ <pre><code class="language-javascript">alice.registerPeerPublicKey('bob', bob.getPublicKey());
279
+ bob.registerPeerPublicKey('alice', alice.getPublicKey());
280
+
281
+ await alice.sendDirectMessage('bob', 'dm', { text: 'private payload' });</code></pre>
282
+ </div>
283
+ </section>
284
+
285
+ <section id="concurrency" class="section">
286
+ <h2>Optimistic concurrency</h2>
287
+ <p>
288
+ Every update carries a monotonic <code>version</code>. Stale operations are rejected when
289
+ <code>baseVersion</code> does not match. Listen for <code>conflict</code> events or use
290
+ built-in helpers:
291
+ </p>
292
+ <div class="code-block">
293
+ <pre><code class="language-javascript">node.on('conflict', (event) => {
294
+ // event.phase: 'local' | 'remote'
295
+ console.log(event.expectedVersion, event.currentVersion);
296
+ });
297
+
298
+ // Fail fast on stale local writes
299
+ await node.update('games', 'g1', { score: 10 }, { expectedVersion: 3 });
300
+
301
+ // Automatic retry for read-modify-write loops
302
+ await node.updateWithRetry('games', 'g1', (current) => ({
303
+ score: current.data.score + 1
304
+ }));</code></pre>
305
+ </div>
306
+ </section>
307
+
308
+ <section id="security" class="section">
309
+ <h2>Security model</h2>
310
+ <p>All security features are enabled by default. Configure via the <code>security</code> constructor option.</p>
311
+
312
+ <div class="table-wrap">
313
+ <table>
314
+ <thead>
315
+ <tr>
316
+ <th>Feature</th>
317
+ <th>Default</th>
318
+ <th>Details</th>
319
+ </tr>
320
+ </thead>
321
+ <tbody>
322
+ <tr>
323
+ <td>Signing</td>
324
+ <td>On</td>
325
+ <td>Ed25519 signature on every message</td>
326
+ </tr>
327
+ <tr>
328
+ <td>Broadcast encryption</td>
329
+ <td>On</td>
330
+ <td>AES secretbox; PBKDF2-SHA256 key from <code>appPassword</code> (100k iterations)</td>
331
+ </tr>
332
+ <tr>
333
+ <td>Direct encryption</td>
334
+ <td>On</td>
335
+ <td>NaCl box (X25519) — true E2E to recipient public key</td>
336
+ </tr>
337
+ <tr>
338
+ <td>Proof-of-work</td>
339
+ <td>On</td>
340
+ <td>Sloth VDF; default <code>powSteps: 22</code> (~1s on reference hardware)</td>
341
+ </tr>
342
+ <tr>
343
+ <td>Peer bans</td>
344
+ <td>On</td>
345
+ <td>Invalid signature or PoW → 48h ban (configurable)</td>
346
+ </tr>
347
+ </tbody>
348
+ </table>
349
+ </div>
350
+
351
+ <div class="callout callout--warn">
352
+ <strong>Broadcast mode is not E2E</strong>
353
+ All peers that know the scope password can decrypt broadcast traffic. Use direct mode for sensitive per-peer data.
354
+ </div>
355
+
356
+ <h3>Security configuration</h3>
357
+ <div class="code-block">
358
+ <pre><code class="language-javascript">const node = new DignityP2P({
359
+ nodeId: 'alice',
360
+ networkAdapter,
361
+ security: {
362
+ appPassword: 'shared-out-of-band-password',
363
+ broadcastPasswords: {
364
+ 'coop:red': 'red-team-secret',
365
+ 'coop:blue': 'blue-team-secret'
366
+ },
367
+ powSteps: 22,
368
+ kdfIterations: 100000,
369
+ banDurationMs: 48 * 60 * 60 * 1000
370
+ }
371
+ });</code></pre>
372
+ </div>
373
+ </section>
374
+
375
+ <section id="persistence" class="section">
376
+ <h2>IndexedDB persistence</h2>
377
+ <p>Survive page reloads by persisting replicated object state to IndexedDB:</p>
378
+ <div class="code-block">
379
+ <pre><code class="language-javascript">const { DignityP2P, IndexedDBPersistence } = require('dignity.js');
380
+
381
+ const node = new DignityP2P({ nodeId, networkAdapter, security });
382
+ const persistence = new IndexedDBPersistence({
383
+ dbName: 'my-app',
384
+ collections: ['games', 'matches'] // omit to persist all collections
385
+ });
386
+
387
+ await node.start();
388
+ await persistence.attach(node);
389
+
390
+ // Later
391
+ await persistence.detach();</code></pre>
392
+ </div>
393
+ </section>
394
+
395
+ <section id="react" class="section">
396
+ <h2>React hooks</h2>
397
+ <p>Optional integration via <code>dignity.js/react</code> (requires React ≥ 18):</p>
398
+ <div class="code-block">
399
+ <pre><code class="language-javascript">import { useDignity, useCollection, usePeers } from 'dignity.js/react';
400
+
401
+ function Room() {
402
+ const { node, status, error } = useDignity(config);
403
+ const games = useCollection(node, 'games');
404
+ const peers = usePeers(node, 'room:chess', { includeSelf: false });
405
+
406
+ if (status === 'starting') return &lt;p&gt;Connecting…&lt;/p&gt;;
407
+ if (error) return &lt;p&gt;Error: {error.message}&lt;/p&gt;;
408
+
409
+ return (
410
+ &lt;pre&gt;{JSON.stringify({ status, games, peers }, null, 2)}&lt;/pre&gt;
411
+ );
412
+ }</code></pre>
413
+ </div>
414
+
415
+ <div class="table-wrap">
416
+ <table>
417
+ <thead>
418
+ <tr>
419
+ <th>Hook</th>
420
+ <th>Returns</th>
421
+ </tr>
422
+ </thead>
423
+ <tbody>
424
+ <tr>
425
+ <td><code>useDignity(config)</code></td>
426
+ <td><code>{ node, status, error }</code> — starts/stops node on mount/unmount</td>
427
+ </tr>
428
+ <tr>
429
+ <td><code>useCollection(node, name)</code></td>
430
+ <td>Reactive array from <code>node.list(name)</code></td>
431
+ </tr>
432
+ <tr>
433
+ <td><code>usePeers(node, scope, options?)</code></td>
434
+ <td>Reactive peer list from discovery</td>
435
+ </tr>
436
+ </tbody>
437
+ </table>
438
+ </div>
439
+ </section>
440
+
441
+ <section id="signaling" class="section">
442
+ <h2>Signaling</h2>
443
+ <p>Default PeerJS-compatible signaling endpoints are included. Customize with <code>createDefaultSignalingPool</code>:</p>
444
+ <div class="code-block">
445
+ <pre><code class="language-javascript">const {
76
446
  createDefaultSignalingPool,
77
447
  WebSocketSignalingProvider
78
448
  } = require('dignity.js');
79
449
 
80
450
  const pool = createDefaultSignalingPool({
81
- cloudflareUrls: ['wss://your-cloudflare-endpoint.example'],
451
+ cloudflareUrls: ['wss://your-endpoint.example/peerjs?key=peerjs'],
82
452
  fallbackUrls: ['wss://relay-a.example', 'wss://relay-b.example'],
83
453
  customProviders: [
84
454
  new WebSocketSignalingProvider({
@@ -88,30 +458,180 @@ const pool = createDefaultSignalingPool({
88
458
  })
89
459
  ]
90
460
  });</code></pre>
91
- <p>
92
- Machine-readable API metadata is available in
93
- <a href="./openapi-like.json">openapi-like.json</a>.
94
- </p>
95
- </section>
96
-
97
- <section>
98
- <h2>Minimal examples</h2>
99
- <ul>
100
- <li><code>examples/decentralized-tictactoe.js</code> - replicated board state and owner authorization.</li>
101
- <li><code>examples/decentralized-chess-lite.js</code> - replicated move history with a compact board model.</li>
102
- </ul>
103
- <pre><code>npm run example:tictactoe\nnpm run example:chess</code></pre>
104
- </section>
105
-
106
- <section>
107
- <h2>Publish-ready browser bundles</h2>
108
- <ul>
109
- <li><code>dist/dignity.min.js</code> - minified browser IIFE bundle (global <code>DignityJS</code>).</li>
110
- <li><code>dist/dignity.esm.js</code> - browser ESM bundle.</li>
111
- <li><code>dist/dignity.cjs.js</code> - Node/CommonJS bundle.</li>
112
- </ul>
113
- <pre><code>npm test\nnpm run build\nnpm publish --access public</code></pre>
114
- </section>
115
- </main>
461
+ </div>
462
+ <p>Default public endpoints:</p>
463
+ <ul>
464
+ <li><code>wss://peerjs.92k.de/peerjs?key=peerjs</code></li>
465
+ <li><code>wss://0.peerjs.com/peerjs?key=peerjs</code></li>
466
+ </ul>
467
+ </section>
468
+
469
+ <section id="api-reference" class="section">
470
+ <h2>API reference</h2>
471
+ <p>Primary class: <code>DignityP2P</code></p>
472
+
473
+ <h3>Constructor</h3>
474
+ <div class="code-block">
475
+ <pre><code class="language-javascript">new DignityP2P({
476
+ nodeId, // required — unique peer identifier
477
+ networkAdapter, // required — transport implementation
478
+ security, // optional — MessageSecurityService options
479
+ now, // optional clock injection (tests)
480
+ idGenerator // optional custom operation id factory
481
+ })</code></pre>
482
+ </div>
483
+
484
+ <h3>Lifecycle</h3>
485
+ <div class="table-wrap">
486
+ <table>
487
+ <thead>
488
+ <tr>
489
+ <th>Method</th>
490
+ <th>Description</th>
491
+ </tr>
492
+ </thead>
493
+ <tbody>
494
+ <tr><td><code>start()</code></td><td>Connect adapter and begin receiving messages</td></tr>
495
+ <tr><td><code>stop()</code></td><td>Leave discovery scopes and disconnect</td></tr>
496
+ <tr><td><code>getPublicKey()</code></td><td>Return this node's public key bundle</td></tr>
497
+ <tr><td><code>registerPeerPublicKey(id, key)</code></td><td>Trust a remote peer's keys</td></tr>
498
+ </tbody>
499
+ </table>
500
+ </div>
501
+
502
+ <p>
503
+ Machine-readable metadata:
504
+ <a href="./openapi-like.json">openapi-like.json</a>
505
+ </p>
506
+ </section>
507
+
508
+ <section id="events" class="section">
509
+ <h2>Events</h2>
510
+ <p><code>DignityP2P</code> extends <code>EventEmitter</code>. Common events:</p>
511
+ <div class="table-wrap">
512
+ <table>
513
+ <thead>
514
+ <tr>
515
+ <th>Event</th>
516
+ <th>Payload</th>
517
+ </tr>
518
+ </thead>
519
+ <tbody>
520
+ <tr>
521
+ <td><span class="tag tag--event">change</span></td>
522
+ <td><code>{ kind, collection, id }</code> — object created, updated, or deleted</td>
523
+ </tr>
524
+ <tr>
525
+ <td><span class="tag tag--event">conflict</span></td>
526
+ <td><code>{ kind, collection, id, expectedVersion, currentVersion, phase }</code></td>
527
+ </tr>
528
+ <tr>
529
+ <td><span class="tag tag--event">peerdiscovered</span></td>
530
+ <td><code>{ scope, peerId, metadata }</code></td>
531
+ </tr>
532
+ <tr>
533
+ <td><span class="tag tag--event">peerleft</span></td>
534
+ <td><code>{ scope, peerId, reason }</code></td>
535
+ </tr>
536
+ <tr>
537
+ <td><span class="tag tag--event">message</span></td>
538
+ <td><code>{ senderId, targetId, type, payload }</code> — custom decrypted messages</td>
539
+ </tr>
540
+ <tr>
541
+ <td><span class="tag tag--event">securityerror</span></td>
542
+ <td><code>{ senderId, error }</code></td>
543
+ </tr>
544
+ <tr>
545
+ <td><span class="tag tag--event">warning</span></td>
546
+ <td><code>{ type, ... }</code> — non-fatal issues (heartbeat, persistence, etc.)</td>
547
+ </tr>
548
+ </tbody>
549
+ </table>
550
+ </div>
551
+ </section>
552
+
553
+ <section id="exports" class="section">
554
+ <h2>Package exports</h2>
555
+ <div class="table-wrap">
556
+ <table>
557
+ <thead>
558
+ <tr>
559
+ <th>Import path</th>
560
+ <th>Exports</th>
561
+ </tr>
562
+ </thead>
563
+ <tbody>
564
+ <tr>
565
+ <td><code>dignity.js</code></td>
566
+ <td><code>DignityP2P</code>, <code>IndexedDBPersistence</code>, signaling providers, in-memory adapters, security utilities</td>
567
+ </tr>
568
+ <tr>
569
+ <td><code>dignity.js/react</code></td>
570
+ <td><code>useDignity</code>, <code>useCollection</code>, <code>usePeers</code></td>
571
+ </tr>
572
+ </tbody>
573
+ </table>
574
+ </div>
575
+ </section>
576
+
577
+ <section id="examples" class="section">
578
+ <h2>Examples</h2>
579
+ <div class="table-wrap">
580
+ <table>
581
+ <thead>
582
+ <tr>
583
+ <th>Script</th>
584
+ <th>Description</th>
585
+ <th>Run</th>
586
+ </tr>
587
+ </thead>
588
+ <tbody>
589
+ <tr>
590
+ <td><code>examples/decentralized-tictactoe.js</code></td>
591
+ <td>Replicated board state and owner authorization</td>
592
+ <td><code>npm run example:tictactoe</code></td>
593
+ </tr>
594
+ <tr>
595
+ <td><code>examples/decentralized-chess-lite.js</code></td>
596
+ <td>Replicated move history with compact board model</td>
597
+ <td><code>npm run example:chess</code></td>
598
+ </tr>
599
+ </tbody>
600
+ </table>
601
+ </div>
602
+ </section>
603
+
604
+ <section id="development" class="section">
605
+ <h2>Development</h2>
606
+ <div class="code-block">
607
+ <pre><code class="language-bash"># Run tests (177+ passing, ~99.5% line coverage)
608
+ npm test
609
+
610
+ # Build browser + Node bundles
611
+ npm run build
612
+
613
+ # Serve docs locally
614
+ npm run docs:serve
615
+
616
+ # Run examples
617
+ npm run example:tictactoe
618
+ npm run example:chess</code></pre>
619
+ </div>
620
+ </section>
621
+
622
+ <footer class="site-footer">
623
+ <p>
624
+ dignity.js v0.4.0 ·
625
+ <a href="https://github.com/jose-compu/dignity.js/blob/main/LICENSE">Apache 2.0</a> ·
626
+ <a href="https://github.com/jose-compu/dignity.js">GitHub</a> ·
627
+ <a href="https://www.npmjs.com/package/dignity.js">npm</a>
628
+ </p>
629
+ </footer>
630
+ </div>
631
+ </main>
632
+ </div>
633
+
634
+ <script src="./assets/highlight/highlight.min.js"></script>
635
+ <script src="./assets/docs.js"></script>
116
636
  </body>
117
637
  </html>