feat(devices): operator-provisioned device credentials#299
Merged
Conversation
…dentials Backend for spec DPK: an admin provisions a device at a chosen role and Canopy mints the keypair, stores only the public SPKI, and returns the private key once as an age/passphrase-encrypted blob for one-off download (decryptable with bestool crypto reveal). Private key is never persisted. - commons-servers device_auth::keygen: P-256 keygen, SPKI derived exactly as the mTLS path extracts it - Device::create_at_role for a device trusted from the outset - devices::provision_credential handler (new or existing device) - share generate_passphrase between servers and devices fns Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Create-device button on the trusted list and a Provision credential action on the device page, both opening a one-shot dialog that shows the generated passphrase and downloads the age-encrypted key file (spec DPK). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Endpoint tests for spec DPK: new-device and existing-device provisioning, role update, untrusted rejected (400), missing device 404, and a round-trip that decrypts the returned blob and asserts the revealed private key's SPKI equals the stored public key. Adds commons-tests spki_from_key_pem helper. Provision handler returns 400 (BadRequest) for the untrusted role. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 Operator-provisioned device credentials (spec DPK).
Getting a keypair-authenticated device with a non-server role (releaser, backup-restore) previously meant generating a keypair off-platform and authenticating by hand. This adds an operator-driven flow: create a device at the right role, and Canopy mints the keypair, stores only the public key, and hands the private key back once — encrypted under a generated passphrase, decryptable with
bestool crypto reveal.How it works
SubjectPublicKeyInfoas an activedevice_keysrow, at the operator's chosen role. The credential is immediately valid — no enrolment handshake.mint_enrollment. It is never persisted or logged; losing it means provisioning a new one.bestool crypto reveal <file>.agedecrypts it directly — no bestool changes.Surface
POST /api/devices/provision_credential— new device at a role, or an added key on an existing device..age, shown-once warning).Notes
subject_pki.raw), and a round-trip test decrypts the returned blob and asserts the revealed private key's public half is byte-identical to the stored key.server. This is a weaker posture than server self-enrolment (Canopy briefly holds the private key and it transits a download); it is deliberate per the design discussion.A follow-up PR retires the
untrustedrole and fixes server-binding to trust asserver.