Skip to content

feat(tangle-cloud): sandboxed iframe blueprint apps#3175

Merged
drewstone merged 1 commit intodevelopfrom
feat/iframe-blueprint-apps
May 6, 2026
Merged

feat(tangle-cloud): sandboxed iframe blueprint apps#3175
drewstone merged 1 commit intodevelopfrom
feat/iframe-blueprint-apps

Conversation

@drewstone
Copy link
Copy Markdown
Contributor

Closes #3171.

Adds first-party iframe support for blueprint apps hosted under *.blueprint.tangle.tools / *.blueprint.tangle.sh. Designed to onboard the two apps Tangle Labs already operates (ai-trading-blueprint arena, ai-agent-sandbox-blueprint console) without opening the iframe surface to arbitrary publishers.

Trust gate (AND of all four)

  1. Ops kill switch (VITE_BLUEPRINT_IFRAME_ENABLED) — single env flip turns iframe mode off everywhere, falls back to link-out. Default off.
  2. Iframe-eligible publisher allowlist — distinct from the verified-publisher set. Defaults to `tangle`, `tangle-labs`. Adding a new publisher is a governance call (env var `VITE_BLUEPRINT_IFRAME_PUBLISHERS`), not a self-serve flow.
  3. Host suffix allowlist — `.blueprint.tangle.tools`, `.blueprint.tangle.sh`, plus exact entries in the existing trusted-host list. Wildcard form lets us onboard new per-blueprint subdomains without redeploying.
  4. Per-manifest contract grants — the IPFS metadata declares which `{chainId, address, selectors[]}` triples the iframe is allowed to ask the wallet to call. Anything outside is rejected before the approval modal surfaces.

Security architecture

Layer Defence
iframe element `sandbox="allow-scripts allow-forms"` — no `allow-same-origin` (opaque origin, can't reach `parent.localStorage` / cookies / `window.parent.ethereum`); no `allow-top-navigation` (no parent-window phishing redirect)
iframe element `allow=""` — deny-all Permissions-Policy at the element level
iframe element `referrerPolicy="no-referrer"`
Parent CSP `frame-src 'self' https://.blueprint.tangle.tools https://.blueprint.tangle.sh ...`; `frame-ancestors 'self'`
Parent header `Permissions-Policy` denies camera/mic/geo/payment/usb/bluetooth/etc.
Parent header `X-Frame-Options: SAMEORIGIN` (legacy mobile webview clickjack guard)
postMessage gate Strict equality check on `event.origin` AND `event.source === iframe.contentWindow` — origin spoofing via lookalike or popup-from-trusted-origin both blocked
postMessage gate Typed protocol validation: bounded payload sizes (4KB messages, 128KB calldata), ASCII-printable correlation IDs ≤128 chars, integer wei strings, `isAddress` / `isHex` viem validators
postMessage gate Semantic policy: contract + selector + chain allowlist enforced BEFORE surfacing the approval modal so denied requests never prompt the user
Approval modal Prominently attributes requests to `{appDisplayName} at {origin}`, shows decoded calldata preview, routes signing through the dapp's existing wagmi flow (`useSendTransaction` / `useSignMessage` / `useSwitchChain`)

What's NOT in scope

  • Third-party iframe (anyone outside `IFRAME_ELIGIBLE_PUBLISHERS`) — still rejected, downgraded to link-out.
  • Publisher-shipped JavaScript bundles in the dapp origin — never. The iframe IS the trust boundary.
  • Tier-3 declarative + custom widgets — deferred for security reasons.

Manifest authoring

For each iframe-eligible blueprint, the IPFS metadata's `blueprintUi` block adds:

```json
{
"externalApp": {
"url": "https://trading-arena.blueprint.tangle.tools/\",
"mode": "iframe",
"label": "Open Arena",
"iframe": {
"appId": "trading-arena",
"allowedChainIds": [3799],
"contracts": [
{
"chainId": 3799,
"address": "0x...",
"selectors": ["0xa9059cbb", "0x095ea7b3"]
}
],
"allowReadAccount": true,
"allowChainSwitch": false,
"allowPopups": false
}
},
"publisher": { "namespace": "tangle" }
}
```

Onboarding a new iframe app (runbook)

  1. Deploy the bundle to `.blueprint.tangle.tools` (or `.sh`)
  2. Publish manifest with `mode: "iframe"` + the block above
  3. Set `VITE_BLUEPRINT_IFRAME_ENABLED=true` if not already
  4. (For non-Tangle namespaces) add the namespace to `VITE_BLUEPRINT_IFRAME_PUBLISHERS` — governance step
  5. Verify the manifest renders inline; verify postMessage signing flow lands a real tx

Files

```
apps/tangle-cloud/src/blueprintApps/
├── policy.ts # iframe gates + kill switch
├── manifest.ts # parser surfaces iframe config
└── components/
├── BlueprintAppFrame.tsx # hardened <iframe>
├── BlueprintAppFrameHost.tsx # frame + bridge + modal
├── IframeAppApprovalModal.tsx # signing UI with attribution
└── BlueprintAppLandingPage.tsx (modified) # mounts the host inline

apps/tangle-cloud/src/blueprintApps/iframe/
├── types.ts # iframe config types
├── manifest.ts # IPFS metadata parser
├── protocol.ts # typed postMessage protocol
├── origin.ts # origin/source equality checks
├── policy.ts # semantic capability gate
└── useIframeBridge.ts # parent-side message handler

apps/tangle-cloud/netlify.toml # CSP + Permissions-Policy + XFO
```

Test plan

  • Unit: 42 new tests across protocol/policy/origin/manifest (full suite 105 pass)
  • Lint clean
  • Typecheck clean
  • Build succeeds
  • Staging: set `VITE_BLUEPRINT_IFRAME_ENABLED=true`, point manifest at staging URL, verify iframe renders + handshake completes
  • Staging: postMessage signing — confirm txHash returns to iframe via correlationId
  • Staging: confirm denied request (out-of-allowlist contract) returns error to iframe without prompting user
  • Production: kill-switch flip — verify iframe falls back to link-out across both apps

Adds first-party iframe support for blueprint apps hosted under
*.blueprint.tangle.tools / *.blueprint.tangle.sh, gated behind
- ops kill switch (VITE_BLUEPRINT_IFRAME_ENABLED)
- iframe-eligible publisher allowlist (distinct from verified-publisher)
- host suffix allowlist
- per-manifest contract + chainId + selector grants

Security stack:
- iframe sandbox="allow-scripts allow-forms" (no allow-same-origin,
  no allow-top-navigation) so the frame runs in an opaque origin and
  cannot reach parent.localStorage / cookies / window.parent.ethereum
- iframe allow="" (deny-all Permissions-Policy at element level)
- parent-side CSP frame-src allowlist + frame-ancestors 'self'
- parent-side Permissions-Policy denying camera/mic/geo/payment/usb/
  bluetooth/etc.
- X-Frame-Options: SAMEORIGIN on parent for legacy webview clickjack
  protection
- strict origin + source equality on every postMessage event
- typed postMessage protocol with bounded payload sizes, ASCII-only
  correlation ids, calldata size cap, value (wei) string validation
- semantic policy gate: contract+selector+chain allowlist BEFORE the
  approval modal surfaces, so denied requests never prompt the user
- approval modal renders the iframe app's display name + origin so
  users can attribute requests; sign flow goes through wagmi's
  useSendTransaction / useSignMessage / useSwitchChain in the parent

Wallet flow stays in the parent: iframe never touches window.ethereum,
sign requests bubble up via correlationId, parent posts results back
keyed on that id.

42 new unit tests cover protocol validation, semantic policy,
manifest parsing, origin checks.
@drewstone drewstone requested a review from AtelyPham as a code owner May 6, 2026 22:13
@netlify
Copy link
Copy Markdown

netlify Bot commented May 6, 2026

Deploy Preview for tangle-cloud ready!

Name Link
🔨 Latest commit 947293d
🔍 Latest deploy log https://app.netlify.com/projects/tangle-cloud/deploys/69fbbd21f40d9a00083723d8
😎 Deploy Preview https://deploy-preview-3175--tangle-cloud.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 6, 2026

Deploy Preview for tangle-dapp ready!

Name Link
🔨 Latest commit 947293d
🔍 Latest deploy log https://app.netlify.com/projects/tangle-dapp/deploys/69fbbd21487cad00082d1750
😎 Deploy Preview https://deploy-preview-3175--tangle-dapp.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 6, 2026

Deploy Preview for tangle-leaderboard ready!

Name Link
🔨 Latest commit 947293d
🔍 Latest deploy log https://app.netlify.com/projects/tangle-leaderboard/deploys/69fbbd21d13ccd0008d710cb
😎 Deploy Preview https://deploy-preview-3175--tangle-leaderboard.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@drewstone drewstone merged commit 5176efb into develop May 6, 2026
20 checks passed
@drewstone drewstone deleted the feat/iframe-blueprint-apps branch May 6, 2026 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Host ai-trading-blueprint arena + ai-agent-sandbox-blueprint UI on cloud.tangle.tools via sandboxed iframe

1 participant