Indexer Contract

May 31, 2026 · View on GitHub

This document is the authoritative reference for all data the member service sends to the indexer service, which makes resources searchable via the query service.

Update this document in the same PR as any change to indexer message construction.


Resource Types


B2B Org

Object type: b2b_org

NATS subject: lfx.index.b2b_org

Source struct: internal/domain/model/b2b_org.goB2BOrg

Indexed on: create, update, delete of a B2B org.

Data Schema

FieldTypeDescription
uidstringB2B org unique identifier
namestringOrganization display name
descriptionstring (optional)Free-text description
phonestring (optional)Contact phone number
websitestring (optional)Website URL
primary_domainstring (optional)Canonical primary domain
domain_aliases[]string (optional)Additional normalized domains
logo_urlstring (optional)Logo image URL
industrystring (optional)Industry classification
sectorstring (optional)Sector classification
crunch_base_urlstring (optional)CrunchBase profile URL
number_of_employeesint64 (optional)Employee count
statusstring (optional)LF membership status
is_memberboolWhether the org is an active LF member
parent_uidstring (optional)UID of the parent org
parent_detailobject (optional)Denormalized parent info: uid, name, logo_url
is_parentbool (optional)true when this org has at least one direct member-eligible child. Omitted when false. To retrieve children, query the index for parent_uid = <this uid>.
created_attimestampCreation time (RFC3339)
updated_attimestampLast update time (RFC3339)

Tags

Tag FormatExamplePurpose
{uid}cbef1ed5-...Direct lookup by UID
b2b_org_uid:{uid}b2b_org_uid:cbef1ed5-...Find orgs by UID
parent_b2b_org_uid:{uid}parent_b2b_org_uid:abc-...Find all children of a parent org
is_member:{true|false}is_member:trueFilter by LF member status

parent_b2b_org_uid tag is only emitted when parent_uid is non-empty.

Access Control (IndexingConfig)

FieldValue
access_check_objectb2b_org:{uid}
access_check_relationauditor
history_check_objectb2b_org:{uid}
history_check_relationauditor

Search Behavior

FieldValue
fulltextname, primary_domain, description, industry, sector
name_and_aliasesname, primary_domain, all domain_aliases
sort_namename (lowercased)
publicfalse

Parent References

RefCondition
b2b_org:{parent_uid}Only when parent_uid is set

Project Membership

Object type: project_membership

NATS subject: lfx.index.project_membership

Source struct: internal/domain/model/membership.goProjectMembership

Indexed on: create via /admin/reindex (memberships are Salesforce-managed).

Data Schema

FieldTypeDescription
uidstringMembership unique identifier
tier_uidstringUID of the associated membership tier
project_uidstringv2 UUID of the project
project_slugstring (optional)URL slug of the project
b2b_org_uidstring (optional)UUID of the member company
statusstringMembership status, e.g. Active
yearstring (optional)Membership year, e.g. 2025
tierstring (optional)Tier label, e.g. Gold
auto_renewboolWhether automatic renewal is enabled
renewal_typestring (optional)Renewal cadence
pricefloat64 (optional)Current membership price
annual_full_pricefloat64 (optional)Full annual list price
payment_frequencystring (optional)Payment frequency
payment_termsstring (optional)Payment terms
agreement_datestring (optional)Date the membership agreement was signed
purchase_datestring (optional)Effective purchase date
start_datestring (optional)Membership start date
end_datestring (optional)Membership end date
company_namestringMember company name
company_logo_urlstring (optional)Member company logo URL
company_domainstring (optional)Member company website/domain
tier_namestring (optional)Product name, e.g. Gold Corporate Membership
tier_familystring (optional)Product family, e.g. Membership
tier_product_typestring (optional)Product type
created_attimestampCreation time (RFC3339)
updated_attimestampLast update time (RFC3339)

Tags

Tag FormatExamplePurpose
{uid}cbef1ed5-...Direct lookup by UID
project_membership_uid:{uid}project_membership_uid:cbef1ed5-...Find memberships by UID
project_uid:{uid}project_uid:abc-...Find all memberships for a project
b2b_org_uid:{uid}b2b_org_uid:def-...Find all memberships for an org

Access Control (IndexingConfig)

FieldValue
access_check_objectproject_membership:{uid}
access_check_relationauditor
history_check_objectproject_membership:{uid}
history_check_relationauditor

Search Behavior

FieldValue
fulltextcompany_name, tier_name, status, year
name_and_aliasescompany_name, company_domain
sort_namecompany_name (lowercased)
publicfalse

Parent References

RefCondition
b2b_org:{b2b_org_uid}Only when b2b_org_uid is set
project:{project_uid}Only when project_uid is set

Key Contact

Object type: key_contact

NATS subject: lfx.index.key_contact

Source struct: internal/domain/model/key_contact.goKeyContact

Indexed on: create, update, delete via /project_memberships/{uid}/key_contacts and by /admin/reindex.

Data Schema

FieldTypeDescription
uidstringKey contact unique identifier
membership_uidstringUID of the associated project membership
tier_uidstringUID of the associated membership tier
project_uidstringv2 UUID of the project
project_namestring (optional)Display name of the project — also indexed in fulltext for keyword search
project_logo_urlstring (optional)Logo image URL for the project
b2b_org_uidstring (optional)UUID of the member company
rolestringContact role, e.g. Voting Representative
statusstringRole record status, e.g. Active
board_memberboolWhether this contact holds a board member role
primary_contactboolWhether this is the primary contact for the membership
first_namestringContact's first name
last_namestringContact's last name
titlestring (optional)Contact's job title
emailstring (optional)Primary email address
usernamestring (optional)Resolved OIDC sub
emails[]string (optional)Full list of email addresses
company_namestringMember company name
company_logo_urlstring (optional)Member company logo URL
company_domainstring (optional)Member company website/domain
created_attimestampCreation time (RFC3339)
updated_attimestampLast update time (RFC3339)

Tags

Tag FormatExamplePurpose
{uid}cbef1ed5-...Direct lookup by UID
key_contact_uid:{uid}key_contact_uid:cbef1ed5-...Find contacts by UID
project_membership_uid:{uid}project_membership_uid:abc-...Find all contacts for a membership
project_uid:{uid}project_uid:def-...Find all contacts for a project
b2b_org_uid:{uid}b2b_org_uid:ghi-...Find all contacts for an org
role:{value}role:Voting RepresentativeFilter contacts by role
status:{value}status:ActiveFilter contacts by status

role and status tags are only emitted when non-empty.

Access Control (IndexingConfig)

FieldValue
access_check_objectproject_membership:{membership_uid}
access_check_relationauditor
history_check_objectproject_membership:{membership_uid}
history_check_relationauditor

Search Behavior

FieldValue
fulltextfirst_name, last_name, email, role, company_name, project_name
name_and_aliasesFull name, email
sort_namelast_name first_name (lowercased)
publicfalse
contacts[{lfx_principal: uid, name: full_name, emails: [...]}]

Parent References

RefCondition
b2b_org:{b2b_org_uid}Only when b2b_org_uid is set
project:{project_uid}Only when project_uid is set
project_membership:{membership_uid}Only when membership_uid is set

B2B Org Settings

Object type: b2b_org_settings

NATS subject: lfx.index.b2b_org_settings

Source struct: internal/service/messaging.gob2bOrgSettingsIndexerView (adapter view; canonical state is model.B2BOrgSettings in the org-settings KV bucket)

Trigger: PUT /b2b_orgs/{uid}/settings (online path) or POST /admin/reindex with types=["b2b_org_settings"] (backfill). The doc exists only when writers/auditors have been explicitly configured via PUT.

Action mapping: ActionCreated on first write (when no prior KV record exists); ActionUpdated on all subsequent writes.

Note: ObjectID equals the parent org UID (not a separate settings UID) so a single point-lookup retrieves both the org doc and the settings doc. Callers filter by object_type=b2b_org_settings.

Payload Fields

Flat members[] array — role is a first-class field on each entry. Both accepted and pending entries are included; revoked and expired are excluded.

FieldExample value
uidcbef1ed5-...
members[{username, email, name, role, invite_status, updated_at}]
created_at2026-01-15T10:00:00Z
updated_at2026-05-20T14:30:00Z

Per-member entry shape:

FieldExample valueNotes
usernameauth0|<lfid>Absent for pending invites
emailuser@example.orgAlways present
nameDisplay NameOptional
rolewriter"writer" or "auditor"; writer takes precedence if user holds both
invite_statusacceptedaccepted, pending
updated_at2026-01-15T10:00:00ZLast modification to this membership row

Tags

TagCondition
has_writers≥1 writer with invite_status=accepted
has_auditors≥1 auditor with invite_status=accepted
has_pending_invites≥1 entry (writer or auditor) with invite_status=pending
writer:{username}One tag per accepted writer with a non-empty LFID username
auditor:{username}One tag per accepted auditor with a non-empty LFID username
member:{username}One tag per accepted user — role-agnostic union, deduped across writer+auditor

Revoked and expired entries do not trigger any tag — they are audit-trail data, not actionable state. Pending users (no username) are included in members[] but produce no writer:, auditor:, or member: tags.

Query Patterns

"Which orgs is user X a member of?" (role-agnostic)

GET /query/resources?v=1&type=b2b_org_settings&tags=member:auth0|{username}

Returns one doc per org where the user is an accepted writer or auditor. Each doc contains data.uid (the org UID) and the full data.members[] array. The member: tag covers both roles so a single call suffices.

"Which orgs is user X a writer on?" (role-specific)

GET /query/resources?v=1&type=b2b_org_settings&tags=writer:auth0|{username}

"Which orgs is user X an auditor on?" (role-specific)

GET /query/resources?v=1&type=b2b_org_settings&tags=auditor:auth0|{username}

"How many orgs does user X belong to?"

GET /query/resources/count?type=b2b_org_settings&tags=member:auth0|{username}

Returns {"count": N, "has_more": false}. has_more is true when the result exceeds the aggregation bucket limit.

"Who has access to org Y?"

GET /query/resources?v=1&type=b2b_org_settings&object_id={org_uid}

Returns the single settings doc for that org with the full members[] roster.

All queries are enforced by an FGA auditor check on b2b_org:{uid} — only the calling user's accessible orgs are returned regardless of filter. Tag values containing : or | must be URL-encoded in query strings: :%3A, |%7C.

Access / History Check

FieldValue
access_check_objectb2b_org:{uid} (parent, not self — settings has no separate FGA type)
access_check_relationauditor
history_check_objectb2b_org:{uid}
history_check_relationwriter (history = write-side concern; matches project-service precedent)

Name and Aliases / Fulltext

FieldContents
name_and_aliases[org.Name, org.PrimaryDomain, ...org.DomainAliases] — domain typeahead works even with no writers configured
fulltextAccepted writers/auditors: Name + Email. Pending entries: Email + Name-if-present. Revoked/expired excluded.
sort_namelower(org.Name)

Parent References

RefCondition
b2b_org:{uid}Always (self-ref for point-lookup)
b2b_org:{parent_uid}Only when parent_uid is set

NATS RPC Endpoints

Generic SFID↔UUID Lookup

Two entity-agnostic request/reply endpoints for translating between Salesforce IDs and v2 UUIDs. Pure CPU — no Salesforce call, no NATS KV. Covers all entity types (b2b_org, project_membership, key_contact, membership_tier).

FieldValue
Subject (SFID→UUID)lfx.member.sfid-to-uuid.lookup
Subject (UUID→SFID)lfx.member.uuid-to-sfid.lookup
TransportNATS core request/reply

SFID→UUID request: {"sfid":"<15 or 18-char Salesforce ID>"} SFID→UUID response — success: {"uuid":"<uuid v8>"}

UUID→SFID request: {"uuid":"<uuid v8>"} UUID→SFID response — success: {"sfid":"<15-char Salesforce ID>"}

Response — error: {"error":"<human-readable message>"}

The reply is always valid JSON. Callers should check for the "error" key to detect failure. 15-char SFIDs are normalised to 18 characters internally before translation; both forms are accepted.