Join the Federation
TL;DR — Download starter pack (descriptor, sites.json, search.php, and .htaccess). Upload to your web root, edit your domain and pages, then register below.
If you want to contribute to the Litter Layer Federation, you can register your site as a federated node. Approved nodes share search results when visitors turn on Federated search on the results page.
How federated search works
When a visitor has the federated toggle on, Litter Layer queries approved partner nodes once per search and merges their results with this hub's index. Network results can appear across multiple result pages when the visitor clicks “Load more results,” until that search's federated pool runs out. After that, later pages come from this hub's central index only.
This keeps the network efficient: nodes are not re-queried on every page. The hub caches the merged pool briefly (about 10 minutes) so load-more stays fast on shared hosting. If a visitor waits too long before loading more, federated results may expire and only hub results will appear.
Each node may return up to 10 results per search. Those results may spread across several pages — not only the first page. No changes are required on node operators' servers for multi-page pagination.
Who is this guide for? We want anyone to contribute — including people on cheap shared hosting with cPanel, Plesk, or similar. The steps below assume your website files live in a folder called public_html (sometimes named www or httpdocs). You upload files with File Manager or FTP.
If your setup is different — VPS, static host, your own app stack — you probably already know how to adapt these files. The rules are the same: publish a descriptor, answer search requests at /search, then register here.
What you will create
You do not need to install all of Litter Layer on your host. You need four small files:
- A descriptor that tells the network who you are (
.well-known/litterlayer.json). - A list of pages you want searchable (
sites.json— edit this like a simple spreadsheet). - A search script that reads that list (
search.php). - A tiny .htaccess rule so
/searchreaches your script (common on Apache hosts).
When someone searches, Litter Layer sends your site a query; your script returns matching rows from sites.json. You can add or edit entries anytime.
File tree (inside public_html)
When you are done, your hosting account should look like this:
- public_html/
- .well-known/
- litterlayer.json
- sites.json
- search.php
- .htaccess (add rewrite rule here)
- …your normal site files (index.html, WordPress, etc.)…
- .well-known/
In cPanel: open File Manager → public_html. Create the .well-known folder if it does not exist (some hosts hide dot-folders — enable “Show Hidden Files” in Settings).
Step 1 — Create .well-known/litterlayer.json
This file describes your node. Replace your-domain.example with your real domain (no www is fine if that is your canonical host). Use https:// in base_url.
Live example from this hub: https://litterlayer.com/.well-known/litterlayer.json
node_id— pick a short unique name (often your domain).categories— required. Add one or more that fit your site:music,tech,art,writing,games.
{
"node_id": "your-domain.example",
"base_url": "https://your-domain.example",
"categories": ["writing", "tech"],
"capabilities": {
"search": true
},
"performance": {
"timeout_ms": 1200
}
}
Test: open https://your-domain.example/.well-known/litterlayer.json in a browser. You should see the JSON text, not a 404.
Step 2 — Create sites.json
List every page you want searchable. Copy the sample below into public_html/sites.json and edit the URLs, titles, and descriptions. Add more objects inside the [ ] array as you publish new pages.
[
{
"url": "https://your-domain.example/",
"title": "Home page",
"description": "Welcome to my site — essays, links, and projects.",
"score": 1.0
},
{
"url": "https://your-domain.example/blog/hello-world/",
"title": "Hello world",
"description": "My first post about joining the small web.",
"score": 0.9
},
{
"url": "https://your-domain.example/links/",
"title": "Links",
"description": "Friends, tools, and places I like on the internet.",
"score": 0.8
}
]
Tip: keep URLs on the same domain as your descriptor. Use the full https:// address for each entry.
Step 3 — Create search.php
Save this file as public_html/search.php. It reads sites.json, matches the visitor’s keywords, and returns JSON. No database required.
<?php
header('Content-Type: application/json; charset=utf-8');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Only POST allowed']);
exit;
}
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
$query = is_array($data) ? trim((string)($data['query'] ?? '')) : '';
$limit = is_array($data) ? max(1, min(50, (int)($data['limit'] ?? 10))) : 10;
$path = __DIR__ . '/sites.json';
if (!is_file($path)) {
echo json_encode(['results' => []]);
exit;
}
$sites = json_decode(file_get_contents($path), true);
if (!is_array($sites)) {
echo json_encode(['results' => []]);
exit;
}
$terms = preg_split('/\s+/u', mb_strtolower($query), -1, PREG_SPLIT_NO_EMPTY);
$results = [];
foreach ($sites as $row) {
if (!is_array($row)) {
continue;
}
$haystack = mb_strtolower(
($row['title'] ?? '') . ' ' . ($row['description'] ?? '') . ' ' . ($row['url'] ?? '')
);
if ($terms) {
$match = true;
foreach ($terms as $term) {
if ($term !== '' && mb_strpos($haystack, $term) === false) {
$match = false;
break;
}
}
if (!$match) {
continue;
}
}
$results[] = [
'url' => (string)($row['url'] ?? ''),
'title' => (string)($row['title'] ?? ''),
'description' => (string)($row['description'] ?? ''),
'score' => (float)($row['score'] ?? 0.5),
];
}
usort($results, function ($a, $b) {
return ($b['score'] <=> $a['score']);
});
echo json_encode(
['results' => array_slice($results, 0, $limit)],
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);
Step 4 — Update .htaccess
Litter Layer calls https://your-domain.example/search (not search.php). On most shared Apache hosts, add this rewrite rule so /search runs your script:
# Add these lines to public_html/.htaccess
# (If you already have a .htaccess file, paste inside it — do not delete existing rules.)
RewriteEngine On
RewriteRule ^search$ search.php [L,QSA]
If your host uses Nginx or you cannot edit what you need to, ask your web hosting support how to route /search to search.php. Also, these instructions are mainly for non-developers. If you have web building experience you may set this up however works best for your environment.
Test the search endpoint: after uploading, you can use your host’s Terminal (if available) or any online HTTP client. A successful response looks like {"results":[...]}. From a terminal:
curl -sS -X POST https://your-domain.example/search \
-H "Content-Type: application/json" \
-d '{"query":"hello","limit":5}'
Step 5 — Register your node
Once the descriptor URL works and search returns JSON, submit your descriptor below. We fetch it to verify HTTPS and settings, then queue your node for approval.
Step 6 — Heartbeats (optional)
After approval, you can occasionally ping this hub so we know your node is online. On shared hosting, add a once-daily cron job in cPanel if you like — it is optional.
curl -sS -X POST https://litterlayer.com/api/federation/heartbeat.php -H "Content-Type: application/json" -d '{"node_id":"your-domain.example"}'
Replace your-domain.example with the same node_id from your descriptor.
litterlayer.com.Need help?
See the Help page for how federated search works for visitors.
Instructions for other environments can be found at our Github wiki(opens in new tab).
For any questions, please ask in our Github forum(opens in new tab).
Flag this result
Hide this result?
Are you sure? This will hide this search result from you while using your current web browser. This cannot be undone. Unless you visit with a different web browser.
Bookmark this result
Bookmarked
What would you like to do?