Identities & Approvals
How Honeycomb decides which real person a handle belongs to — why email is the only identity that grants access, how usernames link automatically, and the 'is this email you?' approval that stops a namesake from inheriting someone's private memory.
Access in Honeycomb is granted to people, not to raw handles. A single human shows up as many identifiers — an email address, a Slack ID, a GitHub login, a display name — and Honeycomb resolves those to one canonical person so a grant made to [email protected] also works when Priya appears by her Slack handle. This page explains how that resolution stays safe: which handles can confer access, which only improve graph quality, and the confirmation step that keeps one person's private Spaces from ever landing in a namesake's view.
At a glance
- Email is the only identity that grants access. A person's read view is resolved from a verified email. Usernames, Slack IDs, and display names help build the knowledge graph but never, on their own, unlock anyone's private Spaces.
- Usernames link automatically; emails need proof. Linking
scotty595to "Scott" is low-risk — it can't grant access — so it happens automatically. Attaching a new email to an existing person is access-bearing, so it isn't done silently. - A namesake can't inherit your memory. When ingest wants to attach an email to a pre-existing person by a weak signal (a matching name), Honeycomb does not link it. It records a proposal that only the real owner of that identity can confirm.
- Approvals go to the owner, not the claimant. The "is this email you?" prompt appears for the already-verified person whose identity is the target — so someone inviting a look-alike can't approve their own link.
- One address, one owner. An email can be the access-bearing identity for at most one person, and only the first confirmation wins.
Why identity is an access-control decision
Every grant — every share, every membership, every private thread — is keyed to a canonical person, not to a handle. When someone reads, Honeycomb resolves their verified email to that canonical person and returns exactly the Spaces that person can see (Spaces & permissions).
That makes identity resolution part of the security boundary. If the wrong handle resolved to a person who holds grants, the holder of that handle would inherit those grants' access. So Honeycomb draws a hard line by handle type:
| Handle | Example | Links people in the graph? | Can unlock private Spaces? |
|---|---|---|---|
[email protected] | Yes | Yes — the access-bearing identity | |
| Login / username | scotty595, a GitHub login | Yes | No |
| Slack / platform ID | U04F… | Yes | No |
| Display name | "Scott" | Yes (for graph quality) | No |
Usernames, IDs, and names are used freely to stitch the graph together — to merge five fragments of the same person into one node — because getting one wrong only affects graph tidiness, never access. Email is different: an email is what a read is resolved from, so attaching one is the moment access can change hands.
How an email becomes an access-bearing identity
An email is registered as a person's access-bearing identity only when it genuinely is their identity — never as a side effect of a name match:
- A person is created from that email — the person is the address (a brand-new contact who emailed in). No ambiguity, no approval needed.
- The same address already belongs to that person, seen again from another source. Re-attesting a known email is safe.
- The person's owner explicitly confirms it — the approval flow below.
Any other path — a new email that lines up with an existing person only because a name or a look-alike local-part matched — is treated as unproven. Honeycomb records it as a pending proposal and grants no access until it's confirmed.
Note: This is why merging a username to a person is instant but attaching a second email isn't. The username can't grant access; the email can.
The "is this email you?" approval
When ingest encounters an email that would attach to a pre-existing person by a weak signal, it creates a pending identity link instead of wiring it up. A pending link is inert: it confers no access, appears in no one else's view, and never resolves a read — it's just a question waiting for an answer.
The prompt — "Is [email protected] you?" — is shown to the person who already owns the target identity, in their Lua inbox. That direction is the whole safeguard: an attacker who invites a look-alike account can't see or approve the link, because they aren't the owner of the identity it targets.
- Confirm promotes the email to that person's access-bearing identity. From then on it resolves their view like any verified address.
- Reject tombstones the proposal. The same email→person link is never raised again by ingestion, so a rejected suggestion won't nag on every re-sync.
Guarantees
- Owner-only. A proposal is visible and actionable only by the person whose identity it targets. No one else — not an admin acting broadly, not the account that introduced the email — can approve it.
- First confirmation wins. If two people could plausibly claim the same address, the first to confirm owns it; a later attempt to move it is refused, so an address can never be re-homed off the person who confirmed it.
- Atomic. Confirmation promotes the email and closes the proposal in one step — there's no in-between state where an email is live but the request still looks open.
API
Identity approvals are surfaced in the Lua desktop inbox, and are also available directly. All three are scoped to the acting user — the engine only ever returns or acts on proposals whose target identity is the caller:
| Endpoint | Method | What it does |
|---|---|---|
/api/v1/identity/pending-links | GET | List email→identity proposals awaiting my confirmation |
/api/v1/identity/pending-links/:id/confirm | POST | Confirm — the email becomes my access-bearing identity |
/api/v1/identity/pending-links/:id/reject | POST | Reject — tombstone the proposal so ingest never re-raises it |
curl -s "$HONEYCOMB_URL/api/v1/identity/pending-links" \
-H "Authorization: Bearer hck_..." \
-H "x-acting-user: [email protected]"{
"links": [
{
"id": "pil_7c21",
"email": "[email protected]",
"proposedPersonName": "Stefan Kruger",
"via": "display_name",
"source": "gmail",
"createdAt": "2026-07-10T14:00:00.000Z"
}
]
}Acting as a verified user
Reads are scoped to the acting user's identity (see acting on behalf of users). Honeycomb accepts that identity two ways, in order of trust:
- A verified assertion. Platforms integrated with Lua pass a short-lived, cryptographically signed identity assertion. It's verified against the issuer's keys on every request and always takes precedence — it can't be forged or replayed.
- The
x-acting-userheader, honored only on a key that carries the act-as-user capability. It's the migration path; the verified assertion is the destination.
Workspaces can require the verified path: once assertions are confirmed flowing, a workspace can be switched so the plain header is ignored for it entirely, leaving only the cryptographic identity to resolve a read. There's no downgrade to the spoofable header. Until a workspace opts in, both paths work and a divergence between them is logged as a signal to watch.
Note: With no resolvable acting user — no assertion, and no header on a capable key — a read returns org-wide content only. It never falls back to full-workspace visibility.
Where to next
- Spaces & permissions — what a resolved identity can actually see, and how "who can see this" is surfaced.
- Security & data isolation — the wider trust model, including the emergency lockdown.
- Ingesting data — the audience and participant metadata that seed identity resolution.
Spaces, Sharing & Permissions
How Honeycomb decides who sees what — Spaces, the golden rule of visibility, private-by-default ingest, sharing and revoking access, promotion, and acting on behalf of users.
Security & Data Isolation
How Honeycomb keeps your organization's memory isolated, scoped, and under your control: workspace isolation, Spaces and grants, sensitivity ranking, API keys, and the data lifecycle.