Skip to content

data_classification.rego

Cross-cutting access control based on a data_classification label attached to an event. Four tiers, with default-deny outside the explicitly allowed combinations.

This is not a separate event type — data_classification is a field on every GovernanceEvent, so the same policy gates a tool_call that carries confidential data, an agent.delegate that would forward restricted data, etc. See governance events for the full event shape.

Package: kitelogik.data_classification · Source: kitelogik/policies/data_classification.rego

The four tiers

TierAllowed whenDenied when
null (no label)Always
"public"Always
"internal"Any session
"confidential"delegation_depth == 0 (primary session)delegation_depth > 0
"restricted"Session has restricted_data scopeScope missing

Anything outside these labels falls through to the default allow := false — treat unknown classifications as deny.

Source

rego
package kitelogik.data_classification

import future.keywords.if
import future.keywords.in

default allow := false
default deny := false

allow if { input.data_classification == null }
allow if { input.data_classification == "public" }
allow if { input.data_classification == "internal" }

allow if {
    input.data_classification == "confidential"
    input.context.delegation_depth == 0
}

allow if {
    input.data_classification == "restricted"
    "restricted_data" in input.context.session_scopes
}

deny if {
    input.data_classification == "confidential"
    input.context.delegation_depth > 0
}

deny if {
    input.data_classification == "restricted"
    not "restricted_data" in input.context.session_scopes
}

How main.rego uses this module

Both allow and deny propagate, regardless of event type:

rego
allow if { not deny; data_classification.allow }
deny if { data_classification.deny }

This is intentional — data classification flow control should apply everywhere data moves, not be tied to a specific event type.

Setting data_classification on an event

The runtime (or an adapter) attaches the classification when it knows the data tier. For example, a tool that returns confidential data:

python
from kitelogik.tether.models import GovernanceEvent, ToolCallInput

event = GovernanceEvent(
    event_type="tool_call",
    session_id=context.session_id,
    action="get_customer_pii",
    tool_name="get_customer_pii",
    args={"customer_id": "c1"},
    context=context,
    data_classification="confidential",       # ← key field
)

decision = await gate.evaluate(event)

For most tools this label is set declaratively next to the tool definition — your adapter can attach it automatically based on the tool's known data tier rather than at the call site.

Common patterns

Block confidential data from reaching a third-party tool. Combine this module with a custom tool-call rule:

rego
package kitelogik.financial    # any package — denies merge in main.rego

import future.keywords.if

deny if {
    input.tool_name in {"send_to_external_api", "post_to_webhook"}
    input.data_classification in {"confidential", "restricted"}
}

Strict tenant isolation for restricted data.

rego
package kitelogik.data_classification

import future.keywords.if

deny if {
    input.data_classification == "restricted"
    input.context.tenant_id != input.args.target_tenant_id
}

Extending the tier set

The OSS module ships four tiers. To add a fifth (e.g. "highly_sensitive"), add same-package allow + deny rules:

rego
package kitelogik.data_classification

import future.keywords.if
import future.keywords.in

allow if {
    input.data_classification == "highly_sensitive"
    input.context.user_role == "compliance_officer"
    "highly_sensitive" in input.context.session_scopes
}

deny if {
    input.data_classification == "highly_sensitive"
    not (input.context.user_role == "compliance_officer"
         and "highly_sensitive" in input.context.session_scopes)
}

Both rules are necessary — Rego's default allow := false doesn't imply default deny := true. Without the explicit deny, an unhandled classification would just produce no decision (resolves to allow=False, deny=False, which surfaces as a soft deny / HITL candidate in main.rego).

Released under the Apache 2.0 License.