Quick Start
#Get from zero to a governed agent intent in under five minutes. This guide walks you through authentication, agent registration, your first intent submission, and reading the audit trail.
https://console.regulator.ai. All endpoints use the /api/v1 prefix. For self-hosted deployments, replace the base URL with your own.Step 1: Authenticate & Get Your API Key#
Vienna OS supports session-based authentication for the operator console and API key authentication for programmatic access. Start by logging in to get a session token.
curl -X POST https://console.regulator.ai/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "your-email@company.com",
"password": "your-password"
}'{
"success": true,
"data": {
"operator": {
"id": "op-7f3a2b1c",
"email": "your-email@company.com",
"role": "admin",
"organization": "your-org"
},
"sessionId": "sess-a8b3c2d1-4e5f-6789-abcd-ef0123456789",
"apiKey": "vos_live_k1_7f3a2b1c9d4e5f6789abcdef01234567",
"expiresAt": "2026-03-26T21:35:00.000Z"
}
}Store the apiKey securely. For all subsequent requests, include it as a Bearer token or pass the session cookie.
const VIENNA_API_KEY = process.env.VIENNA_API_KEY;
const BASE_URL = "https://console.regulator.ai";
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${VIENNA_API_KEY}`,
};import os
import requests
VIENNA_API_KEY = os.environ["VIENNA_API_KEY"]
BASE_URL = "https://console.regulator.ai"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {VIENNA_API_KEY}",
}Step 2: Register Your First Agent#
Before an agent can submit intents, it must be registered with a trust profile. This creates an identity in the fleet and assigns default permissions.
curl -X POST https://console.regulator.ai/api/v1/fleet \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $VIENNA_API_KEY" \
-d '{
"name": "support-agent-01",
"description": "Customer support automation agent",
"type": "autonomous",
"trust_score": 50,
"allowed_actions": ["send_email", "query_database", "create_ticket"],
"metadata": {
"framework": "langchain",
"version": "0.1.0",
"owner": "platform-team"
}
}'{
"success": true,
"data": {
"agent_id": "agt-9c8b7a6f-5e4d-3c2b-1a09-876543210fed",
"name": "support-agent-01",
"status": "active",
"trust_score": 50,
"api_key": "vos_agent_k1_9c8b7a6f5e4d3c2b1a09876543210fed",
"created_at": "2026-03-25T21:35:00.000Z"
}
}trust_score (0–100) determines baseline risk assessment. New agents start at 50. Vienna adjusts this score based on the agent's behavior over time. Higher trust scores may qualify for automatic approval on lower-risk actions.Step 3: Submit an Intent#
Every action an agent wants to perform is submitted as an intent. The intent flows through the governance pipeline: validation → policy evaluation → risk tier assignment → approval (if required) → warrant issuance → execution → verification.
curl -X POST https://console.regulator.ai/api/v1/agent/intent \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $VIENNA_AGENT_KEY" \
-d '{
"action": "send_email",
"source": {
"platform": "langchain",
"agent_id": "agt-9c8b7a6f"
},
"tenant_id": "your-org",
"parameters": {
"to": "customer@example.com",
"subject": "Your support ticket #1234",
"body": "Hi, your ticket has been resolved."
},
"context": {
"ticket_id": "TKT-1234",
"conversation_id": "conv-5678",
"reason": "Ticket resolution notification"
}
}'{
"success": true,
"data": {
"intent_id": "int-2d4f6a8b-1c3e-5d7f-9a0b-c2d4e6f8a0b2",
"status": "executed",
"risk_tier": "T0",
"execution_id": "exec-3e5f7a9b-2d4f-6a8c-0b1c-d3e5f7a9b1c3",
"warrant": {
"warrant_id": "wrt-4f6a8c0b-3e5f-7a9b-1c2d-e4f6a8c0b2d4",
"scope": "send_email",
"ttl": 300,
"issued_at": "2026-03-25T21:35:01.000Z",
"expires_at": "2026-03-25T21:40:01.000Z"
},
"verification": {
"status": "passed",
"scope_match": true,
"verified_at": "2026-03-25T21:35:02.000Z"
},
"result": {
"email_sent": true,
"message_id": "msg-abc123"
}
}
}{
"success": true,
"data": {
"intent_id": "int-5a7b9c0d-4e6f-8a1b-2c3d-f5a7b9c0d1e2",
"status": "pending_approval",
"risk_tier": "T1",
"approval": {
"approval_id": "apr-6b8c0d1e-5a7b-9c2d-3e4f-a6b8c0d1e2f3",
"required_approvers": 1,
"current_approvals": 0,
"expires_at": "2026-03-25T22:35:00.000Z"
}
}
}async function submitIntent(action: string, parameters: Record<string, unknown>) {
const response = await fetch(`${BASE_URL}/api/v1/agent/intent`, {
method: "POST",
headers,
body: JSON.stringify({
action,
source: { platform: "custom", agent_id: "agt-9c8b7a6f" },
tenant_id: "your-org",
parameters,
}),
});
const data = await response.json();
if (data.data.status === "pending_approval") {
console.log(`⏳ Waiting for approval: ${data.data.approval.approval_id}`);
// Poll or use webhooks to wait for approval
return data.data;
}
if (data.data.status === "executed") {
console.log(`✅ Executed: ${data.data.execution_id}`);
console.log(`🔒 Warrant: ${data.data.warrant.warrant_id}`);
return data.data;
}
if (data.data.status === "denied") {
console.log(`❌ Denied by policy: ${data.data.reason}`);
throw new Error(data.data.reason);
}
}
// Usage
await submitIntent("send_email", {
to: "customer@example.com",
subject: "Ticket resolved",
body: "Your issue has been fixed.",
});import requests
def submit_intent(action: str, parameters: dict) -> dict:
response = requests.post(
f"{BASE_URL}/api/v1/agent/intent",
headers=headers,
json={
"action": action,
"source": {"platform": "custom", "agent_id": "agt-9c8b7a6f"},
"tenant_id": "your-org",
"parameters": parameters,
},
)
data = response.json()
match data["data"]["status"]:
case "executed":
print(f"✅ Executed: {data['data']['execution_id']}")
print(f"🔒 Warrant: {data['data']['warrant']['warrant_id']}")
case "pending_approval":
print(f"⏳ Awaiting approval: {data['data']['approval']['approval_id']}")
case "denied":
raise RuntimeError(f"❌ Denied: {data['data']['reason']}")
return data["data"]
# Usage
submit_intent("send_email", {
"to": "customer@example.com",
"subject": "Ticket resolved",
"body": "Your issue has been fixed.",
})Step 4: View the Audit Trail#
Every intent, policy evaluation, warrant, execution, and verification is recorded in the append-only audit trail. Query it to see exactly what happened.
curl https://console.regulator.ai/api/v1/audit \
-H "Authorization: Bearer $VIENNA_API_KEY" \
-G \
-d "limit=10" \
-d "agent_id=agt-9c8b7a6f"{
"success": true,
"data": {
"entries": [
{
"id": "aud-7c9d0e1f",
"timestamp": "2026-03-25T21:35:02.000Z",
"type": "verification_complete",
"intent_id": "int-2d4f6a8b",
"execution_id": "exec-3e5f7a9b",
"warrant_id": "wrt-4f6a8c0b",
"agent_id": "agt-9c8b7a6f",
"action": "send_email",
"risk_tier": "T0",
"result": "passed",
"scope_compliance": true,
"duration_ms": 847
},
{
"id": "aud-6b8c0d1e",
"timestamp": "2026-03-25T21:35:01.000Z",
"type": "warrant_issued",
"warrant_id": "wrt-4f6a8c0b",
"scope": "send_email",
"ttl": 300,
"issuer": "system:auto-approve"
}
],
"total": 2,
"has_more": false
}
}Core Concepts
#Vienna OS is built on seven foundational concepts. Understanding these is essential for integrating effectively and designing governance policies that match your organization's risk tolerance.
Intent Gateway#
The Intent Gateway is the single entry point for all agent actions. No agent communicates directly with external systems. Instead, every action is expressed as a declarative intent and submitted to the gateway.
This design is deliberate. By funneling all agent behavior through one point, Vienna can normalize, validate, and govern every action before it reaches execution. There are no side channels.
┌─────────────────────────────────────────────────┐
│ Intent Gateway │
│ │
│ ┌──────────┐ ┌────────────┐ ┌─────────────┐ │
│ │ Normalize │→ │ Validate │→ │ Deduplicate │ │
│ └──────────┘ └────────────┘ └─────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────┐ │
│ │ Enrich with │ │
│ │ context │ │
│ └─────────────┘ │
│ │ │
│ ↓ │
│ Policy Engine │
└─────────────────────────────────────────────────┘Normalization. The gateway accepts intents from any framework — LangChain, CrewAI, AutoGen, or raw HTTP. It normalizes the payload into a canonical format regardless of source, so the policy engine evaluates a consistent schema.
Validation. Intents are validated against registered action types. If an agent submits an action not in its allowed set, the intent is rejected before reaching the policy engine. Parameters are type-checked against the action type schema.
Deduplication. The gateway detects and collapses duplicate intents within a configurable time window (default: 5s). This prevents runaway agents from flooding the pipeline.
Context enrichment. The gateway attaches metadata: agent trust score, historical behavior patterns, time-of-day, and organizational context. This enriched payload feeds into policy evaluation.
Policy Engine#
The Policy Engine is where governance rules live. Policies are defined as structured rules — not natural language, not LLM prompts, but deterministic code that evaluates in microseconds. Every intent is evaluated against the full policy set before any action is taken.
Rule Structure
A policy rule consists of conditions, an action, and a priority. Rules are evaluated in priority order (highest first). The first matching rule wins.
{
"id": "pol-rule-001",
"name": "Block high-value transactions after hours",
"description": "Prevent financial actions over $10K outside business hours",
"priority": 100,
"enabled": true,
"conditions": [
{
"field": "action",
"operator": "equals",
"value": "transfer_funds"
},
{
"field": "parameters.amount",
"operator": "gt",
"value": 10000
},
{
"field": "context.timestamp",
"operator": "time_between",
"value": ["18:00", "08:00"]
}
],
"action": {
"type": "deny",
"reason": "High-value transfers blocked outside business hours (8AM-6PM ET)"
},
"risk_tier_override": null
}Evaluation Order
Rules are sorted by priority (descending). The first rule whose conditions all match determines the outcome. If no rule matches, the default policy applies (configurable: deny-by-default or allow-by-default).
Condition Operators
Vienna supports 14 condition operators for precise policy expression:
| Operator | Description | Example Value |
|---|---|---|
| equals | Exact match | "transfer_funds" |
| not_equals | Negated exact match | "read_only" |
| contains | Substring / array inclusion | "admin" |
| gt | Greater than (numeric) | 10000 |
| lt | Less than (numeric) | 100 |
| gte | Greater than or equal | 80 |
| lte | Less than or equal | 5 |
| in | Value in set | ["us","eu","uk"] |
| not_in | Value not in set | ["sandbox","test"] |
| matches | Regex match | "^prod-.*" |
| between | Numeric range (inclusive) | [100, 5000] |
| time_between | Time-of-day range | ["09:00","17:00"] |
| exists | Field is present and non-null | true |
| not_exists | Field is absent or null | true |
Risk Tiers#
Every intent is assigned a risk tier that determines the approval workflow. Tiers are assigned by the policy engine based on the action type, parameters, agent trust score, and contextual factors.
Auto-Approved — Reversible / Low-Stakes
No human intervention. Warrant is issued automatically, action executes immediately.
Single Approval — Moderate Stakes
One operator must approve before execution. Approval timeout is configurable (default: 1 hour). Escalation triggers if no response.
Multi-Party Approval — Irreversible / High-Impact
Requires approval from multiple operators (configurable: 2–5 approvers). Warrants carry strict scope constraints and short TTLs (default: 5 min).
Tier assignment is dynamic. The same action can be T0 in one context and T1 in another. For example, send_email might be T0 for internal notifications but T1 for customer-facing messages. Policy rules control this.
Approval Workflows#
When an intent is classified as T1 or T2, it enters the approval queue. Operators are notified via the console, webhooks, or integrated channels (Slack, email).
T1 — Single Operator Approval
─────────────────────────────
Intent → Policy Engine → T1 → Approval Queue
│
┌─────┴─────┐
│ Operator │
│ Reviews │
└─────┬─────┘
│
┌──────────┼──────────┐
│ │ │
Approve Deny Timeout
│ │ │
Warrant Denied Escalate
Issued + Log to T2 or
│ Manager
Execute
T2 — Multi-Party Approval
─────────────────────────
Intent → Policy Engine → T2 → Approval Queue
│
┌────────────────┼────────────────┐
│ │ │
Approver 1 Approver 2 Approver N
(required) (required) (optional)
│ │ │
└────────────────┼────────────────┘
│
All approved? ───No──→ Wait/Escalate
│
Yes
│
Warrant Issued (strict TTL)
│
ExecuteTimeout handling. If an approval is not acted on within the configured window, the system either escalates to a higher authority, notifies the organization admin, or auto-denies (configurable per policy). Default timeout: 1 hour for T1, 4 hours for T2.
Execution Warrants#
Execution warrants are the central innovation in Vienna OS. A warrant is a cryptographically signed, scope-constrained, time-limited authorization token. It is the only mechanism that authorizes execution. Without a valid warrant, no action executes. Period.
{
"warrant_id": "wrt-4f6a8c0b-3e5f-7a9b-1c2d-e4f6a8c0b2d4",
"intent_id": "int-2d4f6a8b-1c3e-5d7f-9a0b-c2d4e6f8a0b2",
"scope": {
"action": "send_email",
"target": "smtp-gateway",
"parameters": {
"to": "customer@example.com",
"subject": "Your support ticket #1234"
}
},
"constraints": {
"max_retries": 1,
"max_recipients": 1,
"allowed_domains": ["example.com"],
"rollback_on_failure": true
},
"ttl": 300,
"issued_at": "2026-03-25T21:35:01.000Z",
"expires_at": "2026-03-25T21:40:01.000Z",
"issuer": {
"type": "operator",
"id": "op-7f3a2b1c",
"approval_id": "apr-6b8c0d1e"
},
"chain": {
"parent_warrant": null,
"depth": 0
},
"signature": "hmac-sha256:a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0"
}Scope enforcement. The warrant specifies exactly what action is permitted, against what target, with what parameters. The execution router checks the warrant scope before running any action. Parameter drift (e.g., changing the email recipient) invalidates the warrant.
Time-limited by design. Every warrant has a TTL measured in seconds. Once expired, the warrant is inert. There are no permanent warrants. Default TTL is 300 seconds (5 minutes) for T0, 600 seconds for T1, and 300 seconds for T2 (shorter because higher-risk actions should execute promptly or not at all).
Post-execution verification. After execution, the Verification Engine checks whether the actual operation matched the warrant scope. Did the email go to the authorized recipient? Did the database query touch only the permitted tables? Any mismatch generates an alert and is recorded in the audit trail.
Verification Engine#
The Verification Engine runs after every execution. It compares what was authorized (the warrant) with what actually happened (the execution result). This is the "trust but verify" layer.
Execution Complete
│
↓
┌─────────────────┐
│ Load Warrant │ ← Original warrant scope and constraints
│ Load Result │ ← Actual execution result and side effects
└────────┬────────┘
│
↓
┌─────────────────┐
│ Scope Check │ ← Did the action match the warrant scope?
│ Parameter Check │ ← Were parameters within constraints?
│ Target Check │ ← Was the correct target affected?
│ Side Effect │ ← Any unauthorized side effects?
│ Detection │
└────────┬────────┘
│
┌────┴────┐
│ │
Pass Fail
│ │
Log to Generate Alert
Audit Flag Agent
Trail Log to Audit Trail
Revoke future warrants (optional)Mismatch handling. When verification fails, the system can: (1) generate an alert to operators, (2) reduce the agent's trust score, (3) suspend the agent pending review, or (4) quarantine the execution result. The response is configurable per action type and severity.
Audit Trail#
The audit trail is an append-only, immutable log of every event in the governance pipeline. It is tamper-evident: each entry includes a hash chain linking it to the previous entry, making retroactive modification detectable.
What gets logged:
- • Intent submission (raw payload, source agent, timestamp)
- • Policy evaluation result (matched rule, tier assignment, deny reason)
- • Approval events (who approved/denied, when, with what context)
- • Warrant issuance (full warrant including scope, TTL, signature)
- • Execution start and completion (duration, result, errors)
- • Verification result (pass/fail, scope compliance details)
- • Agent trust score changes (before/after, reason)
- • System events (policy changes, agent registration, configuration updates)
Retention. Default retention is 90 days for standard entries, 7 years for compliance-flagged events (financial, legal, healthcare). Configurable per organization and action type.
Architecture
#Vienna OS is a monolithic governance engine deployed as a single binary. Internally, it is composed of nine distinct services communicating via in-process function calls (no inter-service network hops in default deployment).
System Diagram#
┌──────────────────┐
│ Agent / Client │
└────────┬─────────┘
│
▼
┌─────────────────────────┐
│ Intent Gateway │
│ normalize · validate │
│ deduplicate · enrich │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ Policy Engine │
│ evaluate rules │
│ assign risk tier │
│ check rate limits │
└────────────┬────────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ T0: Auto │ │ T1: Single │ │ T2: Multi │
│ Approve │ │ Approval │ │ Approval │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘
│ │ │
└─────────────────┼─────────────────┘
│
▼
┌─────────────────────────┐
│ Warrant Authority │
│ scope · ttl · sign │
│ constraints · chain │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ Execution Router │
│ warrant check · route │
│ execute · capture │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ Verification Engine │
│ scope compliance │
│ parameter check │
│ side effect detection │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ Audit Trail │
│ append-only · hashed │
│ tamper-evident · query │
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ State Graph │
│ SQLite / Postgres │
│ persistent storage │
└─────────────────────────┘Data Flow#
A single intent traverses the full pipeline in milliseconds for T0 actions. Here is the data flow for each stage:
/api/v1/agent/intent. Gateway parses, validates auth token, normalizes payload.State Graph#
The State Graph is Vienna's persistence layer. It stores all entities and their relationships: agents, policies, warrants, executions, and audit entries. Default storage is SQLite (embedded, zero-config). Production deployments can use Postgres for horizontal scaling.
┌─────────────┐ ┌──────────────┐ ┌───────────────┐
│ Agents │────→│ Intents │────→│ Warrants │
│ │ │ │ │ │
│ id │ │ id │ │ id │
│ name │ │ agent_id │ │ intent_id │
│ trust_score │ │ action │ │ scope │
│ status │ │ parameters │ │ ttl │
│ allowed_ │ │ risk_tier │ │ constraints │
│ actions │ │ status │ │ signature │
└─────────────┘ └──────┬───────┘ └───────┬───────┘
│ │
▼ ▼
┌──────────────┐ ┌───────────────┐
│ Executions │ │ Audit Trail │
│ │ │ │
│ id │ │ id │
│ intent_id │ │ type │
│ warrant_id │ │ entity_id │
│ result │ │ data │
│ duration_ms │ │ hash │
│ verified │ │ prev_hash │
└──────────────┘ └───────────────┘
┌──────────────┐ ┌──────────────┐ ┌───────────────┐
│ Policies │ │ Action Types │ │ Approvals │
│ │ │ │ │ │
│ id │ │ id │ │ id │
│ name │ │ name │ │ intent_id │
│ priority │ │ schema │ │ approver_id │
│ conditions │ │ risk_tier │ │ status │
│ action │ │ description │ │ expires_at │
└──────────────┘ └──────────────┘ └───────────────┘Service Breakdown#
Vienna OS comprises nine internal services. In the default monolithic deployment, these are in-process modules. In a distributed deployment, they can be separated into independent services communicating over gRPC.
Deployment Topology#
Vienna OS supports three deployment models. All produce the same governance guarantees — the deployment model only affects scaling and operational complexity.
fly deployAPI Reference
#All endpoints are prefixed with /api/v1. Authentication is required for all endpoints except /health and /api/v1/auth/login. Pass credentials as a Bearer token or session cookie.
{"success": boolean, "data": ..., "error": ...}. Error responses include a machine-readable code and human-readable message.Authentication#
Agent Intent#
The intent endpoints are the primary API surface for agent governance. Every agent action flows through these endpoints.
Policies#
Action Types#
Fleet Management#
Integrations#
Compliance#
Approvals#
Integration Guides
#Vienna OS is runtime-agnostic. Any system that can make HTTP requests can integrate. These guides show how to wire governance into popular AI agent frameworks.
OpenClaw#
OpenClaw agents can integrate with Vienna OS by wrapping action execution in intent submissions. Every tool call routes through the governance pipeline.
import { ViennaClient } from "./vienna-client";
const vienna = new ViennaClient({
baseUrl: process.env.VIENNA_URL || "https://console.regulator.ai",
apiKey: process.env.VIENNA_AGENT_KEY!,
agentId: process.env.VIENNA_AGENT_ID!,
tenantId: process.env.VIENNA_TENANT_ID!,
});
/**
* Wrap any action in Vienna governance.
* The action only executes if Vienna issues a warrant.
*/
async function governedAction(
action: string,
parameters: Record<string, unknown>,
context?: Record<string, unknown>
) {
// Submit intent to Vienna
const intent = await vienna.submitIntent({
action,
parameters,
context: {
...context,
source_framework: "openclaw",
timestamp: new Date().toISOString(),
},
});
switch (intent.status) {
case "executed":
console.log(`✅ ${action} executed (warrant: ${intent.warrant.warrant_id})`);
return intent.result;
case "pending_approval":
console.log(`⏳ ${action} awaiting approval (${intent.approval.approval_id})`);
// Optionally wait for approval via webhook or polling
return await vienna.waitForApproval(intent.approval.approval_id, {
timeoutMs: 3600_000, // 1 hour
pollIntervalMs: 5_000,
});
case "denied":
throw new Error(`❌ ${action} denied: ${intent.reason}`);
default:
throw new Error(`Unexpected status: ${intent.status}`);
}
}
// Usage in an OpenClaw agent
const result = await governedAction("send_email", {
to: "customer@example.com",
subject: "Your order has shipped",
body: "Track your package at ...",
});LangChain#
Wrap LangChain tools with a Vienna governance layer. The tool checks with Vienna before executing any action.
from langchain.tools import BaseTool
from typing import Optional, Type
from pydantic import BaseModel, Field
import requests
import os
VIENNA_URL = os.environ.get("VIENNA_URL", "https://console.regulator.ai")
VIENNA_KEY = os.environ["VIENNA_AGENT_KEY"]
AGENT_ID = os.environ["VIENNA_AGENT_ID"]
TENANT_ID = os.environ["VIENNA_TENANT_ID"]
class GovernedTool(BaseTool):
"""Base class for Vienna-governed LangChain tools.
Subclass this instead of BaseTool. Your tool's _run method
will only execute if Vienna issues a warrant.
"""
vienna_action: str = "" # Override in subclass
def _call_vienna(self, parameters: dict) -> dict:
"""Submit intent to Vienna and handle the response."""
response = requests.post(
f"{VIENNA_URL}/api/v1/agent/intent",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {VIENNA_KEY}",
},
json={
"action": self.vienna_action,
"source": {"platform": "langchain", "agent_id": AGENT_ID},
"tenant_id": TENANT_ID,
"parameters": parameters,
},
)
return response.json()["data"]
def _run(self, **kwargs) -> str:
# Check with Vienna first
result = self._call_vienna(kwargs)
if result["status"] == "denied":
return f"Action denied by governance policy: {result['reason']}"
if result["status"] == "pending_approval":
return (
f"Action requires approval (ID: {result['approval']['approval_id']}). "
f"An operator must approve before execution."
)
# Warrant issued — proceed with actual execution
return self._execute_with_warrant(result["warrant"], **kwargs)
def _execute_with_warrant(self, warrant: dict, **kwargs) -> str:
raise NotImplementedError("Override in subclass")
# Example: Governed email sender
class GovernedEmailTool(GovernedTool):
name = "send_email"
description = "Send an email (governed by Vienna OS)"
vienna_action = "send_email"
def _execute_with_warrant(self, warrant, **kwargs):
# Your actual email sending logic here
return f"Email sent (warrant: {warrant['warrant_id']})"
# Usage with LangChain agent
from langchain.agents import initialize_agent, AgentType
from langchain.llms import OpenAI
tools = [GovernedEmailTool()]
llm = OpenAI(temperature=0)
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)CrewAI#
CrewAI tasks can be wrapped with Vienna governance using a callback pattern. The crew's task outputs are submitted as intents before being acted upon.
from crewai import Agent, Task, Crew
import requests, os
VIENNA_URL = os.environ.get("VIENNA_URL", "https://console.regulator.ai")
VIENNA_KEY = os.environ["VIENNA_AGENT_KEY"]
def governed_callback(output):
"""Submit task output to Vienna for governance before execution."""
response = requests.post(
f"{VIENNA_URL}/api/v1/agent/intent",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {VIENNA_KEY}",
},
json={
"action": output.get("action", "task_execution"),
"source": {"platform": "crewai", "agent_id": output.get("agent", "unknown")},
"tenant_id": os.environ["VIENNA_TENANT_ID"],
"parameters": output,
},
)
result = response.json()
if not result.get("success") or result["data"]["status"] == "denied":
raise Exception(f"Governance denied: {result.get('error', 'Policy violation')}")
return result["data"]
# Define agents and tasks
researcher = Agent(role="Researcher", goal="Find market data", backstory="...")
analyst = Agent(role="Analyst", goal="Analyze trends", backstory="...")
research_task = Task(
description="Research Q1 market trends",
agent=researcher,
callback=governed_callback, # Vienna checks before execution
)
crew = Crew(agents=[researcher, analyst], tasks=[research_task])
result = crew.kickoff()Generic HTTP#
Any system that can make HTTP requests can integrate with Vienna OS. Here's the universal pattern.
# Submit any agent action through governance
curl -X POST https://console.regulator.ai/api/v1/agent/intent \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $VIENNA_AGENT_KEY" \
-d '{
"action": "your_custom_action",
"source": {
"platform": "your-framework",
"agent_id": "your-agent-id"
},
"tenant_id": "your-tenant",
"parameters": {
"key": "value"
}
}'
# Response:
# {
# "success": true,
# "data": {
# "intent_id": "int-abc123",
# "status": "executed",
# "execution_id": "exec-def456",
# "warrant": { "warrant_id": "wrt-...", "scope": {...}, "ttl": 300 },
# "audit_id": "aud-ghi789"
# }
# }Receiving Webhooks#
Vienna OS can send webhooks when governance events occur (approvals needed, actions executed, policy violations). Configure integrations via the console or API.
import express from "express";
import crypto from "crypto";
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.VIENNA_WEBHOOK_SECRET!;
// Verify HMAC-SHA256 signature
function verifySignature(payload: string, signature: string): boolean {
const expected = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
app.post("/webhooks/vienna", (req, res) => {
const signature = req.headers["x-vienna-signature"] as string;
const rawBody = JSON.stringify(req.body);
if (!verifySignature(rawBody, signature)) {
return res.status(401).json({ error: "Invalid signature" });
}
const event = req.body;
switch (event.type) {
case "approval_required":
console.log("Approval needed:", event.data.action_type);
// Notify your team via Slack, email, etc.
break;
case "action_executed":
console.log("Action completed:", event.data.execution_id);
break;
case "policy_violation":
console.log("ALERT:", event.data.agent_id, event.data.message);
break;
}
res.json({ received: true });
});
app.listen(3001);Policy-as-Code Guide
#Vienna OS policies are rules that automatically evaluate every agent intent. Rules are evaluated top-down by priority (highest first). First matching rule wins, like firewall rules.
Creating Rules#
Create policies via the API or the visual Policy Builder in the console.
curl -X POST https://console.regulator.ai/api/v1/policies \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $VIENNA_API_KEY" \
-d '{
"name": "High-Value Transaction Gate",
"description": "Require T2 multi-party approval for transactions over $10,000",
"conditions": [
{ "field": "action_type", "operator": "equals", "value": "financial_transaction" },
{ "field": "amount", "operator": "gt", "value": 10000 }
],
"action_on_match": "require_approval",
"approval_tier": "T2",
"priority": 100,
"enabled": true
}'Condition Operators#
Vienna supports 14 condition operators. The available operators depend on the field type being evaluated.
| Operator | Description | Example |
|---|---|---|
| equals | Exact match | { field: "action_type", operator: "equals", value: "deploy" } |
| not_equals | Not equal | { field: "environment", operator: "not_equals", value: "production" } |
| contains | String contains | { field: "action_type", operator: "contains", value: "delete" } |
| gt | Greater than (numeric) | { field: "amount", operator: "gt", value: 10000 } |
| gte | Greater than or equal | { field: "risk_score", operator: "gte", value: 80 } |
| lt | Less than | { field: "trust_score", operator: "lt", value: 50 } |
| lte | Less than or equal | { field: "retry_count", operator: "lte", value: 3 } |
| in | Value in array | { field: "agent_id", operator: "in", value: ["bot-a", "bot-b"] } |
| not_in | Value not in array | { field: "environment", operator: "not_in", value: ["prod", "staging"] } |
| matches | Regex match | { field: "action_type", operator: "matches", value: "^deploy_.*" } |
| between | Numeric range | { field: "amount", operator: "between", value: [1000, 50000] } |
| time_between | Time-of-day range (HH:MM) | { field: "time_of_day", operator: "time_between", value: ["18:00", "06:00"] } |
| exists | Field is present | { field: "parameters.override", operator: "exists", value: true } |
| not_exists | Field is absent | { field: "parameters.approval_bypass", operator: "not_exists", value: true } |
Complex Examples#
{
"name": "After-Hours High-Value Block",
"conditions": [
{ "field": "action_type", "operator": "equals", "value": "financial_transaction" },
{ "field": "amount", "operator": "gt", "value": 50000 },
{ "field": "time_of_day", "operator": "time_between", "value": ["18:00", "06:00"] }
],
"action_on_match": "deny",
"priority": 200
}{
"name": "Prod DB Migration Gate",
"conditions": [
{ "field": "action_type", "operator": "equals", "value": "database_migration" },
{ "field": "environment", "operator": "equals", "value": "production" }
],
"action_on_match": "require_approval",
"approval_tier": "T2",
"required_approvers": ["operator-cto"],
"priority": 150
}{
"name": "Trusted Agent Auto-Approve",
"conditions": [
{ "field": "action_type", "operator": "contains", "value": "read" },
{ "field": "trust_score", "operator": "gt", "value": 80 }
],
"action_on_match": "allow",
"priority": 50
}{
"name": "Agent Rate Limiter",
"conditions": [
{ "field": "rate", "operator": "gt", "value": 100 }
],
"action_on_match": "rate_limit",
"priority": 300
}Dry-Run Testing#
Test any intent against all active policies without executing it. The evaluate endpoint returns which rules matched and what action would be taken.
curl -X POST https://console.regulator.ai/api/v1/policies/evaluate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $VIENNA_API_KEY" \
-d '{
"action_type": "financial_transaction",
"amount": 75000,
"agent_id": "billing-bot",
"environment": "production",
"time_of_day": "22:30"
}'
# Response:
# {
# "results": [
# {
# "rule_id": "...",
# "rule_name": "After-Hours High-Value Block",
# "matched": true,
# "action": "deny",
# "conditions_detail": [
# { "field": "action_type", "passed": true },
# { "field": "amount", "passed": true, "actual": 75000, "expected": "> 50000" },
# { "field": "time_of_day", "passed": true, "actual": "22:30", "expected": "18:00-06:00" }
# ]
# }
# ],
# "final_action": "deny",
# "matching_rule": "After-Hours High-Value Block"
# }Industry Templates#
Vienna ships with pre-built policy templates for regulated industries. Import them via the API or the Policy Builder UI.
curl https://console.regulator.ai/api/v1/policies/templates \
-H "Authorization: Bearer $VIENNA_API_KEY"
# Returns: financial_services, healthcare, devops, legal, general
# Each template contains 5-8 rules configured for that industryWarrant Deep Dive
#The execution warrant is Vienna OS's core innovation. This section explains why warrants exist, how they work, and why they're necessary for governing autonomous AI agents.
Problem Statement#
Traditional authorization (API keys, OAuth tokens, IAM roles) answers one question: “Who are you?” But for AI agents taking real-world actions, we need to answer a different question: “What exactly are you authorized to do, right now, this one time?”
An API key that authorizes “billing-bot” to “make payments” is dangerously broad. It doesn't constrain amount, recipient, timing, or frequency. If the agent is compromised or hallucinates, it can drain an account. Traditional auth is identity-based. Agent governance needs to be action-based.
The Warrant Model#
A Vienna execution warrant is a cryptographically signed, time-limited, scope-constrained authorization token. It authorizes exactly one action with specific parameters, issued only after policy evaluation and (optionally) operator approval.
{
"warrant_id": "wrt-7f3a2b1c-e8d4-4a9f-b2c1-9d8e7f6a5b4c",
"scope": {
"action": "wire_transfer",
"target": "payments-service",
"parameters": {
"amount": 75000,
"currency": "USD",
"recipient": "vendor-456"
}
},
"constraints": {
"max_amount": 75000,
"allowed_recipients": ["vendor-456"],
"max_retries": 0,
"rollback_on_failure": true
},
"ttl_seconds": 300,
"issued_at": "2026-03-25T21:30:00Z",
"expires_at": "2026-03-25T21:35:00Z",
"issuer": {
"type": "multi_party",
"operators": ["operator-jane", "operator-mike"],
"approval_id": "appr-abc123"
},
"chain_of_custody": {
"intent_id": "int-xyz789",
"policy_match": "high-value-transfer-gate",
"risk_tier": "T2",
"approval_time_ms": 45200
},
"signature": "hmac-sha256:a1b2c3d4e5f67890..."
}Comparison with Traditional Auth#
| Property | API Key / OAuth | Vienna Warrant |
|---|---|---|
| Authorizes | Identity (who you are) | Specific action (what you can do) |
| Scope | Broad (all permitted actions) | Narrow (one action, one time) |
| Time limit | Long-lived (months/years) | Seconds to minutes (TTL) |
| Parameters | None | Amount, recipient, target constrained |
| Post-execution check | None | Verification Engine confirms compliance |
| Compromise impact | Full access until revoked | One action, already expired |
| Audit chain | Who authenticated | Full intent → policy → approval → execution chain |
| Tamper evidence | None | HMAC-SHA256 signature |
Warrant Lifecycle#
1. INTENT → Agent submits action request
2. EVALUATE → Policy Engine checks against rules
3. TIER → Risk tier assigned (T0/T1/T2)
4. APPROVE → Operator(s) approve (if T1/T2)
5. ISSUE → Warrant Authority creates signed warrant
- Scope locked to exact parameters
- TTL set (300s default, configurable)
- Constraints attached (max amount, allowed targets)
- HMAC-SHA256 signature computed
6. EXECUTE → Execution Router validates warrant, runs action
7. VERIFY → Verification Engine checks:
- Did action match warrant scope?
- Was amount within constraints?
- Was target in allowed list?
- Was execution within TTL?
8. ARCHIVE → Warrant + verification result → immutable audit trailSecurity Properties#
Vienna warrants provide five security guarantees:
- Non-repudiation — Every warrant records who approved it and why. The chain of custody is immutable.
- Scope enforcement — A warrant for $75,000 to vendor-456 cannot be used to send $750,000 to vendor-789.
- Temporal constraint — Warrants expire. A 300-second TTL means the window for execution is minutes, not months.
- Tamper evidence — HMAC-SHA256 signature means any modification to the warrant payload invalidates it.
- Post-execution verification — Even if execution occurs, the Verification Engine confirms the action matched the warrant. Mismatches trigger alerts.
Implementation#
Warrants use HMAC-SHA256 with a server-side signing key. The signature covers the entire warrant payload (minus the signature field itself).
import crypto from "crypto";
function signWarrant(warrant: Omit<Warrant, "signature">, signingKey: string): string {
const payload = JSON.stringify(warrant, Object.keys(warrant).sort());
return "hmac-sha256:" + crypto
.createHmac("sha256", signingKey)
.update(payload)
.digest("hex");
}
function verifyWarrant(warrant: Warrant, signingKey: string): boolean {
const { signature, ...payload } = warrant;
const expected = signWarrant(payload, signingKey);
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Industry Use Cases#
Financial Services
Wire transfer warrants constrain amount, recipient, currency. Multi-party T2 approval for high-value. 60-second TTL on execution. Post-verification confirms exact amount transferred.
Healthcare
PHI access warrants scope to specific patient ID and data fields. HIPAA-compliant audit trail. 30-second TTL. Verification confirms only authorized fields were accessed.
Legal
Court filing warrants constrain to specific case number, document type, and filing deadline. Dual attorney-supervisor approval. Verification confirms correct court and case.
DevOps
Deploy warrants scope to specific service, environment, and version. Rollback constraints enabled. After-hours escalation. Verification confirms deployment target matched.
Security
#Authentication#
Vienna OS supports three authentication mechanisms:
- Session-based (operators) — Login with username/password, receive session cookie. Used by console UI.
- API key (agents) — Bearer token in Authorization header. Scoped per agent with configurable permissions.
- mTLS (enterprise) — Mutual TLS for agent identity verification. Certificate-based, no shared secrets.
Authorization#
Authorization in Vienna is two-layered: identity authorization (who can access the API) and action authorization (what they can do, enforced by warrants). Even an authenticated agent with valid API keys cannot execute an action without a warrant.
Encryption#
- In transit — TLS 1.3 enforced on all connections
- At rest — Database encryption via provider (Neon/RDS). Sensitive config fields encrypted with AES-256-GCM.
- Warrant signatures — HMAC-SHA256 with rotating server-side keys
Audit Trail Integrity#
The audit trail is append-only. Entries cannot be modified or deleted. Each entry includes a hash of the previous entry, creating a tamper-evident chain (similar to blockchain but without consensus overhead). Any gap or modification in the chain is detectable.
Incident Response#
When Vienna detects a security event (signature mismatch, scope violation, unauthorized access attempt):
- Alert generated with severity level (info/warning/critical)
- Agent automatically suspended if critical
- Integration adapters notified (Slack, email, webhook)
- Full context captured in audit trail
- Operator review required to reactivate suspended agents
Compliance#
Self-Hosting
#Vienna OS can be self-hosted for teams that need on-premise deployment, air-gapped environments, or custom infrastructure.
Docker#
# Pull the image
docker pull ghcr.io/risk-ai/vienna-os:latest
# Run with environment variables
docker run -d \
--name vienna-os \
-p 8080:8080 \
-e VIENNA_ENV=prod \
-e VIENNA_SECRET_KEY=your-secret-key \
-e POSTGRES_URL=postgresql://user:pass@host:5432/vienna \
-e VIENNA_SIMULATION=false \
ghcr.io/risk-ai/vienna-os:latest
# Verify
curl http://localhost:8080/health
# {"status":"ok"}Environment Variables#
| Variable | Required | Default | Description |
|---|---|---|---|
| VIENNA_SECRET_KEY | Yes | — | Server signing key for warrants and sessions |
| POSTGRES_URL | Yes | — | PostgreSQL connection string |
| VIENNA_ENV | No | prod | Environment: prod, staging, test |
| PORT | No | 8080 | Server listen port |
| VIENNA_SIMULATION | No | true | Enable simulation engine |
| VIENNA_LOG_LEVEL | No | info | Logging level: debug, info, warn, error |
| VIENNA_CORS_ORIGIN | No | * | Allowed CORS origins |
| VIENNA_SESSION_TTL | No | 86400 | Session TTL in seconds (24h) |
| VIENNA_WARRANT_TTL | No | 300 | Default warrant TTL in seconds (5min) |
| VIENNA_MAX_AGENTS | No | 100 | Maximum registered agents |
PostgreSQL Setup#
# Create database
createdb vienna
# Run migrations (auto-runs on first boot, or manually)
cd apps/console/server
for f in src/db/migrations/*.sql; do
psql vienna < "$f"
doneFly.io#
# Install flyctl
curl -L https://fly.io/install.sh | sh
# Launch (first time)
fly launch --name vienna-os --region iad
# Set secrets
fly secrets set VIENNA_SECRET_KEY=your-secret-key
fly secrets set POSTGRES_URL=your-connection-string
# Deploy
fly deploy
# Scale
fly scale count 2 # Run 2 instances for HAKubernetes#
apiVersion: apps/v1
kind: Deployment
metadata:
name: vienna-os
labels:
app: vienna-os
spec:
replicas: 2
selector:
matchLabels:
app: vienna-os
template:
metadata:
labels:
app: vienna-os
spec:
containers:
- name: vienna-os
image: ghcr.io/risk-ai/vienna-os:latest
ports:
- containerPort: 8080
env:
- name: VIENNA_SECRET_KEY
valueFrom:
secretKeyRef:
name: vienna-secrets
key: secret-key
- name: POSTGRES_URL
valueFrom:
secretKeyRef:
name: vienna-secrets
key: postgres-url
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: vienna-os
spec:
selector:
app: vienna-os
ports:
- port: 80
targetPort: 8080
type: LoadBalancerReady to govern your agents?
Start with the free tier. No credit card required.