Appearance
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. |
kernl:relays:v1 | createRelayTiers | The four relay tiers, each a list of URLs. See Relay tiers. |
kernl:messaging:v1 | createDefaultMessaging | MessagingPreferences: the enabled provider ids and the optional defaultProvider. See Messaging. |
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. |
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.addAccountrefuses the account types whose serialised form carries a raw secret (PLAINTEXT_KEY_ACCOUNT_TYPES:nsecandseed-words) withdenied:plaintext-key-persistence, and restore drops any such entry it finds in storage. The only stored form of a local key is applesauce'sPasswordAccount, whosencryptsecis 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
AccountManagerthat is not connected to storage, so it disappears on reload. - The guard profile.
GuardFacade.setProfilechanges the profile for the running shell only. Every boot startsstrict(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.