Philosophy: JSON-LD First
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:
- Stores everything as JSON-LD - No RDF parsing overhead for standard operations
- Serves JSON-LD by default - Modern web applications can consume responses directly
- Content negotiation is optional - Enable Turtle support with
{ conneg: true }when needed - Fast by design - Skip the RDF parsing tax when you don't need it
Why JSON-LD First?
- Performance: JSON parsing is native to JavaScript - no external RDF libraries needed for basic operations
- Simplicity: JSON-LD is valid JSON - works with any JSON tooling
- Web-native: Browsers and web apps understand JSON natively
- Semantic web ready: JSON-LD is a W3C standard RDF serialization
When to Enable Content Negotiation
Enable conneg: true when:
- Interoperating with Turtle-based Solid apps
- Serving data to legacy Solid clients
- Running conformance tests that require Turtle support
import { createServer } from './src/server.js'; // Default: JSON-LD only (fast) const server = createServer(); // With Turtle support (for interoperability) const serverWithConneg = createServer({ conneg: true });
createServer({ logger: true, // Enable Fastify logging (default: true) conneg: false, // Enable content negotiation (default: false) notifications: false, // Enable WebSocket notifications (default: false) subdomains: false, // Enable subdomain-based pods (default: false) baseDomain: null, // Base domain for subdomains (e.g., "example.com") mashlib: false, // Enable Mashlib data browser - local mode (default: false) mashlibCdn: false, // Enable Mashlib data browser - CDN mode (default: false) mashlibVersion: '2.0.0', // Mashlib version for CDN mode });
Mashlib Data Browser
Enable the SolidOS Mashlib data browser for RDF resources. Two modes are available:
CDN Mode (recommended for getting started):
jss start --mashlib-cdn --conneg
Loads mashlib from unpkg.com CDN. Zero footprint - no local files needed.
Local Mode (for production/offline):
jss start --mashlib --conneg
Serves mashlib from src/mashlib-local/dist/. Requires building mashlib locally:
cd src/mashlib-local npm install && npm run build
ES Module Mode (for custom or next-gen mashlib builds):
jss start --mashlib-module https://example.com/mashlib.js
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.
How it works:
- Browser requests
/alice/public/data.ttlwithAccept: text/html - Server returns Mashlib HTML wrapper
- Mashlib fetches the actual data via content negotiation
- Mashlib renders an interactive, editable view
Note: Mashlib works best with --conneg enabled for Turtle support.
Modern UI (SolidOS UI):
jss start --mashlib --solidos-ui --conneg
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:
- Modern file browser with breadcrumb navigation
- Profile, Contacts, Sharing, and Settings views
- Path-based URLs (browser URL reflects current resource)
- Responsive design for mobile devices
Requires solidos-ui dist files in src/mashlib-local/dist/solidos-ui/. See solidos-ui for details.
Profile Pages
Pod profiles (/alice/) use HTML with embedded JSON-LD data islands and are rendered using:
- mashlib-jss - A fork of mashlib with
getPod()fix for path-based pods - solidos-lite - Parses JSON-LD data islands into the RDF store
This allows profiles to work without server-side content negotiation while still providing full SolidOS editing capabilities.
WebSocket Notifications
Enable real-time notifications for resource changes:
const server = createServer({ notifications: true });
Clients discover the WebSocket URL via the Updates-Via header:
curl -I http://localhost:3000/alice/public/
# Updates-Via: ws://localhost:3000/.notificationsProtocol (solid-0.1, compatible with SolidOS):
Server: protocol solid-0.1
Client: sub http://localhost:3000/alice/public/data.json
Server: ack http://localhost:3000/alice/public/data.json
Server: pub http://localhost:3000/alice/public/data.json (on change)
CLI Start Options
Start Options
| Option | Description | Default |
|---|---|---|
-p, --port <n> |
Port to listen on | 3000 |
-h, --host <addr> |
Host to bind to | 0.0.0.0 |
-r, --root <path> |
Data directory | ./data |
-c, --config <file> |
Config file path | - |
--ssl-key <path> |
SSL private key (PEM) | - |
--ssl-cert <path> |
SSL certificate (PEM) | - |
--conneg |
Enable Turtle support | false |
--notifications |
Enable WebSocket | false |
--idp |
Enable built-in IdP | false |
--idp-issuer <url> |
IdP issuer URL | (auto) |
--subdomains |
Enable subdomain-based pods | false |
--base-domain <domain> |
Base domain for subdomains | - |
--mashlib |
Enable Mashlib (local mode) | false |
--mashlib-cdn |
Enable Mashlib (CDN mode) | false |
--mashlib-module <url> |
Enable ES module data browser from a URL | - |
--mashlib-version <ver> |
Mashlib CDN version | 2.0.0 |
--solidos-ui |
Enable modern SolidOS UI (requires --mashlib) | false |
--git |
Enable Git HTTP backend | false |
--nostr |
Enable Nostr relay | false |
--nostr-path <path> |
Nostr relay WebSocket path | /relay |
--nostr-max-events <n> |
Max events in relay memory | 1000 |
--invite-only |
Require invite code for registration | false |
--webid-tls |
Enable WebID-TLS client certificate auth | false |
--default-quota <size> |
Default storage quota per pod (e.g., 50MB) | 50MB |
--activitypub |
Enable ActivityPub federation | false |
--ap-username <name> |
ActivityPub username | me |
--ap-display-name <name> |
ActivityPub display name | (username) |
--ap-summary <text> |
ActivityPub bio/summary | - |
--ap-nostr-pubkey <hex> |
Nostr pubkey for identity linking | - |
--public |
Allow unauthenticated access (skip WAC) | false |
--read-only |
Disable PUT/DELETE/PATCH methods | false |
--live-reload |
Auto-refresh browser on file changes | false |
--pay |
Enable HTTP 402 paid access for /pay/* | false |
--pay-cost <n> |
Cost per request in satoshis | 1 |
--pay-mempool-url <url> |
Mempool API URL for deposit verification | (testnet4) |
--pay-address <addr> |
Address for receiving deposits | - |
--pay-token <ticker> |
Token to sell (enables primary market + withdrawal) | - |
--pay-rate <n> |
Sats per token for buy/withdraw | 1 |
--pay-chains <ids> |
Multi-chain deposits + AMM (e.g. "tbtc3,tbtc4") | - |
--mongo |
Enable MongoDB-backed /db/ route | false |
--mongo-url <url> |
MongoDB connection URL | mongodb://localhost:27017 |
--mongo-database <name> |
MongoDB database name | solid |
--webrtc |
Enable WebRTC signaling server | false |
--webrtc-path <path> |
WebRTC signaling WebSocket path | /.webrtc |
--tunnel |
Enable tunnel proxy (decentralized ngrok) | false |
--tunnel-path <path> |
Tunnel WebSocket path | /.tunnel |
--terminal |
Enable WebSocket shell at /.terminal |
false |
-q, --quiet |
Suppress logs | false |
Environment Variables
All options can be set via environment variables with JSS_ prefix:
export JSS_PORT=8443 export JSS_SSL_KEY=/path/to/key.pem export JSS_SSL_CERT=/path/to/cert.pem export JSS_CONNEG=true export JSS_SUBDOMAINS=true export JSS_BASE_DOMAIN=example.com export JSS_MASHLIB=true export JSS_MASHLIB_MODULE=https://example.com/mashlib.js export JSS_NOSTR=true export JSS_INVITE_ONLY=true export JSS_WEBID_TLS=true export JSS_DEFAULT_QUOTA=100MB export JSS_ACTIVITYPUB=true export JSS_AP_USERNAME=alice export JSS_PUBLIC=true export JSS_READ_ONLY=true export JSS_LIVE_RELOAD=true export JSS_SOLIDOS_UI=true export JSS_PAY=true export JSS_PAY_COST=10 export JSS_PAY_ADDRESS=your-address export JSS_PAY_TOKEN=PODS export JSS_PAY_RATE=10 export JSS_MONGO=true export JSS_MONGO_URL=mongodb://localhost:27017 export JSS_MONGO_DATABASE=solid export JSS_WEBRTC=true jss start
Config File
Create config.json:
{
"port": 8443,
"root": "./data",
"sslKey": "./ssl/key.pem",
"sslCert": "./ssl/cert.pem",
"conneg": true,
"notifications": true
}Then: jss start --config config.json
Creating a Pod
curl -X POST http://localhost:3000/.pods \ -H "Content-Type: application/json" \ -d '{"name": "alice"}'
Response:
{
"name": "alice",
"webId": "http://localhost:3000/alice/#me",
"podUri": "http://localhost:3000/alice/",
"token": "eyJ..."
}Single-User Mode
For personal pod servers where only one user needs access:
# Basic single-user mode (creates pod at /me/) jss start --single-user --idp # Custom username jss start --single-user --single-user-name alice --idp # Root-level pod (pod at /, WebID at /profile/card#me) jss start --single-user --single-user-name '' --idp # Via environment JSS_SINGLE_USER=true jss start --idp
Features:
- Pod auto-created on first startup with full structure (inbox, public, private, profile)
- Registration endpoint disabled (returns 403)
- Login still works for the single user
- Proper ACLs generated automatically
Invite-Only Registration
Control who can create accounts by requiring invite codes:
jss start --idp --invite-only
Managing Invite Codes
# Create a single-use invite jss invite create # Created invite code: ABCD1234 # Create multi-use invite with note jss invite create -u 5 -n "For team members" # List all active invites jss invite list # CODE USES CREATED NOTE # ------------------------------------------------------- # ABCD1234 0/1 2026-01-03 # EFGH5678 2/5 2026-01-03 For team members # Revoke an invite jss invite revoke ABCD1234
How It Works
| Mode | Registration | Pod Creation |
|---|---|---|
| Open (default) | Anyone can register | Anyone can create pods |
| Invite-only | Requires valid invite code | Via registration only |
When --invite-only is enabled:
- The registration page shows an "Invite Code" field
- Invalid or expired codes are rejected with an error
- Each use decrements the invite's remaining uses
- Depleted invites are automatically removed
Invite codes are stored in .server/invites.json in your data directory.
Storage Quotas
Limit storage per pod to prevent abuse and manage resources:
jss start --default-quota 50MB
Managing Quotas
# Set quota for a user (overrides default) jss quota set alice 100MB # Show quota info jss quota show alice # alice: # Used: 12.5 MB # Limit: 100 MB # Free: 87.5 MB # Usage: 12% # Recalculate from actual disk usage jss quota reconcile alice
How It Works
- Quotas are tracked incrementally on PUT, POST, and DELETE operations
- When quota is exceeded, the server returns HTTP 507 Insufficient Storage
- Each pod stores its quota in
/{pod}/.quota.json - Use
reconcileto fix quota drift from manual file changes
Size Formats
Supported formats: 50MB, 1GB, 500KB, 1TB
Mashlib Data Browser
Enable the SolidOS Mashlib data browser for RDF resources. Two modes are available:
CDN Mode (recommended for getting started):
jss start --mashlib-cdn --conneg
Loads mashlib from unpkg.com CDN. Zero footprint - no local files needed.
Local Mode (for production/offline):
jss start --mashlib --conneg
Serves mashlib from src/mashlib-local/dist/. Requires building mashlib locally:
cd src/mashlib-local npm install && npm run build
ES Module Mode (for custom or next-gen mashlib builds):
jss start --mashlib-module https://example.com/mashlib.js
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.
How it works:
- Browser requests
/alice/public/data.ttlwithAccept: text/html - Server returns Mashlib HTML wrapper
- Mashlib fetches the actual data via content negotiation
- Mashlib renders an interactive, editable view
Note: Mashlib works best with --conneg enabled for Turtle support.
Modern UI (SolidOS UI):
jss start --mashlib --solidos-ui --conneg
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:
- Modern file browser with breadcrumb navigation
- Profile, Contacts, Sharing, and Settings views
- Path-based URLs (browser URL reflects current resource)
- Responsive design for mobile devices
Requires solidos-ui dist files in src/mashlib-local/dist/solidos-ui/. See solidos-ui for details.
Profile Pages
Pod profiles (/alice/) use HTML with embedded JSON-LD data islands and are rendered using:
- mashlib-jss - A fork of mashlib with
getPod()fix for path-based pods - solidos-lite - Parses JSON-LD data islands into the RDF store
This allows profiles to work without server-side content negotiation while still providing full SolidOS editing capabilities.
WebSocket Notifications
Enable real-time notifications for resource changes:
const server = createServer({ notifications: true });
Clients discover the WebSocket URL via the Updates-Via header:
curl -I http://localhost:3000/alice/public/
# Updates-Via: ws://localhost:3000/.notificationsProtocol (solid-0.1, compatible with SolidOS):
Server: protocol solid-0.1
Client: sub http://localhost:3000/alice/public/data.json
Server: ack http://localhost:3000/alice/public/data.json
Server: pub http://localhost:3000/alice/public/data.json (on change)