Skip to content

wiqilee/Vettra

Repository files navigation

Vettra

AI agents should earn trust, not assume it.

Live Demo Watch on YouTube Read on Medium
Vercel Google Cloud Auth0 Okta

Built for the Authorized to Act: AI Agents with Auth0 Hackathon 2026 Build an agentic AI application using Auth0 for AI Agents Token Vault.

Author: Wiqi Lee  |  Follow on X


Table of Contents


Overview

Most AI agents today operate with a blank check. You hand them credentials, they act on your behalf, and somewhere between "helpful automation" and "catastrophic mistake" there is no checkpoint. Vettra closes that gap.

Vettra is a real-time authorization middleware for AI agents. It intercepts high-risk actions before they execute, surfaces them to the user with full context, and waits for an explicit decision. Built on Auth0 Token Vault, it ensures credentials are never exposed to the agent directly, every action is scoped to the minimum necessary permission, and every decision leaves an immutable audit trail.


The Problem



AI agents are becoming the new automation layer across software. They read your emails, manage your calendar, push code, file tickets, move money. The tooling to build them has matured fast. The tooling to control them has not.

Today, when you give an agent access to your Gmail, it gets access to all of Gmail. When it connects to your database, it can read and write anything. When it calls an external API, it uses your credentials with your full scope. There is no native concept of bounded permission, no moment where a human says "yes, that specific action, right now, is approved."

This creates three compounding risks:

Credential exposure. Agent frameworks store API keys and OAuth tokens in environment variables, config files, or memory. One compromised agent means every integrated service is compromised.

Scope creep. Agents accumulate permissions over time. An agent that "just needed read access" ends up with write, delete, and admin. Nobody audits this because there is no interface to audit it.

Silent failure at scale. When an agent makes a mistake at 3am with no human in the loop, the blast radius is whatever permissions it had. By the time anyone notices, the action has propagated.

These are not hypothetical. They are the documented failure modes of every major AI agent deployment to date. And they are going to get dramatically worse as agents become more autonomous.


The Solution



Vettra sits between your AI agent and the outside world. Every action the agent wants to take passes through a policy engine. Low-risk actions (reading data, fetching context) proceed automatically. Medium-risk actions (sending messages, creating records) trigger a real-time approval request. High-risk actions (deleting data, making payments, modifying permissions) require step-up authentication before they can even be presented for approval.

The agent never holds credentials. Auth0 Token Vault stores all secrets and releases them only after Vettra confirms that the action has been approved, the scope matches, and the identity of the approver is verified. If any of those checks fail, the token is never issued and the action never runs.

The result is an agent that operates with the minimum possible privilege, in a loop that keeps a human genuinely informed and in control, with a full record of every decision made.


What Makes Vettra Different

Most agent security tools are perimeter-based. They try to prevent bad actors from accessing your agent. Vettra assumes the agent itself may need to be constrained, and builds the control surface accordingly.

Most authorization systems are static. Permissions are configured once and never revisited. Vettra treats authorization as a live, contextual decision made at the moment of action, with full awareness of what the action is, who is affected, and what the agent has already done in this session.

Most agent frameworks leak credentials by design. API keys flow through environment variables into agent memory into tool calls. Vettra breaks this chain entirely. The agent requests a token, Vettra evaluates the request, Auth0 Token Vault releases a scoped, time-limited credential if approved. The agent never sees the underlying secret.

The Rust WASM cryptographic layer is unique in this space. Before any token is released from Token Vault, Vettra runs a WebAssembly verification module that checks the integrity of the action request against a signed policy bundle. This verification happens client-side, is tamper-evident, and adds a hardware-grade trust anchor that no pure-Python or pure-JavaScript agent framework can match.


Architecture

graph TB
    subgraph Frontend["Vercel (Frontend)"]
        REACT["React 18 + Vite 5<br/>Dashboard UI"]
        WS_CLIENT["WebSocket Client<br/>Auto-reconnect"]
    end

    subgraph Backend["Google Cloud Run (Backend · asia-southeast2)"]
        direction TB
        INTERCEPT["Interceptor<br/>@protect Decorator"]
        POLICY["Policy Engine<br/>Risk Scoring 0-100"]
        WASM["Rust WASM Verifier<br/>Policy Integrity Check"]
        WEBSOCKET["WebSocket Server<br/>Live Approval Push"]
        AUDIT["Audit Logger<br/>Hash-chained SHA-256"]
        SESSION["Session Manager<br/>Pre-authorization"]
        TV_CLIENT["Token Vault Client<br/>RFC 8693 Exchange"]
    end

    subgraph Auth0["Auth0"]
        TOKEN_VAULT["Token Vault<br/>Scoped Credential Issuance"]
        STEPUP["Step-up Auth + MFA"]
    end

    subgraph External["External Services"]
        GMAIL["Gmail"]
        GITHUB["GitHub"]
        STRIPE["Stripe"]
        SLACK["Slack"]
    end

    DB[("SQLite / Cloud SQL<br/>Audit + Actions + Sessions")]

    USER["User / Browser"] -->|HTTPS + WebSocket| REACT
    REACT -->|"/api/* proxy"| INTERCEPT
    WS_CLIENT ---|real-time| WEBSOCKET
    INTERCEPT --> POLICY
    POLICY --> WASM
    WASM --> WEBSOCKET
    POLICY --> AUDIT
    SESSION --> STEPUP
    TV_CLIENT -->|Token Exchange| TOKEN_VAULT
    TOKEN_VAULT -->|Scoped Token| GMAIL
    TOKEN_VAULT -->|Scoped Token| GITHUB
    TOKEN_VAULT -->|Scoped Token| STRIPE
    TOKEN_VAULT -->|Scoped Token| SLACK
    AUDIT --> DB
    TV_CLIENT --> DB
Loading

Deployment Architecture

graph LR
    subgraph GitHub["GitHub Repository"]
        PUSH["Push to main"]
    end

    subgraph Vercel["Vercel"]
        FE["React SPA<br/>CDN + Edge<br/>vettra-eight.vercel.app"]
    end

    subgraph CloudRun["Google Cloud Run"]
        BE["FastAPI + WASM<br/>asia-southeast2<br/>Auto-scaling 0→3<br/>512Mi memory"]
    end

    subgraph Auth0["Auth0"]
        TV["Token Vault<br/>OAuth + MFA"]
    end

    DB[("SQLite /<br/>Cloud SQL")]

    PUSH -->|auto-deploy| FE
    PUSH -->|manual deploy| BE
    FE -->|"/api/* rewrite"| BE
    BE -->|SDK| TV
    BE --> DB
Loading

Backend Infrastructure (Google Cloud Run)

Google Cloud Run

Vettra's backend runs on Google Cloud Run, a fully managed serverless container platform that scales from zero to handle production traffic without infrastructure management.

Live Backend

Endpoint URL Status
Root https://vettra-backend-810928102951.asia-southeast2.run.app {"name":"Vettra","version":"0.1.0","status":"running"}
Health https://vettra-backend-810928102951.asia-southeast2.run.app/health {"status":"healthy"}
API Docs https://vettra-backend-810928102951.asia-southeast2.run.app/docs Swagger UI

Why Google Cloud Run

  1. Serverless auto-scaling (0 to 3 instances). Zero cost when idle, instant scale when traffic arrives. For a hackathon demo that gets periodic bursts of judge traffic, this is ideal: no idle server burning credits.

  2. Container-native deployment. The backend ships as a Docker container (backend/Dockerfile) with all dependencies baked in: FastAPI, wasmtime (for Rust WASM execution), SQLAlchemy, Auth0 SDK, and 12+ other packages. gcloud run deploy --source . builds and deploys in one command.

  3. Region: asia-southeast2 (Jakarta). Chosen for low latency to the primary development location. Cloud Run supports 30+ regions; production deployments can be multi-region with Cloud Run's traffic splitting.

  4. Managed HTTPS and TLS. Cloud Run provisions TLS certificates automatically. No cert management, no nginx config, no Let's Encrypt renewal scripts. The backend is immediately accessible over HTTPS with a Google-managed certificate.

  5. WebSocket support. Cloud Run supports long-lived WebSocket connections (up to 60 minutes), which Vettra uses for real-time approval push. The WebSocket endpoint runs at wss://vettra-backend-810928102951.asia-southeast2.run.app/ws/{user_id}.

  6. Environment variable injection. All secrets (Auth0 credentials, signing keys, database URLs) are injected via Cloud Run environment variables, never baked into the container image. This aligns with Vettra's zero-credential-exposure philosophy.

Cloud Run Configuration

Service:          vettra-backend
Region:           asia-southeast2 (Jakarta)
Platform:         managed (fully serverless)
Authentication:   allow-unauthenticated (public API)
Memory:           512Mi per instance
Min instances:    0 (scale to zero)
Max instances:    3 (cost-bounded)
Port:             8080
Container:        Python 3.12 + FastAPI + wasmtime

Environment Variables (Cloud Run)

APP_NAME=Vettra
ENVIRONMENT=production
PORT=8080
DATABASE_URL=sqlite+aiosqlite:///./vettra.db
CORS_ORIGINS=https://vettra-eight.vercel.app
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=your_client_id
AUTH0_CLIENT_SECRET=your_client_secret
AUTH0_AUDIENCE=https://your-api-identifier
AUDIT_SIGNING_KEY=<generated-hex-key>
SESSION_SECRET=<generated-hex-key>

How Frontend Connects to Cloud Run

The frontend (Vercel) proxies all /api/* requests to Cloud Run via vercel.json rewrites:

{
  "rewrites": [
    {
      "source": "/api/:path*",
      "destination": "https://vettra-backend-810928102951.asia-southeast2.run.app/api/:path*"
    },
    {
      "source": "/(.*)",
      "destination": "/index.html"
    }
  ]
}

This means the frontend never exposes the Cloud Run URL to the browser. All API traffic appears to come from vettra-eight.vercel.app/api/*. CORS is configured on the backend to only accept requests from the Vercel domain.

Note on WebSocket: Vercel's edge network does not proxy WebSocket connections. The frontend connects directly to the Cloud Run WebSocket endpoint at wss://vettra-backend-810928102951.asia-southeast2.run.app/ws/{user_id} for real-time approval push and event streaming.


Token Vault Credential Lifecycle

flowchart TD
    A["AI Agent Requests Action"] --> B["Vettra Intercepts<br/>Parse · Classify · Verify (WASM)"]
    B --> C{"Risk Classification<br/>(0-100)"}

    C -->|"0-19"| LOW["LOW<br/>Auto-approve"]
    C -->|"20-54"| MED["MEDIUM<br/>Human Approval Required"]
    C -->|"55-100"| HIGH["HIGH<br/>Step-up Auth + Approval"]

    LOW --> EXEC
    MED --> APPROVE["User Approves on Dashboard"]
    HIGH -->|MFA Challenge| APPROVE

    APPROVE --> TV["Auth0 Token Vault Issues Credential<br/>Scoped · Time-limited · Single-use<br/>(RFC 8693 Exchange)"]
    TV --> EXEC["Agent Executes Action<br/>Token Expires Automatically"]

    AUDIT["Hash-chained Audit Trail<br/>(SHA-256 · HMAC-signed)"]
    B -.->|log| AUDIT
    APPROVE -.->|log| AUDIT
    TV -.->|log| AUDIT
    EXEC -.->|log| AUDIT

    style LOW fill:#d1fae5,stroke:#16a34a,color:#000
    style MED fill:#fef9c3,stroke:#ca8a04,color:#000
    style HIGH fill:#fecaca,stroke:#dc2626,color:#000
    style TV fill:#fef9c3,stroke:#ca8a04,color:#000,stroke-width:2px
    style AUDIT fill:#f3f4f6,stroke:#9ca3af,color:#555,stroke-dasharray: 5 5
Loading



Project Structure

Vettra/
│
├── README.md                               # This file
├── FINDINGS.md                             # 4 ecosystem findings (hackathon deliverable)
├── LICENSE                                 # MIT License
├── .gitignore                              # Python, Node, Rust, IDE, Docker
├── .env.example                            # Environment variables template
├── docker-compose.yml                      # PostgreSQL + backend containers
│
├── docs/
│   └── images/
│       ├── vettra_github_banner.svg        # Animated SVG banner (README header)
│       ├── architecture_overview.png       # Architecture diagram (high-res)
│       ├── tech_stack.png                  # Tech stack diagram (high-res)
│       ├── token_vault_flow.png            # Token Vault credential flow (high-res)
│
├── .github/
│   └── workflows/
│       └── ci.yml                          # GitHub Actions: backend tests + frontend build check
│
├── backend/                                # FastAPI Backend (Python)
│   ├── main.py                             # App entry point: wires all services
│   ├── config.py                           # Environment config + Auth0 settings
│   ├── database.py                         # PostgreSQL models + async SQLAlchemy
│   ├── requirements.txt                    # Python dependencies
│   ├── .env.example                        # Backend-specific env template
│   ├── Dockerfile                          # Container build
│   │
│   ├── app/
│   │   ├── models/
│   │   │   ├── action.py                   # ActionBundle, RiskAssessment, PolicyVerifyResult
│   │   │   ├── audit.py                    # AuditEntry, AuditChainStatus
│   │   │   └── session.py                  # Session, PreAuth, PolicyRule
│   │   │
│   │   ├── middleware/
│   │   │   └── interceptor.py              # Core: intercept → parse → classify → route
│   │   │
│   │   ├── services/
│   │   │   ├── policy_engine.py            # Policy evaluation via WASM bridge
│   │   │   ├── session_manager.py          # Pre-authorization (Finding #2 workaround)
│   │   │   ├── audit_logger.py             # Hash-chained SHA-256 log (Finding #3)
│   │   │   └── auth0_client.py             # Token Vault + step-up auth client
│   │   │
│   │   ├── wasm/
│   │   │   └── bridge.py                   # Python ↔ WASM bridge (auto-fallback to Python)
│   │   │
│   │   ├── websocket/
│   │   │   ├── manager.py                  # WS connections, approval push, timeout
│   │   │   └── route.py                    # /ws/{user_id} endpoint
│   │   │
│   │   └── routes/
│   │       ├── actions.py                  # POST /api/actions/intercept, /approve, /pending
│   │       ├── approvals.py                # Sessions, pre-auth, step-up trigger/verify
│   │       ├── audit.py                    # GET /api/audit/entries, /verify
│   │       └── inventory.py                # Permissions CRUD + scope translation
│   │
│   └── tests/
│       └── test_vettra.py                  # Risk, audit chain, policy, parsing tests
│
├── frontend/                               # React Dashboard (Vite)
│   ├── index.html                          # HTML entry point
│   ├── package.json                        # Node dependencies
│   ├── vercel.json                         # Vercel deployment config (API proxy → Cloud Run)
│   ├── vite.config.js                      # Vite config + API proxy
│   └── src/
│       ├── main.jsx                        # React root
│       ├── App.jsx                         # Sidebar nav + page router + WS status
│       ├── hooks/
│       │   └── useWebSocket.js             # WebSocket hook with auto-reconnect
│       ├── pages/
│       │   ├── Overview.jsx                # Stats: pending, chain status, sessions
│       │   ├── ApprovalQueue.jsx           # Real-time approval cards + countdown
│       │   ├── AuditLog.jsx                # Hash chain viewer + expandable entries
│       │   ├── Inventory.jsx               # Scope visualizer + one-click revoke
│       │   └── ThreatSim.jsx               # 5 test scenarios + results
│       ├── utils/
│       │   └── api.js                      # REST API client
│       └── styles/
│           └── global.css                  # Dark theme CSS
│
├── wasm-verifier/                          # Rust WASM Policy Verifier (optional build)
│   ├── Cargo.toml                          # Rust dependencies
│   ├── README.md                           # Build instructions
│   └── src/
│       ├── lib.rs                          # WASM-exported entry points
│       ├── action.rs                       # ActionBundle schema + framework adapters
│       ├── risk.rs                         # Risk classification engine
│       ├── policy.rs                       # Policy verification + integrity check
│       └── audit.rs                        # Hash-chained audit entry signing
│
├── demo-agent/                             # Demo Agent + @protect decorator
│   └── demo_agent.py                       # 5 escalation scenarios + prompt injection
│
└── scripts/
    └── setup.sh                            # Automated setup script

CI/CD: GitHub Actions runs backend tests and frontend build checks on every push (.github/workflows/ci.yml). Frontend auto-deploys to Vercel via GitHub integration. Backend deploys to Google Cloud Run via gcloud run deploy.


Tech Stack

Layer Technology Purpose
Frontend React 18 + Vite 5 Approval dashboard, scope visualizer, audit viewer
Frontend Hosting Vercel (Edge CDN) React SPA with auto-deploy on push, /api/* proxy to Cloud Run
Backend FastAPI + Python 3.12 Policy engine, action interceptor, session manager
Backend Hosting Google Cloud Run (asia-southeast2) Serverless container with auto-scaling 0→3, managed HTTPS
Crypto Rust → WebAssembly (wasmtime) Policy verification, audit signing, tamper detection
Auth Auth0 Token Vault Credential isolation, scoped token issuance, step-up auth
Database SQLite (demo) / PostgreSQL 16 (production) Actions, audit log, sessions, permission inventory
Real-time WebSocket (native, via Cloud Run) Live approval push, step-up challenge delivery
Demo Python + @protect decorator 5 escalation scenarios + prompt injection defense
Container Docker Compose PostgreSQL + backend orchestration (local dev)
CI/CD GitHub Actions Automated backend tests + frontend build checks



Prerequisites

Before running Vettra, make sure you have these installed:

Tool Version Check Install
Python 3.10+ python --version python.org
Node.js 18+ node --version nodejs.org
npm 9+ npm --version Comes with Node.js
Docker 20+ docker --version docker.com
Git 2.0+ git --version git-scm.com
Rust (optional) 1.75+ rustc --version rustup.rs

Note: Rust is only needed if you want to compile the WASM verifier from source. Without Rust, the backend automatically uses an identical Python fallback. All features work the same.


Installation & Setup (VS Code)

Step 1: Clone the repository

Open VS Code Terminal (Ctrl+`` or Cmd+`` ) and run:

git clone https://github.com/wiqilee/Vettra.git
cd Vettra

Step 2: Create environment file

cp .env.example .env

Then open .env in VS Code and fill in your Auth0 credentials:

AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=your_client_id
AUTH0_CLIENT_SECRET=your_client_secret
AUTH0_AUDIENCE=https://your-api-identifier

Get Auth0 credentials: Sign up at auth0.com/signup, create a new API application, and copy the values.

Step 3: Generate security keys

python -c "import secrets; print('AUDIT_SIGNING_KEY=' + secrets.token_hex(32))"
python -c "import secrets; print('SESSION_SECRET=' + secrets.token_hex(32))"

Copy the output and paste into your .env file, replacing the dev_... placeholder values.

Step 4: Start PostgreSQL

docker compose up -d db

Verify it's running:

docker compose ps

You should see vettra-db-1 with status healthy.

Step 5: Install backend dependencies

cd backend
pip install -r requirements.txt

This installs: FastAPI, SQLAlchemy, asyncpg, Auth0 SDK, wasmtime, cryptography, and 12 other packages.

Step 6: Install frontend dependencies

Open a new terminal in VS Code (Ctrl+Shift+`` ):

cd frontend
npm install

This installs: React, React DOM, React Router, Vite, Lucide React icons.


Running the Application

You need 3 terminals in VS Code. Use Ctrl+Shift+`` to open new ones.

Terminal 1: Backend API (port 8000)

cd backend
uvicorn main:app --reload

You should see:

══════════════════════════════════════════════════
             VETTRA — Starting up
══════════════════════════════════════════════════
            WASM: Python fallback
         Auth0: your-tenant.auth0.com
               VETTRA — Ready
══════════════════════════════════════════════════

Terminal 2: Frontend Dashboard (port 3000)

cd frontend
npm run dev

You should see:

  VITE v5.4.3  ready in 300ms

  ➜  Local:   http://localhost:3000/

Terminal 3: Demo Agent (optional)

cd demo-agent
pip install httpx
python demo_agent.py

This runs 5 escalation scenarios through the interceptor and shows results in the dashboard.

Access Points

Service URL Description
Dashboard (Local) http://localhost:3000 Main interface for approval queue, audit logs, scope visualization, and threat simulation
Dashboard (Live) https://vettra-eight.vercel.app Production deployment on Vercel
API (Local) http://localhost:8000/docs Interactive Swagger UI (local development)
API (Live) https://vettra-backend-810928102951.asia-southeast2.run.app/docs Swagger UI on Cloud Run
Health (Live) https://vettra-backend-810928102951.asia-southeast2.run.app/health Backend health check
WebSocket (Local) ws://localhost:8000/ws/{user_id} Real-time approval and event stream
WebSocket (Live) wss://vettra-backend-810928102951.asia-southeast2.run.app/ws/{user_id} Production WebSocket

Running Tests

cd backend
pytest tests/ -v

Expected output:

tests/test_vettra.py::TestRiskClassification::test_read_is_low PASSED
tests/test_vettra.py::TestRiskClassification::test_financial_is_high PASSED
tests/test_vettra.py::TestRiskClassification::test_write_reversible_is_medium PASSED
tests/test_vettra.py::TestRiskClassification::test_delete_irreversible_is_high PASSED
tests/test_vettra.py::TestRiskClassification::test_deterministic PASSED
tests/test_vettra.py::TestActionParsing::test_gmail_send PASSED
tests/test_vettra.py::TestActionParsing::test_delete PASSED
tests/test_vettra.py::TestActionParsing::test_financial PASSED
tests/test_vettra.py::TestAuditChain::test_create_entry PASSED
tests/test_vettra.py::TestAuditChain::test_chain_valid PASSED
tests/test_vettra.py::TestAuditChain::test_chain_broken PASSED
tests/test_vettra.py::TestPolicyVerification::test_read_allowed PASSED
tests/test_vettra.py::TestPolicyVerification::test_delete_denied PASSED
tests/test_vettra.py::TestPolicyVerification::test_tampered_rejected PASSED

API Documentation

Once the backend is running, open http://localhost:8000/docs for the interactive Swagger UI where you can test every endpoint directly.

Live API docs: https://vettra-backend-810928102951.asia-southeast2.run.app/docs





API Endpoints

Actions

Method Endpoint Description
POST /api/actions/intercept Submit an agent tool call for interception
POST /api/actions/approve Approve or deny a pending action
GET /api/actions/pending List all pending actions
GET /api/actions/{action_id} Get details of a specific action

Approvals & Sessions

Method Endpoint Description
POST /api/approvals/sessions Create a new agent session
POST /api/approvals/sessions/pre-authorize Set risk tier pre-authorization
GET /api/approvals/sessions List active sessions
GET /api/approvals/sessions/{id} Get session details
DELETE /api/approvals/sessions/{id} Revoke a session
POST /api/approvals/stepup/trigger Trigger step-up auth challenge
POST /api/approvals/stepup/verify Verify step-up auth code

Audit Log

Method Endpoint Description
GET /api/audit/entries Get audit entries (paginated, filterable)
GET /api/audit/verify Verify audit chain integrity
GET /api/audit/action/{id} Get full audit trail for an action

Permission Inventory

Method Endpoint Description
GET /api/inventory/permissions List all active permissions
POST /api/inventory/permissions Grant a permission
DELETE /api/inventory/permissions/{id} Revoke a permission
GET /api/inventory/scope-translate Translate scope strings to plain language

WebSocket

Endpoint Description
ws://localhost:8000/ws/{user_id} Real-time approval channel (local development)
wss://vettra-backend-810928102951.asia-southeast2.run.app/ws/{user_id} Secure real-time channel (production on Cloud Run)
GET /ws/status WebSocket connection status endpoint

Key Features

Real-time Approval Queue

Every medium and high-risk action appears in the dashboard with full context: what the agent wants to do, which resource it targets, the risk score, and a countdown timer. Approve or deny with one click.

WASM Policy Verification

Signed policy bundles are verified in a Rust WASM sandbox before any action is evaluated. Tampered policies are rejected before they can influence decisions.

Risk Classification Engine

Every action is scored 0-100 across four factors:

Factor Weight Example
Action type 0-40 read=0, write=20, delete=35, financial/auth=40
Reversibility 0-25 reversible=0, unknown=15, irreversible=25
Blast radius 0-20 single=0, multiple=12, system=20
Sensitivity 0-15 public=0, internal=5, confidential=10, regulated=15

Score thresholds: LOW (0-19) → auto-approve | MEDIUM (20-54) → human approval | HIGH (55-100) → step-up auth + approval

Override rules: Financial and Auth actions are always HIGH regardless of score.

Hash-Chained Audit Log

Every entry is SHA-256 hash-chained to the previous entry and HMAC-signed by a deployment key stored in Auth0. Retroactive tampering invalidates all subsequent entries and triggers a dashboard alert.

Scope Visualizer

OAuth scope strings are translated to human-readable descriptions. Users see "Send emails on your behalf" instead of https://www.googleapis.com/auth/gmail.modify.

Session Pre-Authorization

Users can set "approve all MEDIUM actions for 30 minutes" with a single MFA check, reducing step-up auth latency without sacrificing control. Addresses Finding #2.


Demo Agent & @protect Decorator

The @protect decorator wraps any agent tool function to route it through Vettra's interceptor:

from demo_agent import protect

@protect(framework="langchain")
async def gmail_send_message(to, subject, body):
    # This code only runs if Vettra approves
    return send_email(to, subject, body)

When the agent calls gmail_send_message(), Vettra intercepts it, classifies risk as MEDIUM (write + irreversible + email), and pushes an approval request to the dashboard. The function only executes if a human approves.


Threat Simulation Scenarios

The dashboard includes a built-in Threat Simulation page. You can also run them from the terminal:

cd demo-agent
python demo_agent.py
# Scenario Risk Expected Result
1 Read user profile LOW ✅ Auto-approved
2 Send email MEDIUM ⏳ Queued for approval
3 Delete GitHub repository HIGH 🔐 Step-up auth required
4 Charge $500 via Stripe HIGH 🔐 Step-up auth required (financial)
5 Grant admin permissions HIGH ⛔ Denied by policy (auth escalation)

WASM Verifier (Optional Rust Build)

The backend works without Rust. It automatically uses a Python fallback with identical logic. To compile the Rust WASM module for production:

# Install Rust (if not installed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-unknown-unknown

# Build
cd wasm-verifier
cargo build --target wasm32-unknown-unknown --release

# Run Rust tests
cargo test

The compiled module appears at wasm-verifier/target/wasm32-unknown-unknown/release/vettra_verifier.wasm. The backend auto-detects it on startup.


Docker Deployment

Run the full stack with Docker:

# Start everything
docker compose up -d

# Check status
docker compose ps

# View logs
docker compose logs -f backend

# Stop
docker compose down
Service Port Description
db 5432 PostgreSQL 16
backend 8000 FastAPI + WebSocket

Frontend is served separately via npm run dev or deployed to Vercel.


Cloud Deployment

Vettra runs as a split deployment: frontend on Vercel, backend on Google Cloud Run.

flowchart TD
    GH["GitHub<br/>(push to main)"] -->|auto-deploy| VERCEL
    GH -->|manual deploy| CLOUDRUN

    subgraph VERCEL["Vercel (auto-deploy frontend)"]
        FE["vettra-eight.vercel.app<br/>/api/* rewrites to Cloud Run"]
    end

    subgraph CLOUDRUN["Google Cloud Run (manual deploy)"]
        BE["vettra-backend-810928102951<br/>asia-southeast2.run.app"]
        FEATURES["FastAPI + WASM/Python fallback<br/>SQLite (demo) · Cloud SQL (prod)<br/>Auto-scaling 0→3 · 512Mi<br/>Managed HTTPS + TLS"]
    end

    subgraph AUTH0["Auth0"]
        TV["Token Vault Integration"]
    end

    BE --> FEATURES
    BE -->|SDK| TV
Loading

Deploy Frontend to Vercel

  1. Connect your GitHub repository to Vercel
  2. Set root directory to frontend
  3. Vercel auto-detects Vite and deploys on every push to main

Deploy Backend to Cloud Run

cd backend

gcloud run deploy vettra-backend \
  --source . \
  --region asia-southeast2 \
  --platform managed \
  --allow-unauthenticated \
  --set-env-vars "APP_NAME=Vettra,ENVIRONMENT=production,PORT=8080" \
  --set-env-vars "DATABASE_URL=sqlite+aiosqlite:///./vettra.db" \
  --set-env-vars "CORS_ORIGINS=https://vettra-eight.vercel.app" \
  --min-instances 0 \
  --max-instances 3 \
  --memory 512Mi

After deploying, update frontend/vercel.json with the Cloud Run URL to connect frontend to backend.

Important: The Cloud Run region is asia-southeast2 (Jakarta). Make sure vercel.json rewrites point to the correct Cloud Run URL including the region.










Production Readiness

Vettra is not a toy. The demo uses mock data to show the authorization flow, but the architecture is designed for real-world deployment. No code changes are required to go from demo to production, only configuration.

What the demo shows

The demo agent (demo_agent.py) simulates 5 escalation scenarios to demonstrate Vettra's risk classification, human-in-the-loop approval, and audit trail. All data flows through the same interceptor, policy engine, and WebSocket pipeline that a production agent would use.

What production requires

To connect Vettra to real external services (Gmail, GitHub, Stripe, Slack), you need one thing: Auth0 Token Vault.

# This is the only missing piece for production
AUTH0_TOKEN_VAULT_URL=https://your-tenant.auth0.com/oauth/token

Token Vault is not a separate service. It is a built-in Auth0 feature. To enable it:

  1. Go to Auth0 Dashboard → Applications → Your App → Advanced Settings → Grant Types
  2. Enable: Token Vault, Refresh Token, Authorization Code
  3. Go to Authentication → Social and connect providers (Google, GitHub, etc.)
  4. Set AUTH0_TOKEN_VAULT_URL=https://your-tenant.auth0.com/oauth/token in your .env

Auth0 Dual Application Setup

Vettra uses two Auth0 applications, each serving a distinct role in the authorization pipeline:

Application Type Purpose
Vettra Frontend Single Page Application (SPA) User login and session management on the React dashboard
Vettra Backend Regular Web Application (confidential client) Token Vault integration via OAuth 2.0 Token Exchange (RFC 8693)

Token Vault requires a confidential client with a valid client secret to perform token exchange. SPA applications are public clients and cannot enable the Token Vault grant type. The Regular Web Application provides the client credentials needed for the backend to request scoped, time-limited tokens from Token Vault on behalf of the user.

The frontend .env uses the SPA application's client ID. The backend .env uses the Regular Web Application's client ID and client secret. This separation ensures that sensitive credentials never reach the browser while maintaining a seamless user experience.

Once configured, the flow changes from:

Demo:  Agent → Vettra intercepts → mock approval → mock execution
Prod:  Agent → Vettra intercepts → real approval → Token Vault issues scoped credential → real API call

The agent code stays identical. The @protect decorator works the same way. The only difference is that Token Vault now issues real, scoped, time-limited OAuth tokens instead of mock responses.









Why this matters

Most hackathon projects are demos that cannot scale. Vettra is a middleware layer with a clear production path: swap mock mode for Token Vault credentials, deploy backend to Cloud Run, and every LangChain/LlamaIndex agent in your stack gets real-time authorization for free.


Judging Notes

Security Model: All credentials live in Auth0 Token Vault. The Rust WASM verifier enforces policy integrity before any token is released. Step-up authentication protects high-risk approvals. Scope is always minimum-necessary and time-bounded.

User Control: The dashboard gives users a complete real-time view of what their agent is doing, what it wants to do, and what it has already done. Every permission grant is explicit, reversible, and logged.

Technical Execution: Production-aware patterns throughout: hash-chained audit logs, WASM-sandboxed policy verification, WebSocket-based real-time approval, scoped token issuance. The Rust WASM integration is not a novelty; it is the trust anchor for the entire system.

Design: The approval UI is designed for speed and clarity under pressure. A user should be able to read an approval request, understand exactly what will happen, and make a confident decision in under 10 seconds. The scope visualizer exists specifically to make that possible.

Potential Impact: Every team building AI agents faces this problem today. Vettra is designed as a drop-in middleware layer that works with any LangChain-compatible agent without requiring changes to existing agent code. The target community is large, the problem is urgent, and the solution is deployable.

Insight Value: Vettra surfaces four concrete gaps in the current Auth0 and agent ecosystem (fully documented in FINDINGS.md): (1) there is no standard action bundle format for agent authorization requests; (2) step-up auth flows are not designed for the latency requirements of real-time agent approval; (3) audit log integrity is an unsolved problem in every current agent framework; (4) the authorization UX problem is harder than the security problem. These findings are built into the architecture and demonstrated live in the threat simulation.


Bonus Blog Post

Read on Medium

Vettra: What Happens When You Actually Try to Control an AI Agent

I gave an AI agent access to my email. Then I tried to figure out what I had agreed to.

I was testing a LangChain agent that could read my Gmail, send messages, and manage my calendar. Standard productivity assistant. Then I paused and asked myself: what can this agent actually do right now? It could read every email in my inbox. It could send emails to anyone, from my address, with any content. It could delete emails. It could forward sensitive threads to an external API. All of this was within the scope I had granted. I had not approved any of that.

I went looking for a way to scope this down. LangChain has no permission model. OpenAI function calling has no concept of scope. Every major agent framework treats tool access as binary: the agent either has a tool or it does not. No conditional access. No approval flow. No audit trail.

That felt like a problem worth solving. So I spent ten days building Vettra.

The full blog post covers:

  • Why every agent framework leaks credentials by design, and how Token Vault breaks that chain
  • The five-layer architecture: interceptor, risk engine, WASM verifier, approval dashboard, and hash-chained audit log
  • How Token Vault's OAuth 2.0 Token Exchange (RFC 8693) replaces static API keys with scoped, time-limited credentials
  • The complete infrastructure: React + Vercel on the frontend, FastAPI + Google Cloud Run on the backend, Rust WASM for cryptographic integrity
  • Five threat simulation scenarios that demonstrate every risk tier and authorization flow
  • Four ecosystem findings that surface real gaps in agent authorization (no standard action bundle format, step-up auth latency, unsolved audit integrity, and the UX problem that turned out to be harder than the security problem)
  • Concrete recommendations for Auth0 and the broader agent community

Read the full post on Medium →


License

MIT License. See LICENSE for details.


Built By

Wiqi Lee  |  Full Stack Engineer

X Medium Vercel


Acknowledgments

Built for the Authorized to Act: AI Agents with Auth0 Hackathon 2026 organized by Auth0, Okta and Devpost. Powered by Auth0 Token Vault, FastAPI, React, Google Cloud Run, and a Rust WASM module that takes policy integrity seriously.

About

AI agents act with a blank check. Vettra fixes that, real-time interception, human-in-the-loop approval, WASM-signed audit trails, and zero credential exposure. Built on Auth0 Token Vault.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors