CHAM Policies
CHAM stands for Configurable, Hot-swappable, Auditable, Measurable. CHAM policies are the configurable rules layer that sits between Sentinel's hardcoded SGP principles and the raw action stream from agents. They let you tailor governance behavior to your organization's specific needs without modifying the core engine.
CHAM Properties
| Property | Meaning |
|---|---|
| Configurable | Policies are defined as JSON configuration. No code changes required. |
| Hot-swappable | Policies can be created, modified, or deactivated at runtime without restarting the governance engine. Changes take effect on the next evaluation cycle. |
| Auditable | Every policy change is recorded in the audit trail. Every policy that fires during an evaluation is listed in the policies_fired response field. |
| Measurable | Every policy tracks how many times it has been evaluated, how many times it has fired, and its impact on verdict distribution. |
Policy Scope
Policies can be applied at two levels:
| Scope | Applies To | Precedence |
|---|---|---|
| Tenant-wide | All agents in the tenant | Lower |
| Agent-scoped | A specific agent only | Higher (overrides tenant-wide) |
When both a tenant-wide and agent-scoped policy of the same type exist, the agent-scoped policy takes precedence for that agent. Other agents continue using the tenant-wide policy.
Policy Types
confidence_floor
Sets minimum confidence scores per dimension. If an agent's reported confidence is below the floor, the action's tier is escalated.
{
"type": "confidence_floor",
"name": "Production confidence requirements",
"scope": "tenant",
"config": {
"environment": "production",
"floors": {
"incident": 0.80,
"fix": 0.85,
"containment": 0.90
},
"escalation": "tier_up"
}
}| Field | Description |
|---|---|
floors.incident | Minimum confidence in situation understanding |
floors.fix | Minimum confidence that the proposed action will work |
floors.containment | Minimum confidence that no collateral damage will occur |
escalation | What happens when a floor is breached: tier_up (A becomes B, B becomes C) or block (immediate BLOCKED) |
TIP
Set higher confidence floors for production environments and lower ones for staging. This lets agents operate more freely in non-production while enforcing rigor where it matters.
environment_restriction
Controls which tiers are permitted in specific environments.
{
"type": "environment_restriction",
"name": "No autonomous actions in production",
"scope": "tenant",
"config": {
"environment": "production",
"max_tier": "B",
"action_types": ["code_deploy", "config_change", "data_migration"],
"reason": "All production changes require human approval"
}
}| Field | Description |
|---|---|
environment | Target environment (e.g., production, staging, development) |
max_tier | Maximum allowed tier. Actions classified above this are escalated. B means Tier A actions are escalated to B. |
action_types | Optional. If specified, restriction only applies to these action types. If omitted, applies to all. |
reason | Human-readable explanation included in verdict reasoning. |
action_type_block
Explicitly blocks specific action types, regardless of tier or confidence.
{
"type": "action_type_block",
"name": "Block database drops",
"scope": "tenant",
"config": {
"action_types": ["database_drop", "table_truncate", "schema_delete"],
"environments": ["production", "staging"],
"verdict": "BLOCKED",
"tier": "C",
"reason": "Destructive database operations are prohibited via CHAM policy"
}
}| Field | Description |
|---|---|
action_types | List of action types to block |
environments | Optional. If specified, block only applies in these environments. If omitted, applies everywhere. |
verdict | Always BLOCKED for this policy type |
tier | The tier to assign (C for policy violation, X for SGP-level severity) |
rate_limit
Controls the maximum number of governed actions per time window.
{
"type": "rate_limit",
"name": "Agent burst protection",
"scope": "agent",
"agent_id": "agt_abc123",
"config": {
"windows": [
{ "period": "1s", "max": 10 },
{ "period": "1m", "max": 60 },
{ "period": "5m", "max": 300 }
],
"on_exceed": "block",
"reason": "Rate limit exceeded -- agent is submitting too many actions"
}
}| Field | Description |
|---|---|
windows | Array of time windows with maximum action counts |
windows[].period | Time window duration (1s, 1m, 5m, 1h, 1d) |
windows[].max | Maximum actions allowed in this window |
on_exceed | What happens when limit is exceeded: block (BLOCKED verdict) or hold (HELD verdict for review) |
Rate limits are enforced via Redis sliding window counters. Each agent has independent counters.
require_reasoning
Requires agents to provide reasoning with their actions.
{
"type": "require_reasoning",
"name": "Reasoning required for all actions",
"scope": "tenant",
"config": {
"min_length": 20,
"required_fields": ["reasoning"],
"environments": ["production", "staging"],
"on_missing": "block",
"reason": "All governed actions must include reasoning for audit purposes"
}
}| Field | Description |
|---|---|
min_length | Minimum character length for the reasoning field |
required_fields | List of fields that must be present in the action payload |
on_missing | block (BLOCKED verdict) or hold (HELD for human to add reasoning) |
grant_lifecycle
Defines pre-approved grants for specific action types, allowing agents to bypass full evaluation for known-good operations.
{
"type": "grant_lifecycle",
"name": "Pre-approved log rotation",
"scope": "agent",
"agent_id": "agt_ops_bot",
"config": {
"action_type": "log_rotate",
"environments": ["production", "staging"],
"max_uses": 100,
"expires_at": "2026-05-01T00:00:00Z",
"tier_override": "A",
"conditions": {
"max_file_size_mb": 500,
"allowed_targets": ["app-logs", "access-logs"]
}
}
}| Field | Description |
|---|---|
action_type | The action type this grant applies to |
max_uses | Maximum number of times this grant can be used before expiring |
expires_at | ISO 8601 timestamp when the grant expires |
tier_override | The tier to assign to granted actions (typically A for auto-approve) |
conditions | Optional constraints that must be met for the grant to apply |
WARNING
Grants are powerful -- they can bypass Sentinel evaluation for specific action types. Use them sparingly and always with expiration dates. Every grant usage is still recorded in the audit trail.
compliance_pack
Industry-specific rule sets that add regulatory compliance checks.
{
"type": "compliance_pack",
"name": "HIPAA compliance pack",
"scope": "tenant",
"config": {
"standard": "HIPAA",
"rules": [
{
"id": "hipaa_phi_access",
"description": "PHI access requires Tier B minimum",
"action_types": ["data_read", "data_export", "data_query"],
"data_classifications": ["PHI", "ePHI"],
"min_tier": "B"
},
{
"id": "hipaa_phi_modification",
"description": "PHI modification requires Tier C",
"action_types": ["data_write", "data_delete", "data_update"],
"data_classifications": ["PHI", "ePHI"],
"min_tier": "C"
},
{
"id": "hipaa_audit_retention",
"description": "Audit records for PHI actions retained 6 years",
"retention_days": 2190
}
]
}
}| Field | Description |
|---|---|
standard | The compliance standard (HIPAA, SOC2, PCI-DSS, GDPR, etc.) |
rules | Array of compliance rules to enforce |
rules[].data_classifications | Data types that trigger the rule |
rules[].min_tier | Minimum tier for matching actions |
rules[].retention_days | Audit record retention period override |
time_window
Restricts actions to approved change windows.
{
"type": "time_window",
"name": "Production change window",
"scope": "tenant",
"config": {
"environment": "production",
"windows": [
{
"days": ["tuesday", "thursday"],
"start": "02:00",
"end": "06:00",
"timezone": "America/New_York"
}
],
"outside_window": "hold",
"emergency_override": true,
"reason": "Production changes only during approved maintenance windows"
}
}| Field | Description |
|---|---|
windows | Array of approved time windows |
windows[].days | Days of the week when the window is open |
windows[].start / end | Start and end times in HH:MM format |
windows[].timezone | IANA timezone identifier |
outside_window | What happens outside the window: hold (HELD for review) or block (BLOCKED) |
emergency_override | If true, actions marked as emergency: true bypass the window restriction |
Policy Lifecycle
Creating a Policy
POST /policies
Content-Type: application/json
{
"type": "confidence_floor",
"name": "Staging confidence floors",
"scope": "tenant",
"config": { ... }
}Updating a Policy
Policies are updated in place. The previous version is recorded in the audit trail.
PUT /policies/:policy_id
Content-Type: application/json
{
"config": { ... }
}Deactivating a Policy
Policies are soft-deleted (deactivated), never hard-deleted, to preserve audit history.
DELETE /policies/:policy_idThis sets the policy's active flag to false. It will no longer be loaded during evaluation but remains in the database for audit purposes.
Conflict Detection
When multiple CHAM policies apply to the same action, conflicts can occur. TheWARDN includes five conflict detectors:
| Detector | What It Catches |
|---|---|
| Tier Conflict | Two policies assign different tiers to the same action type |
| Window Overlap | Two time_window policies have overlapping schedules with different rules |
| Grant vs. Block | A grant_lifecycle policy approves an action that an action_type_block policy blocks |
| Confidence Conflict | Two confidence_floor policies set different floors for the same dimension |
| Scope Ambiguity | Agent-scoped and tenant-wide policies of the same type have contradictory effects |
When a conflict is detected, the system follows a resolution order:
- More restrictive wins -- if two policies disagree, the more restrictive one takes effect.
- Agent-scoped over tenant-wide -- agent-specific policies take precedence.
- Newer over older -- if two policies at the same scope and restriction level conflict, the most recently created one wins.
TIP
Use the POST /policies/test endpoint to simulate how a new policy would interact with existing policies before activating it. This runs the conflict detectors and shows you any issues.
Policy Metrics
Every policy tracks its own performance metrics:
| Metric | Description |
|---|---|
evaluation_count | How many times this policy was loaded for evaluation |
fire_count | How many times this policy actually changed a verdict |
fire_rate | fire_count / evaluation_count |
last_fired_at | Timestamp of the last time this policy fired |
verdict_impact | Breakdown of how many CLEARED, HELD, and BLOCKED verdicts this policy produced |
These metrics enable you to measure the real-world impact of your policies and identify policies that are too broad (firing too often), too narrow (never firing), or misconfigured.