Captured during freenet-email v0.1.0/v0.1.1 production publish and identity-creation debugging (April 2026). Filing so local-dev and dapp-builder skills absorb the gotchas before the next dApp hits them.
1. Gateway iframe shell + same-origin WebSocket check
The gateway wraps every webapp in a sandboxed iframe at ?__sandbox=1. The parent shell (freenetBridge) intercepts window.WebSocket from inside the iframe and proxies to the real node WS via postMessage.
The bridge enforces strict origin equality:
const LOCAL_API_ORIGIN = location.origin; // e.g. "http://127.0.0.1:7509"
if (httpProto + '//' + u.host !== LOCAL_API_ORIGIN) reject;
host includes hostname, so localhost ≠ 127.0.0.1. Hardcoding ws://localhost:7509/... in the dApp breaks the moment the page is opened by IP (or vice versa).
Fix pattern — derive WS URL from window.location at runtime:
let host = window().location().host()?; // "127.0.0.1:7509"
let scheme = if location.protocol()? == "https:" { "wss" } else { "ws" };
let url = format!(\"{scheme}://{host}/v1/contract/command?encodingProtocol=native\");
Skill should warn against hardcoded WS URLs and document the bridge's origin check.
2. wasm-bindgen onerror shim crash
Bridge proxy fires new Event('error') (no filename/message). wasm-bindgen's onerror handler calls event.filename → undefined → wbg crash:
wasm-bindgen: imported JS function that was not marked as 'catch' threw an error: expected a string argument, found undefined
Surfaces as a console error during normal operation. Smoke tests asserting consoleErrors === [] will fail. Allowlist the message until wbg upstream fixes, or use --catch on the import.
3. fdev port targeting
fdev defaults to port 7509. Targeting an isolated test node needs:
fdev --port 7510 execute put ... # CLI flag
WS_API_PORT=7510 cargo make publish-... # env override for cargo-make
Argument order matters: --port before execute, --code/--parameters before the contract subcommand. Skill already says this — worth a more prominent callout because the failure mode is put failed after 4 attempts which gives no hint about port.
4. Monotonic version requirement for web-container updates
On-chain web-container update check is strictly monotonic. Common broken schemes:
- commit-hash hex truncated to u32 — not monotonic, newer commit can hash lower
- commit count (
git rev-list --count HEAD) — monotonic per branch but starts at ~22, far below versions already accepted under broken schemes
Working scheme: date -u +%s (unix timestamp at sign time). Tradeoff: contract IDs no longer reproducible from source — the committed snapshot becomes the authoritative ID.
5. Persistent vs ephemeral test nodes
--id creates ephemeral temp directories wiped on restart, destroying delegate secrets (signing keys, app data). Use --data-dir for persistent isolation. Skill already covers — keep.
6. Mobile browser WASM cache
Firefox mobile especially aggressive. After republish, clear cache or use ?_v={timestamp} cache-buster. Skill covers — keep.
7. Delegate-not-found loop (open question)
Observed during identity-management delegate calls on a fresh local node:
Delegate not found in store (expected for migration probes) delegate_key=Cu...
Module cache miss — compiling
Loops indefinitely on each call. Suggests delegate registration not persisting between calls, or migration probe path is hot. Worth investigating + documenting in skill if it's a known pattern.
8. GNU tar requirement
compress-webapp needs --sort=name --mtime=… --owner=0 --group=0 flags only GNU tar supports. macOS BSD tar silently produces non-reproducible archives. Install gnu-tar brew and detect gtar. Worth mentioning in dapp-builder when scaffolding signed-webapp pipelines.
9. Browser MCP for dApp debugging
chrome-devtools MCP + Playwright MCP both work for diagnosing UI ↔ node interaction. Particularly useful for:
- reading actual gateway shell HTML (to find bridge origin checks)
- capturing console errors invisible on mobile
- watching network requests against the node WS
Could be a local-dev subsection: "Debugging dApps via browser MCP".
Suggested skill changes
local-dev: add "Gateway iframe + WS origin check" section with the window.location derivation pattern
local-dev: expand fdev port section with the WS_API_PORT env var
dapp-builder: add "Reproducible signed webapps" section covering monotonic version, GNU tar, committed-snapshot vs reproducible-from-source tradeoff
dapp-builder: add "WebSocket URL derivation" as a required pattern when the dApp loads inside the gateway iframe
- Either: capture the wbg onerror crash as a known noise pattern + smoke-test allowlist guidance
Captured during freenet-email v0.1.0/v0.1.1 production publish and identity-creation debugging (April 2026). Filing so
local-devanddapp-builderskills absorb the gotchas before the next dApp hits them.1. Gateway iframe shell + same-origin WebSocket check
The gateway wraps every webapp in a sandboxed iframe at
?__sandbox=1. The parent shell (freenetBridge) interceptswindow.WebSocketfrom inside the iframe and proxies to the real node WS via postMessage.The bridge enforces strict origin equality:
hostincludes hostname, solocalhost≠127.0.0.1. Hardcodingws://localhost:7509/...in the dApp breaks the moment the page is opened by IP (or vice versa).Fix pattern — derive WS URL from
window.locationat runtime:Skill should warn against hardcoded WS URLs and document the bridge's origin check.
2. wasm-bindgen onerror shim crash
Bridge proxy fires
new Event('error')(nofilename/message). wasm-bindgen's onerror handler callsevent.filename→undefined→ wbg crash:Surfaces as a console error during normal operation. Smoke tests asserting
consoleErrors === []will fail. Allowlist the message until wbg upstream fixes, or use--catchon the import.3. fdev port targeting
fdevdefaults to port 7509. Targeting an isolated test node needs:Argument order matters:
--portbeforeexecute,--code/--parametersbefore thecontractsubcommand. Skill already says this — worth a more prominent callout because the failure mode isput failed after 4 attemptswhich gives no hint about port.4. Monotonic version requirement for web-container updates
On-chain web-container update check is strictly monotonic. Common broken schemes:
git rev-list --count HEAD) — monotonic per branch but starts at ~22, far below versions already accepted under broken schemesWorking scheme:
date -u +%s(unix timestamp at sign time). Tradeoff: contract IDs no longer reproducible from source — the committed snapshot becomes the authoritative ID.5. Persistent vs ephemeral test nodes
--idcreates ephemeral temp directories wiped on restart, destroying delegate secrets (signing keys, app data). Use--data-dirfor persistent isolation. Skill already covers — keep.6. Mobile browser WASM cache
Firefox mobile especially aggressive. After republish, clear cache or use
?_v={timestamp}cache-buster. Skill covers — keep.7. Delegate-not-found loop (open question)
Observed during identity-management delegate calls on a fresh local node:
Loops indefinitely on each call. Suggests delegate registration not persisting between calls, or migration probe path is hot. Worth investigating + documenting in skill if it's a known pattern.
8. GNU tar requirement
compress-webappneeds--sort=name --mtime=… --owner=0 --group=0flags only GNU tar supports. macOS BSD tar silently produces non-reproducible archives. Installgnu-tarbrew and detectgtar. Worth mentioning in dapp-builder when scaffolding signed-webapp pipelines.9. Browser MCP for dApp debugging
chrome-devtools MCP + Playwright MCP both work for diagnosing UI ↔ node interaction. Particularly useful for:
Could be a
local-devsubsection: "Debugging dApps via browser MCP".Suggested skill changes
local-dev: add "Gateway iframe + WS origin check" section with thewindow.locationderivation patternlocal-dev: expand fdev port section with theWS_API_PORTenv vardapp-builder: add "Reproducible signed webapps" section covering monotonic version, GNU tar, committed-snapshot vs reproducible-from-source tradeoffdapp-builder: add "WebSocket URL derivation" as a required pattern when the dApp loads inside the gateway iframe