FGA Contract

April 8, 2026 · View on GitHub

This document is the authoritative reference for all messages the committee service sends to the fga-sync service, which writes and deletes OpenFGA relationship tuples to enforce access control.

The full OpenFGA type definitions (relations, schema) for all object types are defined in the platform model.

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


Object Types


Message Format

All messages use the generic FGA message format on the following NATS subjects:

SubjectUsed for
lfx.fga-sync.update_accessCreate and update operations
lfx.fga-sync.delete_accessDelete operations
lfx.fga-sync.member_putAdd or update individual committee members
lfx.fga-sync.member_removeRemove individual committee members

Each message carries object_type, operation, and a data map. The sections below describe the data contents for each operation.


Committee

Source struct: internal/domain/model/Committee (base + settings)

Synced on: create, update of committee base, update of committee settings, delete of a committee. Committee member changes are synced separately via member_put.

update_access

Published to lfx.fga-sync.update_access on committee create or update (base or settings).

Message Envelope

FieldValue
object_typecommittee
operationupdate_access

Data Fields

These fields are carried inside the message data object.

FieldValue
uidCommitteeBase.UID
publicCommitteeBase.Public (passed through directly)

Relations

RelationValueCondition
writerUsernames from CommitteeSettings.WritersOnly when Writers is non-empty
auditorUsernames from CommitteeSettings.AuditorsOnly when Auditors is non-empty

Usernames are the Username field of each CommitteeUser entry (Auth0 sub values). Users with an empty Username are skipped.

References

ReferenceValueCondition
projectCommitteeBase.ProjectUIDAlways

Exclude Relations

exclude_relations: ["member"] — always set. Individual committee members are managed via member_put and must not be overwritten by the update_access handler.

member_put (Committee Member Create/Update)

Published to lfx.fga-sync.member_put when a committee member is created or updated and the member has a non-empty Username.

The object UID is the committee UID (CommitteeBase.UID), not the member UID.

Message Envelope

FieldValue
object_typecommittee
operationmember_put

Data (FGAMemberData)

FieldValueCondition
uidCommitteeMember.CommitteeUID (parent committee)Always
usernameCommitteeMember.Username (Auth0 sub)Always (skipped if Username is empty)
relations["member"]Always

member_remove (Committee Member Delete)

Published to lfx.fga-sync.member_remove when a committee member is deleted and the member has a non-empty Username. Sends an empty relations array, which instructs fga-sync to remove all tuples for that user on the committee object.

Message Envelope

FieldValue
object_typecommittee
operationmember_remove

Data (FGAMemberData)

FieldValueCondition
uidCommitteeMember.CommitteeUID (parent committee)Always
usernameCommitteeMember.Username (Auth0 sub)Always (skipped if Username is empty)
relations[] (empty — remove all)Always

Delete

On delete, a delete_access message is sent to lfx.fga-sync.delete_access with only the committee uid — all FGA tuples for committee:{uid} are removed by the fga-sync service.


Triggers

OperationObject TypeSubjectNotes
Create committeecommitteelfx.fga-sync.update_accessAlways sent
Update committee basecommitteelfx.fga-sync.update_accessAlways sent
Update committee settingscommitteelfx.fga-sync.update_accessAlways sent
Delete committeecommitteelfx.fga-sync.delete_accessAlways sent
Create committee member (with username)committeelfx.fga-sync.member_putSkipped if Username is empty
Update committee member (with username)committeelfx.fga-sync.member_putSkipped if Username is empty
Delete committee member (with username)committeelfx.fga-sync.member_removeSkipped if Username is empty; empty relations removes all tuples for the user