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.
- package/README.md +56 -1349
- package/docs/activitypub.md +109 -0
- package/docs/architecture.md +165 -0
- package/docs/authentication.md +157 -0
- package/docs/configuration.md +471 -0
- package/docs/mongodb.md +42 -0
- package/docs/payments.md +94 -0
- package/docs/remotestorage.md +86 -0
- package/docs/security.md +96 -0
- package/docs/webrtc.md +66 -0
- package/package.json +1 -1
- package/src/patch/n3-patch.js +40 -23
- package/test/patch.test.js +61 -0
package/README.md
CHANGED
|
@@ -8,1212 +8,97 @@ A minimal, fast, JSON-LD native Solid server.
|
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
- **remoteStorage
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
30
|
-
- **
|
|
31
|
-
- **
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
270
|
-
curl http://localhost:3000/alice/
|
|
57
|
+
# Read
|
|
58
|
+
curl http://localhost:3000/alice/public/
|
|
271
59
|
|
|
272
|
-
# Write
|
|
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
|
-
|
|
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
|
-
|
|
70
|
+
pkg install nodejs git
|
|
71
|
+
npm install -g javascript-solid-server
|
|
72
|
+
jss start --port 8080 --nostr --git
|
|
1184
73
|
```
|
|
1185
74
|
|
|
1186
|
-
|
|
75
|
+
## CLI Reference
|
|
1187
76
|
|
|
1188
77
|
```bash
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
jss
|
|
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
|
-
|
|
84
|
+
Key options: `--port`, `--idp`, `--conneg`, `--mashlib`, `--git`, `--nostr`, `--activitypub`, `--webrtc`, `--tunnel`, `--mongo`, `--pay`, `--public`, `--single-user`
|
|
1203
85
|
|
|
1204
|
-
|
|
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
|
-
|
|
88
|
+
## Documentation
|
|
1211
89
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
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.
|