Skip to Content
Card SDKAPI referenceStorage & settings

Storage & settings

Storage

A card’s page has no localStorage, indexedDB or cookies — its sandbox has no origin to keep them in. card.storage is the replacement: JSON values the app keeps on disk, atomically, across reloads, suspension and restarts. No permission needed.

// This card on the canvas (instance scope). const draft = await card.storage.get('draft', '') // string, '' when missing await card.storage.set('draft', `${draft}\nmore`) await card.storage.set('runs', [{ at: Date.now(), failed: 3 }]) const runs = await card.storage.get<{ at: number; failed: number }[]>('runs') // undefined when missing await card.storage.delete('draft') const keys = await card.storage.keys('run:') // keys starting with a prefix const { bytes, quota } = await card.storage.usage() render(runs?.length, keys, bytes, quota) // Shared by every copy of this card, on every canvas (package scope). await card.storage.package.set('lastSync', Date.now()) card.storage.onChange(({ key, byInstance }) => { if (key === 'lastSync') render(`updated by card ${byInstance}`) })
MethodResult
get(key)The value, or undefined.
get(key, fallback)The value, or fallback. The result is typed like the fallback.
set(key, value)Stores any JSON value.
delete(key)
keys(prefix?)string[]
clear()Deletes every key of that scope.
usage(){ bytes, quota, keys }
onChange(handler)Another copy of this card wrote a package key: { scope, key, byInstance }.

The same methods exist on card.storage.package.

Limits: keys ≤ 256 characters, values ≤ 1 MB, ≤ 10 000 keys; 5 MB per card, 20 MB per package. Going over fails with QUOTA_EXCEEDED. Writes reach the disk within about a quarter of a second and are flushed when the app quits.

What happens to it: instance storage is deleted with the card. Package storage survives uninstalling unless the user ticks Also delete its saved data and secrets. Storage survives updates — keep your stored shapes readable by newer versions (store a version field if you might change them).

Save before you are unloaded: a card that is hidden for a while is suspended, and its page is thrown away. Use card.lifecycle.onSuspend to write what is still in memory.

Settings

Declare fields in the manifest’s settings; the app draws the form (from the card’s ⋯ → Settings…, or when you call card.settings.open()), validates it and stores the values. Your card reads them:

const command = card.settings.value<string>('command') ?? 'npm test' const all = card.settings.values // defaults filled in, secrets never included const hasKey = card.settings.hasSecret('apiKey') // true when the user saved one card.settings.onChange((snapshot) => { render(snapshot.values['command'], snapshot.secrets['apiKey']) }) // Change your own non-secret settings (a toggle in your UI, say). Validated like the form. await card.settings.set({ compact: true }) // Open the form on the card (only while it is visible). if (!hasKey) await card.settings.open() render(command, all)
MemberWhat
valuesCurrent values, defaults filled in. Kept current.
secretsRecord<key, boolean>: which secret settings are set.
value(key)One value, typed by you.
hasSecret(key)Whether a secret setting is set.
get()A fresh SettingsSnapshot from the app.
set(values)Changes non-secret settings; every copy of the card gets settings.changed. Invalid values fail with INVALID_PARAMS. At most 60 a minute.
open()Opens the settings form on the card. NOT_VISIBLE if the card is off screen.
onChange(handler)The user saved the form, or set changed something.

Scopes. A field with "scope": "package" has one value for every copy of your card; the rest are per card.

Secrets. A secret field is typed only into the app’s own dialog — in the settings form its row shows just “set” or “not set” and opens that window-wide dialog — and stored encrypted by the app. The card can never read it — not through values, not through any method. To use one, put {{secret:<key>}} into a network request header; the app fills it in on its way out, only for internet hosts your card was granted (never for localhost). Do not build your own “paste your API key” field inside the card: the key would then be yours to leak, and users are told that NeuroSquad never asks for keys inside a card.