5.2 Identity bridges — resolve: chains
A bridge links a canonical Person/Organization to an external identity for any person/org,
not just the connecting user, via an ordered rule chain (first match wins), resolved lazily
at query time for the anchors a query actually binds, and persisted (writeThrough) so it is
reused — re-resolved only after refreshAfter.
| rule | how it resolves |
|---|---|
existingBridge |
A fresh bridge already linked to the anchor — use it, stop. |
learnedHandle: { property, as } |
An explicit handle stored on the anchor (e.g. Person.githubLogin). No lookup. |
canonicalIdentity: { producer } |
The anchor’s spine keys — emails for a Person, domains for an Organization, whatever a realm-declared spine is keyed by (§5.17) → call the producer. |
canonicalEmail: { producer } |
Alias of canonicalIdentity, for an email-keyed anchor. |
canonicalDomain: { producer } |
Alias of canonicalIdentity, for a domain-keyed anchor. |
The two aliases are one function: what a canonical rule keys on is decided by the anchor’s spine and
by nothing else, so writing canonicalEmail on an Organization resolves by domain regardless. The
names are kept because realms are written with them, and a name its anchor contradicts is reported
when the realm loads — but canonicalIdentity is the one to write. A kind outside this table is
refused at validate/install; nothing will ever answer it.
Resolution is batched (one producer call for many anchors) and negatively cached (an anchor
that resolved to nothing is not re-queried until refreshAfter), so a recurring “who that I email
is on GitHub” doesn’t re-storm the source. A fetch that failed is never negatively cached — “could
not ask” must not become “asked, nothing there” and then stay that way past a fixed token. The
negative cache is recorded on the bridge store, so it rides writeThrough: a join that declares
writeThrough: false has nowhere to record a miss and re-asks every query.

