Skip to content

Y.js POC Deployment Guide

The 3 Y.js + WebRTC POC tools are the only parts of Nexus SecOps that load external runtime dependencies. Everything else is zero-dependency, vendor-self-hosted. These POCs document the exception and the operational discipline required to keep them shippable.

Tools covered

Pinned dependencies (s53 verified)

Package Pinned version URL SRI hash
yjs 13.6.18 https://cdn.jsdelivr.net/npm/yjs@13.6.18/dist/yjs.mjs sha384-ghlZMZ/j8iYg2QKKfDUo2/h7LTtqgqEDJZLR58Aha3XZ9l4K/MTy+y2mTTbKNPa9
y-webrtc 10.3.0 https://cdn.jsdelivr.net/npm/y-webrtc@10.3.0/+esm sha384-RKwQKt46wors3na9Q6Qb84Zjf8xvNiTF3T7ntMTLvRg/Kt+2GdH7owP4oiDhUBXn

Why /+esm for y-webrtc: y-webrtc@10.3.0 ships only CJS (/dist/y-webrtc.cjs), not ESM. The pinned URL /dist/y-webrtc.mjs returns HTTP 404 — that file does not exist. jsdelivr's /+esm path auto-converts CJS to a minimal ESM wrapper. This is documented at https://www.jsdelivr.com/esm. Switching y-webrtc versions to one that ships native ESM (if released) lets you go back to a static /dist/y-webrtc.mjs path.

Cloudflare _headers CSP overrides (s53)

The site's default CSP (in /* block of docs/_headers) does not allow WebSocket connections to the y-webrtc default signaling servers. Per-route overrides relax CSP only for the 3 Y.js POCs:

/tools/purple-team-arena-poc.html
  Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' wss://signaling.yjs.dev wss://y-webrtc-signaling-eu.herokuapp.com wss://y-webrtc-signaling-us.herokuapp.com stun:stun.l.google.com:19302; frame-src 'none'

/tools/peer-review-poc.html
  Content-Security-Policy: ... (same connect-src)

/tools/team-leaderboards-poc.html
  Content-Security-Policy: ... (same connect-src)

The rest of the site keeps the stricter default CSP. Granular per-route override avoids relaxing posture for the whole site.

Manual test checklist (pre-deploy)

Open the deployed site (or mkdocs serve locally) and verify each of the 3 POCs:

  1. Resource loading
  2. Open DevTools Network tab
  3. Load the POC page
  4. Confirm both yjs.mjs and y-webrtc/+esm requests return 200
  5. Confirm SRI verification passes (no console error like "Failed to find a valid digest")
  6. Confirm CSP allows the script-src + connect-src (no console CSP violation reports)

  7. Real-time sync (Arena + Peer Review)

  8. Open the same room in 2 browser tabs (or 2 different browsers)
  9. Make changes in tab A; verify they appear in tab B within ~1-2 seconds
  10. Verify presence indicator shows the other peer

  11. localStorage cross-tool reads (Leaderboards only)

  12. Open Skill Mastery Map; mark a few concepts mastered
  13. Open Daily Practice; review a few cards
  14. Open Skill Portfolio; issue a badge if eligible
  15. Open Team Leaderboards; verify your stats appear in the local-stats panel

  16. Honest-framing UX

  17. Confirm each POC's top-of-page warning admonitions are visible
  18. Confirm the "what this POC does NOT do" lists are present at the bottom

Regenerating SRI hashes

Hashes change when the upstream package version changes (i.e., when bumping yjs@13.6.18 to a newer version). To regenerate:

python scripts/regenerate_sri_hashes.py             # print only
python scripts/regenerate_sri_hashes.py --update    # in-place patch the 3 POCs

Verification after --update: the script greps for PLACEHOLDER strings in the 3 POC HTML files and exits non-zero if any survive.

The hashes are deterministic for any given pinned version — anyone running the script later against the same versions will get the same SRI strings.

Pre-merge checklist for any future Y.js dep bump

When bumping yjs or y-webrtc to a newer version:

  1. Update version constants in all 3 POC HTML files:
  2. YJS_URL in purple-team-arena-poc.html, peer-review-poc.html, team-leaderboards-poc.html
  3. YWEBRTC_URL in same 3 files
  4. In-page documentation comments mentioning the pinned version
  5. Verify the URL pattern still works for the new version. y-webrtc may eventually ship native ESM and the /dist/y-webrtc.mjs path may become valid. yjs has shipped ESM since 13.x.
  6. Update MODULES dict in scripts/regenerate_sri_hashes.py
  7. Run python scripts/regenerate_sri_hashes.py --update
  8. Verify the script reports both hashes computed + zero PLACEHOLDER survivors
  9. Run strict build python -m mkdocs build --strict
  10. Manual test the POCs locally per the checklist above (especially Network tab for SRI verification + CSP allow)
  11. Git commit with both the version bump and the SRI hash update in the same commit (atomic)

Honest limitations of the POCs (carried forward)

These are documented in each POC's "what this POC does NOT do" list, restated here as a deployment-time reminder:

  • Public signaling servers: room name leaks plaintext to the y-webrtc default signaling endpoints (signaling.yjs.dev, *.herokuapp.com). For private use, self-host signaling.
  • No persistence: state is ephemeral and lost when the last peer leaves. For persistence, integrate y-leveldb or y-indexeddb.
  • No authentication: anyone with the room name joins. For real use, gate behind OAuth or a shared-secret signaling layer.
  • No NAT traversal beyond STUN: symmetric NAT / strict corporate firewalls will fail. Add TURN servers for production.
  • No moderation: no kick, no rate-limit, no content filter. CRDT growth is unbounded.
  • Public-CDN dependency: if cdn.jsdelivr.net is down or the pinned version is yanked, the POCs fail. For high-availability, mirror the modules to your own CDN or self-host them.

Deployment status (as of s53)

  • ✅ Real SRI hashes applied (deterministic)
  • ✅ Per-route CSP overrides in docs/_headers
  • ✅ y-webrtc URL bug fixed (switched to /+esm)
  • ✅ Strict build passing
  • ⏳ End-to-end manual test in production (requires deployed instance + 2 browsers)
  • ⏳ Self-hosted signaling decision (current default = public servers)

References