S00-005: Select idb and verify transactional storage failure policies
Changes: Propose ADR 0004 comparing native IndexedDB, idb and Dexie; select idb 8.0.4 with account, transaction, quota, upgrade and key ownership policy. Repair rollback and add injected quota/blocked upgrade checks, exact wrapper lock, runtime guard, license texts and reproduction notes. Preserve historical recovery artifacts. Validation: Fresh-cache npm ci and npm run check passed on Node 24.21.0/npm 11.19.0; manifest, HTTPS/SHA512 and license checks passed. make check passed 72 tooling tests; tasks.py validate and git diff --check passed. Native browser quota, disk durability and process restart were not tested; ADR remains proposed for independent QA. Task: S00-005 Agent: auto-storage-0ce544ac4b40 Role: storage Action: claim Status: review Task title: Выбрать IndexedDB wrapper и migration policy
This commit is contained in:
1 parent
8f918bc040
commit
0e2b3098df
17 files changed
+713
-128
No files matched your search
@@ -0,0 +1,166 @@
|
||||
# ADR 0004: IndexedDB wrapper and migration policy
|
||||
|
||||
- Status: proposed; independent QA acceptance pending.
|
||||
- Task / requirements: S00-005 / STORE-001, STORE-004.
|
||||
- Date: 2026-10-15.
|
||||
- Decision owner: auto-storage-0ce544ac4b40 (storage).
|
||||
- Reviewer: assigned qa, not yet reviewed.
|
||||
|
||||
## Context and required capabilities
|
||||
|
||||
Choose browser persistence tooling and policy, using accepted ADR 0001 (TypeScript,
|
||||
ESM, Node 24.21.0/npm 11.19.0). Account history, crypto state and settings require
|
||||
isolation, transactional writes and honest failure reporting. This decision does
|
||||
not define application record schemas or port signatures. The registry reserves
|
||||
`docs/contracts/persistence.md` for S01; that file does not exist yet. No current
|
||||
experiment needs a new shared interface. Downstream implementation must wait for
|
||||
its accepted models/lifecycle/persistence contracts.
|
||||
|
||||
## Candidates and primary sources
|
||||
|
||||
Documentation and registry metadata inspected on 2026-10-15:
|
||||
|
||||
| Candidate | Benefits | Cost / outcome |
|
||||
| --- | --- | --- |
|
||||
| Native IndexedDB | Standard API, no package | Event/request handling requires more adapter code; reference behavior, not selected ergonomics |
|
||||
| idb 8.0.4, ISC | Promise API, typed schemas, explicit transaction completion and upgrade callbacks | Native transaction lifetime still applies; selected and executed |
|
||||
| Dexie 4.4.6, Apache-2.0 | Query helpers, declarative versions and transaction API | Broader abstraction than this bounded adapter needs; researched, not installed or benchmarked |
|
||||
|
||||
Select **idb 8.0.4**. Its small API surface keeps native transaction boundaries
|
||||
visible while providing promises and TypeScript declarations. Dexie remains a
|
||||
viable option if measured query requirements justify it. No speed or security
|
||||
superiority is claimed. `npm view` verified exact candidate versions/licenses;
|
||||
installed idb metadata has no runtime dependencies. Only the isolated experiment
|
||||
manifest/lock changes here; production adoption belongs to later scoped tasks.
|
||||
|
||||
Sources:
|
||||
|
||||
- [idb documentation](https://github.com/jakearchibald/idb): promises, `tx.done`,
|
||||
typed schemas, transaction lifetime and upgrade/blocked/blocking/terminated APIs.
|
||||
- [Dexie design](https://dexie.org/docs/Tutorial/Design): queries, transactions,
|
||||
versioning and asynchronous transaction limits.
|
||||
- [IndexedDB standard](https://w3c.github.io/IndexedDB/): request errors,
|
||||
transaction abort/commit, version changes and durability hints.
|
||||
- [Storage standard](https://storage.spec.whatwg.org/): estimates and persistence
|
||||
requests; [WebKit storage policy](https://webkit.org/blog/14403/updates-to-storage-policy/)
|
||||
illustrates browser-controlled quotas/eviction. No fixed quota is promised.
|
||||
- Registry records: [idb 8.0.4](https://registry.npmjs.org/idb/8.0.4),
|
||||
[Dexie 4.4.6](https://registry.npmjs.org/dexie/4.4.6),
|
||||
[fake-indexeddb 6.2.5](https://registry.npmjs.org/fake-indexeddb/6.2.5).
|
||||
- [fake-indexeddb documentation](https://github.com/dumbmatter/fakeIndexedDB):
|
||||
in-memory testing implementation, without disk persistence.
|
||||
|
||||
## Transaction and account policy
|
||||
|
||||
Use one versioned application database per origin with distinct history, crypto,
|
||||
settings and metadata stores. Every account-owned key and index starts with the
|
||||
canonical accountId; unscoped scans are forbidden in account APIs. AccountId is
|
||||
an opaque application identity, not a password or a raw JID database name. Capture
|
||||
account/generation before asynchronous work and validate it at application entry;
|
||||
do not resolve an account from whichever context is active after awaiting.
|
||||
|
||||
History mutations and their archive checkpoint share one readwrite transaction.
|
||||
Any coupled crypto mutation must use that same database transaction when required
|
||||
by the future crypto contract. Separate stores allow this; separate databases
|
||||
would not. Cache is disposable and must never be the only copy of crypto state.
|
||||
Network/worker/crypto preparation finishes before starting a transaction. Await
|
||||
only IDB requests within it; any other asynchronous work risks auto-commit.
|
||||
|
||||
Success is published only after `tx.done` resolves. Synchronous exceptions,
|
||||
request failures and completion failures all produce a failed save; abort an
|
||||
active transaction and observe completion before returning. Never swallow a
|
||||
request error and then report success. External XMPP delivery is not part of the
|
||||
local transaction: reconcile delivery separately rather than blindly resend.
|
||||
Use strict durability hints for critical state where supported/verified, with
|
||||
default durability explicitly recorded otherwise; neither is a hardware vault
|
||||
or proof against device failure, eviction or user clearing.
|
||||
|
||||
## Schema upgrade and connection ownership
|
||||
|
||||
Use monotonically increasing integer versions, sequential oldVersion steps and
|
||||
one versionchange transaction. A fresh database runs every needed step. Modify
|
||||
existing stores via the upgrade transaction; do not recreate them accidentally.
|
||||
Upgrade code is deterministic and local: no network, worker waits, secrets or
|
||||
unbounded whole-history transformation. Large resumable transformations require
|
||||
an independently designed recovery/checkpoint contract before implementation.
|
||||
|
||||
Each tab/worker owns its connection. On blocking/versionchange, stop accepting
|
||||
new writes, let current work finish/abort, close the connection and request reload
|
||||
to the supported schema. On blocked, show an upgrade-wait state with instructions
|
||||
to close/reload other tabs; do not delete the database or claim readiness. A wait
|
||||
deadline changes UI state, not the upgrade result. IndexedDB open cannot simply
|
||||
be cancelled: a late success for a retired attempt must be closed and ignored.
|
||||
On terminated, invalidate the handle and reopen under lifecycle control.
|
||||
|
||||
An aborted migration leaves the prior version/data available for investigation
|
||||
and a corrected forward retry. No automatic wipe, schema downgrade or silent
|
||||
rebuild of non-recoverable state. An older build receiving VersionError asks for
|
||||
an update; release rollback must retain a compatible reader or ship a corrective
|
||||
forward migration. Tests must cover fresh creation and each supported old version.
|
||||
|
||||
## Quota, eviction and key ownership
|
||||
|
||||
Estimates are advisory and race with other origin storage. Request persistent
|
||||
storage through supported StorageManager APIs and report the actual granted
|
||||
state; denial does not mean storage is unavailable. Quota/unknown disk failures
|
||||
must stop the batch without advancing checkpoint. UI reports history not saved
|
||||
locally and retains bounded pending work in memory under the later queue policy.
|
||||
Retry is explicit/bounded after freeing disposable media/cache; never remove
|
||||
crypto, trust state or unsynced history to make space automatically. Reload or
|
||||
origin clearing may lose pending memory and local data; startup must detect missing
|
||||
schema/account markers and request a real recovery path rather than imply success.
|
||||
Browser testing still must verify native exhaustion, eviction and restart behavior.
|
||||
|
||||
Storage owns byte persistence and isolation, **not cryptographic key generation,
|
||||
trust or unlock policy**. Crypto owns serialization/versioning of opaque state
|
||||
and any wrapping/unlock material under its accepted ADR/contract. This task does
|
||||
not authorize saving raw private keys. Passwords remain memory-only; no password,
|
||||
unlock secret or reusable wrapping secret in localStorage/IndexedDB. Persistent
|
||||
unlock needs its own accepted decision. Same-origin script access is a security
|
||||
boundary: IndexedDB alone provides neither XSS resistance nor hardware backing.
|
||||
Account removal must delete its records across all owned stores atomically,
|
||||
invalidate in-flight context and release in-memory crypto; exact deletion and
|
||||
suppression semantics remain S01/application work, not an implemented API here.
|
||||
|
||||
## Focused experiment and actual result
|
||||
|
||||
`experiments/0004-storage-design` uses exact idb 8.0.4 and fake-indexeddb 6.2.5,
|
||||
locked HTTPS/SHA512 dependencies and the accepted Node/npm pins. Fresh-cache
|
||||
`npm ci --ignore-scripts --no-audit --no-fund` and `npm run check` passed.
|
||||
|
||||
- Real fixture ConstraintError during v1→v2 unique-index migration aborts open;
|
||||
v1 store/data survive, new store/index disappear. Corrected retry reaches v2,
|
||||
is queryable and survives connection reopen in the same process.
|
||||
- Explicit QuotaExceededError injection after successful history/checkpoint
|
||||
requests returns failed save, aborts both mutations and preserves account B.
|
||||
A clean retry commits both. This tests policy, not native quota generation.
|
||||
- Holding v1 produces versionchange then blocked; upgrade has not executed.
|
||||
Closing v1 allows v2 and preserves checkpoint. Opening v1 again fails VersionError.
|
||||
|
||||
These are synthetic API experiments, not production adapters. Browser inventory
|
||||
was empty. Native disk quota, crash/process restart, browser multi-tab, eviction,
|
||||
persistence grants and crypto unlock were not tested. No browser readiness or
|
||||
application durability acceptance is implied. See README and task evidence for
|
||||
commands and limits; preserved RECOVERY.md describes the historical checkpoint.
|
||||
|
||||
## License / browser constraints and consequences
|
||||
|
||||
idb's ISC notice is retained in the experiment; fake-indexeddb's Apache-2.0 text
|
||||
is retained as a test-only dependency notice. Neither package declares runtime
|
||||
dependencies in this lock graph. Installs disable lifecycle scripts and enforce
|
||||
normal TLS; audit/provenance assessment was not performed. Future distributions
|
||||
must retain notices for the actual bundled graph.
|
||||
|
||||
idb targets modern Chrome/Firefox/Safari/Edge and excludes IE. TypeScript types
|
||||
do not polyfill IndexedDB, Proxy or storage capabilities. ADR 0001 syntax floors
|
||||
are not a browser test matrix; S00-006 owns product support. If IndexedDB opening
|
||||
fails (private mode, policy, permissions or security errors), expose unavailable
|
||||
storage and fail saves honestly. No fake-indexeddb production fallback.
|
||||
|
||||
S01 must define exact keys/indexes, normalized account identifiers, save errors,
|
||||
cancellation, crypto ownership and valid/invalid fixtures before adapter work.
|
||||
Future tests must add account deletion/suppression, cancelled generations,
|
||||
every supported migration and native browser quota/restart coverage. New query
|
||||
requirements, browser incompatibility, unacceptable migration cost or changed
|
||||
crypto atomicity requirements trigger a bounded ADR revision, never an implicit
|
||||
wrapper/stack replacement.
|
||||
Reference in new issue
Block a user