App-faced Spaces
A Space can wear an app: the full-screen surface is your app site, and the private room rides over it as a pull-up card — the input line sits exactly where a normal Space's does. The agent's tools execute against the app pane over an origin-pinned bridge, and the card's grab bar shows exactly the structured fields that crossed to your site — nothing else ever does. The reference is the Event Finder concierge: a Space whose "chat history area" is a live commercial events site.
Status: the app-site mode and the wire contract below are live (the Event Finder demo runs on them). The sign-in patterns in §3 are documented designs — the recommended shapes, not yet shipped primitives.
1. Declaring an app site
A branded deployment declares its app in the brand config:
{ "agentName": "Concierge", "persona": "…", "appSite": "https://your-app.example", "tools": [ … ] }Opening a Space on that origin renders appSite full-screen with the room as the card. Every Space on the origin
gets the app face, the brand's tools, and per-room resume: the last search is stored in the room's own sealed
state doc, so returning to a room (any device, any member) replays it.
Three gates for a new app-site origin, all fail-closed:
- the Spaces app's CSP must allow framing it (
SPACE_VAULT_APP_ORIGINSat deploy); - the app site's
frame-ancestorsmust list the whole ancestor chain (the Spaces origin, and the vault origin if the app runs vaulted — browsers check every ancestor); - the app site should origin-pin inbound messages to the Spaces origin(s).
2. The wire contract
The pane speaks two messages down, one up — all postMessage, origin-pinned both ways:
| direction | message | meaning |
|---|---|---|
| Space → pane | { type: "search", args } |
run a query; args are exactly the disclosure-strip fields |
| Space → pane | { type: "focus", index } |
highlight the Nth already-shown result |
| pane → Space | { type: "results", events: [...] } |
the public results — returned into the agent's turn, so it recommends from live data |
The pane sees only these messages. It never receives the conversation, names, or reasons — the same-origin policy walls it off, and the bridge carries nothing but the structured arguments the strip displays.
3. Signing into the app pane
Three patterns, in ascending capability. A key browser fact shapes all of them: the pane is an iframe, so it cannot see your normal first-party login cookies (third-party cookies are blocked or partitioned), and most authenticated sites refuse framing entirely — an arbitrary site can't be pointed at and inherit your session. A cooperating app site, however, has clean options.
3a. No sign-in (public data) — what the demo ships
The pane serves public data keyed by nothing. Simplest, fully anonymous toward the site: it learns only the strip's fields per query. Right default for discovery-type apps.
3b. Personal account (per member)
The standard embedded-app pattern (the same shape Spaces' own central auth broker uses):
- the pane opens its login in a popup — top-level, so cookies are first-party and Google/password/passkeys work normally;
- the popup hands the session back to the pane, which keeps it in partitioned storage (a
Partitionedcookie or a token in the iframe's storage). Partitioned storage is stable per embedding origin, so you sign in once per device and stay signed in across rooms and reloads.
What it buys: account prices, saved payment, a "buy" that completes. Disclosure honesty: the site can now join
your queries to your identity — the strip should say so (shared → … · as you@example.com). Each member is signed
into their own account, so panes may show member-specific views, and tool results come from whoever asked.
3c. Shared room account (a "party" account) — the room-native pattern
If all members use one stateful account, the site's own backend becomes the room's shared surface: one cart, one favorites list, one booking — every member's pane converges by definition, with no extra sync protocol.
The elegant part is how the account is shared: never a password. The site issues a scoped session token (a party token), and the token rides sealed inside the room — in the same encrypted per-room state doc that already carries the resume search:
- Invite to the room = access to the account. A new member's pane receives the token over the bridge and is signed in with zero ceremony. The room key already gates who's in; the site token inherits exactly that boundary.
- The platform never sees it — the token lives in the mk-sealed doc, end-to-end encrypted like the chat.
- Revocation = rotate the token when membership changes (the same epoch model a membership change already implies).
- The site sees one identity doing minimal structured queries; the strip shows
as <party account>.
Caveats: one identity for N people (right for a family or a trip crew, wrong where per-person accountability matters); concurrent edits are the site's job (carts handle this well); token rotation must accompany member removal, or a removed member's pane keeps working until expiry.
Choosing
| site learns | shared state | best for | |
|---|---|---|---|
| 3a public | queries only | none (per-pane) | discovery, demos |
| 3b personal | queries + who you are | per member | purchases, personal accounts |
| 3c party token | queries + one shared identity | the site's own state, room-wide | planning together: shared carts, group bookings |
Next
- The Concierge — a real app, end to end — the reference app-faced Space.
- Embed — the host-page bridge this contract descends from.
- Trust model — where the disclosure boundary sits.
Machine-readable source: app-spaces.md · Generated 2026-08-29T18:46:30Z · build f3ff88cc · every doc in one fetch: llms-full.txt (HTML) · ← concierge-app · api-reference →