Persistence (Governance Evidence Sinks)
Kernite is dependency-free and intentionally stateless. It does not persist logs or write to your database. In production, persist governance evidence in your application layer (or an adjacent service). Capture the decision contract fromevaluate_execute(...) or POST /execute and write it to a durable sink (DB, object storage, log pipeline, etc.).
Why persist governance evidence
Persisting governance evidence enables:- Auditability: reproduce what was evaluated and why for any write attempt.
- Security posture reporting: quantify what was blocked (deny events) and why.
- Operational debugging: reason codes and trace hashes reduce time-to-resolution.
- Analytics: top deny reasons, noisy principals, hot operations, and policy regressions.
Persist both approved and denied decisions
Do not store only approvals. Store:- approved decisions (writes that were allowed)
- denied decisions (writes that were blocked)
- application outcomes for request errors (for example invalid request or transport failure)
The decision contract (what to store)
Kernite decision values areapproved or denied.
If you track error, treat it as an application-level outcome, not a Kernite decision enum.
Recommended minimal record
Store these fields in your sink:created_at(server timestamp)workspace_idprincipal_idobject_typeoperationdecision(approved|denied)outcome(approved|denied|error) optional, app-levelreason_codes[]ctx_idtrace_hashidempotency_keypolicy_version(if available)latency_ms(if measured)
request_json(redacted/sanitized)response_json(full decision response; may be large)
response_json. If you need analytics, store indexable columns plus optional JSON.
Transaction semantics: where persistence should happen
A robust write path is:- Evaluate:
evaluate_execute(...)(orPOST /execute) - Persist governance evidence (approved/denied/error outcome)
- If
decision == "approved", perform the actual mutation - Return
{ok, data, governance}
Strong pattern: same transaction for evidence and mutation
If your app uses a DB transaction, write both governance evidence and domain mutation in the same transaction. This prevents missing evidence for successful writes.Failure policy: fail-closed vs fail-open
Decide what happens if your sink is unavailable.Fail-closed (strict audit/compliance)
If sink write fails, deny the operation. This guarantees evidence exists for any allowed mutation.Fail-open (availability first) + outbox
If availability matters more, allow mutation even if sink write fails, then retry evidence persistence via outbox table or queue.Patterns by sink type
Database sink (recommended for analytics)
Pros:- easy querying (top deny reasons, per-principal rates, per-operation breakdown)
- optional joins with domain data
- schema and retention planning required
- index
workspace_id,principal_id,decision,created_at - unique key
(workspace_id, idempotency_key)for retry dedupe
Object storage sink (S3/GCS/Azure Blob)
Pros:- cheap and durable
- good for long-term retention
- querying needs secondary system (Athena/BigQuery/etc.) or ETL
- JSONL partitioned by date, for example
s3://bucket/kernite/year=YYYY/month=MM/day=DD/*.jsonl
Log pipeline sink (OpenTelemetry/Datadog/Cloud Logging)
Pros:- immediate dashboards and alerts
- easy anomaly detection (deny spikes)
- retention and full-fidelity JSON may be expensive
- emit metric counters for
decisionandreason_codes - emit structured logs including
trace_hashandctx_id
Redaction and sensitive data
Governance evidence may contain sensitive data (PII, secrets, tokens). If you storerequest_json or full response_json, apply redaction.
- remove or hash emails, phone numbers, addresses
- remove tokens, credentials, and secrets
- prefer IDs/references over raw payload values
- store indexable fields plus
reason_codes - store a redacted subset of request/response JSON
Example: minimal persistence in an application write path
Replacepersist_evidence(...) with your sink implementation.