Identity & devices¶
Hive distinguishes three layers of identity:
| Layer | Stable? | Where it lives |
|---|---|---|
| Account | yes | ~/Library/Application Support/Hive/identity.json |
| Device | yes per device | identity.json (public) + Keychain (private Ed25519) |
| Workspace member | per workspace | .hive/.../events.ndjson as memberAdded events |
Account¶
An HumanAccount represents you-the-person across machines. It
carries a display name, a handle (@you), and an Ed25519 account
public key. The account key is the root of trust for every device
you authorize.
You get one account per Hive install on first launch. The
generation is silent — Hive reads $USER/hostname for defaults you
can edit in onboarding.
Device¶
A DeviceIdentity represents one machine. It has:
- A device-specific Ed25519 keypair (private in Keychain, public in the workspace device roster).
- A
DeviceCertificatesigned by the account key, asserting "this device belongs to this account."
Every signed envelope carries the signerDeviceID. Receiving peers
look up the device's public key in the workspace device roster and
verify.
Workspace membership¶
When you join a workspace, an memberAdded event lands in the log
with your ActorIdentity (id + display name + kind). Subsequent
events from your devices verify against the rosters.
What travels in invites¶
A WorkspaceInvitePayload (the thing you paste into a peer's Hive
to grant access) contains:
- The workspace UUID + display name.
- The inviter's
accountID+deviceID+displayName. - The inviter's device Ed25519 public key (the recipient uses it to verify the first envelopes from the workspace).
- Optional relay endpoint + token env var hint.
- Optional expiry timestamp and passcode.
What it does not contain:
- Any private key material.
- The inviter's API credentials.
- Workspace content.
The recipient adds the inviter as a member, fetches the workspace
roster, and starts syncing. Their device key joins the device roster
through their own deviceAdded event.
Keychain layout¶
Hive stores its private keys under generic-password items keyed by
service com.hive.identity.<accountID>. Inspect with:
File-backed identity (dev builds)¶
Debug builds set HIVE_IDENTITY_STORE=file so the private key lives
on disk under ~/Library/Application Support/Hive/identity-keys/
instead of Keychain. Saves the Keychain modal loop when iterating
in development. Release dists always use Keychain.
Trust handshake (cross-network)¶
sequenceDiagram
autonumber
participant A as Alice (inviter)
participant R as Relay
participant B as Bob (recipient)
A->>R: publish signed RendezvousRecord
A->>B: invite payload (any channel)
B->>R: lookup workspace+device
R-->>B: signed publication
B-->>B: verify sig against invite's pubkey
B->>A: UDP hole-punch — PeerLinkHello (signed nonce)
A->>B: PeerLinkHelloAck (signed nonce)
Note over A,B: each side proved possession of its device key
A-->A: emit memberAdded event for Bob
B-->B: emit deviceAdded event for self
When two devices on different networks join the same workspace:
- Inviter publishes their STUN candidates to the rendezvous relay, signed with their device key.
- Recipient pastes the invite, looks up the inviter's candidates via the relay (verifies signature), opens a UDP link.
- Both sides exchange a signed
PeerLinkHelloover the link. The hello carries the workspace UUID + a fresh nonce; both sides sign(workspaceID || nonce)with their device key. - Each side verifies the other's hello signature against the public key the rendezvous publication / invite carried.
No central party brokers trust. The relay only routes signed publications it can't forge.