Skip to content

Risk and policy model

When your agent asks to do something, Ony.ai has to answer two questions: how risky is it, and what does approving it require? Both answers are computed on the server. The client can send hints, but it cannot talk itself into a lower classification. This page describes the shipped model (ADR 0007).

An agent event can carry an action_type hint and a risk_level hint. The server:

  1. Ignores the risk_level hint entirely. It is accepted on the wire only to make the “we received it and discarded it” contract explicit. It never influences the outcome.
  2. Validates the action_type against a closed taxonomy (below). Any value that is not a known member - including a missing value or an unknown string - becomes unknown.
  3. Derives risk from the validated action_type using a fixed server-owned table.

Because unknown maps to critical, an unrecognized or malformed action fails closed: it is treated as the most dangerous class, not the least.

action_type is a closed set. Each member maps to exactly one risk level:

Risk Action types
low continue_task, choose_approach, explain_request, status_query
medium file_create, file_modify, package_install, schema_change_dev
high file_delete, db_migration, branch_push, env_change, secret_access, send_email, external_paid_api
critical deploy_production, db_production_change, payment_settings_change, dns_change, credential_rotation, destructive_command, infra_change
critical (fail-closed) unknown - any unrecognized action

This table is the single source of truth for risk. There are four risk levels, ordered low < medium < high < critical.

Once the server has the authoritative action_type and risk_level, the policy engine decides what approving the action requires. Rules are data, evaluated in order (most specific first), and they match only on the server-derived action_type and risk_level - never on anything the client supplied. Each rule can set:

  • allow_voice - whether the action can be approved on a phone call at all.
  • require_readback - whether the caller must hear the action read back and confirm, rather than approving on a single press.
  • require_pin - whether the caller must also enter a PIN/TOTP second factor.
  • dashboard_required - whether the action can only be approved in the dashboard.

The first matching rule wins. If no rule matches, the engine fails closed: voice is denied and the dashboard is required.

These are the rules shipped out of the box (and shown on the dashboard Policy page):

Rule Matches Effect
block_production_deploy_by_voice action_type = deploy_production Voice denied, dashboard required
critical_blocked_by_voice risk = critical Voice denied, dashboard required
high_requires_readback risk = high Voice allowed with read-back confirmation
medium_requires_readback risk = medium Voice allowed with read-back confirmation
low_allowed risk = low Voice allowed on a single press (no read-back)
(no match) anything unmodeled Voice denied, dashboard required

So a low-risk ask finalizes on one keypress; medium and high risk add a spoken read-back turn; critical actions and production deploys can never be approved by voice and must be confirmed in the dashboard.

No default rule requires a PIN. Ony.ai dials the org’s verified number for an approval, so answering that call is itself the possession factor - the same trust anchor as SMS 2FA. Demanding a TOTP on top by default would make approval impossible for any account without an authenticator enrolled (deny would work, approve would not).

require_pin stays available per rule as opt-in hardening for orgs that do enroll TOTP. Independent of the rules, two things always hold: inbound calls still demand the TOTP/SMS second factor (caller ID is spoofable), and deny/refuse keypresses are always PIN-exempt.

agent event (action_type + risk hints)
|
v
coerce action_type to the closed taxonomy (unknown -> unknown)
|
v
derive risk from action_type (client risk hint ignored)
|
v
policy engine evaluates rules in order (server-derived values only)
|
v
first match wins; no match -> dashboard required (fail closed)

The result travels back to the agent as a signed verdict bound to that specific handoff, session, tool call, and expiry. See how it works and the threat model for how the verdict is signed and verified.