agentgate-runtime-control 2.13.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +229 -0
- package/LICENSE +21 -0
- package/README.md +534 -0
- package/SECURITY.md +19 -0
- package/bin/agentgate.js +303 -0
- package/docs/case-study-technical-validation.md +25 -0
- package/docs/case-study-template.md +37 -0
- package/docs/data-protection.md +49 -0
- package/docs/design-partner-checklist.md +32 -0
- package/docs/design-partner-kit.md +51 -0
- package/docs/design-partner-rollout.md +35 -0
- package/docs/design-partner.md +67 -0
- package/docs/external-security-review-test-pack.md +132 -0
- package/docs/external-security-review.md +33 -0
- package/docs/incident-response.md +54 -0
- package/docs/integration-matrix.md +17 -0
- package/docs/managed-postgres-acceptance-test.md +138 -0
- package/docs/marketing-plan.md +33 -0
- package/docs/observability-alerting.md +44 -0
- package/docs/outreach.md +26 -0
- package/docs/partner-intake-template.md +26 -0
- package/docs/performance-baseline.md +23 -0
- package/docs/performance.md +27 -0
- package/docs/pricing.md +53 -0
- package/docs/production-deployment.md +70 -0
- package/docs/production-quickstart.md +58 -0
- package/docs/production-readiness.md +29 -0
- package/docs/quickstart.md +115 -0
- package/docs/release-checklist.md +33 -0
- package/docs/security-hardening-release-report.md +69 -0
- package/docs/threat-model.md +47 -0
- package/docs/website-copy.md +44 -0
- package/examples/basic.mjs +14 -0
- package/examples/control-plane.mjs +17 -0
- package/examples/design-partner-refund.mjs +33 -0
- package/examples/design-partner-shadow.mjs +27 -0
- package/examples/mcp-gateway.mjs +26 -0
- package/examples/policy-bundle.mjs +18 -0
- package/examples/refund-agent.mjs +20 -0
- package/examples/runtime.mjs +12 -0
- package/package.json +49 -0
- package/schema/postgres.sql +17 -0
- package/src/admin-rbac.js +3 -0
- package/src/agentgate.js +85 -0
- package/src/approval.js +30 -0
- package/src/attack-lab.js +94 -0
- package/src/auth.js +27 -0
- package/src/behavior.js +146 -0
- package/src/control-plane.js +215 -0
- package/src/egress-guard.js +132 -0
- package/src/event-bus.js +10 -0
- package/src/identity.js +109 -0
- package/src/index.js +44 -0
- package/src/local-experience.js +46 -0
- package/src/mcp-gateway.js +383 -0
- package/src/mcp-scanner.js +45 -0
- package/src/middleware.js +17 -0
- package/src/multi-tenant.js +29 -0
- package/src/observability.js +395 -0
- package/src/oidc.js +38 -0
- package/src/persistent-store.js +56 -0
- package/src/policy-builder.js +74 -0
- package/src/policy-bundles.js +17 -0
- package/src/policy-engine.js +67 -0
- package/src/policy-packs.js +115 -0
- package/src/policy-registry.js +58 -0
- package/src/postgres-adapter.js +76 -0
- package/src/runtime.js +138 -0
- package/src/saas.js +67 -0
- package/src/security-report.js +42 -0
- package/src/security-validation.js +92 -0
- package/src/shadow-mode.js +47 -0
- package/src/telemetry.js +28 -0
- package/src/webhook-delivery.js +70 -0
- package/standalone.html +86 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Production Quickstart: PostgreSQL
|
|
2
|
+
|
|
3
|
+
This is a reference deployment for a pilot. Put a TLS reverse proxy in front of AgentGate in any non-local deployment.
|
|
4
|
+
|
|
5
|
+
## 1. Set the database secret
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
export POSTGRES_PASSWORD='use-a-long-random-secret'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 2. Start PostgreSQL and AgentGate
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
docker compose -f docker-compose.production.yml up -d --build
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 3. Verify readiness
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
curl -fsS http://127.0.0.1:8787/api/health
|
|
21
|
+
curl -fsS http://127.0.0.1:8787/api/ready
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 4. Verify the database schema
|
|
25
|
+
|
|
26
|
+
The compose file mounts `schema/postgres.sql` as an initialization script. For an existing database, apply the schema through your normal migration/change-management process instead of relying on container initialization.
|
|
27
|
+
|
|
28
|
+
The schema must have:
|
|
29
|
+
|
|
30
|
+
- `agentgate_records`
|
|
31
|
+
- `(tenant_id, kind)` index
|
|
32
|
+
- RLS enabled
|
|
33
|
+
- tenant-scoped `USING` and `WITH CHECK` policy
|
|
34
|
+
|
|
35
|
+
## 5. Production hardening
|
|
36
|
+
|
|
37
|
+
- Replace the sample password with a secret-manager value.
|
|
38
|
+
- Put the Control Plane behind HTTPS/OIDC or another production authentication boundary.
|
|
39
|
+
- Do not publish PostgreSQL directly to the Internet.
|
|
40
|
+
- Configure automated backups and test restore into an isolated database.
|
|
41
|
+
- Configure monitoring for `/api/health`, `/api/ready`, error rate, latency and database connectivity.
|
|
42
|
+
- Keep the exact image/package version and database schema version together for rollback decisions.
|
|
43
|
+
|
|
44
|
+
## 6. Backup/restore acceptance test
|
|
45
|
+
|
|
46
|
+
Record:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
backup timestamp:
|
|
50
|
+
backup identifier:
|
|
51
|
+
restore target:
|
|
52
|
+
restore duration:
|
|
53
|
+
RPO:
|
|
54
|
+
RTO:
|
|
55
|
+
replay verification:
|
|
56
|
+
approval verification:
|
|
57
|
+
tenant-isolation verification:
|
|
58
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Production Readiness Status — 2.13.8
|
|
2
|
+
|
|
3
|
+
## Completed in the release
|
|
4
|
+
|
|
5
|
+
- Runtime/SDK enforcement and approval lifecycle.
|
|
6
|
+
- Live `agentgate dev` Attack Lab with 5/5 demo tools.
|
|
7
|
+
- Explicit SKIPPED handling for missing tools in real gateways.
|
|
8
|
+
- Control Plane policy lifecycle and audit export.
|
|
9
|
+
- Browser syntax/release smoke coverage.
|
|
10
|
+
- Tenant isolation, egress controls, malformed-input handling and approval race tests.
|
|
11
|
+
- 139/139 automated tests passing.
|
|
12
|
+
- Reproducible performance benchmark and recorded baseline.
|
|
13
|
+
- PostgreSQL/Supabase deployment reference and RLS schema checks.
|
|
14
|
+
- Docker Compose production pilot reference.
|
|
15
|
+
- Retention, encryption, backup/restore and deletion guidance.
|
|
16
|
+
- Incident-response runbook.
|
|
17
|
+
- Threat model and integration matrix.
|
|
18
|
+
- Release consistency and npm artifact checks.
|
|
19
|
+
|
|
20
|
+
## Environment-dependent gates
|
|
21
|
+
|
|
22
|
+
These cannot be truthfully marked complete inside the source archive alone:
|
|
23
|
+
|
|
24
|
+
1. **Backup/restore execution:** run against the customer's real PostgreSQL/Supabase environment and record RPO/RTO.
|
|
25
|
+
2. **Production monitoring/alerting:** connect the documented metrics and health probes to the customer's monitoring system.
|
|
26
|
+
3. **Customer-specific integration validation:** run the actual agent/framework/tool stack.
|
|
27
|
+
4. **External security review:** an independent reviewer must perform and sign off on the agreed scope.
|
|
28
|
+
|
|
29
|
+
The release intentionally does not claim any of these have been completed.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# AgentGate Quickstart
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## 0. Start with a policy pack
|
|
5
|
+
|
|
6
|
+
For the fastest first protection, use the Support Refund Safety Pack:
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx agentgate pack list
|
|
10
|
+
npx agentgate pack init support-refund-safety
|
|
11
|
+
npx agentgate simulate
|
|
12
|
+
npx agentgate attack
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The generated config starts in `enforce` mode. For a focused Shadow/Observe demonstration, explicitly change `mode` to `observe` and do not use that configuration for sensitive production tools:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx agentgate demo refund
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
See [`docs/design-partner.md`](design-partner.md) for the observe → enforce rollout and acceptance criteria.
|
|
22
|
+
|
|
23
|
+
AgentGate is a runtime control plane for AI agent tool execution. Put it between your agent and side-effecting tools so every action gets a deterministic `ALLOW`, `ASK`, or `BLOCK` decision.
|
|
24
|
+
|
|
25
|
+
## 1. Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install agentgate-runtime-control
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 2. Protect a tool
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
import { protect } from 'agentgate-runtime-control';
|
|
35
|
+
|
|
36
|
+
const refund = protect(
|
|
37
|
+
async ({ amount, customerId }) => ({ refunded: amount, customerId }),
|
|
38
|
+
{
|
|
39
|
+
approvalActions: ['refund'],
|
|
40
|
+
autoApproveAmount: 500,
|
|
41
|
+
approvalAmount: 5000
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
await refund({ amount: 250, customerId: 'cus_123' });
|
|
46
|
+
// status: executed
|
|
47
|
+
|
|
48
|
+
await refund({ amount: 1200, customerId: 'cus_123' });
|
|
49
|
+
// status: approval_required
|
|
50
|
+
|
|
51
|
+
// amount 9000 is BLOCKED because it exceeds approvalAmount
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 3. Run the attack lab
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx agentgate attack
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The lab exercises representative destructive, export, escalation, and high-value actions through the same gateway path used at runtime.
|
|
61
|
+
|
|
62
|
+
## 4. Start the local control plane
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx agentgate dev
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Then open `http://localhost:8787`. Local development creates a short-lived browser session automatically; production authentication remains required.
|
|
69
|
+
|
|
70
|
+
The dashboard starts with seeded ALLOW / ASK / BLOCK demo runs so you can verify the control plane before connecting your own agent.
|
|
71
|
+
|
|
72
|
+
Useful endpoints include:
|
|
73
|
+
|
|
74
|
+
- `/api/health` — process health
|
|
75
|
+
- `/api/ready` — readiness state
|
|
76
|
+
- `/api/metrics` — runtime telemetry
|
|
77
|
+
- `/api/runs` — replayable runs
|
|
78
|
+
- `/api/approvals` — pending approvals
|
|
79
|
+
- `/api/behavior` — behavior signals
|
|
80
|
+
- `/api/blast-radius` — blast-radius analysis
|
|
81
|
+
|
|
82
|
+
## 5. Start from the CLI
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx agentgate init
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
This creates `agentgate.config.mjs` with a conservative production policy baseline.
|
|
89
|
+
|
|
90
|
+
## 6. Recommended rollout
|
|
91
|
+
|
|
92
|
+
1. Start in `observe` mode.
|
|
93
|
+
2. Run your normal agent traffic.
|
|
94
|
+
3. Run `agentgate attack` and your own abuse cases.
|
|
95
|
+
4. Review replay IDs, behavior signals, and generated reports.
|
|
96
|
+
5. Activate reviewed policies.
|
|
97
|
+
6. Switch to `enforce` mode for production side effects.
|
|
98
|
+
7. Keep approvals and audit events connected to your operational workflow.
|
|
99
|
+
|
|
100
|
+
AgentGate is a control layer, not a guarantee that an agent is safe. Test the actions and tools that matter to your application.
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## Approval lifecycle for `createAgentGate`
|
|
104
|
+
|
|
105
|
+
When a protected SDK tool returns `approval_required`, the returned `approvalId` is resolved through the same gate:
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
const result = await refund({ amount: 900 });
|
|
109
|
+
if (result.status === 'approval_required') {
|
|
110
|
+
const approved = await gate.approve(result.approvalId);
|
|
111
|
+
// or: await gate.deny(result.approvalId, 'Not authorized');
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
You can inspect pending requests with `gate.approvals()` and retrieve one with `gate.getApproval(approvalId)`. The approval API executes the original protected tool only after explicit approval.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Production Release Checklist
|
|
2
|
+
|
|
3
|
+
## Automated gate
|
|
4
|
+
|
|
5
|
+
- [ ] `npm test` is green.
|
|
6
|
+
- [ ] `node scripts/release-check.mjs` is green.
|
|
7
|
+
- [ ] `npm pack --dry-run` contains no tests, internal tarballs, or stale versions.
|
|
8
|
+
- [ ] `node --check` passes for extracted standalone JavaScript.
|
|
9
|
+
- [ ] release browser smoke passes.
|
|
10
|
+
- [ ] `agentgate dev` Live Attack Lab returns 5/5 protected in its demo environment.
|
|
11
|
+
- [ ] clean consumer install from the generated npm tarball passes.
|
|
12
|
+
- [ ] approval race test confirms exactly one execution.
|
|
13
|
+
- [ ] tenant isolation test passes.
|
|
14
|
+
- [ ] egress test passes.
|
|
15
|
+
|
|
16
|
+
## Operational gate
|
|
17
|
+
|
|
18
|
+
- [ ] Production PostgreSQL/Supabase is configured with RLS.
|
|
19
|
+
- [ ] TLS is enabled.
|
|
20
|
+
- [ ] Backup and restore have been exercised.
|
|
21
|
+
- [ ] Retention period is documented by the deployment owner.
|
|
22
|
+
- [ ] RPO/RTO are documented by the deployment owner.
|
|
23
|
+
- [ ] Incident-response contacts/runbook are configured.
|
|
24
|
+
- [ ] Monitoring and alerting are configured.
|
|
25
|
+
- [ ] Application-specific abuse cases are recorded.
|
|
26
|
+
|
|
27
|
+
## Assurance gate
|
|
28
|
+
|
|
29
|
+
- [ ] Threat model reviewed.
|
|
30
|
+
- [ ] Dependency/supply-chain review completed.
|
|
31
|
+
- [ ] External security review completed, if required by the customer/plan.
|
|
32
|
+
|
|
33
|
+
An unchecked external-review item must never be described as completed or certified.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# AgentGate v2.13.8 — Security Hardening Release Report
|
|
2
|
+
|
|
3
|
+
## Changes closed from the v2.13.7 verification report
|
|
4
|
+
|
|
5
|
+
### 1. Generic secret detection
|
|
6
|
+
|
|
7
|
+
Added default field-aware egress protection for:
|
|
8
|
+
|
|
9
|
+
- `secret`
|
|
10
|
+
- `password`
|
|
11
|
+
- `passphrase`
|
|
12
|
+
- `private_key`
|
|
13
|
+
- `access_token`
|
|
14
|
+
- `refresh_token`
|
|
15
|
+
- `authorization`
|
|
16
|
+
- `auth_token`
|
|
17
|
+
- `client_secret`
|
|
18
|
+
- `api_secret`
|
|
19
|
+
|
|
20
|
+
Sensitive values are redacted as `[REDACTED:SECRET]` and the egress decision is `BLOCK` by default.
|
|
21
|
+
|
|
22
|
+
Regression coverage includes nested objects, arrays, inspection, and existing MCP egress behavior.
|
|
23
|
+
|
|
24
|
+
### 2. Enforcement configuration
|
|
25
|
+
|
|
26
|
+
The shipped `agentgate.config.mjs` now uses `mode: 'enforce'` so the packaged runtime example does not accidentally present Observe as an enforcement configuration.
|
|
27
|
+
|
|
28
|
+
Observe remains supported for explicit shadow/testing workflows.
|
|
29
|
+
|
|
30
|
+
### 3. PostgreSQL reference hardening
|
|
31
|
+
|
|
32
|
+
The reference schema now uses both:
|
|
33
|
+
|
|
34
|
+
```sql
|
|
35
|
+
ALTER TABLE agentgate_records ENABLE ROW LEVEL SECURITY;
|
|
36
|
+
ALTER TABLE agentgate_records FORCE ROW LEVEL SECURITY;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The managed production acceptance procedure remains separate because it requires a real managed PostgreSQL environment.
|
|
40
|
+
|
|
41
|
+
### 4. External security review
|
|
42
|
+
|
|
43
|
+
No external audit is claimed. The release contains a reviewer-ready test pack covering authentication, authorization, tenant isolation, runtime enforcement, approval races, egress, malformed input, MCP boundaries, replay/audit integrity, persistence, and abuse controls.
|
|
44
|
+
|
|
45
|
+
### 5. Managed PostgreSQL production acceptance
|
|
46
|
+
|
|
47
|
+
The release contains a provider-neutral acceptance test covering RLS, tenant isolation, backup/restore, application-role privileges, TLS, RPO/RTO, and restore verification.
|
|
48
|
+
|
|
49
|
+
## Release verification executed in this environment
|
|
50
|
+
|
|
51
|
+
- `npm test` — 144/144 PASS.
|
|
52
|
+
- `node bin/agentgate.js --version` — 2.13.8.
|
|
53
|
+
- `node bin/agentgate.js doctor` — OK, enforce mode, no warnings.
|
|
54
|
+
- `node bin/agentgate.js validate-security` — OK.
|
|
55
|
+
- `node bin/agentgate.js attack-ci` — 5/5 PROTECTED.
|
|
56
|
+
- `node --test test/release-browser-smoke.mjs` — PASS.
|
|
57
|
+
- `npm run benchmark` — PASS, environment-specific baseline.
|
|
58
|
+
- `npm run release:check` — PASS.
|
|
59
|
+
- `npm audit --omit=dev` — 0 vulnerabilities reported.
|
|
60
|
+
|
|
61
|
+
## Remaining evidence gates
|
|
62
|
+
|
|
63
|
+
These cannot honestly be marked complete by an internal local build:
|
|
64
|
+
|
|
65
|
+
1. Independent external security review — requires an independent reviewer and dated report.
|
|
66
|
+
2. Managed PostgreSQL production acceptance — requires the target managed PostgreSQL environment and its backup/restore evidence.
|
|
67
|
+
3. Production SLA/capacity — requires HTTP + managed DB + network testing in the target deployment environment.
|
|
68
|
+
|
|
69
|
+
The release therefore closes the code-level finding and packages the remaining independent/production evidence tests without falsely claiming those external validations.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# AgentGate Threat Model
|
|
2
|
+
|
|
3
|
+
## Assets
|
|
4
|
+
|
|
5
|
+
- Tool execution authority.
|
|
6
|
+
- Approval decisions.
|
|
7
|
+
- Run/replay evidence.
|
|
8
|
+
- Tenant-scoped audit data.
|
|
9
|
+
- Policy definitions and versions.
|
|
10
|
+
- Credentials used by the protected application.
|
|
11
|
+
|
|
12
|
+
## Trust boundaries
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
Untrusted model/agent
|
|
16
|
+
|
|
|
17
|
+
v
|
|
18
|
+
AgentGate runtime boundary
|
|
19
|
+
|
|
|
20
|
+
+--> policy engine
|
|
21
|
+
+--> approval boundary
|
|
22
|
+
+--> egress guard
|
|
23
|
+
+--> audit/persistence
|
|
24
|
+
|
|
|
25
|
+
v
|
|
26
|
+
Side-effecting tool / MCP / API
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Threats and controls
|
|
30
|
+
|
|
31
|
+
| Threat | Primary control | Evidence |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| Prompt injection causes dangerous tool call | Deterministic tool/action policy | Attack Lab |
|
|
34
|
+
| High-value action bypasses approval | ASK before handler execution | Approval tests |
|
|
35
|
+
| Double approval causes duplicate side effect | Single-resolution approval state | Approval race test |
|
|
36
|
+
| Destructive tool executes | BLOCK before handler | Security validation / Attack Lab |
|
|
37
|
+
| Cross-tenant replay/read | Tenant-scoped API and persistence | Tenant tests + RLS schema |
|
|
38
|
+
| Secret/PII leaves tool boundary | Egress inspection | Egress tests |
|
|
39
|
+
| Policy drift | Versioned policy registry/test/activate | Policy lifecycle tests |
|
|
40
|
+
| Audit loss during rollback | Export + durable store + backup procedure | Operations runbook |
|
|
41
|
+
| Credential theft outside AgentGate | Application/IAM/secret-manager controls | Out of scope |
|
|
42
|
+
| Compromised host/kernel | Infrastructure security | Out of scope |
|
|
43
|
+
| Unsafe model output without tool call | Model/provider safety controls | Out of scope |
|
|
44
|
+
|
|
45
|
+
## Security boundary
|
|
46
|
+
|
|
47
|
+
AgentGate is an execution-control layer. It is not an IAM replacement, secret manager, network firewall, model safety guarantee, or host security boundary.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# AgentGate Website Copy — Launch Draft
|
|
2
|
+
|
|
3
|
+
## Hero
|
|
4
|
+
**Control what your AI agents can actually do.**
|
|
5
|
+
|
|
6
|
+
AgentGate is the runtime control plane for AI agent actions. Stop dangerous tool calls before they execute, require approval for sensitive actions, and keep replayable evidence of every decision.
|
|
7
|
+
|
|
8
|
+
**CTA:** Start with the SDK
|
|
9
|
+
**Secondary CTA:** Run the Attack Lab
|
|
10
|
+
|
|
11
|
+
## Proof strip
|
|
12
|
+
`ALLOW` · `ASK` · `BLOCK` · Approval boundaries · Egress protection · Tenant isolation · Replay
|
|
13
|
+
|
|
14
|
+
## How it works
|
|
15
|
+
**Install → Observe → Attack → Enforce → Replay**
|
|
16
|
+
|
|
17
|
+
AgentGate sits between your agent and its tools. The model can request an action; AgentGate makes the deterministic runtime decision.
|
|
18
|
+
|
|
19
|
+
## Why AgentGate
|
|
20
|
+
### Prevent side effects
|
|
21
|
+
A BLOCK decision happens before the protected handler executes.
|
|
22
|
+
|
|
23
|
+
### Make approvals real
|
|
24
|
+
Sensitive actions can pause for human approval. No pre-approval handler execution.
|
|
25
|
+
|
|
26
|
+
### Prove what happened
|
|
27
|
+
Every important decision can carry a winning rule, rule trace, execution outcome, and Replay evidence.
|
|
28
|
+
|
|
29
|
+
### Start without production risk
|
|
30
|
+
Use sandbox, replay traffic, and Shadow Mode before enforcing a sensitive tool.
|
|
31
|
+
|
|
32
|
+
## Use cases
|
|
33
|
+
- Support and financial operations: refunds, cancellations, customer changes.
|
|
34
|
+
- DevOps and IT: deployments, production changes, destructive operations.
|
|
35
|
+
- Data and CRM: exports, bulk updates, cross-tenant access.
|
|
36
|
+
|
|
37
|
+
## Security boundary
|
|
38
|
+
AgentGate is a runtime control layer. It does not replace application authorization, IAM, secret management, network isolation, provider controls, or threat modeling.
|
|
39
|
+
|
|
40
|
+
## CTA
|
|
41
|
+
**Give one agent one sensitive tool. See what AgentGate would allow, ask, and block.**
|
|
42
|
+
|
|
43
|
+
Install:
|
|
44
|
+
`npm install agentgate-runtime-control`
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { protect } from 'agentgate-runtime-control';
|
|
2
|
+
|
|
3
|
+
const refund = protect(
|
|
4
|
+
async ({ amount, customerId }) => ({ refunded: amount, customerId }),
|
|
5
|
+
{ autoApproveAmount: 500, approvalAmount: 5000, requireApprovalForDestructive: false }
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
console.log(await refund({ amount: 250, customerId: 'cus_123' })); // executed
|
|
9
|
+
console.log(await refund({ amount: 1200, customerId: 'cus_123' })); // approval_required
|
|
10
|
+
try {
|
|
11
|
+
await refund({ amount: 9000, customerId: 'cus_123' });
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.log({ status: 'blocked', decision: error.agentgate.decision, reason: error.agentgate.reason });
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { createControlPlane } from '../src/control-plane.js';
|
|
3
|
+
import { createMCPGateway } from '../src/mcp-gateway.js';
|
|
4
|
+
|
|
5
|
+
const html = await fs.readFile(new URL('../standalone.html', import.meta.url), 'utf8');
|
|
6
|
+
const gateway = createMCPGateway({
|
|
7
|
+
mode: 'enforce',
|
|
8
|
+
policies: { productionBlock: true },
|
|
9
|
+
tools: [
|
|
10
|
+
{ name: 'read', handler: async (args) => ({ ok: true, data: args }) },
|
|
11
|
+
{ name: 'refund', handler: async (args) => ({ refunded: args.amount }) },
|
|
12
|
+
{ name: 'delete', handler: async (args) => ({ deleted: args.id }) }
|
|
13
|
+
]
|
|
14
|
+
});
|
|
15
|
+
const { server } = createControlPlane({ gateway, html });
|
|
16
|
+
const port = Number(process.env.PORT || 8787);
|
|
17
|
+
server.listen(port, () => console.log(`AgentGate Control Plane: http://localhost:${port}`));
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createAgentGate, getPolicyPack } from 'agentgate-runtime-control';
|
|
2
|
+
|
|
3
|
+
const pack = getPolicyPack('support-refund-safety');
|
|
4
|
+
const gate = createAgentGate({
|
|
5
|
+
agent: 'SupportAgent',
|
|
6
|
+
mode: 'observe',
|
|
7
|
+
policies: pack.policies
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
let executions = 0;
|
|
11
|
+
const refund = gate.protect(
|
|
12
|
+
async ({ amount, customerId }) => {
|
|
13
|
+
executions += 1;
|
|
14
|
+
return { refunded: amount, customerId };
|
|
15
|
+
},
|
|
16
|
+
{ tool: 'refund', action: 'refund' }
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
for (const amount of [250, 1200, 5000.01]) {
|
|
20
|
+
const result = await refund(
|
|
21
|
+
{ amount, customerId: 'sandbox_customer' },
|
|
22
|
+
{ amount, environment: 'production' }
|
|
23
|
+
);
|
|
24
|
+
console.log({
|
|
25
|
+
amount,
|
|
26
|
+
proposedDecision: result.agentgate.decision,
|
|
27
|
+
winningRule: result.agentgate.winningRule,
|
|
28
|
+
simulated: result.agentgate.simulated,
|
|
29
|
+
handlerExecuted: result.status === 'executed'
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log({ mode: gate.mode, executions, note: 'Observe mode records proposed decisions and intentionally does not enforce them.' });
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createAgentGate, getPolicyPack, recordShadowEvent, analyzeShadowEvents } from 'agentgate-runtime-control';
|
|
2
|
+
|
|
3
|
+
const pack = getPolicyPack('support-refund-safety');
|
|
4
|
+
const gate = createAgentGate({ agent: 'SupportAgent', mode: 'observe', policies: pack.policies });
|
|
5
|
+
const events = [];
|
|
6
|
+
|
|
7
|
+
async function refundTool({ amount, customerId }) {
|
|
8
|
+
return { refunded: amount, customerId };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const refund = gate.protect(refundTool, { tool: 'refund', action: 'refund' });
|
|
12
|
+
|
|
13
|
+
for (const amount of [250, 1200, 5000.01]) {
|
|
14
|
+
const result = await refund({ amount, customerId: 'sandbox_customer' }, { amount });
|
|
15
|
+
events.push(recordShadowEvent({
|
|
16
|
+
runId: result.agentgate.id,
|
|
17
|
+
decision: result.agentgate.decision,
|
|
18
|
+
executed: result.status === 'executed',
|
|
19
|
+
request: result.agentgate.request
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(JSON.stringify({
|
|
24
|
+
mode: gate.mode,
|
|
25
|
+
shadow: analyzeShadowEvents(events),
|
|
26
|
+
events
|
|
27
|
+
}, null, 2));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createMCPGatewayServer } from '../src/mcp-gateway.js';
|
|
2
|
+
|
|
3
|
+
const { server } = createMCPGatewayServer({
|
|
4
|
+
mode: 'enforce',
|
|
5
|
+
policies: {
|
|
6
|
+
approvalActions: ['refund'],
|
|
7
|
+
blockActions: ['export_all']
|
|
8
|
+
},
|
|
9
|
+
tools: [
|
|
10
|
+
{
|
|
11
|
+
name: 'read_customer',
|
|
12
|
+
description: 'Read a customer record',
|
|
13
|
+
inputSchema: { type: 'object', properties: { customerId: { type: 'string' } } },
|
|
14
|
+
handler: async ({ customerId }) => ({ customerId, name: 'Demo Customer' })
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'refund',
|
|
18
|
+
description: 'Issue a refund',
|
|
19
|
+
inputSchema: { type: 'object', properties: { amount: { type: 'number' } } },
|
|
20
|
+
handler: async ({ amount }) => ({ refunded: amount })
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const port = Number(process.env.PORT || 8787);
|
|
26
|
+
server.listen(port, () => console.log(`AgentGate MCP Gateway listening on http://localhost:${port}/mcp`));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createPolicyBundleRegistry } from 'agentgate-runtime-control';
|
|
2
|
+
|
|
3
|
+
const registry = createPolicyBundleRegistry({ filePath: '.agentgate/policy-bundles.json' });
|
|
4
|
+
|
|
5
|
+
const bundle = registry.create('support-production', {
|
|
6
|
+
productionBlock: true,
|
|
7
|
+
approvalActions: ['refund', 'delete'],
|
|
8
|
+
autoApproveAmount: 500,
|
|
9
|
+
approvalAmount: 5000
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
registry.test('support-production', bundle.version, [
|
|
13
|
+
{ input: { action: 'read', environment: 'production' }, expected: 'ALLOW' },
|
|
14
|
+
{ input: { action: 'refund', amount: 1200, environment: 'staging' }, expected: 'ASK' },
|
|
15
|
+
{ input: { action: 'delete', environment: 'production' }, expected: 'BLOCK' }
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
console.log(registry.activate('support-production', bundle.version));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createAgentGate } from 'agentgate-runtime-control';
|
|
2
|
+
|
|
3
|
+
const gate = createAgentGate({
|
|
4
|
+
agent: 'SupportAgent',
|
|
5
|
+
mode: 'enforce',
|
|
6
|
+
policies: { productionBlock: true, autoApproveAmount: 500, approvalAmount: 5000 }
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const refund = gate.protect(
|
|
10
|
+
async ({ amount, customerId }) => ({ refunded: amount, customerId }),
|
|
11
|
+
{ tool: 'refund', action: 'refund' }
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
for (const amount of [250, 1200]) {
|
|
15
|
+
try {
|
|
16
|
+
console.log(await refund({ amount, customerId: 'cus_123' }));
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.log({ status: 'blocked', code: error.code, decision: error.agentgate?.decision, reason: error.agentgate?.reason });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createRuntime, runAttackLab } from 'agentgate-runtime-control';
|
|
2
|
+
|
|
3
|
+
const gate = createRuntime({ mode: 'enforce', policies: { productionBlock: true, approvalAmount: 5000, autoApproveAmount: 500 } });
|
|
4
|
+
const charge = async ({ amount }) => ({ charged: amount });
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
console.log(await gate.execute(charge, { action: 'refund', amount: 120 }));
|
|
8
|
+
} catch (error) {
|
|
9
|
+
console.log({ status: 'blocked', code: error.code, decision: error.agentgate?.decision, reason: error.agentgate?.reason });
|
|
10
|
+
}
|
|
11
|
+
console.log(gate.runs());
|
|
12
|
+
console.table(runAttackLab({ productionBlock: true }));
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agentgate-runtime-control",
|
|
3
|
+
"version": "2.13.8",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Runtime control plane and SaaS governance layer for AI agent tool execution",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/index.js",
|
|
8
|
+
"./policy-engine": "./src/policy-engine.js",
|
|
9
|
+
"./mcp-gateway": "./src/mcp-gateway.js",
|
|
10
|
+
"./policy-builder": "./src/policy-builder.js",
|
|
11
|
+
"./control-plane": "./src/control-plane.js",
|
|
12
|
+
"./agentgate": "./src/agentgate.js",
|
|
13
|
+
"./security-report": "./src/security-report.js",
|
|
14
|
+
"./behavior": "./src/behavior.js",
|
|
15
|
+
"./identity": "./src/identity.js",
|
|
16
|
+
"./policy-registry": "./src/policy-registry.js",
|
|
17
|
+
"./multi-tenant": "./src/multi-tenant.js",
|
|
18
|
+
"./telemetry": "./src/telemetry.js",
|
|
19
|
+
"./oidc": "./src/oidc.js",
|
|
20
|
+
"./saas": "./src/saas.js",
|
|
21
|
+
"./mcp-scanner": "./src/mcp-scanner.js",
|
|
22
|
+
"./egress-guard": "./src/egress-guard.js",
|
|
23
|
+
"./observability": "./src/observability.js"
|
|
24
|
+
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"agentgate": "./bin/agentgate.js"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"test": "node --test",
|
|
30
|
+
"prepublishOnly": "npm test",
|
|
31
|
+
"benchmark": "node scripts/performance-benchmark.mjs",
|
|
32
|
+
"release:check": "node scripts/release-check.mjs"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"src",
|
|
39
|
+
"bin",
|
|
40
|
+
"examples",
|
|
41
|
+
"schema",
|
|
42
|
+
"docs",
|
|
43
|
+
"standalone.html",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"SECURITY.md",
|
|
47
|
+
"CHANGELOG.md"
|
|
48
|
+
]
|
|
49
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- AgentGate production Postgres schema
|
|
2
|
+
-- Runtime database sessions should set: select set_config('agentgate.tenant_id', '<tenant>', true);
|
|
3
|
+
create table if not exists agentgate_records (
|
|
4
|
+
id text primary key,
|
|
5
|
+
tenant_id text not null,
|
|
6
|
+
kind text not null,
|
|
7
|
+
data jsonb not null,
|
|
8
|
+
version integer not null default 1,
|
|
9
|
+
created_at timestamptz not null default now()
|
|
10
|
+
);
|
|
11
|
+
create index if not exists agentgate_records_tenant_kind_idx on agentgate_records (tenant_id, kind);
|
|
12
|
+
alter table agentgate_records enable row level security;
|
|
13
|
+
alter table agentgate_records force row level security;
|
|
14
|
+
drop policy if exists agentgate_tenant_isolation on agentgate_records;
|
|
15
|
+
create policy agentgate_tenant_isolation on agentgate_records
|
|
16
|
+
using (tenant_id = current_setting('agentgate.tenant_id', true))
|
|
17
|
+
with check (tenant_id = current_setting('agentgate.tenant_id', true));
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export const ADMIN_ROLES=Object.freeze({OWNER:['*'],ADMIN:['tenant:read','tenant:write','keys:read','keys:write','policies:read','policies:write','webhooks:read','webhooks:write','runs:read','approvals:resolve'],OPERATOR:['runs:read','approvals:resolve','policies:read'],VIEWER:['runs:read','policies:read']});
|
|
2
|
+
export function adminAuthorize(identity, permission){ const roles=Array.isArray(identity?.roles)?identity.roles:[]; return roles.some(role=>ADMIN_ROLES[role]?.includes('*')||ADMIN_ROLES[role]?.includes(permission)); }
|
|
3
|
+
export function requireAdmin(identity,permission){ if(!adminAuthorize(identity,permission)){const e=new Error('Admin permission denied');e.code='AGENTGATE_ADMIN_FORBIDDEN';throw e;} return true; }
|