Source: docs/integration/auth.md

Authentication — API key

The Forgium administrator provides an API key for each environment. Use it with

the Public API (/v1/*):

Authorization: Bearer <FORGIUM_AGENT_API_KEY>

Obtain access

Request these values from the Forgium administrator:

FORGIUM_AGENT_BASE_URL=<value issued for the target environment>
FORGIUM_AGENT_API_KEY=<value issued out of band>

Store the key in the host application's secret manager. Do not commit it,

include it in a capability manifest, or put it in logs.

If a request returns 401, ask the administrator to verify or replace the key.

Forgium-Context verification

Capability endpoints receive a short-lived Forgium-Context Ed25519

JWS. You must validate its issuer, audience, kid, time claims, exact

method and URL, body hash, and replay ID before accepting a request.

Quick start with the reference adapter

The easiest way to verify Forgium-Context is the framework-neutral reference

adapter. It works with any HTTP framework (Express, Hono, Fastify, Workers):

import { verifyForgiumContext, createInMemoryReplayStore } from "@micro-agent/capabilities/capability-context-reference";

// In your HTTP handler:
const result = await verifyForgiumContext({
  contextHeader: request.headers.get("Forgium-Context"),
  method: request.method,
  url: request.url,
  body: await request.text(),
  jwks: await fetchJwks(), // See "Retrieving JWKS" below
  issuer: "https://api.forgium.dev",
  audience: "https://your-endpoint.example",
  // Required: verification fails closed without an atomic replay store.
  rememberJti: async (jti, expiresAt) => {
    // Store in KV with TTL (see "Replay protection" below)
    const existing = await env.KV.get(`jti:${jti}`);
    if (existing) return false;
    await env.KV.put(`jti:${jti}`, "1", { expiration: expiresAt });
    return true;
  },
});

if (!result.ok) {
  // result.code is a stable error code safe for logging
  return new Response(JSON.stringify({ error: result.code }), {
    status: 401,
    headers: { "Content-Type": "application/json" },
  });
}

// Use result.claims for business logic
const { subject_id, capability_id, revision } = result.claims;

Stable error codes

The adapter returns one of these codes on failure:

CodeMeaning
CONTEXT_MISSING_HEADERForgium-Context header is missing or empty
CONTEXT_INVALID_JWSToken is not a valid compact JWS
CONTEXT_INVALID_HEADERProtected header is invalid
CONTEXT_UNKNOWN_KEYkid not found in JWKS
CONTEXT_INVALID_CLAIMSClaims structure is invalid
CONTEXT_CLAIMS_MISMATCHClaims don't match the request
CONTEXT_EXPIREDToken has expired (TTL is 5 minutes)
CONTEXT_INVALID_SIGNATURESignature verification failed
CONTEXT_BODY_HASH_MISMATCHBody hash doesn't match claims
CONTEXT_REPLAY_PROTECTION_REQUIREDReplay store callback was not configured
CONTEXT_REPLAYJTI has been seen before

These codes are safe to log and return to clients. Never log the JWS token,

claims payload, or request body.

Retrieving JWKS

Retrieve verification keys from

/.well-known/forgium/capability-context/v1/jwks.json. Cache the response

and refresh when kid lookup fails.

async function fetchJwks(): Promise<JsonWebKey[]> {
  const response = await fetch(
    "https://api.forgium.dev/.well-known/forgium/capability-context/v1/jwks.json"
  );
  const { keys } = await response.json();
  return keys;
}

Key rotation: The JWKS may contain multiple keys. The adapter selects the

key matching the token's kid header. When Forgium rotates keys, the JWKS

will include both old and new keys during the transition period.

Replay protection

You must implement replay protection. The JTI (JWT ID) is unique per

token. Store seen JTIs with TTL equal to the token's expiration.

Production options:

Testing/development:

import { createInMemoryReplayStore } from "@micro-agent/capabilities/capability-context-reference";

const { rememberJti, cleanup } = createInMemoryReplayStore();
// Pass rememberJti to verifyForgiumContext
// Call cleanup() when shutting down

What the host must verify

The Forgium-Context contains these claims that must match the request:

ClaimMust match
issYour expected issuer (e.g., https://api.forgium.dev)
audYour endpoint URL or audience identifier
methodThe HTTP method (e.g., POST)
urlThe exact request URL
body_sha256SHA-256 hash of the raw request body
iat / expCurrent time (with 60s clock skew tolerance)
jtiUnique ID (for replay protection)

Additional claims available after verification:

Security requirements

  1. Always verify the signature. Never skip cryptographic verification.
  2. Always check replay. Without replay protection, a captured token can be
    reused.
  3. Validate all claims. Don't cherry-pick claims to verify.
  4. Stop on failure. If verification fails, reject the request entirely.
  5. Never log secrets. The JWS token, claims, and body are sensitive.

Example with Express

import express from "express";
import { verifyForgiumContext } from "@micro-agent/capabilities/capability-context-reference";

const app = express();

app.post("/v1/people/lookup", express.raw({ type: "application/json" }), async (req, res) => {
  const result = await verifyForgiumContext({
    contextHeader: req.headers["forgium-context"],
    method: req.method,
    url: `${req.protocol}://${req.get("host")}${req.originalUrl}`,
    body: req.body.toString(),
    jwks: await fetchJwks(),
    issuer: "https://api.forgium.dev",
    audience: `${req.protocol}://${req.get("host")}`,
    rememberJti: async (jti, expiresAt) => {
      // Your replay store implementation
    },
  });

  if (!result.ok) {
    return res.status(401).json({ error: result.code });
  }

  // Your business logic here
  const { subject_id } = result.claims;
  // ...
});

Example with Cloudflare Workers

import { verifyForgiumContext } from "@micro-agent/capabilities/capability-context-reference";

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const result = await verifyForgiumContext({
      contextHeader: request.headers.get("Forgium-Context"),
      method: request.method,
      url: request.url,
      body: await request.text(),
      jwks: await fetchJwks(),
      issuer: "https://api.forgium.dev",
      audience: new URL(request.url).origin,
      rememberJti: async (jti, expiresAt) => {
        const existing = await env.KV.get(`jti:${jti}`);
        if (existing) return false;
        await env.KV.put(`jti:${jti}`, "1", { expiration: expiresAt });
        return true;
      },
    });

    if (!result.ok) {
      return new Response(JSON.stringify({ error: result.code }), {
        status: 401,
        headers: { "Content-Type": "application/json" },
      });
    }

    // Your business logic here
    return new Response(JSON.stringify({ ok: true }));
  },
};

API key authentication

Retrieve verification keys from

/.well-known/forgium/capability-context/v1/jwks.json; never request or

store a private signing key.

Rotate endpoint tokens with the token rotation runbook.

Example

curl -fsS "$FORGIUM_AGENT_BASE_URL/v1/capabilities" \
  -H "Authorization: Bearer $FORGIUM_AGENT_API_KEY"