cd /news/ai-agents/show-hn-agentgate-signed-receipts-fo… · home topics ai-agents article
[ARTICLE · art-115581] src=github.com ↗ pub= topic=ai-agents verified=true sentiment=· neutral

Show HN: AgentGate – signed receipts for AI agent SaaS actions

Clawdlinux released AgentGate, an API gateway that lets AI agents call SaaS APIs on behalf of users without exposing tokens, and provides signed, gap-free receipts for every action, including failed attempts. The gateway handles OAuth, encrypted token storage, and request proxying, and its receipts can be verified offline by anyone with a copy of the SQLite log and a pinned trust file. The project is available as a Docker image on GHCR and includes a standalone verifier tool.

read7 min views15 publishedAug 30, 2026
Show HN: AgentGate – signed receipts for AI agent SaaS actions
Image: Michielbdejong (auto-discovered)

A thin API gateway that lets AI agents call SaaS APIs (GitHub, Slack, Google Workspace) on behalf of users. Agents never see tokens — the gateway handles OAuth, encrypted token storage, and request proxying. Every action gets a signed, gap-free receipt that anyone can verify offline, without AgentGate's secret key.

Run the released image. No clone, no build, no Go toolchain — just the image published to GHCR on every tagged release:

mkdir -p data
docker run -d --name agentgate \
  -p 8080:8080 \
  -e AGENTGATE_VAULT_KEY=dev-key-change-in-production-32b \
  -e AGENTGATE_ADMIN_SECRET=admin-dev-secret-change-me!! \
  -v $(pwd)/data:/data \
  ghcr.io/clawdlinux/agentgate:latest

It bootstraps one agent API key on first boot and logs it once:

docker logs agentgate | grep agent_key

Call an action. Without a linked account this returns token_missing

— the point being that a receipt is still committed for the attempt, not just for successful calls, so the audit trail can't have quiet gaps:

curl -s -X POST http://localhost:8080/v1/act \
  -H "Authorization: Bearer <agent-key-from-above>" \
  -H "Content-Type: application/json" \
  -d '{"service":"github","action":"list_repos","on_behalf_of":"demo-user","params":{"per_page":1}}'

Connect a real account. Register a GitHub OAuth App once at github.com/settings/developers (callback URL http://localhost:8080/auth/callback/github

), pass GITHUB_CLIENT_ID

/GITHUB_CLIENT_SECRET

as extra -e

flags on the docker run

above, then get the authorization link and open it in a browser:

curl -s -X POST http://localhost:8080/admin/link \
  -H "X-Admin-Secret: admin-dev-secret-change-me!!" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"demo-user","service":"github"}'

Inspect the receipt ledger. Open http://localhost:8080/dashboard/ and enter the gateway URL, then the value of

AGENTGATE_ADMIN_SECRET

. The dashboard checks the visible receipt chain locally. Use the standalone verifier below for full cryptographic proof.Verification never depends on the gateway, Docker, or a Go toolchain — it reads the SQLite receipt log and a pinned trust file directly. Anyone with a copy of agentgate.db

and a trust file can run this, including someone who has never seen this repo:

curl -s http://localhost:8080/v1/receipts/pubkey -o trust.json

tar xzf agentgate_<version>_<os>_<arch>.tar.gz

./agentgate-verify --source sqlite --path ./data/agentgate.db --trust-root ./trust.json

No network call happens during verification itself — trust.json

is a pinned local file, and agentgate-verify

only reads the local SQLite file. Pass --expected-head <seq>:<hash>

(from a checkpoint recorded separately, e.g. at handoff to an auditor) to also assert completeness, not just chain integrity. For scripts, add --format json

to receive one machine-readable result object while keeping the same exit codes. Add --quiet

(or -q

) to text output to print only the PASS:

summary on successful verification.

To build from source instead of pulling the release image — useful when iterating on the gateway itself — docker compose

still works and rebuilds the image on every up

:

git clone https://github.com/Clawdlinux/agentgate.git
cd agentgate
docker compose up -d --build

It reads OAuth credentials from a .env

file (see .env.example

) instead of inline -e

flags, and the same /v1/act

and /admin/link

calls above work against it unchanged.

The compose image is also built FROM scratch

(no shell, no package manager — see the comment in Dockerfile

), so verifying "inside" the container isn't an option here either. Use the same standalone flow from the Auditing section above, pointed at compose's bind-mounted ./data/agentgate.db

: curl

the trust root, download or make build-verify

the verifier, and run it against the host path directly.

Stop and restart the container (docker compose restart agentgate

) and run the verify command again — the signing identity and the receipt are still there, from the bind-mounted ./data/agentgate.db

.

Agent → POST /v1/act → [Auth MW] → [Registry] → [Vault: get token] → [Proxy: call upstream] → Response
┌─────────────────────────────────────────────────────┐
│                    AI AGENT                          │
│  POST http://localhost:8080/v1/act                   │
│  { service, action, on_behalf_of, params }           │
└──────────────────────┬──────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────┐
│                 AGENTGATE GATEWAY                    │
│                                                      │
│  Auth MW → Registry → Vault → Proxy → Upstream       │
│  Rate Limiter │ Audit Logger │ OAuth Callbacks        │
└──────────────────────┬──────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────┐
│               UPSTREAM SaaS APIs                     │
│      GitHub  │  Slack  │  Google Workspace           │
└─────────────────────────────────────────────────────┘

Execute a SaaS API action on behalf of a user.

Request:

{
  "service": "github",
  "action": "list_repos",
  "on_behalf_of": "user-42",
  "params": {"type": "owner", "sort": "updated"}
}

Headers: Authorization: Bearer <agent-api-key>

Response (success):

{
  "status": 200,
  "body": [{"id": 1, "name": "my-repo"}],
  "latency_ms": 142
}

Response (error):

{
  "error": "no token for user user-42 on service github",
  "code": "token_missing"
}

List available services.

Describe a service and its actions.

Health check endpoint.

All admin endpoints require X-Admin-Secret

header.

Create a new agent API key.

{"name": "my-agent", "allowed_services": ["github", "slack"], "allowed_users": ["user-42"]}

Revoke an API key.

Get OAuth authorization URL for user account linking.

{"user_id": "user-42", "service": "github"}

Connect a bearer token for Slack, Stripe, or Calendly. The token is encrypted in the vault and never returned.

{"user_id": "user-42", "service": "stripe", "access_token": "<stripe-token>"}

List linked services for a user (no token values exposed).

OAuth redirect handler — exchanges code for tokens, stores encrypted in vault.

import "github.com/Clawdlinux/agentgate/pkg/sdk"

client := sdk.NewClient("http://localhost:8080", "ag_live_...")

// Call any service
resp, err := client.Act(ctx, sdk.ActRequest{
    Service:    "github",
    Action:     "list_repos",
    OnBehalfOf: "user-42",
})

// Convenience helpers
resp, err := client.GitHub(ctx, "user-42", "list_repos", nil)
resp, err := client.Slack(ctx, "user-42", "post_message", map[string]interface{}{"channel": "#general", "text": "Hello"})
resp, err := client.Act(ctx, sdk.ActRequest{Service: "google_workspace", Action: "list_labels", OnBehalfOf: "user-42"})

// Stripe remains fully functional though unfeatured at launch:
resp, err := client.Stripe(ctx, "user-42", "list_invoices", map[string]interface{}{"limit": 10})

// Error handling
if sdk.IsTokenMissing(err) {
    // User needs to link their account
}
if sdk.IsRateLimited(err) {
    // Back off and retry
}

Agent keys are scoped (service × user). Agents can only access what's explicitly granted.Tokens encrypted at rest. AES-256-GCM with 32-byte key from environment.No token exposure. Agents never see OAuth tokens — only the gateway touches them.Signed receipts. Every authenticated action attempt commits one Ed25519-signed, hash-chained receipt before the response is returned — verify offline withagentgate-verify

, no gateway state or private key needed.Rate limiting. Per-(agent, service) token bucket prevents runaway API usage.OAuth state encrypted with AES-256-GCM and expires after 10 minutes.OAuth refresh. Configured OAuth providers refresh tokens expiring within 5 minutes before dispatch. A failed refresh returnstoken_expired

without calling the SaaS API.

Variable Description Required
AGENTGATE_VAULT_KEY
32-byte encryption key for the token vault and the receipt signing key Yes
AGENTGATE_ADMIN_SECRET
Secret for admin API access Yes
AGENTGATE_PUBLIC_URL
Base URL used to build the OAuth callback (default http://localhost:8080 )
No
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET
GitHub OAuth credentials For GitHub OAuth
SLACK_CLIENT_ID / SLACK_CLIENT_SECRET
Slack OAuth credentials For Slack OAuth
GOOGLE_WORKSPACE_CLIENT_ID / GOOGLE_WORKSPACE_CLIENT_SECRET
Google OAuth credentials, requesting only the narrow gmail.labels scope
For Google Workspace OAuth
STRIPE_CLIENT_ID / STRIPE_CLIENT_SECRET
Stripe OAuth credentials (Stripe remains configured and functional, just not a featured launch connector) For Stripe OAuth

Agent API keys are bootstrapped automatically on first boot and logged once — there is no env var for a pre-supplied key (they are bcrypt-hashed in SQLite; see POST /admin/keys

to create more).

Service configurations are YAML files in configs/services/

. See configs/services/google_workspace.yaml

for an example. The gateway itself loads the merged configs/services.yaml

.

Go 1.22+— single binary, no runtime dependencies** SQLite**— embedded database for keys, tokens, audit log** AES-256-GCM**— token encryption at rest** bcrypt**— API key hashing** Docker**— containerized deployment

make build

make test

make run

make lint

Contributions are welcome. See CONTRIBUTING.md for prerequisites, build/test/lint instructions, focused pull request guidance, and DCO sign-off. Issues labeled good first issue are scoped to be independently testable without needing secrets or maintainer context.

AgentGate is licensed under the Apache License 2.0. See LICENSE.

── more in #ai-agents 4 stories · sorted by recency
── more on @clawdlinux 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-agentgate-si…] indexed:0 read:7min 2026-08-30 ·