Audit Sink Configuration#
Flipt's audit subsystem routes mutation events (flag created, token deleted, etc.) to one or more sinks. Everything β which sinks are active, how events are buffered, and which event types are emitted β is controlled by the audit block in Flipt's configuration file, modelled by AuditConfig.
Configuration Model#
internal/config/audit.go defines the full config schema:
| Field | Purpose |
|---|---|
sinks.log | Write audit events to stdout or a file (LogSinkConfig) |
sinks.webhook | POST events to a URL or templated destinations (WebhookSinkConfig) |
sinks.kafka | Publish events to Kafka (KafkaSinkConfig) |
buffer.capacity | Batch size for the span exporter (2β10, default 2) |
buffer.flush_period | How often the batch is flushed (2β5 min, default 2 min) |
events | Which noun:verb pairs to emit (default ["*:*"]) |
All three sinks default to enabled: false . Kafka defaults to protobuf encoding .
Enabled() check β AuditConfig.Enabled() returns true if any of the three sinks has its Enabled flag set. This is the single gate used throughout initialization.
Validation#
AuditConfig.validate() runs at startup and enforces:
- Webhook (when enabled): exactly one of
urlortemplatesmust be provided β not both, not neither . - Kafka (when enabled):
topicand at least onebootstrap_serversentry are required . - Buffer capacity: must be between 2 and 10 .
- Buffer flush_period: must be between 2 minutes and 5 minutes .
The log sink has no additional validation beyond the Enabled flag.
Initialization in grpc.go#
Audit sinks are wired up in internal/cmd/grpc.go during gRPC server construction:
1. Event checker setup
A NoOpChecker is the default (rejects all events). If cfg.Audit.Enabled() is true, audit.NewChecker(cfg.Audit.Events) is called instead, expanding the configured noun:verb patterns into a concrete allow-set.
2. Sink construction
Each sink type is instantiated only if its Enabled flag is set:
- Log sink β
log.NewSinkis called with optionalWithPathandWithEncodingoptions. If no encoding is specified, the globalcfg.Log.Encodingis inherited . - Webhook sink β If
URLis non-empty, a basic webhook sink is created; ifTemplatesis non-empty, a template sink is used instead . The max backoff defaults to 15 s if not configured . - Kafka sink β
kafka.NewSinkis called with the fullKafkaSinkConfig.
3. Middleware and exporter wiring
If the sinks slice is non-empty:
AuditEventUnaryInterceptoris added to the gRPC interceptor chain, carrying the checker.- A
SinkSpanExporteris created and registered with the OTel tracing provider via a batch span processor, usingbuffer.flush_periodandbuffer.capacityfrom config . - A shutdown hook is registered to drain and close the exporter .
Event Filtering: EventPairChecker#
internal/server/audit/checker.go implements the filtering interface:
Checkerβ built fromcfg.Audit.Events, expands wildcard patterns at construction time. Valid nouns:constraint,distribution,flag,namespace,rollout,rule,segment,token,variant(or*for all). Valid verbs:created,deleted,updated(or*for all). Duplicate pairs and unrecognised tokens are rejected with an error .NoOpCheckerβ always returnsfalse; used when audit is disabled .
The AuditEventUnaryInterceptor defers event emission until after the handler returns, then calls eventPairChecker.Check("noun:verb") for each collected event. Only matching events are added to the OTel span , where the SinkSpanExporter later picks them up and dispatches them to every registered sink .
Key Source Files#
| File | Role |
|---|---|
internal/config/audit.go | Config structs, defaults, and validation |
internal/cmd/grpc.go | Sink construction and wiring |
internal/server/audit/checker.go | Event pair filtering logic |
internal/server/audit/audit.go | Sink interface and SinkSpanExporter |
internal/server/audit/log/log.go | Log sink implementation |
internal/server/middleware/grpc/middleware.go | AuditEventUnaryInterceptor |