io.github.vassiliylakhonin/vizier-guard
Provides VizierCrewAIToolGuard, which wraps CrewAI tools so each agent action (e.g. payments) is authorized by Vizier against amount limits and policy constraints before execution.
Provides a LangChain tool guard (VizierLangChainToolGuard) that wraps existing LangChain tools so each invocation is submitted to Vizier for a deterministic ALLOW/BLOCK/REVIEW decision before the tool executes, with configurable allowed actions and amount limits.
The same tool guard integrates with LangGraph agent graphs, enforcing Vizier authorization decisions on every tool call so graph-driven agents cannot execute protected side effects without approval.
Provides a TelegramHITLHandler that sends interactive approval buttons to a Telegram bot when a Vizier decision is REVIEW, letting a human operator approve or deny an agent action before it runs.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@io.github.vassiliylakhonin/vizier-guardscreen this action: purchase $500 from supplier.example.com"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Vizier
Deterministic Authorization & Non-Repudiation Audit Firewall for AI Agents.
Vizier is an ultra-fast, edge-native deterministic authorization and audit firewall for action-taking AI agents. Before an agent executes an external side effect (making a payment, executing code, modifying a database, dispatching messages, or deploying infrastructure), it submits the proposed action to Vizier.
ā” Try the Live Interactive Playground: https://vizier.vassiliy-lakhonin.workers.dev/playground
Test policy presets (ALLOW,BLOCK_AMOUNT,BLOCK_TARGET,SENSITIVE), inspect sub-25ms edge latency, and verify SHA-256 non-repudiation audit receipts in real time directly from your browser.
šļø Architecture
flowchart TD
subgraph Agents["AI Agent Runtimes"]
A1["Python Agent (LangChain / CrewAI / AutoGen)"]
A2["MCP Client (Claude / Cursor / Tools)"]
A3["TypeScript / Node.js Agent"]
end
subgraph Guards["Vizier Enforcement Boundary"]
G1["@vizier_guard / Python SDK"]
G2["@vizier/mcp-proxy CLI"]
G3["@vizier/sdk (TypeScript)"]
end
subgraph Kernel["Cloudflare Workers Global Edge"]
K["Vizier Deterministic Kernel (/v1/verify)"]
P["Policy Engine: Limits, Targets, Roles, Grants"]
D1["D1 Audit Ledger & Cryptographic Receipts"]
end
subgraph Targets["Protected External Side-Effects"]
T1["Payment / Financial APIs"]
T2["Database Writes & Deletions"]
T3["Worker / Infrastructure Deployments"]
T4["External Message Dispatch"]
end
A1 --> G1
A2 --> G2
A3 --> G3
G1 -->|"POST /v1/verify"| K
G2 -->|"POST /v1/verify"| K
G3 -->|"POST /v1/verify"| K
K --> P
P --> D1
G1 -.->|"Decision: ALLOW"| T1
G2 -.->|"Decision: ALLOW"| T2
G3 -.->|"Decision: ALLOW"| T3
P -.->|"Decision: BLOCK / REVIEW"| G1
P -.->|"Decision: BLOCK / REVIEW"| G2
P -.->|"Decision: BLOCK / REVIEW"| G3The decision path is strictly deterministic ā no non-deterministic LLMs in the critical decision loop. It checks delegated actions, principal identity, amount limits, targets, sensitive operations, and authenticated integration boundaries. Every response includes policy results and a tamper-proof SHA-256 canonical receipt hash.
Status: experimental v0.3.0, deployed on Cloudflare Workers edge. Since v0.3.0, authority can be proved rather than asserted: a principal signs a delegation grant, Vizier verifies it against a registered public key, and the receipt records authority provenance. Read the threat model before placing this service in an execution path.
Related MCP server: evav-gateway
š Public Surfaces
Edge Worker: https://vizier.vassiliy-lakhonin.workers.dev
Action Playground: https://vizier.vassiliy-lakhonin.workers.dev/playground
Live Field Reference: https://vizier.vassiliy-lakhonin.workers.dev/docs
OpenAPI 3.1 Contract: https://vizier.vassiliy-lakhonin.workers.dev/openapi.json
AI Discovery Catalog: https://vizier.vassiliy-lakhonin.workers.dev/.well-known/ai-catalog.json
MCP Server Manifest: https://vizier.vassiliy-lakhonin.workers.dev/.well-known/mcp.json
MCP Registry Entry:
io.github.vassiliylakhonin/vizierAgent Card: https://vizier.vassiliy-lakhonin.workers.dev/.well-known/agent-card.json
Public Key Set (JWKS): https://vizier.vassiliy-lakhonin.workers.dev/.well-known/jwks.json
š Quickstarts
1. Python SDK (vizier-guard)
Zero external dependencies (Python standard library only):
pip install vizier-guardfrom vizier import VizierClient, vizier_guard
client = VizierClient(
base_url="https://vizier.vassiliy-lakhonin.workers.dev",
api_key="your-api-key"
)
# Protect any function or tool:
@vizier_guard(
client=client,
action_type="purchase",
max_amount=500.0,
currency="USD",
allowed_targets=["supplier.example"]
)
def execute_order(amount: float, target: str):
# Runs ONLY if Vizier decision is ALLOW
return {"status": "success", "amount": amount}
execute_order(amount=450.0, target="supplier.example") # Allowed
execute_order(amount=1200.0, target="supplier.example") # Raises ActionBlockedErrorLangChain / LangGraph & CrewAI:
from vizier.integrations.langchain import VizierLangChainToolGuard
from vizier.integrations.crewai import VizierCrewAIToolGuard
# LangChain / LangGraph
safe_tool = VizierLangChainToolGuard(
tool=my_search_tool,
client=client,
allowed_actions=["search"],
max_amount=0.0
)
# CrewAI
safe_crew_tool = VizierCrewAIToolGuard(
tool=my_payment_tool,
client=client,
max_amount=250.0
)Human-in-the-Loop (Telegram / CLI / Webhooks) & Async:
from vizier import AsyncVizierClient, vizier_guard, TelegramHITLHandler
# Interactive approval buttons via Telegram Bot when decision is REVIEW
telegram_approver = TelegramHITLHandler(
bot_token=os.environ["TELEGRAM_BOT_TOKEN"],
chat_id=os.environ["TELEGRAM_CHAT_ID"]
)
@vizier_guard(
client=AsyncVizierClient(),
action_type="transfer_funds",
hitl_handler=telegram_approver
)
async def transfer(amount: float, target: str):
# Executes ONLY if human operator clicks [Approve] in Telegram
return await bank_api.send(amount, target)MCP Server for Claude Desktop & Cursor:
Equip Claude Desktop or Cursor with deterministic guardrails (vizier_screen_action, vizier_verify_receipt, vizier_check_policy):
{
"mcpServers": {
"vizier": {
"command": "uvx",
"args": ["vizier-guard", "mcp"],
"env": {
"VIZIER_BASE_URL": "https://vizier.vassiliy-lakhonin.workers.dev",
"VIZIER_API_KEY": "your-vizier-api-key"
}
}
}
}2. MCP Enforcement Proxy CLI
Wrap any local or remote MCP server with deterministic authorization:
npx @vizier/mcp-proxy \
--upstream http://localhost:3000/mcp \
--tools "query_db,execute_command,fetch_api" \
--vizier https://vizier.vassiliy-lakhonin.workers.dev \
--api-key $VIZIER_API_KEY3. TypeScript SDK (@vizier/sdk)
npm install @vizier/sdkimport { Vizier } from "@vizier/sdk";
const vizier = new Vizier({
baseUrl: "https://vizier.vassiliy-lakhonin.workers.dev",
apiKey: process.env.VIZIER_API_KEY,
});
const decision = await vizier.verify({
agent: { id: "agent-01", owner: "acme-corp" },
principal: { id: "acme-corp" },
action: {
type: "purchase",
target: "supplier.example",
parameters: { amount: 820, currency: "USD" }
},
authority: {
allowed_actions: ["purchase"],
constraints: { max_amount: 1000, currency: "USD" }
},
context: { source: "rest" }
});
if (decision.decision === "ALLOW") {
// Execute protected operation
}4. Direct HTTP / cURL
curl -sS https://vizier.vassiliy-lakhonin.workers.dev/v1/verify \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_KEY' \
-d '{
"agent": { "id": "agent-01", "owner": "acme" },
"principal": { "id": "acme" },
"action": {
"type": "purchase",
"target": "supplier.example",
"parameters": { "amount": 820, "currency": "USD" }
},
"authority": {
"allowed_actions": ["purchase"],
"constraints": { "max_amount": 1000, "currency": "USD" }
},
"context": { "source": "rest" }
}'š Agent Circuit Breaker & Loop Killer
Infinite tool loops and runaway retry storms are among the most catastrophic failure modes of autonomous agents ā in minutes, an agent stuck in a loop can exhaust external API rate limits, burn through thousands of dollars in LLM tokens, or flood production databases.
Vizier provides built-in circuit breakers across both Python and MCP environments:
Sliding-Window Loop Detection: Computes deterministic SHA-256 canonical JSON hashes of tool arguments. If the same tool is invoked repeatedly within a sliding window (e.g. 3 times in 30 seconds), the circuit trips immediately (
CIRCUIT_TRIPPED:LOOP_DETECTED).Session Action Budgets: Caps the total number of actions an agent can execute within a single task or session (
CIRCUIT_TRIPPED:BUDGET_EXCEEDED).Python Guard Decorator:
from vizier import CircuitBreaker, vizier_guard breaker = CircuitBreaker(max_repeated_calls=3, time_window_seconds=30.0, max_session_actions=25) @vizier_guard(action_type="query_db", circuit_breaker=breaker) def query_database(query: str): return db.execute(query)MCP Enforcement Proxy:
const proxy = createMcpEnforcementProxy({ // ... circuitBreaker: { maxRepeats: 3, windowMs: 30_000 }, });Returns standardized JSON-RPC 2.0 error
-32028on tripped loops without invoking the upstream tool.
Proving the authority instead of asserting it
By default the authority in a request is whatever the calling application says
it is. Vizier checks the action against it faithfully and signs the result ā but
the receipt then attests to a decision, not to a delegation.
A delegation grant closes that gap. The principal signs a compact JWS that
binds one authority to one agent for a bounded window, the agent sends it as a
grant field, and Vizier verifies it against a public key registered for that
principal:
# once, on the principal's machine
node scripts/mint-grant.mjs keygen --kid acme-2026-09 --out principal.jwk.json
# per delegation
node scripts/mint-grant.mjs sign --key principal.jwk.json --grant grant.json --ttl 3600The public half is registered as VIZIER_PRINCIPAL_KEYS; the private half never
leaves the principal, and no endpoint would accept it. A verified grant makes the
receipt say so:
{
"authority_provenance": "principal_signed",
"grant": {
"jti": "grant_5f1cā¦",
"issuer": "acme-corp",
"subject": "procurement-agent-01",
"key_id": "acme-2026-09",
"expires_at": "2026-09-07T16:00:00.000Z"
}
}Two properties are worth stating plainly:
A grant that does not verify is
BLOCK, never a quiet fall back to the caller-asserted path. Failing it open would make a forged grant strictly better for an attacker than sending none.The request's
authoritymust match the signed one exactly. A genuine grant carried beside an enlarged authority isBLOCK, not an allow at the larger limit.
Keys are registered out of band and never fetched at decision time, so the authorization kernel still makes no outbound request. Full contract, reason codes and limits: docs/DELEGATION_GRANTS.md.
API
POST /v1/verify accepts one proposed action and its delegated authority.
Malformed requests return a structured error and never produce ALLOW.
JSON bodies are capped at 1 MiB, 64 levels, and 50,000 aggregate values before
recursive schema validation.
When VIZIER_API_KEY is absent, the service is in evaluation mode: valid
requests can return REVIEW or BLOCK, never ALLOW. When the secret is
configured, REST and MCP verification calls require Authorization: Bearer <key>. A2A also accepts an anonymous evaluation call for discovery and
conformance checks, but that call can return only REVIEW or BLOCK.
{
"agent": { "id": "procurement-agent-01", "owner": "acme-corp" },
"principal": { "id": "acme-corp" },
"action": {
"type": "purchase",
"target": "supplier.example",
"parameters": { "amount": 8200, "currency": "USD" }
},
"authority": {
"allowed_actions": ["purchase"],
"constraints": { "max_amount": 10000, "currency": "USD" }
},
"context": {
"request_id": "order-1842",
"timestamp": null,
"source": "rest"
}
}principal must be present. Set it to null when the principal is unknown;
Vizier returns REVIEW. Omitting the field is a validation error.
Decision priority is BLOCK, then REVIEW, then ALLOW. The numeric risk
score explains accumulated risk but does not override policy results.
Action Covenant lifecycle
The v0.3.0 resources are additive; /v1/verify remains compatible.
POST /v1/covenantsaccepts a strictActionCovenantDraftplus a principal acceptance bound to the draft hash. A model may produce the draft, but it cannot activate it by naming itself as the principal.POST /v1/authorizationschecks covenant integrity and expiry, exact action equality, evidence presence and freshness, shallow exact-match invalidation signals, and the existing delegated-authority policies. It returns a compact ES256 authorization JWS for every decision.The executor acts only on
ALLOWbefore the receipt expires.POST /v1/outcomesverifies the authorization receipt, binds the reported execution outcome, checks exact forbidden-effect rules, and returns a compact ES256 outcome JWS.
All three resources require authenticated enforcement and
RECEIPT_SIGNING_KEY; there is no evaluation-only activation path. Covenants
remain caller-held immutable envelopes in this milestone. Vizier stores bounded
operational metadata and hashes asynchronously, but not full action parameters,
evidence, signals, outcome effects, JWS tokens, or signing material. It does not
retrieve evidence independently.
Integration rule
Call Vizier immediately before the external action. Treat timeout, invalid JSON,
REVIEW, and BLOCK as stop conditions.
const decision = await vizier.verify(proposedAction);
if (decision.decision === "ALLOW") {
await executeAction();
}The thin TypeScript client lives in packages/sdk:
import { Vizier } from "@vizier/sdk";
const vizier = new Vizier({
baseUrl: "https://vizier.vassiliy-lakhonin.workers.dev",
apiKey: process.env.VIZIER_API_KEY,
});
const decision = await vizier.verify(request);Python SDK (vizier-guard)
The Python SDK lives in packages/python-sdk with zero external dependencies:
from vizier import VizierClient, vizier_guard
client = VizierClient(
base_url="https://vizier.vassiliy-lakhonin.workers.dev",
api_key=os.environ["VIZIER_API_KEY"],
)
# Protect any function / agent tool:
@vizier_guard(client=client, action_type="purchase", max_amount=1000.0, currency="USD")
def execute_order(amount: float, target: str):
return {"status": "success", "amount": amount}Or protect LangChain / LangGraph tools:
from vizier.integrations.langchain import VizierLangChainToolGuard
guarded_tool = VizierLangChainToolGuard(
tool=my_search_or_db_tool,
client=client,
allowed_actions=["query_db"],
)MCP Enforcement Proxy
Protect any existing local or remote MCP server with deterministic policy checks:
npx @vizier/mcp-proxy \
--upstream http://localhost:3000/mcp \
--tools "query_db,transfer_funds,send_message" \
--vizier https://vizier.vassiliy-lakhonin.workers.dev \
--api-key $VIZIER_API_KEYThe API key belongs only in a controlled backend or orchestrator. Do not expose it to the action-taking agent. This key authenticates the integration; it does not prove that each supplied delegation was issued by the principal.
Policy rules
Rule | Result |
No authenticated integration credential is configured |
|
Action is absent from |
|
Principal is |
|
Amount exceeds |
|
Amount or currency cannot be checked |
|
Target is blocked or absent from an allowlist |
|
Sensitive action lacks explicit sensitive authority |
|
Authority requires reversibility and the action is not declared reversible |
|
The default sensitive actions are transfer_funds, delete_data,
deploy_worker, execute_code, send_external_message,
modify_permissions, and sign_contract.
action.is_reversible is an integration-supplied assertion, not an independently
verified property. require_review_for_irreversible fails to REVIEW when that
assertion is absent or false; it cannot prove a true assertion is accurate.
Protocol endpoints
GET /openapi.jsonandGET /.well-known/openapi.jsonreturn the same OpenAPI 3.1 contract for/v1/verify,/v1/covenants,/v1/authorizations,/v1/outcomes, and the authenticated/v1/insights. Request schemas are emitted from the same Zod definitions used at the runtime boundary.GET /.well-known/ai-catalog.jsonroutes machines to the A2A Agent Card, the OpenAPI contract, and the MCP server manifest.GET /.well-known/agent-card.jsonreturns an A2A v1.0 Agent Card with a canonical ES256 JWS insignatures[]whenAGENT_CARD_SIGNING_KEYis configured.GET /.well-known/jwks.jsonreturns the matching public key. The JWS protected header points to this endpoint through a same-originjku.POST /a2aimplements the A2A v1.0 JSON-RPCSendMessagemethod. Anonymous requests run only in evaluation mode; a wrong supplied credential is rejected.POST /mcpimplements MCP2026-07-28withserver/discover,tools/list, andtools/callforvizier_verify_action. The same endpoint also answers the session handshake used by shipping clients:initialize,notifications/initialized,ping,tools/list, andtools/callover2025-06-18,2025-03-26, or2024-11-05. The request body selects the profile: only2026-07-28carries its protocol version inparams._meta.GET /.well-known/mcp.jsonreturns the MCP server manifest, the same document published to the MCP Registry fromserver.jsonat the repository root.
The session profile exists because no off-the-shelf client speaks the stateless
profile yet. Measured 2026-09-02 against the deployed Worker: a standard
initialize was rejected with -32600, so the endpoint could not be connected
from any MCP client. The stateless contract is unchanged; the session profile is
additive and shares one verification path.
MCP Enforcement Proxy (@vizier/mcp-proxy)
packages/mcp-proxy is a standalone reverse proxy adapter that places any existing MCP server behind Vizier. The proxy:
exposes only the configured upstream tool names;
authenticates every MCP request with a proxy-specific Bearer token;
maps the exact tool name and arguments to one
mcp_tool_callverification;forwards the unchanged MCP request only after a verified
ALLOW;stops on
REVIEW,BLOCK, timeout, invalid Vizier output, or upstream error;replaces the incoming credential with a separate upstream credential; and
logs integration ID, request ID, tool name, decision, receipt ID, outcome, and latency without logging arguments or secrets.
Run directly via npx:
npx @vizier/mcp-proxy \
--upstream http://127.0.0.1:8791/mcp \
--tools "write_file,query_db" \
--vizier https://vizier.vassiliy-lakhonin.workers.dev \
--api-key $VIZIER_API_KEYOr configure via environment variables:
export VIZIER_BASE_URL="http://127.0.0.1:8787"
export VIZIER_API_KEY="local-development-key"
export VIZIER_PROXY_INTEGRATION_ID="pilot-acme"
export VIZIER_PROXY_CLIENT_TOKEN="replace-with-a-random-client-token"
export VIZIER_PROXY_AGENT_ID="coding-agent-01"
export VIZIER_PROXY_AGENT_OWNER="acme"
export VIZIER_PROXY_PRINCIPAL_ID="platform-team"
export VIZIER_PROXY_UPSTREAM_ID="filesystem"
export VIZIER_PROXY_UPSTREAM_URL="http://127.0.0.1:8791/mcp"
export VIZIER_PROXY_UPSTREAM_BEARER_TOKEN="replace-with-upstream-token"
export VIZIER_PROXY_ALLOWED_TOOLS="write_file"
npm exec --workspace @vizier/mcp-proxy -- vizier-mcp-proxyPoint the pilot MCP client at http://127.0.0.1:8790/mcp, use
VIZIER_PROXY_CLIENT_TOKEN as its Bearer credential, and remove its direct
access to the upstream URL and credential. The proxy is not an enforcement
boundary if the agent can still reach the upstream server, read either backend
credential, or use a shell with equivalent authority.
Connect an MCP client
The credential is optional at connect time. An anonymous tools/call runs in
evaluation mode: the decision is real but the supplied authority is untrusted, so
it can never return ALLOW. The credential unlocks enforcement results, and a
credential that is supplied and wrong is rejected with -32001.
Anonymous calls share a budget of 60 requests per minute per client IP. Past it
the endpoint answers 429 with JSON-RPC error -32029 and a Retry-After
header. Attaching a wrong credential does not leave that budget; a valid one
does.
An anonymous call leaves no receipt, so the only record of it is a counter: one
row per UTC day per surface per outcome, bumped in place. It holds no client IP,
no arguments, and nothing else about the caller, and it is swept by the same
30-day retention as the rest of the audit store. GET /v1/insights reads it
back. Counts are best-effort instrumentation, not proof of adoption, and each
deploy adds exactly four to mcp / served: the live check below probes the
credential-free path on purpose.
claude mcp add --transport http vizier \
https://vizier.vassiliy-lakhonin.workers.dev/mcpAdd the header once you hold a credential:
claude mcp add --transport http vizier \
https://vizier.vassiliy-lakhonin.workers.dev/mcp \
--header "Authorization: Bearer <integration-credential>"Any client that accepts a Streamable HTTP URL works the same way. Verify the handshake without a client:
curl -sS https://vizier.vassiliy-lakhonin.workers.dev/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'Registry listing
server.json at the repository root is the MCP Registry entry for
io.github.vassiliylakhonin/vizier, published on 2026-09-02 and validated
against the 2025-12-11 server schema. It carries no repository block: the
source repository is private, and an entry pointing at a URL that answers 404 is
worse than no link at all. The namespace is claimed through the GitHub account,
not the repository.
The deploy workflow republishes it. scripts/publish-registry.mjs compares
server.json against the live entry and publishes only when the registry lacks
that version, authenticating through GitHub Actions OIDC so no registry token is
stored anywhere. The registry keys an entry on its version, so a manifest edit
rides along with a version bump; an edit without one is reported and skipped
rather than rejected by the registry.
Check the decision without making it, using an existing mcp-publisher login github session:
node scripts/publish-registry.mjs --dry-runPublishing by hand still works and takes the same path:
mcp-publisher login github
node scripts/publish-registry.mjsRead the live entry back:
curl -sS "https://registry.modelcontextprotocol.io/v0/servers?search=vizier"An unrelated io.github.pipeworx-io/vizier is listed in the same registry. The
namespace is what separates them, so a search by bare name returns both.
tests/discovery-contracts.test.ts holds server.json and the served
/.well-known/mcp.json to the same content, so a registry listing cannot drift
away from the endpoint the Worker serves.
Receipts
Each successful verification returns a receipt ID, creation time, request hash, decision, risk score, rule IDs, and reason codes. Object keys are sorted before SHA-256 hashing. This canonicalization is documented and tested, but it is not an RFC 8785 claim.
Legacy /v1/verify receipts remain unsigned and caller-held for compatibility.
The SDK validates the complete response, checks decision-to-receipt consistency,
and recomputes the request hash before returning a decision.
Action Covenant authorization and outcome receipts are compact ES256 JWS values.
They use a dedicated receipt key and protected typ values for domain
separation. The SDK obtains the matching public key from
/.well-known/jwks.json, verifies the signature, and recomputes the covenant,
request, action, evidence, signal, authorization-token, and outcome bindings
before returning. Signed does not mean independently timestamped or
principal-issued. Vizier persists only selected receipt metadata and hashes; the
complete signed receipt and token remain caller-held.
GET /v1/insights exposes authenticated decision counts, lifecycle totals,
average legacy risk score, and reported failure/violation count. These are
best-effort operational aggregates: asynchronous audit writes can fail, and the
numbers are not proof of production adoption or complete execution history.
Metadata is retained for 30 days and pruned daily by a scheduled Worker handler.
Development
npm run typecheck
npm test
npm run build
npm run checknpm run build compiles @vizier/sdk, compiles the private gated-deploy tool,
and runs a Cloudflare deployment dry run. It does not deploy the Worker.
Two checks describe production rather than a commit, so they are separate from
npm run check and need the network:
npm run check:deployed
npm run check:livecheck:deployed compares the current bundle digest against the newest
deployment: is production running this code? check:live calls the deployed
Worker and asserts what it answers ā health, the released version on the service
index and the MCP manifest, a signed agent card, both JWKS keys, the MCP session
handshake, tools/list, an anonymous tools/call that returns a receipt and
cannot grant ALLOW, and the stateless server/discover. A digest can match
while the endpoint is broken, which is why both exist. Point it elsewhere with
VIZIER_ORIGIN. The deploy workflow runs it after the deploy and before the
registry publish, so a Worker that stopped answering is never advertised as a
new version.
To prepare an enforcement deployment after reviewing the threat model:
npx wrangler whoami
npx wrangler secret put VIZIER_API_KEY
npx wrangler secret put AGENT_CARD_SIGNING_KEY
npx wrangler secret put RECEIPT_SIGNING_KEY
npx wrangler deployAGENT_CARD_SIGNING_KEY and RECEIPT_SIGNING_KEY are separate private P-256
JWKs with distinct kid values, alg: "ES256", use: "sig", and stable key
identifiers. Wrangler stores them as secrets; they must not be committed. A
malformed configured key fails the affected signed surface instead of silently
downgrading it.
The public Worker completed its one-time v0.1-to-v0.2 bootstrap on 2026-08-24.
After the integration credential has been stored in macOS Keychain under service
com.vizier.gated-deploy and account VIZIER_API_KEY, normal deployments use:
npm run deploy:gatedThe private tool accepts no command arguments. It drafts and accepts a five-minute
covenant for the current commit, the fixed deploy_worker action, and the fixed
worker:vizier target. A worktree snapshot is freshness evidence and a dirty
worktree is an invalidation signal. It runs wrangler deploy --strict only after
the SDK verifies a signed ALLOW receipt, then records a signed success or
failure outcome. If outcome recording fails after execution, the command returns
outcome_unrecorded and a non-zero exit code instead of reporting a complete lifecycle.
A new Worker name or fresh environment that does not expose the covenant endpoints needs one explicit bootstrap deployment through its existing v0.1 gate:
VIZIER_V0_2_BOOTSTRAP=1 npm run deploy:gatedThis bypass is only for the deployment that introduces the covenant endpoints
and receipt key. Do not set VIZIER_V0_2_BOOTSTRAP for normal deployments of the
public Vizier Worker; they use the covenant lifecycle.
This wrapper is an integration test, not an operating-system security boundary. An agent with unrestricted shell access and Cloudflare credentials can bypass it by invoking Wrangler directly. A production integration must expose only the wrapper capability and keep both Cloudflare and Vizier credentials outside the action-taking agent.
The deployment command is intentionally not part of npm run build. Without
the secret, a deployment remains evaluation-only and cannot return ALLOW.
The Worker uses D1 only for an asynchronous, metadata-only operational audit
trail. The authorization decision path does not depend on D1 availability. It
uses no KV, Durable Object, queue, AI model, or outbound fetch. npm run check
applies every D1 migration in order to an in-memory SQLite database and verifies
that legacy payload-bearing columns are removed.
The repository structure and protocol sources are documented in
docs/ARCHITECTURE.md,
docs/ADR-0001-ACTION-COVENANTS.md,
docs/PILOT.md,
docs/CLAIMS.md, and
docs/SECURITY_REVIEW.md.
What is deferred
Independent principal authentication, durable policy/evidence/full-receipt storage, principal-signed delegation and acceptance, billing, dashboards, reputation models, payment settlement, and LLM policy evaluation inside the privileged kernel remain outside v0.3.0. See FUTURE.md.
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP enforcement layer that intercepts AI agent actions and blocks rule violations before execution.
Pre-execution governance for AI agents. Deterministic PASS/FAIL/REVIEW verdicts, replayable proof.
Zero-secret MCP gateway for AI agents: risk-scored, audited calls with human-in-the-loop approval.
Agent governance with A2A/Shopify/MCP trust audits, action screening, and decision UI.
Related MCP Servers
- AlicenseBqualityAmaintenanceA governance proxy for AI tools ā every MCP/agent tool call is policy-gated, secret-redacted, and written to a hash-chained, offline-verifiable audit trail.13MIT

evav-gatewayofficial
AlicenseNot gradedqualityBmaintenanceGoverned MCP gateway that lets AI agents call tools with policy enforcement, prompt-injection screening, a kill-switch, and tamper-evident signed audit logs.Apache 2.0- FlicenseNot gradedqualityBmaintenanceProvides a secure MCP boundary for AI agents, intercepting and validating tool calls, redacting secrets, and requiring human approval for sensitive actions with a tamper-evident audit trail.-
- FlicenseNot gradedqualityBmaintenanceAn MCP server that acts as an authorization gateway between an AI agent and external systems, deterministically refusing actions that exceed granted authority and sealing every decision into an auditable chain of custody.-