Strata Storage
com.aoneahsan.stratastorage
Strata Storage is a zero-dependency TypeScript storage engine that unifies `localStorage`, IndexedDB, cookies, the URL, native Keychain/Keystore, SQLite, and the filesystem behind one adapter-based API across web, iOS, and Android โ with encryption, TTL, querying, cross-tab sync, and disaster recovery built in.
At a glance
- Category
- other
- Status
- Live
- Started
- Jun 2024
- Last updated
- 2 months ago
Identifier
com.aoneahsan.stratastoragePreview
Take a look before you go
The still below is painted first, then the running site loads behind it โ so the dialog is never an empty rectangle waiting on a network round trip. Where a site allows framing the live app takes over; where it refuses, the still stays and says so. Nothing is requested until you press.
About
What this actually is
Nothing in my own projects calls localStorage directly.
That rule is not fussiness. A value starts in localStorage because it is the shortest thing to type, then it has to survive a reinstall, then something native has to read it, and by the time all three are true the code touching it is spread across a dozen files nobody wants to open.
Storage looks trivial. It is the layer you get to change least often.
Each backend has its own grammar and none of them agree with the others. localStorage is synchronous and holds strings. IndexedDB is asynchronous and holds structures. Cookies carry expiry and scope. The URL is storage that people forget is storage, right up until a filter panel loses its state on a refresh.
Strata Storage puts one grammar over all of it. Get, set, remove, query, subscribe.
Five verbs, and the answer they give does not change depending on where the value happens to sit.
Zero runtime dependencies was the second decision, and it is the one that cost the most to hold.
React, Vue, Angular and the Capacitor core are optional peers. Your project installs them if it already has them, and the package brings none of them along. A library that arrives with its own opinion about your framework is a library you argue with eighteen months later, usually during an upgrade you did not plan.
Everything expensive stays off. Encryption, compression, expiry, queries, integrity checksums, durable writes, mirroring and snapshots are enabled per call or per instance, never by a global switch inherited from somebody else's configuration.
A default that encrypts everything makes every read slower for the sake of the one value that needed it.
Two of the optional pieces earn their weight more often than I expected. A query engine over stored values means a lookup stops being a full read followed by a filter in JavaScript, and cross-tab observers mean two open tabs stop disagreeing about who is signed in.
The native side is where a parity claim has to be earned rather than announced. A key-value store on a phone can be a preferences file, a SQLite table or a file on disk, and those three diverge exactly where a wrapper is tempted to paper over them: what happens to a half-finished write, and what a stored expiry means after a reboot.
I wrote that part twice.
A multi-store SQLite backend and a file-per-key filesystem adapter now answer the same call the same way, which is a smaller sentence than the work behind it.
None of that was asked for. What it cost is a strict TypeScript build, a suite of 140 tests and a documentation site nobody asked for. A storage library whose semantics you cannot read is a storage library nobody should hand a session token to, so the documentation is part of the product rather than an afterthought attached to it.
There is a second cost I did not choose. Every backend belongs to somebody else. A browser tightens its eviction policy, a platform revises what counts as durable, and the abstraction has to absorb that without moving the five verbs, because the whole promise is that the verbs do not move.
Apache-2.0 on npm, source on GitHub, documentation at stratastorage-docs.aoneahsan.com.
It is the storage layer inside my own products, which means it gets used badly and early, by me, in the places where a shortcut would have been easier.
Worth knowing
- Genuinely zero runtime dependencies โ an IRON-SOLID project rule, not marketing. There is no `dependencies` key in `package.json`; every framework/Capacitor binding is an *optional* peer, so consumers only pull what they use.
- Layered architecture with hard boundaries: core engine โ adapters โ feature modules โ integrations โ native plugin, each with its own nested CLAUDE.md/AGENTS.md instruction files.
- Provider-free, framework-agnostic API (v2.5.0) borrows the Zustand "define once, import anywhere" ergonomics โ no React context / Vue plugin / Angular module required, but those styles still ship.
- Native parity work (v2.6.0) closed real correctness gaps: SQLite multi-store separation, full value round-trip on native `get`, and a working filesystem backend that previously didn't exist on any platform.
- Security hardening baked in: AES-CBC IV correctness, prototype-pollution + ReDoS + cookie hardening, secure adapters mandatory for sensitive data, `androidx.security` on stable (off alpha).
- Honest shipping discipline: native 2.6.0 changes are code-complete and reviewed but documented as *device-unverified* (`docs/guides/platforms/device-verification.md`) โ no overclaiming. The quality gate is honestly framed too: as of 2026-06-03 there is no test runner, so the package leans on strict typecheck + a real ESM build + lint rather than claiming test coverage it no longer has.
- Ships an AI-INTEGRATION-GUIDE so Claude Code / Cursor / Copilot can integrate the package quickly โ AEO/agent-readiness applied to a library.
Built with
- TypeScript
- CapacitorJS
- Swift
- Java
- Vitest
Key features
What it does, and what that costs to build
Each of these is a capability the stack genuinely provides. None of them is an outcome nobody measured โ there is no "cut onboarding by 40%" on this page, because no one measured onboarding.
- Unified API across 8+ storage backends
- Zero runtime dependencies
- AES encryption, compression & TTL (opt-in)
- Query engine + cross-tab sync + observers
- Disaster recovery (checksums, durable writes, snapshots)
- Native SQLite multi-store + filesystem adapters
- Provider-free React/Vue/Angular bindings
Screenshots
5 slots, 2 of them filled in
These are designed frames, not pictures. Each one records what belongs in the slot and at what width; the capture itself is taken from the running app. Drawing a fake screenshot here would put an image on the site that no build has ever produced.
Timeline
Still being worked on
Jun 2024
Work started
The earliest month on record for Strata Storage.
Sep 2026
Last change
Status is completed โ 2 months ago. Ongoing, so this row moves whenever something lands.
Questions
Common questions
The questions that actually arrive, answered in the first sentence.
- Who built Strata Storage?
- Ahsan Mahmood wrote all of it, from the browser tier down to the native adapters. That matters more in a storage library than it does in an application, because the person who decided what an expiry means in localStorage is the same person who implemented it in SQLite, so the two agree rather than drift. It is published on npm as strata-storage under Apache-2.0, the source is at github.com/aoneahsan/strata-storage, and the documentation site is at stratastorage-docs.aoneahsan.com. Every claim here is checkable from one of those.
- Why use this instead of calling localStorage directly?
- Because localStorage is the decision you are least able to reverse cheaply. It is synchronous, it holds strings, it is per-origin, and every one of those becomes a constraint the day the value has to survive a reinstall or be read from a native shell. Strata Storage gives you one set of verbs over eight or more backends, so changing where a value lives is a configuration change rather than a rewrite of every call site. If a value genuinely never leaves one browser tab, the plain API is fine and this is overhead.
- Does Strata Storage work on iPhone?
- The browser backends run wherever the browser runs, an iPhone included, because they are web APIs rather than anything I wrote for a platform. What does not exist is an iOS release: there is no Apple Developer account here, so nothing I build has shipped to the App Store, and Android is the native tier I can actually vouch for. Treat the mobile parity claim as proven on Android and unverified anywhere Apple has to approve it.
- What does adding it cost a bundle?
- The runtime ships zero dependencies, so the baseline cost is the package itself and nothing it drags in. React, Vue, Angular and the Capacitor core are optional peers, installed only if your project already uses them, which means a plain TypeScript project pays for none of that surface. The power features are the same shape: encryption, compression, expiry, queries, checksums, durable writes, mirroring and snapshots are all off until a call or an instance turns them on.
- Is stored data encrypted by default?
- No. Encryption is opt-in, per call or per instance, and that is a deliberate choice rather than an oversight. A library that encrypts everything by default makes every read slower for the sake of the one value that needed it, and it hides the question of which values those are. Deciding what deserves AES is a decision about your data, so the library asks you to make it. Compression, integrity checksums and durable writes follow the same rule and are switched on the same way.
Keep looking
More from the catalogue
- NA
Native Update
com.aoneahsan.nativeupdate
Over-the-air updates for Capacitor apps โ bundle signing, staged rollout, and automatic rollback when a bundle fails to boot twice.
- CA
Capacitor Auth Manager
com.aoneahsan.capacitorauthmanager
Framework-agnostic TypeScript authentication library unifying 15 OAuth, passwordless, credential, and biometric sign-in flows behind one provider-less API.
- ZT
ZTools
com.zaions.ztools
A privacy-first developer and power-user toolbox covering 20 categories, where the work happens in your browser rather than on someone's server.