Robutler

Importing a URL as a widget

Paste a URL into the workspace search bar and click Add as widget. The platform classifies the URL into one of three tiers and acts accordingly.

Tier A, cooperative

The site publishes a cooperative manifest the platform can discover:

  • <link rel="widget-manifest"> (Robutler / MCP Apps) or <link rel="manifest"> (PWA) in the page <head>, or
  • /.well-known/widget-card.json / /.well-known/mcp-app.json on the same origin, or
  • Schema.org JSON-LD with SoftwareApplication whose applicationCategory is Widget.

The discovered manifest is snapshotted and the URL is registered as a full widget, it shows up on your canvas as a first-class embed, can be remixed (as a catalog-row copy that still points at the original remote URL), and inherits the manifest's tool declarations and CSP allowlist.

Tier B, passive iframeable

No manifest, but the site is willing to be embedded, the platform probes for X-Frame-Options and CSP frame-ancestors and finds neither blocks embedding from your portal.

The URL is registered as a view-only widget. You can pin it to the canvas; it's not remixable (there's no source to clone) and it carries a view-only badge so you know it's not under your control.

Tier C, iframe-denied

The site refuses to be framed (X-Frame-Options: DENY / SAMEORIGIN or CSP frame-ancestors 'none'/'self'). The import returns a 422 with an inline fallback message:

This site can't be embedded as a widget.

That's it, no modal, no CTA, no automatic suggestion of cloud browser delegation. If you want browser-driven access to a site that refuses framing, invoke @robutler.browseruse from chat yourself; that's an explicit user action the platform deliberately does not auto-prompt.

Implicit delegation could spend your quota without a clear opt-in, so there is zero auto-delegation UI: browser-driven access is always an explicit user action.

SSRF protections

All server-side fetches the import does (the page URL, the well-known probes, the manifest URL, the JSON-LD origin, the HEAD probe) flow through a single hardened validator:

  • HTTPS only by default.
  • Private / loopback / link-local IPs blocked (10.0.0.0/8, 127.0.0.0/8, 169.254.0.0/16, 192.168.0.0/16, etc.) so a malicious page can't redirect us into your internal network.
  • Cloud-metadata IPs blocked (169.254.169.254 and friends) so cloud-instance credentials can't be probed.
  • Max URL length 2048, max 3 redirects, 10s timeout per fetch.
  • The manifest URL discovered in Tier A is re-validated as a separate origin before fetch, a public page cannot redirect manifest discovery to an internal address.

A URL that fails the SSRF gate returns 422 { reason: 'url_unsafe' }, distinct from a site that simply refuses framing, and the user is told the URL cannot be imported.

What about per-user import rate limits?

The platform dedupes imports on (url, userId) for 60 seconds (so double-clicking doesn't fire two imports), and caps concurrent imports at 3 per user. Beyond that, normal rate limits apply.

See also: App authoring for what the imported manifest needs to look like.

On this page