Skip to main content

Overview

@grantex/gateway is a standalone reverse-proxy that enforces Grantex grant tokens in front of any upstream API. Define routes and required scopes in a YAML config — no code required.
npm install @grantex/gateway @grantex/sdk

Quick Start

1. Create gateway.yaml:
upstream: https://api.internal.example.com
jwksUri: https://your-auth-server/.well-known/jwks.json
port: 8080
upstreamHeaders:
  X-Internal-Auth: "secret-key"
routes:
  - path: /calendar/**
    methods: [GET]
    requiredScopes: [calendar:read]
  - path: /calendar/**
    methods: [POST, PUT, PATCH]
    requiredScopes: [calendar:write]
  - path: /payments/**
    methods: [POST]
    requiredScopes: [payments:initiate]
2. Start the gateway:
npx @grantex/gateway --config gateway.yaml
3. Make requests with grant tokens:
curl -H "Authorization: Bearer <grant-token>" \
  http://localhost:8080/calendar/events

How It Works

Client → Gateway (verify token + check scopes) → Upstream API
  1. Route matching — finds the first route matching the request method + path
  2. Token verification — extracts Bearer token and verifies offline via JWKS
  3. Scope checking — ensures the grant includes all required scopes
  4. Proxy — strips Authorization header, adds upstream headers + X-Grantex-* context, forwards to upstream
  5. Response — returns the upstream response to the client

YAML Config Reference

FieldTypeRequiredDefaultDescription
upstreamstringYesBase URL of the upstream API
jwksUristringYesJWKS endpoint for offline verification
portnumberNo8080Listen port
upstreamHeadersobjectNoHeaders added to every upstream request
grantexApiKeystringNoAPI key for audit logging
routesarrayYesRoute definitions

Route Definition

FieldTypeDescription
pathstringURL pattern. * matches one segment, ** matches any depth
methodsstring[]HTTP methods (GET, POST, PUT, PATCH, DELETE)
requiredScopesstring[]Scopes that must be present in the grant

Path Matching

PatternMatchesDoes Not Match
/users/*/users/123/users/123/profile
/calendar/**/calendar/events, /calendar/events/123/attendees/api/calendar
/health/health/health/check

Context Headers

The gateway adds these headers to every upstream request:
HeaderValue
X-Grantex-PrincipalPrincipal ID (end-user)
X-Grantex-AgentAgent DID
X-Grantex-GrantIdGrant ID
Your upstream API can use these to apply fine-grained business logic without re-verifying the token.

Error Responses

All errors return JSON with error and message fields:
StatusError CodeWhen
404ROUTE_NOT_FOUNDNo route matches the request
401TOKEN_MISSINGNo Bearer token in Authorization header
401TOKEN_INVALIDToken signature verification failed
401TOKEN_EXPIREDToken has expired
403SCOPE_INSUFFICIENTGrant doesn’t include required scopes
502UPSTREAM_ERRORUpstream API is unreachable

CLI Usage

# Start with config file
npx @grantex/gateway --config gateway.yaml

# Short flag
npx @grantex/gateway -c gateway.yaml

# Default: looks for gateway.yaml in current directory
npx @grantex/gateway

Library API

Use the gateway programmatically in your own server:
import { createGatewayServer, loadConfig } from '@grantex/gateway';

const config = loadConfig('./gateway.yaml');
const server = createGatewayServer(config);

await server.listen({ port: config.port, host: '0.0.0.0' });

Docker Deployment

FROM node:20-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --ignore-scripts
COPY tsconfig*.json ./
COPY src/ src/
RUN npm run build

FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/package*.json ./
RUN npm ci --omit=dev --ignore-scripts
COPY --from=builder /app/dist/ dist/
EXPOSE 8080
ENTRYPOINT ["node", "dist/cli.js"]
CMD ["--config", "/etc/grantex/gateway.yaml"]
docker build -t grantex-gateway .
docker run -p 8080:8080 -v ./gateway.yaml:/etc/grantex/gateway.yaml grantex-gateway

Example: Protecting a Calendar API

upstream: https://calendar-api.internal.svc
jwksUri: https://grantex-auth-dd4mtrt2gq-uc.a.run.app/.well-known/jwks.json
port: 8080
upstreamHeaders:
  X-Service-Key: "calendar-internal-key"
routes:
  - path: /calendars/*/events
    methods: [GET]
    requiredScopes: [calendar:read]
  - path: /calendars/*/events
    methods: [POST]
    requiredScopes: [calendar:write]
  - path: /calendars/*/events/*
    methods: [PUT, PATCH]
    requiredScopes: [calendar:write]
  - path: /calendars/*/events/*
    methods: [DELETE]
    requiredScopes: [calendar:delete]