> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://ensforge.com/api/mcp` to find what you need.

# Storage and recovery

Storage belongs to the SDK configuration. Registration and Rhinestone funding share that storage
boundary, so your application does not need separate database adapters for each workflow.

## Choose a store

| Application                        | Default                      | When to customize                                             |
| ---------------------------------- | ---------------------------- | ------------------------------------------------------------- |
| SDK or core                        | In-memory storage            | Process restart, multiple workers or durable jobs             |
| React provider created from config | IndexedDB                    | Shared server state or a different browser persistence policy |
| Server                             | Supply your database adapter | Always, when users rely on restart recovery                   |

In-memory storage lasts only as long as the instance. Keep your SDK and provider adapters stable
instead of recreating them for every operation.

## Explicit in-memory storage

```ts [storage.ts]
import { createMemoryWorkflowStorage } from "@ensforge/core/storage";

export const storage = createMemoryWorkflowStorage();
```

Pass `storage` alongside the clients when constructing `new Ensforge({ ... })`. This is useful when
several SDK instances in one process must share workflows.

## Browser storage

```ts [storage.ts]
import { createIndexedDbWorkflowStorage } from "@ensforge/core/storage/browser";

export const storage = createIndexedDbWorkflowStorage();
```

The browser subpath keeps IndexedDB out of server imports. React uses it by default when the provider
constructs config. If you supply an existing config or storage instance, that choice is preserved.
Private keys still belong in a wallet or protected signer service, not IndexedDB workflow records.

## Database storage

Implement `WorkflowStorage` from `@ensforge/core/storage`, including atomic create and
compare-and-swap. Preserve the SDK's opaque record value and namespace. Read the
[storage contract](/core/guides/workflow-storage#custom-database-adapter) before implementing concurrency.
The [SQLite example](https://github.com/thenamespace/ensforge/blob/main/apps/docs/examples/hca/sqlite-storage.ts)
uses Node's built-in SQLite module. The
[React example](https://github.com/thenamespace/ensforge/blob/main/apps/docs/examples/hca/browser.tsx)
shows registration controls and reload recovery.

## Resume the right operation

* Registration: retain the operation ID and use `getHcaRegistration`, then `resumeHcaRegistration`.
* Provider calls: retain the submission; use the adapter's `serializeSubmission` and
  `restoreSubmission` when persisting it, with the expected chain, HCA, profile and plan fingerprint.
* Rhinestone funding: retain the funding ID and use `getFundingStatus` or `waitForFunding`.

Workflow storage saves progress, not your provider API key or session private key. Reconstruct a
compatible adapter from protected configuration after restart. Session-backed work must resolve the
same signer reference unless you explicitly authorize a replacement.

## Uncertain submissions

A timeout can happen after broadcast. Query saved progress before retrying, and use the dedicated
recovery action if the submission reference was lost. Do not delete the database, change IDs or
reset revisions to force another submission. Cancelling local work cannot cancel an on-chain transaction.

For a server-held session, the [remote registration API](/hca/remote) lets authenticated clients
advance saved work without receiving the key.
