---
title: "Storage"
url: "https://effect-auth.itsbroly.com/reference/storage/"
description: "Public storage service contracts, result semantics, atomic operations, SQLite adapters, and migrations."
---



Effect Auth exposes focused Effect services, not one `Storage` service. Programs require only tags used by enabled features. Exported `*StoreService`, record, row, and input types are the authoritative signatures; construct implementations with each tag's `make` function.

For deployment choices and ownership, see [Storage Overview](/storage/). For implementation and concurrency guidance, see [Custom Database](/storage/custom-database/).

## Base services [#base-services]

The base contracts are exported from `@effect-auth/core/Storage` and fail with `StorageError`.

| Service             | Rows and responsibility                                                                                                             | Result-sensitive mutations                                                                                                                                                                     |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `UserStore`         | Identity-neutral `NewUserRow` / `UserRow`; lookup by ID and insert                                                                  | User lifecycle stays separate from identifiers and credentials                                                                                                                                 |
| `IdentityStore`     | Identity lookup and lifecycle for global email and global/tenant username or custom kinds                                           | Verification, primary selection, replacement, and revocation are identity-scoped CAS mutations; active-value uniqueness permits immediate reuse after revoke/replace                           |
| `RegistrationStore` | Atomic user + identity + password credential creation                                                                               | The transaction either commits all three records or none                                                                                                                                       |
| `CredentialStore`   | `NewPasswordCredentialRow` / `PasswordCredentialRow`; password lookup, insert, update                                               | `updatePassword` returns the updated row as `Option`                                                                                                                                           |
| `SessionStore`      | `NewSessionRow` / `SessionRow`; server-only authentication events, derived summaries, lookup, refresh, rotation, claims, revocation | `assureAndRotate` compare-and-swaps expected evidence/secret state, writes derived summaries, rotates the secret, and invalidates the old token atomically; omitted claim fields are preserved |
| `VerificationStore` | `NewVerificationRow` / `VerificationRow`; one-time challenges and cleanup                                                           | `consume` returns `Option` and is atomic; `deleteExpired` returns the deleted count                                                                                                            |

`SessionStore.findById` returns stored rows even when expired or revoked; policy interprets lifecycle fields. `VerificationStore.consume` succeeds only when a row exists, is unconsumed, matches an optional type, and has `expiresAt > consumedAt`. Concurrent consumers have at most one winner.

`StorageError` carries `entity`, `operation`, `message`, and optional `cause`. `StorageError.fromUnknown(entity, operation, cause)` maps backend failures without exposing row contents or secrets.

## Feature-owned services [#feature-owned-services]

Feature contracts and errors live in the named public module. The maintained SQLite bundle implements these services. `*` requires eligibility and mutation in one atomic statement or transaction; `CAS` also requires the expected prior state.

| Module               | Public service                                                    | Contract summary and atomic operations                                                                                                                          |
| -------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AuditLog`           | `AuditLogStore`                                                   | append/query audit events                                                                                                                                       |
| `LoginApproval`      | `LoginApprovalReviewStore`                                        | create and review approval records; review transition `*`                                                                                                       |
| `LoginRisk`          | `LoginRiskHistory`                                                | record/query risk history                                                                                                                                       |
| `TrustedDevice`      | `TrustedDeviceStore`                                              | device lookup, touch, revoke, cleanup                                                                                                                           |
| `Passkey`            | `PasskeyCredentialStore`                                          | credential lookup/list; sign-count update `CAS`; revoke                                                                                                         |
| `Totp`               | `TotpFactorStore`                                                 | factor lifecycle; confirm and last-used update `*`                                                                                                              |
| `RecoveryCode`       | `RecoveryCodeStore`                                               | issue/list/revoke codes; one-time use `*`                                                                                                                       |
| `ApiKey`             | `ApiKeyStore`                                                     | ID/prefix lookup, list, use update, revoke                                                                                                                      |
| `RefreshToken`       | `RefreshTokenStore`                                               | ID/hash lookup and family lifecycle; rotation inserts the replacement and retires the source atomically `*`; reuse marking                                      |
| `Jwt`                | `JwtRevocationStore`                                              | store and find JWT revocations; failures use `JwtError`                                                                                                         |
| `OAuth`              | `OAuthAccountStore`, `OAuthIdentityBridgeStore`                   | provider-account links and unlinking; atomic user + optional email identity + provider-account creation for OAuth JIT                                           |
| `OAuth`              | `OAuthClientStore`, `OAuthConsentStore`, `OAuthClientSecretStore` | clients, consent, and hashed client secrets                                                                                                                     |
| `OAuth`              | `OAuthAuthorizationCodeStore`                                     | authorization-code lookup and one-time `markConsumed` `*`                                                                                                       |
| `OAuth`              | `OAuthProviderModeTokenStore`, `OAuthProviderTokenVault`          | provider-token and encrypted-vault lifecycle; rotation `*`                                                                                                      |
| `DomainVerification` | `DomainVerificationStore`                                         | owner/domain lookup and status transitions `*`                                                                                                                  |
| `SecurityTimeline`   | `SecurityTimelineStore`                                           | append and query security events                                                                                                                                |
| `Webhook`            | `WebhookOutboxStore`                                              | enqueue, lease due work `*`, mark outcomes, delete old rows                                                                                                     |
| `Webhook`            | `WebhookReplayStore`                                              | idempotent replay claim `*` and expiry cleanup                                                                                                                  |
| `Permission`         | `PermissionStore`                                                 | CAS-protected definition lifecycle; direct and role grants; role mappings; deterministic active/inactive/exact-scope queries; exact/global authorization checks |

`RateLimitStore` is public but is not in the maintained SQLite bundle. Supply a feature-specific implementation when required.

`IdentityManagement` relies on storage, not a preflight availability query, for correctness. Replace, revoke, and primary mutations match owner, active state, and `expectedUpdatedAt`; revoke additionally prevents removing the final login-eligible identity. Custom adapters must make those checks and writes atomic. The OAuth bridge likewise commits its new user, optional trusted verified-email identity, and provider account together. A provider-account race resolves to the winner; an email uniqueness race becomes an explicit-linking result rather than a partial user or silent merge.

## Results, time, and encoding [#results-time-and-encoding]

| Shape              | Meaning                                                                         |
| ------------------ | ------------------------------------------------------------------------------- |
| `Option.some(row)` | Lookup found a row, or a conditional mutation won and returns its resulting row |
| `Option.none()`    | Missing or ineligible row; successful operation, not storage failure            |
| `readonly Row[]`   | Successful list; no matches is an empty array                                   |
| `boolean`          | Claim/admission result, as defined by the owning service                        |
| `number`           | Affected-row count for cleanup or deletion                                      |
| `void`             | Success; missing rows may be an intentional idempotent no-op                    |

Driver, timeout, uniqueness, serialization, and decode failures belong in the Effect error channel: `StorageError` for base stores and the owning module's error elsewhere. Never convert backend failures to `Option.none()`.

Identifiers are branded runtime strings. `UnixMillis` values are finite epoch-millisecond numbers; use exact boundaries and caller-supplied mutation times, not database time. Preserve optional fields. Metadata, claims, scopes, canonical AMR, transports, request context, and versioned authentication events must round-trip losslessly; SQLite encodes structures as JSON text and booleans as compatible values. Materialized AAL/AMR/timestamps must match central derivation on every read and write. Raw events are security provenance: keep them server-side and never include them in ordinary session HTTP responses. Store hashes and ciphertext unchanged.

## Maintained SQLite entry points [#maintained-sqlite-entry-points]

| Public module                                          | Entry point                                                                                                                                  |
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `EffectQbSqliteStorage`                                | `makeEffectQbSqliteStorage(executor, options?)`, `EffectQbSqliteAuthStorageLive(executor, options?)`, `D1EffectQbSqliteAuthStorageLive(...)` |
| `DrizzleEffectSqliteStorage`                           | `makeDrizzleEffectSqliteStorage(database, options?)`, `DrizzleEffectSqliteAuthStorageLive(database, options?)`                               |
| `DrizzleD1SqliteStorage`                               | `makeD1DrizzleEffectSqliteStorage(...)`, `DrizzleD1SqliteAuthStorageLive(...)`                                                               |
| `DrizzleNodeSqliteStorage` / `DrizzleBunSqliteStorage` | `makeNodeDrizzleEffectSqliteStorage(...)` / `makeBunDrizzleEffectSqliteStorage(...)` and matching `*AuthStorageLive` layers                  |
| `DrizzleDurableObjectSqliteStorage`                    | `makeDurableObjectDrizzleEffectSqliteStorage(...)`, `DrizzleDurableObjectSqliteAuthStorageLive(...)`                                         |
| `DrizzleWasmSqliteStorage`                             | persistent and memory `makeWasm*Storage(...)` functions and matching `DrizzleWasm*AuthStorageLive` layers                                    |

All return the same service set; options can replace `AuthSqliteTables` and tune login-risk history. Constructors acquiring a client are scoped. Provided executors and databases remain application-owned.

## Migrations [#migrations]

`authStorageMigrations` from `@effect-auth/core/StorageMigrations` is the authoritative ordered array of `AuthStorageMigration { id, sql }`. It contains `0001_auth_storage` through `0026_auth_user_identity`, covering the maintained bundle. Apply each entry once, in order, before traffic and record its `id`. Migration `0026_auth_user_identity` upgrades the published email-first user schema to identity-first storage and backfills each existing email as the user's primary global identity. Adapters never migrate automatically.

<CalloutContainer type="error">
  <CalloutTitle>
    Assurance v2 alpha reset
  </CalloutTitle>

  <CalloutDescription>
    Assurance v2 rewrote the baseline `0001_auth_storage` and `0006_auth_totp_factor` migrations to add session evidence and the TOTP replay floor. There is no compatibility read, backfill, or upgrade migration. Reset development databases created by an earlier alpha and apply the current baseline from empty storage. Do not apply the rewritten SQL over a database that already recorded those migration IDs.
  </CalloutDescription>
</CalloutContainer>

