Skip to content

Mac self‐hosting

Billy Wilcosky edited this page Jun 24, 2026 · 6 revisions

Run a federation node on your Mac

What you need

  • PHP (Mac often has it already — check with php -v in Terminal)
  • A public HTTPS URL (tunnel) so Litter Layer can reach your Mac — e.g. Cloudflare Tunnel or ngrok
  • The starter pack from litterlayer.com/federation/
  • No PHP? Install with Homebrew: brew install php

1. Put the files in a folder

Unzip the starter pack, e.g. ~/Sites/my-node/:

my-node/
  .well-known/litterlayer.json
  sites.json
  search.php
  crawl_tick.php
  ll-widget.js
  lib/
    ll_node_common.php
    ll_node_crawler.php
  data/                 (must be writable by PHP)
  .htaccess

2. Edit two files

.well-known/litterlayer.json — your public HTTPS URL (not localhost):

{
  "node_id": "my-node.example.com",
  "base_url": "https://my-node.example.com",
  "categories": ["writing", "tech"],
  "capabilities": { "search": true },
  "performance": { "timeout_ms": 1200 }
}

Optional: if your node files live in a subfolder (e.g. https://example.com/test/) but your website is at the domain root, you do not need to change anything — the auto-crawler seeds from https://example.com/sitemap.xml and / by default. To crawl a subfolder only, add:

"crawl_root_url": "https://example.com/test"

sites.json — pages you want searchable (url, title, description, score). You can also leave a minimal list and let the auto-crawler fill this file over time (see step 6).

[
  {
    "url": "https://wilcosky.com/",
    "title": "wilcosky.com",
    "description": "Personal site of Billy Wilcosky — writing, projects, and links on the small web.",
    "score": 1.0
  },
  {
    "url": "http://www.internetlastpage.com/",
    "title": "The Last Page of the Internet",
    "description": "Ta Da! You have reached the very last page of the Internet. Turn off your computer and go have fun.",
    "score": 0.9
  }
]

Auto-crawled pages get a default score of 0.8. Manual scores are kept on re-crawl.

3. Run PHP on your Mac

Open Terminal, go to your folder, start the built-in server:

cd ~/Sites/my-node
php -S localhost:8080

Leave that window open while testing.

Alternative: MAMP — set the document root to my-node and use MAMP’s port (often 8888).

Make sure the data/ folder is writable by PHP.

4. Expose it with HTTPS

Your Mac isn’t reachable from the internet by default. Use a tunnel:

ngrok (quick test):

brew install ngrok
ngrok http 8080

Copy the https://….ngrok-free.app URL.

Cloudflare Tunnel — good for a stable URL if you have a domain.

Put that HTTPS URL in litterlayer.json as base_url.

Note: php -S doesn’t read .htaccess, so /search and /crawl_tick may not work until you use MAMP/Apache, a router script, or Nginx. For registration, the hub calls https://your-url/search — Apache + .htaccess, or MAMP with mod_rewrite, is the reliable Mac setup.

PHP built-in server router (optional)

Save as router.php in my-node/:

<?php
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if ($uri === '/search') {
    require __DIR__ . '/search.php';
    return true;
}
if ($uri === '/crawl_tick') {
    require __DIR__ . '/crawl_tick.php';
    return true;
}
return false;

Run:

php -S localhost:8080 router.php

Using Nginx instead of Apache?

Point root at your folder and add /search and /crawl_tick rules:

server {
    listen 443 ssl;
    server_name my-node.example.com;
    root /var/www/my-node;
    index index.html;
    # SSL cert paths here (certbot, etc.)
    location = /.well-known/litterlayer.json {
        default_type application/json;
        try_files /litterlayer.json =404;
    }
    location = /search {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/search.php;
        fastcgi_pass unix:/opt/homebrew/var/run/php-fpm.sock;   # Homebrew PHP-FPM socket
    }
    location = /crawl_tick {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/crawl_tick.php;
        fastcgi_pass unix:/opt/homebrew/var/run/php-fpm.sock;
    }
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/opt/homebrew/var/run/php-fpm.sock;
    }
}

5. Test

Descriptor (Safari/Chrome):

https://your-url/.well-known/litterlayer.json

Search (Terminal):

curl -sS -X POST https://your-url/search \
  -H "Content-Type: application/json" \
  -d '{"query":"hello","limit":5}'

→ expect {"results":[...], ...}

Crawl tick (Terminal, optional auto-crawler):

curl -sS https://your-url/crawl_tick

→ expect JSON like {"ok":true,...} (or "skipped":"throttled" if called again within 5 minutes)

6. Optional — Personal site search widget + auto-crawler

The starter pack includes a search box for your own site (not Litter Layer’s index). It uses the same sites.json as federated search.

Embed on any page on your domain (same origin as the node files):

<div id="ll-site-search"></div>
<script src="/ll-widget.js" defer></script>

Point src at wherever you uploaded ll-widget.js. If your node lives in a subfolder, use e.g. src="/test/ll-widget.js" — the widget finds /search and /crawl_tick relative to that script.

What happens when someone visits the page:

  • The widget searches your local /search endpoint (25 results per page, Load more for the next page).
  • A background crawl tick runs (at most once every 5 minutes, 2 pages per tick).
  • The crawler tries https://yoursite.com/sitemap.xml, then your homepage at /, then follows same-domain links.
  • Discovered pages are merged into sites.json (manual entries are kept). No cron job required.

Mac note: your tunnel must be running and the page with the widget must be reachable over HTTPS for crawls to advance. Testing with curl /crawl_tick also works.

7. Register

Go to litterlayer.com/federation/ and submit:

https://your-url/.well-known/litterlayer.json

Wait for approval.

Mac-specific tips

  • Show hidden files in Finder: Cmd + Shift + . to see .well-known
  • Firewall: if using your own domain/IP, allow incoming HTTP/HTTPS in System Settings → Network → Firewall
  • Keep Terminal (or MAMP + tunnel) running while the node is live
  • localhost won’t work for registration — the hub needs your public HTTPS URL
  • The crawler uses your site root for sitemap/homepage by default, even if node files are in a subfolder

Full shared-hosting steps (same files, more detail): litterlayer.com/federation/