ADR-014: Application Roles and Marketplace Seat Assignments Are Separate Records¶
Status: Proposed
Deciders: @grokify
Relates to: ADR-007 (memberships and entitlements as claims; this ADR
amends its decision 8), ADR-008 (resource-scoped collaborators).
Updates: docs/design/architecture/organizations.md (seat accounting)
and docs/design/architecture/authorization.md (application roles). Both
already describe the behavior below; they are not changed by this ADR.
Context¶
ADR-007 decision 8 says that application roles are stored in sf_app_roles,
"which replaces seat assignments as the record of who may use what", and
that a seat-licensed application counts its rows against the license. The
initiative's TRD went further and planned for SeatAssignment to become a
view derived from sf_app_roles, kept for one release and then removed.
The implementation does the second half of the sentence and not the first:
- Seat limits are enforced on application roles. The seat limit of an
application is the sum of the seats of its valid
seat_basedlicenses;approlecountssf_app_rolesrows for (organization, application) against it and refuses withErrNoSeats. There is no limit when any entitlement for the application is not seat-limited. - Seat assignments were not replaced.
sf_seat_assignments, the marketplace seat endpoints,License.used_seats,CheckPrincipaland the SpiceDBseat_holderrelation (license#use) all remain, and none of them is derived from application roles. Assigning a role creates no seat assignment, and assigning a seat grants no role.
Replacing one record with the other would not be a rename, because they describe different things:
| Application role | Seat assignment | |
|---|---|---|
| Keyed by | (principal, organization, application) | (license, principal) |
| Answers | What may this person do inside the application? | Does this person hold a seat of this license? |
| Needs an application | Yes, a registered AppVocabulary |
No: a listing need not name an application |
| Carries a level | Yes, a role from the vocabulary | No |
| Counted by | approle, per application |
used_seats, per license |
A listing that names no application (a template, a dataset, a service sold without an in-product role) has seats and no application roles. An application entitled through several licenses, a subscription and a default has one set of roles and several license records.
Leaving the ADR as written means a reader looking for "who may use application X" finds two answers, and the architecture pages already say that the marketplace's seat assignments remain a separate record while ADR-007 says they were replaced.
Decision¶
- Application roles are the record of who may act inside an
application. Applications decide access from the
orgsclaim'sappsmap and never from seat assignments. This confirms ADR-007's decision 1 and the first clause of decision 8. - Seat assignments remain the marketplace's record of who holds a seat
of a particular license. They are not replaced, not derived from
application roles and not required for application access. ADR-007
decision 8 is amended:
sf_app_rolesdoes not replace seat assignments. - Seat limits for an application are enforced on application roles, as implemented: a principal holding a role in a seat-licensed application consumes one seat for that application, whether or not a seat assignment exists. Owners' implicit roles hold no seat.
- The two counters are different numbers.
License.used_seatscounts seat assignments of one license. The number of seats an application is using is the count of its application roles (approle.Service.ListForOrganization(org, application)). Interfaces that show seat usage for an application must use the second, and the marketplace response is not a substitute for it. - A listing that names an application is entitled through the marketplace, and its access is granted by application role. For such a listing the seat endpoints are legacy. They keep working, so existing integrations are not broken, and they do not grant application access. For a listing with no application, seat assignments are the only per-person record and keep their present behavior.
Consequences¶
- ADR-007's text is wrong on one point and this ADR corrects it, so the record matches the architecture pages and the code. ADR-007's status line gains "decision 8 amended by ADR-014" when this ADR is accepted.
- No code changes. The behavior this ADR records is the behavior of the code; the TRD's derived-view plan is withdrawn.
- A deployment can show a principal with a role and no seat assignment, or the reverse, and both are valid states. Support and billing tooling should not treat a mismatch as an error.
- Two ways to give someone a seat remain for listings that name an application. This is the cost of keeping the marketplace's licensing record intact, and it is accepted until decision 5's open question is settled.
Alternatives considered¶
- Derive seat assignments from application roles, as the TRD planned. Rejected: it cannot cover a listing with no application, loses the per-license attribution that a customer with several licenses of one product needs, and breaks the marketplace seat API for no gain in access control.
- Make application roles require a seat assignment. Rejected: it makes the licensing record an access gate, so changing a license changes access invisibly, and it forces a seat per person even for applications the deployment grants to everyone.
- Remove seat assignments and keep only application roles. Rejected
for the same reason as deriving them; it also drops the
seat_holderrelation that the relationship-based provider reads for licenseuse.
Open questions¶
- Whether the seat endpoints should refuse, or be marked deprecated in the API schema, for a listing that names an application, so there is one way to give a person a seat of such an application.
- Whether a seat-based license that names an application should report
used_seatsfrom application roles instead of seat assignments, which would make decision 4's two numbers one for that case.