javascript-solid-server 0.0.109 → 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.
@@ -0,0 +1,471 @@
1
+ ## Philosophy: JSON-LD First
2
+
3
+ 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:
4
+
5
+ - **Stores everything as JSON-LD** - No RDF parsing overhead for standard operations
6
+ - **Serves JSON-LD by default** - Modern web applications can consume responses directly
7
+ - **Content negotiation is optional** - Enable Turtle support with `{ conneg: true }` when needed
8
+ - **Fast by design** - Skip the RDF parsing tax when you don't need it
9
+
10
+ ### Why JSON-LD First?
11
+
12
+ 1. **Performance**: JSON parsing is native to JavaScript - no external RDF libraries needed for basic operations
13
+ 2. **Simplicity**: JSON-LD is valid JSON - works with any JSON tooling
14
+ 3. **Web-native**: Browsers and web apps understand JSON natively
15
+ 4. **Semantic web ready**: JSON-LD is a W3C standard RDF serialization
16
+
17
+ ### When to Enable Content Negotiation
18
+
19
+ Enable `conneg: true` when:
20
+ - Interoperating with Turtle-based Solid apps
21
+ - Serving data to legacy Solid clients
22
+ - Running conformance tests that require Turtle support
23
+
24
+ ```javascript
25
+ import { createServer } from './src/server.js';
26
+
27
+ // Default: JSON-LD only (fast)
28
+ const server = createServer();
29
+
30
+ // With Turtle support (for interoperability)
31
+ const serverWithConneg = createServer({ conneg: true });
32
+ ```
33
+
34
+
35
+ ## Configuration
36
+
37
+ ```javascript
38
+ createServer({
39
+ logger: true, // Enable Fastify logging (default: true)
40
+ conneg: false, // Enable content negotiation (default: false)
41
+ notifications: false, // Enable WebSocket notifications (default: false)
42
+ subdomains: false, // Enable subdomain-based pods (default: false)
43
+ baseDomain: null, // Base domain for subdomains (e.g., "example.com")
44
+ mashlib: false, // Enable Mashlib data browser - local mode (default: false)
45
+ mashlibCdn: false, // Enable Mashlib data browser - CDN mode (default: false)
46
+ mashlibVersion: '2.0.0', // Mashlib version for CDN mode
47
+ });
48
+ ```
49
+
50
+ ### Mashlib Data Browser
51
+
52
+ Enable the [SolidOS Mashlib](https://github.com/SolidOS/mashlib) data browser for RDF resources. Two modes are available:
53
+
54
+ **CDN Mode** (recommended for getting started):
55
+ ```bash
56
+ jss start --mashlib-cdn --conneg
57
+ ```
58
+ Loads mashlib from unpkg.com CDN. Zero footprint - no local files needed.
59
+
60
+ **Local Mode** (for production/offline):
61
+ ```bash
62
+ jss start --mashlib --conneg
63
+ ```
64
+ Serves mashlib from `src/mashlib-local/dist/`. Requires building mashlib locally:
65
+ ```bash
66
+ cd src/mashlib-local
67
+ npm install && npm run build
68
+ ```
69
+
70
+ **ES Module Mode** (for custom or next-gen mashlib builds):
71
+ ```bash
72
+ jss start --mashlib-module https://example.com/mashlib.js
73
+ ```
74
+ 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.
75
+
76
+ **How it works:**
77
+ 1. Browser requests `/alice/public/data.ttl` with `Accept: text/html`
78
+ 2. Server returns Mashlib HTML wrapper
79
+ 3. Mashlib fetches the actual data via content negotiation
80
+ 4. Mashlib renders an interactive, editable view
81
+
82
+ **Note:** Mashlib works best with `--conneg` enabled for Turtle support.
83
+
84
+ **Modern UI (SolidOS UI):**
85
+ ```bash
86
+ jss start --mashlib --solidos-ui --conneg
87
+ ```
88
+ 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:
89
+ - Modern file browser with breadcrumb navigation
90
+ - Profile, Contacts, Sharing, and Settings views
91
+ - Path-based URLs (browser URL reflects current resource)
92
+ - Responsive design for mobile devices
93
+
94
+ 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.
95
+
96
+ ### Profile Pages
97
+
98
+ Pod profiles (`/alice/`) use HTML with embedded JSON-LD data islands and are rendered using:
99
+ - [mashlib-jss](https://github.com/JavaScriptSolidServer/mashlib-jss) - A fork of mashlib with `getPod()` fix for path-based pods
100
+ - [solidos-lite](https://github.com/SolidOS/solidos-lite) - Parses JSON-LD data islands into the RDF store
101
+
102
+ This allows profiles to work without server-side content negotiation while still providing full SolidOS editing capabilities.
103
+
104
+ ### WebSocket Notifications
105
+
106
+ Enable real-time notifications for resource changes:
107
+
108
+ ```javascript
109
+ const server = createServer({ notifications: true });
110
+ ```
111
+
112
+ Clients discover the WebSocket URL via the `Updates-Via` header:
113
+
114
+ ```bash
115
+ curl -I http://localhost:3000/alice/public/
116
+ # Updates-Via: ws://localhost:3000/.notifications
117
+ ```
118
+
119
+ Protocol (solid-0.1, compatible with SolidOS):
120
+ ```
121
+ Server: protocol solid-0.1
122
+ Client: sub http://localhost:3000/alice/public/data.json
123
+ Server: ack http://localhost:3000/alice/public/data.json
124
+ Server: pub http://localhost:3000/alice/public/data.json (on change)
125
+ ```
126
+
127
+
128
+ ## CLI Start Options
129
+
130
+ ### Start Options
131
+
132
+ | Option | Description | Default |
133
+ |--------|-------------|---------|
134
+ | `-p, --port <n>` | Port to listen on | 3000 |
135
+ | `-h, --host <addr>` | Host to bind to | 0.0.0.0 |
136
+ | `-r, --root <path>` | Data directory | ./data |
137
+ | `-c, --config <file>` | Config file path | - |
138
+ | `--ssl-key <path>` | SSL private key (PEM) | - |
139
+ | `--ssl-cert <path>` | SSL certificate (PEM) | - |
140
+ | `--conneg` | Enable Turtle support | false |
141
+ | `--notifications` | Enable WebSocket | false |
142
+ | `--idp` | Enable built-in IdP | false |
143
+ | `--idp-issuer <url>` | IdP issuer URL | (auto) |
144
+ | `--subdomains` | Enable subdomain-based pods | false |
145
+ | `--base-domain <domain>` | Base domain for subdomains | - |
146
+ | `--mashlib` | Enable Mashlib (local mode) | false |
147
+ | `--mashlib-cdn` | Enable Mashlib (CDN mode) | false |
148
+ | `--mashlib-module <url>` | Enable ES module data browser from a URL | - |
149
+ | `--mashlib-version <ver>` | Mashlib CDN version | 2.0.0 |
150
+ | `--solidos-ui` | Enable modern SolidOS UI (requires --mashlib) | false |
151
+ | `--git` | Enable Git HTTP backend | false |
152
+ | `--nostr` | Enable Nostr relay | false |
153
+ | `--nostr-path <path>` | Nostr relay WebSocket path | /relay |
154
+ | `--nostr-max-events <n>` | Max events in relay memory | 1000 |
155
+ | `--invite-only` | Require invite code for registration | false |
156
+ | `--webid-tls` | Enable WebID-TLS client certificate auth | false |
157
+ | `--default-quota <size>` | Default storage quota per pod (e.g., 50MB) | 50MB |
158
+ | `--activitypub` | Enable ActivityPub federation | false |
159
+ | `--ap-username <name>` | ActivityPub username | me |
160
+ | `--ap-display-name <name>` | ActivityPub display name | (username) |
161
+ | `--ap-summary <text>` | ActivityPub bio/summary | - |
162
+ | `--ap-nostr-pubkey <hex>` | Nostr pubkey for identity linking | - |
163
+ | `--public` | Allow unauthenticated access (skip WAC) | false |
164
+ | `--read-only` | Disable PUT/DELETE/PATCH methods | false |
165
+ | `--live-reload` | Auto-refresh browser on file changes | false |
166
+ | `--pay` | Enable HTTP 402 paid access for /pay/* | false |
167
+ | `--pay-cost <n>` | Cost per request in satoshis | 1 |
168
+ | `--pay-mempool-url <url>` | Mempool API URL for deposit verification | (testnet4) |
169
+ | `--pay-address <addr>` | Address for receiving deposits | - |
170
+ | `--pay-token <ticker>` | Token to sell (enables primary market + withdrawal) | - |
171
+ | `--pay-rate <n>` | Sats per token for buy/withdraw | 1 |
172
+ | `--pay-chains <ids>` | Multi-chain deposits + AMM (e.g. "tbtc3,tbtc4") | - |
173
+ | `--mongo` | Enable MongoDB-backed /db/ route | false |
174
+ | `--mongo-url <url>` | MongoDB connection URL | mongodb://localhost:27017 |
175
+ | `--mongo-database <name>` | MongoDB database name | solid |
176
+ | `--webrtc` | Enable WebRTC signaling server | false |
177
+ | `--webrtc-path <path>` | WebRTC signaling WebSocket path | /.webrtc |
178
+ | `--tunnel` | Enable tunnel proxy (decentralized ngrok) | false |
179
+ | `--tunnel-path <path>` | Tunnel WebSocket path | /.tunnel |
180
+ | `-q, --quiet` | Suppress logs | false |
181
+
182
+ ### Environment Variables
183
+
184
+ All options can be set via environment variables with `JSS_` prefix:
185
+
186
+ ```bash
187
+ export JSS_PORT=8443
188
+ export JSS_SSL_KEY=/path/to/key.pem
189
+ export JSS_SSL_CERT=/path/to/cert.pem
190
+ export JSS_CONNEG=true
191
+ export JSS_SUBDOMAINS=true
192
+ export JSS_BASE_DOMAIN=example.com
193
+ export JSS_MASHLIB=true
194
+ export JSS_MASHLIB_MODULE=https://example.com/mashlib.js
195
+ export JSS_NOSTR=true
196
+ export JSS_INVITE_ONLY=true
197
+ export JSS_WEBID_TLS=true
198
+ export JSS_DEFAULT_QUOTA=100MB
199
+ export JSS_ACTIVITYPUB=true
200
+ export JSS_AP_USERNAME=alice
201
+ export JSS_PUBLIC=true
202
+ export JSS_READ_ONLY=true
203
+ export JSS_LIVE_RELOAD=true
204
+ export JSS_SOLIDOS_UI=true
205
+ export JSS_PAY=true
206
+ export JSS_PAY_COST=10
207
+ export JSS_PAY_ADDRESS=your-address
208
+ export JSS_PAY_TOKEN=PODS
209
+ export JSS_PAY_RATE=10
210
+ export JSS_MONGO=true
211
+ export JSS_MONGO_URL=mongodb://localhost:27017
212
+ export JSS_MONGO_DATABASE=solid
213
+ export JSS_WEBRTC=true
214
+ jss start
215
+ ```
216
+
217
+ ### Config File
218
+
219
+ Create `config.json`:
220
+
221
+ ```json
222
+ {
223
+ "port": 8443,
224
+ "root": "./data",
225
+ "sslKey": "./ssl/key.pem",
226
+ "sslCert": "./ssl/cert.pem",
227
+ "conneg": true,
228
+ "notifications": true
229
+ }
230
+ ```
231
+
232
+ Then: `jss start --config config.json`
233
+
234
+ ### Creating a Pod
235
+
236
+ ```bash
237
+ curl -X POST http://localhost:3000/.pods \
238
+ -H "Content-Type: application/json" \
239
+ -d '{"name": "alice"}'
240
+ ```
241
+
242
+ Response:
243
+ ```json
244
+ {
245
+ "name": "alice",
246
+ "webId": "http://localhost:3000/alice/#me",
247
+ "podUri": "http://localhost:3000/alice/",
248
+ "token": "eyJ..."
249
+ }
250
+ ```
251
+
252
+ ### Single-User Mode
253
+
254
+ For personal pod servers where only one user needs access:
255
+
256
+ ```bash
257
+ # Basic single-user mode (creates pod at /me/)
258
+ jss start --single-user --idp
259
+
260
+ # Custom username
261
+ jss start --single-user --single-user-name alice --idp
262
+
263
+ # Root-level pod (pod at /, WebID at /profile/card#me)
264
+ jss start --single-user --single-user-name '' --idp
265
+
266
+ # Via environment
267
+ JSS_SINGLE_USER=true jss start --idp
268
+ ```
269
+
270
+ **Features:**
271
+ - Pod auto-created on first startup with full structure (inbox, public, private, profile)
272
+ - Registration endpoint disabled (returns 403)
273
+ - Login still works for the single user
274
+ - Proper ACLs generated automatically
275
+
276
+
277
+ ## Invite-Only Registration
278
+
279
+ Control who can create accounts by requiring invite codes:
280
+
281
+ ```bash
282
+ jss start --idp --invite-only
283
+ ```
284
+
285
+ ### Managing Invite Codes
286
+
287
+ ```bash
288
+ # Create a single-use invite
289
+ jss invite create
290
+ # Created invite code: ABCD1234
291
+
292
+ # Create multi-use invite with note
293
+ jss invite create -u 5 -n "For team members"
294
+
295
+ # List all active invites
296
+ jss invite list
297
+ # CODE USES CREATED NOTE
298
+ # -------------------------------------------------------
299
+ # ABCD1234 0/1 2026-01-03
300
+ # EFGH5678 2/5 2026-01-03 For team members
301
+
302
+ # Revoke an invite
303
+ jss invite revoke ABCD1234
304
+ ```
305
+
306
+ ### How It Works
307
+
308
+ | Mode | Registration | Pod Creation |
309
+ |------|--------------|--------------|
310
+ | Open (default) | Anyone can register | Anyone can create pods |
311
+ | Invite-only | Requires valid invite code | Via registration only |
312
+
313
+ When `--invite-only` is enabled:
314
+ - The registration page shows an "Invite Code" field
315
+ - Invalid or expired codes are rejected with an error
316
+ - Each use decrements the invite's remaining uses
317
+ - Depleted invites are automatically removed
318
+
319
+ Invite codes are stored in `.server/invites.json` in your data directory.
320
+
321
+ ## Storage Quotas
322
+
323
+ Limit storage per pod to prevent abuse and manage resources:
324
+
325
+ ```bash
326
+ jss start --default-quota 50MB
327
+ ```
328
+
329
+ ### Managing Quotas
330
+
331
+ ```bash
332
+ # Set quota for a user (overrides default)
333
+ jss quota set alice 100MB
334
+
335
+ # Show quota info
336
+ jss quota show alice
337
+ # alice:
338
+ # Used: 12.5 MB
339
+ # Limit: 100 MB
340
+ # Free: 87.5 MB
341
+ # Usage: 12%
342
+
343
+ # Recalculate from actual disk usage
344
+ jss quota reconcile alice
345
+ ```
346
+
347
+ ### How It Works
348
+
349
+ - Quotas are tracked incrementally on PUT, POST, and DELETE operations
350
+ - When quota is exceeded, the server returns HTTP 507 Insufficient Storage
351
+ - Each pod stores its quota in `/{pod}/.quota.json`
352
+ - Use `reconcile` to fix quota drift from manual file changes
353
+
354
+ ### Size Formats
355
+
356
+ Supported formats: `50MB`, `1GB`, `500KB`, `1TB`
357
+
358
+ ## Storage Quotas
359
+
360
+ Limit storage per pod to prevent abuse and manage resources:
361
+
362
+ ```bash
363
+ jss start --default-quota 50MB
364
+ ```
365
+
366
+ ### Managing Quotas
367
+
368
+ ```bash
369
+ # Set quota for a user (overrides default)
370
+ jss quota set alice 100MB
371
+
372
+ # Show quota info
373
+ jss quota show alice
374
+ # alice:
375
+ # Used: 12.5 MB
376
+ # Limit: 100 MB
377
+ # Free: 87.5 MB
378
+ # Usage: 12%
379
+
380
+ # Recalculate from actual disk usage
381
+ jss quota reconcile alice
382
+ ```
383
+
384
+ ### How It Works
385
+
386
+ - Quotas are tracked incrementally on PUT, POST, and DELETE operations
387
+ - When quota is exceeded, the server returns HTTP 507 Insufficient Storage
388
+ - Each pod stores its quota in `/{pod}/.quota.json`
389
+ - Use `reconcile` to fix quota drift from manual file changes
390
+
391
+ ### Size Formats
392
+
393
+ Supported formats: `50MB`, `1GB`, `500KB`, `1TB`
394
+
395
+ ### Mashlib Data Browser
396
+
397
+ Enable the [SolidOS Mashlib](https://github.com/SolidOS/mashlib) data browser for RDF resources. Two modes are available:
398
+
399
+ **CDN Mode** (recommended for getting started):
400
+ ```bash
401
+ jss start --mashlib-cdn --conneg
402
+ ```
403
+ Loads mashlib from unpkg.com CDN. Zero footprint - no local files needed.
404
+
405
+ **Local Mode** (for production/offline):
406
+ ```bash
407
+ jss start --mashlib --conneg
408
+ ```
409
+ Serves mashlib from `src/mashlib-local/dist/`. Requires building mashlib locally:
410
+ ```bash
411
+ cd src/mashlib-local
412
+ npm install && npm run build
413
+ ```
414
+
415
+ **ES Module Mode** (for custom or next-gen mashlib builds):
416
+ ```bash
417
+ jss start --mashlib-module https://example.com/mashlib.js
418
+ ```
419
+ 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.
420
+
421
+ **How it works:**
422
+ 1. Browser requests `/alice/public/data.ttl` with `Accept: text/html`
423
+ 2. Server returns Mashlib HTML wrapper
424
+ 3. Mashlib fetches the actual data via content negotiation
425
+ 4. Mashlib renders an interactive, editable view
426
+
427
+ **Note:** Mashlib works best with `--conneg` enabled for Turtle support.
428
+
429
+ **Modern UI (SolidOS UI):**
430
+ ```bash
431
+ jss start --mashlib --solidos-ui --conneg
432
+ ```
433
+ 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:
434
+ - Modern file browser with breadcrumb navigation
435
+ - Profile, Contacts, Sharing, and Settings views
436
+ - Path-based URLs (browser URL reflects current resource)
437
+ - Responsive design for mobile devices
438
+
439
+ 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.
440
+
441
+ ### Profile Pages
442
+
443
+ Pod profiles (`/alice/`) use HTML with embedded JSON-LD data islands and are rendered using:
444
+ - [mashlib-jss](https://github.com/JavaScriptSolidServer/mashlib-jss) - A fork of mashlib with `getPod()` fix for path-based pods
445
+ - [solidos-lite](https://github.com/SolidOS/solidos-lite) - Parses JSON-LD data islands into the RDF store
446
+
447
+ This allows profiles to work without server-side content negotiation while still providing full SolidOS editing capabilities.
448
+
449
+ ### WebSocket Notifications
450
+
451
+ Enable real-time notifications for resource changes:
452
+
453
+ ```javascript
454
+ const server = createServer({ notifications: true });
455
+ ```
456
+
457
+ Clients discover the WebSocket URL via the `Updates-Via` header:
458
+
459
+ ```bash
460
+ curl -I http://localhost:3000/alice/public/
461
+ # Updates-Via: ws://localhost:3000/.notifications
462
+ ```
463
+
464
+ Protocol (solid-0.1, compatible with SolidOS):
465
+ ```
466
+ Server: protocol solid-0.1
467
+ Client: sub http://localhost:3000/alice/public/data.json
468
+ Server: ack http://localhost:3000/alice/public/data.json
469
+ Server: pub http://localhost:3000/alice/public/data.json (on change)
470
+ ```
471
+
@@ -0,0 +1,42 @@
1
+ ## MongoDB Storage (`/db/` Route)
2
+
3
+ Optional MongoDB-backed route for JSON-LD documents that need scale (social feeds, posts, follows). All other routes continue using the filesystem unchanged.
4
+
5
+ ```bash
6
+ # Install the optional MongoDB driver
7
+ npm install mongodb
8
+
9
+ # Start with MongoDB enabled
10
+ jss start --mongo --mongo-url mongodb://localhost:27017 --mongo-database solid
11
+ ```
12
+
13
+ ### Operations
14
+
15
+ ```bash
16
+ # Store a document
17
+ curl -X PUT http://localhost:3000/db/alice/notes/1 \
18
+ -H "Content-Type: application/ld+json" \
19
+ -H "Authorization: Bearer <token>" \
20
+ -d '{"@context": "https://schema.org/", "@type": "Note", "text": "Hello"}'
21
+
22
+ # Read it back
23
+ curl http://localhost:3000/db/alice/notes/1
24
+
25
+ # List container (derived from URI prefixes)
26
+ curl http://localhost:3000/db/alice/
27
+
28
+ # Delete
29
+ curl -X DELETE http://localhost:3000/db/alice/notes/1 \
30
+ -H "Authorization: Bearer <token>"
31
+ ```
32
+
33
+ ### How It Works
34
+
35
+ - `GET /db/:path` — retrieve a document by URI, or list a virtual container
36
+ - `PUT /db/:path` — create or update (upsert) a JSON-LD document
37
+ - `DELETE /db/:path` — remove a document
38
+ - Returns standard LDP headers (Link, ETag, WAC-Allow, CORS)
39
+ - Supports conditional requests (If-Match, If-None-Match)
40
+ - Container listings are computed from URI prefix queries — no directory management needed
41
+ - Auth: pod owner can write (`/db/{podName}/...`), reads are public
42
+ - MongoDB is an optional dependency — the server runs without it
@@ -0,0 +1,94 @@
1
+ ## HTTP 402 Paid Access
2
+
3
+ Monetize API endpoints with per-request satoshi payments. Resources under `/pay/*` require NIP-98 authentication and a positive balance.
4
+
5
+ ```bash
6
+ jss start --pay --pay-cost 10 --pay-address your-address --pay-token PODS --pay-rate 10
7
+ ```
8
+
9
+ ### Routes
10
+
11
+ | Method | Path | Description |
12
+ |--------|------|-------------|
13
+ | GET | `/pay/.info` | Public: cost, token info, chains, pool |
14
+ | GET | `/pay/.balance` | Check your balance (NIP-98 auth) |
15
+ | POST | `/pay/.deposit` | Deposit sats via TXO URI or MRC20 state proof |
16
+ | POST | `/pay/.buy` | Buy tokens with sat balance (requires `--pay-token`) |
17
+ | POST | `/pay/.withdraw` | Withdraw balance as portable tokens (requires `--pay-token`) |
18
+ | GET | `/pay/.offers` | List open sell orders (secondary market) |
19
+ | POST | `/pay/.sell` | Create a sell order (requires `--pay-token`) |
20
+ | POST | `/pay/.swap` | Execute a swap against a sell order |
21
+ | GET | `/pay/.pool` | AMM pool state (requires `--pay-chains`) |
22
+ | POST | `/pay/.pool` | AMM swap, add/remove liquidity |
23
+ | GET | `/pay/*` | Paid resource access (deducts balance) |
24
+
25
+ ### How It Works
26
+
27
+ 1. Authenticate with NIP-98 (Nostr HTTP Auth)
28
+ 2. Check balance at `/pay/.balance`
29
+ 3. Deposit sats by POSTing a TXO URI to `/pay/.deposit`
30
+ 4. Access paid resources — each request deducts the configured cost
31
+ 5. Optionally buy tokens (`/pay/.buy`) or withdraw as portable tokens (`/pay/.withdraw`)
32
+ 6. Balance tracked in a [Web Ledger](https://webledgers.org/) at `/.well-known/webledgers/webledgers.json`
33
+
34
+ ### Example
35
+
36
+ ```bash
37
+ # Check balance
38
+ curl -H "Authorization: Nostr <base64-event>" http://localhost:3000/pay/.balance
39
+
40
+ # Deposit (post a confirmed transaction output)
41
+ curl -X POST -H "Authorization: Nostr <base64-event>" \
42
+ http://localhost:3000/pay/.deposit \
43
+ -d "txid:vout"
44
+
45
+ # Access paid resource
46
+ curl -H "Authorization: Nostr <base64-event>" http://localhost:3000/pay/my-resource
47
+
48
+ # Buy tokens with sat balance
49
+ curl -X POST -H "Authorization: Nostr <base64-event>" \
50
+ -H "Content-Type: application/json" \
51
+ http://localhost:3000/pay/.buy \
52
+ -d '{"amount": 100}'
53
+
54
+ # Withdraw entire balance as portable tokens
55
+ curl -X POST -H "Authorization: Nostr <base64-event>" \
56
+ -H "Content-Type: application/json" \
57
+ http://localhost:3000/pay/.withdraw \
58
+ -d '{"all": true}'
59
+ ```
60
+
61
+ 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.
62
+
63
+ ### Secondary Market
64
+
65
+ 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.
66
+
67
+ ### Multi-Chain AMM
68
+
69
+ Enable multi-chain deposits and an automated market maker:
70
+
71
+ ```bash
72
+ jss start --pay --pay-chains "tbtc3,tbtc4"
73
+ ```
74
+
75
+ 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.
76
+
77
+ ```bash
78
+ # Add liquidity
79
+ curl -X POST -H "Authorization: Nostr <token>" \
80
+ -H "Content-Type: application/json" \
81
+ http://localhost:3000/pay/.pool \
82
+ -d '{"action": "add-liquidity", "tbtc3": 1000, "tbtc4": 5000}'
83
+
84
+ # Swap
85
+ curl -X POST -H "Authorization: Nostr <token>" \
86
+ -H "Content-Type: application/json" \
87
+ http://localhost:3000/pay/.pool \
88
+ -d '{"action": "swap", "sell": "tbtc3", "amount": 100}'
89
+
90
+ # Check pool state
91
+ curl http://localhost:3000/pay/.pool
92
+ ```
93
+
94
+ Supported chains: `btc`, `tbtc3`, `tbtc4`, `ltc`, `signet`.
@@ -0,0 +1,86 @@
1
+ ## remoteStorage
2
+
3
+ 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.
4
+
5
+ ```bash
6
+ jss start --activitypub --idp
7
+ ```
8
+
9
+ ### Discovery
10
+
11
+ remoteStorage clients discover the storage endpoint via WebFinger:
12
+
13
+ ```bash
14
+ curl "http://localhost:3000/.well-known/webfinger?resource=acct:me@localhost:3000"
15
+ ```
16
+
17
+ The response includes a `remotestorage` link relation pointing to `/storage/me/`.
18
+
19
+ ### Endpoints
20
+
21
+ | Method | Endpoint | Description |
22
+ |--------|----------|-------------|
23
+ | `GET` | `/storage/:user/*` | Read file or list folder (JSON-LD) |
24
+ | `HEAD` | `/storage/:user/*` | Get metadata (ETag, Content-Type, size) |
25
+ | `PUT` | `/storage/:user/*` | Write file (creates parent folders) |
26
+ | `DELETE` | `/storage/:user/*` | Delete file |
27
+
28
+ ### How It Works
29
+
30
+ - **Auth**: Bearer token via OAuth 2.0 (same flow as Mastodon clients)
31
+ - **Public folder**: `/storage/me/public/*` is readable without auth
32
+ - **Conditional requests**: If-Match, If-None-Match (uses shared ETag utilities)
33
+ - **Dotfile protection**: `.acl`, `.meta`, and other dotfiles are blocked
34
+ - **Read-only mode**: Respects `--read-only` flag
35
+ - **Streaming**: Large files are streamed, not buffered
36
+
37
+ ### Testing
38
+
39
+ ```bash
40
+ # Write a file (needs Bearer token from OAuth flow)
41
+ curl -X PUT http://localhost:3000/storage/me/documents/hello.txt \
42
+ -H "Authorization: Bearer YOUR_TOKEN" \
43
+ -H "Content-Type: text/plain" \
44
+ -d "Hello, remoteStorage!"
45
+
46
+ # Read it back
47
+ curl -H "Authorization: Bearer YOUR_TOKEN" \
48
+ http://localhost:3000/storage/me/documents/hello.txt
49
+
50
+ # List a folder
51
+ curl -H "Authorization: Bearer YOUR_TOKEN" \
52
+ http://localhost:3000/storage/me/documents/
53
+
54
+ # Read from public folder (no auth needed)
55
+ curl http://localhost:3000/storage/me/public/readme.txt
56
+ ```
57
+
58
+ ### Linking Nostr to WebID (did:nostr)
59
+
60
+ Bridge your Nostr identity to a Solid WebID for seamless authentication:
61
+
62
+ **Step 1:** Add your WebID to your Nostr profile (kind 0 event):
63
+ ```json
64
+ {
65
+ "name": "alice",
66
+ "alsoKnownAs": ["https://solid.social/alice/profile/card#me"]
67
+ }
68
+ ```
69
+
70
+ **Step 2:** Add the did:nostr link to your WebID profile:
71
+ ```json
72
+ {
73
+ "@id": "#me",
74
+ "owl:sameAs": "did:nostr:<your-64-char-hex-pubkey>"
75
+ }
76
+ ```
77
+
78
+ **How it works:**
79
+ 1. NIP-98 signature is verified (existing flow)
80
+ 2. DID document is fetched from `nostr.social/.well-known/did/nostr/<pubkey>.json`
81
+ 3. `alsoKnownAs` is checked for a WebID URL
82
+ 4. WebID profile is fetched and `owl:sameAs` verified
83
+ 5. If bidirectional link exists → authenticated as WebID
84
+
85
+ This enables Nostr users to access their Solid pods using existing NIP-07 browser extensions.
86
+