Introduce Credential Activity History Store - #481
Conversation
a003298 to
c53e3ff
Compare
| credential_type INTEGER NOT NULL, | ||
| credential_source INTEGER NOT NULL, |
There was a problem hiding this comment.
isn't that basically included in issuer_schema_id?
There was a problem hiding this comment.
Sorta! The issuer_schema_id could request one type, but we could provide a higher assurance credential instead. So they may request a document, but we provide a PoH.
| credential_type INTEGER NOT NULL, | ||
| credential_source INTEGER NOT NULL, | ||
| proof_kind INTEGER NOT NULL, |
There was a problem hiding this comment.
maybe just a metadata json column?
c53e3ff to
57a7ff5
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 57a7ff5. Configure here.
828b94b to
e2490d0
Compare
Introduces a sharable on-device store for Android and iOS to retain credential activity. We're re-using some components from the Wallet storage here, while maintaining a separate database for the local activity as it is a separate concern and should never be backed up with the existing systems. I've modeled the changes after how I typically use rust; mod.rs is mainly imports with most of the code in dedicated files, and tests in their related files. This system is expected to evolve over time so i've prioritized a sensible migration strategy as part of these changes. Tested integration on iOS. Will do Android later and follow up with any PRs that might be necessary for their integration. No runtime differences until the host apps are updated to use the new system.
e2490d0 to
d44b0b2
Compare
| //! ```text | ||
| //! account.cache.sqlite # sqlite3mc-encrypted cache DB (keyed by K_intermediate) | ||
| //! account.vault.sqlite # sqlite3mc-encrypted vault DB (keyed by K_intermediate) | ||
| //! account.activity.sqlite # sqlite3mc-encrypted activity DB (keyed by K_intermediate) |
There was a problem hiding this comment.
high. the account.cache is probably the best suited place for this functionality. it's meant to store things that are per-device only. in fact, depending on how we'd query this, we may not even need a new table (haven't gotten there in the review yet)
| }; | ||
|
|
||
| /// Storage handle for credential-activity history: a device-local, | ||
| /// chronological log of proof-share request outcomes. |
There was a problem hiding this comment.
| /// chronological log of proof-share request outcomes. | |
| /// chronological log of [`ProofRequest`] outcomes. |
nit. don't forget to import ProofRequest
| /// Storage handle for credential-activity history: a device-local, | ||
| /// chronological log of proof-share request outcomes. | ||
| #[derive(uniffi::Object)] | ||
| pub struct ActivityStore { |
There was a problem hiding this comment.
one thing that would be very helpful is to document in the doc comments here the expected flow. how is this intended to work? how are logs stored? how are they expected to be consumed?
| /// chronological log of proof-share request outcomes. | ||
| #[derive(uniffi::Object)] | ||
| pub struct ActivityStore { | ||
| activity: Mutex<Option<OpenActivity>>, |
There was a problem hiding this comment.
(see previous comment), with the change to using the cache db, this won't be necessary
|
|
||
| /// A single row of credential activity history. | ||
| #[derive(Debug, Clone, PartialEq, Eq, uniffi::Record)] | ||
| pub struct ActivityEntry { |
There was a problem hiding this comment.
high (from live discussion). proposal is to keep only this and one-shot insert an entry once its state is resolved (either in success or failure)
| activity_store.destroy_storage(paths)?; | ||
| } | ||
| #[cfg(target_arch = "wasm32")] | ||
| let _ = activity_store; |
|
|
||
| /// Version range applied by a schema migration run during [`super::ActivityStore::open`]. | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, uniffi::Record)] | ||
| pub struct ActivitySchemaMigration { |
There was a problem hiding this comment.
might not be needed if we use the existing cache table

Introduces a sharable on-device store for Android and iOS to retain credential activity. We're re-using some components from the Wallet storage here, while maintaining a separate database for the local activity as it is a separate concern and should never be backed up with the existing systems.
I've modeled the changes after how I typically use rust; mod.rs is mainly imports with most of the code in dedicated files, and tests in their related files.
This system is expected to evolve over time so i've prioritized a sensible migration strategy as part of these changes.
Tested integration on iOS. Will do Android later and follow up with any PRs that might be necessary for their integration. No runtime differences until the host apps are updated to use the new system.
Note
Medium Risk
Touches account teardown (
destroy_storageAPI change), shared encryption key lifetime, and destructive DB rebuild paths; host apps must wire activity open/record/reconcile and pass the store on destroy.Overview
Adds a device-local credential activity history layer: a separate encrypted SQLite database (
account.activity.sqlite) keyed with the sameK_intermediateas vault/cache, exposed to host apps via UniFFI asActivityStore.Hosts can record proof-share lifecycle (start → finalize with outcomes/failure reasons), list paginated history, read metadata, and reconcile stale pending rows to
Incomplete(with optionalclient_idexclusions). Schema migrations are reported on first open; open retries transient lock contention and can wipe/rebuild on corruption or wrong key. Native builds get anActivityChangedListener(background thread, same pattern as vault changes).Credential storage now exposes
intermediate_key()through a shareableIntermediateKeyHandle, refactorsStorageKeysfor shared/destroyable keys, and explicitly zeroizes keys ondestroy_storage.Authenticator::destroy_storagetakes an optionalActivityStoreand deletes the activity DB on logout/account deletion (native only; wasm ignores it).New activity-specific
StorageErrorvariants andwalletkit-dbquery_row_optionalon transactions support finalize-by-client_id.Reviewed by Cursor Bugbot for commit d44b0b2. Bugbot is set up for automated code reviews on this repo. Configure here.