8gent Code.Docs
Device Kit (8DK), Preview

Pairing

A 6-digit code on the device and in the prompt, the person picks what to allow, and nothing is allowed by default.

A device is paired only when a person says yes, and a paired device can do nothing until the person allows a capability.

What happens

  1. The device sends its manifest (a device:hello frame). The vessel validates it. An invalid manifest ends pairing here.
  2. The vessel picks a random 6-digit code and sends it to the device to display (showCode, a device:pairing frame).
  3. The vessel asks the person (consent) with the device's id, name and kind, the code, and every capability with its kind, description and whether it asks every time.
  4. The person checks the code on the device matches the one in the prompt, and ticks the capabilities to allow.
  5. On a yes, the vessel issues a device token and stores the pairing. On a no, a timeout or an error, nothing is stored.
const result = await registry.pair(manifest, {
  showCode: (code) => send({ type: "device:pairing", code }),
  consent: async (req) => askThePerson(req), // returns { approved, grant? }
  consentTimeoutMs: 120_000,                 // the default: 2 minutes
});
// { ok: true, deviceId, token } or { ok: false, reason }. In v1 it can also
// throw if showCode throws or grant is not an array; nothing is stored.

askThePerson and send stand for your own prompt and transport. In 8gent they will be the consent prompt and the /device route, both slice 2.

Nothing allowed by default

grant is the list of capabilities the person allows. Leave it out and the device pairs with none: no tools, no calls. A grant that names a capability the device does not have fails the whole pairing.

A prompt nobody answers is a no. So is a prompt that throws.

Devices with no screen

In v1 showCode is optional. A device that cannot display the code leaves the person nothing to compare, and the prompt still asks. Slice 2 (#3363) defines a fallback for devices with no screen, so the check does not silently disappear.

What the code proves, and what it does not

The code is not protection against interception

The matching code tells the person that the device in front of them is the one talking to the vessel on this connection. It does not protect against someone who can sit between the device and the vessel and relay frames both ways: they could pass the code along. 8DK v1 does not claim resistance to that kind of interception. The connection itself has to be trusted end to end, for example an encrypted (wss) connection to your own vessel.

The device token

On a yes, pair returns a token: 32 random bytes as 64 hex characters. The vessel sends it to the device once (device:paired). The registry keeps only its SHA-256, never the token, and compares in constant time.

When the device connects again it sends device:auth with its id and token:

registry.authenticate(deviceId, token, manifest); // false if the token is wrong or the manifest changed

Always pass the manifest the device sends now. Without it a changed manifest is not noticed. In v1 a failed check does not unpair the device; the caller has to refuse the connection.

A device whose manifest changed (a new capability, a reworded description) fails authentication when the manifest is passed, and has to pair again to get anything new. Tools always come from the manifest the person approved, so a new capability can never be called without a new pairing.

Pairing again

Pairing a device id that is already paired replaces the old pairing: a new token, and only the capabilities allowed in the new prompt. The old token stops working. In v1 the prompt does not say that it replaces an existing device, so any device can claim a known id and name. Slice 2 adds a 'replaces' notice to the prompt and limits pairing attempts.

In memory only

v1 keeps pairings in memory. They are gone when the process exits. Keeping pairings and grants across restarts, with the token stored only as a hash, is part of slice 2 (#3363).

Next: Grants, revoke and unpair.