Skip to content

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:

MethodBehaviour
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 ​

KeyWritten byHolds
kernl:accounts:v1createKernlAccountsapplesauce's AccountManager.toJSON(true): the serialised accounts, minus any that cannot be serialised. Rewritten whenever the account list changes.
kernl:active-account:v1createKernlAccountsThe active account's id. Removed when no account is active.
kernl:grants:v1createGrantsOne record per verified applet build, keyed <dTag>:<hash>: { dTag, hash, domains, blocked }. See Identity and grants.
kernl:relays:v1createRelayTiersThe four relay tiers, each a list of URLs. See Relay tiers.
kernl:messaging:v1createDefaultMessagingMessagingPreferences: 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 ​

KeyWritten byHolds
kernl:recent:v1recent.ts, through the host storageThe 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:v1window-store.svelte.ts, to localStorage directlyfloating 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:v1the 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).
  • 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.

MIT licensed. Protocol authority: NIP-5D (nostr-protocol/nips PR #2303) and the NAPs (napplet/naps).