Token Delivery at Scale
Use SatGate Mint with your identity provider to auto-issue tokens to agents — no manual minting.
The problem
Manually minting tokens works for 5 agents. It doesn't work for 500. When your platform team is deploying AI agents across Kubernetes clusters, CI pipelines, and multi-cloud environments, you need tokens issued automatically based on the agent's identity — not a human pasting secrets.
How Mint works
SatGate Mint is a token issuance service that maps workload identity to capability tokens. The flow:
- Agent starts up and presents its identity proof — a JWT from its identity provider (OIDC token, K8s service account token, AWS STS token)
- Mint verifies the identity against configured trust policies
- Mint issues a macaroon with caveats derived from the identity claims (tenant, department, budget, TTL)
- Agent uses the macaroon to connect through SatGate — no human in the loop
Agent (K8s Pod) SatGate Mint SatGate Proxy
│ │ │
├── Present SA token ────────────→ │ │
│ ├── Verify with K8s OIDC │
│ ├── Match trust policy │
│ ├── Mint macaroon (budget, TTL) │
│ ← Return capability token ────┤ │
│ │ │
├── Connect with macaroon ──────────────────────────────────────→ │
│ ├── Validate
│ ← Tools available ─────────────────────────────────────────────┤Supported identity providers
Kubernetes
Service account tokens (projected volume). Mint verifies via the cluster's OIDC discovery endpoint. Each namespace/SA maps to a trust policy.
AWS IAM
IAM role credentials via STS. Agents running on EC2, ECS, or Lambda present their role identity. Mint verifies via AWS STS GetCallerIdentity.
OIDC (Generic)
Any OIDC-compliant IdP — Azure AD, Google Workspace, Okta, Auth0. Mint verifies the JWT signature against the IdP's JWKS endpoint.
GitHub Actions
OIDC tokens from GitHub's token endpoint. CI/CD pipelines get scoped tokens per workflow run — no stored secrets.
Setting up a trust policy
Trust policies define which identities get which tokens. Configure them in the Mint dashboard or via API.
Example: Kubernetes service account
# Trust policy: issue tokens to agents in the "ai-agents" namespace
provider: kubernetes
issuer: "https://oidc.eks.us-east-1.amazonaws.com/id/ABCDEF"
audience: "satgate"
trust_rules:
- match:
namespace: "ai-agents"
service_account: "research-agent"
issue:
budget_credits: 1000
ttl: "8h"
caveats:
- "department = research"
- "cost_center = eng-ai"
- match:
namespace: "ai-agents"
service_account: "*"
issue:
budget_credits: 200
ttl: "4h"
caveats:
- "department = general"Example: GitHub Actions
# Trust policy: CI agents get limited tokens per workflow run
provider: oidc
issuer: "https://token.actions.githubusercontent.com"
audience: "satgate"
trust_rules:
- match:
claims:
repository: "acme-corp/ai-pipeline"
ref: "refs/heads/main"
issue:
budget_credits: 500
ttl: "1h"
label_template: "ci-{{ .Claims.run_id }}"
caveats:
- "department = ci-cd"Public issue/pay/verify quickstart
This path uses the public mock IdP and seeded demo Mint policies. It does not require dashboard login. Copy it into a clean shell to issue a capability token, hit the L402-only paid route, then verify an Evidence Pack.
set -euo pipefail
# 1) Issue: get a signed workload identity JWT from the public mock IdP.
curl -sS -X POST https://satgate-mock-idp.fly.dev/token -H "Content-Type: application/json" -d '{"preset":"research-agent"}' > /tmp/satgate-idp.json
IDENTITY_JWT=$(python3 - <<'PY'
import json
print(json.load(open('/tmp/satgate-idp.json'))['token'])
PY
)
# Exchange the JWT for a SatGate capability token. The cloud route uses the
# seeded public demo policies for research-agent/data-pipeline/ci-runner/intern-bot.
curl -sS -X POST https://cloud.satgate.io/api/mint/exchange -H "Content-Type: application/json" -d "{"credentials":"$IDENTITY_JWT"}" > /tmp/satgate-capability.json
CAPABILITY=$(python3 - <<'PY'
import json
print(json.load(open('/tmp/satgate-capability.json'))['token'])
PY
)
# 2) Pay: call the L402-only paid route. First call returns a 402 L402 challenge.
# The capability proves issue worked; the paid route answers with an L402 invoice.
# To prove the route is L402-only, do not send a payment credential yet.
curl -i https://satgate-gateway.fly.dev/paid/api -H "X-SatGate-Tenant: b605e0f0-053a-40d2-97f4-7434e28bf947"
# The 402 body includes invoice, macaroon, payment_hash, amount, and unit.
# In production, pay the invoice with an L402-capable wallet/client and retry
# with the returned L402 authorization token.
# 3) Verify: fetch and cryptographically verify a live Evidence Pack.
PACK_URL=https://api.satgate.io/v1/evidence/evid_LrlgUSR1R3SEYtxy0npX7mgneWZFa5ek
curl -sS "$PACK_URL" -o /tmp/satgate-evidence-pack.json
curl -sS https://raw.githubusercontent.com/SatGate-io/satgate-enterprise/main/demo/verify_evidence_pack.py -o /tmp/verify_evidence_pack.py
python3 /tmp/verify_evidence_pack.py --discover-jwks --require-trusted-issuer "$PACK_URL"Seeded public policies
research-agent— Kubernetes-style service account, 1000 credits/day, delegation enableddata-pipeline— data pipeline agent, scoped paid-route accessci-runner— GitHub Actions-style OIDC identityintern-bot— restricted service account, no delegation
Agent-side integration
From the agent's perspective, getting a token is one HTTP call:
import requests
# Get the workload identity token (K8s projected volume)
with open("/var/run/secrets/tokens/satgate-token") as f:
identity_token = f.read()
# Exchange for a SatGate capability token
resp = requests.post(
"https://cloud.satgate.io/api/mint/exchange",
headers={"Authorization": f"Bearer {identity_token}"},
json={"credentials": identity_token}
)
satgate_token = resp.json()["token"]
# Now connect through SatGate
from satgate import SatGateClient
client = SatGateClient(
url="https://satgate-mcp-saas.fly.dev", # or https://gw.yourdomain.com/mcp for hybrid
token=satgate_token
)Kubernetes pod spec
apiVersion: v1
kind: Pod
spec:
serviceAccountName: research-agent
containers:
- name: agent
image: acme/research-agent:latest
env:
- name: SATGATE_URL
value: "https://satgate-mcp-saas.fly.dev"
- name: SATGATE_MINT_URL
value: "https://cloud.satgate.io/api/mint/exchange"
volumeMounts:
- name: satgate-token
mountPath: /var/run/secrets/tokens
readOnly: true
volumes:
- name: satgate-token
projected:
sources:
- serviceAccountToken:
audience: satgate
expirationSeconds: 3600
path: satgate-tokenProduction pattern: zero-touch agent provisioning
In production, agents never see credentials. The entire flow is automated by infrastructure:
1. Platform team configures trust policies
In the Mint dashboard, define which identities get which capabilities. Policies map identity claims (namespace, role, repository) to budgets, TTLs, and scopes.
2. Agent starts and auto-authenticates
When a pod starts, a CI job runs, or a Lambda fires — the agent reads its environment identity and exchanges it in one call:
# Init container or agent startup
import requests, os
# Identity is already in the environment — no secrets to manage
identity = open("/var/run/secrets/tokens/satgate-token").read()
# One call to get a capability token
token = requests.post(
os.environ["SATGATE_MINT_URL"],
json={"credentials": identity}
).json()["token"]
# Token has: tenant binding, budget, scope, TTL — all from policy
# Agent connects and starts calling tools
os.environ["SATGATE_TOKEN"] = token3. Scale to thousands
This pattern scales linearly. Each agent independently authenticates — no central secret store, no credential rotation pipeline, no shared API keys that one breach could compromise. When you add a new agent class, add a trust policy. When you decommission agents, revoke the policy. The identity provider handles the rest.
┌─────────────────────────────────────────────────────────────┐
│ Your Infrastructure │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Agent A │ │ Agent B │ │ Agent C │ │ Agent D │ │
│ │ (K8s SA) │ │ (IAM) │ │ (OIDC) │ │ (GitHub) │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │ │
│ └──────────────┴──────────────┴──────────────┘ │
│ │ │
│ POST /api/mint/exchange │
│ { credentials: <identity_token> } │
│ │ │
│ ┌──────┴──────┐ │
│ │ SatGate │ │
│ │ Mint │ │
│ │ │ │
│ │ Verify → │ │
│ │ Match → │ │
│ │ Issue → │ │
│ └──────┬──────┘ │
│ │ │
│ Macaroon (tenant + budget + scope) │
│ │ │
│ ┌──────┴──────┐ │
│ │ SatGate │ │
│ │ MCP Proxy │──→ Upstream Tools │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘CI/CD integration (GitHub Actions)
# .github/workflows/agent-job.yml
jobs:
run-agent:
permissions:
id-token: write # Required for OIDC token
steps:
- name: Get SatGate token
run: |
# GitHub provides the OIDC token automatically
IDENTITY=$(curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=satgate" | jq -r '.value')
# Exchange for SatGate capability token
SATGATE_TOKEN=$(curl -s -X POST $SATGATE_MINT_URL \
-H "Content-Type: application/json" \
-d "{\"credentials\": \"$IDENTITY\"}" | jq -r '.token')
echo "SATGATE_TOKEN=$SATGATE_TOKEN" >> $GITHUB_ENV
- name: Run AI agent
run: |
# Agent uses the token — scoped to this workflow, this repo, this run
python agent.py --satgate-token $SATGATE_TOKENToken lifecycle
- Auto-rotation. Agents can re-exchange their identity token before the macaroon expires. The SDK handles this automatically with a refresh loop.
- Budget inheritance. Trust policies define the budget ceiling. Agents can't request more than the policy allows. Delegation from Mint-issued tokens still works — agents can create child tokens with lower budgets.
- Revocation. Revoke individual tokens or invalidate an entire trust policy. Policy revocation immediately stops new issuance; existing tokens expire at their TTL.
- Audit trail. Every Mint exchange is logged — which identity, which policy matched, what caveats were applied. Visible in the Audit Log dashboard.
Why this matters
The shift: Traditional API gateways authenticate with static API keys or OAuth client credentials. Both require secrets management infrastructure. With Mint, your agents authenticate with the identity they already have — their K8s service account, their IAM role, their OIDC token. No secrets to rotate. No credentials to leak. The identity is the credential.