Chio/Docs

Baselines

Capability tokens bound what an agent is allowed to do. Guards bound how it can do it. Baselines are a third, statistical layer: a running picture of what each agent normally looks like, so the kernel can notice when the same capability is being used in a way that no longer matches the agent's own history. They live behind two guards and one signed export, and by default they observe rather than block.

Advisory by default

behavioral-profile always returns Verdict::Allow. Its output is an advisory signal attached to the receipt, not a denial. Operators promote signals into denials explicitly — see From Signal to Denial. The sequence guard (behavioral-sequence) is the opposite: fail-closed on ordering violations.

Why Baselines

An agent can hold a valid capability and pass every stateless guard and still do something strange: hammer one tool forty thousand times in a window that normally sees two hundred calls, suddenly start issuing queries with much higher-entropy parameters, or repeatedly attempt denied operations without backing off. Nothing in the capability token or the guard pipeline is structurally wrong; the agent is just behaving unlike itself.

Baselines turn that observation into a primitive. The kernel keeps an exponentially-weighted moving average for each (agent, metric) pair, samples the current window, computes a z-score, and emits a signal when the score crosses a configurable threshold. The receipt now carries evidence that the action was permitted, was not obviously malicious, and was still a departure from the agent's own norms — which is enough information for an operator or an underwriter to act on later.


The Four Metrics

A baseline is always keyed by (agent_id, metric). Four metrics are tracked per rolling window:

MetricSampleDetects
call_rateReceipts per windowActivity spikes and prolonged lulls
deny_rateFraction of denies per windowRetry storms, probing, drift toward forbidden scopes
unique_toolsDistinct tool names per windowScope widening: an agent that usually touches two tools suddenly touches twenty
avg_parameter_entropyShannon entropy of invocation parametersExfiltration-shaped payloads, fuzzing, generated junk

Only call_rate is sampled synchronously inside the guard's evaluate path so the kernel stays on a sub-millisecond budget per invocation. The other three are exposed through observe_sample so that dashboards, operators, or out-of-band workers can fold samples into the same baseline without blocking the pipeline.


The EMA Model

Each baseline is an EmaBaselineState: sample count, running mean, running variance, and the last-update timestamp. Samples are folded in using a Welford-style incremental update (West, 1979) so the baseline can be maintained without storing any history:

rust
pub struct EmaBaselineState {
    pub sample_count: u64,
    pub ema_mean: f64,
    pub ema_variance: f64,
    pub last_update: u64,
}

// On each new sample:
let prev_mean = self.ema_mean;
self.ema_mean = prev_mean + alpha * (sample - prev_mean);
let diff = sample - prev_mean;
self.ema_variance = (1.0 - alpha) * (self.ema_variance + alpha * diff * diff);

The smoothing factor alpha defaults to 0.2, which weights the baseline over roughly a ten-sample effective window. The window itself defaults to sixty seconds. A baseline requires at least three samples before it will flag anything, so a new agent's first few windows never trigger on their own coldness.

Robust Z-Score

A naïve z-score — (sample − mean) / stddev — is brittle on count data. If an agent sits dead-steady at ten calls per window for an hour, the measured variance is numerically zero and the z-score blows up. The kernel floors the effective standard deviation at sqrt(max(mean, 1)), which is the expected standard deviation of a Poisson process with rate mean. A 50× spike over a steady 10/window baseline now crosses the sigma threshold cleanly.

The threshold defaults to ±2σ, which is roughly the 95th percentile for a normal-shaped distribution. Operators who want more or fewer signals tune sigma_threshold rather than rewriting the math.


Sequence Rules

The profile guard watches how much an agent does. The sequence guard — behavioral-sequence — watches in what order. It consults the session journal and enforces four kinds of policy. Unlike the profile guard, this one is fail-closed: an ordering violation returns Verdict::Deny.

RuleSemantics
required_predecessorsTool X may only run after tool Y has been invoked in this session
forbidden_transitionsIf the last tool was X, Y may not be the next
max_consecutiveCap on how many times the same tool can run back-to-back
required_first_toolThe first tool of a session must be a specific name — a handshake or setup step

Because the rules are structural, the sequence guard can fail closed without false positives — an agent that holds a capability granting access to a write tool but has never handshaken is, by policy, not allowed to write. If the journal is unavailable, the guard denies rather than defaulting permissive.


From Signal to Denial

Advisory signals are first-class, typed outputs, not log lines. The anomaly-advisory guard emits structured signals with explicit severity tiers — info, low, medium, high, critical — for conditions like repeated invocation and excessive delegation depth:

ConditionSeverity
Tool invoked ≥ invocation_thresholdMedium
Tool invoked ≥ 2× thresholdHigh
Delegation depth ≥ depth_thresholdHigh

Operators configure a promotion rule that says, for example, treat any signal at High or above as a denial. The promotion happens at pipeline composition time, not inside the guard, so the same signal can remain advisory in staging and block in production without any code change. Every advisory, promoted or not, is persisted as GuardOutput::Advisory in the receipt's evidence array so downstream review has the same data the kernel did.

Rollout pattern

Turn behavioral-profile on with the default ±2σ threshold, watch receipts for a week or two, decide which metrics are actually noisy in your traffic, and only then add a promotion rule. Baselines bootstrap themselves; there is nothing to train.

The Behavioral Feed

Baselines are runtime machinery. The offline view is the signed behavioral-feed export at /v1/reports/behavioral-feed. It is the surface insurers, risk teams, and regulators consume.

A request specifies the filter scope — capability_id, agent_subject, tool filters, a time window, and a receipt-detail limit. The response is a BehavioralFeedReport wrapped in a signed export envelope:

rust
pub struct BehavioralFeedReport {
    pub schema: String,
    pub generated_at: u64,
    pub filters: BehavioralFeedQuery,
    pub privacy: BehavioralFeedPrivacyBoundary,
    pub decisions: BehavioralFeedDecisionSummary,
    pub settlements: BehavioralFeedSettlementSummary,
    pub governed_actions: BehavioralFeedGovernedActionSummary,
    pub metered_billing: BehavioralFeedMeteredBillingSummary,
    pub reputation: Option<BehavioralFeedReputationSummary>,
    pub shared_evidence: SharedEvidenceReferenceSummary,
    pub receipts: Vec<BehavioralFeedReceiptRow>,
}

The report reuses the canonical receipt, settlement, reputation, and shared-evidence substrate rather than inventing a parallel telemetry pipeline. Every row cites a specific receipt; every summary is derivable from receipts the operator already has.


What Baselines Aren't

The behavioral feed is a truthful evidence export, not an underwriting model. It does not assign a single risk score, predict insurance premiums, or hide its sources behind a learned function. Scoring belongs one layer up, in /v1/reports/underwriting-input, which consumes the behavioral feed (alongside reputation, certification, and runtime assurance) and produces a policy input against a stable taxonomy.

Baselines also aren't a replacement for velocity guards. Velocity caps an individual capability by absolute rate and spend, deterministically. Baselines model an agent's character across capabilities and are comparative. A well-tuned deployment uses both: velocity for hard ceilings, baselines for drift from normal.


Next Steps

  • Guards — where behavioral-profile, behavioral-sequence, and anomaly-advisory sit in the pipeline.
  • Receipts — the evidence substrate baselines read from and write back to.
  • Query & Audit Receipts — consuming advisory signals from a running system.
Baselines · Chio Docs