Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ packages/<name>/
- supertest for Express middleware integration tests

### current status
Tests are not yet implemented across packages. Priority order for adding tests:
1. proxyabl-core (security-critical: SSRF, auth modes, proxy execution)
2. transformabl-core (correctness-critical: PII regex patterns)
3. validatabl-core (security-critical: policy enforcement)
4. identifiabl-core (security-critical: JWT verification)
5. limitabl-core (correctness-critical: rate limiting, budget tracking)
6. request-context, explicabl, middleware wrappers
Every `-core` package and the middleware wrappers have vitest suites under
`packages/<name>/__tests__/` (note: `__tests__/`, not `tests/`). `npm test` at
the repo root runs the whole suite; `npm run build` must pass first (the tests
import built workspace packages). Security-critical coverage is real:
proxyabl-core SSRF, transformabl-core PII regex + redaction, validatabl-core
policy enforcement, identifiabl-core JWT verification, limitabl-core
rate/budget/agent-guard.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ Hermes Agent uses a native Python plugin instead ([why](https://agenticcontrolpl
pip install hermes-acp && hermes plugins enable acp
```

**Prefer to see the guarantees work first — zero config, no IdP, no backend?**

```bash
git clone https://github.com/agentic-control-plane/GatewayStack
cd GatewayStack/examples/quickstart && npm install && npm start
```

Prints three real decisions — a deny-by-default policy, PII redaction, and a
rate limit — as pure local library code ([`examples/quickstart`](examples/quickstart)).

Your own framework code — drop in a package:

```bash
Expand Down
12 changes: 12 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Examples

Runnable GatewayStack examples.

| Example | What it shows | Setup |
|---------|---------------|-------|
| [`quickstart/`](./quickstart) | Deny-by-default policy, PII redaction, and rate limiting — three real decisions printed to your console | **None.** `npm install && npm start` |

The quickstart uses only the framework-agnostic `-core` packages, so it runs
anywhere Node runs, with zero external services. Wiring the same layers as
Express middleware (which does need an IdP for the identity layer) is covered in
the [root README](../README.md#full-stack-example).
32 changes: 32 additions & 0 deletions examples/quickstart/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Quickstart — governance in 30 seconds

Three real GatewayStack decisions running on your machine. **No IdP, no backend,
no config.**

```bash
npm install
npm start
```

You'll see:

```
1 · Deny-by-default policy (validatabl)
✅ ALLOW read_file → Matched rule: allow-reads (allow)
🛑 DENY delete_database → No rules matched; default: deny

2 · PII redaction (transformabl)
in : Email me at [email protected] or call about SSN 123-45-6789.
out: Email me at [EMAIL] or call about SSN [SSN].
detected: email, ssn

3 · Rate limit + budget guard (limitabl)
call 1: ✅ ALLOW
...
call 4: 🛑 DENY → Rate limited. Retry after 60s
```

Everything here is pure `-core` library code — the same logic the Express
middleware wraps. See [`index.ts`](./index.ts); it's ~50 lines. To wire these as
HTTP middleware in front of your own tools/models, see the
[full-stack example](../../README.md#full-stack-example).
69 changes: 69 additions & 0 deletions examples/quickstart/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* GatewayStack — 30-second quickstart.
*
* Three real governance decisions, running entirely on your machine. No IdP, no
* backend, no config. Run it:
*
* npm install && npm start
*
* Everything here is pure library code from the `-core` packages — the same
* logic the Express middleware wraps. If you can see these decisions happen,
* you can drop them into your own gateway.
*/

import { applyPolicies, type PolicySet } from "@gatewaystack/validatabl-core";
import { transformContent } from "@gatewaystack/transformabl-core";
import { LimitablEngine } from "@gatewaystack/limitabl-core";

const ALLOW = "\x1b[32m✅ ALLOW\x1b[0m";
const DENY = "\x1b[31m🛑 DENY \x1b[0m";
const h = (s: string) => console.log(`\n\x1b[1m${s}\x1b[0m`);

// ─────────────────────────────────────────────────────────────────────────
// 1. Deny-by-default policy — a tool call is refused unless a rule allows it.
// ─────────────────────────────────────────────────────────────────────────
h("1 · Deny-by-default policy (validatabl)");

const policy: PolicySet = {
defaultEffect: "deny", // nothing is allowed unless a rule says so
rules: [
{
id: "allow-reads",
effect: "allow",
conditions: [{ field: "tool", operator: "in", value: ["read_file", "list_files"] }],
},
],
};

for (const tool of ["read_file", "delete_database"]) {
const d = applyPolicies(policy, { identity: { sub: "agent-1" }, tool });
console.log(` ${d.allowed ? ALLOW : DENY} ${tool.padEnd(16)} → ${d.reason}`);
}

// ─────────────────────────────────────────────────────────────────────────
// 2. PII redaction — sensitive values are stripped before they leave.
// ─────────────────────────────────────────────────────────────────────────
h("2 · PII redaction (transformabl)");

const raw = "Email me at [email protected] or call about SSN 123-45-6789.";
const result = transformContent(raw, { redaction: { mode: "placeholder" } });

console.log(` in : ${raw}`);
console.log(` out: ${result.content}`);
console.log(` detected: ${result.piiMatches.map((m) => m.type).join(", ") || "none"}`);

// ─────────────────────────────────────────────────────────────────────────
// 3. Rate limit — the 4th call in the window is refused.
// ─────────────────────────────────────────────────────────────────────────
h("3 · Rate limit + budget guard (limitabl)");

const engine = new LimitablEngine({ rateLimit: { windowMs: 60_000, maxRequests: 3 } });
const key = { sub: "agent-1" };

for (let i = 1; i <= 5; i++) {
const r = engine.preflight(key);
console.log(` call ${i}: ${r.allowed ? ALLOW : DENY} ${r.allowed ? "" : "→ " + r.reason}`);
}

h("Next → wire these as Express middleware: see ../../README.md#full-stack-example");
console.log();
17 changes: 17 additions & 0 deletions examples/quickstart/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "gatewaystack-quickstart",
"private": true,
"type": "module",
"description": "30-second, zero-config GatewayStack governance demo",
"scripts": {
"start": "tsx index.ts"
},
"dependencies": {
"@gatewaystack/validatabl-core": "^0.1.0",
"@gatewaystack/transformabl-core": "^0.4.0",
"@gatewaystack/limitabl-core": "^0.1.0"
},
"devDependencies": {
"tsx": "^4.19.2"
}
}
Loading