# Open Swarm — Catalog & payment API (machine JSON)

**Base URL (box):** `http://127.0.0.1:8765`  
**Public tunnel:** see `/workspace/open-swarm/PUBLIC_URL.txt` or `data/PUBLIC_URL.txt`

Legal: **site catalog is SPDX-allowlisted only.** Arbitrary magnets → **403**.  
Clients MAY seed user magnets locally; this hosted index WILL NOT.

All successful bodies are JSON. Errors often include `"ok": false` and `"blocked": true`.

---

## Health

```bash
curl -sS "$BASE/api/health" | jq .
```

Example fields: `ln_mode` (`nwc`|`sim`), `ln_address`, `user_pays`, `nwc_configured` (boolean only — never a secret).

---

## Catalog (allowlisted fixtures)

```bash
export BASE=http://127.0.0.1:8765
curl -sS "$BASE/api/catalog" | jq '{allowlist, count:(.items|length), items:[.items[]|{id,spdx,infohash,piece_count,has_real_torrent,sats_per_piece}]}'
```

| Field | Meaning |
|-------|---------|
| `items[].id` | Fixture id |
| `items[].spdx` | License (allowlisted) |
| `items[].infohash` | Protocol content id |
| `items[].piece_count` / `piece_size` | Piece layout |
| `items[].has_real_torrent` | BT magnet available |
| `allowlist` | SPDX list enforced by site |

CLI equivalent:

```bash
python thick-client/seed_cli.py catalog --api "$BASE"
python thick-client/seed_cli.py catalog --local
```

---

## Reject arbitrary magnets (always)

```bash
curl -sS -X POST "$BASE/api/magnet" \
  -H 'Content-Type: application/json' \
  -d '{"magnet":"magnet:?xt=urn:btih:deadbeef"}' | jq .
# → HTTP 403, blocked:true
```

Same for `/api/magnet/add` and `/api/torrent/add`.

---

## Register seeder (lud16 payee)

```bash
IH=$(curl -sS "$BASE/api/catalog" | jq -r '.items[]|select(.id=="tiny-lm-demo")|.infohash')
curl -sS -X POST "$BASE/api/seeder/register" \
  -H 'Content-Type: application/json' \
  -d "{\"infohash\":\"$IH\",\"sats_per_piece\":1,\"ln_receive\":\"erudite-jellyfish@lexe.app\"}" | jq '{ok,ad:{infohash:.ad.infohash,ln_receive:.ad.ln_receive,sats_per_piece:.ad.sats_per_piece}}'
```

Do **not** send `magnet` fields — rejected.

---

## Piece invoice → pay → claim

```bash
# 1) Invoice (real LN floors to ≥21 sats)
INV=$(curl -sS -X POST "$BASE/api/piece/invoice" \
  -H 'Content-Type: application/json' \
  -d "{\"infohash\":\"$IH\",\"piece_index\":0}")
echo "$INV" | jq '{ok,invoice_id,amount_sats,lud16,memo,simulated,bolt11:(.bolt11[:24]+"…")}'

# 2) User pays bolt11 (WebLN / QR / NWC). Box does not auto-pay browser downloads.

# 3) Claim after settle (optional preimage)
IID=$(echo "$INV" | jq -r .invoice_id)
curl -sS -X POST "$BASE/api/piece/claim" \
  -H 'Content-Type: application/json' \
  -d "{\"invoice_id\":\"$IID\"}" | jq '{ok,verified,piece_index,size,pieces_held}'
```

Whole file:

```bash
curl -sS -X POST "$BASE/api/file/invoice" -H 'Content-Type: application/json' \
  -d "{\"infohash\":\"$IH\"}" | jq '{ok,invoice_id,amount_sats,remaining_count,lud16,memo}'
curl -sS -X POST "$BASE/api/file/claim" -H 'Content-Type: application/json' \
  -d "{\"invoice_id\":\"$IID\"}" | jq '{ok,pieces_held,filename}'
```

Invoice memo formats (protocol v0.2.0):

```
infohash|piece_index|piece_hash|nonce
infohash|ALL|remaining_count|file_hash|nonce
```

---

## Stats / ToS pages

```bash
curl -sS "$BASE/api/stats" | jq '{ok, catalog:(.catalog|length), ln, legal}'
curl -sS -o /dev/null -w '%{http_code}\n' "$BASE/tos" "$BASE/stats"
```

---

## Never logged

NWC URIs, seeds, preimages in server logs, or wallet secrets. Health/stats expose `nwc_configured` boolean only.
