Go SDK · Behaviors worth knowing
Empty Action is a no-op
Events with no Action set are dropped at send time. Useful when you want a default-audit handler that conditionally opts out — set Action only when something audit-worthy happens.
func readSecret(v *vault, rec recorder.Recorder) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
e := event.FromContext(r.Context())
defer func() { _ = rec.Record(r.Context(), e) }()
s, err := v.get(r.PathValue("id"))
if err != nil {
http.NotFound(w, r)
return // Action never set → event silently dropped
}
e.Action = "secret.read"
e.Target = event.Target{Type: "secret", ID: s.ID}
writeJSON(w, http.StatusOK, s)
}
}
Record is non-blocking
Record enqueues the event on the in-memory buffer and returns immediately — the background goroutine handles the HTTP call on the next flush. Don't await audit acks in critical paths, and don't wrap Record in your own goroutine "for safety" (you'll lose context propagation and gain nothing).
start := time.Now()
_ = rec.Record(ctx, e)
fmt.Println(time.Since(start)) // microseconds — no network round-trip
Caller-set fields take precedence
The middleware auto-populates Result from the response status. If you set Result yourself, that wins — useful for anti-enumeration handlers that return the same HTTP status for different audit outcomes.
func revealSecret(v *vault, rec recorder.Recorder) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
me, _ := actorFromRequest(r)
id := r.PathValue("id")
e := event.FromContext(r.Context())
e.Action = "secret.reveal"
e.Target = event.Target{Type: "secret", ID: id}
defer func() { _ = rec.Record(r.Context(), e) }()
s, err := v.get(id)
if err != nil {
http.NotFound(w, r) // genuinely doesn't exist
return // middleware records Result{Status: "error", Code: 404}
}
if s.TenantID != me.TenantID {
// Return 404 instead of 403 so attackers can't probe for
// secret IDs in other tenants. The user-facing response
// can't tell the two cases apart — but the audit log should.
e.Result = event.Result{Status: "denied", Code: 403, Message: "cross-tenant access"}
http.NotFound(w, r)
return
}
writeJSON(w, http.StatusOK, map[string]string{"value": s.Value})
}
}
Context cancellation is respected
Record short-circuits on a cancelled context — no enqueue attempt, no allocation. The buffered recorder also bails on cancellation when blocked waiting for buffer space under PolicyBlock.
ctx, cancel := context.WithCancel(context.Background())
cancel()
err := rec.Record(ctx, e)
// err == context.Canceled; e was never enqueued.