Rebrand package.json (name, homepage, repository) so render.js's page footer, which is built from pkg.homepage + git HEAD, points at this repo rather than upstream's ssb:// URL. Track package-lock.json instead of ignoring it. It was gitignored upstream, but this is a deployed application on a 469MB box running node 18 — a reproducible dependency tree is the difference between RESTORE.md working and not. Upstream's README is kept verbatim as UPSTREAM-README.md; the new one documents the custo data model, including the two things that will otherwise cost someone an afternoon: custodisco is the string "true", and no message ever sets content.channel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0192zBTNZKZn5svyJ5HTnYds
60 lines
2.4 KiB
Markdown
60 lines
2.4 KiB
Markdown
# custo-viewer
|
|
|
|
The web view behind **[www.cust.ooo](https://www.cust.ooo)** — a fork of
|
|
[ssb-viewer](https://gitlab.com/dwynen/ssb-viewer) that renders custo items from a
|
|
Scuttlebutt pub.
|
|
|
|
Upstream's own docs are kept verbatim in [UPSTREAM-README.md](UPSTREAM-README.md).
|
|
|
|
## What custo adds
|
|
|
|
| | |
|
|
|---|---|
|
|
| `/items` | One merged grid of every item the pub knows about, across all kiosks. New kiosks appear by being followed — no code change. |
|
|
| known-blob gate | `serveBlob` only serves blobs referenced by a feed we replicate, so the node never hands out a stranger's cached blob. |
|
|
| exit on dead sbot | `bin.js` exits when its muxrpc handle dies, instead of holding the port open and hanging every request forever. |
|
|
| `blob-wanter.js` | Standing `blobs.want` orders for our own feeds' blobs, replacing what `ssb-blobs` `sympathy` used to do before it was turned off. |
|
|
|
|
## The data model
|
|
|
|
An **item** is an ordinary `type: "post"` carrying custo's own fields:
|
|
|
|
```json
|
|
{ "type": "post", "custodisco": "true", "nft": "mint",
|
|
"text": "\n\n…\n\n a #custodisco item ",
|
|
"mentions": [{ "name": "photo.jpg", "type": "image/jpeg", "link": "&…sha256" }] }
|
|
```
|
|
|
|
A **transfer** of custody is a reply to that message:
|
|
|
|
```json
|
|
{ "type": "post", "custodisco": "true", "nft": "give",
|
|
"target": "@…ed25519", "root": "%…sha256", "branch": "%…sha256" }
|
|
```
|
|
|
|
Two things to know before writing a query against this:
|
|
|
|
- `custodisco` is the **string** `"true"`, not a boolean.
|
|
- **No message ever sets `content.channel`.** The `#custodisco` hashtag exists only in
|
|
post text. Anything keyed on the channel index will silently return nothing — which is
|
|
why `/channel/custodisco` renders an empty page.
|
|
|
|
Messages are published by the kiosks (`/home/trav/custodisco-kiosk/ssb-post.sh`), not by
|
|
this viewer. The viewer is read-only.
|
|
|
|
## Running it
|
|
|
|
`bin.js` connects to a local `ssb-server` and serves on `conf.viewer.port` (8807).
|
|
See `UPSTREAM-README.md` for the plugin-vs-standalone options.
|
|
|
|
## Deployment
|
|
|
|
Deployed by rsync from a laptop, not by `git pull` — the server holds no push
|
|
credentials, and one deploy path is better than two. The script, the systemd units, the
|
|
nginx config, and the disaster-recovery runbook all live in the ops repo alongside this
|
|
one (`documents/custo/ssb-viewer`), which is the source of truth for the server itself.
|
|
|
|
**Never commit secrets here.** Keys and server config belong in the ops repo.
|
|
|
|
AGPL-3.0+, inherited from upstream.
|