# Vault apps — a private "For you" your app can't read

A **vault app** adds a private personalization layer to any web app. Your app renders its own UI as usual; a **vault**
you don't build — a verified Witbitz frame — reads the user's sealed profile and paints a personalized *"For you"* into
its own pixels. **Your app never sees the profile, never sees the take, and never handles the sign-in.** It just hands
its content in and gets a blind slot back.

It's the inversion of the usual deal: instead of your app collecting the user to personalize them, the personalization
happens in a vault the user owns, and your app learns nothing.

> **Status: preview.** Served from the preview origin during beta (`https://preview.witbitz-spaces.pages.dev/app.js`).
> The `WitbitzApp` surface below is small on purpose and meant to stay stable. It's the high-level SDK; for the raw
> room runtime see **[the SDK](./sdk.md)**.

## The whole app

This is **Beacon**, a news reader, in its entirety — every line a builder writes. It fetches its own headlines, renders
its own list, and hands the day's stories to the vault, which privately curates a briefing from them plus the reader's
profile.

```js
import { WitbitzApp } from 'https://preview.witbitz-spaces.pages.dev/app.js'

// 1. Declare the personalization: a name, where it renders, and the private editor's brief.
const app = await WitbitzApp.create({
  brand:  'Beacon',
  accent: '#c24a30',
  mount:  '#foryou',                 // the vault paints "For you" here — a bare <div>
  reader: true,                      // it's an article, not a chat
  agent:  `You are the reader's private news editor. curate({ articles }) →
           pick the 3-5 that matter to THEM, one bullet each, grounded in the articles.`,
  skills: { curate: { in: { articles: 'array' } } },
})

// 2. Fetch your OWN data, render your OWN UI.
const items = (await fetch('/api/feed').then(r => r.json())).items || []
renderHeadlines(items)               // your DOM, your styles — nothing to do with Witbitz

// 3. Hand the content in. That's the whole integration.
app.curate({ articles: items })
```

```html
<!-- the mount is a bare div; the vault sizes + paints it -->
<div id="foryou"></div>
```

Three moves: **declare** the personalization, **render your own UI**, **hand your content in**. There is no vault code,
no profile handling, no sign-in code, no layout wiring.

## The surface

`WitbitzApp.create(manifest)` is the whole API. The manifest:

| field | what it is |
|---|---|
| `brand` | your app's name, shown in the vault's trust chrome |
| `accent` | your accent colour for the "For you" card |
| `mount` | a CSS selector for where "For you" renders — a bare `<div>` |
| `reader` | `true` → render the take as an article, not a chat transcript |
| `agent` | the private editor's brief. It reads the user's sealed profile as context; write it to work over *whatever content you hand in* |
| `skills` | the typed calls your app may make, `{ name: { in: { field: type } } }`. Each becomes `app.<name>(args)` and is schema-gated server-side |
| `model` | *(optional)* model override for the editor |

The returned `app` gives you one method per skill (`app.curate(...)` above) plus `app.on(event, fn)`.

## What the vault does, so you don't

When your app is hosted inside a Witbitz vault, the SDK handles the entire personalization loop automatically:

- **detects** it's inside a vault and where to render;
- **tracks** your mount's geometry (on scroll and resize) and tells the vault where to paint;
- **shows** the vault's loading state while the take streams;
- **reserves** the height the vault reports, so your content flows below it;
- **re-personalizes** — re-runs your last call — the instant the reader edits their profile.

The vault itself — a separate verified frame — owns the **sign-in** (a real email-code login), the **profile** (edit it
once, it's yours, and it follows you across devices via a recovery code), and the **pixels** of the take.

## What your app never touches

- **The profile.** It lives in the vault, sealed to the user's identity. Your app can't read it; the editor gets it as
  private context. Nothing crosses to your code.
- **The take.** The vault renders it in its own frame, on a verified origin. Your app is a different origin — it gets a
  `SecurityError` if it even reaches for it, and it can't paint over it.
- **The sign-in.** The login renders in the vault's top frame. Credentials never enter your app.

Your app hands in *content* and gets back a *blind slot*. That's the guarantee, and it's enforced by the browser, not by
your good behaviour.

## Run it

Open your app **inside a vault** by pointing the vault at it:

```text
https://preview.witbitz-spaces.pages.dev/vault?app=<your-app-origin>/
```

The vault becomes the top-level page (it owns the login and the trust chrome), embeds your app in a sandboxed frame, and
paints "For you" over the mount. Standalone (opened directly, no vault) your app still runs — the take just renders in
the mount itself instead of the vault's pixels.

The preview vault needs no account and no registration — build and try the whole model first. When you're ready to make
it a hosted app that's yours (a tenant, a registered app identity, optionally rooms only your users may join), see
**[Register your app](./register-vault-app.md)**.

## Next

- **[Register your app](./register-vault-app.md)** — from the preview vault to a hosted, tenant-owned app.
- **[The SDK](./sdk.md)** — the room runtime `WitbitzApp` is built on.
- **[Trust model](./trust-model.md)** — why the app provably can't read what it renders.
- **[The certified app](./the-certified-app.md)** — verifying an app's egress lock end to end.
