FRAMES
Your app.
Inside Facade.
A Frame is a web app embedded in Facade. It receives approved workspace context and can connect to services beyond Facade.
Build the Frame with your own web stack. Facade hosts its UI in an iframe; your app owns its interface and integrations. Store distribution will require review before a Frame is listed. Local installations of extensions / Frames must be available without store submission, publisher approval, or review.
Available today
The desktop Frames rail opens a searchable store sidebar. Its bundled entries include OpenRouter and the Facade Model Picker, a built-in React interface backed by imported JSON. These are built-in React interfaces, not iframe apps.
- Install or override the model-picker dataset from a local JSON file without review, or restore the bundled picker.
- Imports are validated and saved in local storage; model rankings feed the soul and chief-of-staff pickers.
- OpenRouter ships installed by default. Selecting a soul checks a saved key, prompts for a key only when needed, and resumes setting the agent’s soul after successful setup. Native credentials stay in the operating system credential store; a configured OPENROUTER_API_KEY environment variable can also be used.
- Missing coding harnesses offer official installation instructions and a check-again action. Harness authentication remains in the harness’s own sign-in flow.
- There is no general iframe loader, context bridge, permission broker, or store submission/review service yet.
Implementation references: desktop/src/components/Frames.tsx and desktop/src/lib/modelFrames.ts. Everything below is the proposed general Frame contract.
Receiving context
Use window.postMessage for initial context and later updates. Do not depend on shared JavaScript globals: a separately hosted Frame has a different origin.
| Channel | Proposed use |
|---|---|
| URL parameters | Public bootstrap values only: protocol version, instance ID, and expected parent origin. A parent-origin parameter must match the Frame's trusted deployment allowlist. |
| postMessage | Permission-filtered context after a ready handshake, followed by replacement snapshots when context changes. |
| Shared variables | No cross-origin contract. Do not read the parent DOM or assume access to Facade storage. |
Start with display context: theme and locale. Workspace ID, display name, or selection require explicit grants. Omit ungranted fields. Never include local filesystem paths, raw conversations, credentials, or API keys by default. If access is revoked, send a reduced snapshot and clear previously cached values.
Message protocol Proposed v1
- Facade loads the installed Frame’s permitted entry point and assigns a unique
instanceId. - The Frame installs its listener, then sends
frame.readyto the trusted parent origin. - The host validates origin, source window, protocol version, instance ID, and payload shape, then replies with
facade.context. - Updates replace the prior context. Use a monotonically increasing
revisionto ignore stale snapshots. - On navigation, reload, or unmount, discard the session and listeners. On a handshake timeout, show a retry state rather than sending context blindly.
{
"protocol": "facade.frames",
"version": 1,
"type": "facade.context",
"instanceId": "frame-instance-123",
"revision": 1,
"context": {
"theme": "dark",
"locale": "en-US",
"workspace": { "id": "workspace-123", "name": "Design team" }
}
}
Both sides must validate each message. The host checks event.source === iframe.contentWindow; the Frame checks event.source === window.parent. Both check the exact allowed origin and schema. Ignore unknown types and unsupported versions; never execute received code or treat messages as native commands.
Use a dedicated remote origin with a reviewed iframe sandbox policy. A candidate is allow-scripts allow-same-origin for a cross-origin Frame that needs its own origin; never serve untrusted Frames on the host origin. Additional sandbox capabilities require explicit host permission grants; store-distributed releases also undergo review. Without allow-same-origin, a sandboxed Frame has an opaque origin and this exact-origin protocol needs a different design.
The host must configure CSP, embedding policy, and Tauri capabilities before this runtime ships. Review does not itself enforce permissions. No Frame should receive Tauri IPC, shell, or filesystem access through this bridge.
Frame example
Illustrative client code for the proposed contract. Replace the example origin with an explicit allowlist of your supported Facade host origins. Actual production and development host origins still need to be specified.
const trustedHosts = new Set(['https://host.example.com']);
const params = new URLSearchParams(location.search);
const hostOrigin = params.get('parentOrigin');
const instanceId = params.get('instanceId');
let revision = -1;
if (!trustedHosts.has(hostOrigin) || !instanceId || parent === window) {
throw new Error('Unsupported Frame host');
}
function receiveContext(event) {
if (event.origin !== hostOrigin || event.source !== parent) return;
const message = event.data;
if (!message || message.protocol !== 'facade.frames' ||
message.version !== 1 || message.instanceId !== instanceId ||
message.type !== 'facade.context' ||
!Number.isSafeInteger(message.revision) ||
message.revision <= revision) return;
const context = message.context;
if (!context || !['light', 'dark'].includes(context.theme) ||
typeof context.locale !== 'string') return;
if (context.workspace !== undefined &&
(!context.workspace || typeof context.workspace.id !== 'string' ||
typeof context.workspace.name !== 'string')) return;
revision = message.revision;
document.documentElement.dataset.theme = context.theme;
// Replace app state with these validated fields; clear omitted fields.
renderContext({ theme: context.theme, locale: context.locale,
workspace: context.workspace });
}
// Supply renderContext in your app; use textContent or escaped rendering.
window.addEventListener('message', receiveContext);
parent.postMessage({ protocol: 'facade.frames', version: 1,
type: 'frame.ready', instanceId }, hostOrigin);
// On component teardown:
// window.removeEventListener('message', receiveContext);
This snippet is documentation, not a working Facade SDK. The embedding host must implement the matching handshake and context sender before a Frame can run in the desktop app.
Connecting to external APIs
A Frame can call its own backend or a third-party API. Context delivery and service authentication are separate: receiving a workspace ID does not authenticate a user to your service.
- Public browser APIs: use HTTPS and configure the API's CORS policy to accept your Frame origin. Embedding does not bypass CORS.
- Private APIs: keep long-lived secrets on your backend. Authenticate the user through your service; prefer short-lived, narrowly scoped sessions.
- OAuth: use the provider's supported authorization flow and validate redirects/state on your backend. Do not assume login pages or third-party cookies will work inside an iframe; plan an approved external sign-in flow.
- Data sharing: disclose which context leaves the Frame, where it goes, retention, and deletion. Request only the context needed for the feature.
Do not put tokens in iframe URLs or use Facade's desktop credentials. Network-domain declarations in a manifest describe intended access; runtime enforcement still needs implementation.
Frame manifest
Proposed installation metadata for local and store distribution, distinct from today's model-picker JSON. No current installer accepts this format. Permission names and schema remain draft.
{
"id": "com.example.project-dashboard",
"name": "Project dashboard",
"version": "1.0.0",
"protocolVersion": 1,
"entryUrl": "https://frames.example.com/dashboard",
"contextPermissions": ["workspace.summary"],
"networkDomains": ["https://api.example.com"],
"publisher": "Example Studio",
"supportUrl": "https://example.com/support",
"privacyUrl": "https://example.com/privacy"
}
The installed entry origin must match the host's message allowlist. Any redirect to another origin must fail closed. Installation grants and enforced network policies are future host responsibilities, not properties a manifest can enforce on its own.
The Frame store & review
Facade's Frame store will have a review process. The following lifecycle is a proposal; submission tooling, review criteria, and service timelines have not been finalized.
Local installations without review
Local installation is an independent distribution path for extensions / Frames. Users must be able to install a versioned local package without a store listing, review status, publisher approval, or a review-service connection. Local packages must not be rejected solely because they are unsigned or unreviewed. Installing locally does not submit or publish a package to the store.
Format validation, sandboxing, and user-granted permissions apply to both distribution paths. These checks run locally and must not depend on store approval. Local updates follow the same path, with user consent for expanded permissions. Users must be able to remove a local Frame or restore a bundled replacement.
Today this path is implemented for model-picker JSON catalogs through Frames → Install locally. General executable Frame packages and their permission/runtime handling remain proposed.
Store distribution
- Submit
Provide a versioned manifest, publisher identity, screenshots, test access, permission rationale, external domains, and privacy/support details.
- Validate & review
Check HTTPS and embedding, context handling, authentication, data practices, accessibility, and behavior against the declared purpose. A reviewer can request changes.
- Approve & list
Only approved submissions become store listings. Users should see publisher, version, requested context, and data disclosures before installing.
- Maintain
Submit changes for review. Expanded permissions need renewed consent. Define suspension and removal paths for harmful or noncompliant Frames.
A remote URL can change after approval. Before launch, define release integrity and monitoring controls so review applies to the served app, not just its metadata. Approval is not a guarantee of third-party uptime or safety.
Before you submit
- Document required context and behavior when permission is denied or revoked.
- Test handshake timeout, duplicate messages, stale revisions, navigation, and unsupported protocol versions.
- Reject messages from incorrect origins, windows, or instances and malformed payloads.
- Test loading, empty, offline, API error, and signed-out states.
- Support keyboard navigation, readable contrast, and responsive layouts.
- Provide a privacy policy, support contact, and a reproducible review path.
Browser references
Underlying browser behavior: MDN: postMessage, iframe sandbox, and CORS. These references describe web standards, not an implemented Facade runtime.