javascript-solid-server 0.0.110 → 0.0.111

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
@@ -8,1212 +8,97 @@ A minimal, fast, JSON-LD native Solid server.
8
8
 
9
9
  ## Features
10
10
 
11
- ### Implemented
12
-
13
- - **Live Reload** - Auto-refresh browser on file changes (`--live-reload`)
14
- - **Read-Only Mode** - Disable write operations for static hosting (`--read-only`)
15
- - **Public Mode** - Skip WAC for open read/write access (`--public`)
16
- - **Schnorr SSO** - Passwordless login via BIP-340 Schnorr signatures using NIP-07 browser extensions (Podkey, nos2x, Alby)
17
- - **Passkey Authentication** - WebAuthn/FIDO2 passwordless login with Touch ID, Face ID, or security keys
18
- - **HTTP Range Requests** - Partial content delivery for large files and media streaming
19
- - **Single-User Mode** - Simplified setup for personal pod servers
20
- - **ActivityPub Federation** - Fediverse integration with WebFinger, inbox/outbox, HTTP signatures
21
- - **Mastodon-compatible API** - Dynamic client registration, instance info, account verification
22
- - **OAuth 2.0 Authorization** - Shared auth flow for Mastodon clients, remoteStorage apps, and third-party panes
23
- - **remoteStorage Protocol** - [draft-dejong-remotestorage-22](https://remotestorage.io/spec/) file sync (requires `--activitypub` for WebFinger discovery + OAuth)
24
- - **LDP CRUD Operations** - GET, PUT, POST, DELETE, HEAD
25
- - **N3 Patch** - Solid's native patch format for RDF updates
26
- - **SPARQL Update** - Standard SPARQL UPDATE protocol for PATCH
27
- - **Conditional Requests** - If-Match/If-None-Match headers (304, 412)
28
- - **CLI & Config** - `jss` command with config file/env var support
29
- - **SSL/TLS** - HTTPS support with certificate configuration
30
- - **WebSocket Notifications** - Real-time updates via solid-0.1 protocol (SolidOS compatible)
31
- - **Container Management** - Create, list, and manage containers
32
- - **Multi-user Pods** - Path-based (`/alice/`) or subdomain-based (`alice.example.com`)
33
- - **Subdomain Mode** - XSS protection via origin isolation
34
- - **Mashlib Data Browser** - Optional SolidOS UI (CDN or local hosting)
35
- - **WebID Profiles** - HTML with JSON-LD data islands, rendered with mashlib-jss + solidos-lite
36
- - **Web Access Control (WAC)** - `.acl` file-based authorization with relative URL support
37
- - **Solid-OIDC Identity Provider** - Built-in IdP with DPoP, RS256/ES256, dynamic registration
38
- - **Solid-OIDC Resource Server** - Accept DPoP-bound access tokens from external IdPs
39
- - **NSS-style Registration** - Username/password auth compatible with Solid apps
40
- - **Nostr Authentication** - NIP-98 HTTP Auth with Schnorr signatures, did:nostr → WebID resolution
41
- - **WebID-TLS** - Client certificate authentication for backend services and CLI tools
42
- - **Simple Auth Tokens** - Built-in token authentication for development
43
- - **Content Negotiation** - Turtle <-> JSON-LD conversion, including HTML data islands
44
- - **CORS Support** - Full cross-origin resource sharing
45
- - **Git HTTP Backend** - Clone and push to containers via `git` protocol
46
- - **Nostr Relay** - Integrated NIP-01/NIP-11/NIP-16 relay on the same port (`wss://your.pod/relay`)
47
- - **Invite-Only Registration** - CLI-managed invite codes for controlled signups
48
- - **Storage Quotas** - Per-user storage limits with CLI management
49
- - **HTTP 402 Paid Access** - Monetize API endpoints with per-request sat payments (`--pay`)
50
- - **Security** - Blocks access to dotfiles (`.git/`, `.env`, etc.) except Solid-specific ones
51
-
52
- ### HTTP Methods
53
-
54
- | Method | Support |
55
- |--------|---------|
56
- | GET | Full - Resources and containers |
57
- | HEAD | Full |
58
- | PUT | Full - Create/update resources |
59
- | POST | Full - Create in containers |
60
- | DELETE | Full |
61
- | PATCH | N3 Patch + SPARQL Update |
62
- | OPTIONS | Full with CORS |
63
-
64
- ## Getting Started
65
-
66
- ### Prerequisites
67
-
68
- - Node.js 18+
69
-
70
- ### Android/Termux
71
-
72
- JSS runs on Android via Termux (uses pure JavaScript `bcryptjs` for compatibility):
73
-
74
- ```bash
75
- pkg install nodejs git
11
+ - **LDP CRUD** — GET, PUT, POST, DELETE, HEAD, PATCH (N3 + SPARQL Update)
12
+ - **JSON-LD Native** — Stores and serves JSON-LD by default, Turtle via `--conneg`
13
+ - **Web Access Control** `.acl` file-based authorization
14
+ - **Solid-OIDC** — Built-in Identity Provider with DPoP, passkeys, Schnorr SSO
15
+ - **WebSocket Notifications** — Real-time updates (solid-0.1 protocol)
16
+ - **Content Negotiation** Turtle JSON-LD conversion (optional)
17
+ - **Multi-user Pods** — Path-based (`/alice/`) or subdomain-based (`alice.example.com`)
18
+ - **Single-User Mode** Personal pod server with `--single-user`
19
+ - **Git HTTP Backend** Clone and push to pod containers
20
+ - **Nostr Relay** Integrated NIP-01 relay (`wss://your.pod/relay`)
21
+ - **Nostr Auth** — NIP-98 signatures, did:nostr WebID resolution
22
+ - **ActivityPub** Fediverse federation with Mastodon-compatible API
23
+ - **remoteStorage** [draft-dejong-remotestorage-22](https://remotestorage.io/spec/) file sync
24
+ - **MongoDB Storage** Optional `/db/` route for JSON-LD at scale
25
+ - **WebRTC Signaling** — Peer-to-peer connections via WebID-authenticated signaling
26
+ - **Tunnel Proxy** Decentralized ngrok through your pod
27
+ - **HTTP 402 Payments** Monetize endpoints with per-request sat payments
28
+ - **Mashlib / SolidOS UI** Optional data browser (CDN, local, or ES module)
29
+ - **Storage Quotas** — Per-user limits with CLI management
30
+ - **Invite-Only Mode** Controlled registration via invite codes
31
+ - **SSL/TLS, CORS, Range Requests, Conditional Requests**
32
+
33
+ ## Quick Start
34
+
35
+ ```bash
36
+ # Install
76
37
  npm install -g javascript-solid-server
77
- jss start --port 8080 --nostr --git
78
- ```
79
-
80
- Use PM2 for persistence:
81
- ```bash
82
- npm install -g pm2
83
- pm2 start jss -- start --port 8080 --nostr --git
84
- pm2 save
85
- ```
86
-
87
- ### Installation
88
-
89
- ```bash
90
- npm install
91
-
92
- # Or install globally
93
- npm install -g javascript-solid-server
94
- ```
95
-
96
- ### Quick Start
97
-
98
- ```bash
99
- # Initialize configuration (interactive)
100
- jss init
101
-
102
- # Start server
103
- jss start
104
-
105
- # Or with options
106
- jss start --port 8443 --ssl-key ./key.pem --ssl-cert ./cert.pem
107
- ```
108
-
109
- ### CLI Commands
110
-
111
- ```bash
112
- jss start [options] # Start the server
113
- jss init [options] # Initialize configuration
114
- jss invite <cmd> # Manage invite codes (create, list, revoke)
115
- jss quota <cmd> # Manage storage quotas (set, show, reconcile)
116
- jss --help # Show help
117
- ```
118
-
119
- ### Start Options
120
38
 
121
- | Option | Description | Default |
122
- |--------|-------------|---------|
123
- | `-p, --port <n>` | Port to listen on | 3000 |
124
- | `-h, --host <addr>` | Host to bind to | 0.0.0.0 |
125
- | `-r, --root <path>` | Data directory | ./data |
126
- | `-c, --config <file>` | Config file path | - |
127
- | `--ssl-key <path>` | SSL private key (PEM) | - |
128
- | `--ssl-cert <path>` | SSL certificate (PEM) | - |
129
- | `--conneg` | Enable Turtle support | false |
130
- | `--notifications` | Enable WebSocket | false |
131
- | `--idp` | Enable built-in IdP | false |
132
- | `--idp-issuer <url>` | IdP issuer URL | (auto) |
133
- | `--subdomains` | Enable subdomain-based pods | false |
134
- | `--base-domain <domain>` | Base domain for subdomains | - |
135
- | `--mashlib` | Enable Mashlib (local mode) | false |
136
- | `--mashlib-cdn` | Enable Mashlib (CDN mode) | false |
137
- | `--mashlib-module <url>` | Enable ES module data browser from a URL | - |
138
- | `--mashlib-version <ver>` | Mashlib CDN version | 2.0.0 |
139
- | `--solidos-ui` | Enable modern SolidOS UI (requires --mashlib) | false |
140
- | `--git` | Enable Git HTTP backend | false |
141
- | `--nostr` | Enable Nostr relay | false |
142
- | `--nostr-path <path>` | Nostr relay WebSocket path | /relay |
143
- | `--nostr-max-events <n>` | Max events in relay memory | 1000 |
144
- | `--invite-only` | Require invite code for registration | false |
145
- | `--webid-tls` | Enable WebID-TLS client certificate auth | false |
146
- | `--default-quota <size>` | Default storage quota per pod (e.g., 50MB) | 50MB |
147
- | `--activitypub` | Enable ActivityPub federation | false |
148
- | `--ap-username <name>` | ActivityPub username | me |
149
- | `--ap-display-name <name>` | ActivityPub display name | (username) |
150
- | `--ap-summary <text>` | ActivityPub bio/summary | - |
151
- | `--ap-nostr-pubkey <hex>` | Nostr pubkey for identity linking | - |
152
- | `--public` | Allow unauthenticated access (skip WAC) | false |
153
- | `--read-only` | Disable PUT/DELETE/PATCH methods | false |
154
- | `--live-reload` | Auto-refresh browser on file changes | false |
155
- | `--pay` | Enable HTTP 402 paid access for /pay/* | false |
156
- | `--pay-cost <n>` | Cost per request in satoshis | 1 |
157
- | `--pay-mempool-url <url>` | Mempool API URL for deposit verification | (testnet4) |
158
- | `--pay-address <addr>` | Address for receiving deposits | - |
159
- | `--pay-token <ticker>` | Token to sell (enables primary market + withdrawal) | - |
160
- | `--pay-rate <n>` | Sats per token for buy/withdraw | 1 |
161
- | `--pay-chains <ids>` | Multi-chain deposits + AMM (e.g. "tbtc3,tbtc4") | - |
162
- | `--mongo` | Enable MongoDB-backed /db/ route | false |
163
- | `--mongo-url <url>` | MongoDB connection URL | mongodb://localhost:27017 |
164
- | `--mongo-database <name>` | MongoDB database name | solid |
165
- | `--webrtc` | Enable WebRTC signaling server | false |
166
- | `--webrtc-path <path>` | WebRTC signaling WebSocket path | /.webrtc |
167
- | `--tunnel` | Enable tunnel proxy (decentralized ngrok) | false |
168
- | `--tunnel-path <path>` | Tunnel WebSocket path | /.tunnel |
169
- | `-q, --quiet` | Suppress logs | false |
170
-
171
- ### Environment Variables
172
-
173
- All options can be set via environment variables with `JSS_` prefix:
174
-
175
- ```bash
176
- export JSS_PORT=8443
177
- export JSS_SSL_KEY=/path/to/key.pem
178
- export JSS_SSL_CERT=/path/to/cert.pem
179
- export JSS_CONNEG=true
180
- export JSS_SUBDOMAINS=true
181
- export JSS_BASE_DOMAIN=example.com
182
- export JSS_MASHLIB=true
183
- export JSS_MASHLIB_MODULE=https://example.com/mashlib.js
184
- export JSS_NOSTR=true
185
- export JSS_INVITE_ONLY=true
186
- export JSS_WEBID_TLS=true
187
- export JSS_DEFAULT_QUOTA=100MB
188
- export JSS_ACTIVITYPUB=true
189
- export JSS_AP_USERNAME=alice
190
- export JSS_PUBLIC=true
191
- export JSS_READ_ONLY=true
192
- export JSS_LIVE_RELOAD=true
193
- export JSS_SOLIDOS_UI=true
194
- export JSS_PAY=true
195
- export JSS_PAY_COST=10
196
- export JSS_PAY_ADDRESS=your-address
197
- export JSS_PAY_TOKEN=PODS
198
- export JSS_PAY_RATE=10
199
- export JSS_MONGO=true
200
- export JSS_MONGO_URL=mongodb://localhost:27017
201
- export JSS_MONGO_DATABASE=solid
202
- export JSS_WEBRTC=true
39
+ # Start
203
40
  jss start
204
- ```
205
-
206
- ### Config File
207
-
208
- Create `config.json`:
209
41
 
210
- ```json
211
- {
212
- "port": 8443,
213
- "root": "./data",
214
- "sslKey": "./ssl/key.pem",
215
- "sslCert": "./ssl/cert.pem",
216
- "conneg": true,
217
- "notifications": true
218
- }
42
+ # With common options
43
+ jss start --port 8443 --idp --mashlib --conneg --git --nostr
219
44
  ```
220
45
 
221
- Then: `jss start --config config.json`
222
-
223
46
  ### Creating a Pod
224
47
 
225
- ### Single-User Mode
226
-
227
- For personal pod servers where only one user needs access:
228
-
229
- ```bash
230
- # Basic single-user mode (creates pod at /me/)
231
- jss start --single-user --idp
232
-
233
- # Custom username
234
- jss start --single-user --single-user-name alice --idp
235
-
236
- # Root-level pod (pod at /, WebID at /profile/card#me)
237
- jss start --single-user --single-user-name '' --idp
238
-
239
- # Via environment
240
- JSS_SINGLE_USER=true jss start --idp
241
- ```
242
-
243
- **Features:**
244
- - Pod auto-created on first startup with full structure (inbox, public, private, profile, Settings)
245
- - Registration endpoint disabled (returns 403)
246
- - Login still works for the single user
247
- - Proper ACLs generated automatically
248
-
249
-
250
48
  ```bash
251
49
  curl -X POST http://localhost:3000/.pods \
252
50
  -H "Content-Type: application/json" \
253
51
  -d '{"name": "alice"}'
254
52
  ```
255
53
 
256
- Response:
257
- ```json
258
- {
259
- "name": "alice",
260
- "webId": "http://localhost:3000/alice/#me",
261
- "podUri": "http://localhost:3000/alice/",
262
- "token": "eyJ..."
263
- }
264
- ```
265
-
266
54
  ### Using the Pod
267
55
 
268
56
  ```bash
269
- # Read public profile
270
- curl http://localhost:3000/alice/
57
+ # Read
58
+ curl http://localhost:3000/alice/public/
271
59
 
272
- # Write to pod (with token)
60
+ # Write
273
61
  curl -X PUT http://localhost:3000/alice/public/data.json \
274
62
  -H "Authorization: Bearer YOUR_TOKEN" \
275
63
  -H "Content-Type: application/ld+json" \
276
64
  -d '{"@id": "#data", "http://example.org/value": 42}'
277
-
278
- # Read back
279
- curl http://localhost:3000/alice/public/data.json
280
- ```
281
-
282
- ### PATCH with N3
283
-
284
- ```bash
285
- curl -X PATCH http://localhost:3000/alice/public/data.json \
286
- -H "Authorization: Bearer YOUR_TOKEN" \
287
- -H "Content-Type: text/n3" \
288
- -d '@prefix solid: <http://www.w3.org/ns/solid/terms#>.
289
- _:patch a solid:InsertDeletePatch;
290
- solid:inserts { <#data> <http://example.org/name> "Updated" }.'
291
- ```
292
-
293
- ### PATCH with SPARQL Update
294
-
295
- ```bash
296
- curl -X PATCH http://localhost:3000/alice/public/data.json \
297
- -H "Authorization: Bearer YOUR_TOKEN" \
298
- -H "Content-Type: application/sparql-update" \
299
- -d 'PREFIX ex: <http://example.org/>
300
- DELETE DATA { <#data> ex:value 42 } ;
301
- INSERT DATA { <#data> ex:value 43 }'
302
- ```
303
-
304
- ### Conditional Requests
305
-
306
- Use `If-Match` for safe updates (optimistic concurrency):
307
-
308
- ```bash
309
- # Get current ETag
310
- ETAG=$(curl -sI http://localhost:3000/alice/public/data.json | grep -i etag | awk '{print $2}')
311
-
312
- # Update only if ETag matches
313
- curl -X PUT http://localhost:3000/alice/public/data.json \
314
- -H "Authorization: Bearer YOUR_TOKEN" \
315
- -H "Content-Type: application/ld+json" \
316
- -H "If-Match: $ETAG" \
317
- -d '{"@id": "#data", "http://example.org/value": 100}'
318
- ```
319
-
320
- Use `If-None-Match: *` for create-only semantics:
321
-
322
- ```bash
323
- # Create only if resource doesn't exist (returns 412 if it does)
324
- curl -X PUT http://localhost:3000/alice/public/new-resource.json \
325
- -H "Authorization: Bearer YOUR_TOKEN" \
326
- -H "Content-Type: application/ld+json" \
327
- -H "If-None-Match: *" \
328
- -d '{"@id": "#new"}'
329
- ```
330
-
331
- ## Philosophy: JSON-LD First
332
-
333
- This is a **JSON-LD native implementation**. Unlike traditional Solid servers that treat Turtle as the primary format and convert to/from it, this server:
334
-
335
- - **Stores everything as JSON-LD** - No RDF parsing overhead for standard operations
336
- - **Serves JSON-LD by default** - Modern web applications can consume responses directly
337
- - **Content negotiation is optional** - Enable Turtle support with `{ conneg: true }` when needed
338
- - **Fast by design** - Skip the RDF parsing tax when you don't need it
339
-
340
- ### Why JSON-LD First?
341
-
342
- 1. **Performance**: JSON parsing is native to JavaScript - no external RDF libraries needed for basic operations
343
- 2. **Simplicity**: JSON-LD is valid JSON - works with any JSON tooling
344
- 3. **Web-native**: Browsers and web apps understand JSON natively
345
- 4. **Semantic web ready**: JSON-LD is a W3C standard RDF serialization
346
-
347
- ### When to Enable Content Negotiation
348
-
349
- Enable `conneg: true` when:
350
- - Interoperating with Turtle-based Solid apps
351
- - Serving data to legacy Solid clients
352
- - Running conformance tests that require Turtle support
353
-
354
- ```javascript
355
- import { createServer } from './src/server.js';
356
-
357
- // Default: JSON-LD only (fast)
358
- const server = createServer();
359
-
360
- // With Turtle support (for interoperability)
361
- const serverWithConneg = createServer({ conneg: true });
362
- ```
363
-
364
- ## Configuration
365
-
366
- ```javascript
367
- createServer({
368
- logger: true, // Enable Fastify logging (default: true)
369
- conneg: false, // Enable content negotiation (default: false)
370
- notifications: false, // Enable WebSocket notifications (default: false)
371
- subdomains: false, // Enable subdomain-based pods (default: false)
372
- baseDomain: null, // Base domain for subdomains (e.g., "example.com")
373
- mashlib: false, // Enable Mashlib data browser - local mode (default: false)
374
- mashlibCdn: false, // Enable Mashlib data browser - CDN mode (default: false)
375
- mashlibVersion: '2.0.0', // Mashlib version for CDN mode
376
- });
377
- ```
378
-
379
- ### Mashlib Data Browser
380
-
381
- Enable the [SolidOS Mashlib](https://github.com/SolidOS/mashlib) data browser for RDF resources. Two modes are available:
382
-
383
- **CDN Mode** (recommended for getting started):
384
- ```bash
385
- jss start --mashlib-cdn --conneg
386
- ```
387
- Loads mashlib from unpkg.com CDN. Zero footprint - no local files needed.
388
-
389
- **Local Mode** (for production/offline):
390
- ```bash
391
- jss start --mashlib --conneg
392
- ```
393
- Serves mashlib from `src/mashlib-local/dist/`. Requires building mashlib locally:
394
- ```bash
395
- cd src/mashlib-local
396
- npm install && npm run build
397
- ```
398
-
399
- **ES Module Mode** (for custom or next-gen mashlib builds):
400
- ```bash
401
- jss start --mashlib-module https://example.com/mashlib.js
402
- ```
403
- Loads an ES module-based data browser from any URL. Uses `<script type="module">` and `<div id="mashlib">` (self-initializing). CSS is auto-derived by replacing `.js` with `.css`. Content negotiation is auto-enabled.
404
-
405
- **How it works:**
406
- 1. Browser requests `/alice/public/data.ttl` with `Accept: text/html`
407
- 2. Server returns Mashlib HTML wrapper
408
- 3. Mashlib fetches the actual data via content negotiation
409
- 4. Mashlib renders an interactive, editable view
410
-
411
- **Note:** Mashlib works best with `--conneg` enabled for Turtle support.
412
-
413
- **Modern UI (SolidOS UI):**
414
- ```bash
415
- jss start --mashlib --solidos-ui --conneg
416
- ```
417
- Serves a modern Nextcloud-style UI shell while reusing mashlib's data layer. The `--solidos-ui` flag swaps the classic databrowser interface for a cleaner, mobile-friendly design with:
418
- - Modern file browser with breadcrumb navigation
419
- - Profile, Contacts, Sharing, and Settings views
420
- - Path-based URLs (browser URL reflects current resource)
421
- - Responsive design for mobile devices
422
-
423
- Requires solidos-ui dist files in `src/mashlib-local/dist/solidos-ui/`. See [solidos-ui](https://github.com/solidos/solidos/tree/main/workspaces/solidos-ui) for details.
424
-
425
- ### Profile Pages
426
-
427
- Pod profiles (`/alice/`) use HTML with embedded JSON-LD data islands and are rendered using:
428
- - [mashlib-jss](https://github.com/JavaScriptSolidServer/mashlib-jss) - A fork of mashlib with `getPod()` fix for path-based pods
429
- - [solidos-lite](https://github.com/SolidOS/solidos-lite) - Parses JSON-LD data islands into the RDF store
430
-
431
- This allows profiles to work without server-side content negotiation while still providing full SolidOS editing capabilities.
432
-
433
- ### WebSocket Notifications
434
-
435
- Enable real-time notifications for resource changes:
436
-
437
- ```javascript
438
- const server = createServer({ notifications: true });
439
- ```
440
-
441
- Clients discover the WebSocket URL via the `Updates-Via` header:
442
-
443
- ```bash
444
- curl -I http://localhost:3000/alice/public/
445
- # Updates-Via: ws://localhost:3000/.notifications
446
- ```
447
-
448
- Protocol (solid-0.1, compatible with SolidOS):
449
- ```
450
- Server: protocol solid-0.1
451
- Client: sub http://localhost:3000/alice/public/data.json
452
- Server: ack http://localhost:3000/alice/public/data.json
453
- Server: pub http://localhost:3000/alice/public/data.json (on change)
454
- ```
455
-
456
- ## Git Support
457
-
458
- Enable Git HTTP backend to clone and push to pod containers:
459
-
460
- ```bash
461
- jss start --git
462
- ```
463
-
464
- ### Initialize a Repository
465
-
466
- ```bash
467
- # Create a git repo in a pod container
468
- cd data/alice/myrepo
469
- git init
470
- echo "# My Project" > README.md
471
- git add . && git commit -m "Initial commit"
472
- ```
473
-
474
- ### Clone and Push
475
-
476
- ```bash
477
- # Clone (public read access)
478
- git clone http://localhost:3000/alice/myrepo
479
-
480
- # Push (requires write access via WAC)
481
- cd myrepo
482
- echo "New content" >> README.md
483
- git add . && git commit -m "Update"
484
- git push
485
- ```
486
-
487
- Git operations respect WAC permissions - clone requires Read access, push requires Write access.
488
-
489
- **Auto-checkout:** After a successful push to a non-bare repository, JSS automatically updates the working directory - no post-receive hooks needed.
490
-
491
- ### Git Push with Nostr Authentication
492
-
493
- Git push supports NIP-98 authentication via Basic Auth. Install the credential helper:
494
-
495
- ```bash
496
- npm install -g git-credential-nostr
497
- git-credential-nostr generate
498
- git config --global credential.helper nostr
499
- git config --global nostr.privkey <key-from-generate>
500
- ```
501
-
502
- Create an ACL for your repo (includes public read for clone + owner write for push):
503
-
504
- ```bash
505
- cd myrepo
506
- git-credential-nostr acl > .acl
507
- git add .acl && git commit -m "Add ACL"
508
- ```
509
-
510
- See [git-credential-nostr](https://github.com/JavaScriptSolidServer/git-credential-nostr) for more details.
511
-
512
- ## ActivityPub Federation
513
-
514
- Enable ActivityPub to federate with Mastodon, Pleroma, Misskey, and other Fediverse servers:
515
-
516
- ```bash
517
- jss start --activitypub --ap-username alice --ap-display-name "Alice" --ap-summary "Hello from JSS!"
518
- ```
519
-
520
- ### Endpoints
521
-
522
- | Endpoint | Description |
523
- |----------|-------------|
524
- | `/.well-known/webfinger` | Actor discovery (Mastodon searches here) |
525
- | `/.well-known/nodeinfo` | NodeInfo discovery |
526
- | `/profile/card` | Actor (returns JSON-LD when `Accept: application/activity+json`) |
527
- | `/inbox` | Shared inbox for receiving activities |
528
- | `/profile/card/inbox` | Personal inbox |
529
- | `/profile/card/outbox` | User's activities |
530
- | `/profile/card/followers` | Followers collection |
531
- | `/profile/card/following` | Following collection |
532
-
533
- ### How It Works
534
-
535
- 1. **Discovery**: Mastodon looks up `@alice@your.server` via WebFinger
536
- 2. **Actor**: Returns ActivityPub Actor JSON-LD with public key
537
- 3. **Follow**: Remote servers POST Follow activities to inbox
538
- 4. **Accept**: JSS auto-accepts follows and sends Accept back
539
- 5. **Delivery**: Posts are signed with HTTP Signatures and delivered to follower inboxes
540
-
541
- ### Identity Linking
542
-
543
- Your WebID (`/profile/card#me`) becomes your ActivityPub Actor. Link to Nostr identity:
544
-
545
- ```bash
546
- jss start --activitypub --ap-nostr-pubkey <64-char-hex-pubkey>
547
- ```
548
-
549
- This adds `alsoKnownAs: ["did:nostr:<pubkey>"]` to your Actor profile, creating a verifiable link between your Solid, ActivityPub, and Nostr identities (the SAND stack).
550
-
551
- ### Programmatic Usage
552
-
553
- ```javascript
554
- import { createServer } from 'javascript-solid-server';
555
-
556
- const server = createServer({
557
- activitypub: true,
558
- apUsername: 'alice',
559
- apDisplayName: 'Alice',
560
- apSummary: 'Building the decentralized web!',
561
- apNostrPubkey: 'abc123...' // Optional: links to did:nostr
562
- });
563
- ```
564
-
565
- ### Testing Federation
566
-
567
- ```bash
568
- # Check WebFinger
569
- curl "http://localhost:3000/.well-known/webfinger?resource=acct:alice@localhost:3000"
570
-
571
- # Get Actor (AP format)
572
- curl -H "Accept: application/activity+json" http://localhost:3000/profile/card
573
-
574
- # Check NodeInfo
575
- curl http://localhost:3000/.well-known/nodeinfo/2.1
576
- ```
577
-
578
- ## Mastodon-compatible API
579
-
580
- JSS exposes Mastodon API endpoints so that Mastodon clients (Elk, Phanpy, Ice Cubes) can connect:
581
-
582
- ```bash
583
- jss start --activitypub --idp
584
- ```
585
-
586
- ### Endpoints
587
-
588
- | Endpoint | Description |
589
- |----------|-------------|
590
- | `POST /api/v1/apps` | Dynamic client registration |
591
- | `GET /api/v1/accounts/verify_credentials` | Current user profile |
592
- | `GET /api/v1/instance` | Instance metadata |
593
- | `GET /oauth/authorize` | OAuth authorize page |
594
- | `POST /oauth/authorize` | Process login |
595
- | `POST /oauth/token` | Exchange code for Bearer token |
596
-
597
- ### OAuth 2.0 Flow
598
-
599
- The OAuth layer is shared between Mastodon clients, remoteStorage apps, and third-party Solid panes:
600
-
601
- 1. Client registers via `POST /api/v1/apps` (gets `client_id` + `client_secret`)
602
- 2. Client redirects user to `GET /oauth/authorize?client_id=...&redirect_uri=...&response_type=code`
603
- 3. User logs in, JSS redirects back with `?code=...`
604
- 4. Client exchanges code for Bearer token via `POST /oauth/token`
605
- 5. Bearer token works with all JSS endpoints (Solid, ActivityPub, remoteStorage)
606
-
607
- Supports out-of-band (OOB) redirect for CLI/desktop clients.
608
-
609
- ### Testing
610
-
611
- ```bash
612
- # Register a client
613
- curl -X POST http://localhost:3000/api/v1/apps \
614
- -H "Content-Type: application/json" \
615
- -d '{"client_name": "Test App", "redirect_uris": "urn:ietf:wg:oauth:2.0:oob"}'
616
-
617
- # Check instance info
618
- curl http://localhost:3000/api/v1/instance
619
- ```
620
-
621
- ## remoteStorage
622
-
623
- JSS implements the [remoteStorage protocol](https://remotestorage.io/spec/draft-dejong-remotestorage-22). The storage routes are always available, but WebFinger discovery and OAuth require `--activitypub` (which provides the WebFinger and OAuth endpoints). Any remoteStorage-compatible app can store and sync data on your pod.
624
-
625
- ```bash
626
- jss start --activitypub --idp
627
- ```
628
-
629
- ### Discovery
630
-
631
- remoteStorage clients discover the storage endpoint via WebFinger:
632
-
633
- ```bash
634
- curl "http://localhost:3000/.well-known/webfinger?resource=acct:me@localhost:3000"
635
- ```
636
-
637
- The response includes a `remotestorage` link relation pointing to `/storage/me/`.
638
-
639
- ### Endpoints
640
-
641
- | Method | Endpoint | Description |
642
- |--------|----------|-------------|
643
- | `GET` | `/storage/:user/*` | Read file or list folder (JSON-LD) |
644
- | `HEAD` | `/storage/:user/*` | Get metadata (ETag, Content-Type, size) |
645
- | `PUT` | `/storage/:user/*` | Write file (creates parent folders) |
646
- | `DELETE` | `/storage/:user/*` | Delete file |
647
-
648
- ### How It Works
649
-
650
- - **Auth**: Bearer token via OAuth 2.0 (same flow as Mastodon clients)
651
- - **Public folder**: `/storage/me/public/*` is readable without auth
652
- - **Conditional requests**: If-Match, If-None-Match (uses shared ETag utilities)
653
- - **Dotfile protection**: `.acl`, `.meta`, and other dotfiles are blocked
654
- - **Read-only mode**: Respects `--read-only` flag
655
- - **Streaming**: Large files are streamed, not buffered
656
-
657
- ### Testing
658
-
659
- ```bash
660
- # Write a file (needs Bearer token from OAuth flow)
661
- curl -X PUT http://localhost:3000/storage/me/documents/hello.txt \
662
- -H "Authorization: Bearer YOUR_TOKEN" \
663
- -H "Content-Type: text/plain" \
664
- -d "Hello, remoteStorage!"
665
-
666
- # Read it back
667
- curl -H "Authorization: Bearer YOUR_TOKEN" \
668
- http://localhost:3000/storage/me/documents/hello.txt
669
-
670
- # List a folder
671
- curl -H "Authorization: Bearer YOUR_TOKEN" \
672
- http://localhost:3000/storage/me/documents/
673
-
674
- # Read from public folder (no auth needed)
675
- curl http://localhost:3000/storage/me/public/readme.txt
676
- ```
677
-
678
- ### Linking Nostr to WebID (did:nostr)
679
-
680
- Bridge your Nostr identity to a Solid WebID for seamless authentication:
681
-
682
- **Step 1:** Add your WebID to your Nostr profile (kind 0 event):
683
- ```json
684
- {
685
- "name": "alice",
686
- "alsoKnownAs": ["https://solid.social/alice/profile/card#me"]
687
- }
688
- ```
689
-
690
- **Step 2:** Add the did:nostr link to your WebID profile:
691
- ```json
692
- {
693
- "@id": "#me",
694
- "owl:sameAs": "did:nostr:<your-64-char-hex-pubkey>"
695
- }
696
- ```
697
-
698
- **How it works:**
699
- 1. NIP-98 signature is verified (existing flow)
700
- 2. DID document is fetched from `nostr.social/.well-known/did/nostr/<pubkey>.json`
701
- 3. `alsoKnownAs` is checked for a WebID URL
702
- 4. WebID profile is fetched and `owl:sameAs` verified
703
- 5. If bidirectional link exists → authenticated as WebID
704
-
705
- This enables Nostr users to access their Solid pods using existing NIP-07 browser extensions.
706
-
707
- ## Invite-Only Registration
708
-
709
- Control who can create accounts by requiring invite codes:
710
-
711
- ```bash
712
- jss start --idp --invite-only
713
- ```
714
-
715
- ### Managing Invite Codes
716
-
717
- ```bash
718
- # Create a single-use invite
719
- jss invite create
720
- # Created invite code: ABCD1234
721
-
722
- # Create multi-use invite with note
723
- jss invite create -u 5 -n "For team members"
724
-
725
- # List all active invites
726
- jss invite list
727
- # CODE USES CREATED NOTE
728
- # -------------------------------------------------------
729
- # ABCD1234 0/1 2026-01-03
730
- # EFGH5678 2/5 2026-01-03 For team members
731
-
732
- # Revoke an invite
733
- jss invite revoke ABCD1234
734
- ```
735
-
736
- ### How It Works
737
-
738
- | Mode | Registration | Pod Creation |
739
- |------|--------------|--------------|
740
- | Open (default) | Anyone can register | Anyone can create pods |
741
- | Invite-only | Requires valid invite code | Via registration only |
742
-
743
- When `--invite-only` is enabled:
744
- - The registration page shows an "Invite Code" field
745
- - Invalid or expired codes are rejected with an error
746
- - Each use decrements the invite's remaining uses
747
- - Depleted invites are automatically removed
748
-
749
- Invite codes are stored in `.server/invites.json` in your data directory.
750
-
751
- ## Storage Quotas
752
-
753
- Limit storage per pod to prevent abuse and manage resources:
754
-
755
- ```bash
756
- jss start --default-quota 50MB
757
- ```
758
-
759
- ### Managing Quotas
760
-
761
- ```bash
762
- # Set quota for a user (overrides default)
763
- jss quota set alice 100MB
764
-
765
- # Show quota info
766
- jss quota show alice
767
- # alice:
768
- # Used: 12.5 MB
769
- # Limit: 100 MB
770
- # Free: 87.5 MB
771
- # Usage: 12%
772
-
773
- # Recalculate from actual disk usage
774
- jss quota reconcile alice
775
- ```
776
-
777
- ## MongoDB Storage (`/db/` Route)
778
-
779
- Optional MongoDB-backed route for JSON-LD documents that need scale (social feeds, posts, follows). All other routes continue using the filesystem unchanged.
780
-
781
- ```bash
782
- # Install the optional MongoDB driver
783
- npm install mongodb
784
-
785
- # Start with MongoDB enabled
786
- jss start --mongo --mongo-url mongodb://localhost:27017 --mongo-database solid
787
- ```
788
-
789
- ### Operations
790
-
791
- ```bash
792
- # Store a document
793
- curl -X PUT http://localhost:3000/db/alice/notes/1 \
794
- -H "Content-Type: application/ld+json" \
795
- -H "Authorization: Bearer <token>" \
796
- -d '{"@context": "https://schema.org/", "@type": "Note", "text": "Hello"}'
797
-
798
- # Read it back
799
- curl http://localhost:3000/db/alice/notes/1
800
-
801
- # List container (derived from URI prefixes)
802
- curl http://localhost:3000/db/alice/
803
-
804
- # Delete
805
- curl -X DELETE http://localhost:3000/db/alice/notes/1 \
806
- -H "Authorization: Bearer <token>"
807
- ```
808
-
809
- ### How It Works
810
-
811
- - `GET /db/:path` — retrieve a document by URI, or list a virtual container
812
- - `PUT /db/:path` — create or update (upsert) a JSON-LD document
813
- - `DELETE /db/:path` — remove a document
814
- - Returns standard LDP headers (Link, ETag, WAC-Allow, CORS)
815
- - Supports conditional requests (If-Match, If-None-Match)
816
- - Container listings are computed from URI prefix queries — no directory management needed
817
- - Auth: pod owner can write (`/db/{podName}/...`), reads are public
818
- - MongoDB is an optional dependency — the server runs without it
819
-
820
- ### How It Works
821
-
822
- - Quotas are tracked incrementally on PUT, POST, and DELETE operations
823
- - When quota is exceeded, the server returns HTTP 507 Insufficient Storage
824
- - Each pod stores its quota in `/{pod}/.quota.json`
825
- - Use `reconcile` to fix quota drift from manual file changes
826
-
827
- ### Size Formats
828
-
829
- Supported formats: `50MB`, `1GB`, `500KB`, `1TB`
830
-
831
- ## WebRTC Signaling
832
-
833
- Peer-to-peer communication via WebRTC, using JSS as the signaling server. Once peers are connected, all media and data flows directly between them.
834
-
835
- ```bash
836
- jss start --webrtc
837
- ```
838
-
839
- ### How It Works
840
-
841
- 1. Both peers connect to `wss://your.pod/.webrtc` (WebID auth required)
842
- 2. Caller sends an SDP offer targeting the callee's WebID
843
- 3. JSS relays the offer/answer and ICE candidates between peers
844
- 4. Once a direct path is found, the peer-to-peer connection is established
845
- 5. JSS steps out — video, audio, files, and data flow directly between peers
846
-
847
- ### Protocol
848
-
849
- Messages are JSON over WebSocket:
850
-
851
- ```js
852
- // Send an offer to another user
853
- { "type": "offer", "to": "https://bob.example/profile/card#me", "sdp": "..." }
854
-
855
- // Receive an offer from another user
856
- { "type": "offer", "from": "https://alice.example/profile/card#me", "sdp": "..." }
857
-
858
- // ICE candidate exchange
859
- { "type": "candidate", "to": "https://bob.example/profile/card#me", "candidate": {...} }
860
-
861
- // Hang up
862
- { "type": "hangup", "to": "https://bob.example/profile/card#me" }
863
- ```
864
-
865
- On connect, peers receive a list of online users and get notified when others join or leave.
866
-
867
- ## Tunnel Proxy (Decentralized ngrok)
868
-
869
- Expose a local dev server to the internet through your JSS pod. A tunnel client connects via WebSocket, registers a name, and receives proxied HTTP requests.
870
-
871
- ```bash
872
- jss start --tunnel
873
- ```
874
-
875
- ### How It Works
876
-
877
- 1. Tunnel client connects to `wss://your.pod/.tunnel` (WebID auth required)
878
- 2. Client registers a name: `{ "type": "register", "name": "myapp" }`
879
- 3. Public URL becomes available at `https://your.pod/tunnel/myapp/`
880
- 4. HTTP requests to that URL are serialized and sent to the tunnel client over WebSocket
881
- 5. Tunnel client forwards to localhost, returns the response
882
-
883
- ### Tunnel Client Protocol
884
-
885
- ```js
886
- // 1. Register a tunnel
887
- → { "type": "register", "name": "myapp" }
888
- ← { "type": "registered", "name": "myapp", "url": "/tunnel/myapp/" }
889
-
890
- // 2. Receive proxied HTTP requests
891
- ← { "type": "request", "id": "uuid", "method": "GET", "path": "/api/hello", "headers": {...} }
892
-
893
- // 3. Return the response
894
- → { "type": "response", "id": "uuid", "status": 200, "headers": {...}, "body": "..." }
895
- ```
896
-
897
- ## HTTP 402 Paid Access
898
-
899
- Monetize API endpoints with per-request satoshi payments. Resources under `/pay/*` require NIP-98 authentication and a positive balance.
900
-
901
- ```bash
902
- jss start --pay --pay-cost 10 --pay-address your-address --pay-token PODS --pay-rate 10
903
- ```
904
-
905
- ### Routes
906
-
907
- | Method | Path | Description |
908
- |--------|------|-------------|
909
- | GET | `/pay/.info` | Public: cost, token info, chains, pool |
910
- | GET | `/pay/.balance` | Check your balance (NIP-98 auth) |
911
- | POST | `/pay/.deposit` | Deposit sats via TXO URI or MRC20 state proof |
912
- | POST | `/pay/.buy` | Buy tokens with sat balance (requires `--pay-token`) |
913
- | POST | `/pay/.withdraw` | Withdraw balance as portable tokens (requires `--pay-token`) |
914
- | GET | `/pay/.offers` | List open sell orders (secondary market) |
915
- | POST | `/pay/.sell` | Create a sell order (requires `--pay-token`) |
916
- | POST | `/pay/.swap` | Execute a swap against a sell order |
917
- | GET | `/pay/.pool` | AMM pool state (requires `--pay-chains`) |
918
- | POST | `/pay/.pool` | AMM swap, add/remove liquidity |
919
- | GET | `/pay/*` | Paid resource access (deducts balance) |
920
-
921
- ### How It Works
922
-
923
- 1. Authenticate with NIP-98 (Nostr HTTP Auth)
924
- 2. Check balance at `/pay/.balance`
925
- 3. Deposit sats by POSTing a TXO URI to `/pay/.deposit`
926
- 4. Access paid resources — each request deducts the configured cost
927
- 5. Optionally buy tokens (`/pay/.buy`) or withdraw as portable tokens (`/pay/.withdraw`)
928
- 6. Balance tracked in a [Web Ledger](https://webledgers.org/) at `/.well-known/webledgers/webledgers.json`
929
-
930
- ### Example
931
-
932
- ```bash
933
- # Check balance
934
- curl -H "Authorization: Nostr <base64-event>" http://localhost:3000/pay/.balance
935
-
936
- # Deposit (post a confirmed transaction output)
937
- curl -X POST -H "Authorization: Nostr <base64-event>" \
938
- http://localhost:3000/pay/.deposit \
939
- -d "txid:vout"
940
-
941
- # Access paid resource
942
- curl -H "Authorization: Nostr <base64-event>" http://localhost:3000/pay/my-resource
943
-
944
- # Buy tokens with sat balance
945
- curl -X POST -H "Authorization: Nostr <base64-event>" \
946
- -H "Content-Type: application/json" \
947
- http://localhost:3000/pay/.buy \
948
- -d '{"amount": 100}'
949
-
950
- # Withdraw entire balance as portable tokens
951
- curl -X POST -H "Authorization: Nostr <base64-event>" \
952
- -H "Content-Type: application/json" \
953
- http://localhost:3000/pay/.withdraw \
954
- -d '{"all": true}'
955
65
  ```
956
66
 
957
- Deposit verification uses the mempool API (default: testnet4). The `X-Balance` and `X-Cost` headers are returned on successful paid requests. Buy and withdraw return portable MRC20 proofs with Bitcoin anchor data for independent verification.
958
-
959
- ### Secondary Market
960
-
961
- Users can trade tokens peer-to-peer through the pod. Sell orders are created via `/pay/.sell` and filled via `/pay/.swap`. The pod acts as escrow — transferring tokens on the Bitcoin-anchored MRC20 trail and settling sats in the webledger.
962
-
963
- ### Multi-Chain AMM
964
-
965
- Enable multi-chain deposits and an automated market maker:
966
-
967
- ```bash
968
- jss start --pay --pay-chains "tbtc3,tbtc4"
969
- ```
970
-
971
- Deposits detect the chain from the TXO URI prefix (`txo:tbtc3:txid:vout`). Each chain's balance is tracked separately. The AMM uses a constant-product formula (x × y = k) with a 0.3% fee.
972
-
973
- ```bash
974
- # Add liquidity
975
- curl -X POST -H "Authorization: Nostr <token>" \
976
- -H "Content-Type: application/json" \
977
- http://localhost:3000/pay/.pool \
978
- -d '{"action": "add-liquidity", "tbtc3": 1000, "tbtc4": 5000}'
979
-
980
- # Swap
981
- curl -X POST -H "Authorization: Nostr <token>" \
982
- -H "Content-Type: application/json" \
983
- http://localhost:3000/pay/.pool \
984
- -d '{"action": "swap", "sell": "tbtc3", "amount": 100}'
985
-
986
- # Check pool state
987
- curl http://localhost:3000/pay/.pool
988
- ```
989
-
990
- Supported chains: `btc`, `tbtc3`, `tbtc4`, `ltc`, `signet`.
991
-
992
- ## Authentication
993
-
994
- ### Simple Tokens (Development)
995
-
996
- Use the token returned from pod creation:
997
-
998
- ```bash
999
- curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:3000/alice/private/
1000
- ```
1001
-
1002
- ### Built-in Identity Provider (v0.0.12+)
1003
-
1004
- Enable the built-in Solid-OIDC Identity Provider:
1005
-
1006
- ```bash
1007
- jss start --idp
1008
- ```
1009
-
1010
- With IdP enabled, pod creation requires email and password:
1011
-
1012
- ```bash
1013
- curl -X POST http://localhost:3000/.pods \
1014
- -H "Content-Type: application/json" \
1015
- -d '{"name": "alice", "email": "alice@example.com", "password": "secret123"}'
1016
- ```
1017
-
1018
- Response:
1019
- ```json
1020
- {
1021
- "name": "alice",
1022
- "webId": "http://localhost:3000/alice/#me",
1023
- "podUri": "http://localhost:3000/alice/",
1024
- "idpIssuer": "http://localhost:3000",
1025
- "loginUrl": "http://localhost:3000/idp/auth"
1026
- }
1027
- ```
1028
-
1029
- OIDC Discovery: `/.well-known/openid-configuration`
1030
-
1031
- ### Programmatic Login (CTH Compatible)
1032
-
1033
- For automated testing and scripts, use the credentials endpoint:
1034
-
1035
- ```bash
1036
- curl -X POST http://localhost:3000/idp/credentials \
1037
- -H "Content-Type: application/json" \
1038
- -d '{"email": "alice@example.com", "password": "secret123"}'
1039
- ```
1040
-
1041
- Response:
1042
- ```json
1043
- {
1044
- "access_token": "...",
1045
- "token_type": "Bearer",
1046
- "expires_in": 3600,
1047
- "webid": "http://localhost:3000/alice/#me"
1048
- }
1049
- ```
1050
-
1051
- For DPoP-bound tokens (Solid-OIDC compliant), include a DPoP proof header.
1052
-
1053
- ### Passkey Authentication (v0.0.77+)
1054
-
1055
- Enable passwordless login with WebAuthn/FIDO2:
1056
-
1057
- ```bash
1058
- jss start --idp
1059
- ```
1060
-
1061
- **How it works:**
1062
- 1. User logs in with username/password
1063
- 2. Prompted to add a passkey (Touch ID, Face ID, security key)
1064
- 3. Future logins: tap "Sign in with Passkey" → biometric → done!
1065
-
1066
- **Benefits:**
1067
- - Phishing-resistant (bound to domain)
1068
- - No passwords to remember or leak
1069
- - Works on mobile and desktop
1070
-
1071
- Passkeys are stored per-account and work across devices via platform sync (iCloud Keychain, Google Password Manager, etc.).
1072
-
1073
- ### Schnorr SSO (v0.0.79+)
1074
-
1075
- Sign in with your Nostr key using NIP-07 browser extensions:
1076
-
1077
- ```bash
1078
- jss start --idp
1079
- ```
1080
-
1081
- **How it works:**
1082
- 1. User clicks "Sign in with Schnorr" on the login page
1083
- 2. NIP-07 extension (Podkey, nos2x, Alby) signs a NIP-98 auth event
1084
- 3. Server verifies BIP-340 Schnorr signature
1085
- 4. User authenticated via linked did:nostr identity
1086
-
1087
- **Requirements:**
1088
- - Account must have a `did:nostr:<pubkey>` WebID linked
1089
- - User needs a NIP-07 compatible browser extension
1090
-
1091
- **Benefits:**
1092
- - No passwords - cryptographic authentication
1093
- - Works with existing Nostr identity
1094
- - Single sign-on across Solid and Nostr ecosystems
1095
-
1096
- ### Solid-OIDC (External IdP)
1097
-
1098
- The server also accepts DPoP-bound access tokens from external Solid identity providers:
1099
-
1100
- ```bash
1101
- curl -H "Authorization: DPoP ACCESS_TOKEN" \
1102
- -H "DPoP: DPOP_PROOF" \
1103
- http://localhost:3000/alice/private/
1104
- ```
1105
-
1106
- ### WebID-TLS (Client Certificates)
1107
-
1108
- For backend services, CLI tools, and automated agents that need non-interactive authentication:
1109
-
1110
- ```bash
1111
- jss start --ssl-key key.pem --ssl-cert cert.pem --webid-tls
1112
- ```
1113
-
1114
- **How it works:**
1115
- 1. Client presents X.509 certificate during TLS handshake
1116
- 2. Certificate's `SubjectAlternativeName` contains a WebID URI
1117
- 3. Server fetches the WebID profile
1118
- 4. Server verifies the certificate's public key matches one in the profile
1119
-
1120
- **Testing with curl:**
1121
-
1122
- ```bash
1123
- # Generate self-signed cert with WebID in SAN
1124
- openssl req -x509 -newkey rsa:2048 -keyout client-key.pem -out client-cert.pem -days 365 \
1125
- -subj "/CN=Test" -addext "subjectAltName=URI:https://example.com/alice/#me" -nodes
1126
-
1127
- # Make authenticated request
1128
- curl --cert client-cert.pem --key client-key.pem https://localhost:8443/alice/private/
1129
- ```
1130
-
1131
- **Profile requirement:** Your WebID profile must contain the certificate's public key:
1132
-
1133
- ```turtle
1134
- @prefix cert: <http://www.w3.org/ns/auth/cert#> .
1135
-
1136
- <#me> cert:key [
1137
- a cert:RSAPublicKey;
1138
- cert:modulus "abc123..."^^xsd:hexBinary;
1139
- cert:exponent 65537
1140
- ] .
1141
- ```
1142
-
1143
- **Use cases:**
1144
- - Enterprise backend services with existing PKI
1145
- - Server-to-server communication
1146
- - CLI tools and scripts
1147
- - IoT devices with embedded certificates
1148
-
1149
- ## Pod Structure
1150
-
1151
- ```
1152
- /alice/
1153
- ├── index.html # WebID profile (HTML with JSON-LD)
1154
- ├── .acl # Root ACL (owner + public read)
1155
- ├── inbox/ # Notifications (public append)
1156
- │ └── .acl
1157
- ├── public/ # Public files
1158
- ├── private/ # Private files (owner only)
1159
- │ └── .acl
1160
- └── settings/ # User preferences (owner only)
1161
- ├── .acl
1162
- ├── prefs
1163
- ├── publicTypeIndex
1164
- └── privateTypeIndex
1165
- ```
1166
-
1167
- ## Subdomain Mode (XSS Protection)
1168
-
1169
- By default, JSS uses **path-based pods** (`/alice/`, `/bob/`). This is simple but has a security limitation: all pods share the same origin, making cross-site scripting (XSS) attacks possible between pods.
1170
-
1171
- **Subdomain mode** provides **origin isolation** - each pod gets its own subdomain (`alice.example.com`, `bob.example.com`), preventing XSS attacks between pods.
1172
-
1173
- ### Why Subdomain Mode?
1174
-
1175
- | Mode | URL | Origin | XSS Risk |
1176
- |------|-----|--------|----------|
1177
- | Path-based | `example.com/alice/` | `example.com` | Shared origin - pods can XSS each other |
1178
- | Subdomain | `alice.example.com/` | `alice.example.com` | Isolated - browser's Same-Origin Policy protects |
1179
-
1180
- ### Enabling Subdomain Mode
67
+ ### Android/Termux
1181
68
 
1182
69
  ```bash
1183
- jss start --subdomains --base-domain example.com
70
+ pkg install nodejs git
71
+ npm install -g javascript-solid-server
72
+ jss start --port 8080 --nostr --git
1184
73
  ```
1185
74
 
1186
- Or via environment variables:
75
+ ## CLI Reference
1187
76
 
1188
77
  ```bash
1189
- export JSS_SUBDOMAINS=true
1190
- export JSS_BASE_DOMAIN=example.com
1191
- jss start
1192
- ```
1193
-
1194
- ### DNS Configuration
1195
-
1196
- You need a **wildcard DNS record** pointing to your server:
1197
-
1198
- ```
1199
- *.example.com A <your-server-ip>
78
+ jss start [options] # Start the server
79
+ jss init [options] # Initialize configuration
80
+ jss invite <cmd> # Manage invite codes
81
+ jss quota <cmd> # Manage storage quotas
1200
82
  ```
1201
83
 
1202
- ### Pod URLs in Subdomain Mode
84
+ Key options: `--port`, `--idp`, `--conneg`, `--mashlib`, `--git`, `--nostr`, `--activitypub`, `--webrtc`, `--tunnel`, `--mongo`, `--pay`, `--public`, `--single-user`
1203
85
 
1204
- | Path Mode | Subdomain Mode |
1205
- |-----------|----------------|
1206
- | `example.com/alice/` | `alice.example.com/` |
1207
- | `example.com/alice/public/file.txt` | `alice.example.com/public/file.txt` |
1208
- | `example.com/alice/#me` | `alice.example.com/#me` |
86
+ Full options: [docs/configuration.md](docs/configuration.md)
1209
87
 
1210
- Pod creation still uses the main domain:
88
+ ## Documentation
1211
89
 
1212
- ```bash
1213
- curl -X POST https://example.com/.pods \
1214
- -H "Content-Type: application/json" \
1215
- -d '{"name": "alice"}'
1216
- ```
90
+ | Topic | Link |
91
+ |-------|------|
92
+ | Configuration & Options | [docs/configuration.md](docs/configuration.md) |
93
+ | Authentication | [docs/authentication.md](docs/authentication.md) |
94
+ | Git Support | [docs/git-support.md](docs/git-support.md) |
95
+ | ActivityPub & Mastodon API | [docs/activitypub.md](docs/activitypub.md) |
96
+ | remoteStorage | [docs/remotestorage.md](docs/remotestorage.md) |
97
+ | Security & Subdomain Mode | [docs/security.md](docs/security.md) |
98
+ | HTTP 402 Payments | [docs/payments.md](docs/payments.md) |
99
+ | WebRTC & Tunnel | [docs/webrtc.md](docs/webrtc.md) |
100
+ | MongoDB `/db/` Route | [docs/mongodb.md](docs/mongodb.md) |
101
+ | Architecture & Structure | [docs/architecture.md](docs/architecture.md) |
1217
102
 
1218
103
  ## Comparison
1219
104
 
@@ -1224,55 +109,8 @@ curl -X POST https://example.com/.pods \
1224
109
  | [CSS](https://github.com/CommunitySolidServer/CommunitySolidServer) | 5.8 MB | 70 | Modular, configurable |
1225
110
  | [Pivot](https://github.com/solid-contrib/pivot) | ~6 MB | 70+ | Built on CSS |
1226
111
 
1227
- ## Security
1228
-
1229
- ### Root ACL Required
1230
-
1231
- JSS uses **restrictive mode** by default: if no ACL file exists for a resource, access is denied. This prevents unauthorized writes to unprotected containers.
1232
-
1233
- **You must create a root `.acl` file** in your data directory. Example (JSON-LD format):
1234
-
1235
- ```json
1236
- {
1237
- "@context": {
1238
- "acl": "http://www.w3.org/ns/auth/acl#",
1239
- "foaf": "http://xmlns.com/foaf/0.1/"
1240
- },
1241
- "@graph": [
1242
- {
1243
- "@id": "#owner",
1244
- "@type": "acl:Authorization",
1245
- "acl:agent": { "@id": "https://your-domain.com/profile/card#me" },
1246
- "acl:accessTo": { "@id": "https://your-domain.com/" },
1247
- "acl:default": { "@id": "https://your-domain.com/" },
1248
- "acl:mode": [
1249
- { "@id": "acl:Read" },
1250
- { "@id": "acl:Write" },
1251
- { "@id": "acl:Control" }
1252
- ]
1253
- },
1254
- {
1255
- "@id": "#public",
1256
- "@type": "acl:Authorization",
1257
- "acl:agentClass": { "@id": "foaf:Agent" },
1258
- "acl:accessTo": { "@id": "https://your-domain.com/" },
1259
- "acl:default": { "@id": "https://your-domain.com/" },
1260
- "acl:mode": [
1261
- { "@id": "acl:Read" }
1262
- ]
1263
- }
1264
- ]
1265
- }
1266
- ```
1267
-
1268
- Save this as `data/.acl` (replacing `your-domain.com` with your actual domain).
1269
-
1270
- See [Issue #32](https://github.com/JavaScriptSolidServer/JavaScriptSolidServer/issues/32) for background.
1271
-
1272
112
  ## Performance
1273
113
 
1274
- This server is designed for speed. Benchmark results on a typical development machine:
1275
-
1276
114
  | Operation | Requests/sec | Avg Latency | p99 Latency |
1277
115
  |-----------|-------------|-------------|-------------|
1278
116
  | GET resource | 5,400+ | 1.2ms | 3ms |
@@ -1281,143 +119,12 @@ This server is designed for speed. Benchmark results on a typical development ma
1281
119
  | POST (create) | 5,200+ | 1.3ms | 3ms |
1282
120
  | OPTIONS | 10,000+ | 0.4ms | 1ms |
1283
121
 
1284
- Run benchmarks yourself:
1285
- ```bash
1286
- npm run benchmark
1287
- ```
1288
-
1289
122
  ## Running Tests
1290
123
 
1291
124
  ```bash
1292
125
  npm test
1293
126
  ```
1294
127
 
1295
- Currently passing: **289 tests** (including 27 conformance tests)
1296
-
1297
- ### Conformance Test Harness (CTH)
1298
-
1299
- This server passes the Solid Conformance Test Harness authentication tests:
1300
-
1301
- ```bash
1302
- # Start server with IdP and content negotiation
1303
- JSS_PORT=4000 JSS_CONNEG=true JSS_IDP=true jss start
1304
-
1305
- # Create test users
1306
- curl -X POST http://localhost:4000/.pods \
1307
- -H "Content-Type: application/json" \
1308
- -d '{"name": "alice", "email": "alice@example.com", "password": "alicepassword123"}'
1309
-
1310
- curl -X POST http://localhost:4000/.pods \
1311
- -H "Content-Type: application/json" \
1312
- -d '{"name": "bob", "email": "bob@example.com", "password": "bobpassword123"}'
1313
-
1314
- # Run CTH authentication tests
1315
- docker run --rm --network=host \
1316
- -e SOLID_IDENTITY_PROVIDER="http://localhost:4000/" \
1317
- -e USERS_ALICE_WEBID="http://localhost:4000/alice/#me" \
1318
- -e USERS_ALICE_PASSWORD="alicepassword123" \
1319
- -e USERS_BOB_WEBID="http://localhost:4000/bob/#me" \
1320
- -e USERS_BOB_PASSWORD="bobpassword123" \
1321
- solidproject/conformance-test-harness:latest \
1322
- --filter="authentication"
1323
- ```
1324
-
1325
- **CTH Status (v0.0.15):**
1326
- - Authentication tests: 6/6 passing
1327
-
1328
- ## Project Structure
1329
-
1330
- ```
1331
- src/
1332
- ├── index.js # Entry point
1333
- ├── server.js # Fastify setup
1334
- ├── handlers/
1335
- │ ├── resource.js # GET, PUT, DELETE, HEAD, PATCH
1336
- │ ├── container.js # POST, pod creation
1337
- │ ├── git.js # Git HTTP backend
1338
- │ └── pay.js # HTTP 402 paid access
1339
- ├── storage/
1340
- │ ├── filesystem.js # File operations
1341
- │ └── quota.js # Storage quota management
1342
- ├── auth/
1343
- │ ├── middleware.js # Auth hook
1344
- │ ├── token.js # Simple token auth
1345
- │ ├── solid-oidc.js # DPoP verification
1346
- │ ├── nostr.js # NIP-98 Nostr authentication
1347
- │ ├── did-nostr.js # did:nostr → WebID resolution
1348
- │ └── webid-tls.js # WebID-TLS client certificate auth
1349
- ├── wac/
1350
- │ ├── parser.js # ACL parsing
1351
- │ └── checker.js # Permission checking
1352
- ├── ldp/
1353
- │ ├── headers.js # LDP Link headers
1354
- │ └── container.js # Container JSON-LD
1355
- ├── webid/
1356
- │ └── profile.js # WebID generation
1357
- ├── patch/
1358
- │ ├── n3-patch.js # N3 Patch support
1359
- │ └── sparql-update.js # SPARQL Update support
1360
- ├── notifications/
1361
- │ ├── index.js # WebSocket plugin
1362
- │ ├── events.js # Event emitter
1363
- │ └── websocket.js # solid-0.1 protocol
1364
- ├── idp/
1365
- │ ├── index.js # Identity Provider plugin
1366
- │ ├── provider.js # oidc-provider config
1367
- │ ├── adapter.js # Filesystem adapter
1368
- │ ├── accounts.js # User account management
1369
- │ ├── credentials.js # Credentials endpoint
1370
- │ ├── keys.js # JWKS key management
1371
- │ ├── interactions.js # Login/consent handlers
1372
- │ ├── passkey.js # WebAuthn/FIDO2 passkey support
1373
- │ ├── views.js # HTML templates
1374
- │ └── invites.js # Invite code management
1375
- ├── ap/
1376
- │ ├── index.js # ActivityPub plugin
1377
- │ ├── keys.js # RSA keypair management
1378
- │ ├── store.js # SQLite storage (followers, activities)
1379
- │ └── routes/
1380
- │ ├── actor.js # Actor JSON-LD
1381
- │ ├── inbox.js # Receive activities
1382
- │ ├── outbox.js # User's activities
1383
- │ ├── collections.js # Followers/following
1384
- │ ├── mastodon.js # Mastodon API (apps, instance, verify_credentials)
1385
- │ └── oauth.js # OAuth 2.0 authorize/token flow
1386
- ├── webledger.js # Web Ledger balance tracking (webledgers.org)
1387
- ├── mrc20.js # State chain verification
1388
- ├── remotestorage.js # remoteStorage protocol (draft-dejong-remotestorage-22)
1389
- ├── rdf/
1390
- │ ├── turtle.js # Turtle <-> JSON-LD
1391
- │ └── conneg.js # Content negotiation
1392
- ├── mashlib/
1393
- │ └── index.js # Mashlib data browser plugin
1394
- └── utils/
1395
- ├── url.js # URL utilities
1396
- ├── conditional.js # If-Match/If-None-Match
1397
- └── ssrf.js # SSRF protection
1398
- ```
1399
-
1400
- ## Dependencies
1401
-
1402
- 14 direct dependencies for a fast, secure server:
1403
-
1404
- - **fastify** - High-performance HTTP server
1405
- - **@fastify/middie** - Express/Connect middleware bridge (for IdP)
1406
- - **@fastify/rate-limit** - Rate limiting for API endpoints
1407
- - **@fastify/websocket** - WebSocket support for notifications
1408
- - **@simplewebauthn/server** - Passkey/WebAuthn authentication
1409
- - **bcryptjs** - Password hashing (pure JS, works on Termux/Android)
1410
- - **commander** - CLI command parsing
1411
- - **fs-extra** - Enhanced file operations
1412
- - **jose** - JWT/JWK handling for Solid-OIDC
1413
- - **microfed** - ActivityPub primitives (only when activitypub enabled)
1414
- - **n3** - Turtle parsing (only used when conneg enabled)
1415
- - **nostr-tools** - Nostr protocol and Schnorr signature verification
1416
- - **oidc-provider** - OpenID Connect Identity Provider (only when IdP enabled)
1417
- - **sql.js** - SQLite storage for federation data (WASM, cross-platform)
1418
-
1419
128
  ## License
1420
129
 
1421
130
  AGPL-3.0-only
1422
-
1423
- This project is licensed under the GNU Affero General Public License v3.0. If you run a modified version as a network service, you must make the source code available to users of that service.