Metadata-Version: 2.4
Name: kernite
Version: 0.1.6
Summary: Contract-first policy decision CLI for deterministic decision + reason + trace.
Project-URL: Homepage, https://github.com/kerniteio/kernite
Project-URL: Documentation, https://github.com/kerniteio/kernite
Project-URL: Repository, https://github.com/kerniteio/kernite
Author: Sanka
License: Apache-2.0
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# Kernite

<!-- BEGIN SYNC:docs/index.mdx -->
<!-- Auto-generated from docs/index.mdx by scripts/sync_readme_from_docs_index.py. Do not edit this block manually. -->

Kernite is a policy engine to make your APIs LLM-ready.

It scans API schema, generates initial policy, and returns a reproducible decision contract with AI-operable reasons.

Unlike other general policy engines such as Casbin/OPA/Cedar, Kernite is purpose-built for making APIs policy-enforceable and ready for programmatic remediation with AI agents.

## Why Kernite

- Developer-friendly: scan API schema, generates policy, and apply them to endpoints in 60 seconds. 
- Reason by Design: decision reasons are structured for programmatic remediation, not only human logs.
- Contract-First: `decision`, `reason_codes`, `reasons`, `trace_hash` are required integration surfaces.
- Reproducible tracing: canonical input and policy context produce deterministic `trace_hash` output.

## Design Philosophy
- Zero overhead: no runtime dependencies (`dependencies = []`).
- Zero DSL: enforce your policy without learning a new language.
- Zero bypass: every write-mutation gets checked by `kernite check`.

## When should I use this?

Use Kernite if:
- Your API surface is large and you want policy coverage visibility from OpenAPI.
- You want machine-operable deny reasons for automated remediation and retry.
- You need deterministic evidence (`trace_hash`) that you can persist and replay.

Use Casbin/OPA/Cedar if:
- You want a mature policy language ecosystem with centralized governance tooling.
- You already have an established policy pipeline and need a dedicated PDP.

## Comparisons with other policy engines

| Topic | Kernite | Casbin/OPA/Cedar |
| --- | --- | --- |
| Primary goal | Policy-gate API for AI operations and remediations (fix-and-retry with `reason_codes`). | Human-authored policy enforcement and policy-language tooling. |
| Decision contract | Required structured contract (`decision`, `reason_codes`, `reasons`, `trace_hash`). | Explanations exist, but the remediation contract is not standardized across integrations (codes/reasons/evidence vary by setup). |
| Reproducible evidence | Built-in deterministic evidence surface (`trace_hash`) plus conformance vectors for replay. | Strong policy engines; replay determinism depends on integration and policy/data pipeline discipline. |

While Casbin/OPA/Cedar/others are strong options for centralized policy-language workflows, **Kernite is purpose-built for app-embedded, machine-operable, write-path enforcement with a stable response contract.**

## Non-goals

- A general-purpose policy language / DSL runtime.
- Proxying or routing traffic (Kernite returns decisions; your app performs the write).
- Replacing your domain logic. Kernite decides and explains; your app enforces.


## Install and Scan First

```bash
uv tool install kernite
```

Start from your API project repo and run a strict coverage scan before wiring enforcement.

Example project used here:

- `https://github.com/fastapi/full-stack-fastapi-template`

```bash
git clone https://github.com/fastapi/full-stack-fastapi-template.git
cd full-stack-fastapi-template/backend
uv sync
```

Export OpenAPI JSON from the app code:

```bash
mkdir -p .kernite
uv run python -c 'import json; from pathlib import Path; from app.main import app; Path(".kernite/openapi.json").write_text(json.dumps(app.openapi(), indent=2), encoding="utf-8")'
```

Run strict scan and write a machine-readable report:

```bash
kernite check \
  --schema .kernite/openapi.json \
  --report-out .kernite/kernite-check.strict.json || true
```

Inspect what needs to be addressed:

```bash
jq '.summary' .kernite/kernite-check.strict.json
jq '.violations[:10] | map({code, method, path, field})' .kernite/kernite-check.strict.json
```

From a real run on the template above (2026-02-23), strict scan reported:

- `write_operations = 17`
- `violations = 34`
- top remediation codes: `missing_x_kernite`, `missing_policy_key`

This is the intended first checkpoint for large API surfaces: identify uncovered write operations before you enforce.

## OpenAPI Policy Generate and Check

Input format for v1 is OpenAPI **JSON only**.
YAML files are intentionally rejected with conversion guidance to keep the core runtime dependency-free.

Generate bundle + mapping + report:

```bash
kernite policy generate \
  --schema ./openapi.json \
  --out-dir ./.kernite
```

Generated artifacts:

- `policy-bundle.generated.json`
- `policy-map.generated.json`
- `policy-generation-report.json`

Check coverage (strict by default):

```bash
kernite check \
  --schema ./openapi.json \
  --mapping ./.kernite/policy-map.generated.json \
  --bundle ./.kernite/policy-bundle.generated.json \
  --report-out ./kernite-check-report.json
```

Exit codes:

- `0` valid coverage
- `1` coverage violations (strict)
- `2` input/parse errors

## Library Quick Start (Python)

```python
from kernite import evaluate_execute

request = {
    "workspace_id": "workspace-demo",
    "principal": {"type": "token", "id": "api:ops-bot"},
    "object_type": "document",
    "operation": "create",
    "payload": {"title": "Q1 Plan"},
}
result = evaluate_execute(request, idempotency_key="req-001")

decision = result["data"]["decision"]
trace_hash = result["data"]["trace_hash"]  # persist with your write/audit record
reason_codes = result["data"]["reason_codes"]
```

## Embedded Runtime Modes (Enforce / Observe / Skip)

Use `evaluate_execute_controlled(...)` when you want staged rollout and built-in sink emission.

```python
from kernite import ObservabilityConfig, evaluate_execute_controlled

config = ObservabilityConfig.from_env()
sink = config.create_sink()

result = evaluate_execute_controlled(
    request,
    idempotency_key="req-001",
    mode=config.mode,
    sink=sink,
    sink_failure_policy=config.sink_failure_policy,
)

if result["allow_write"]:
    # proceed with mutation
    pass
```

Environment defaults:

- `KERNITE_MODE=enforce|observe|skip` (default `enforce`)
- `KERNITE_SINK=none|jsonl|csv|sqlite` (default `none`)
- `KERNITE_SINK_PATH=/path/to/file_or_db` (required when sink enabled)
- `KERNITE_SINK_FAIL_POLICY=fail_open|fail_closed` (default `fail_open`)

## Production Notes (OSS Reference Server)

- Authentication/authorization: the OSS reference server does not include built-in authn/authz. Run it behind your trusted boundary (for example mTLS, JWT verification, internal network policy, or API gateway auth).
- Request size/timeouts: the OSS reference server does not define built-in max body size or per-request timeout controls. Enforce size limits and timeouts at ingress/runtime (reverse proxy, gateway, or process supervisor).
- Logging/metrics: the OSS reference server is minimal by design (startup log only, request access logs suppressed by default). Add structured logs/metrics in your service wrapper or edge layer, and persist `ctx_id`/`trace_hash` from responses for audit evidence.

## Quick Start: Execute

Request (governed scope with one policy, missing required `title`):

In v1, an `allow` policy is approved only if all its rules pass; any rule failure produces a `denied` decision with structured reasons.

```json
{
  "workspace_id": "workspace-demo",
  "principal": {
    "type": "token",
    "id": "api:ops-bot"
  },
  "object_type": "document",
  "operation": "create",
  "payload": {},
  "policy_context": {
    "governed": true,
    "selected_policies": [
      {
        "policy_key": "document_create_default",
        "policy_version": 1,
        "effect": "allow",
        "rules": [
          {
            "rule_key": "require_title",
            "rule_definition": {
              "type": "required_fields",
              "fields": ["title"]
            },
            "reason_code": "missing_required_fields",
            "reason_message": "title is required."
          }
        ]
      }
    ]
  }
}
```

Response shape:

```json
{
  "ctx_id": "ctx_...",
  "message": "Denied by governance policy.",
  "data": {
    "decision": "denied",
    "reason_codes": ["missing_required_fields"],
    "reasons": [
      {
        "code": "missing_required_fields",
        "message": "title is required.",
        "rule_key": "require_title",
        "field_path": "payload.title",
        "details": {
          "missing_fields": ["title"]
        }
      }
    ],
    "policy_selection_reason_code": "policy_selected_workspace_default",
    "policy": {
      "policy_key": "document_create_default",
      "policy_version": 1
    },
    "trace_hash": "sha256:...",
    "idempotency_key": "..."
  }
}
```

Happy-path response example (approved):

```json
{
  "ctx_id": "ctx_...",
  "message": "Approved by governance policy.",
  "data": {
    "decision": "approved",
    "reason_codes": [],
    "reasons": [],
    "policy_selection_reason_code": "policy_selected_workspace_default",
    "policy": {
      "policy_key": "document_create_default",
      "policy_version": 1
    },
    "trace_hash": "sha256:...",
    "idempotency_key": "..."
  }
}
```

Contract invariants (v1):

- Required response fields: `ctx_id`, `message`, `data`, `data.decision`, `data.reason_codes`, `data.reasons`, `data.policy_selection_reason_code`, `data.policy`, `data.trace_hash`, `data.idempotency_key`.
- `data.decision` enum values are only `approved` or `denied`.
- `message` is human-readable (best-effort); integrations should branch on `data.*` fields, not message text.
- `data.reason_codes` and `data.reasons` may be empty arrays when `data.decision` is `approved`.
- When `data.decision` is `denied`, at least one `data.reason_codes` entry is present.
- `data.trace_hash` is stable across v1.x for the same canonicalized input and policy context.
- Canonicalization rule: Kernite deterministically canonicalizes all hash-participating arrays (for example sorted `reason_codes` and canonical policy-match entries used for hashing). This makes order-only differences in `selected_policies`/`rules` not change `data.trace_hash`.

## Policy Context Model

`policy_context` is optional, but recommended for production integrations.
Kernite evaluates the policies you provide in `policy_context.selected_policies`; in OSS mode it does not fetch policies from storage.

Main fields:

- `governed` (bool): whether this request must be enforced as governed scope.
- `selected_policies` (array): policies selected by your resolver.
- `governed_scopes` (array): optional scope list (`object_type` + `operation`) to infer governed status.
- `policy_selection_reason_code` (string): explicit selection reason, if already known.

Default behavior:

- governed + no selected policy => `denied` with `no_matching_policy`
- not governed + no selected policy => `approved` with `out_of_scope_phase1`

## PARC Request Model

Kernite uses a Cedar-style PARC shape:

- principal
- action (`operation`)
- resource
- context

This keeps policy evaluation explicit and stable for relationship operations like `associate`.

See `docs/concepts/parc-model.mdx` for details and examples.

## Use Cases (AI and Non-AI)

- AI-assisted actions: gate tool calls and use `reason_codes` for automatic retry/remediation.
- Internal APIs: apply one deterministic write guard across UI/API/workers.
- SaaS multi-tenant systems: enforce tenant-scoped write decisions and persist evidence.

See `docs/use-cases.md` for details and examples.

## Compatibility and Conformance

- Contract policy: `docs/concepts/compatibility.mdx`
- Conformance vectors: `docs/conformance/v1/execute_vectors.json`
- Reason code semantics: `docs/conformance/v1/reason_codes_v1.json`

## Objective Performance Check (Python)

Kernite includes a dependency-free benchmark harness.

```bash
uv run python benchmarks/benchmark_execute.py --iterations 20000
```

This gives p50/p95 latency and throughput from your actual environment so language/runtime decisions are based on measured data.

Latest measured snapshot is tracked in `docs/concepts/performance.mdx`.
<!-- END SYNC:docs/index.mdx -->
