agentgate
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Fail
- process.env — Environment variable access in examples/adapter-demo.ts
- process.env — Environment variable access in examples/marketgate/mock-exchange.ts
- process.env — Environment variable access in examples/marketgate/toy-trader.ts
- network request — Outbound network request in examples/marketgate/toy-trader.ts
- network request — Outbound network request in examples/toy-agent.ts
- crypto private key — Private key handling in examples/toy-agent.ts
- network request — Outbound network request in scripts/spam-sim.ts
- crypto private key — Private key handling in scripts/spam-sim.ts
- network request — Outbound network request in src/agent-adapter.ts
- crypto private key — Private key handling in src/agent-adapter.ts
- process.env — Environment variable access in src/app.ts
- fs.rmSync — Destructive file system operation in src/backup.ts
- process.env — Environment variable access in src/dashboard.ts
- exec() — Shell command execution in src/db.ts
Permissions Pass
- Permissions — No dangerous permissions requested
This tool is a collateralized execution engine that acts as an accountability layer for AI agents. It uses Ed25519 identities and a bond-and-slash system to govern autonomous agents' external actions, requiring them to lock up collateral before executing operations.
Security Assessment
The overall risk is High. The tool handles cryptographic private keys directly and accesses environment variables to manage sensitive configurations. While network requests are expected for an API/action gatekeeper, the core engine contains two critical security failures: it performs destructive file system operations (`rmSync`) and executes raw shell commands (`exec()`). There are no hardcoded secrets, but the combination of shell execution and destructive file operations within the server code presents a significant attack surface that could lead to remote code execution or data loss if exploited.
Quality Assessment
The project is actively maintained, with its most recent push occurring today. It is properly licensed under the permissive and standard MIT license. However, community trust and visibility are currently very low. With only 5 GitHub stars, the tool has not yet been widely peer-reviewed or battle-tested by the broader developer community, making it a highly experimental project.
Verdict
Use with caution — the core architecture provides a unique accountability mechanism, but the inclusion of raw shell execution and destructive file operations in the source code requires a thorough manual code review before deploying it in any production environment.
Collateralized execution engine for AI agents: bond-and-slash accountability with Ed25519 identities, bounded exposure, and progressive trust tiers.
AgentGate
AgentGate is a collateralized execution engine for AI agents: Ed25519 identities, reusable bonds, bounded exposure, settlement and slashing, and progressive trust tiers at the point where agents attempt external actions.
[!IMPORTANT]
Where To StartAgentGate is the deeper accountability substrate underneath parts of this ecosystem. It is usually not the first repo a cold reader should start with.
Recommended order for a first read:
- Governed WriteFile Demo for the narrowest end-to-end proof path
- MCP Firewall for the governed tool-call layer built on top
- AgentGate (this repo) for the underlying execution engine
Start here when you want the engine itself: identity registration, bond lifecycle, bounded exposure accounting, settlement and slashing, and transport/security mechanics.
AgentGate is the bond-and-slash substrate itself. It requires signed identities, lets agents lock reusable bond capacity against declared exposure, and records settlement outcomes in durable audit state at the deterministic choke point between agents and external actions.
Visual Explainer
Prefer a short visual overview before reading? Watch the 4-part intro on X:
AgentGate explainer thread (4 short videos)
Covers:
- what AgentGate is
- why permissions are not enough
- a concrete MCP Firewall example
- why runtime accountability matters
What AgentGate Does
AgentGate sits between an autonomous agent and an external action such as an API call, bid, or financial operation. Before the action runs, the agent authenticates with an Ed25519 identity and locks bond capacity against a declared exposure.
If the action resolves cleanly, the reserved exposure is settled and released. Manual malicious resolution now requires two distinct eligible resolver identities before the slash finalizes, while expired-action sweeps still slash immediately. The system keeps that lifecycle in durable audit state and uses prior outcomes to gate larger bond sizes through progressive trust tiers.
This is a narrow runtime accountability mechanism, not a general AI safety or general agent security solution.
The actions table serves double duty: it's both a real-time enforcement log for slashing and a durable post-incident audit trail that can support disclosure to affected parties. The threat model doc covers this in more detail.
Threat Model → — What AgentGate defends against, what it doesn't, and why.
Read the full story: How I Built AgentGate
Quick Integration
AgentGate works with any agent that can make HTTP requests. If you want the shortest outsider-readable proof, start with the Governed WriteFile Demo. If you want the governance layer that fronts MCP tools, see MCP Firewall. The flow below is the direct engine integration path: register an identity, lock a bond, execute an action against that bond, and resolve the outcome.
Note: The curl examples below assume
AGENTGATE_DEV_MODE=truefor local development (auth key enforcement is skipped). In production, add-H 'x-agentgate-key: YOUR_KEY'to every POST request.
1. Register an identity
Identity registration requires proof-of-possession — the caller must sign the request with the private key matching the public key being registered. This uses the same signature headers as all other state-changing endpoints:
curl -s http://127.0.0.1:3000/v1/identities \
-H 'content-type: application/json' \
-H "x-agentgate-timestamp: $TIMESTAMP" \
-H "x-agentgate-signature: $SIGNATURE" \
-H "x-nonce: $(uuidgen)" \
-d '{ "publicKey": "<base64-encoded Ed25519 public key>" }'
Returns an identityId (e.g., id_abc123).
2. Lock a bond
All state-changing requests must be signed. Headers required on every request:
x-agentgate-timestamp— current time in epoch millisecondsx-agentgate-signature— Ed25519 signature oversha256(nonce + method + path + timestamp + JSON.stringify(body))x-nonce— a unique string per request (UUID recommended); bound into the signed message AND stored server-side — the server rejects duplicates per identity, providing replay protection on top of the timestamp window
curl -s http://127.0.0.1:3000/v1/bonds/lock \
-H 'content-type: application/json' \
-H "x-agentgate-timestamp: $TIMESTAMP" \
-H "x-agentgate-signature: $SIGNATURE" \
-H "x-nonce: $(uuidgen)" \
-d '{ "identityId": "id_abc123", "amountCents": 5000, "currency": "USD", "ttlSeconds": 300, "reason": "marketplace bid" }'
Returns a bondId.
3. Execute a bonded action
curl -s http://127.0.0.1:3000/v1/actions/execute \
-H 'content-type: application/json' \
-H "x-agentgate-timestamp: $TIMESTAMP" \
-H "x-agentgate-signature: $SIGNATURE" \
-H "x-nonce: $(uuidgen)" \
-d '{ "identityId": "id_abc123", "bondId": "bond_xyz", "actionType": "place-bid", "payload": { "item": "widget-42", "price": 1500 }, "exposure_cents": 1500 }'
Returns an actionId. The bond's available capacity is reduced by ceil(exposure_cents × 1.2).
4. Resolve the action
curl -s http://127.0.0.1:3000/v1/actions/<actionId>/resolve \
-H 'content-type: application/json' \
-H "x-agentgate-timestamp: $TIMESTAMP" \
-H "x-agentgate-signature: $SIGNATURE" \
-H "x-nonce: $(uuidgen)" \
-d '{ "outcome": "success", "resolverId": "id_resolver123" }'
Outcome must be one of: success, failed, or malicious. On success or failed, that action's reserved exposure is settled and released immediately. On manual malicious, the first eligible resolver vote returns a pending response with finalized: false, actionStatus: "open", maliciousVotes: 1, and maliciousVotesRequired: 2; the second distinct eligible resolver vote finalizes the slash. Sweeper auto-slash on expired actions remains immediate.
Common Errors
| Error | Cause | Fix |
|---|---|---|
INVALID_SIGNATURE |
Missing signature headers, stale timestamp, or signature doesn't match the signed payload | Verify you're signing sha256(nonce + method + path + timestamp + JSON.stringify(body)) with the correct private key and a fresh timestamp |
MISSING_NONCE |
x-nonce header is missing |
Send a fresh nonce on every POST request |
DUPLICATE_NONCE |
Same nonce reused by the same identity | Generate a fresh UUID for every request |
DUPLICATE_MALICIOUS_VOTE |
Same resolver tried to cast a second malicious vote for the same open action | Use a different eligible resolver identity for the second malicious vote |
TIER_BOND_CAP_EXCEEDED |
Bond amount exceeds identity's trust tier cap | Build reputation with successful resolutions to unlock higher tiers |
INSUFFICIENT_BOND_CAPACITY |
Bond doesn't have enough remaining capacity | Lock a larger bond or resolve outstanding actions to free capacity |
RATE_LIMIT_EXCEEDED |
More than 10 executes in 60 seconds for this identity | Wait and retry, or spread actions across a longer window |
IDENTITY_BANNED |
Identity has been banned (manually or after 3 malicious resolutions) | Contact the operator or use a different identity |
SERVER_MISCONFIGURED |
Auth key not set and AGENTGATE_DEV_MODE is not true |
Set the missing auth key or set AGENTGATE_DEV_MODE=true for local dev |
For the full security posture, see the Threat Model.
Core Concepts
Identity
- Ed25519 public key (raw 32-byte base64)
- All state-changing endpoints require signed requests — including identity registration itself, which requires proof-of-possession (the caller must sign the request with the private key matching the public key being registered)
- Public key uniqueness enforced at the database level — duplicate identity registration is rejected with
409 DUPLICATE_IDENTITY - Replay protection via timestamp validation (60-second window, 5-second future tolerance) AND nonce store (duplicate rejection per identity)
- Named agent support: set
AGENTGATE_AGENT_NAMEenv var to create separate identity files per agent (e.g.,agent-identity-trader.json)
Signed message format: sha256(nonce + method + path + timestamp + JSON.stringify(body))
Required headers: x-agentgate-timestamp, x-agentgate-signature, x-nonce
Reusable Bond Model
Bonds are not single-use. Each bond represents reusable execution capacity.
- Capacity rule: effective exposure =
ceil(declared_exposure × 1.2) - Constraint:
outstanding_exposure_cents + effective_exposure <= amount_cents - If exceeded →
INSUFFICIENT_BOND_CAPACITY - TTL cap: maximum 86400 seconds (24 hours) — requests exceeding the cap are rejected
- Bond status lifecycle during normal settlement:
active→occupied(when action attached) →released/burned/slashed - Idle bonds that are touched after their TTL has elapsed are marked
expired; expired bonds with open actions are handled by the sweeper
Exposure Lifecycle
Bonds support multiple concurrent actions. Each action reserves its own slice of the bond's capacity, and resolving one action only releases that action's exposure — other open actions on the same bond are unaffected.
- Execute: exposure reserved,
outstanding_exposure_centsincremented, bond markedoccupied - Resolve (success): that action's exposure released;
refund_centsaccumulated on the bond; when the last open action settles and no prior burn/slash exists, the bond closes asreleased - Resolve (failed): that action's exposure released; 95% of that action's effective exposure goes to
refund_cents, 5% toburned_cents; when the last open action settles and no slash exists, the bond closes asburned - Resolve (malicious): manual resolution uses a narrow dual-control seam: the first eligible non-executor resolver records a pending malicious vote and leaves the action open; the second distinct eligible resolver releases that action's exposure, reduces
amount_cents(clamped at zero), increasesslashed_cents, and closes the bond asslashedwhen no open actions remain - Settlement accounting:
refund_cents,burned_cents, andslashed_centsaccumulate as each action settles;closed_atis written when the last open action on the bond resolves
Auto-Slash Sweeper
A background sweeper runs every 60 seconds, checking for actions whose associated bond has expired while the action is still open. Any such action is automatically resolved as malicious and slashed immediately; the sweeper does not go through the manual dual-control vote path. On the same 60-second interval, the server also cleans up expired nonces (older than 5 minutes) and expired rate-limit buckets (older than 60 seconds). All three run with clean shutdown on SIGINT/SIGTERM.
Reputation Scoring & Trust Tiers
Each identity accumulates a reputation score based on its history:
score = locks×2 + actions×3 + successes×10 - failures×5 - malicious×20
The dashboard shows per-identity scores with color coding (green for positive, red for negative, gray for zero). Available via the get_reputation MCP tool or the dashboard.
Progressive Trust Tiers — bond capacity is reputation-gated. Tiers are purely computed from resolution history at bond-lock time (no stored state):
| Tier | Label | Requirement | Bond Cap |
|---|---|---|---|
| 1 | New | Default | 100¢ |
| 2 | Established | 5 qualifying successes from 2 distinct resolvers, 0 malicious | 500¢ |
| 3 | Trusted | 20 qualifying successes from 20 distinct resolvers, 0 malicious | No tier cap (normal capacity rules) |
- Any malicious resolution forces immediate demotion to Tier 1
- Only successes with at least 100¢ effective exposure count toward promotion
- Trust tiers are computed from the resolution history record only: outcome, effective exposure, and resolver identity
- Banned identities are blocked separately; the dashboard keeps showing the history-based tier and adds a
[BANNED]tag - Exceeding the tier cap returns
403 TIER_BOND_CAP_EXCEEDED
Recent hardening
- What changed: manual
maliciousaction resolution now requires two distinct eligible resolver identities; the first vote is stored as a pending malicious vote, the second vote finalizes the existing slash path, andsuccess,failed, plus sweeper auto-slash stay unchanged. - How to verify: run
npm test -- --run test/app.test.ts test/trust-tier.test.ts test/sweeper.test.ts, thennpm test,npm run lint, andnpm run build. - Next steps: if you ever need broader adjudication than this one manual malicious seam, add it as a separate design instead of expanding this into a general quorum system.
Outbound HTTP Safety (market.http)
- Host:port allowlist — each allowlist entry is a
host:portpair (e.g.,localhost:3000). Wildcard port supported (e.g.,localhost:*). Default allowlist is fail-closed to loopback ports 80 and 443 only; local dev services on custom ports must be explicitly listed inAGENTGATE_HTTP_ALLOWLIST - http/https only (default port inferred: 80 for http, 443 for https)
- Timeout (default 2500ms)
- Max request size, max response size
- Response sanitization — before persisting outbound response data to the database, headers are stripped and the body is truncated to 1024 characters (with a
[truncated]marker). This only affects storage — the full response is still returned to the caller - Redirect protection —
fetchis called withredirect: "manual"; every redirect hop has itsLocationheader re-checked against the allowlist before following (max 5 hops). Prevents an allowlisted host from acting as a trampoline to a non-allowlisted host. - Errors wrapped as
DESTINATION_BLOCKEDorREDIRECT_BLOCKED
Environment variables: AGENTGATE_HTTP_ALLOWLIST, AGENTGATE_HTTP_TIMEOUT_MS, AGENTGATE_HTTP_MAX_BODY_BYTES, AGENTGATE_MAX_RESPONSE_BYTES
MCP Transport
AgentGate exposes its 7 tools to Claude Desktop over two transports:
- Streamable HTTP (recommended) — Express server on port 3001 at
/mcp. Claude Desktop connects viamcp-remote. Sessions are managed server-side with a 100-session cap, 1MB body size limit, and automatic cleanup of sessions idle for more than 5 minutes. - stdio — launches
src/mcp/server.tsas a subprocess directly from Claude Desktop.
Both transports require the AgentGate HTTP server (npm run restart) to be running.
Claude Desktop config (HTTP transport via mcp-remote)
Local:
{
"mcpServers": {
"agentgate": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://127.0.0.1:3001/mcp"]
}
}
}
File location: ~/Library/Application Support/Claude/claude_desktop_config.json
MCP Tools (7 total)
| Tool | Description |
|---|---|
create_identity |
Create or load an Ed25519 agent identity |
lock_bond |
Lock a bond (stake) for an identity |
execute_bonded_action |
Execute a bonded action through the gate |
resolve_action |
Resolve an action as success/failed/malicious |
get_reputation |
Get identity reputation score |
create_market |
Create a prediction market with a yes/no question and resolution deadline |
resolve_market |
Resolve an open market as yes/no — settles all positions automatically |
Prediction Markets
AgentGate includes a prediction market demo that illustrates multi-agent economic coordination using bonds as stake.
How it works:
- An operator creates a market with a yes/no question and a resolution deadline (must be a valid future ISO 8601 timestamp)
- Agents take positions by executing a
market.positionaction against a locked bond, declaring asideofyesorno - When the market resolves, winning positions are settled as
successand losing positions asfailed; each position settles only its own reserved exposure, so shared bonds stayoccupieduntil every attached action is resolved
REST endpoints:
# Create a market
curl -s http://127.0.0.1:3000/markets \
-H 'content-type: application/json' \
-H 'x-agentgate-key: YOUR_KEY' \
-H "x-agentgate-timestamp: $TIMESTAMP" \
-H "x-agentgate-signature: $SIGNATURE" \
-H "x-nonce: $(uuidgen)" \
-d '{ "creatorId": "id_abc123", "question": "Will BTC hit 100k by Friday?", "resolutionDeadline": "2026-04-20T00:00:00Z" }'
# Resolve a market
curl -s http://127.0.0.1:3000/markets/<marketId>/resolve \
-H 'content-type: application/json' \
-H 'x-agentgate-key: YOUR_KEY' \
-H "x-agentgate-timestamp: $TIMESTAMP" \
-H "x-agentgate-signature: $SIGNATURE" \
-H "x-nonce: $(uuidgen)" \
-d '{ "outcome": "yes", "resolverId": "id_abc123" }'
MCP tools: create_market and resolve_market expose the same flow to Claude Desktop.
The dashboard shows a live Markets table with status color-coding (open → amber, resolved → green).
Dashboard
AgentGate includes a real-time HTML dashboard at http://127.0.0.1:3000/dashboard. It shows:
- Summary bar with identity, bond, action, and market counts
- Per-identity reputation scores with color coding and trust tier labels (Tier 1 New / Tier 2 Established / Tier 3 Trusted)
- Tables for bonds, actions, identities, and markets with truncated IDs and status indicators
- Agent names displayed per identity (for multi-agent setups)
[BANNED]tags on banned identities
The page auto-refreshes every 5 seconds. The server must be running.
Health Check
GET /health
Returns 200 OK with { "status": "ok", "timestamp": "<ISO>" }. No authentication required — designed for external uptime monitors (e.g., UptimeRobot).
Running Locally
Install:
npm install
Start server:
npm run restart
This kills any old server process on port 3000 and starts fresh. Fastify REST API runs at http://127.0.0.1:3000, MCP HTTP server at http://127.0.0.1:3001/mcp, dashboard at http://127.0.0.1:3000/dashboard. The sweeper, nonce cleanup, and bucket cleanup logs appear every 60 seconds. Database file is created automatically at data/agentgate.sqlite on first run, with automatic backups to data/backups/ on each startup (keeps the 5 most recent).
Run tests:
npm run test
123 tests across 12 test suites (API, MCP integration, prediction markets, sweeper, red team, outbound HTTP, dashboard, adapter).
Security
Nonce Replay Protection
All POST endpoints require an x-nonce header. The server stores each nonce per identity and rejects duplicates with a 409 DUPLICATE_NONCE response. The AgentAdapter generates UUID nonces automatically. Expired nonces (older than 5 minutes) are cleaned up every 60 seconds alongside the sweeper. This provides replay protection on top of the 60-second timestamp window.
MCP Endpoint Authentication
The MCP HTTP endpoint (port 3001) is protected by a shared-secret header.
- Set
AGENTGATE_MCP_KEYin your.envfile (loaded automatically via dotenv on startup) - If the key is not set and
AGENTGATE_DEV_MODEis nottrue, requests are rejected with500 SERVER_MISCONFIGURED - If the key is not set and
AGENTGATE_DEV_MODE=true, auth is skipped (suitable for local dev) - If the key is set, any request to
/mcpwithout a matchingx-agentgate-keyheader receives a401 UNAUTHORIZEDresponse
.env entry:
AGENTGATE_MCP_KEY=your-long-random-secret
REST API, Admin & Dashboard Authentication
Auth is split into three independent environment variables, each protecting a different surface:
| Variable | Protects | How it's checked |
|---|---|---|
AGENTGATE_REST_KEY |
All non-admin POST routes (bonds, actions, markets) | x-agentgate-key header |
AGENTGATE_ADMIN_KEY |
Admin endpoints (/admin/ban-identity, /admin/unban-identity) |
x-agentgate-key header |
AGENTGATE_DASHBOARD_KEY |
Dashboard (/dashboard) |
HTTP Basic Auth (username admin, password = key value) |
- Auth is required by default. If a key is not set and
AGENTGATE_DEV_MODEis nottrue, requests to that surface are rejected with500 SERVER_MISCONFIGURED - If
AGENTGATE_DEV_MODE=true, missing keys are allowed and auth is skipped for that surface (suitable for local dev) - If a key is set, requests without a valid credential receive
401 UNAUTHORIZED - Each key can be rotated independently without affecting the others
AGENTGATE_DEV_MODE=true # skip auth enforcement for local dev (default: not set — auth required)
AGENTGATE_REST_KEY=your-rest-secret
AGENTGATE_ADMIN_KEY=your-admin-secret
AGENTGATE_DASHBOARD_KEY=your-dashboard-secret
Identity Governance
Operators can ban and unban identities via the admin API. Banned identities receive 403 IDENTITY_BANNED on all lockBond and executeAction calls.
# Ban an identity
curl -s http://127.0.0.1:3000/admin/ban-identity \
-H 'content-type: application/json' \
-H 'x-agentgate-key: YOUR_KEY' \
-d '{ "publicKey": "<base64-encoded Ed25519 public key>" }'
# Unban an identity
curl -s http://127.0.0.1:3000/admin/unban-identity \
-H 'content-type: application/json' \
-H 'x-agentgate-key: YOUR_KEY' \
-d '{ "publicKey": "<base64-encoded Ed25519 public key>" }'
Auto-ban: an identity is automatically banned after 3 malicious action resolutions. The trigger logs an identity_auto_banned security event.
Security Event Logging
All security-relevant events are logged as structured JSON to stderr with an event field for easy filtering:
event |
Trigger |
|---|---|
auth_failed |
Wrong or missing x-agentgate-key on REST or MCP |
signature_failed |
Missing headers, stale timestamp, or bad Ed25519 signature |
duplicate_nonce |
Same nonce reused by the same identity |
bond_slashed |
Action resolved as malicious (via API or sweeper) |
identity_auto_banned |
Identity automatically banned after 3 malicious resolutions |
outbound_blocked |
market.http action blocked by allowlist or protocol check |
Each entry includes relevant context: identityId (truncated), endpoint, reason, and requestId where available.
Security Hardening
AgentGate v0.2.0 was put through a structured red team process before release: 20 adversarial attack scenarios written as automated tests across 5 phases.
Invariant validator — every attack test ends by calling validateInvariants(db), which runs 8 SQL assertions against the live database: bond amounts never go negative, outstanding exposure never exceeds bond capacity, settlement is always consistent, and nonces are never duplicated. Any state corruption — however subtle — causes an immediate test failure with a precise diagnostic.
Attack phases:
| Phase | Focus | Tests | Findings |
|---|---|---|---|
| 1 | Bond/exposure math | 6 | Fixed: slashed_cents not written to DB on malicious resolution; negative exposure_cents bypassed service-layer guard |
| 2 | Sweeper edge cases | 3 | Confirmed: resolve/sweep race is safe (SQLite serialization); double-slash prevented by open-action query |
| 3 | Replay attacks | 4 | Confirmed: nonce check catches replays even within the 60-second timestamp window; parallel duplicate nonce via Promise.all safely rejected |
| 4 | SQLite concurrency | 2 | Confirmed: better-sqlite3 synchronous transactions serialize concurrent requests correctly |
| 5 | Outbound HTTP | 9 | Fixed: redirect bypass SSRF (allowlisted host could 302 to non-allowlisted target); IPv6 bracket allowlist bug ([::1] vs ::1) |
Total: 3 logic bugs fixed, 1 SSRF vulnerability fixed, 29 red team tests passing.
Full attack scenarios documented in docs/red-team-plan.md.
Post-v0.2.0 hardening (Session 15): A cold-eyes security audit identified additional issues that have since been fixed:
- Timestamp validation now rejects future-dated timestamps (>5 seconds ahead) in addition to stale ones, closing a clock-skew attack vector
- Dashboard HTML output is now XSS-safe — all database-backed values are escaped via
escapeHtml()before interpolation - MCP HTTP server hardened with a 1MB body size limit, 100-session cap, and automatic cleanup of sessions idle for more than 5 minutes
- Bond locking (
POST /v1/bonds/lock) now requires Ed25519 signature verification, matching the auth model on all other state-changing endpoints - Fastify upgraded to 5.8.2
Post-v0.3.0 hardening (Session 20):
- Bond TTL capped at 24 hours (86400 seconds) — requests exceeding the cap are rejected with
400 TTL_TOO_LONG - Action payload capped at 4096 bytes — oversized payloads are rejected with
400 PAYLOAD_TOO_LARGE - SQLite WAL mode enabled for improved concurrent read performance
- SQLite busy timeout set to 5 seconds — database operations wait instead of failing immediately when the database is locked
Post-v0.3.0 hardening (Session 21):
- Fail-closed auth by default — all auth keys (
AGENTGATE_REST_KEY,AGENTGATE_ADMIN_KEY,AGENTGATE_MCP_KEY,AGENTGATE_DASHBOARD_KEY) are now required unlessAGENTGATE_DEV_MODE=trueis explicitly set. Missing keys return500 SERVER_MISCONFIGUREDinstead of silently skipping auth - SQLite CHECK constraints — database-level enforcement on bonds (
amount_cents >= 0,outstanding_exposure_cents >= 0,slashed_cents >= 0, valid status enum), actions (exposure_cents >= 0, valid status enum), and identities (status IN ('active', 'banned')). Startup data validation catches violations in existing databases - Rate-limit bucket cleanup — expired
action_execute_bucketsentries (older than 60 seconds) are now pruned on the same 60-second interval as the sweeper and nonce cleanup - Demo echo route gated —
POST /v1/demo/echois only registered whenAGENTGATE_DEV_MODE=true; returns 404 in production - Market position filtering at DB level —
resolveMarket()now usesjson_extract(payload, '$.marketId')in the SQL query instead of loading all open positions into memory - Market deadline validation —
resolutionDeadlinemust be a valid future ISO 8601 timestamp (enforced via Zod.refine()) - Payload size measured in bytes — the 4096 limit now uses
Buffer.byteLength()instead of.length, correctly measuring multi-byte characters
Post-v0.3.0 hardening (Session 22):
- Positive exposure required —
exposure_centsis now mandatory and must be a positive integer. Zero-stake actions are rejected with400 INVALID_EXPOSURE - Prediction market payload validation —
market.positionactions now require a JSON object withmarketIdandside(yesorno) before anything is persisted - Legacy malformed market rows no longer break resolution —
resolveMarket()now filters malformed JSON safely instead of letting SQLite throwmalformed JSON - Banned identities fully blocked from market writes — banned identities can no longer create markets or resolve markets they previously created
- Final bond status reflects aggregate outcomes — once the last action settles, bond status is derived from cumulative settlement totals (
slashedbeatsburned, which beatsreleased) - Safer outbound defaults — the implicit outbound allowlist no longer grants arbitrary loopback ports
- WAL-safe backups — startup backups now use SQLite’s backup API instead of copying only the main
.sqlitefile - Agent adapter auto-init fix —
AgentAdapter.lockBond(),executeBondedAction(), and market helpers now auto-create/load the identity before building signed request bodies - MCP log sanitization — MCP tool completion logs now record result shape/keys instead of dumping full tool payloads or outbound response bodies to stderr
Comprehensive Security Audit (Codex + Claude Code cross-audit, 3 passes):
The full src/ codebase was put through a structured security audit — 3 passes to clean, with each pass re-reading every source file and verifying prior fixes before looking for new issues.
- Pass 1: 22 findings (6 critical/high, 10 medium, 6 low). All fixed: self-resolution bypass, market creator arbitrage, missing signature verification on market endpoints, adapter missing auth headers, 204 response crash, identity nonce replay, market resolution ordering, silent catch-all in settlement, bond expiration TOCTOU, dashboard auth weakness, protocol-relative redirect, malformed deadline NaN bypass, agent name path traversal, dashboard XSS via title length, weak request ID entropy, nonce recording order, SQLite error detection fragility, and documentation gaps.
- Pass 2: 6 medium findings, 5 low. All fixed: upper bounds on exposure/bond amounts (1B cent cap), market deadline capped at 1 year, MCP transport close error logging, strict MCP schema types, and known-limitation documentation for accepted trade-offs.
- Pass 3: Clean. No new findings. All prior fixes verified in place.
How To Verify
Run:
npm run lint
npm run test
Regression coverage added for:
- adapter auto-identity creation
- WAL-safe backups
- banned market create/resolve attempts
- mixed-outcome bond status precedence
- explicit allowlisting for outbound HTTP tests
- market position payload validation and malformed legacy row handling
Next Steps
- Add a small admin repair tool for malformed legacy
market.positionrows so operators can settle or purge bad historical data explicitly - Add one end-to-end test for the startup backup path in
src/index.tsto exercise the async backup call in the same flow production uses
Remote Deployment (Archived)
AgentGate was previously deployed to a DigitalOcean droplet at agentgate.run with Caddy reverse proxy, UFW firewall, pm2 process management, and UptimeRobot monitoring. The live instance was decommissioned in March 2026. The project runs fully on localhost — see the "Running Locally" section above.
Ecosystem
AgentGate is the core substrate in a larger family of reference projects. For a cold first run, start with the Governed WriteFile Demo; it is the narrowest proof path. The rest of this section is for readers who want adjacent reference implementations built on the same bond-and-slash model.
Reference Agents
| # | Agent | What it proves | Tests | Repo |
|---|---|---|---|---|
| 001 | Bonded File Transform | Deterministic verification — machine checks machine | 22 | agentgate-bonded-file-transform |
| 002 | File Guardian | Command-based verification + rollback on failure | 50 | agentgate-bonded-file-guardian |
| 003 | Email Rewriter | Human judgment in the loop | 11 | agentgate-bonded-email-rewriter |
| 004 | Red Team Simulator | Adversarial probing — five stages from solo attacker to 9-agent coordinated swarms. Includes Sleeper Agent (v0.6.0). | 330 | agentgate-red-team-simulator |
| 005 | Recursive Verifier | Proof-style verification — generates executable scripts, runs in sandbox, scores, iterates | 149 | agentgate-recursive-verifier |
| 006 | Incentive Wargame | Stress-tests incentive rules with AI-generated economic strategies | 301 | agentgate-incentive-wargame |
Governance Extensions
| Project | What it adds | Status | Repo |
|---|---|---|---|
| Governed WriteFile Demo | Narrowest first-run path: identity -> bond -> authenticated governed write_file -> independent on-disk verification -> audit artifact |
v0.1.0 shipped | agentgate-governed-writefile-demo |
| Delegation Identity Proof | Bounded human-to-agent delegation with dual bonds and a 6-state machine | v0.1.0 shipped | agentgate-delegation-proof |
| MCP Firewall | Governance proxy for MCP tool calls — bonds before forwarding, slashes on bad outcomes | v0.1.0 shipped | agentgate-mcp-firewall |
| Epistemic Poisoning Simulator | Tests whether bond-and-slash can govern knowledge integrity | Design stage | — |
Writeups
- What Happens When an AI Agent Has to Post Collateral Before It Acts?
- What If Your AI Coder Had Skin in the Game? (Agent 002)
- Agent 003 writeup submitted to Coding Nexus on Medium (pending review)
Related
- RestaRules — machine-readable agent conduct rules for restaurants. A different angle on agent governance: environment-published norms rather than substrate-enforced bonds.
Tech Stack
- Language: TypeScript (100%)
- Runtime: Node.js 20+
- Web framework: Fastify
- Database: SQLite via better-sqlite3
- Validation: Zod
- Testing: Vitest (123 tests)
- MCP SDK: @modelcontextprotocol/sdk
- CI: GitHub Actions (build, lint, and test on every push and PR to main)
Project Files
src/— core server logic (Fastify API, service layer, database, signing, structured logging)src/mcp/— MCP server exposing 7 tools over stdio and Streamable HTTP transportssrc/agent-adapter.ts— clean agent-facing interface that hides signing, nonces, and HTTP detailssrc/dashboard.ts— real-time HTML dashboardsrc/backup.ts— automatic database backup on startup (keeps 5 most recent)src/reputation.ts— reputation scoring and trust tier computationtest/— 123 tests across 12 suites (API, MCP integration, prediction markets, sweeper, red team, outbound HTTP, dashboard, adapter, reputation, trust tier)examples/— demo agents and adapter demodocs/threat-model.md— threat model (attacks, defenses, non-goals, assumptions)docs/red-team-plan.md— 20 adversarial attack scenarios across 5 phasesdocs/manifesto.md— "How I Built AgentGate" — the full story.github/workflows/ci.yml— GitHub Actions CI (build, lint, and test on every push/PR)AGENTS.md— conventions for AI coding agentsLICENSE— MIT License
Roadmap
Future modules under consideration (not yet in development):
- ZK-proof bond verification — Anonymous agent identity with economic accountability. Agents prove they hold a valid bond via zero-knowledge proof without revealing which agent they are. Monitoring ZK-API (Buterin/Davide, 2026) and OpenAnonymity for integration-ready implementations.
- Non-deterministic verifiers — LLM-as-judge, API state checks, and human review verification modes. The bond loop is verifier-agnostic by design.
- Hardware-backed delegation — YubiKey/FIDO2/WebAuthn to authorize agent bonds, replacing OAuth.
License
MIT — see LICENSE.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found