Docs/Token Delivery at Scale

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:

  1. Agent starts up and presents its identity proof — a JWT from its identity provider (OIDC token, K8s service account token, AWS STS token)
  2. Mint verifies the identity against configured trust policies
  3. Mint issues a macaroon with caveats derived from the identity claims (tenant, department, budget, TTL)
  4. 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

yaml
# 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

yaml
# 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"

Agent-side integration

From the agent's perspective, getting a token is one HTTP call:

python
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://mcp.your-tenant.satgate.io",
    token=satgate_token
)

Kubernetes pod spec

yaml
apiVersion: v1
kind: Pod
spec:
  serviceAccountName: research-agent
  containers:
    - name: agent
      image: acme/research-agent:latest
      env:
        - name: SATGATE_URL
          value: "https://mcp.your-tenant.satgate.io"
        - 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-token

Production 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:

python
# 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"] = token

3. 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)

yaml
# .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_TOKEN

Token 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.

Related