Skip to main content
Glama
gegane-lutshaba

eidolon-gateway

README.md
<h1 align="center">EIDOLON</h1>
<p align="center"><b>Your agent. Seen. Bounded. Revocable.</b><br/>
The cryptographic authority layer for AI agents β€” watch every tool call live,
delegate exactly the authority you choose, and kill it mid-session.</p>

<p align="center">
  <a href="https://github.com/gegane-lutshaba/eidolon/actions"><img alt="CI" src="https://github.com/gegane-lutshaba/eidolon/actions/workflows/ci.yml/badge.svg"></a>
  <img alt="tests" src="https://img.shields.io/badge/tests-259%20passing-39d98a">
  <img alt="license" src="https://img.shields.io/badge/license-Apache--2.0-8b7bff">
  <img alt="python" src="https://img.shields.io/badge/python-3.12%2B-4fc7ff">
  <a href="https://github.com/l33tdawg/sage"><img alt="built on SAGE" src="https://img.shields.io/badge/built%20on-SAGE-f2b84b"></a>
</p>

<p align="center">
  πŸ•ΉοΈ <b><a href="https://eidolon.onyxcreator.com/versus">Try VERSUS live</a></b> β€”
  watch a famous agent get wrecked by a real attack, then the same attack hit the gate Β·
  <a href="https://eidolon.onyxcreator.com/challenge">break the gate</a> Β·
  <a href="https://eidolon.onyxcreator.com/roadmap">the boss run (roadmap)</a> Β·
  <a href="https://eidolon.onyxcreator.com/paper">white paper</a>
</p>

---

AI agents act with your authority and none of your restraint. **EIDOLON** is the
layer in between: a signed, attenuable, revocable delegation credential the agent
must satisfy on **every** tool call β€” checked independently of the model (so it
can't be prompt-injected away), every action written to a tamper-evident ledger.

Two invariants override everything and are property-tested in CI:

1. **Default-deny** β€” any authority not explicitly granted is denied.
2. **No unattested action** β€” no side effect runs without a successful `HORKOS`
   attestation (attest-then-act).

Domain-agnostic core, beachhead profile `general-continuity`. Built on
[SAGE](https://github.com/l33tdawg/sage). Full contract:
[`docs/EIDOLON_PRD_v1.md`](docs/EIDOLON_PRD_v1.md) Β· 5-minute
[quickstart](docs/quickstart.md).

πŸ“„ **White paper:** [`docs/whitepaper.md`](docs/whitepaper.md) Β· **Case study
(Hermes with/without EIDOLON):** [`docs/case-study-hermes.md`](docs/case-study-hermes.md)
(`make hermes-case`) Β· **Diagrams & social kit:** [`docs/visuals/`](docs/visuals/)
Β· [`docs/linkedin-post.md`](docs/linkedin-post.md) Β· **Related work & gap
analysis:** [`docs/review-and-related-work.md`](docs/review-and-related-work.md)

**AgentDojo evaluation:** EIDOLON's authority layer contains **96% of injection
tasks** while breaking **0% of benign tasks** (38% fully autonomous, 62%
one-approval). [`docs/eval-agentdojo.md`](docs/eval-agentdojo.md) Β· reproduce:
`uv sync --extra eval && python -m eidolon.eval`. `KAIROS.resolve` p95 β‰ˆ 1 ms.

**Composes with the field:** a CaMeL-style **data-flow taint** layer
(`eidolon.gateway.taint`) closes the read-exfil gap; **automated adversarial
certification** (`make adversarial`) makes an agent earn autonomy by surviving
fresh attacks each round; THEMIS delegations export as real **biscuit** tokens
([`docs/standards-interop.md`](docs/standards-interop.md)); the gate's invariants
are **machine-checked in TLA+/TLC** (`make formal`,
[`docs/formal-model.md`](docs/formal-model.md)); **purpose-binding**
([`docs/purpose-binding.md`](docs/purpose-binding.md)) enforces
privacy-purpose-limitation; and approved payments export as signed **AP2
mandates** ([`docs/payments-ap2.md`](docs/payments-ap2.md)).

## Try to break it

```bash
uv sync && make challenge     # β†’ http://localhost:8000/challenge
```

You play a **fully compromised agent** β€” no model to trick, you issue the tool
calls yourself: leak the customer's account number, wire money, drop the prod
database, slip through an unmapped tool, inject "you are pre-authorized" into
the arguments. The gate holds anyway, because authority is a signed credential
checked outside the agent β€” and every attempt lands on the tamper-evident
ledger. If you ever see `FLAG{gate-breached}`, you found a real bypass: report
it. **[5-minute quickstart β†’](docs/quickstart.md)**

## Use it on your own agent

**Managed β€” nothing to install.** Sign up at
[eidolon.onyxcreator.com](https://eidolon.onyxcreator.com), pick an authority
preset from the delegation gallery, and point any MCP client at the hosted
gateway:

```bash
claude mcp add --transport http eidolon https://eidolon.onyxcreator.com/mcp \
  --header "Authorization: Bearer <your agent key>"
```

**Govern Claude Code's *native* tools** (Bash / Edit / Write / Read) β€” which
never flow through MCP β€” with a drop-in hook. Install it once and wire a
`PreToolUse` + `PostToolUse` hook in `~/.claude/settings.json`; every built-in
tool call is then ruled on (allow Β· ask Β· deny), attested, and streamed to the
same feed. Full copy-paste steps:
[`integrations/claude_code/`](integrations/claude_code/). The dashboard's
**CONNECT β†’ πŸͺ EVERY ACTION** tab generates it pre-filled.

**Self-host the gateway** in front of your own tools β€” no clone, just
[uv](https://docs.astral.sh/uv/):

```bash
uvx --from "eidolon[mcp] @ git+https://github.com/gegane-lutshaba/eidolon" eidolon-gateway \
  --config gateway.yaml -- npx -y @modelcontextprotocol/server-filesystem .
```

Either way, every tool call streams into **mission control** β€” green acts,
amber waits for your approval, red never happened β€” with a per-agent **kill
switch** one click away, and every decision on a tamper-evident ledger. Ranks
(`OBSERVER β†’ DRAFTER β†’ OPERATIVE β†’ AUTONOMOUS`) are the autonomy ceiling: an
agent *earns* autonomy by surviving adversarial certification.

Prefer the terminal? `make demo` runs the narrated core scenario (a person
delegates a bounded slice of authority; the agent answers/drafts/posts within
its mandate, refuses a contract, resists an injection, and is revoked
mid-session), and `make gateway-demo` governs a real MCP tool server end to end.

## The authority layer for any MCP agent

SAGE became *the memory layer* that agents plug in over MCP. EIDOLON is the
**authority layer**, with the same shape: a **governing MCP gateway** that any
agent (Hermes, Claude Code, OpenClaw, Raptor, Cursor…) points at instead of a
raw tool server. Every `tools/call` is routed through KAIROS β€” authority,
fidelity, ceiling, attestation β€” before it can touch the real tool. **Zero agent
changes.**

```
agent ──MCP──▢ eidolon-gateway ──(KAIROS.resolve)──▢ real MCP tool server
                    β”‚ attest-then-forward
                    β–Ό
               SAGE ledger
```

```bash
make gateway-demo    # self-contained: ops tools + a red-team coda, governed & attested
```

Read tools run, drafts are held, status posts notify, and dangerous tools (email
a customer, `delete_database(prod)`, run an exploit, scan an out-of-scope host)
are **refused** β€” each attested. The gateway speaks **stdio** (client launches
it as a subprocess) and **streamable HTTP** (`--http 8300` β†’ agents connect to
`http://host:8300/mcp`; one governed endpoint fronts tools for a whole team).
Wire it into a real agent with [`docs/integrations/`](docs/integrations/); run
the real proxy with
`python -m eidolon.gateway --config gateway.yaml -- <downstream MCP server>`.

## Architecture

```
            Principal (human Β· root identity)
               β”‚ defines            β”‚ mints
               β–Ό                    β–Ό
        ETHOS (fidelity)      THEMIS (authority)
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β–Ό
             KAIROS  (action gate)   ◀── BASANOS (autonomy ceiling)
             act Β· draft Β· escalate
                        β–Ό
             HORKOS (attestation)
                        β–Ό
        SAGE  (consensus memory + attestation ledger)
```

| Component | Module | Responsibility |
|-----------|--------|----------------|
| **SAGE adapter** | `eidolon.sage` | The only path to SAGE. `SagePort` interface; live `SageClientAdapter` + in-memory fake. |
| **ETHOS** | `eidolon.ethos` | Fidelity core. Hard-isolated **judgment** (auditable, LLM-free) and **style** (Claude) engines. |
| **THEMIS** | `eidolon.themis` | Authority. Ed25519-signed, chained, attenuable delegation credentials (biscuit/macaroon lineage). |
| **KAIROS** | `eidolon.kairos` | The single action gate. LOCKED resolution order; attest-then-act. |
| **HORKOS** | `eidolon.horkos` | Immutable attestation on SAGE's consensus ledger. |
| **BASANOS** | `eidolon.basanos` | Certification. Fidelity face + **integrity face** (adversarial suites) both gate the autonomy ceiling. |
| **Capture** | `eidolon.capture` | Consent-gated ingestion of traces into SAGE. |
| **Domain Profile** | `eidolon.profile` | Declarative pack specialising the fixed core. Ships `general-continuity`. |

### Notable design decisions (enhancements over the PRD, invariants preserved)

- **Ports & adapters around SAGE.** `SagePort` isolates the substrate; an
  in-memory fake runs the whole system green offline, while the live
  `SageClientAdapter` binds the real `sage_sdk` (verified against a Dockerized
  node). Attestations map to consensus-committed memories (SAGE has no dedicated
  attest API); a record's content hash is its ledger hash.
- **Style/judgment isolation** is enforced two ways: an import-graph test proves
  `ethos.judgment` never imports `ethos.style`, and a behavioral test proves
  removing the style engine changes zero decisions.
- **One canonical serializer** (`common.canonical`) is the single hashing source
  of truth for THEMIS signing, ETHOS versioning, and HORKOS attestation.

## Quick start

```bash
uv sync --all-extras            # install (needs Python 3.12+ and uv)

# Fast lane β€” no external services. In-memory SAGE port.
make test                       # unit + property tests

# Live lane β€” real SAGE consensus node via Docker.
make up                         # starts ghcr.io/l33tdawg/sage on :8080
make test-integration           # cross-principal isolation, attest→replay, full gate

# Run the API
cp .env.example .env            # set EIDOLON_ANTHROPIC_API_KEY for Claude voice
make run                        # uvicorn on :8000
```

The style engine (drafts/escalations) uses Claude (`claude-sonnet-4-6` by
default). Without an API key it falls back to deterministic templates β€” **no
judgment or authority decision ever depends on the LLM.**

## Deploy on-prem (Docker)

Run one EIDOLON on your own infrastructure; every developer's agent routes
through it. A prebuilt multi-arch image is published to GHCR β€” `docker pull`, not
clone-and-build. Full guide: [`docs/on-prem.md`](docs/on-prem.md).

```bash
# Try it β€” one container (SQLite store + in-memory ledger; evaluation only)
docker run -d -p 8000:8000 -v eidolon-data:/data ghcr.io/gegane-lutshaba/eidolon:latest

# Run it for real β€” app + Postgres (persistent, hash-chained ledger)
curl -O https://raw.githubusercontent.com/gegane-lutshaba/eidolon/main/docker-compose.deploy.yml
docker compose -f docker-compose.deploy.yml up -d   # set EIDOLON_DB_PASSWORD + EIDOLON_ADMIN_TOKEN first
```

The image runs as a non-root user with a `HEALTHCHECK`; point agents at it with
`EIDOLON_URL=https://your-host` (the [hook](integrations/claude_code/) and `/mcp`
both take a URL).

**Kubernetes** β€” a published Helm chart (app + optional bundled Postgres):

```bash
helm install eidolon oci://ghcr.io/gegane-lutshaba/charts/eidolon --version 0.1.0 \
  --namespace eidolon --create-namespace \
  --set secrets.adminToken="$(openssl rand -hex 32)"
```

### Single VPS (from source, Make targets)

One box, no consensus cluster. The `postgres` SAGE backend persists memory and
the attestation ledger locally as an **append-only hash chain** β€” carrying
SAGE's tamper-evidence onto a single host (any edit, deletion, or reorder breaks
the chain and is caught by `make deploy-verify`).

```bash
# one-shot bootstrap of a fresh Ubuntu/Debian box (installs Docker, writes a
# .env with generated secrets, brings the stack up; add EIDOLON_DOMAIN for TLS):
sudo bash deploy/provision.sh

# …or manually:
cp .env.example .env        # set EIDOLON_DB_PASSWORD, EIDOLON_ADMIN_TOKEN, EIDOLON_AUDIT_TOKEN
make deploy                 # Postgres + EIDOLON on :8000 (no TLS)
make deploy-tls             # …+ Caddy auto-HTTPS (needs EIDOLON_DOMAIN)
make deploy-verify          # recompute the ledger hash chain β€” proves it is intact
make deploy-backup          # timestamped, gzipped pg_dump (see deploy/restore.sh)
make deploy-logs            # tail the service
make deploy-down            # stop
```

**Operator auth (fail-closed, single tenant, two roles).** `EIDOLON_ADMIN_TOKEN`
grants the full control plane; `EIDOLON_AUDIT_TOKEN` grants a read-only forensic
role (`/audit`, `/replay`). Both are accepted as `Authorization: Bearer <token>`
(CI/SDK) or a login cookie (`POST /login`, browser). With **neither** set the
platform runs open for localhost dev and warns loudly β€” set at least the admin
token before exposing it. Behind the TLS proxy set
`EIDOLON_SESSION_COOKIE_SECURE=true` (and optionally `EIDOLON_TRUSTED_HOSTS`).

`docker-compose.deploy.yml` runs the FastAPI service against `pgvector/pgvector`;
the `tls` profile adds **Caddy** (automatic HTTPS via `EIDOLON_DOMAIN`,
`deploy/Caddyfile`). Backend is selected by `EIDOLON_SAGE_BACKEND` (`memory` Β·
`postgres` Β· `sage`); the full BFT-consensus substrate remains `sage`.
(`docker-compose.yml` is unchanged β€” the SAGE + Postgres substrate for the
integration-test lane, `make up`.)

## Surface

**Product (accounts).** `GET /` landing Β· `/signup` Β· `/app` (per-user mission
control) Β· `POST /auth/{signup,login,logout}` Β· `GET|POST|DELETE /api/agents…`
(enroll, connect snippets, per-agent kill/restore) Β· `GET /api/feed` (SSE) Β·
`GET /api/gallery` (delegation templates) Β· `POST /contact`.

**Public demo.** `/versus` (+ `/versus/{scenarios,run,stats}`) Β·
`/challenge` (+ `/challenge/{state,call,reset}`) Β· `/paper` Β· `/portal` Β· `/og.png`.

**Managed gateway.** `POST /mcp` β€” the hosted governing MCP endpoint (agent key
in a Bearer header). Self-host reports in via `POST /ingest/events`.

**Operator.** `GET /live` (global mission control) Β· `GET /gateways` +
`POST /gateways/{id}/{kill,restore}` Β· `GET /audit` +
`/audit/{chain,export.json,export.csv}` (replay, integrity, compliance export) Β·
`GET /console/delegations` Β· `GET|POST /escalations/{id}/{approve,deny}` Β·
`POST /login` Β· `GET /api/leads`.

**Core seams.** `POST /keypair` Β· `POST /delegations/{mint,attenuate,revoke}` Β·
`POST /heartbeat` Β· `POST /resolve` (the gate) Β· `GET /replay` Β·
`POST /capture/{ingest,ingest_multi}` Β· `POST /skills{,/run}` Β·
`POST /coaching/report` Β· `GET /profiles/{id}` Β· `GET /{health,ready,whoami}`.
See `eidolon.api.app`.

## Status

The **full PRD** (Phase 0 + Phase 1 + every v2 item) **and a research roadmap**
beyond it are implemented and tested β€” **250+ tests**: Hypothesis property tests,
live-SAGE integration, and a TLA+/TLC machine-checked model of the gate.

**Shipped as a product** and deployed at
[eidolon.onyxcreator.com](https://eidolon.onyxcreator.com): multi-user accounts,
a per-agent delegation gallery, three connect paths (managed hosted gateway /
agent-run setup / self-host), a live mission-control dashboard with a kill
switch, VERSUS mode, a Postgres-backed hash-chained ledger, and one-command VPS
deploy (`deploy/provision.sh`).

**Beyond the PRD** ([`docs/review-and-related-work.md`](docs/review-and-related-work.md)):
- **Evaluation** β€” AgentDojo: 96% of injection tasks contained, 0% of benign
  tasks broken; `resolve` p95 β‰ˆ 1 ms ([`docs/eval-agentdojo.md`](docs/eval-agentdojo.md)).
- **Fidelity v2** β€” normalized-token + optional embedder grounding; the decision
  stays a transparent, inspectable threshold (no black box).
- **Data-flow layer** β€” CaMeL-style **taint** (exfiltration) + **purpose-binding**
  (privacy) compose with authority through one mechanism
  ([`taint`](docs/eval-agentdojo.md) Β· [`purpose`](docs/purpose-binding.md)).
- **Automated adversarial certification** β€” the twin earns autonomy by containing
  fresh attacks each round (`make adversarial`).
- **Standards** β€” THEMIS delegations export as **biscuit** tokens
  ([`docs/standards-interop.md`](docs/standards-interop.md)); the gate is
  **machine-checked** ([`docs/formal-model.md`](docs/formal-model.md)).
- **Deployment** β€” an **escalation β†’ approval** workflow (signed, one-time),
  **sub-agent** attenuated delegation, and **AP2 payment mandates**
  ([`docs/payments-ap2.md`](docs/payments-ap2.md)).

Only distributed operation (multi-node SAGE + revocation propagation) remains.

**v2:**

- **Multi-connector capture** in `eidolon.capture` β€” a registry of consent-gated
  source connectors (documents, messages, calendar, email, code) with per-source
  normalizers; `ingest_all` captures several sources at once (each still requires
  its own `ConsentGrant`), and `register_source` lets new profiles add sources.
  Endpoints: `GET /capture/sources`, `POST /capture/ingest_multi`.


- **Aspirational-self / coaching layer** in `eidolon.coaching` β€” reads ETHOS
  version diffs and the HORKOS attestation ledger, compares the twin's actual
  behavior against a declared `Aspiration`, and returns advisory coaching notes
  (under/over-escalation, acting on thin confidence, policy drift). **Fully
  decoupled:** an import-graph test proves the decision path never imports it,
  and a behavioral test proves running the coach changes zero decisions. It
  writes nothing back to the operating model. Endpoint: `POST /coaching/report`.


- **Self-generated procedural skills** (Hermes-style) in `eidolon.skills` β€” the
  twin learns a reusable plan from a completed session (`synthesize`), stores it
  principal-scoped on SAGE (`SkillLibrary`), and replays it (`SkillExecutor`).
  **Subordinate to ETHOS/THEMIS:** every replayed step is re-resolved through
  KAIROS, so a skill learned under broad authority yields nothing it isn't
  currently authorized for β€” verified by a "cannot smuggle authority" test.
  Endpoints: `POST /skills`, `GET /skills`, `POST /skills/run`.


- **BASANOS integrity face** β€” adversarial suites (memory-poisoning, injection,
  scope-evasion) in `eidolon.basanos.integrity`, an `IntegrityCertificate`, and
  integrity gating of the autonomy ceiling (enable globally with
  `EIDOLON_REQUIRE_INTEGRITY_CERTIFICATION=true`).
- **`offensive-security` profile** β€” a governance-only red-teamer pack for an
  authorized, time-boxed engagement in a **CTF/lab range** (Β§12). Per the
  permanent non-goal (Β§2.3) it ships **no offensive capability** β€” it governs
  authority over range-bound tools. Safe-by-construction: `lab_only`,
  `authorization_required`, and `requires_integrity_certification` are set, so
  KAIROS integrity-gates every acting decision even with the global flag off;
  every impactful class (exploit/credential/lateral-movement/persistence)
  always escalates and can never reach an unattended acting level; hard
  exclusions deny out-of-scope targets, production, third parties, exfiltration,
  destruction, and DoS.

Every deferred item from PRD Β§12 is now built. Richer mandate selector types
arrive naturally with each new profile (the manifest's `scope_selectors` is
open-ended).

## License

Apache-2.0.