Prompt: move a Yjs, Trystero or PeerJS app onto ird p2p, no rewrite
Have an agent take a collaborative-editing app that already runs on
y-webrtc, Trystero/WebTorrent or PeerJS and point it at an ird p2p app —
the same hostname speaks all three signaling dialects, so the change is
one URL in the client config, plus TURN so peers behind strict NATs still
connect. Origins locked per environment, tested with two browsers, cost
known.
Needs: ird access (MCP server or the ird CLI, logged in); the app's source with its provider/joinRoom/Peer setup; two browsers (or one plus a private window) for the test
Costs: hourly per app plus per MB relayed through TURN (credits_prices → recurring.p2p-app-hourly: per_hour, per_mb_relayed) and a tiny fee per TURN credential mint (p2p.credentials); signaling traffic is currently free
Group: p2p
Paste everything below the line into a fresh conversation. Fill in the
FILL IN block first.
Task
Create one ird p2p app per environment below, wire my existing
collaborative-editing client to it without changing its data layer, add
TURN, and prove two browsers sync through it. Nothing is created before
I confirm the price.
FILL IN
NAME: notes # base name → notes.ap2p.ro, notes-staging.ap2p.ro, notes-dev.ap2p.ro
STACK: y-webrtc # y-webrtc | trystero | peerjs
PROD: https://notes.example.ro # production origin(s), comma-separated
STAGING: *.staging.example.ro # staging origin(s) — bare wildcard patterns allowed
DEV: http://localhost:5173 # dev origin(s) — try it; if refused, leave blank = any origin
ENVS: prod, staging, dev # which apps to create now
ROOM_VAR: roomName # where my code builds the room/topic id (file or variable)
Non-negotiable rules
- Price before creating. Read
credits_prices and show
per_hour × 730 per app × number of ENVS, plus the per_mb_relayed
rate. Wait for a yes.
- Origins replace whole.
p2p_set_origins overwrites the list;
always read it from p2p_list_apps and resend it complete. Wildcards
are bare (*.example.ro), never https://*.example.ro. An empty
list means any origin — acceptable only on the dev app, and say so.
- Read
p2p_protocol before touching client code. It has the exact
compat endpoints, the PeerJS options and the TURN response format.
Quote it; do not guess.
- Do not change my CRDT/data layer. Only the signaling URL, the ICE
config and the room id derivation move.
Steps
A. Create. For each ENV: p2p_list_apps — reuse NAME[-env] if it
exists. Otherwise p2p_create_app(name="NAME-env", origins=[…]) (CLI:
ird p2p create NAME-env --origin … --origin …); prod is plain NAME.
Keep signaling_url, turn_credentials_url, ice_urls and the
hostname. Confirm each app's allowlist with p2p_list_apps; fix with
p2p_set_origins(app, origins=[full list]) (CLI: ird p2p origins
NAME --set a,b) — takes effect within a minute.
B. Point the client at the compat endpoint. Per p2p_protocol
(also served at https://<host>/.well-known/ap2p/protocol.md):
- y-webrtc:
new WebrtcProvider(room, doc, { signaling:
["wss://<host>/y-webrtc"] }) — remove the public demo signaling
servers from the list.
- trystero:
joinRoom({ appId, trackerUrls: ["wss://<host>/tracker"] },
room) — /tracker speaks the WebTorrent tracker protocol (P2PT and
WebTorrent clients work too).
- peerjs:
new Peer({ host: "<host>", port: 443, secure: true }) —
server key peerjs, path / (PeerJS's default — leave it), ids
allocated at GET /peerjs/id.
Read the hostname from an env variable so each build picks its own
app. Rooms are namespaced per app, so ROOM_VAR needs no environment
prefix.
C. Add TURN. None of these frameworks fetch TURN by themselves.
Add a small function that does GET turn_credentials_url from the page
(origin-checked, so only from an allowed origin) and builds
iceServers from the response's urls, username, credential.
Re-fetch a few minutes before ttl expires; on failure fall back to an
empty iceServers list (direct connections still work). Pass it as
peerOpts: { config: { iceServers } } for y-webrtc, rtcConfig for
Trystero, config: { iceServers } for PeerJS — option names per
p2p_protocol.
D. Verify with two browsers.
GET https://<host>/.well-known/ap2p → the descriptor lists the
compat endpoint, max_rooms and max_room_peers.
p2p_turn_credentials(app=NAME, ttl_sec=600) (CLI: ird p2p creds
NAME --ttl 600) → username/credential; urls include turn: entries.
- Open the app in two browsers on the same room: an edit in one shows
in the other within a second. In
chrome://webrtc-internals (or
about:webrtc) the selected candidate pair is host/srflx.
- Force the relay path once: set
iceTransportPolicy: "relay" in the
same config and repeat — the pair must now be relay, and
p2p_usage (CLI: ird p2p usage) shows relayed bytes growing.
Remove the override afterwards.
- Open the app from a non-allowed origin (or
curl -H "Origin:
https://evil.example.com" turn_credentials_url) — it must be refused.
E. Capacity check. All three dialects share one per-app max_rooms
pool (default 500); y-webrtc topics and tracker swarms count peers
against max_room_peers (default 16). If a document may have more
collaborators at once: p2p_set_limits(app, max_room_peers=N) (CLI:
ird p2p limits NAME --set max_room_peers=N).
F. Cost recap. p2p_usage → hourly and monthly cost plus relayed
bytes; traffic that goes direct over WebRTC costs nothing.
Deliverable
Per environment: app hostname, signaling_url, turn_credentials_url,
ice_urls, the origin allowlist. The client diff (signaling URL, TURN
helper, env variable), test results for D.1–D.5, monthly estimate, and
the teardown commands (ird p2p delete NAME-dev etc.).