7. Hooks and audit
Overview
Section titled “Overview”Hooks use OpenFeature’s hook model, with four stages: before, after, error, and finally. There are two kinds:
- Evaluation hooks run around every resolution (telemetry, logging, caching, metrics such as “how often do we deny
seats.max?”). - Mutation hooks run around every administrative change. This is where the audit trail lives: every plan-value, assignment, override, and grant mutation emits
{actor, reason, before, after, at}.
7.1. Evaluation hooks
Section titled “7.1. Evaluation hooks”Requirement 7.1.1
Section titled “Requirement 7.1.1”An evaluation hook MAY implement
before(hookContext),after(hookContext, details),error(hookContext, error), andfinally(hookContext, details?). The hook context MUST provide the privilege key, the privilege’s value type, the feature code (forisEnabled), the merged evaluation context, and the provider metadata.
Requirement 7.1.2
Section titled “Requirement 7.1.2”Hooks MUST be registrable on the client.
beforehooks MUST run in registration order.after,error, andfinallyhooks MUST run in reverse registration order.
Requirement 7.1.3
Section titled “Requirement 7.1.3”
afterMUST run when resolution returns a reason other thanERROR.errorMUST run when resolution returns reasonERROR, or when abeforehook throws.finallyMUST run unconditionally.
Requirement 7.1.4
Section titled “Requirement 7.1.4”A failed limit check is a successful evaluation. Hooks for the underlying resolution MUST run
after(noterror) beforeEntitlementLimitExceededis thrown.
Requirement 7.1.5
Section titled “Requirement 7.1.5”If a
beforehook throws, the SDK MUST skip resolution and return the default with reasonERRORand codeGENERAL. If anafter,error, orfinallyhook throws, the SDK MUST NOT change the evaluation result, and SHOULD log the error.
7.2. Mutation hooks
Section titled “7.2. Mutation hooks”Requirement 7.2.1
Section titled “Requirement 7.2.1”A mutation hook MAY implement
before(mutationContext),after(mutationContext, auditEvent),error(mutationContext, error), andfinally(mutationContext). The mutation context MUST provide the operation name, its arguments,actor, andreason.
Requirement 7.2.2
Section titled “Requirement 7.2.2”If a mutation hook’s
beforethrows, the mutation MUST NOT be applied. The SDK MUST runerrorandfinallyand rethrow. This lets a policy hook veto a change (for example, “overrides above 1000 seats need approval”).
Requirement 7.2.3
Section titled “Requirement 7.2.3”
afterMUST run exactly once per successful mutation, with its audit event.errorMUST run when the provider rejects the mutation.finallyMUST run unconditionally.
Requirement 7.2.4
Section titled “Requirement 7.2.4”A mutation hook’s
afterthat throws MUST NOT undo a mutation that has already committed. Durable audit is the provider’s job (Requirement 6.3.5). Hooks are for forwarding (to a SIEM, Slack, or a data warehouse).
7.3. Audit event
Section titled “7.3. Audit event”Requirement 7.3.1
Section titled “Requirement 7.3.1”An audit event MUST contain:
field meaning actorwho made the change (a user id, a service name, stripe-webhook)reasonwhy (free text, required) entityone of plan,planValue,assignment,override,grantentityIdthe id of the entity (for planValue:planId:privilegeKey, forassignment: the organization id)actionone of create,update,delete,revokebeforethe entity’s state before the change, or nulloncreateafterthe entity’s state after the change, or nullondeleteatwhen the change took effect (from the client’s clock)
beforeandafterMUST be JSON-serializable per Requirement 1.2.3.
Requirement 7.3.2
Section titled “Requirement 7.3.2”Setting a plan value that has never been set MUST produce
action: createwithbefore: null. Changing an existing value MUST produceaction: updatewith bothbeforeandafter.
Requirement 7.3.3
Section titled “Requirement 7.3.3”Assigning a plan to an organization that has none MUST produce
action: create. Changing an existing assignment MUST produceaction: update. Removing it MUST produceaction: delete.