Skip to content

feat(storage): OrbPcpStore on walletkit-db - #2

Open
danielle-tfh wants to merge 8 commits into
mainfrom
phase-1a-orb-pcp-store
Open

danielle-tfh wants to merge 8 commits into
mainfrom
phase-1a-orb-pcp-store

Conversation

@danielle-tfh

@danielle-tfh danielle-tfh commented May 13, 2026 •

Copy link
Copy Markdown
Contributor

PCP storage groundwork; the shared-vault integration below is still pending.

Notes

  • OrbPcpStore currently owns a separate encrypted SQLite file on walletkit-db. It stores one row per signup, enrollment state and pending download acknowledgements.
  • Repeat put_package merges only the acknowledgement flag and returns the original content id. It now checks the existing row before storing bytes, so different retry payloads cannot leave unused blobs or rewind enrollment.
  • mark_ack reports missing signups; unacked_signups supports acknowledgement retries. Walletkit remains responsible for selecting the latest credential.
  • Proposed handoff to @Dzejkop: use PcpVault from feat(storage): store PCPs in a dedicated vault table walletkit#633 for sealed package bytes and a separate orb_pcp_lookup SQL table in the same vault for signup, status, acknowledgement, version, source, signup reason and timestamps. Walletkit owns persistence; OrbKit owns enrollment rules.
  • The shared-vault API must commit package and lookup writes together, preserve lifecycle fields on repeat downloads, and update status without overwriting concurrent acknowledgement changes. The two existing tables are both named pcp_records but have incompatible schemas, so this needs an explicit migration.
  • Consumer-facing change still to implement: replace OrbKit's directory-based vault setup with the shared storage handle. Migration must preserve failed captures and supported enrollment retries, verify imported bytes and lookup state, and retain legacy data until cutover is verified. Storage failures must reach the caller without sensitive ids.
  • PCP transfer remains open: the linked walletkit change deliberately excludes PCPs from credential export/import/merge and its backup listener. Sharing the database file does not make those packages transfer with credentials.
  • AI-assisted fix and handoff analysis. Ran cargo test -p orb-kit --locked --offline (10 passed), cargo clippy -p orb-kit --all-targets --locked --offline -- -D warnings, and cargo fmt --all -- --check.

Note

Medium Risk
Adds encrypted local storage and a lifecycle state machine for PCPs; on-disk schema and filenames are a long-lived contract, though behavior is covered by unit and integration tests.

Overview
Introduces the first orb-kit slice of on-device Personal Custody Package (PCP) storage, built on walletkit-db instead of oxide. The public storage module exposes OrbPcpStore, which opens a dedicated encrypted vault (fixed filenames, envelope AD, lock) and manages one pcp_records row per signup with encrypted package blobs in blob_objects.

put_package inserts new downloads at Downloaded; repeat downloads only merge the download-ack flag and return the existing content id so status and blobs are not reset. update_status enforces a PackageStatus transition graph; get, mark_ack, and unacked_signups support reads and cold-start ack retry. The DDL is guarded by a frozen schema hash test, and repo AGENTS.md documents byte-stable format and walletkit-db boundary rules. Cargo.toml adds walletkit-db 0.20.4 and thiserror; integration tests use in-memory keystore/blob stubs.

Reviewed by Cursor Bugbot for commit 687fbe9. Bugbot is set up for automated code reviews on this repo. Configure here.

@danielle-tfh
danielle-tfh force-pushed the phase-1a-orb-pcp-store branch 2 times, most recently from 40e7a69 to bbd857d Compare May 13, 2026 14:42
@danielle-tfh
danielle-tfh force-pushed the phase-1a-orb-pcp-store branch 7 times, most recently from 3cae9c2 to 5c5c675 Compare June 25, 2026 11:31
@danielle-tfh
danielle-tfh marked this pull request as ready for review June 25, 2026 14:44
@paolodamico

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5c5c675b0c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/storage/store.rs Resolved
Comment thread src/storage/schema.rs Resolved
Comment thread src/storage/store.rs Resolved
@worldcoin worldcoin deleted a comment from cursor Bot Jun 29, 2026
@paolodamico

Copy link
Copy Markdown

cursor review

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread src/storage/store.rs Resolved
Comment thread README.md Resolved
Comment thread README.md Outdated

## How storage is protected

OrbKit's PCP vault is a sqlite3mc-encrypted `SQLite` file. Page-level encryption uses a 32-byte working key (`K_intermediate`); that key is itself sealed under the device's hardware keystore key (`K_device`) using ChaCha20-Poly1305 AEAD with a per-consumer associated-data string, and persisted as a CBOR envelope.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

why is OrbKit's vault an sqlite db? taking a step back, the PCPs should live as associated data in walletkit's credential vault, I'm not sure introducing another sqlite layer makes sense

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fair q! main reason is PCPs exist before theres a credential to attach them to (downloaded, enrollment pending, no PoH cred yet), plus per-tier shards and the status machine.

What ya think?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

oops missed this. well one way to look at it is that each PCP is an Orb credential. I think nesting another sqlite DB into the credential vault will probably bring some headaches

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yup @paolodamico, i was mixing up reusing walletkit-db with sharing the credential vault. my clanker checked worldcoin/walletkit#633 and pending PCPs no longer need an issued credential.

lets keep the lookup as SQL rows in that same vault. walletkit handles persistence, orbkit keeps the enrollment rules, and package + lookup writes commit together. PCPs are excluded from that PR's credential export, so device transfer still needs explicit support.

@Dzejkop can you take over the lookup API and migration from here? ive updated this PR with the handoff.

@danielle-tfh danielle-tfh reopened this Oct 20, 2026
put_package is strict so a re-download cannot reset a row past Downloaded. walletkit-db bumped to 0.20.4, whose Keystore::seal takes slices.
oxide only ever writes tier 0 in production (tier-1 extraction is test-only), so the composite key and per-tier API were unused. Removes the mixed-tier status case entirely.
Matches what oxide does on a repeat download today; the only caller would swallow AlreadyExists anyway.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ 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 2d114a3. Configure here.

Comment thread src/storage/store.rs Resolved
Drop unused secrecy/serde deps and the gitignore drive-by. open takes the vault directory and owns its file names and lock, so paths.rs goes away. Storage submodules are private behind the re-exports.
Comment thread src/storage/store.rs
use crate::storage::types::{CreationSource, PackageStatus, PcpRecord, SignupId};

// On-disk format: renaming any of these orphans every existing vault.
const VAULT_FILENAME: &str = "orb_pcp.sqlite";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

should the orb pcp be another sqlite db? walletkit has a vault db where the idea is that arbitrary associated data blobs (e.g. PCPs) can be added. the associated data blobs can be anything, but I don't think it makes a lot of sense for it to be another sqlite db

Comment thread src/storage/store.rs Outdated
ingest.encrypted_bytes,
ingest.now_seconds,
)?;
let stored_cid = conn.query_row(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

should we be storing a bunch of PCPs instead of just the latest?

Comment thread src/storage/types.rs
impl PackageStatus {
/// Stable string used as the SQL `TEXT` value.
#[must_use]
pub const fn as_str(self) -> &'static str {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit. you can use strum to avoid the extra code

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants