# packages/storage/README.md · versecafe/solid-native

[View on GitCafe](https://git.cafe/versecafe/solid-native/blob/8ec111b04c5c361b1bd7ff86ec086a87a275264f/packages/storage/README.md)

Repository: [versecafe/solid-native](https://git.cafe/versecafe/solid-native)

Visibility: public

Requested revision: 8ec111b04c5c361b1bd7ff86ec086a87a275264f

Requested commit: 8ec111b04c5c361b1bd7ff86ec086a87a275264f

Commit: 8ec111b04c5c361b1bd7ff86ec086a87a275264f

Blob: c30aee303cac02aea79e99ad5893b930da4a4a49

Size: 2406 bytes

[Immutable source](https://git.cafe/versecafe/solid-native/blob/8ec111b04c5c361b1bd7ff86ec086a87a275264f/packages/storage/README.md?format=markdown)

````
# Storage

```ts
import * as storage from '@solid-native/storage';
import * as secure from '@solid-native/storage/secure';

const settings = storage.open('settings');
await settings.set('theme', 'dark');
const theme = await settings.get('theme');
await settings.remove('theme');

await secure.set('token', token, { access: 'biometric' });
const savedToken = await secure.get('token', { reason: 'Unlock your account' });
await secure.remove('token');
```

Ordinary storage persists finite plain JSON through NSUserDefaults / SharedPreferences.
Missing keys return `undefined`; stored JSON `null` stays `null`. Values are
snapshotted at invocation, and handles opened on the same namespace share ordering
within one JS runtime. There are no multi-key transactions, queries, schema
validation, or database-size guarantees. This is preferences storage, not a
database or secret store. Keys and namespaces use letters, digits, `.`, `_`, `-`.

Secure storage holds strings in iOS Keychain or AES-GCM ciphertext backed by
Android Keystore. Encryption keys never enter JS. The access policy belongs to the
stored item, not an independent `device.verify` prompt:

- `unlocked` (default): available while the device is unlocked.
- `device`: OS device-owner authentication, including PIN/passcode fallback.
- `biometric`: current enrolled strong biometrics, without PIN fallback; enrollment
  changes can invalidate the key.

Unavailable policies reject; they never fall back to weaker protection. Android
requires API 28+ for this secure store and API 30+ for `device`. Android protected
writes as well as reads can prompt; overlapping secure operations reject `E_BUSY`.
iOS stores device-only items (not iCloud-synchronized secrets). Passkey provider
synchronization is separate from secure storage. Keychain values may survive an
app reinstall; neither platform's data should be treated as a portable backup.
Biometric invalidation is not always distinguishable from unavailable/missing
key material. Handle errors and require fresh account login when necessary.

The sample host registers both native modules and Android adapters. Android needs
`USE_BIOMETRIC` and a foreground FragmentActivity for prompts; iOS needs
`NSFaceIDUsageDescription`. Physical-device locked/unlocked, enrollment-change,
and secure-hardware behavior need device verification; simulator persistence tests
do not prove those protections.

````
