Suvra

Policy Model

Suvra uses a deterministic two-tier policy model: one required global policy plus an optional bound agent policy. Every active decision is a pure function of the loaded rules and the action's identity-aware context.

Two-tier model

Global policy: SuvraPolicy

The global policy (internal id suvra-policy, displayed as SuvraPolicy) applies to every agent. It carries the organization-wide rules every action is evaluated against.

Agent policy (optional)

A single optional agent policy can be bound to an agent via agents.policy_id. When both tiers match an action, the agent policy overrides the global policy.

Legacy scopes — tenant, business unit, domain, workspace, environment — remain in audit metadata and params_hash normalization for backward compatibility, but they are not active policy scopes in the runtime. Only global + agent policies are evaluated.

Deny-by-default

If no rule matches, the decision is always deny. There is no mode field, no allow_all, no flag that changes this. Deny-by-default is a hard guarantee — it is not configurable.

Decisions

DecisionBehavior
allowAction is permitted. The executor runs immediately.
denyAction is blocked. No side effect. Denial is audited.
needs_approvalAction is paused until a human approves via dashboard or API.

Precedence

After rule matching, Suvra resolves the winning decision deterministically:

  1. Global policy rules are evaluated in listed order
  2. Agent policy rules are evaluated in listed order
  3. Within a policy, the last matching rule wins
  4. Agent-policy matches override global-policy matches
  5. If no rule matches, the final decision is deny

Every policy-driven response — validate, execute, simulate — includes a structured decision_trace alongside compatibility fields (matched_rule_id, matched_policy_id, reasons, checks).

Rule schema

A policy is an ordered list of rule references:

policy_id: suvra-policy
rules:
  - { rule_id: fs.write_reports_allow, enabled: true }
  - { rule_id: http.external_request_needs_approval, enabled: true }
  - { rule_id: fs.delete_production_deny, enabled: true }

Each library rule has:

  • id — stable identifier referenced from policies
  • type — the action type it applies to (fs.write_file, http.request, shell.exec, email.delete, secrets.read)
  • effectallow | deny | needs_approval
  • constraints — optional constraint map
  • description — human-readable string

At evaluation time Suvra hydrates each reference from the Rules Library into a fully materialized rule.

Constraints

Filesystem

  • path_prefix
  • max_bytes
  • working_dir_prefix

HTTP

  • method
  • allow_domains
  • timeout_seconds
  • host_in, host_prefix

Identity / request context

  • agent, user, role, workspace, environment
  • labels — every listed label must be present (deterministic normalization: trim, dedupe, sort)
  • tenant_id, business_unit, domain — retained for compatibility-sensitive matching

Command / provider

  • allow_commands
  • allow_providers (case-insensitive)
  • allow_mailboxes (case-insensitive)
  • allow_names (exact match)

Matching is exact-string for scalar constraints. A rule matches only when every configured constraint passes.

Identity-aware context

Policy rules can match on identity context normalized from the incoming action:

  • Request-supplied: agent, user, role, environment, workspace_dir, labels
  • Registry-derived (filled from the Agent Registry when agent_id is present): agent_id, risk_tier, approval_profile, runtime_type, owner, purpose, integration (preferred; normalized to legacy framework storage key)

Explicit request values always win field-by-field; the registry only fills missing fields.

Authoring rules

Most deployments start from the shipped Rules Library and only author custom rules when no library rule covers their need. A rule file looks like:

id: fs.write_reports_allow
type: fs.write_file
effect: allow
constraints:
  path_prefix: workspace/reports/
  max_bytes: 2097152
description: Allow report writes under workspace/reports up to 2 MiB.

Example: file writes

Allow writes under workspace/reports/ up to 2 MiB, require approval for writes outside that path:

rules:
  - id: fs.write_reports_allow
    type: fs.write_file
    effect: allow
    constraints:
      path_prefix: workspace/reports/
      max_bytes: 2097152

  - id: fs.write_other_approve
    type: fs.write_file
    effect: needs_approval

Example: HTTP allow-list

rules:
  - id: http.trusted_domains_allow
    type: http.request
    effect: allow
    constraints:
      method: GET
      allow_domains:
        - api.example.com
        - data.example.com
      timeout_seconds: 10

Example: environment-aware deny

rules:
  - id: fs.delete_any_allow
    type: fs.delete_file
    effect: allow
    constraints:
      path_prefix: workspace/

  - id: fs.delete_prod_deny
    type: fs.delete_file
    effect: deny
    constraints:
      environment: production

Example: identity-aware approvals

rules:
  - id: secrets.read_critical_approve
    type: secrets.read
    effect: needs_approval
    constraints:
      allow_names: [stripe_api_key, github_token]
      labels:
        - risk:critical

Effective policy view

/dashboard/policy/effective shows the merged, resolved policy for a given agent — the global policy rules in order, then the bound agent policy rules, rendered in the exact precedence the engine uses. Submit an action payload and the page renders final_decision, matched_rules, matched_scopes, precedence_order, winning_rule, overridden_rules, and reason.

At runtime, the engine calls load_for_agent(agent_id) and returns:

  • policiesLoadedPolicy objects with hydrated rules
  • stack — metadata entries (policy name, type, rule count) for display
  • warnings — non-fatal normalization or shadowing warnings

Testing with /simulate

/simulate evaluates a policy against an action without executing side effects and without creating approvals:

curl -X POST https://suvra.example.com/simulate \
  -H "Content-Type: application/json" \
  -H "X-Suvra-Token: $SUVRA_AUTH_TOKEN" \
  -d '{
    "action": {
      "type": "fs.write_file",
      "params": { "path": "workspace/reports/q4.md", "content": "..." },
      "agent_id": "report-bot"
    },
    "draft_policies": [ ... ]
  }'

/simulate ignores SUVRA_MODE and always runs strict simulation. Responses include a warnings array for precedence/shadowing. Simulator safety: /simulate and the dashboard Effective View simulation workflow never execute side effects and never create approvals.

Explainability output

Every policy-driven response includes:

  • matched_rule_id — the winning rule (present even on constraint-failure fallback)
  • matched_policy_id
  • reasons — business-readable strings
  • checks — per-constraint {name, ok, detail} entries, including target.* for policy target selectors
  • decision_trace — structured trace: request_scope_summary, loaded_policy_layers, matched_rules, matched_scopes, precedence_order, winning_rule, overridden_rules, final_decision, reason
  • precedence_trace — simulator/execution hierarchy explanation

Audit persistence stores this explainability inside result_summary JSON so any historical decision can be re-rendered.

Related