Solid Lite

Getting Started

Build your first Solid Lite server in 10 minutes

Option A: Use JSS (Recommended)

The fastest way to get a full-featured Solid server running.

1

Clone and install

git clone https://github.com/JavaScriptSolidServer/JavaScriptSolidServer.git
cd JavaScriptSolidServer
npm install
2

Start the server

node bin/jss.js

Server runs at http://localhost:3000

3

Test it

# Create a resource
curl -X PUT http://localhost:3000/hello.txt \
  -H "Content-Type: text/plain" \
  -d "Hello, Solid!"

# Read it back
curl http://localhost:3000/hello.txt
Tip: Enable more features with flags:
# Full-featured server
node bin/jss.js --idp --mashlib --conneg --notifications

Option B: Build from Scratch

Understand the protocol by building a minimal server yourself.

1

Create project

mkdir my-solid-server && cd my-solid-server
npm init -y
npm install fastify
2

Minimal server (server.js)

import Fastify from 'fastify';
import fs from 'fs/promises';
import path from 'path';

const app = Fastify({ logger: true });
const DATA_DIR = './data';

// Ensure data directory exists
await fs.mkdir(DATA_DIR, { recursive: true });

// CORS headers (SLIP-11)
app.addHook('onSend', (req, reply, payload, done) => {
  reply.header('Access-Control-Allow-Origin', '*');
  reply.header('Access-Control-Allow-Methods', 'GET, PUT, DELETE, OPTIONS');
  reply.header('Access-Control-Allow-Headers', 'Content-Type');
  done();
});

// OPTIONS for CORS preflight
app.options('*', (req, reply) => reply.send());

// GET - Read resource (SLIP-10)
app.get('/*', async (req, reply) => {
  const filePath = path.join(DATA_DIR, req.url);
  try {
    const content = await fs.readFile(filePath, 'utf8');
    return reply.type('application/ld+json').send(content);
  } catch (e) {
    return reply.code(404).send({ error: 'Not Found' });
  }
});

// PUT - Create/Update resource (SLIP-10)
app.put('/*', async (req, reply) => {
  const filePath = path.join(DATA_DIR, req.url);
  await fs.mkdir(path.dirname(filePath), { recursive: true });
  await fs.writeFile(filePath, req.body);
  return reply.code(201).send({ created: req.url });
});

// DELETE - Remove resource (SLIP-10)
app.delete('/*', async (req, reply) => {
  const filePath = path.join(DATA_DIR, req.url);
  try {
    await fs.unlink(filePath);
    return reply.code(204).send();
  } catch (e) {
    return reply.code(404).send({ error: 'Not Found' });
  }
});

app.listen({ port: 3000 }, () => {
  console.log('Solid Lite server running on http://localhost:3000');
});
3

Run it

node server.js

Congratulations! You've implemented SLIPs 10, 11, and 12 — a minimal Solid Lite server.

Next Steps: Add Features

Add Authentication (SLIP-81: Bearer Token)

// Add to your server
const API_KEY = process.env.SOLID_API_KEY || 'secret';

app.addHook('preHandler', (req, reply, done) => {
  if (req.method === 'GET') return done(); // Public reads

  const auth = req.headers.authorization;
  if (auth !== `Bearer ${API_KEY}`) {
    return reply.code(401).send({ error: 'Unauthorized' });
  }
  done();
});

Add JSON-LD Profile (SLIP-20: WebID)

# Create a profile
curl -X PUT http://localhost:3000/profile \
  -H "Content-Type: application/ld+json" \
  -H "Authorization: Bearer secret" \
  -d '{
    "@context": "https://www.w3.org/ns/activitystreams",
    "@id": "#me",
    "type": "Person",
    "name": "Alice"
  }'

Add Containers (SLIP-40)

// List directory contents as JSON-LD
app.get('/*/', async (req, reply) => {
  const dirPath = path.join(DATA_DIR, req.url);
  const files = await fs.readdir(dirPath);
  return reply.type('application/ld+json').send({
    "@context": "http://www.w3.org/ns/ldp#",
    "@type": "Container",
    "contains": files.map(f => ({ "@id": f }))
  });
});

SLIP Combinations (Recipes)

Profile SLIPs Use Case
Minimal 10, 11, 12 Learning, testing
Personal + 81, 90 Single-user data store
Multi-user + 41, 83, 84, 85, 91 Hosting for others
Nostr-Native + 21, 82, 91 Passwordless, no registration
Full (JSS) All SLIPs Production server

Testing with curl

# Create
curl -X PUT http://localhost:3000/notes/first.json \
  -H "Content-Type: application/ld+json" \
  -d '{"@context": "https://schema.org", "@type": "Note", "text": "Hello!"}'

# Read
curl http://localhost:3000/notes/first.json

# Update
curl -X PUT http://localhost:3000/notes/first.json \
  -H "Content-Type: application/ld+json" \
  -d '{"@context": "https://schema.org", "@type": "Note", "text": "Updated!"}'

# Delete
curl -X DELETE http://localhost:3000/notes/first.json

# List container
curl http://localhost:3000/notes/

Browser App Example

<!DOCTYPE html>
<html>
<head>
  <title>Solid Lite Client</title>
</head>
<body>
  <h1>My Notes</h1>
  <div id="notes"></div>
  <input id="text" placeholder="New note...">
  <button onclick="addNote()">Add</button>

  <script>
    const SERVER = 'http://localhost:3000';

    async function loadNotes() {
      const res = await fetch(`${SERVER}/notes/`);
      const data = await res.json();
      document.getElementById('notes').innerHTML =
        data.contains?.map(n => `<p>${n['@id']}</p>`).join('') || 'No notes';
    }

    async function addNote() {
      const text = document.getElementById('text').value;
      const id = Date.now();
      await fetch(`${SERVER}/notes/${id}.json`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/ld+json' },
        body: JSON.stringify({
          '@context': 'https://schema.org',
          '@type': 'Note',
          'text': text
        })
      });
      loadNotes();
    }

    loadNotes();
  </script>
</body>
</html>

Download Examples

Ready-to-run example code:

minimal-server.js

~50 lines, SLIPs 10-12

personal-server.js

+Auth, +Containers

client.html

Browser notes app

Or clone all examples:

git clone https://github.com/solid-lite/draft-spec.git
cd draft-spec/examples
npm install && npm start

Resources