Documentation
¶
Overview ¶
Package audit records every mutating admin request against the control plane. Flipping a value here changes the behaviour of every consuming microservice within milliseconds, so "who changed what, when" has to survive the request that made the change — the KV bucket only ever holds the current value.
An audit write is best effort by design: it is recorded after the handler has already committed, and a failure is logged rather than propagated. Losing the record of a change is bad; refusing a legitimate production change because the audit table is unavailable is worse.
Index ¶
Constants ¶
const MaxStoredBody = 3800
MaxStoredBody bounds the recorded body. It is a byte budget, while REQUEST_BODY's VARCHAR(4000) is measured in characters, so the budget is deliberately conservative: a body of multi-byte characters is truncated earlier than the column requires, never later. That is the safe direction to be wrong in — this insert is on the request path, and a body the column rejects is a failed INSERT rather than a shortened row. The headroom covers the truncation marker.
Variables ¶
This section is empty.
Functions ¶
func Redact ¶
Redact prepares a request body for storage: secret-looking values are masked and the result is bounded. A body that is not JSON is dropped entirely rather than stored blind — the API only accepts JSON, so anything else is either a probe or a mistake and neither is worth persisting verbatim.
func WithEnvironments ¶
WithEnvironments narrows what the request may read to the given environment ids. The admin API resolves the reader's credential and applies its scope here, so a request that arrives unnarrowed reads the whole trail — which is what a full-scope token, and a deployment with auth disabled, is entitled to.
Types ¶
type Entry ¶
type Entry struct {
ID int64 `json:"id"`
OccurredAt time.Time `json:"occurredAt"`
Actor string `json:"actor"`
Method string `json:"method"`
Path string `json:"path"`
Domain string `json:"domain,omitempty"`
TargetID string `json:"targetId,omitempty"`
EnvironmentID int64 `json:"environmentId,omitempty"`
StatusCode int `json:"statusCode"`
RemoteAddr string `json:"remoteAddr,omitempty"`
RequestBody string `json:"requestBody,omitempty"`
}
Entry is one recorded mutation. Domain/TargetID/EnvironmentID are best-effort — the middleware fills them from whatever the route makes knowable, and a request that never reached a route (404, 401) carries only the envelope.
type Filter ¶
type Filter struct {
Actor string
From time.Time
To time.Time
Environments []int64
Limit int
Offset int
}
Filter narrows the audit listing. A zero From/To is an open end of the range.
Environments is the reader's own scope rather than a query parameter: a nil slice is full scope, and any other value restricts the listing to rows recorded against those environments. A row whose environment is unknown — the 404 and 401 envelopes, and the writes that belong to no environment — is outside every narrowed scope, because its body is the one thing about it that is not already known.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler serves the audit log.
GET /audit?actor=&from=&to=&limit=&offset=
It follows the list-endpoint conventions of the domain handlers: ParsePage for the page, a flat JSON array, and no internal detail in error bodies. The rows carry request bodies from every environment, so the reader's scope (WithEnvironments) narrows the listing the same way it narrows a write.
func NewHandler ¶
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store reads and writes CONFIG_AUDIT_LOG. Unlike the domain repositories there is a single implementation: the two statements differ only in bind syntax and how a timestamp is bound, so a dialect flag is cheaper than a second file that would have to be kept in sync by hand.