Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
241 commits
Select commit Hold shift + click to select a range
0f9437c
feat(wallet-sqlite): add platform-wallet-sqlite crate
lklimek May 11, 2026
adf4212
ci(wallet-sqlite): wire crate into workspace CI, Dockerfile, and Carg…
lklimek May 11, 2026
cea9dda
fix(wallet-sqlite): library/CLI/tests/docs fix wave from Phase 3 QA
lklimek May 11, 2026
e26945c
feat(platform-wallet): add optional serde derives behind serde feature
lklimek May 11, 2026
8e08306
refactor(wallet-storage): rename platform-wallet-sqlite to platform-w…
lklimek May 11, 2026
74acc81
refactor(wallet-storage): use bincode-serde for BLOB columns, remove …
lklimek May 11, 2026
5bac6e3
refactor(wallet-storage): drop per-blob schema-rev tag, rely on migra…
lklimek May 11, 2026
540decf
refactor(wallet-storage): drop --dry-run from prune CLI
lklimek May 11, 2026
4cfec30
fix(platform-wallet): correct stale crate name in doc comment after w…
lklimek May 11, 2026
bd4216d
refactor(wallet-storage): rename SqlitePersisterError → WalletStorage…
lklimek May 11, 2026
f58e784
fix(wallet-storage): SEC-003 defensive update triggers + build-script…
lklimek May 11, 2026
87f38c0
chore(wallet-storage): post-review cleanup (delete CHANGELOG, JSON es…
lklimek May 11, 2026
d7a88a9
Merge branch 'v3.1-dev' into feat/platform-wallet-sqlite-persistor
lklimek May 11, 2026
dea32cc
merge: v3.1-dev into PR #3625
lklimek May 13, 2026
2caf602
docs(platform-wallet-storage): tighten comments + post-merge fmt
lklimek May 13, 2026
f6e90d1
fix(rs-platform-wallet-storage): chmod 0o600 on initial DB + backup c…
lklimek May 13, 2026
738091f
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek May 14, 2026
ae6b4af
feat(platform-wallet): persistor backports — retry-safe flush, prepar…
Claudius-Maginificent May 18, 2026
90441b9
feat(platform): getDocuments v1 — SQL-shaped select + count surface (…
QuantumExplorer May 15, 2026
c976d53
fix: paid/unpaid classification for invalid batch transitions (#3616)
shumkov May 15, 2026
16c8156
refactor: structure v1 getDocuments where/order_by/having as typed pr…
QuantumExplorer May 17, 2026
cec1766
ci: preserve Swift SDK Rust build cache (#3632)
vivekgsharma May 17, 2026
e38c411
chore(swift-sdk): remove dash-spv-ffi crate usage, spv is wrapped by …
ZocoLini May 17, 2026
3c4ced7
feat(drive): expand count-index group-by carrier shapes (G1a/G1b/G8a-…
QuantumExplorer May 17, 2026
4cde1aa
chore: bump grovedb to develop (352c2f55) (#3656)
lklimek May 18, 2026
368d119
Merge remote-tracking branch 'origin/v3.1-dev' into work-3625-mergebase
lklimek May 19, 2026
b93bbcf
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek May 19, 2026
f45ab09
fix(platform-wallet-storage): drain buffer on wallet delete (CMT-001)
lklimek May 19, 2026
f18a1e4
fix(platform-wallet-storage): recheck schema/version on staged copy (…
lklimek May 19, 2026
bed413e
fix(platform-wallet-storage): reject trailing bytes in blob decode (C…
lklimek May 19, 2026
e2746c3
test(platform-wallet-storage): make tc054 root-safe (CMT-004)
lklimek May 19, 2026
0eba0a8
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek May 19, 2026
3b694f6
fix(platform-wallet-storage): handle AssetLockStatus::Consumed from v…
lklimek May 19, 2026
72a4eca
feat(platform-wallet-storage): SecretStore foundation — zeroizing wra…
lklimek May 19, 2026
183c9f3
feat(platform-wallet-storage): EncryptedFileStore — Argon2id + XChaCh…
lklimek May 19, 2026
bfbb551
feat(platform-wallet-storage): KeyringStore — OS keyring backend (key…
lklimek May 19, 2026
e3ac1a6
test(platform-wallet-storage): positive secrets guard + API-shape int…
lklimek May 19, 2026
029753f
ci(platform-wallet-storage): cargo-deny advisories gate covering the …
lklimek May 19, 2026
1c55f89
fix(platform-wallet-storage): passphrase-verification token + hardene…
lklimek May 19, 2026
0a7c3f0
fix(platform-wallet-storage): map keyring-core NoStorageAccess to Key…
lklimek May 19, 2026
884d470
fix(platform-wallet-storage): MemoryStore stores SecretBytes so it ze…
lklimek May 19, 2026
1256cb8
docs(platform-wallet-storage): correct keyring-core attribution in Ca…
lklimek May 19, 2026
1c29698
docs(platform-wallet-storage): SECRETS.md reflects the delivered Secr…
lklimek May 19, 2026
2c7927b
chore(platform-wallet-storage,ci): drop cargo-deny, flip secrets defa…
lklimek May 20, 2026
123f908
refactor(platform-wallet-storage): adopt keyring_core SPI for secret …
lklimek May 20, 2026
f733d38
docs(platform-wallet-storage): SECRETS.md + lib root reflect keyring_…
lklimek May 20, 2026
18a0655
test(platform-wallet-storage): default-build proof for the secrets su…
lklimek May 20, 2026
3eefec2
fix(platform-wallet-storage): forbid == on SecretBytes/SecretString (…
lklimek May 20, 2026
c7a4ded
feat(platform-wallet-storage): add keyless account-manifest reader (A1)
lklimek May 19, 2026
b9af993
feat(platform-wallet): SeedProvider port + CredentialStore adapter + …
lklimek May 20, 2026
79f53ed
feat(platform-wallet-storage): bulk core-state reconstruction reader (B)
lklimek May 19, 2026
e49fcc4
feat(platform-wallet-storage): Consumed-filtered asset-lock rehydrati…
lklimek May 19, 2026
afd14cb
feat(platform-wallet-storage): wire keyless load() rehydration payloa…
lklimek May 19, 2026
8232896
test(platform-wallet): end-to-end rehydration RT suite (E)
lklimek May 19, 2026
e2e04b7
test(platform-wallet-storage): flip deferral test to positive rehydra…
lklimek May 19, 2026
b9d46a5
test(platform-wallet-storage): allow-list one-shot rehydration reader…
lklimek May 19, 2026
96a9aa9
fix(platform-wallet): no-BIP44 wallet silent-zero balance + chainlock…
lklimek May 19, 2026
62bd475
refactor(platform-wallet): drop dead post-insert wallet_id re-check (F4)
lklimek May 19, 2026
b7508a0
fix(platform-wallet-ffi): adapt FFI consumer to keyless rehydration A…
lklimek May 19, 2026
06dd453
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek May 22, 2026
689a61e
chore(platform-wallet-storage): refresh Cargo.lock version after v3.1…
lklimek May 22, 2026
e93dfc7
Merge branch 'feat/platform-wallet-sqlite-persistor' into feat/platfo…
lklimek May 22, 2026
34532e5
Merge branch 'feat/platform-wallet-storage-secrets' into feat/platfor…
lklimek May 22, 2026
8a5ef7a
fix(platform-wallet-storage): rekey returns FileStoreError::Busy inst…
lklimek May 22, 2026
b6a84fd
refactor(platform-wallet-storage)!: unify FileStoreError, drop error_…
lklimek May 22, 2026
647567e
fix(platform-wallet-storage): remove redundant SecretString Drop (UB)…
lklimek May 22, 2026
8ab4208
feat(platform-wallet-storage)!: serde_json vault format with versione…
lklimek May 22, 2026
68ed3d1
fix(platform-wallet-storage): cross-platform atomic vault write via N…
lklimek May 22, 2026
0066a5a
feat(platform-wallet-storage)!: public SecretStore API exposing Secre…
lklimek May 22, 2026
c636ac0
refactor(platform-wallet-storage): string-only keyring_core From; typ…
lklimek May 22, 2026
a5c5bf0
fix(platform-wallet-storage): box typed FileStoreError into keyring_c…
lklimek May 22, 2026
e1c7fa9
refactor(platform-wallet-storage): remove MemoryCredentialStore; reti…
lklimek May 22, 2026
671ce69
fix(platform-wallet-storage): enforce lowercase-hex service, widen ex…
lklimek May 22, 2026
dc492cc
docs(platform-wallet-storage): strip historical comments + license he…
lklimek May 22, 2026
c58a2b5
feat(platform-wallet-storage): log swallowed mlock + corruption/write…
lklimek May 22, 2026
316c4ee
fix(platform-wallet): serde derives on shielded changeset types
lklimek May 22, 2026
6aa2942
docs(platform-wallet-storage): drop deleted MemoryCredentialStore / _…
lklimek May 22, 2026
f78f2e6
fix(platform-wallet-storage): gate test-only contacts reader off-state
lklimek May 22, 2026
b5a8439
refactor(platform-wallet-storage)!: native FK schema, drop barrel, co…
lklimek May 22, 2026
eb2b6b0
fix(platform-wallet-storage): resolve real account_index for multi-ac…
lklimek May 22, 2026
ce8ca25
fix(platform-wallet-storage): fast-fail mixed-wallet and divergent id…
lklimek May 22, 2026
36577d2
refactor(platform-wallet-storage): drop delete-wallet CLI, propagate …
lklimek May 22, 2026
a996b93
test(platform-wallet-storage): cover #3625 hardening (native FK, mult…
lklimek May 22, 2026
df202ea
test(platform-wallet-storage): assert UTXO row survival in TC-048; sc…
lklimek May 22, 2026
4c9664d
chore(platform-wallet-storage): drop unused key-wallet-manager depend…
lklimek May 22, 2026
b4ffcbc
fix(platform-wallet-storage): canonicalize TC-031 expected dir for ma…
lklimek May 22, 2026
df1e59c
Merge branch 'v3.1-dev' into feat/platform-wallet-sqlite-persistor
lklimek May 25, 2026
9c1bec9
fix(platform-wallet-storage): atomic restore + 0o600 backup file + WA…
lklimek May 25, 2026
36728e5
feat(platform-wallet-storage): cheap pre-staging schema sniff in rest…
lklimek May 25, 2026
8f1d0c4
fix(platform-wallet-storage): restore drained buffer on delete_wallet…
lklimek May 25, 2026
ad3810a
feat(platform-wallet-storage): forward-version gate on open() symmetr…
lklimek May 25, 2026
9b2664e
fix(platform-wallet-storage): serialize delete-wallet against concurr…
lklimek May 25, 2026
ad6c25e
fix(platform-wallet-storage): bounded blob decode + typed BlobTooLarg…
lklimek May 25, 2026
7dd8ef0
fix(platform-wallet-storage): strict consumed-count on identity_keys …
lklimek May 25, 2026
dcf1655
refactor(platform-wallet-storage): collapse list_active into load_sta…
lklimek May 25, 2026
49d429e
test(platform-wallet-storage): assert clap usage exit code Some(2) (C…
lklimek May 25, 2026
614c43d
chore(platform-wallet-storage): clippy cleanups for new test files
lklimek May 25, 2026
cfb93a2
refactor(platform-wallet): seedless watch-only load via Wallet::new_w…
lklimek May 25, 2026
21215d3
refactor(platform-wallet-ffi): drop resolver arg from load_from_persi…
lklimek May 25, 2026
92f849b
fix(swift-sdk): align PlatformWalletManager.loadFromPersistor with se…
lklimek May 25, 2026
3cd4264
style: cargo fmt across seedless-load touch points
lklimek May 25, 2026
f57b117
docs(platform-wallet): adjust rehydration_load test header to reflect…
lklimek May 25, 2026
81ed297
Merge branch 'feat/platform-wallet-storage-secrets' into feat/platfor…
lklimek May 25, 2026
b117ff2
fix(platform-wallet-storage): backup::run_to atomic via NamedTempFile…
lklimek May 25, 2026
9b39783
fix(platform-wallet-storage): restore_from holds exclusive lock + chm…
lklimek May 25, 2026
ae9e629
feat(platform-wallet-storage): commit_writes returns CommitReport on …
lklimek May 25, 2026
fb3362e
feat(platform-wallet-storage): Drop logs uncommitted dirty buffer in …
lklimek May 25, 2026
a23afd9
fix(platform-wallet-storage): expand is_transient to cover I/O-class …
lklimek May 25, 2026
df54420
feat(platform-wallet-storage): backup::prune accumulates per-file err…
lklimek May 25, 2026
df76e1a
feat(platform-wallet-storage): integrity_check before migrations on o…
lklimek May 25, 2026
c0b45c0
refactor(platform-wallet-storage): remove unreachable MigrationDirty …
lklimek May 25, 2026
1840b35
chore(platform-wallet-storage): document ensure_dir TOCTOU probe as b…
lklimek May 25, 2026
722de93
docs(platform-wallet-storage): atomicity contract rustdoc (N-3/N-4/N-…
lklimek May 25, 2026
fe01634
docs(platform-wallet-storage): Drop side-effect comment + restore loc…
lklimek May 25, 2026
34c8ecb
Merge remote-tracking branch 'origin/feat/platform-wallet-sqlite-pers…
lklimek May 25, 2026
54621bc
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek May 26, 2026
db7b6b5
Merge branch 'feat/platform-wallet-sqlite-persistor' into feat/platfo…
lklimek May 26, 2026
543d0da
Merge branch 'feat/platform-wallet-storage-secrets' into feat/platfor…
lklimek May 26, 2026
cf5f87a
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek May 27, 2026
e7e1de8
Merge remote-tracking branch 'origin/feat/platform-wallet-sqlite-pers…
lklimek May 27, 2026
4d21651
Merge remote-tracking branch 'origin/feat/platform-wallet-storage-sec…
lklimek May 27, 2026
e014555
feat(platform-wallet)!: rs-platform-wallet-storage crate (SQLite pers…
Claudius-Maginificent May 27, 2026
64a85a8
Merge branch 'v3.1-dev' into feat/platform-wallet-sqlite-persistor
lklimek May 27, 2026
016987c
Merge branch 'v3.1-dev' into feat/platform-wallet-sqlite-persistor
lklimek May 27, 2026
187a026
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek May 28, 2026
eadae31
chore(Cargo.lock): sync platform-wallet-storage to workspace version …
lklimek May 28, 2026
436c196
fix(platform-wallet-ffi): drop From<String> usage in shielded FFI loa…
lklimek May 28, 2026
03fd3dd
docs(platform-wallet-storage): add SQLite SCHEMA.md with Mermaid ER d…
lklimek May 28, 2026
97713ee
docs(platform-wallet-storage): split SCHEMA.md into domain-grouped di…
lklimek May 28, 2026
aa95add
feat(platform-wallet-storage): enforce enum-domain CHECK constraints …
lklimek May 28, 2026
8c4a88a
feat(platform-wallet-storage): add generic key/value store
lklimek May 28, 2026
9bc1fc2
feat(platform-wallet-storage): gate KV store behind `kv` feature
lklimek May 28, 2026
cd76fcd
ci: bump dorny/paths-filter from v3 (Node 20, deprecated) to v4
lklimek May 28, 2026
22e496d
feat(platform-wallet): keyring_core secret backends — encrypted-file …
Claudius-Maginificent May 29, 2026
aeb1f04
Merge branch 'v3.1-dev' into feat/platform-wallet-sqlite-persistor
lklimek May 29, 2026
d04685e
chore(Cargo.lock): bump platform-wallet-storage to workspace 3.1.0-dev.8
lklimek May 29, 2026
9ba47e1
refactor(platform-wallet-storage): drop footprint mechanism + tighten…
lklimek May 29, 2026
8a6fe8a
feat(platform-wallet-storage): cap KV get value size at 16 MiB (CMT-006)
lklimek May 29, 2026
f9b3438
fix(platform-wallet-storage): drop dest lock conn before WAL/SHM unli…
lklimek May 29, 2026
c35be7a
fix(platform-wallet-storage): tighten CLI gates (CMT-010, CMT-014)
lklimek May 29, 2026
5c37004
chore(platform-wallet-storage): apply CMT-007/012/013/016/018/020-022…
lklimek May 29, 2026
9698927
feat(platform-wallet-storage): swap kv_store for six meta_* tables in…
lklimek Jun 1, 2026
1b1c6a4
feat(platform-wallet-storage): reshape KvStore to per-ObjectId scope
lklimek Jun 1, 2026
27188a6
test(platform-wallet-storage): cover per-object metadata (TC-MD-001..…
lklimek Jun 1, 2026
3b4a038
docs(platform-wallet-storage): document meta_* tables in SCHEMA.md
lklimek Jun 1, 2026
08b0ed9
test(platform-wallet-storage): tighten metadata QA assertions + doc f…
lklimek Jun 1, 2026
0556928
fix(platform-wallet-storage): allow metadata writes before parent exi…
lklimek Jun 1, 2026
05d54d2
test(platform-wallet-storage): cover recursive_triggers enforcement +…
lklimek Jun 1, 2026
35e4a2f
refactor(platform-wallet-storage): drop inert recursive_triggers; met…
lklimek Jun 1, 2026
1053caa
fix(platform-wallet-storage): single-read KV get to close size-cap TO…
lklimek Jun 1, 2026
f1f0717
docs(platform-wallet-storage): surface cross-process rollback caveat …
lklimek Jun 1, 2026
d638c4f
refactor(platform-wallet-storage): encode outpoints via bincode (CMT-…
lklimek Jun 1, 2026
3f69c31
test(platform-wallet-storage): tighten malformed-outpoint assertion +…
lklimek Jun 1, 2026
269e578
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek Jun 2, 2026
ffdc28b
fix(platform-wallet): gate shielded-only event_manager field to fix d…
lklimek Jun 2, 2026
9e2d2b0
feat(platform-wallet): add contacts and identity-key rehydration (ite…
Claudius-Maginificent Jun 2, 2026
7c2b2f9
Merge remote-tracking branch 'origin/feat/platform-wallet-sqlite-pers…
lklimek Jun 2, 2026
d99d7a5
chore(platform-wallet): untrack .review-3625 review scratch + gitignore
lklimek Jun 2, 2026
14d868e
refactor(platform-wallet): remove dead wrong-seed-gate scaffolding (#…
lklimek Jun 2, 2026
0a9c972
Merge remote-tracking branch 'origin/feat/platform-wallet-rehydration…
lklimek Jun 2, 2026
c3cf8b0
fix(platform-wallet-storage): reconcile identity_keys schema with #36…
lklimek Jun 2, 2026
ddfa66e
test(platform-wallet-storage): pin identity_keys dual-FK cascade + no…
lklimek Jun 2, 2026
e78eb55
fix(platform-wallet-storage): enforce identity-wallet precondition at…
lklimek Jun 2, 2026
4e38975
fix(platform-wallet-storage): drop keyutils backend + dependency (CMT…
lklimek Jun 2, 2026
3fc28b9
refactor(platform-wallet-storage): rename SecretStoreError + IO path …
lklimek Jun 2, 2026
932b923
refactor(platform-wallet-storage): unify contact tables into one life…
lklimek Jun 2, 2026
052db80
Merge commit '932b923b2b277dc80c7c0dd59a332e0ab7dc76b1' into feat/pla…
lklimek Jun 2, 2026
3166090
Merge branch 'v3.1-dev' into feat/platform-wallet-sqlite-persistor
lklimek Jun 3, 2026
2f35190
Merge #3625 (feat/platform-wallet-sqlite-persistor) into rehydration
lklimek Jun 3, 2026
0466241
chore: update Cargo.lock
lklimek Jun 3, 2026
3f2e7d2
Merge #3625 (feat/platform-wallet-sqlite-persistor @ 04662411cf) into…
lklimek Jun 3, 2026
89a433a
build(wallet-storage): exact-pin bincode and getrandom; strip transie…
lklimek Jun 3, 2026
d241f61
fix(platform-wallet-storage): make meta_* soft-cascade state-agnostic…
lklimek Jun 3, 2026
57f0f72
fix(platform-wallet-storage): promote contact to established when bot…
lklimek Jun 3, 2026
0f7b7ae
fix(platform-wallet-storage): reject unbucketable unspent UTXOs and m…
lklimek Jun 3, 2026
569a19a
refactor(platform-wallet-storage): drop per-table row counting from d…
lklimek Jun 3, 2026
0435f3e
refactor(platform-wallet): slim persistence trait and relocate report…
lklimek Jun 3, 2026
19e8980
docs(platform-wallet): record deferred persistence contract and FFI f…
lklimek Jun 3, 2026
4bc8d2a
fix(wallet-storage): drop SQLITE_OPEN_URI from the read-only open path
lklimek Jun 3, 2026
bab3c63
refactor(wallet-storage): fix secrets error taxonomy and unify wallet…
lklimek Jun 3, 2026
0bba1f7
docs(wallet-storage): strip transient review IDs from backup comments
lklimek Jun 3, 2026
1b89b4b
docs(wallet-storage): align README/SCHEMA/SECRETS with shipped behaviour
lklimek Jun 3, 2026
f4aa2df
feat(platform-wallet-storage): reconstruct platform per_account state…
lklimek Jun 3, 2026
17ea61f
feat(platform-wallet-storage): cap KV value size on put and document …
lklimek Jun 3, 2026
30955c6
refactor(platform-wallet-storage): narrow the public module surface t…
lklimek Jun 3, 2026
0abdb12
fix(platform-wallet-storage): pre-create the SQLite DB owner-only to …
lklimek Jun 3, 2026
c6dbc11
test(platform-wallet-storage): add cascade-completeness test and prun…
lklimek Jun 3, 2026
30147e0
docs(platform-wallet-storage): strip transient review IDs from core s…
lklimek Jun 3, 2026
97be1e9
docs(platform-wallet-storage): strip transient review IDs from edited…
lklimek Jun 3, 2026
522d4ce
chore(platform-wallet-storage): finish review-ID strip, fix secret-sc…
lklimek Jun 3, 2026
3eac940
merge: integrate deps/docs/secrets/conn tail into #3625 fix batch
lklimek Jun 3, 2026
a90c84d
docs(platform-wallet-storage): document zero-pad diagnostic risk + st…
lklimek Jun 3, 2026
1d7c94d
docs(platform-wallet-storage): fix secrets-guard test ref + honest me…
lklimek Jun 3, 2026
bfc34cc
test(platform-wallet-storage): assert reconstructed per-account state
lklimek Jun 3, 2026
c92e9f9
test(platform-wallet-storage): catch over-broad wallet-delete cascade
lklimek Jun 3, 2026
b262e1b
chore(platform-wallet-storage): strip transient review IDs from comments
lklimek Jun 3, 2026
6f1e3e4
docs(platform-wallet-storage): document orphan metadata as accepted l…
lklimek Jun 3, 2026
c9d1c81
docs(platform-wallet-storage): lead crate docs with why/value before …
lklimek Jun 3, 2026
7de6678
refactor(platform-wallet-storage): rename wallet_metadata table to wa…
lklimek Jun 8, 2026
f69746d
merge: propagate #3625 (platform-wallet-storage incl. wallets rename)…
lklimek Jun 8, 2026
8ba49c3
test(platform-wallet-storage): reconcile #3692 tests with #3625's str…
lklimek Jun 8, 2026
9e1248c
fix(platform-wallet-storage): un-gate schema readers used by producti…
lklimek Jun 8, 2026
5a9cc73
test(platform-wallet-storage): cover garbage/truncated/unknown-versio…
lklimek Jun 8, 2026
6dba193
chore(platform-wallet-storage): harden secrets dependency closure
lklimek Jun 8, 2026
62e9c96
refactor(platform-wallet-storage): granular AEAD errors + typed salt …
lklimek Jun 8, 2026
f2f54a9
fix(platform-wallet-storage): harden the encrypted-file vault seams
lklimek Jun 8, 2026
a7e5d85
fix(platform-wallet-storage): authenticate the KDF header via verify-…
lklimek Jun 8, 2026
77ec94b
test(platform-wallet-storage): sound zeroize coverage + SPI seam doc
lklimek Jun 8, 2026
bdf2c27
chore(platform-wallet-storage): record argon2 zeroize edge in Cargo.lock
lklimek Jun 8, 2026
6e6a78d
refactor(platform-wallet-storage): graceful put rollback + write-bit …
lklimek Jun 8, 2026
86c5e63
feat(platform-wallet-storage): seal the no-key-material-in-DB invariant
lklimek Jun 8, 2026
f07f3c5
docs(platform-wallet-storage): keyring metadata + SPI error-format ha…
lklimek Jun 8, 2026
f36b719
test(platform-wallet-storage): fuzz the vault parser + tamper coverage
lklimek Jun 8, 2026
ff296df
docs(platform-wallet-storage): annotate accepted risks + narrow unsaf…
lklimek Jun 8, 2026
409dd5a
test(platform-wallet-storage): secure integration tempdir for parent-…
lklimek Jun 8, 2026
6502171
test(platform-wallet-storage): honest header-tamper tests + verify-to…
lklimek Jun 8, 2026
975cd47
test(platform-wallet-storage): pin delete_wallet pre-flush BEGIN-EXCL…
lklimek Jun 8, 2026
0bacb45
test(platform-wallet-storage): cover IdentityChangeSet.removed tombst…
lklimek Jun 8, 2026
88cee36
test(platform-wallet-storage): cover money-column read overflow + com…
lklimek Jun 8, 2026
5949439
test(platform-wallet-storage): pin prune embedded-timestamp parse + s…
lklimek Jun 8, 2026
f543e31
test(platform-wallet-storage): cover FK parent-before-child ordering …
lklimek Jun 8, 2026
af32973
fix(platform-wallet-storage): restore buffer when pre-flush BEGIN EXC…
lklimek Jun 8, 2026
cc33189
feat(platform-wallet-storage): wallet-DB identity gates + journal-mod…
lklimek Jun 8, 2026
d4523c0
feat(platform-wallet-storage): refuse a second in-process open on the…
lklimek Jun 8, 2026
e42f64b
fix(platform-wallet-storage): wallet-scope identity tombstone; correc…
lklimek Jun 8, 2026
08239c1
refactor(platform-wallet-storage): centralize i64->u32 boundary casts…
lklimek Jun 8, 2026
8e50d48
fix(platform-wallet-storage): fail-hard watermark, deterministic utxo…
lklimek Jun 8, 2026
40f9997
docs(platform-wallet-storage): reconcile README/SCHEMA/SECRETS with s…
lklimek Jun 8, 2026
475e3ae
docs(platform-wallet-storage): scope vault cross-process lock to loca…
lklimek Jun 8, 2026
e79e6cf
fix(platform-wallet-storage): drop SQLITE_OPEN_URI from CLI schema peek
lklimek Jun 8, 2026
039596d
chore(platform-wallet-storage): acknowledge RUSTSEC-2025-0141 (bincod…
lklimek Jun 8, 2026
9d40bdb
style(platform-wallet-storage): collapse single-line match arm per ru…
lklimek Jun 8, 2026
b4a3aa9
test(platform-wallet-storage): rename cascade test to reflect actual …
lklimek Jun 8, 2026
66c2142
docs(platform-wallet-storage): compact verbose comments in sqlite core
lklimek Jun 9, 2026
d73071f
docs(platform-wallet-storage): compact verbose comments in secrets mo…
lklimek Jun 9, 2026
197949d
docs(platform-wallet-storage): compact verbose comments in schema mod…
lklimek Jun 9, 2026
0ceb56e
docs(platform-wallet-storage): compact verbose comments in tests
lklimek Jun 9, 2026
60f2bd7
fix(platform-wallet-storage): repair comment-edit fallout (clippy doc…
lklimek Jun 9, 2026
855471e
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek Jun 9, 2026
2d22cf3
merge: propagate v3.1-dev (shielded spend detection #3819, IdentityCr…
lklimek Jun 9, 2026
50a4a0a
Merge remote-tracking branch 'origin/v3.1-dev' into merge/wallet-rehy…
lklimek Jun 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .cargo/audit.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[advisories]
# TODO Remove it from here
ignore = [ "RUSTSEC-2020-0071"] # advisory IDs to ignore e.g. ["RUSTSEC-2019-0001", ...]
# Advisory IDs to ignore, e.g. ["RUSTSEC-2019-0001", ...]. Each entry
# must point at a live advisory in the resolved graph and carry a dated
# rationale; an entry matching nothing trains reviewers to skim the list.
ignore = []
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,6 @@ __pycache__/

# Security audit reports (local-only, not committed)
audits/

# Review scratch (grumpy-review / triage output, local-only)
.review-*/
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

136 changes: 130 additions & 6 deletions packages/rs-platform-wallet-ffi/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,51 @@ unsafe fn create_wallet_from_mnemonic_impl(
PlatformWalletFFIResult::ok()
}

/// One wallet skipped during `load_from_persistor` because its
/// persisted row was structurally corrupt (per-row decode failure).
/// The load path is seedless and watch-only, so this is the only skip
/// reason. `reason_code` is per-`CorruptKind` family — see its table.
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct SkippedWalletFFI {
/// The (public) 32-byte wallet id that was skipped.
pub wallet_id: [u8; 32],
/// Structural skip reason. `100` = missing account manifest,
/// `101` = malformed account xpub, `102` = any other structural
/// decode error. No secret material is ever carried.
pub reason_code: u32,
}

/// C-visible summary of one `load_from_persistor` pass so the host can
/// see which wallets loaded and which were skipped (and why) instead
/// of the outcome being silently discarded.
///
/// `skipped` is a heap array of length `skipped_count`; pass this
/// struct (by pointer) to
/// [`platform_wallet_load_outcome_free`] exactly once to release it.
#[repr(C)]
#[derive(Debug)]
pub struct LoadOutcomeFFI {
/// Number of wallets fully reconstructed + registered.
pub loaded_count: usize,
/// Length of the `skipped` array.
pub skipped_count: usize,
/// Heap-allocated skipped-wallet array (null iff `skipped_count`
/// is 0). Owned by Rust until `platform_wallet_load_outcome_free`.
pub skipped: *mut SkippedWalletFFI,
}

fn skip_reason_code(reason: &platform_wallet::SkipReason) -> u32 {
use platform_wallet::manager::load_outcome::CorruptKind;
match reason {
platform_wallet::SkipReason::CorruptPersistedRow { kind } => match kind {
CorruptKind::MissingManifest => 100,
CorruptKind::MalformedXpub => 101,
CorruptKind::DecodeError(_) => 102,
},
}
}

/// Create a wallet from raw seed bytes (64 bytes).
///
/// On success, `out_wallet_handle` is set to a `PlatformWallet` handle and
Expand Down Expand Up @@ -296,23 +341,102 @@ pub unsafe extern "C" fn platform_wallet_manager_create_wallet_from_mnemonic_wit
///
/// Triggers `on_load_wallet_list_fn` on the persistence callbacks to
/// fetch the persisted wallet list from the client side (SwiftData),
/// reconstructs each wallet as **watch-only** via its stored root +
/// per-account xpubs, and registers them inside the manager. Does not
/// produce wallet handles — the caller should follow up with
/// [`platform_wallet_manager_get_wallet`] per `wallet_id` it knows
/// about.
/// builds a keyless reconstruction payload per wallet, then registers
/// each one as a **watch-only** wallet. No signing keys are derived
/// here — signing happens later, on demand, via the configured
/// `MnemonicResolverHandle` (`sign_with_mnemonic_resolver` and its
/// siblings), which fail-closed gate the resolver-supplied seed
/// against the loaded `wallet_id`. Does not produce wallet handles —
/// follow up with [`platform_wallet_manager_get_wallet`] per
/// `wallet_id`.
///
/// A wallet whose persisted row is structurally corrupt is
/// **skipped**, not failed: the call still returns `Success`, every
/// skipped `(wallet_id, reason)` is logged, and — when `out_outcome`
/// is non-null — surfaced through it.
///
/// # Safety
/// - `out_outcome` may be null (caller doesn't want the summary);
/// otherwise it must point to writable `LoadOutcomeFFI` storage and
/// the caller must later release it via
/// [`platform_wallet_load_outcome_free`].
#[no_mangle]
pub unsafe extern "C" fn platform_wallet_manager_load_from_persistor(
manager_handle: Handle,
out_outcome: *mut LoadOutcomeFFI,
) -> PlatformWalletFFIResult {
let option = PLATFORM_WALLET_MANAGER_STORAGE.with_item(manager_handle, |manager| {
runtime().block_on(manager.load_from_persistor())
});
let result = unwrap_option_or_return!(option);
unwrap_result_or_return!(result);
let outcome = unwrap_result_or_return!(result);

// Never silently drop the outcome: log a structured summary plus
// one line per skipped wallet (the host can inspect / clear the
// corrupt rows).
tracing::info!(
loaded = outcome.loaded.len(),
skipped = outcome.skipped.len(),
"platform_wallet_manager_load_from_persistor complete"
);
for (wid, reason) in &outcome.skipped {
tracing::warn!(
wallet_id = %hex::encode(wid),
reason = %reason,
"load_from_persistor skipped wallet (corrupt persisted row)"
);
}

if !out_outcome.is_null() {
let skipped_vec: Vec<SkippedWalletFFI> = outcome
.skipped
.iter()
.map(|(wid, reason)| SkippedWalletFFI {
wallet_id: *wid,
reason_code: skip_reason_code(reason),
})
.collect();
let skipped_count = skipped_vec.len();
let skipped_ptr = if skipped_count == 0 {
std::ptr::null_mut()
} else {
let boxed = skipped_vec.into_boxed_slice();
Box::into_raw(boxed) as *mut SkippedWalletFFI
};
std::ptr::write(
out_outcome,
LoadOutcomeFFI {
loaded_count: outcome.loaded.len(),
skipped_count,
skipped: skipped_ptr,
},
);
}
PlatformWalletFFIResult::ok()
}

/// Release the heap `skipped` array a successful
/// [`platform_wallet_manager_load_from_persistor`] wrote into a
/// `LoadOutcomeFFI`. Idempotent: nulls the pointer after freeing, and
/// a null `outcome` (or already-freed array) is a no-op.
///
/// # Safety
/// `outcome` must point to a `LoadOutcomeFFI` previously populated by
/// `platform_wallet_manager_load_from_persistor`, not freed already.
#[no_mangle]
pub unsafe extern "C" fn platform_wallet_load_outcome_free(outcome: *mut LoadOutcomeFFI) {
if outcome.is_null() {
return;
}
let o = &mut *outcome;
if !o.skipped.is_null() && o.skipped_count > 0 {
let slice = std::slice::from_raw_parts_mut(o.skipped, o.skipped_count);
drop(Box::from_raw(slice as *mut [SkippedWalletFFI]));
}
o.skipped = std::ptr::null_mut();
o.skipped_count = 0;
}

/// Get a `PlatformWallet` handle for a wallet registered in the
/// manager. Returns `NotFound` if no wallet with the given
/// id is currently held.
Expand Down
52 changes: 50 additions & 2 deletions packages/rs-platform-wallet-ffi/src/persistence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3361,11 +3361,59 @@ fn build_wallet_start_state(
// status without rebroadcasting.
let unused_asset_locks = build_unused_asset_locks(entry)?;

// Project the reconstructed `wallet` + `wallet_info` into the
// keyless `ClientWalletStartState` the persister contract requires
// (SECRETS.md: no `Wallet`/seed crosses `load()`). The manager
// rebuilds a watch-only wallet from this manifest via
// `Wallet::new_watch_only` and applies this `core_state` projection.
// Signing happens later via the on-demand
// `sign_with_mnemonic_resolver` path, which fail-closed gates the
// resolver-supplied seed against the loaded `wallet_id`. The
// locally-built `wallet` is dropped — it was only needed to shape
// the account collection / UTXO routing above.
let account_manifest: Vec<AccountRegistrationEntry> = wallet
.accounts
.all_accounts()
.into_iter()
.map(|a| AccountRegistrationEntry {
account_type: a.account_type,
account_xpub: a.account_xpub,
})
.collect();
let new_utxos: Vec<key_wallet::Utxo> = wallet_info
.accounts
.all_funding_accounts()
.into_iter()
.flat_map(|acct| acct.utxos.values().cloned())
.collect();
let core_state = platform_wallet::changeset::CoreChangeSet {
new_utxos,
last_processed_height: (wallet_info.metadata.last_processed_height > 0)
.then_some(wallet_info.metadata.last_processed_height),
synced_height: (wallet_info.metadata.synced_height > 0)
.then_some(wallet_info.metadata.synced_height),
..Default::default()
};

// `contacts` / `identity_keys` are the PR-3 keyless feed the
// manager layers onto the managed identities via
// `apply_contacts_and_keys`. The iOS path does NOT use them:
// identity PUBLIC keys are already reconstructed straight into
// `Identity.public_keys` by `build_wallet_identity_bucket` (feeding
// the slot too would double-apply), and `WalletRestoreEntryFFI`
// carries no contacts back from Swift on load — surfacing them
// would need a new cross-boundary struct field + Swift wiring,
// tracked as a follow-up. Empty slots make `apply_contacts_and_keys`
// a no-op for this path, preserving the established iOS behaviour.
let wallet_state = ClientWalletStartState {
wallet,
wallet_info,
network,
birth_height: entry.birth_height,
account_manifest,
core_state,
identity_manager,
unused_asset_locks,
contacts: Default::default(),
identity_keys: Default::default(),
};

let platform_address_state = if per_account.is_empty()
Expand Down
26 changes: 26 additions & 0 deletions packages/rs-platform-wallet-storage/.cargo/audit.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[advisories]
# Each entry MUST point at a live advisory in this crate's resolved graph
# and carry a dated rationale + remediation plan. Do not blanket-ignore.
ignore = [
# RUSTSEC-2025-0141 — bincode is unmaintained (informational advisory,
# not an exploitable CVE; published 2025-12-16, no patched release).
# Acknowledged 2026-06-08.
#
# Why this is acceptable for now:
# - bincode 2.0.1 is the BLOB-codec trust boundary for every
# persisted column and backup. The known risk class for an
# unmaintained deserializer is unbounded allocation (OOM) on a
# crafted/corrupt input.
# - In-crate size bounds defang that class: MAX_VALUE_LEN /
# BLOB_SIZE_LIMIT_BYTES (kv/blob), the per-secret SecretTooLarge
# write cap, and the MAX_VAULT_SIZE_BYTES read ceiling all reject
# oversized inputs before bincode ever allocates from them.
# - load() is fail-hard: a malformed/over-large row aborts the call
# with a typed error rather than silently over-allocating.
#
# Residual risk: a future bincode defect would go unpatched upstream.
# Remediation plan: migrate the BLOB codec to a maintained equivalent
# (postcard / bitcode candidates) once the wire format is frozen at
# release; revisit this ignore at that time.
"RUSTSEC-2025-0141",
]
16 changes: 15 additions & 1 deletion packages/rs-platform-wallet-storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,14 @@ keyring-core = { version = "=1.0.0", optional = true }
# was removed from the sqlite arm — those tests grep for `fs2`/`fs4`
# literals in this crate's source/manifest and would re-trigger on the
# older crates. `fd-lock` has no such collision.
fd-lock = { version = "4.0.4", optional = true }
# LOCAL-FS ONLY: flock/LockFileEx interlock processes only on local
# filesystems; over NFS/CIFS the lock does not interlock, so a vault file
# must not be shared across hosts — steer multi-host to the OS-keyring arm.
# Exact-pinned (`=`) like the rest of the soundness-critical stack: the
# `VaultLock` unsafe drop-order argument in `secrets/file/mod.rs` is
# calibrated to fd-lock 4.0.4's guard internals; any bump must re-verify
# that the guard releases the OS lock before the backing `RwLock` frees.
fd-lock = { version = "=4.0.4", optional = true }

# CLI deps (gated by the `cli` feature)
clap = { version = "4", features = ["derive"], optional = true }
Expand Down Expand Up @@ -180,6 +187,13 @@ cli = [
# crate without the crypto graph.
secrets = [
"dep:argon2",
# Enable argon2's `zeroize` feature so the KDF wipes its sensitive
# intermediate state (`initial_hash`/`blockhash`) on drop. In argon2
# 0.5.3 this does NOT cover the bulk `Block` matrix — that residual is
# documented at `derive_key` in `secrets/file/crypto.rs`. Keep it in
# the feature list (not a `default-features = false` rewrite) so
# argon2's own default features stay intact.
"argon2/zeroize",
"dep:chacha20poly1305",
# secrets uses serde directly (vault format + crypto envelope derive
# `Serialize`/`Deserialize`); declare the dep here so
Expand Down
43 changes: 30 additions & 13 deletions packages/rs-platform-wallet-storage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,11 @@ flush, 5 s busy timeout, WAL journal, `NORMAL` synchronous, and an
auto-backup dir at `<db_dir>/backups/auto/`.

The trait surface is `store` / `flush` / `load` / `get_core_tx_record`.
Schema migrations are append-only Rust files under `migrations/`, applied
via [`refinery`](https://github.com/rust-db/refinery) on every `open`.
Schema migrations are versioned Rust files under `migrations/`, applied via
[`refinery`](https://github.com/rust-db/refinery) on every `open`. While the
crate is unreleased, in-place edits to the sole shipped `V001` are allowed;
the append-only guarantee (add a new versioned file, never edit a prior one)
takes effect once the schema is frozen at release.

#### Flush semantics (store / flush)

Expand Down Expand Up @@ -138,17 +141,31 @@ so one failed wallet does not hide its siblings.

#### load() reconstruction

`SqlitePersister::load()` returns the base `ClientStartState` (plain struct,
two slots — no `#[non_exhaustive]`):
`SqlitePersister::load()` returns a fully-rehydrated `ClientStartState`
(plain struct — no `#[non_exhaustive]`). Both slots are populated:

| Slot | Reader | Status |
|---|---|---|
| `platform_addresses` | `schema::platform_addrs::load_all` (a fixed set of grouped scans over `platform_address_sync`, `platform_addresses`, and `account_registrations`, driven by the `wallet_meta::list_ids` wallet universe) | populated |
| `wallets` | — | empty pending upstream `Wallet::from_persisted` |

The `identities` / `contacts` / `asset_locks` per-area readers exist as
hardened dormant helpers (`schema::<area>::load_state`) but are not wired
into `load()` — `ClientStartState` carries no slot for them.
| `platform_addresses` | `schema::platform_addrs::load_all` (a fixed set of grouped scans over `platform_address_sync`, `platform_addresses`, and `account_registrations`, driven by the `wallets::list_ids` wallet universe) | populated |
| `wallets` | per-wallet `schema::<area>` readers (see below) | populated |

Each `ClientStartState::wallets` entry is a **keyless** `ClientWalletStartState`
reconstructed from these per-area readers:

| Field | Reader |
|---|---|
| `network` / `birth_height` | `schema::wallets::fetch` |
| `account_manifest` | `schema::accounts::load_state` |
| `core_state` | `schema::core_state::load_state` |
| `identity_manager` | `schema::identities::load_state` |
| `unused_asset_locks` | `schema::asset_locks::load_unconsumed` (`Consumed`-filtered — spent locks stay on disk but are never resurrected) |
| `contacts` | `schema::contacts::load_changeset` |
| `identity_keys` | `schema::identity_keys::load_state` |

The payload carries **no** `Wallet` and no key material — the manager
rebuilds each wallet watch-only via `Wallet::new_watch_only` from the
manifest and applies this state; signing keys are derived later on demand
via the `sign_with_mnemonic_resolver` path.

Loading is **fail-hard**: any row that fails to decode, or a stored
`wallet_id` that is not exactly 32 bytes, aborts the whole call with a typed
Expand All @@ -158,9 +175,9 @@ corruption tolerance, no per-row skip, and no partial `Ok` — a corrupt
database surfaces as an error rather than silently losing rows.

The summary `tracing::info!` carries `wallets_seen`, `addresses_loaded`,
`wallets_rehydrated`, and `wallets_pending_rehydration` (the count of
wallets that *would* be rehydrated once upstream provides
`Wallet::from_persisted`).
`wallets_rehydrated` (the count actually rehydrated this call), and
`wallets_pending_rehydration` (now always `0` — every seen wallet is
rehydrated). The only deferred field is listed in `LOAD_UNIMPLEMENTED`.

### KV metadata API

Expand Down
Loading
Loading