javascript-solid-server 0.0.80 → 0.0.82

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.
@@ -277,7 +277,50 @@
277
277
  "Bash(node index.js:*)",
278
278
  "Bash(timeout 3 npx lws-server@0.0.3:*)",
279
279
  "Bash(npx lws-server@0.0.3)",
280
- "Bash(node /tmp/jssd/verify-spacing.js:*)"
280
+ "Bash(node /tmp/jssd/verify-spacing.js:*)",
281
+ "Bash(gh run list:*)",
282
+ "Bash(pm2 startup:*)",
283
+ "Bash(npm run build:*)",
284
+ "Bash(npx esbuild:*)",
285
+ "Bash(gzip:*)",
286
+ "Bash(sort:*)",
287
+ "Bash(echo __NEW_LINE_496cf3994d374e5c__ echo '1. jsonld \\(277 KB\\) - used by:' grep -r \"from ['''']jsonld\" src/ --include=*.ts --include=*.js 2)",
288
+ "Bash(/dev/null __NEW_LINE_496cf3994d374e5c__ echo echo '2. readable-stream \\(180 KB\\) - used by:' grep -r readable-stream node_modules/n3/package.json node_modules/jsonld/package.json)",
289
+ "Bash(head -3 __NEW_LINE_496cf3994d374e5c__ echo echo '3. n3 \\(156 KB\\) - used by:' grep -r \"from ['''']n3\" src/ --include=*.ts --include=*.js 2)",
290
+ "Bash(/dev/null __NEW_LINE_496cf3994d374e5c__ echo echo '4. @xmldom/xmldom \\(136 KB\\) - used by:' grep -r xmldom src/ --include=*.ts --include=*.js 2)",
291
+ "Bash(/dev/null __NEW_LINE_496cf3994d374e5c__ echo echo '5. @frogcat/ttl2jsonld \\(135 KB\\) - used by:' grep -r ttl2jsonld src/ --include=*.ts --include=*.js)",
292
+ "Bash(echo \"\" __NEW_LINE_b9f7c85f1ada8a30__ echo \"1. jsonld \\(277 KB\\):\" grep -rn \"jsonld\" src/ --include=\"*.ts\" --include=\"*.js\")",
293
+ "Bash(head -5 __NEW_LINE_b9f7c85f1ada8a30__ echo \"\" echo \"2. n3 \\(156 KB\\):\" grep -rn \"from ''''n3''''\" src/ --include=\"*.ts\" --include=\"*.js\")",
294
+ "Bash(head -5 __NEW_LINE_b9f7c85f1ada8a30__ echo \"\" echo \"3. @xmldom \\(136 KB\\):\" grep -rn \"xmldom\" src/ --include=\"*.ts\" --include=\"*.js\")",
295
+ "Bash(head -5 __NEW_LINE_b9f7c85f1ada8a30__ echo \"\" echo \"4. @frogcat/ttl2jsonld \\(135 KB\\):\" grep -rn \"ttl2jsonld\" src/ --include=\"*.ts\" --include=\"*.js\")",
296
+ "Bash(head -5 __NEW_LINE_b9f7c85f1ada8a30__ echo \"\" echo \"5. cross-fetch \\(20 KB\\):\" grep -rn \"cross-fetch\" src/ --include=\"*.ts\" --include=\"*.js\")",
297
+ "Bash(head -5 __NEW_LINE_b9f7c85f1ada8a30__ echo \"\" echo \"6. solid-namespace \\(3 KB\\):\" grep -rn \"solid-namespace\" src/ --include=\"*.ts\" --include=\"*.js\")",
298
+ "Bash(echo __NEW_LINE_fbfd1802fc51eb60__ echo '1. jsonld \\(277 KB\\):' grep -n jsonld src/*.ts src/*.js)",
299
+ "Bash(grep -E \"import|require\" __NEW_LINE_fbfd1802fc51eb60__ echo echo '2. n3 \\(156 KB\\):' grep -n ''n3'' src/*.ts src/*.js)",
300
+ "Bash(grep -E \"import|require\" __NEW_LINE_fbfd1802fc51eb60__ echo echo '3. @xmldom \\(136 KB\\):' grep -n xmldom src/*.ts src/*.js)",
301
+ "Bash(grep -E \"import|require\" __NEW_LINE_fbfd1802fc51eb60__ echo echo '4. @frogcat/ttl2jsonld \\(135 KB\\):' grep -n ttl2jsonld src/*.ts src/*.js)",
302
+ "Bash(grep -E \"import|require\" __NEW_LINE_fbfd1802fc51eb60__ echo echo '5. cross-fetch \\(20 KB\\):' grep -n cross-fetch src/*.ts src/*.js)",
303
+ "Bash(grep -E \"import|require\" __NEW_LINE_fbfd1802fc51eb60__ echo echo '6. solid-namespace \\(3 KB\\):' grep -n solid-namespace src/*.ts src/*.js)",
304
+ "Bash(npm run build:all:*)",
305
+ "Bash(git status:*)",
306
+ "Bash(npm run lint-fix:*)",
307
+ "Bash(npm uninstall:*)",
308
+ "Bash(npm search:*)",
309
+ "Bash(npm run lint:*)",
310
+ "Bash(npm run typecheck:*)",
311
+ "Bash(du:*)",
312
+ "Bash(for pkg in activitystreams-pane chat-pane contacts-pane folder-pane issue-pane meeting-pane profile-pane source-pane pane-registry)",
313
+ "Bash(do du -sh node_modules/$pkg)",
314
+ "Bash(for pkg in solid-ui solid-logic react react-dom lodash core-js @inrupt marked dompurify)",
315
+ "Bash(npm why:*)",
316
+ "Bash(for pkg in activitystreams-pane chat-pane contacts-pane folder-pane issue-pane meeting-pane profile-pane source-pane)",
317
+ "Bash(wc -l echo 'Brings: mime-types \\(we already removed this pattern\\)' echo echo '=== profile-pane unique deps ===')",
318
+ "Bash(npm run build-prod:*)",
319
+ "Bash(ln:*)",
320
+ "WebFetch(domain:remotestorage.io)",
321
+ "Bash(gh issue create --title \"Feature: --public flag to skip WAC and allow open access\" --body \"$\\(cat << ''ENDOFFILE''\n## Summary\n\nAdd a `--public` flag that disables WAC \\(Web Access Control\\) checks, allowing unauthenticated read/write access to all resources. This enables JSS to be used as a simple file server similar to `npx serve`, but with REST write capabilities.\n\n**Difficulty**: 15/100 \n**Estimated Effort**: 2-4 hours \n**Dependencies**: None\n\n---\n\n## Motivation\n\nCurrently, JSS requires either:\n1. ACL files to grant access, or\n2. Authentication via Solid-OIDC\n\nThis makes it impossible to use JSS as a simple \"just serve this folder\" tool like `npx serve`. The `--public` flag would enable:\n\n1. **Quick local development** - No auth setup needed\n2. **Simple file sharing** - LAN file server with write support\n3. **jsserve wrapper** - Foundation for `npx jsserve` \\(see future issue\\)\n4. **WebDAV alternative** - Simple REST-based file server\n5. **Testing/demos** - Quick Solid server without auth complexity\n\n---\n\n## Proposed Implementation\n\n### CLI Flag\n\n```bash\njss start --public # Open read/write, no auth\njss start --public --read-only # Open read, no writes \\(like npx serve\\)\n```\n\n### Environment Variable\n\n```bash\nJSS_PUBLIC=true jss start\n```\n\n### Config File\n\n```json\n{\n \"public\": true\n}\n```\n\n---\n\n## Implementation Details\n\n### 1. Add flag to CLI \\(`bin/jss.js`\\)\n\n```javascript\n.option\\(''--public'', ''Allow unauthenticated access to all resources \\(disables WAC\\)''\\)\n.option\\(''--read-only'', ''Disable PUT/DELETE methods \\(read-only mode\\)''\\)\n```\n\n### 2. Add to config \\(`src/config.js`\\)\n\n```javascript\nconst DEFAULTS = {\n // ... existing ...\n public: false,\n readOnly: false,\n};\n\n// Environment variable mapping\nif \\(process.env.JSS_PUBLIC\\) {\n config.public = process.env.JSS_PUBLIC === ''true'';\n}\nif \\(process.env.JSS_READ_ONLY\\) {\n config.readOnly = process.env.JSS_READ_ONLY === ''true'';\n}\n```\n\n### 3. Skip WAC when public \\(`src/auth/middleware.js`\\)\n\n```javascript\nexport async function authorize\\(request, reply\\) {\n // Public mode - skip all auth/WAC checks\n if \\(request.config?.public\\) {\n return; // Allow request to proceed\n }\n \n // ... existing WAC logic ...\n}\n```\n\n### 4. Block writes when read-only \\(`src/handlers/resource.js`, `src/handlers/container.js`\\)\n\n```javascript\n// At start of PUT/DELETE handlers\nif \\(request.config?.readOnly\\) {\n return reply.code\\(405\\).send\\({ \n error: ''Method Not Allowed'',\n message: ''Server is in read-only mode''\n }\\);\n}\n```\n\n---\n\n## Behavior Matrix\n\n| Flag Combination | GET | PUT/DELETE | Auth Required |\n|------------------|-----|------------|---------------|\n| \\(default\\) | ACL | ACL | Yes \\(if ACL requires\\) |\n| `--public` | ✅ Allow | ✅ Allow | No |\n| `--public --read-only` | ✅ Allow | ❌ Block | No |\n| `--read-only` \\(no public\\) | ACL | ❌ Block | Yes |\n\n---\n\n## Security Considerations\n\n### Warning on Startup\n\nWhen `--public` is enabled, show a clear warning:\n\n```\n⚠️ WARNING: Server running in PUBLIC mode\n All files are readable and writable without authentication.\n Do not use in production or expose to the internet.\n```\n\n### Binding to localhost by default?\n\nConsider: When `--public` is set, should the default host be `localhost` instead of `0.0.0.0`?\n\n```javascript\nif \\(config.public && !explicitHostSet\\) {\n config.host = ''localhost''; // Safer default for public mode\n}\n```\n\nUser can override with `--public --host 0.0.0.0` if they explicitly want network access.\n\n---\n\n## Examples\n\n### Local Development\n```bash\n# Quick Solid-compatible file server for development\njss start --public --port 3000 --root ./test-data\n```\n\n### Read-Only File Sharing\n```bash\n# Share files on LAN, no writes allowed\njss start --public --read-only --host 0.0.0.0 --root ~/shared\n```\n\n### Testing Solid Apps\n```bash\n# Test app without auth complexity\njss start --public --root ./fixtures\nnpm test\n```\n\n---\n\n## Files to Modify\n\n| File | Changes |\n|------|---------|\n| `bin/jss.js` | Add `--public` and `--read-only` options \\(~5 LOC\\) |\n| `src/config.js` | Add defaults and env var mapping \\(~10 LOC\\) |\n| `src/auth/middleware.js` | Skip WAC when public \\(~5 LOC\\) |\n| `src/handlers/resource.js` | Block writes when read-only \\(~5 LOC\\) |\n| `src/handlers/container.js` | Block writes when read-only \\(~5 LOC\\) |\n| **Total** | **~30 LOC** |\n\n---\n\n## Testing\n\n```javascript\ndescribe\\(''--public flag'', \\(\\) => {\n it\\(''should allow unauthenticated GET'', async \\(\\) => {\n const server = await createServer\\({ public: true, root: tmpDir }\\);\n const res = await request\\(server\\).get\\(''/file.txt''\\);\n expect\\(res.status\\).toBe\\(200\\);\n }\\);\n\n it\\(''should allow unauthenticated PUT'', async \\(\\) => {\n const server = await createServer\\({ public: true, root: tmpDir }\\);\n const res = await request\\(server\\)\n .put\\(''/new-file.txt''\\)\n .send\\(''content''\\);\n expect\\(res.status\\).toBe\\(201\\);\n }\\);\n\n it\\(''should block PUT when read-only'', async \\(\\) => {\n const server = await createServer\\({ public: true, readOnly: true, root: tmpDir }\\);\n const res = await request\\(server\\)\n .put\\(''/new-file.txt''\\)\n .send\\(''content''\\);\n expect\\(res.status\\).toBe\\(405\\);\n }\\);\n}\\);\n```\n\n---\n\n## Related Issues\n\n- Future: `jsserve` package \\(thin wrapper using this flag\\)\n- #100 - Production Readiness \\(this is a dev/convenience feature\\)\n\n---\n\n## Open Questions\n\n1. Should `--public` default to `localhost` binding for safety?\n2. Should there be a `--public-read` \\(read-only public\\) shorthand?\n3. Should `--public` disable IdP/login UI entirely, or just make it optional?\nENDOFFILE\n\\)\")",
322
+ "Bash(npx serve --help:*)",
323
+ "Bash(npm exec serve:*)"
281
324
  ]
282
325
  }
283
326
  }
package/README.md CHANGED
@@ -1047,7 +1047,7 @@ Minimal dependencies for a fast, secure server:
1047
1047
  - **oidc-provider** - OpenID Connect Identity Provider (only when IdP enabled)
1048
1048
  - **bcryptjs** - Password hashing (only when IdP enabled)
1049
1049
  - **microfed** - ActivityPub primitives (only when activitypub enabled)
1050
- - **better-sqlite3** - SQLite storage for federation data
1050
+ - **sql.js** - SQLite storage for federation data (WASM, cross-platform)
1051
1051
 
1052
1052
  ## License
1053
1053
 
package/bin/jss.js CHANGED
@@ -76,6 +76,8 @@ program
76
76
  .option('--single-user-name <name>', 'Username for single-user mode (default: me)')
77
77
  .option('--webid-tls', 'Enable WebID-TLS client certificate authentication')
78
78
  .option('--no-webid-tls', 'Disable WebID-TLS authentication')
79
+ .option('--public', 'Allow unauthenticated access (skip WAC, open read/write)')
80
+ .option('--read-only', 'Disable PUT/DELETE/PATCH methods (read-only mode)')
79
81
  .option('-q, --quiet', 'Suppress log output')
80
82
  .option('--print-config', 'Print configuration and exit')
81
83
  .action(async (options) => {
@@ -131,6 +133,8 @@ program
131
133
  webidTls: config.webidTls,
132
134
  singleUser: config.singleUser,
133
135
  singleUserName: config.singleUserName,
136
+ public: config.public,
137
+ readOnly: config.readOnly,
134
138
  });
135
139
 
136
140
  await server.listen({ port: config.port, host: config.host });
@@ -156,6 +160,16 @@ program
156
160
  if (config.singleUser) console.log(` Single-user: ${config.singleUserName || 'me'} (registration disabled)`);
157
161
  else if (config.inviteOnly) console.log(' Registration: invite-only');
158
162
  if (config.webidTls) console.log(' WebID-TLS: enabled (client certificate auth)');
163
+ if (config.public) {
164
+ console.log('');
165
+ console.log(' ⚠️ WARNING: PUBLIC MODE ENABLED');
166
+ console.log(' All files are accessible without authentication.');
167
+ if (!config.readOnly) {
168
+ console.log(' Anyone can read, write, and delete files.');
169
+ }
170
+ console.log(' Do not expose to the internet!');
171
+ }
172
+ if (config.readOnly) console.log(' Read-only: enabled (PUT/DELETE/PATCH disabled)');
159
173
  console.log('\n Press Ctrl+C to stop\n');
160
174
  }
161
175
 
@@ -0,0 +1,120 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>fonstr - Your Nostr Relay</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
11
+ background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
12
+ color: white;
13
+ min-height: 100vh;
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ padding: 2rem;
18
+ }
19
+ .container {
20
+ text-align: center;
21
+ max-width: 600px;
22
+ }
23
+ h1 {
24
+ font-size: 3rem;
25
+ margin-bottom: 0.5rem;
26
+ }
27
+ .emoji {
28
+ font-size: 4rem;
29
+ margin-bottom: 1rem;
30
+ }
31
+ p {
32
+ font-size: 1.25rem;
33
+ opacity: 0.95;
34
+ margin-bottom: 2rem;
35
+ line-height: 1.6;
36
+ }
37
+ .relay-info {
38
+ background: rgba(255, 255, 255, 0.2);
39
+ backdrop-filter: blur(10px);
40
+ border-radius: 1rem;
41
+ padding: 2rem;
42
+ margin: 2rem 0;
43
+ }
44
+ code {
45
+ background: rgba(255, 255, 255, 0.3);
46
+ padding: 0.5rem 1rem;
47
+ border-radius: 0.5rem;
48
+ font-size: 1.1rem;
49
+ display: inline-block;
50
+ margin: 0.5rem 0;
51
+ font-family: 'Monaco', 'Menlo', monospace;
52
+ }
53
+ .stats {
54
+ display: grid;
55
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
56
+ gap: 1rem;
57
+ margin-top: 2rem;
58
+ }
59
+ .stat {
60
+ background: rgba(255, 255, 255, 0.15);
61
+ padding: 1rem;
62
+ border-radius: 0.5rem;
63
+ }
64
+ .stat-label {
65
+ font-size: 0.9rem;
66
+ opacity: 0.8;
67
+ }
68
+ .stat-value {
69
+ font-size: 1.5rem;
70
+ font-weight: 700;
71
+ margin-top: 0.25rem;
72
+ }
73
+ a {
74
+ color: white;
75
+ text-decoration: none;
76
+ border-bottom: 2px solid rgba(255, 255, 255, 0.5);
77
+ transition: border-color 0.2s;
78
+ }
79
+ a:hover {
80
+ border-color: white;
81
+ }
82
+ </style>
83
+ </head>
84
+ <body>
85
+ <div class="container">
86
+ <div class="emoji">⚡</div>
87
+ <h1>fonstr</h1>
88
+ <p>Your Nostr relay is running!</p>
89
+
90
+ <div class="relay-info">
91
+ <p style="font-size: 1rem; margin-bottom: 1rem; opacity: 0.9;">Connect to your relay:</p>
92
+ <code>ws://localhost:4444/relay</code>
93
+
94
+ <div class="stats">
95
+ <div class="stat">
96
+ <div class="stat-label">Status</div>
97
+ <div class="stat-value">✓ Online</div>
98
+ </div>
99
+ <div class="stat">
100
+ <div class="stat-label">Protocol</div>
101
+ <div class="stat-value">NIP-01</div>
102
+ </div>
103
+ <div class="stat">
104
+ <div class="stat-label">Port</div>
105
+ <div class="stat-value">4444</div>
106
+ </div>
107
+ </div>
108
+ </div>
109
+
110
+ <p style="font-size: 1rem;">
111
+ Add this relay to your favorite Nostr client and start using it!<br>
112
+ <a href="https://fonstr.com" target="_blank">Learn more about fonstr</a>
113
+ </p>
114
+
115
+ <p style="font-size: 0.9rem; opacity: 0.7; margin-top: 2rem;">
116
+ Replace this page by editing <code style="font-size: 0.8rem;">index.html</code> in your data directory
117
+ </p>
118
+ </div>
119
+ </body>
120
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "javascript-solid-server",
3
- "version": "0.0.80",
3
+ "version": "0.0.82",
4
4
  "description": "A minimal, fast Solid server",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -28,7 +28,6 @@
28
28
  "@fastify/websocket": "^8.3.1",
29
29
  "@simplewebauthn/server": "^13.2.2",
30
30
  "bcryptjs": "^3.0.3",
31
- "better-sqlite3": "^12.5.0",
32
31
  "commander": "^14.0.2",
33
32
  "fastify": "^4.29.1",
34
33
  "fs-extra": "^11.2.0",
package/src/ap/store.js CHANGED
@@ -2,8 +2,8 @@
2
2
  * ActivityPub SQLite Storage
3
3
  * Persistence layer for federation data
4
4
  *
5
- * Uses better-sqlite3 when available (native, fast)
6
- * Falls back to sql.js on Android/platforms without native builds
5
+ * Uses sql.js (WASM) for cross-platform compatibility
6
+ * Works on Android/Termux, Windows, and all platforms
7
7
  */
8
8
 
9
9
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
@@ -11,7 +11,6 @@ import { dirname } from 'path'
11
11
 
12
12
  let db = null
13
13
  let dbPath = null
14
- let usingSqlJs = false
15
14
 
16
15
  // SQL schema
17
16
  const SCHEMA = `
@@ -59,7 +58,7 @@ const SCHEMA = `
59
58
 
60
59
  /**
61
60
  * Initialize the database
62
- * Tries better-sqlite3 first, falls back to sql.js
61
+ * Uses sql.js (WASM) for cross-platform compatibility
63
62
  * @param {string} path - Path to SQLite file
64
63
  */
65
64
  export async function initStore(path = 'data/activitypub.db') {
@@ -71,43 +70,31 @@ export async function initStore(path = 'data/activitypub.db') {
71
70
 
72
71
  dbPath = path
73
72
 
74
- // Try better-sqlite3 first (fast, native)
75
- try {
76
- const Database = (await import('better-sqlite3')).default
77
- db = new Database(path)
78
- db.exec(SCHEMA)
79
- usingSqlJs = false
80
- return db
81
- } catch (e) {
82
- // Fall back to sql.js (WASM, works everywhere)
83
- console.log('ActivityPub: Using sql.js (WASM) for SQLite storage')
84
-
85
- const initSqlJs = (await import('sql.js')).default
86
- const SQL = await initSqlJs()
87
-
88
- // Load existing database if it exists
89
- if (existsSync(path)) {
90
- const buffer = readFileSync(path)
91
- db = new SQL.Database(buffer)
92
- } else {
93
- db = new SQL.Database()
94
- }
95
-
96
- db.run(SCHEMA)
97
- usingSqlJs = true
98
-
99
- // Save initial database
100
- saveDatabase()
101
-
102
- return db
73
+ // Use sql.js (WASM, works everywhere)
74
+ const initSqlJs = (await import('sql.js')).default
75
+ const SQL = await initSqlJs()
76
+
77
+ // Load existing database if it exists
78
+ if (existsSync(path)) {
79
+ const buffer = readFileSync(path)
80
+ db = new SQL.Database(buffer)
81
+ } else {
82
+ db = new SQL.Database()
103
83
  }
84
+
85
+ db.run(SCHEMA)
86
+
87
+ // Save initial database
88
+ saveDatabase()
89
+
90
+ return db
104
91
  }
105
92
 
106
93
  /**
107
94
  * Save sql.js database to disk
108
95
  */
109
96
  function saveDatabase() {
110
- if (usingSqlJs && db && dbPath) {
97
+ if (db && dbPath) {
111
98
  const data = db.export()
112
99
  const buffer = Buffer.from(data)
113
100
  writeFileSync(dbPath, buffer)
@@ -124,45 +111,33 @@ export function getStore() {
124
111
  return db
125
112
  }
126
113
 
127
- // Helper to run prepared statements across both implementations
114
+ // Helper functions for sql.js API
128
115
  function runStmt(sql, params = []) {
129
- if (usingSqlJs) {
130
- db.run(sql, params)
131
- saveDatabase()
132
- } else {
133
- db.prepare(sql).run(...params)
134
- }
116
+ db.run(sql, params)
117
+ saveDatabase()
135
118
  }
136
119
 
137
120
  function getOne(sql, params = []) {
138
- if (usingSqlJs) {
139
- const stmt = db.prepare(sql)
140
- stmt.bind(params)
141
- if (stmt.step()) {
142
- const row = stmt.getAsObject()
143
- stmt.free()
144
- return row
145
- }
121
+ const stmt = db.prepare(sql)
122
+ stmt.bind(params)
123
+ if (stmt.step()) {
124
+ const row = stmt.getAsObject()
146
125
  stmt.free()
147
- return null
148
- } else {
149
- return db.prepare(sql).get(...params)
126
+ return row
150
127
  }
128
+ stmt.free()
129
+ return null
151
130
  }
152
131
 
153
132
  function getAll(sql, params = []) {
154
- if (usingSqlJs) {
155
- const results = []
156
- const stmt = db.prepare(sql)
157
- stmt.bind(params)
158
- while (stmt.step()) {
159
- results.push(stmt.getAsObject())
160
- }
161
- stmt.free()
162
- return results
163
- } else {
164
- return db.prepare(sql).all(...params)
133
+ const results = []
134
+ const stmt = db.prepare(sql)
135
+ stmt.bind(params)
136
+ while (stmt.step()) {
137
+ results.push(stmt.getAsObject())
165
138
  }
139
+ stmt.free()
140
+ return results
166
141
  }
167
142
 
168
143
  // Followers
@@ -28,6 +28,12 @@ export async function authorize(request, reply, options = {}) {
28
28
  return { authorized: true, webId: null, wacAllow: 'user="read write append control", public="read write append"', authError: null };
29
29
  }
30
30
 
31
+ // Public mode - skip all WAC checks, allow unauthenticated access
32
+ if (request.config?.public) {
33
+ const modes = request.config?.readOnly ? 'read' : 'read write append';
34
+ return { authorized: true, webId: null, wacAllow: `public="${modes}"`, authError: null };
35
+ }
36
+
31
37
  // Get WebID from token (supports both simple and Solid-OIDC tokens)
32
38
  const { webId, error: authError } = await getWebIdFromRequestAsync(request);
33
39
 
package/src/config.js CHANGED
@@ -73,6 +73,12 @@ export const defaults = {
73
73
  // Storage quota (bytes) - 50MB default
74
74
  defaultQuota: 50 * 1024 * 1024,
75
75
 
76
+ // Public mode - skip WAC, allow unauthenticated access
77
+ public: false,
78
+
79
+ // Read-only mode - disable PUT/DELETE/PATCH
80
+ readOnly: false,
81
+
76
82
  // Logging
77
83
  logger: true,
78
84
  quiet: false,
@@ -117,6 +123,8 @@ const envMap = {
117
123
  JSS_SINGLE_USER_NAME: 'singleUserName',
118
124
  JSS_WEBID_TLS: 'webidTls',
119
125
  JSS_DEFAULT_QUOTA: 'defaultQuota',
126
+ JSS_PUBLIC: 'public',
127
+ JSS_READ_ONLY: 'readOnly',
120
128
  };
121
129
 
122
130
  /**
@@ -25,6 +25,11 @@ function getRequestPaths(request) {
25
25
  * Handle POST request to container (create new resource)
26
26
  */
27
27
  export async function handlePost(request, reply) {
28
+ // Read-only mode - block all writes
29
+ if (request.config?.readOnly) {
30
+ return reply.code(405).send({ error: 'Method Not Allowed', message: 'Server is in read-only mode' });
31
+ }
32
+
28
33
  const { urlPath, storagePath } = getRequestPaths(request);
29
34
 
30
35
  // Ensure target is a container
@@ -233,6 +238,11 @@ export async function createPodStructure(name, webId, podUri, issuer, defaultQuo
233
238
  * /{name}/settings/privateTypeIndex
234
239
  */
235
240
  export async function handleCreatePod(request, reply) {
241
+ // Read-only mode - block pod creation
242
+ if (request.config?.readOnly) {
243
+ return reply.code(405).send({ error: 'Method Not Allowed', message: 'Server is in read-only mode' });
244
+ }
245
+
236
246
  const { name, email, password } = request.body || {};
237
247
  const idpEnabled = request.idpEnabled;
238
248
 
@@ -511,6 +511,11 @@ export async function handleHead(request, reply) {
511
511
  * Handle PUT request
512
512
  */
513
513
  export async function handlePut(request, reply) {
514
+ // Read-only mode - block all writes
515
+ if (request.config?.readOnly) {
516
+ return reply.code(405).send({ error: 'Method Not Allowed', message: 'Server is in read-only mode' });
517
+ }
518
+
514
519
  const { urlPath, storagePath, resourceUrl } = getRequestPaths(request);
515
520
  const connegEnabled = request.connegEnabled || false;
516
521
 
@@ -644,6 +649,11 @@ export async function handlePut(request, reply) {
644
649
  * Handle DELETE request
645
650
  */
646
651
  export async function handleDelete(request, reply) {
652
+ // Read-only mode - block all writes
653
+ if (request.config?.readOnly) {
654
+ return reply.code(405).send({ error: 'Method Not Allowed', message: 'Server is in read-only mode' });
655
+ }
656
+
647
657
  const { storagePath, resourceUrl } = getRequestPaths(request);
648
658
 
649
659
  // Check if resource exists and get current ETag
@@ -716,6 +726,11 @@ export async function handleOptions(request, reply) {
716
726
  * Supports N3 Patch format (text/n3) and SPARQL Update for updating RDF resources
717
727
  */
718
728
  export async function handlePatch(request, reply) {
729
+ // Read-only mode - block all writes
730
+ if (request.config?.readOnly) {
731
+ return reply.code(405).send({ error: 'Method Not Allowed', message: 'Server is in read-only mode' });
732
+ }
733
+
719
734
  const { urlPath, storagePath, resourceUrl } = getRequestPaths(request);
720
735
 
721
736
  // Don't allow PATCH to containers
package/src/server.js CHANGED
@@ -135,6 +135,7 @@ export function createServer(options = {}) {
135
135
  fastify.decorateRequest('mashlibVersion', null);
136
136
  fastify.decorateRequest('solidosUiEnabled', null);
137
137
  fastify.decorateRequest('defaultQuota', null);
138
+ fastify.decorateRequest('config', null);
138
139
  fastify.addHook('onRequest', async (request) => {
139
140
  request.connegEnabled = connegEnabled;
140
141
  request.notificationsEnabled = notificationsEnabled;
@@ -146,6 +147,7 @@ export function createServer(options = {}) {
146
147
  request.mashlibVersion = mashlibVersion;
147
148
  request.solidosUiEnabled = solidosUiEnabled;
148
149
  request.defaultQuota = defaultQuota;
150
+ request.config = { public: options.public, readOnly: options.readOnly };
149
151
 
150
152
  // Extract pod name from subdomain if enabled
151
153
  if (subdomainsEnabled && baseDomain) {