Zero-trust identity and authorization for AI agent-to-agent communication
Warning
Early-stage research project β not production-ready.
Cullis is in active study and prototyping. The architecture is real and the demo runs end-to-end on a laptop, but the codebase has not been hardened, security-audited, or validated against real production workloads. Several components are still exploratory and APIs may break without notice.
Use it to learn, explore, prototype, and contribute β not to handle real users, real credentials, or real traffic yet.
Feedback, security reviews, and ideas are very welcome β see Contact.
This is a research and learning project built in the open. The goals right now are: (1) explore what cryptographic identity, federated authorization, and tamper-evident audit look like for cross-organization AI agents; (2) prototype an architecture that composes existing standards (x509, SPIFFE, DPoP, OAuth 2.0, OPA) into something coherent for the agent era; (3) get the design reviewed by security researchers and the workload-identity community.
What this means in practice:
- The demo (
./deploy_demo.sh) is the only end-to-end path covered by automated tests. Use it to explore the architecture. - The standalone deploy scripts (
deploy_broker.sh,deploy_proxy.sh, the Helm chart) run, but they are exploratory β no SLA, no upgrade path, no migration story. - No security review yet. The codebase has gone through internal audit rounds but never independent third-party review.
- Schema and APIs may break. Pre-1.0, no semver guarantees.
If you want to try it, the demo is the right entry point. If you want to review or break it, see SECURITY.md. If you want to deploy it for real workloads β please don't, not yet.
When your AI agents negotiate with another company's AI agents β who verifies identity? Who enforces policy? Who audits what happened?
Cullis is a federated trust broker for AI agents: x509 PKI for identity, DPoP-bound tokens, end-to-end encrypted messaging, default-deny policy, and a cryptographic audit ledger. Purpose-built infrastructure for the agent-to-agent era.
π Why Cullis exists, architectural deep-dives, use cases, and comparisons β cullis.io
This README is the engineer's entry point: how to clone it, how to run it, how the code is laid out. Everything else lives on the site.
Boot the full architecture (broker + 2 MCP proxies + 2 agents in 2 organizations), route one cross-org E2E-encrypted message, tear it all down. About a minute end to end.
| Requirement | Why |
|---|---|
| Docker Engine with the Compose v2 plugin | The demo runs five containers (broker, 2 MCP proxies, postgres, redis) |
| Python 3.10+ on the host | The orchestrator + sender + checker scripts run on the host, not in containers |
httpx Python package |
The only host-side Python dependency the demo touches |
| Free TCP ports 8800, 9800, 9801 | The script fails fast with a clear message if any of them is taken |
| ~2 GB free disk + outbound network | First-time build pulls the broker + proxy images |
Supported hosts: Linux native, macOS with Docker Desktop / OrbStack / Colima, Windows via WSL2 + Docker Desktop. No Nix required (Nix is only used by the maintainer's dev loop).
Installing httpx if you do not already have it:
# Recommended: a project venv (works on every OS, never collides with system Python)
python3 -m venv .venv && .venv/bin/pip install httpx
# the wrapper auto-detects .venv/bin/python, no need to activate it
# Or user-wide
python3 -m pip install --user httpx
# Debian/Ubuntu/macOS Homebrew with PEP 668 ("externally-managed-environment"):
python3 -m pip install --user --break-system-packages httpxgit clone https://github.com/cullis-security/cullis
cd cullis
./deploy_demo.sh up
python scripts/demo/sender.pyThree commands after the clone, including a full Docker build on first run. The demo uses KMS_BACKEND=local, no TLS, no Vault β it is meant for laptops. See scripts/demo/README.md for the full guided tour (dashboards, customization, troubleshooting).
If you want to explore the broker or a proxy on its own (not as part of the bundled demo), see Run the components individually below β note that those standalone paths are not yet production-tested.
Cullis ships as two independent, deployable components:
| Cullis Broker | Cullis MCP Proxy | |
|---|---|---|
| Role | Network control plane | Organization data plane |
| Deployed at | Network operator's infrastructure | Each participating org's network |
| Manages | Identity, routing, policy federation, audit ledger | Agent certs, broker uplink, tool execution |
| Dashboard | Network admin (onboard orgs, approve, audit) | Org admin (register, create agents, manage tools) |
| Default port | 8000 (HTTP) / 8443 (HTTPS) | 9100 |
Fully self-hosted. No SaaS dependency. A single company can run both, or a consortium of organizations can agree on who hosts the broker while each runs their own proxy.
flowchart TB
subgraph org_a["π’ Organization A"]
direction TB
A1["π€ Buyer Agent"]
A2["π€ Inventory Agent"]
VA["π Vault A"]
PA["β‘ MCP Proxy A<br><sub>auto PKI Β· cert issuance Β· API key auth</sub>"]
A1 -->|"β API Key"| PA
A2 -->|"β API Key"| PA
VA -.->|"agent keys"| PA
end
subgraph broker["π Cullis Broker β self-hosted"]
direction TB
AUTH["β‘ Verify x509 chain"]
DPOP["β’ Validate DPoP"]
POL["β£ Query policies"]
FWD["β₯ Forward E2E"]
AUTH --> DPOP --> POL --> FWD
end
subgraph org_b["π’ Organization B"]
direction TB
B1["π€ Supplier Agent"]
T1["π§ Tool: ERP"]
VB["π Vault B"]
PB["β‘ MCP Proxy B<br><sub>auto PKI Β· cert issuance Β· API key auth</sub>"]
PB -->|"β¦ API Key"| B1
PB -->|"β¦ tool call"| T1
VB -.->|"agent keys"| PB
end
PA ==>|"β‘ x509 + DPoP + E2E"| broker
broker ==>|"β₯ E2E encrypted"| PB
broker ---|"β£ policy query"| PDPA["π Org A PDP<br><sub>webhook / OPA</sub>"]
broker ---|"β£ policy query"| PDPB["π Org B PDP<br><sub>webhook / OPA</sub>"]
PDPA -->|"β€ allow β"| DUAL{"Both must<br>allow"}
PDPB -->|"β€ allow β"| DUAL
classDef broker fill:#4f46e5,stroke:#6366f1,color:#fff,font-weight:bold
classDef proxy fill:#0d9488,stroke:#14b8a6,color:#fff,font-weight:bold
classDef agent fill:#1e293b,stroke:#334155,color:#e2e8f0
classDef pdp fill:#92400e,stroke:#d97706,color:#fef3c7
classDef vault fill:#1e1b4b,stroke:#4338ca,color:#c7d2fe
classDef decision fill:#b45309,stroke:#f59e0b,color:#fff
class AUTH,DPOP,POL,FWD broker
class PA,PB proxy
class A1,A2,B1,T1 agent
class PDPA,PDPB pdp
class VA,VB vault
class DUAL decision
- Agent β Proxy β agents authenticate with a local API key (
X-API-Key) - Proxy β Broker β the proxy signs with x509 + DPoP and encrypts E2E; agents never touch crypto keys
- Broker verifies β x509 chain, DPoP proof-of-possession, certificate thumbprint pinning
- Policy query β broker asks both organizations' PDP (webhook or OPA)
- Dual authorization β session proceeds only if both orgs return
allow(default-deny) - E2E forward β broker forwards the encrypted message it cannot read (zero-knowledge)
- Proxy β Agent / Tool β the receiving proxy decrypts and delivers
- 3-tier x509 PKI + SPIFFE workload identity β Broker CA β Org CA β Agent cert with
spiffe://trust-domain/org/agentSAN - DPoP token binding (RFC 9449) β every token bound to an ephemeral EC P-256 key, server nonce rotation
- End-to-end encryption β AES-256-GCM payloads, RSA-OAEP-SHA256 key wrapping, two-layer RSA-PSS signing
- Federated dual-org policy β PDP webhook or OPA, default-deny, both orgs must allow
- Cryptographic audit ledger β append-only, SHA-256 hash chain, tamper detection, NDJSON / CSV export
- Self-service org onboarding β invite tokens, automatic Org CA generation, no manual openssl
- OIDC federation for admin login β Okta, Azure AD, Google, per-org IdP config
- KMS backends β local filesystem (dev), HashiCorp Vault KV v2 (prod), extensible
- Self-hosted, no SaaS dependency
from cullis_sdk.client import CullisClient
client = CullisClient("https://broker.example.com")
client.login("buyer", "acme", "agent.pem", "agent-key.pem")
agents = client.discover(capabilities=["supply"])
session_id = client.open_session("widgets::supplier", "widgets", ["supply"])
client.send(session_id, "acme::buyer", {"order": "100 units"}, "widgets::supplier")A TypeScript SDK lives in sdk-ts/. An MCP server exposing 10 Cullis tools (so any MCP-compatible LLM can become a Cullis agent) is in cullis_sdk/mcp_server.py.
β οΈ Status: exploratory, not production-tested. The bundled demo (./deploy_demo.sh) is the only end-to-end path covered by automated tests. The standalone deploy scripts below let you spin up the broker or a single proxy on its own to evaluate each piece in isolation, but they have not been hardened or validated against a real production workload yet. Use them to explore, not yet to operate.
If you only want to see Cullis in action, stop at the Quickstart above and use the demo.
# Self-signed cert on https://localhost:8443 β no public DNS required
./deploy_broker.sh --dev
# With Let's Encrypt (requires a public domain pointing at the host)
./deploy_broker.sh --prod-acme \
--domain broker.example.com \
--email ops@example.com
# With your enterprise CA (Bring Your Own CA)
./deploy_broker.sh --prod-byoca \
--domain broker.example.com \
--cert /etc/ssl/cullis/fullchain.pem \
--key /etc/ssl/cullis/privkey.pemThree TLS profiles, same script. The --dev profile is the one most likely
to "just work" right now; the --prod-* profiles are still being shaken out.
./deploy_proxy.shPairs with a running broker. Walks you through entering the broker URL, the invite token, and registering the org.
A Helm chart lives in deploy/helm/cullis/. Same
caveat: it captures the deployment topology and is helm lint-clean,
but has not been validated against a managed Kubernetes cluster end to
end yet. Treat it as a starting point for your own packaging, not a
turnkey install.
enterprise-kit/β BYOCA guide, OPA policy bundle, Prometheus alert rules, PDP webhook templatedocs/ops-runbook.mdβ operational pitfalls (DPoP htu, KMS bootstrap, common health-check failures)scripts/demo/README.mdβ the recommended path: full demo guide with dashboard tour and customization hooks
app/ Broker FastAPI application (auth, registry, broker, dashboard, kms)
mcp_proxy/ Org MCP gateway (egress, ingress, dashboard, agent manager)
cullis_sdk/ Python SDK + MCP server
sdk-ts/ TypeScript SDK
alembic/ Broker database migrations
tests/ Unit + integration tests; tests/e2e/ holds the full-stack suite
scripts/ Ops scripts (generate-env, pg-backup) + scripts/demo/ live demo
deploy/ Helm chart for Kubernetes
enterprise-kit/ BYOCA guide, OPA policy bundles, monitoring, PDP template
docs/ cullis.io site source + ops runbook
.github/ CI workflows + issue / PR templates
Runtime: Python 3.11 Β· FastAPI Β· PostgreSQL 16 Β· Redis Β· HashiCorp Vault Β· cryptography Β· PyJWT Β· OpenTelemetry + Jaeger Β· OPA Β· Docker Β· Helm.
See CONTRIBUTING.md for development setup, PR workflow, and code conventions.
Security vulnerabilities: see SECURITY.md for private reporting guidelines.
| General questions, partnerships, demos | hello@cullis.io |
| Security vulnerabilities (private) | security@cullis.io Β· see SECURITY.md |
| Bug reports, feature requests | GitHub Issues |
| Discussion, questions, ideas | GitHub Discussions |
Architecture deep-dives, use cases, comparisons, and the project's reason for existing all live at cullis.io.