Skip to content

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 DeviceCertificate signed 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:

security find-generic-password -s com.hive.identity.<accountID>

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:

  1. Inviter publishes their STUN candidates to the rendezvous relay, signed with their device key.
  2. Recipient pastes the invite, looks up the inviter's candidates via the relay (verifies signature), opens a UDP link.
  3. Both sides exchange a signed PeerLinkHello over the link. The hello carries the workspace UUID + a fresh nonce; both sides sign (workspaceID || nonce) with their device key.
  4. 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.