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
- Reference architecture — understand the full pipeline
- Use cases — see industry-specific scenarios
- View on GitHub — source code, tests, and docs