Skip to content

feat(client): minimal singular import_secret / export_secret wrappers - #253

Open
emostov wants to merge 3 commits into
mainfrom
agent/minimal-secret-import-export
Open

feat(client): minimal singular import_secret / export_secret wrappers#253
emostov wants to merge 3 commits into
mainfrom
agent/minimal-secret-import-export

Conversation

@emostov

@emostov emostov commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds two client methods for single-secret import and export. Each folds the whole flow into one call; callers pass only business inputs

Tested demo locally - requires secrets enabled for org since it is still in private beta.

@emostov
emostov force-pushed the swag/sync-protos-from-mono-20260813 branch 2 times, most recently from 369da54 to 97c53d4 Compare August 24, 2026 16:40
@tkhq-valet-dev
tkhq-valet-dev Bot force-pushed the agent/minimal-secret-import-export branch 2 times, most recently from ccef5ca to 17182c5 Compare August 24, 2026 17:15
@emostov
emostov changed the base branch from swag/sync-protos-from-mono-20260813 to main August 24, 2026 17:19
@emostov
emostov force-pushed the agent/minimal-secret-import-export branch 5 times, most recently from 54d5620 to c84006f Compare August 24, 2026 19:12
…s built on the existing enclave-encrypt clients, with a runnable secrets example
@emostov
emostov force-pushed the agent/minimal-secret-import-export branch from c84006f to fed5248 Compare August 24, 2026 19:28
@emostov
emostov marked this pull request as ready for review August 24, 2026 19:34
@emostov
emostov requested a review from a team as a code owner August 24, 2026 19:34
richardpringle
richardpringle previously approved these changes Aug 24, 2026

@richardpringle richardpringle 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.

deps review

Comment thread Cargo.lock

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.

👍

@figitaki figitaki 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.

Reviewed with tests run locally (secrets tests pass, clippy clean). One question about the hardcoded empty user ID, plus a few non-blocking nits inline. The tests are strong: real HPKE round-trips, per-secret one-shot target keys, and enclave-side decrypt of the import payload.

Comment thread client/src/lib.rs Outdated
// always empty: the flow is scoped to the organization, not to a user.
let target_data = target_data(&target)?;
let secret_payload = ImportClient::new(signer_quorum_public_key)
.encrypt_wallet_with_bundle(&plaintext, &target, &organization_id, "")?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This bakes in the assumption that secrets ingress targets always carry an empty user_id. encrypt_wallet_with_bundle verifies the signed bundle's user_id against this value, so if the signer ever stamps the initiating user into these targets, every import_secret call fails with InvalidUser.

For comparison, mono's e2e test (src/go/e2e/public/secrets_test.go) passes targetData.UserId from the bundle through instead of asserting it is empty. Since target_data() already parses the bundle here, passing target_data.user_id would be forward-compatible. Keeping "" is a stricter contract assertion, which is also defensible — but it should be a deliberate choice. Is "always empty" guaranteed by the signer, or just current behavior?

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.

It will always be empty. The user ID is a vestige of how wallets import/export was designed but is not at all related to secret storage other then the fact it re-uses some of the primitives

Comment thread client/src/lib.rs Outdated
Comment thread client/src/lib.rs
Comment thread client/src/lib.rs

/// Exports secrets and returns their decrypted UTF-8 plaintexts, in the
/// order of `secret_ids`.
pub async fn export_secret(

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: import_secret is truly singular but export_secret takes a slice and returns Vec<String>. I assume the generated export_secrets occupies the plural name — fine, but the asymmetry reads oddly. Worth a doc sentence explaining why, if a rename is off the table.

(One property I checked: trusting response order is safe here, because each payload is encrypted to its own one-shot target key — a reordered response fails HPKE decryption rather than silently mis-associating plaintexts.)

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.

Doc comment should now reflect it is batch

Comment thread client/src/lib.rs Outdated
.map(|(payload, recipient)| {
// Secret payloads are enclave-signed bundles over UTF-8 bytes,
// the same shape a wallet export bundle carries.
recipient.decrypt_wallet_mnemonic_phrase(payload, &organization_id)

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: reusing decrypt_wallet_mnemonic_phrase / encrypt_wallet_with_bundle for secrets is functionally correct (both are just UTF-8 bytes) and the comments explain it, but a neutral-named alias in turnkey_enclave_encrypt (e.g. decrypt_utf8 / encrypt_utf8_with_bundle) would keep call sites honest long-term. Fine as a follow-up.

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.

Originally I had something like this, but was just trying to minimize diff

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.

…ecrypt_secret wrappers and use them in the secrets flow
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.

3 participants