Skip to content
agentic control plane Beta

Getting started

GatewayStack implements the Agentic Control Plane as composable npm modules. Start with identity — it’s the foundation — then add layers as your governance needs grow.

Prerequisites

  • Node.js 20+
  • npm 10+
  • An OIDC provider issuing RS256 access tokens (Auth0, Okta, Entra ID, Keycloak, etc.)

1. Install identifiabl

npm install @gatewaystack/identifiabl express

2. Add identity verification

import express from "express";
import { identifiabl } from "@gatewaystack/identifiabl";

const app = express();

app.use(identifiabl({
  issuer: process.env.OAUTH_ISSUER!,
  audience: process.env.OAUTH_AUDIENCE!,
}));

app.get("/api/me", (req, res) => {
  res.json({ user: req.user.sub, scopes: req.user.scope });
});

app.listen(8080);

Every request now requires a valid RS256 JWT. req.user contains the verified identity — sub, email, scopes, and any custom claims from your identity provider.

3. Add content safety

npm install @gatewaystack/transformabl
import { transformabl } from "@gatewaystack/transformabl";

// After identifiabl
app.use("/tools", transformabl({ blockThreshold: 80 }));

Prompts are now scanned for PII (SSNs, emails, credit cards) before reaching the model. Content exceeding the safety threshold is blocked.

4. Add policy enforcement

npm install @gatewaystack/validatabl
import { validatabl } from "@gatewaystack/validatabl";

app.use("/tools", validatabl({
  requiredPermissions: ["tool:read"],
}));

Requests are now checked against the user’s scopes and roles. Users without tool:read permission get a 403 Forbidden.

5. Add rate limiting and budgets

npm install @gatewaystack/limitabl
import { limitabl } from "@gatewaystack/limitabl";

app.use("/tools", limitabl({
  rateLimit: { windowMs: 60_000, maxRequests: 100 },
  budget: { maxSpend: 500, periodMs: 86_400_000 },
}));

Each user is now rate-limited to 100 requests per minute and $500/day in spend. Agent runaway loops are detected and halted.

6. Add audit logging

npm install @gatewaystack/explicabl
import { createConsoleLogger, explicablLoggingMiddleware } from "@gatewaystack/explicabl";

app.use(explicablLoggingMiddleware(createConsoleLogger()));

Every tool call is now logged with the user’s identity, the policy decision, and cost attribution.

Full pipeline

Wire all six layers together:

npm install @gatewaystack/identifiabl @gatewaystack/transformabl \
  @gatewaystack/validatabl @gatewaystack/limitabl \
  @gatewaystack/proxyabl @gatewaystack/explicabl \
  @gatewaystack/request-context express
import express from "express";
import { runWithGatewayContext } from "@gatewaystack/request-context";
import { identifiabl } from "@gatewaystack/identifiabl";
import { transformabl } from "@gatewaystack/transformabl";
import { validatabl } from "@gatewaystack/validatabl";
import { limitabl } from "@gatewaystack/limitabl";
import { createProxyablRouter, configFromEnv } from "@gatewaystack/proxyabl";
import { createConsoleLogger, explicablLoggingMiddleware } from "@gatewaystack/explicabl";

const app = express();
app.use(express.json());

// 1. Request context for downstream layers
app.use((req, _res, next) => {
  runWithGatewayContext(
    { request: { method: req.method, path: req.path } },
    () => next()
  );
});

// 2. Audit logging
app.use(explicablLoggingMiddleware(createConsoleLogger()));

// 3. Identity verification
app.use(identifiabl({
  issuer: process.env.OAUTH_ISSUER!,
  audience: process.env.OAUTH_AUDIENCE!,
}));

// 4. Content safety
app.use("/tools", transformabl({ blockThreshold: 80 }));

// 5. Policy enforcement
app.use("/tools", validatabl({ requiredPermissions: ["tool:read"] }));

// 6. Rate limits and budgets
app.use("/tools", limitabl({
  rateLimit: { windowMs: 60_000, maxRequests: 100 },
  budget: { maxSpend: 500, periodMs: 86_400_000 },
}));

// 7. Route to backends
app.use("/tools", createProxyablRouter(configFromEnv(process.env)));

app.listen(8080, () => console.log("GatewayStack running on :8080"));

Health checks

Add health endpoints so you can verify your gateway is running and can reach the identity provider:

app.get("/health", (_req, res) => {
  res.json({ status: "ok", version: process.env.npm_package_version });
});

app.get("/health/auth", async (_req, res) => {
  try {
    const issuer = process.env.OAUTH_ISSUER!;
    const disco = await fetch(`${issuer}.well-known/openid-configuration`);
    const { jwks_uri } = await disco.json();
    const jwks = await fetch(jwks_uri);
    const { keys } = await jwks.json();
    res.json({
      status: "ok",
      issuer,
      jwks_uri,
      keys_available: keys.length,
    });
  } catch (err) {
    res.status(503).json({ status: "error", message: String(err) });
  }
});

Use /health for load balancer probes and /health/auth to diagnose identity provider connectivity issues in production.

Environment variables

Create a .env file:

OAUTH_ISSUER=https://your-tenant.us.auth0.com/
OAUTH_AUDIENCE=https://your-api-audience

Next steps