# Persistence

kernl keeps the user's choices in the browser they made them in, and nowhere else. There is no kernl server, no sync and no account to log in to. This page lists every key kernl writes, what each holds, and what is deliberately never written.

## Where values go

`@kernl/core` never touches `localStorage` itself. It writes to `KernlHost.storage`, a `StorageLike` (`getItem`, `setItem`, `removeItem`) the host supplies. The shell app supplies `pickStorage()` from `apps/web/src/shell/host.ts`: `localStorage` when a probe write succeeds, otherwise a `Map` that lives as long as the page. In a private window that refuses storage, the shell still works and forgets everything on reload.

Inside the core, every key goes through `createNamespacedStore(storage, 'kernl')`, which prefixes each key with `kernl:` and stores JSON:

| Method | Behaviour |
| --- | --- |
| `getJson(key, fallback)` | Reads `kernl:<key>` and parses it. Returns `fallback` when the key is absent or the JSON is corrupt; it never throws, so a damaged value cannot stop the shell from booting. |
| `setJson(key, value)` | Serialises and writes `kernl:<key>`. |
| `remove(key)` | Removes `kernl:<key>`. |

The prefix keeps kernl's keys apart from kehto's and from the host app's in the same storage. Every key carries a `:v1` suffix so a future format can move to `:v2` without reading the old one wrongly.

## Keys the core writes

| Key | Written by | Holds |
| --- | --- | --- |
| `kernl:accounts:v1` | `createKernlAccounts` | applesauce's `AccountManager.toJSON(true)`: the serialised accounts, minus any that cannot be serialised. Rewritten whenever the account list changes. |
| `kernl:active-account:v1` | `createKernlAccounts` | The active account's id. Removed when no account is active. |
| `kernl:grants:v1` | `createGrants` | One record per verified applet build, keyed `<dTag>:<hash>`: `{ dTag, hash, domains, blocked }`. See [Identity and grants](https://kernlize.github.io/docs/concepts/identity-and-grants.md). |
| `kernl:relays:v1` | `createRelayTiers` | The four relay tiers, each a list of URLs. See [Relay tiers](https://kernlize.github.io/docs/concepts/relay-tiers.md). |
| `kernl:messaging:v1` | `createDefaultMessaging` | `MessagingPreferences`: the `enabled` provider ids and the optional `defaultProvider`. See [Messaging](https://kernlize.github.io/docs/concepts/messaging.md). |

On boot, `createKernlAccounts` restores the accounts and makes the stored id active if that account still exists. `createGrants` loads its records, and `createRelayTiers` and `createDefaultMessaging` start from the stored value when there is one.

## Keys the shell app writes

| Key | Written by | Holds |
| --- | --- | --- |
| `kernl:recent:v1` | `recent.ts`, through the host storage | The recently opened applets: up to eight `{ address, title, publisher, openedAt }`, newest first. Opening an applet moves it to the front; the launcher can forget one. |
| `kernl:layout:v1` | `window-store.svelte.ts`, to `localStorage` directly | `floating` or `tiled`, as a plain string. A refused write is ignored and the layout applies to the current page only. See [Layouts](https://kernlize.github.io/docs/concepts/layouts.md). |
| `kernl:preview:grants:v1`, `kernl:preview:relays:v1`, `kernl:preview:messaging:v1` | the offline preview (`?preview=1`) | The preview's own grants, tiers and messaging preferences, kept apart so that trying the preview never changes the live shell's state. |

The public website also remembers its colour scheme as `kernl-theme` (`light` or `dark`). That key belongs to the site pages, not to the shell.

## Keys kehto writes

kernl passes no persistence hooks of its own to `createShellBridge`, so kehto uses its defaults from `@kehto/shell`, which write to `localStorage` directly rather than to `host.storage`. In `@kehto/shell` 0.29.0 those are `napplet:acl` (the runtime ACL, which `kernl:grants:v1` mirrors per applet build), `napplet:manifest-cache`, `napplet-shell-secret`, `napplet-guid:<windowId>`, and the scoped keys behind the `storage` NAP domain, where an applet's own data lives. kernl does not read or migrate these keys; their format is kehto's.

## What is never persisted

- **Plaintext private keys.** `KernlAccountManager.addAccount` refuses the account types whose serialised form carries a raw secret (`PLAINTEXT_KEY_ACCOUNT_TYPES`: `nsec` and `seed-words`) with `denied:plaintext-key-persistence`, and restore drops any such entry it finds in storage. The only stored form of a local key is applesauce's `PasswordAccount`, whose `ncryptsec` is NIP-49 encrypted with the user's password. The password itself is never stored.
- **The preview account.** "Try the preview" creates a throwaway key account in a plain `AccountManager` that is not connected to storage, so it disappears on reload.
- **The guard profile.** `GuardFacade.setProfile` changes the profile for the running shell only. Every boot starts `strict` ([D-003](https://kernlize.github.io/docs/decisions.md#d-003)).
- **Open windows.** The window store persists only the layout. Applets are not reopened on reload; the recent list is how the user gets back to them.

Some stored values are sensitive even though they are not the user's key. A browser-extension account stores only its public key, because the extension holds the secret. A remote-signer account stores what applesauce needs to reconnect: the remote signer's public key, its relays, the pairing secret when there is one, and the client key of the pairing session in hex. That client key is not the user's identity key, but anyone who reads it from this browser's storage can talk to the remote signer as this session until the user revokes the pairing in the signer.

## Clearing it

Signing out clears the active account and keeps the account in the list, so it can be picked again. **Forget** on the sign-in screen removes a stored account. Removing everything kernl stored means clearing the site's storage in the browser. Nothing is held anywhere else, so there is nothing else to delete.

## Related

- [Identity and grants](https://kernlize.github.io/docs/concepts/identity-and-grants.md)
- [Relay tiers](https://kernlize.github.io/docs/concepts/relay-tiers.md)
- [Alpha status](https://kernlize.github.io/docs/alpha-status.md#what-does-not-work-yet)
