Prompt: run a js-libp2p / Helia / OrbitDB browser app on the ird relay
Have an agent give a js-libp2p, Helia or OrbitDB browser app a dedicated
circuit-relay-v2 peer on ird — peers reserve a slot on it, find each
other over pubsub, upgrade to direct WebRTC, and, with the offline
mailbox on, catch up on encrypted log entries and head CIDs even when no
other replica is online. Limits sized, cost explained, tested with two
browsers.
Needs: ird access (MCP server or the ird CLI, logged in); the app's source (its createLibp2p / createHelia / createOrbitDB setup); two browsers for the test
Costs: hourly per app plus per MB through the relay circuit and per MiB-hour of mailbox storage (credits_prices → recurring.p2p-app-hourly: per_hour, per_mb_relayed, per_mb_hour_cached) and a tiny fee per API-minted mailbox token (p2p.credentials); direct WebRTC traffic is free
Group: p2p
Paste everything below the line into a fresh conversation. Fill in the
FILL IN block first.
Task
Create an ird p2p app for the app below, make my libp2p node listen on
its relay, enable the mailbox if asked, and prove two browsers
replicate. Nothing is created before I confirm the price.
FILL IN
NAME: ledger # → ledger.ap2p.ro
ORIGINS: https://ledger.example.ro # sites allowed to connect (blank = any — dev only)
STACK: orbitdb # libp2p | helia | orbitdb
MAILBOX: yes # yes if peers must catch up when never online together
MINT: open # open = browsers mint mailbox tokens; closed = only my backend
PEERS: 50 # expected concurrent browser peers
Non-negotiable rules
- Price first. From
credits_prices show per_hour × 730, the
per_mb_relayed rate and, if MAILBOX, per_mb_hour_cached. Wait for
a yes.
- The recipe is in
p2p_protocol. Read it (served locally, no
network) and copy its libp2p section — transports, listen addresses,
discovery topic. Do not invent multiaddrs.
- Origins replace whole.
p2p_set_origins overwrites the list;
read p2p_list_apps first and resend it complete. Bare wildcards
only (*.example.ro).
- Disabling the mailbox drops everything it holds. Never call
p2p_set_mailbox(enabled=false) without my explicit yes.
- Encrypt before depositing. The node stores opaque bytes and sees
room ids, sizes and timing; nothing enters the mailbox in clear.
Steps
A. Create. p2p_list_apps — reuse NAME if present. Otherwise
p2p_create_app(name=NAME, origins=[ORIGINS]) (CLI: ird p2p create
NAME --origin …). The relay is on by default; re-read p2p_list_apps
(CLI: ird p2p relay NAME) until relay_peer_id and relay_multiaddrs
appear, shaped /dns4/<host>/tcp/443/tls/ws/p2p/<relay-peer-id>; the
relay's WS endpoint is the vhost root.
B. Client. Per p2p_protocol: at boot fetch
https://<host>/.well-known/ap2p (CORS *, once) and read
relay.multiaddrs — never hardcode the peer id, so a relay identity
rotation needs no release. Then exactly the recipe:
addresses.listen = [RELAY + "/p2p-circuit", "/webrtc"], transports
webSockets(), webRTC(), circuitRelayTransport(), noise,
yamux, gossipsub, pubsubPeerDiscovery() on the default topic
(_peer-discovery._p2p._pubsub — the relay subscribes only to that
one; a custom discovery topic will not propagate), identify, then
node.dial(multiaddr(RELAY)). Each app's relay is an isolated libp2p
host — discovery never leaks across apps. Helia/OrbitDB sit on this
node unchanged.
C. Mailbox (if MAILBOX). p2p_set_mailbox(app=NAME, enabled=true,
open_mint=<MINT == open>) (CLI: ird p2p mailbox NAME --enable
[--closed-mint]). mailbox_url appears in p2p_list_apps and a
mailbox block in the descriptor. Design, from p2p_protocol:
- room id = the database address (
/orbitdb/zdpu…), percent-encoded
as one path segment; deposit encrypted log entries and head CIDs
there. Identical payloads dedupe per room, so every replica may
deposit the same block.
- tokens: browsers use the open mint
GET https://<host>/mailbox-credentials?room=…&ttl=… (origin-gated,
rate-limited per IP/app — mint one app-scoped token and reuse it for
its ttl); Node/Go/backend peers, and everyone when MINT is closed,
use p2p_mailbox_credentials(app=NAME, room=…, ttl_sec=…) (CLI:
ird p2p mailbox creds NAME --room …), a charged mint.
- replay with
after=<last seq>; seq is an opaque string — never
parse it. A cursor older than oldest_seq means eviction (TTL or
ring buffer): show "history truncated" and re-sync from a live peer.
D. Limits. Defaults per p2p_set_limits: max_relay_reservations
128, max_relay_circuits 64, relay_circuit_data_mb 512,
relay_circuit_duration_sec 3600, max_relay_bytes_per_sec 256 KiB/s,
max_relay_conns_per_ip 20; mailbox_ttl_hours 24, mailbox_max_mb
256, mailbox_max_message_kb 64. Size: reservations ≥ PEERS, circuits
≥ peer pairs that may stay relayed at once, message KB ≥ the largest
entry batch. Only passed keys change, 0 resets one, large values are
clamped. (CLI: ird p2p limits NAME --set k=v.)
E. Verify with two browsers.
- Descriptor shows
relay.peer_id and, if on, a mailbox block.
- Two browsers open the app: each lists the relay in
node.getConnections(), then discovers the other; an OrbitDB write
in one arrives in the other (update event).
- The peer-to-peer connection's remote address contains
/webrtc,
not only /p2p-circuit.
- If MAILBOX:
p2p_mailbox_send(app=NAME, room=<db address>,
payload="test") then p2p_mailbox_recv(app=NAME, room=…) returns
it (CLI: ird p2p mailbox send/recv). Then close A, write in B,
reopen A alone: it catches up from the mailbox.
- A page on a non-allowed origin is refused (403 on the upgrade).
F. Cost recap. p2p_usage → relayed bytes, cached_bytes (a level,
not a total), hourly/monthly cost.
Deliverable
Hostname, relay_peer_id, relay_multiaddrs, mailbox_url and mint
mode, origin allowlist, settings overrides, the client snippet
(descriptor fetch, createLibp2p config, mailbox helper), results for
E.1–E.5, the monthly estimate, and ird p2p delete NAME.