A self-hosted security gateway that protects your applications behind a single, enforceable rule:
Every HTTP/HTTPS request must carry a token the gateway verifies. No token → no server response.
Built on Node.js + TypeScript with a shadCN UI dashboard, a CLI, a demo upstream API, Docker, tests, and a benchmark. Every component shares one type system via npm workspaces.
Too many services expose their own auth, so enforcement is scattered and weak. Zero Trust Guard centralizes authentication, authorization, device binding, and rate limiting at the perimeter — the upstream server never sees an unverified request.
Client ── Bearer <access JWT> ──▶ Gateway ──▶ Upstream
│
1. token signature + expiry │
2. server-side session check │ (revocation is instant)
3. account status │
4. device binding │
5. policy (roles, deny, ratelimit)
6. reverse proxy (injects x-ztg-*)
A failed check returns a clean 401/403 before any upstream round trip. Sessions, devices, and rate counters live in Valkey/Redis (with an in-memory dev fallback); the audit trail lives in PostgreSQL (with an NDJSON file fallback).
- Layered zero-trust gate: JWT signature → session → account → device.
- Short-lived access tokens (HS256 via
node:crypto) + rotating refresh tokens. - Immediate revocation of sessions and devices.
- Policy engine: role requirements, explicit deny rules, per-route rate limits.
- Per-IP login and global rate limiting.
- Admin control plane: manage sessions, devices, routes, policies, audit, metrics.
- Live dashboard over WebSocket (
/ws): audit events + metrics pushed in real time. - Demo upstream that refuses to answer unless
x-ztg-verifiedis present. - CLI (
ztg) for scriptable administration. - Docker Compose stack (postgres + valkey + gateway + dashboard + demo).
cp .env.example .env # set ZTG_JWT_SECRET
docker compose up --build- Dashboard: http://localhost:3000
- Gateway health:
curl http://localhost:8080/health - Demo credentials:
admin/admin123(admin),user/user123,blocked/blocked123(suspended)
A self-contained demo (demo-react + demo-express) showing the gateway standing
between a React frontend and an Express backend — and why that matters beyond HTTPS.
React app ──Bearer JWT──▶ Zero Trust Gateway ──x-ztg-verified──▶ Express API
npm install
npm run build -w @ztg/demo-express -w @ztg/demo-react
node demo-express/dist/index.js # terminal A :4000
env ZTG_REDIS_URL="" ZTG_DATABASE_URL="" ZTG_PORT=8080 node src/gateway/dist/index.js # terminal B
npm run dev -w @ztg/demo-react # terminal C http://localhost:5173In the browser: log in as admin/admin123, then try calling /demo/secret with the
real token, no token, and a forged token; revoke your own live session and watch the
next request die with 401 despite HTTPS + a still-valid token; finally open the
deliberately-insecure /demo/insecure route to see the "trusted HTTPS only" failure
mode. Full explanation: docs/why-zerotrust-over-https.md.
npm install
npm run build
npm run dev -w @ztg/dashboardStart the gateway with the in-memory fallback (single instance, dev only):
env ZTG_REDIS_URL="" ZTG_DATABASE_URL="" ZTG_PORT=8080 node src/gateway/dist/index.jsnode src/cli/dist/index.js login admin admin123
node src/cli/dist/index.js metrics
node src/cli/dist/index.js session list
node src/cli/dist/index.js route listsrc/shared shared types, errors, config, logger, utils (@ztg/shared)
src/gateway the zero-trust security gateway (@ztg/gateway)
src/cli ztg command-line tool (@ztg/cli)
dashboard Next.js + Tailwind + shadCN UI (@ztg/dashboard)
demo demo upstream (refuses unverified traffic) (@ztg/demo)
demo-express Express backend for the React+Express demo (@ztg/demo-express)
demo-react React frontend for the React+Express demo (@ztg/demo-react)
configs users.json, routes.json, policies.json
docs architecture, security, threat model, ADRs, performance, deployment
All settings are environment-driven (ZTG_*), see .env.example and
docs/deployment.md. Users, routes, and policies load from configs/ at boot
(ZTG_CONFIG_DIR); route upstreams may use env:NAME so the same config runs locally
and in Docker.
npm test # all workspaces
npm run test -w @ztg/gateway # unit + integration tests
npm run bench -w @ztg/gateway # benchmark (see docs/performance.md)