Skip to content

feat: cache the master key and skip Argon2id derivation on boot - #228

Open
gmaclennan wants to merge 1 commit into
mainfrom
claude/key-manager-master-key-cache-9k40es
Open

feat: cache the master key and skip Argon2id derivation on boot#228
gmaclennan wants to merge 1 commit into
mainfrom
claude/key-manager-master-key-cache-9k40es

Conversation

@gmaclennan

@gmaclennan gmaclennan commented Aug 17, 2026

Copy link
Copy Markdown
Member

Deriving the master key from the rootkey (Argon2id, 64 MiB memlimit) is paid on every boot — twice, in fact: once inside MapeoManager and once in createMapServer. On low-end devices that is a visible boot-time and memory cost for a deterministic mapping that never changes. This caches the derived key natively next to the rootkey and passes both on the init frame, so steady-state boots run no Argon2id at all. Full design, failure model, and rollout: docs/master-key-cache-plan.md (included in this PR).

On a cache miss, Kotlin/Swift derive the key by dlopen/dlsym of the sodium-native prebuild already shipped for the Node runtime (digidem/sodium-native-nodejs-mobile#3 adds the exported snm_* wrappers), so native and Node derivation agree by construction. The cache is written and read-back-verified before Node boots — no new IPC frames; the only protocol change is the optional masterKey field on the existing init frame. The stores treat it as a pure cache: any failure (corrupt envelope, fingerprint mismatch against the rootkey, missing symbols, even the derivation shim failing to load) deletes the entry or degrades to derivation, never blocking boot and never touching the rootkey. The backend keeps a derive-if-absent fallback whose boot.master-key-derive span is the telemetry signal for a degraded device. The master key is scrubbed everywhere the rootkey is (Sentry scrub layers + tripwire, including quoted/hyphenated/prefixed-identifier shapes).

Reviewed by a three-way agent pass; findings addressed include an FGS crash path when the derivation shim can't load (now degrades to a rootkey-only init), tests that pin the pass-through and derivation count (mutation-checked), keypair validation in createMapServer, a canonical-base64 round-trip check on init keys, and alignment with the merged upstream API (@comapeo/crypto, getMasterKey()).

Verified: backend 127 tests, root jest 89, Android JVM 158, Android on-device storage matrix 19/19 (Pixel 7a API 34), iOS Swift package 163 — all green; lint/tsc clean. The on-device derive contract tests fail by design against today's prebuilds (verified failing at exactly the missing-snm_* step, which also exercises the fallback) and go green once the patched prebuild release is dispatched.

Merge sequencing: needs @comapeo/[email protected] (renamed upstream; masterKey support merged in digidem/comapeo-crypto#37) and @comapeo/[email protected] (digidem/comapeo-core#1310) published, then one npm install --prefix backend to regenerate the lockfile (deliberately not regenerated here — the pinned versions are unpublished, so backend CI fails until then), plus the refreshed sodium-native 5.1.0 prebuild release.

@github-actions github-actions Bot added the feature New feature (changelog) label Aug 17, 2026
@socket-security

socket-security Bot commented Aug 17, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​comapeo/​crypto@​2.0.07410010089100
Updated@​comapeo/​core@​7.2.0 ⏵ 7.5.07810084 +197 +3100

View full report

@RangerMauve RangerMauve left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks decent overal, just a couple of questions.

Comment thread android/src/main/cpp/master-key-derive.c Outdated
Comment thread android/src/main/java/com/comapeo/core/RootKeyStore.kt Outdated
@gmaclennan
gmaclennan requested a review from RangerMauve August 18, 2026 18:13
@gmaclennan
gmaclennan force-pushed the claude/key-manager-master-key-cache-9k40es branch from abc87e2 to bc253e6 Compare August 18, 2026 21:22
@gmaclennan

Copy link
Copy Markdown
Member Author

Some performance and memory measurements on the impact of caching the derived master key. Warm boot (boot #2+ of the same install), Pixel_7a_API_34 emulator, mean of 6 boots each, running on a MacBook M2. nodejs-mobile 24 and its compile cache are constant; only the key-derivation code differs.

Scenario Boot time (initready) Peak RSS (VmHWM)
Pre-PR — 2 derivations every boot 342 ms 271.1 MiB
PR — 0 derivations (cache hit) 41 ms 210.9 MiB
Saving −301 ms (−88%) −60.3 MiB (−22%)

Ranges across the 6 samples: pre-PR 295–405 ms / 268.5–273.9 MiB; PR 30–52 ms / 209.4–214.0 MiB.

I imagine the boot time saving will be a lot more significant on lower-end devices, and the memory impact is a big win.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature (changelog)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants