drift Docs
Start
What is Drift?
The tour, if you are new here.
Why Drift?
The case for a smaller cloud.
Getting started
Nothing to deployed, in one command.
Architecture
How a slice is put together.
What it costs
The free grant, five unit prices, two rules.
Build
Canvas
Static sites, same origin as your API.
Tools
Operate
Auth
Accounts, tokens and scopes.
Security
Boundaries, sandboxing and hardening.

Deed Link

Multi-device continuity for a KeyAuth identity: enroll a second, third, … device without ever handing over the original private key. Not to be confused with cross-slice calling (drift slice link, an unrelated feature for one of your own slices calling another); this Link enrolls a device for one identity.

Called byWhat it does
  • Begin(pubkey, metadata?)
    Called by
    the joining device
    What it does
    Opens a session and returns its id. metadata is an opaque string the attester can read back.
  • QR(text)
    Called by
    either
    What it does
    Renders a short string, in practice the session id, as SVG markup, server-side.
  • SessionInfo(sessionID)
    Called by
    the attesting device
    What it does
    Returns the session's new_pubkey and metadata. Read-only and repeatable.
  • Attest(identity, sessionID, attestingPubkey, sig, sealed?)
    Called by
    an active device
    What it does
    Vouches for the session's device. sealed is an opaque payload relayed to the joiner.
  • Complete(sessionID)
    Called by
    the joining device
    What it does
    Polls the session: pending, then attested with the identity and any sealed payload.
  • Revoke(identity, targetPubkey, revokingPubkey, sig)
    Called by
    an active device
    What it does
    Deactivates a device, including itself.

Enrolling a second device

SessionInfo is not optional in practice. The attesting device only ever learns the session id, from a scanned or typed code, and the message it has to sign is over the joiner's new_pubkey. The slice verifies against its own stored copy of that value and never trusts the request body, so the attester has to fetch it and rebuild the message exactly.

Go
// on the NEW device: fresh keypair, open a session, show the code
sessionID, _ := drift.Deed.Link.Begin(newDevicePubkey, boxPubkeyHex)   // metadata: a key to seal for
svg, _ := drift.Deed.Link.QR(sessionID)

// on an ALREADY-ACTIVE device: read the session, sign client-side, attest
info, _ := drift.Deed.Link.SessionInfo(sessionID)   // info.NewPubkey, info.Metadata
drift.Deed.Link.Attest(identity, sessionID, activePubkey, sig, sealedForJoiner)

// back on the NEW device, polled until an active device has vouched
status, _ := drift.Deed.Link.Complete(sessionID)
// status.Status == "attested" → status.Identity, status.Sealed

sealed is how the joining device receives key material. Deed relays it and never opens it: seal a payload for the ephemeral key the joiner published as metadata, and Complete hands it back once the attestation lands. A session lives 5 minutes.

Link's two signatures are made client-side over a fixed domain of Link's own, not your app's in the same canonical form as KeyAuth's, with sorted keys and no whitespace:

JSON
{"domain":"drift-deed-link-attest-v1","identity":"<identity>","new_pubkey":"<joiner>"}
{"domain":"drift-deed-link-revoke-v1","identity":"<identity>","target_pubkey":"<device>"}

Revoking a device

Go
drift.Deed.Link.Revoke(identity, targetPubkey, revokingPubkey, sig)

Any currently-active device can revoke another, or itself. The revoked key still produces cryptographically valid signatures; KeyAuth refuses it anyway, which is what the registry is for.

Drift verifies, it never decides.

A device joins an identity's registry only on the strength of a signature from a device that is active in that same registry. An identity with no registry yet has exactly one implicit, active device: its own KeyAuth pubkey. The registry materialises the first time Attest succeeds, or the first time that implicit device revokes itself, which writes a one-entry registry marked revoked. Snapshot restore is the one path that writes a registry with no attestation at all, and it is operator-only.