| Mode | Description |
|---|---|
origin |
The one authoritative server. Manages index.json, publishes to IPNS, approves/rejects submissions, notifies mirrors. Locked to the designated hardware. |
mirror |
Read-only replica. Syncs index.json from IPNS, pins it locally, forwards submissions to origin. Anyone can run one. |
# Clone or copy this directory to your server, then:
bash init.sh originThe init script will:
- Verify the hardware fingerprint
- Install Node.js 22, IPFS (kubo), Tor
- Generate an IPNS key (
2kewl-index) - Generate a random
ADMIN_KEY - Create systemd services for IPFS and 2kewl
- Open firewall ports
Origin will refuse to start on any other machine.
bash init.sh mirror https://2kewl.org https://yourmirror.example.com "My Mirror"Mirrors will:
- Pull
index.jsonfrom IPNS every 5 minutes (configurable viaMIRROR_SYNC_INTERVALms) - Pin the current IPFS CID locally
- Register themselves with the origin so the origin can push updates immediately on approval
- Forward board submissions to the origin
┌─────────────┐
submit ───► │ Origin │ ◄── admin approves
│ (Rocky) │
└──────┬──────┘
│ ipfs add + ipfs name publish
▼
┌─────────────┐
│ IPNS key │ /ipns/k51qzi5...
└──────┬──────┘
│ resolves to CID
┌──────────┼──────────┐
▼ ▼ ▼
Mirror A Mirror B Mirror C
pins CID pins CID pins CID
serves serves serves
/api/boards from local index.json
- Origin approves a board → saves to
index.json→ipfs add→ipfs name publish(updates IPNS) - Origin pushes the new CID to all registered mirrors via
POST /api/mirror/sync - Mirrors verify the CID against IPNS (so they can't be fed a fake CID), fetch + pin it
- Mirrors also poll IPNS every N minutes independently (resilient if push fails)
- All
/index.jsonrequests from any node redirect tohttps://ipfs.io/ipns/<IPNS_ADDR>— the same canonical URL regardless of which server you hit
| Variable | Default | Description |
|---|---|---|
MODE |
— | Must be origin |
ADMIN_KEY |
— | Required. Secret key for admin API |
PORT |
3000 |
HTTP port |
IPNS_KEY |
2kewl-index |
IPFS key name for IPNS publishing |
IPNS_ADDR |
hardcoded | IPNS address (k51...) |
ONION |
hardcoded | .onion address |
TOR_SOCKS_PROXY |
socks5h://127.0.0.1:19050 |
Tor SOCKS5 proxy |
MAIL_* |
— | Optional email alerts |
| Variable | Default | Description |
|---|---|---|
MODE |
— | Must be mirror |
PORT |
3000 |
HTTP port |
ORIGIN_URL |
— | URL of origin (for forwarding submissions + registration) |
MIRROR_URL |
— | Public URL of this mirror (sent to origin on registration) |
MIRROR_NAME |
— | Human-readable name |
IPNS_ADDR |
hardcoded | IPNS address to sync from |
MIRROR_SYNC_INTERVAL |
300000 |
Poll interval in ms (default 5 min) |
| Method | Path | Description |
|---|---|---|
| GET | /api/admin/pending |
List pending submissions |
| POST | /api/admin/approve |
{ tag } — approve + publish to IPNS |
| POST | /api/admin/reject |
{ tag } — reject |
| DELETE | /api/admin/boards/:tag |
Remove board + republish |
| GET | /api/admin/mirrors |
List registered mirrors |
| DELETE | /api/admin/mirrors/:url |
Remove a mirror |
| POST | /api/admin/blog |
{ title, content } |
| DELETE | /api/admin/blog/:id |
Remove post |
| Method | Path | Description |
|---|---|---|
| POST | /api/mirror/register |
Mirror registers with origin { url, name } |
| POST | /api/mirror/sync |
Origin pushes CID update { cid, ipns } |
Type help in the running server console for the full list. Key ones:
Origin: pending, approve <tag>, reject <tag>, rm <tag>, add <onion> <tag> <title>, publish, mirrors, notify
Mirror: sync (force immediate IPNS pull)
server {
listen 443 ssl;
server_name 2kewl.org;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}
}