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
package/bin/agentgate.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
import { evaluate } from '../src/policy-engine.js';
|
|
7
|
+
import { createAgentGate } from '../src/agentgate.js';
|
|
8
|
+
import { createControlPlane } from '../src/control-plane.js';
|
|
9
|
+
import { runGatewayAttackLab, summarizeAttackResults } from '../src/attack-lab.js';
|
|
10
|
+
import { createMCPGateway } from '../src/mcp-gateway.js';
|
|
11
|
+
import { generateSecurityReport, renderSecurityReportHTML } from '../src/security-report.js';
|
|
12
|
+
import { createPolicyRegistry } from '../src/policy-registry.js';
|
|
13
|
+
import { TenantRegistry } from '../src/multi-tenant.js';
|
|
14
|
+
import { scanMCPTools, createToolTrustStore } from '../src/mcp-scanner.js';
|
|
15
|
+
import { guardEgress } from '../src/egress-guard.js';
|
|
16
|
+
import { validateAgentGateConfig, simulatePolicyMatrix, runDoctorChecks } from '../src/local-experience.js';
|
|
17
|
+
import { runSecurityValidation } from '../src/security-validation.js';
|
|
18
|
+
import { listPolicyPacks, getPolicyPack, getDefaultPolicyPack } from '../src/policy-packs.js';
|
|
19
|
+
import { analyzeShadowEvents } from '../src/shadow-mode.js';
|
|
20
|
+
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
const PACKAGE_VERSION = require('../package.json').version;
|
|
23
|
+
|
|
24
|
+
const [cmd, action='read', amount='0'] = process.argv.slice(2);
|
|
25
|
+
const showHelp = () => console.log(`AgentGate ${PACKAGE_VERSION} — Runtime Control Plane
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
agentgate init
|
|
29
|
+
agentgate dev [--port <port>]
|
|
30
|
+
agentgate test <action> [amount]
|
|
31
|
+
agentgate attack
|
|
32
|
+
agentgate attack-ci
|
|
33
|
+
agentgate report
|
|
34
|
+
agentgate scan <tools.json> [--strict]
|
|
35
|
+
agentgate egress <response.json> [--strict]
|
|
36
|
+
agentgate policy <create|list|test|activate|rollback|diff> <name> ...
|
|
37
|
+
agentgate tenant <create|list|key|revoke|rotate> ...
|
|
38
|
+
agentgate --help
|
|
39
|
+
agentgate doctor
|
|
40
|
+
agentgate simulate
|
|
41
|
+
agentgate validate-security
|
|
42
|
+
agentgate pack <list|show|init|test> [pack-id]
|
|
43
|
+
agentgate shadow <events.json>
|
|
44
|
+
agentgate partner init [pack]
|
|
45
|
+
agentgate partner check <report.json>
|
|
46
|
+
agentgate demo refund
|
|
47
|
+
agentgate --help
|
|
48
|
+
agentgate --version`);
|
|
49
|
+
if (cmd === '--help' || cmd === '-h' || cmd === 'help') { showHelp(); process.exit(0); }
|
|
50
|
+
if (cmd === '--version' || cmd === '-v' || cmd === 'version') { console.log(PACKAGE_VERSION); process.exit(0); }
|
|
51
|
+
|
|
52
|
+
if (cmd === 'pack') {
|
|
53
|
+
const sub = process.argv[3] || 'list';
|
|
54
|
+
if (sub === 'list') console.log(JSON.stringify(listPolicyPacks(), null, 2));
|
|
55
|
+
else if (sub === 'show') {
|
|
56
|
+
const id = process.argv[4] || 'support-refund-safety';
|
|
57
|
+
const pack = getPolicyPack(id);
|
|
58
|
+
if (!pack) { console.error(`Unknown policy pack: ${id}`); process.exitCode = 1; }
|
|
59
|
+
else console.log(JSON.stringify(pack, null, 2));
|
|
60
|
+
} else if (sub === 'init') {
|
|
61
|
+
const id = process.argv[4] || 'support-refund-safety';
|
|
62
|
+
const pack = getPolicyPack(id);
|
|
63
|
+
if (!pack) { console.error(`Unknown policy pack: ${id}`); process.exitCode = 1; }
|
|
64
|
+
else {
|
|
65
|
+
const file = path.resolve(process.cwd(), 'agentgate.config.mjs');
|
|
66
|
+
const content = `import { createAgentGate, getPolicyPack } from 'agentgate-runtime-control';
|
|
67
|
+
|
|
68
|
+
const pack = getPolicyPack('${pack.id}');
|
|
69
|
+
|
|
70
|
+
export const agentgate = createAgentGate({
|
|
71
|
+
agent: 'SupportAgent',
|
|
72
|
+
mode: 'enforce',
|
|
73
|
+
policies: pack.policies
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export { pack };
|
|
77
|
+
`;
|
|
78
|
+
try { await fs.access(file); console.error('agentgate.config.mjs already exists'); process.exitCode = 1; }
|
|
79
|
+
catch { await fs.writeFile(file, content, 'utf8'); console.log(`Created ${file} from ${pack.name} (enforce mode)`); }
|
|
80
|
+
}
|
|
81
|
+
} else if (sub === 'test') {
|
|
82
|
+
const id = process.argv[4] || 'support-refund-safety';
|
|
83
|
+
const pack = getPolicyPack(id);
|
|
84
|
+
if (!pack) { console.error(`Unknown policy pack: ${id}`); process.exitCode = 1; }
|
|
85
|
+
else {
|
|
86
|
+
const results = simulatePolicyMatrix({ policies: pack.policies, cases: pack.cases });
|
|
87
|
+
const passed = results.filter((x, i) => x.decision === pack.cases[i].expected).length;
|
|
88
|
+
console.log(JSON.stringify({ pack: pack.id, total: results.length, passed, failed: results.length - passed, results }, null, 2));
|
|
89
|
+
if (passed !== results.length) process.exitCode = 2;
|
|
90
|
+
}
|
|
91
|
+
} else { console.error('Usage: agentgate pack <list|show|init|test> [pack-id]'); process.exitCode = 1; }
|
|
92
|
+
} else if (cmd === 'demo' && process.argv[3] === 'refund') {
|
|
93
|
+
const pack = getDefaultPolicyPack();
|
|
94
|
+
const gate = createAgentGate({ agent: 'SupportAgent', mode: 'enforce', policies: pack.policies });
|
|
95
|
+
let executions = 0;
|
|
96
|
+
const refund = gate.protect(async input => { executions += 1; return { refunded: input.amount, customerId: input.customerId }; }, { tool: 'refund', action: 'refund' });
|
|
97
|
+
console.log(`\nAgentGate — ${pack.name} v${pack.version}`);
|
|
98
|
+
console.log('Policy: refund → ASK | > $5,000 → BLOCK | invalid amounts → BLOCK');
|
|
99
|
+
for (const [amount, label] of [[250, 'small-refund'], [1200, 'review-refund'], [5000.01, 'over-ceiling'], ['5000.01', 'invalid-string']]) {
|
|
100
|
+
try {
|
|
101
|
+
const result = await refund({ amount, customerId: 'demo_customer' }, { amount });
|
|
102
|
+
console.log(JSON.stringify({ case: label, amount, status: result.status, decision: result.agentgate?.decision, winningRule: result.agentgate?.winningRule, executionCount: executions }));
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.log(JSON.stringify({ case: label, amount, status: 'blocked', decision: error.agentgate?.decision, winningRule: error.agentgate?.winningRule, executionCount: executions }));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
console.log(JSON.stringify({ proof: 'blocked actions did not execute', refundExecutions: executions }, null, 2));
|
|
108
|
+
} else if (cmd === 'doctor') {
|
|
109
|
+
const configPath = path.resolve(process.cwd(), 'agentgate.config.mjs');
|
|
110
|
+
let config = { mode: 'enforce', policies: { productionBlock: true, autoApproveAmount: 500, approvalAmount: 5000 }, authRequired: true };
|
|
111
|
+
try { const mod = await import(`file://${configPath}?doctor=${Date.now()}`); const gate = mod.agentgate || {}; config = { ...config, ...(mod.config || {}), ...(gate.config || {}), ...(gate.mode ? { mode: gate.mode } : {}), ...(gate.policies ? { policies: gate.policies } : {}) }; } catch {}
|
|
112
|
+
const report = runDoctorChecks({ config });
|
|
113
|
+
console.log(JSON.stringify(report, null, 2)); process.exitCode = report.ok ? 0 : 2;
|
|
114
|
+
} else if (cmd === 'simulate') {
|
|
115
|
+
const policy = { productionBlock: true, autoApproveAmount: 500, approvalAmount: 5000 };
|
|
116
|
+
console.log(JSON.stringify(simulatePolicyMatrix({ policies: policy }), null, 2));
|
|
117
|
+
} else if (cmd === 'partner') {
|
|
118
|
+
const sub = process.argv[3] || 'help';
|
|
119
|
+
if (sub === 'init') {
|
|
120
|
+
const packId = process.argv[4] || 'support-refund-safety';
|
|
121
|
+
const pack = getPolicyPack(packId);
|
|
122
|
+
if (!pack) { console.error(`Unknown policy pack: ${packId}`); process.exitCode = 1; }
|
|
123
|
+
else {
|
|
124
|
+
const dir = path.resolve(process.cwd(), 'design-partner');
|
|
125
|
+
await fs.mkdir(dir, { recursive: true });
|
|
126
|
+
const files = {
|
|
127
|
+
'partner-intake.md': `# Design Partner Intake\n\n- Partner: \n- Agent: \n- Sensitive tool: \n- Environment: sandbox / replay / production\n- Identity/tenant model: \n- Existing authorization: \n- Side effects: \n- Contact: \n\n## Success criteria\n- BLOCK -> 0 handler execution\n- ASK -> 0 pre-approval execution\n- Approved ASK -> exactly 1 execution\n- Cross-tenant access -> 0 leaks\n- Egress secret leakage -> 0 undetected test cases\n- Audit mismatch -> 0\n`,
|
|
128
|
+
'pilot-plan.md': `# Design Partner Pilot Plan\n\n## Phase 1 — Sandbox / Replay\nInstall the real AgentGate package and replay representative traffic.\n\n## Phase 2 — Shadow\nRecord proposed ALLOW/ASK/BLOCK decisions while existing execution continues. Review every mismatch.\n\n## Phase 3 — Enforce one tool\nEnable enforcement only after the shadow report is safe and the approval boundary has been tested.\n\n## Phase 4 — Evidence\nPreserve Run, winningRule, ruleTrace, execution outcome, egress findings, and Replay evidence.\n\n## Exit\nMove to production only when all acceptance criteria are green and the partner approves the evidence.\n`,
|
|
129
|
+
'acceptance-scorecard.md': `# Design Partner Acceptance Scorecard\n\n| Gate | Target | Result |\n|---|---|---|\n| BLOCK side effects | 0 | |\n| ASK pre-approval execution | 0 | |\n| Approved execution | exactly 1 | |\n| Cross-tenant leaks | 0 | |\n| Undetected egress secrets | 0 | |\n| Audit mismatch | 0 | |\n| Restart/Replay | PASS | |\n| First protected tool | < 10 min target | |\n\nDo not mark the pilot production-ready while any critical gate is unresolved.\n`,
|
|
130
|
+
'exit-report.md': `# Design Partner Exit Report\n\n## Outcome\n- Status: pending / ready / blocked\n- Policy Pack: ${pack.id}\n- Agent: \n- Sensitive tool: \n\n## Evidence\n- Shadow report: \n- Attack report: \n- Replay run IDs: \n- Restart verification: \n\n## Before / After\n| Metric | Before | AgentGate |\n|---|---:|---:|\n| Dangerous actions reaching handler | | |\n| Pre-approval executions | | |\n| Duplicate approvals | | |\n| Audit mismatches | | |\n| Undetected egress findings | | |\n\n## Limitations\n\n`
|
|
131
|
+
};
|
|
132
|
+
for (const [name, content] of Object.entries(files)) await fs.writeFile(path.join(dir, name), content, 'utf8');
|
|
133
|
+
console.log(`Created ${dir} for ${pack.name}`);
|
|
134
|
+
}
|
|
135
|
+
} else if (sub === 'check') {
|
|
136
|
+
const file = process.argv[4];
|
|
137
|
+
if (!file) { console.error('Usage: agentgate partner check <report.json>'); process.exitCode = 1; }
|
|
138
|
+
else {
|
|
139
|
+
const report = JSON.parse(await fs.readFile(path.resolve(file), 'utf8'));
|
|
140
|
+
const gates = {
|
|
141
|
+
blockSideEffectsZero: report.blockSideEffects === 0,
|
|
142
|
+
askPreApprovalZero: report.askPreApprovalExecutions === 0,
|
|
143
|
+
approvedExactlyOnce: report.approvedExecutions === 1,
|
|
144
|
+
crossTenantZero: report.crossTenantLeaks === 0,
|
|
145
|
+
egressUndetectedZero: report.undetectedEgressSecrets === 0,
|
|
146
|
+
auditMismatchZero: report.auditMismatches === 0,
|
|
147
|
+
replayAfterRestart: report.replayAfterRestart === true
|
|
148
|
+
};
|
|
149
|
+
const failed = Object.entries(gates).filter(([, ok]) => !ok).map(([name]) => name);
|
|
150
|
+
console.log(JSON.stringify({ ready: failed.length === 0, gates, failed }, null, 2));
|
|
151
|
+
process.exitCode = failed.length ? 2 : 0;
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
console.error('Usage: agentgate partner <init|check> ...'); process.exitCode = 1;
|
|
155
|
+
}
|
|
156
|
+
} else if (cmd === 'shadow') {
|
|
157
|
+
const file = process.argv[3];
|
|
158
|
+
if (!file) { console.error('Usage: agentgate shadow <events.json>'); process.exitCode = 1; }
|
|
159
|
+
else {
|
|
160
|
+
const input = JSON.parse(await fs.readFile(path.resolve(file), 'utf8'));
|
|
161
|
+
const events = Array.isArray(input) ? input : (input.events || []);
|
|
162
|
+
const report = analyzeShadowEvents(events);
|
|
163
|
+
console.log(JSON.stringify(report, null, 2));
|
|
164
|
+
if (!report.safeForEnforcement) process.exitCode = 2;
|
|
165
|
+
}
|
|
166
|
+
} else if (cmd === 'validate-security') {
|
|
167
|
+
const report = await runSecurityValidation();
|
|
168
|
+
console.log(JSON.stringify(report, null, 2)); process.exitCode = report.ok ? 0 : 2;
|
|
169
|
+
} else if (cmd === 'scan') {
|
|
170
|
+
const file = process.argv[3];
|
|
171
|
+
if (!file) { console.error('Usage: agentgate scan <tools.json> [--strict]'); process.exitCode=1; }
|
|
172
|
+
else { const input=JSON.parse(await fs.readFile(path.resolve(file),'utf8')); const result=scanMCPTools(Array.isArray(input)?input:(input.tools||[])); console.log(JSON.stringify(result,null,2)); if(process.argv.includes('--strict') && result.summary.high>0) process.exitCode=2; }
|
|
173
|
+
} else if (cmd === 'egress') {
|
|
174
|
+
const file = process.argv[3];
|
|
175
|
+
if (!file) { console.error('Usage: agentgate egress <response.json> [--strict]'); process.exitCode = 1; }
|
|
176
|
+
else {
|
|
177
|
+
const input = JSON.parse(await fs.readFile(path.resolve(file), 'utf8'));
|
|
178
|
+
const result = guardEgress(input);
|
|
179
|
+
console.log(JSON.stringify(result, null, 2));
|
|
180
|
+
if (process.argv.includes('--strict') && result.action === 'BLOCK') process.exitCode = 2;
|
|
181
|
+
}
|
|
182
|
+
} else if (cmd === 'attack-ci') {
|
|
183
|
+
const gateway = createMCPGateway({ mode:'enforce', policies:{productionBlock:true}, tools:[{name:'export_all',handler:async()=>({})},{name:'update_production',handler:async()=>({})},{name:'delete',handler:async()=>({})},{name:'refund',handler:async()=>({})},{name:'publish',handler:async()=>({})}] });
|
|
184
|
+
const results=await runGatewayAttackLab(gateway); const summary=summarizeAttackResults(results); console.log(JSON.stringify({summary,results},null,2)); process.exitCode=summary.failed?2:0;
|
|
185
|
+
} else if (cmd === 'test' || cmd === 'check') {
|
|
186
|
+
console.log(JSON.stringify(evaluate({ action, amount: Number(amount) }), null, 2));
|
|
187
|
+
} else if (cmd === 'attack') {
|
|
188
|
+
const gateway = createMCPGateway({
|
|
189
|
+
mode: 'enforce',
|
|
190
|
+
policies: { productionBlock: true },
|
|
191
|
+
tools: [
|
|
192
|
+
{ name: 'export_all', handler: async () => ({ executed: true }) },
|
|
193
|
+
{ name: 'update_production', handler: async () => ({ executed: true }) },
|
|
194
|
+
{ name: 'delete', handler: async () => ({ executed: true }) },
|
|
195
|
+
{ name: 'refund', handler: async args => ({ refunded: args.amount }) },
|
|
196
|
+
{ name: 'publish', handler: async () => ({ executed: true }) }
|
|
197
|
+
]
|
|
198
|
+
});
|
|
199
|
+
const results = await runGatewayAttackLab(gateway);
|
|
200
|
+
const summary = summarizeAttackResults(results);
|
|
201
|
+
console.log('\nAgentGate Attack Runner');
|
|
202
|
+
console.table(results.map(x => ({ test: x.name, decision: x.decision, risk: x.risk, passed: x.passed, runId: x.runId })));
|
|
203
|
+
console.log('Summary:', JSON.stringify(summary));
|
|
204
|
+
process.exitCode = summary.failed ? 2 : 0;
|
|
205
|
+
} else if (cmd === 'report') {
|
|
206
|
+
const gateway = createMCPGateway({
|
|
207
|
+
mode: 'enforce',
|
|
208
|
+
policies: { productionBlock: true },
|
|
209
|
+
tools: [
|
|
210
|
+
{ name: 'export_all', handler: async () => ({ executed: true }) },
|
|
211
|
+
{ name: 'update_production', handler: async () => ({ executed: true }) },
|
|
212
|
+
{ name: 'delete', handler: async () => ({ executed: true }) },
|
|
213
|
+
{ name: 'refund', handler: async args => ({ refunded: args.amount }) },
|
|
214
|
+
{ name: 'publish', handler: async () => ({ executed: true }) }
|
|
215
|
+
]
|
|
216
|
+
});
|
|
217
|
+
const attacks = await runGatewayAttackLab(gateway);
|
|
218
|
+
const report = generateSecurityReport({ attackResults: attacks, runs: gateway.runs(), policies: { productionBlock: true }, metadata: { agent: 'Attack Runner', environment: 'production' } });
|
|
219
|
+
const jsonPath = path.resolve(process.cwd(), 'agentgate-security-report.json');
|
|
220
|
+
const htmlPath = path.resolve(process.cwd(), 'agentgate-security-report.html');
|
|
221
|
+
await fs.writeFile(jsonPath, JSON.stringify(report, null, 2), 'utf8');
|
|
222
|
+
await fs.writeFile(htmlPath, renderSecurityReportHTML(report), 'utf8');
|
|
223
|
+
console.log(`Security report: ${report.summary.status}`);
|
|
224
|
+
console.log(`JSON: ${jsonPath}`);
|
|
225
|
+
console.log(`HTML: ${htmlPath}`);
|
|
226
|
+
process.exitCode = report.summary.failed ? 2 : 0;
|
|
227
|
+
} else if (cmd === 'policy') {
|
|
228
|
+
const sub = process.argv[3] || 'list';
|
|
229
|
+
const name = process.argv[4] || 'default';
|
|
230
|
+
const registry = createPolicyRegistry({ filePath: path.resolve(process.cwd(), '.agentgate/policies.json') });
|
|
231
|
+
if (sub === 'create') {
|
|
232
|
+
const policy = JSON.parse(process.argv[5] || '{}');
|
|
233
|
+
console.log(JSON.stringify(registry.create(name, policy), null, 2));
|
|
234
|
+
} else if (sub === 'list') {
|
|
235
|
+
console.log(JSON.stringify(registry.list(name), null, 2));
|
|
236
|
+
} else if (sub === 'diff') {
|
|
237
|
+
console.log(JSON.stringify(registry.diff(name, Number(process.argv[5]), Number(process.argv[6])), null, 2));
|
|
238
|
+
} else if (sub === 'test') {
|
|
239
|
+
const version = Number(process.argv[5]);
|
|
240
|
+
const cases = JSON.parse(process.argv[6] || '[]');
|
|
241
|
+
console.log(JSON.stringify(registry.test(name, version, cases), null, 2));
|
|
242
|
+
} else if (sub === 'activate' || sub === 'rollback') {
|
|
243
|
+
const version = Number(process.argv[5]);
|
|
244
|
+
const result = sub === 'activate' ? registry.activate(name, version) : registry.rollback(name, version);
|
|
245
|
+
console.log(JSON.stringify(result, null, 2));
|
|
246
|
+
} else {
|
|
247
|
+
console.error('Usage: agentgate policy <create|list|test|activate|rollback|diff> <name> ...');
|
|
248
|
+
process.exitCode = 1;
|
|
249
|
+
}
|
|
250
|
+
} else if (cmd === 'tenant') {
|
|
251
|
+
const sub = process.argv[3] || 'list';
|
|
252
|
+
const registry = new TenantRegistry({ filePath: path.resolve(process.cwd(), '.agentgate/tenants.json'), keyPath: path.resolve(process.cwd(), '.agentgate/api-keys.json') });
|
|
253
|
+
if (sub === 'create') console.log(JSON.stringify(registry.create(process.argv[4] || 'workspace'), null, 2));
|
|
254
|
+
else if (sub === 'list') console.log(JSON.stringify(registry.list(), null, 2));
|
|
255
|
+
else if (sub === 'key') {
|
|
256
|
+
const tenantId = process.argv[4]; const scopes = (process.argv[5] || 'runs:read').split(',');
|
|
257
|
+
console.log(JSON.stringify(registry.issueKey(tenantId, { scopes }), null, 2));
|
|
258
|
+
} else if (sub === 'revoke') console.log(JSON.stringify(registry.revoke(process.argv[4]), null, 2));
|
|
259
|
+
else if (sub === 'rotate') console.log(JSON.stringify(registry.rotate(process.argv[4]), null, 2));
|
|
260
|
+
else { console.error('Usage: agentgate tenant <create|list|key|revoke|rotate> ...'); process.exitCode = 1; }
|
|
261
|
+
} else if (cmd === 'init') {
|
|
262
|
+
const packIndex = process.argv.indexOf('--pack');
|
|
263
|
+
const packId = packIndex >= 0 ? process.argv[packIndex + 1] : null;
|
|
264
|
+
const pack = packId ? getPolicyPack(packId) : null;
|
|
265
|
+
if (packId && !pack) { console.error(`Unknown policy pack: ${packId}`); process.exitCode = 1; }
|
|
266
|
+
else {
|
|
267
|
+
const file = path.resolve(process.cwd(), 'agentgate.config.mjs');
|
|
268
|
+
const content = pack
|
|
269
|
+
? `import { createAgentGate, getPolicyPack } from 'agentgate-runtime-control';\n\nconst pack = getPolicyPack('${pack.id}');\n\nexport const agentgate = createAgentGate({\n agent: 'SupportAgent',\n mode: 'observe',\n policies: pack.policies\n});\n\nexport { pack };\n`
|
|
270
|
+
: `import { createAgentGate } from 'agentgate-runtime-control';\n\nexport const agentgate = createAgentGate({\n agent: 'MyAgent',\n mode: 'enforce',\n policies: {\n productionBlock: true,\n autoApproveAmount: 500,\n approvalAmount: 5000\n }\n});\n`;
|
|
271
|
+
try { await fs.access(file); console.error('agentgate.config.mjs already exists'); process.exitCode = 1; }
|
|
272
|
+
catch { await fs.writeFile(file, content, 'utf8'); console.log(`Created ${file}${pack ? ` from ${pack.name} (enforce mode)` : ''}`); }
|
|
273
|
+
}
|
|
274
|
+
} else if (cmd === 'dev') {
|
|
275
|
+
const htmlPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../standalone.html');
|
|
276
|
+
const html = await fs.readFile(htmlPath, 'utf8');
|
|
277
|
+
const gate = createAgentGate({ agent: 'DemoAgent', mode: 'enforce', policies: { productionBlock: true } });
|
|
278
|
+
const gateway = gate.withMCP({ tools: [
|
|
279
|
+
{ name: 'read', handler: async args => ({ ok: true, data: args, simulated: true, environment: 'demo' }) },
|
|
280
|
+
{ name: 'refund', handler: async args => ({ refunded: args.amount, simulated: true, environment: 'demo' }) },
|
|
281
|
+
{ name: 'delete', handler: async args => ({ deleted: args.id, simulated: true, environment: 'demo' }) },
|
|
282
|
+
{ name: 'export_all', handler: async args => ({ simulated: true, environment: 'demo', action: 'export_all', args }) },
|
|
283
|
+
{ name: 'update_production', handler: async args => ({ simulated: true, environment: 'demo', action: 'update_production', args }) },
|
|
284
|
+
{ name: 'publish', handler: async args => ({ simulated: true, environment: 'demo', action: 'publish', args }) }
|
|
285
|
+
]});
|
|
286
|
+
await gateway.handle({ jsonrpc:'2.0', id:1, method:'tools/call', params:{ name:'read', arguments:{ id:'demo_customer' }, agent:'DemoAgent', action:'read', environment:'development' }});
|
|
287
|
+
await gateway.handle({ jsonrpc:'2.0', id:2, method:'tools/call', params:{ name:'refund', arguments:{ amount:1200, customerId:'demo_customer' }, agent:'DemoAgent', action:'refund', environment:'development' }});
|
|
288
|
+
await gateway.handle({ jsonrpc:'2.0', id:3, method:'tools/call', params:{ name:'delete', arguments:{ id:'demo_customer' }, agent:'DemoAgent', action:'delete', environment:'production' }});
|
|
289
|
+
const portArgIndex = process.argv.indexOf('--port');
|
|
290
|
+
const port = Number(portArgIndex >= 0 ? process.argv[portArgIndex + 1] : process.env.PORT || 8787);
|
|
291
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) { console.error('Invalid port. Use --port <1-65535> or PORT=<port>.'); process.exitCode = 1; }
|
|
292
|
+
else {
|
|
293
|
+
const { server } = createControlPlane({ gateway, html, localDevSession: true });
|
|
294
|
+
server.on('error', error => {
|
|
295
|
+
if (error?.code === 'EADDRINUSE') console.error(`Port ${port} is already in use. Use agentgate dev --port <port> or PORT=<port>.`);
|
|
296
|
+
else console.error(`AgentGate dev server error: ${error?.message || error}`);
|
|
297
|
+
process.exitCode = 1;
|
|
298
|
+
});
|
|
299
|
+
server.listen(port, () => console.log(`AgentGate Control Plane: http://localhost:${port}`));
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
showHelp();
|
|
303
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# AgentGate Technical Validation — 2.13.0
|
|
2
|
+
|
|
3
|
+
> This is a technical validation report, not a customer case study. It must not be presented as customer evidence.
|
|
4
|
+
|
|
5
|
+
> The 2.13.6 release retains this validated runtime baseline and adds first-client hardening; this document describes the historical 2.13.0 validation evidence.
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
AgentGate 2.13.0 was tested from an independent consumer project using a real npm tarball, without relying on the package source tree.
|
|
9
|
+
|
|
10
|
+
## Results
|
|
11
|
+
- Design Partner workflow: 19/19 checks passed.
|
|
12
|
+
- Three Policy Packs were discoverable and contained policies/test cases.
|
|
13
|
+
- Shadow mode recorded proposed decisions and mismatch/readiness fields.
|
|
14
|
+
- ASK produced zero pre-approval executions; approved ASK executed exactly once.
|
|
15
|
+
- Live, Run and Replay evidence matched on `winningRule` and `ruleTrace`.
|
|
16
|
+
- Restart preserved blocked Run and Replay evidence.
|
|
17
|
+
- Cross-tenant access returned 403.
|
|
18
|
+
- Egress controls blocked raw credentials and applied configured redaction.
|
|
19
|
+
- 10,000 concurrent destructive attempts produced 10,000 BLOCK decisions and zero executions.
|
|
20
|
+
|
|
21
|
+
## What this proves
|
|
22
|
+
The tested runtime control boundary can be demonstrated with deterministic evidence. It does not prove that AgentGate prevents every AI security failure, replaces IAM, or provides legal/compliance certification.
|
|
23
|
+
|
|
24
|
+
## Customer case study policy
|
|
25
|
+
A customer case study must only be published after a real partner has completed Shadow → Enforce and approved publication of the relevant metrics and quotes.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# AgentGate Case Study Template
|
|
2
|
+
|
|
3
|
+
## 1. Agent and workflow
|
|
4
|
+
- Agent:
|
|
5
|
+
- Sensitive tool:
|
|
6
|
+
- Environment:
|
|
7
|
+
- Existing authorization:
|
|
8
|
+
|
|
9
|
+
## 2. Baseline
|
|
10
|
+
- Requests tested:
|
|
11
|
+
- Risky actions observed:
|
|
12
|
+
- Existing approval path:
|
|
13
|
+
- Existing audit evidence:
|
|
14
|
+
|
|
15
|
+
## 3. AgentGate deployment
|
|
16
|
+
- Policy Pack:
|
|
17
|
+
- Mode: shadow / enforce
|
|
18
|
+
- Integration time:
|
|
19
|
+
- Policies changed during pilot:
|
|
20
|
+
|
|
21
|
+
## 4. Results
|
|
22
|
+
| Metric | Before | With AgentGate |
|
|
23
|
+
|---|---:|---:|
|
|
24
|
+
| Dangerous actions reaching handler | | |
|
|
25
|
+
| Pre-approval executions | | |
|
|
26
|
+
| Duplicate approvals | | |
|
|
27
|
+
| Audit mismatches | | |
|
|
28
|
+
| Undetected egress findings | | |
|
|
29
|
+
|
|
30
|
+
## 5. Evidence
|
|
31
|
+
- Replay run IDs:
|
|
32
|
+
- Winning rules:
|
|
33
|
+
- Attack scenarios:
|
|
34
|
+
- Partner-approved screenshots/logs:
|
|
35
|
+
|
|
36
|
+
## 6. Limitations
|
|
37
|
+
Document what AgentGate did not protect and what remained under application/IAM/provider controls.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Data Protection, Retention, Backup and Restore
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
AgentGate can persist runs, approvals, policy metadata and audit evidence. The application owner is responsible for classifying the data and choosing the retention period appropriate to the workload.
|
|
6
|
+
|
|
7
|
+
## Retention
|
|
8
|
+
|
|
9
|
+
The in-process/local stores enforce bounded retention. Configure the production database retention separately; do not treat the local `maxRuns` limit as a compliance retention policy.
|
|
10
|
+
|
|
11
|
+
Recommended operational baseline for a pilot:
|
|
12
|
+
|
|
13
|
+
- Hot audit/run data: 30 days.
|
|
14
|
+
- Exported security evidence: retain according to the customer's compliance requirement.
|
|
15
|
+
- Approvals: retain with the associated audit record for the same period unless a stricter requirement applies.
|
|
16
|
+
|
|
17
|
+
These are deployment defaults, not legal advice; customers must set their own policy.
|
|
18
|
+
|
|
19
|
+
## Encryption
|
|
20
|
+
|
|
21
|
+
- TLS for AgentGate-to-database and user-to-Control-Plane traffic.
|
|
22
|
+
- Encrypt database storage using the cloud/database provider's encryption-at-rest controls.
|
|
23
|
+
- Encrypt backups using provider-managed or customer-managed keys according to the customer's requirements.
|
|
24
|
+
- Never put API keys, bearer tokens, database credentials, or private keys into policy definitions, browser bundles, or exported audit files unless explicitly required and protected.
|
|
25
|
+
|
|
26
|
+
## Backup
|
|
27
|
+
|
|
28
|
+
For PostgreSQL, enable automated provider backups and point-in-time recovery where available. For higher assurance, keep an independent backup/export according to the customer's RPO.
|
|
29
|
+
|
|
30
|
+
Minimum pilot procedure:
|
|
31
|
+
|
|
32
|
+
1. Take/verify a database backup.
|
|
33
|
+
2. Record backup timestamp and database schema version.
|
|
34
|
+
3. Restore into an isolated database.
|
|
35
|
+
4. Run tenant-isolation, replay, approval, and audit-read tests.
|
|
36
|
+
5. Record restore duration.
|
|
37
|
+
|
|
38
|
+
## Restore acceptance targets
|
|
39
|
+
|
|
40
|
+
The deployment owner should explicitly choose:
|
|
41
|
+
|
|
42
|
+
- RPO: maximum acceptable data loss.
|
|
43
|
+
- RTO: maximum acceptable recovery time.
|
|
44
|
+
|
|
45
|
+
AgentGate does not claim a universal RPO/RTO because those values depend on the customer's database and infrastructure.
|
|
46
|
+
|
|
47
|
+
## Deletion
|
|
48
|
+
|
|
49
|
+
Customer data deletion must be performed at the database layer according to the tenant/data-retention policy. Verify that backups and exported evidence follow the same contractual deletion requirements.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Design Partner Checklist
|
|
2
|
+
|
|
3
|
+
## Before integration
|
|
4
|
+
- [ ] One real agent selected
|
|
5
|
+
- [ ] One sensitive tool selected
|
|
6
|
+
- [ ] Tool side effects documented
|
|
7
|
+
- [ ] Identity/tenant model documented
|
|
8
|
+
- [ ] Sandbox or replay data available
|
|
9
|
+
- [ ] Success criteria agreed
|
|
10
|
+
|
|
11
|
+
## Shadow phase
|
|
12
|
+
- [ ] AgentGate installed from a real package
|
|
13
|
+
- [ ] Policy Pack selected
|
|
14
|
+
- [ ] Decisions recorded
|
|
15
|
+
- [ ] Actual outcomes captured
|
|
16
|
+
- [ ] Would-block executions reviewed
|
|
17
|
+
- [ ] False positives reviewed
|
|
18
|
+
- [ ] False negatives reviewed
|
|
19
|
+
|
|
20
|
+
## Enforcement phase
|
|
21
|
+
- [ ] Low-risk tool passed shadow review
|
|
22
|
+
- [ ] Sensitive tool has explicit approval boundary
|
|
23
|
+
- [ ] BLOCK execution = 0 in test
|
|
24
|
+
- [ ] ASK pre-approval execution = 0 in test
|
|
25
|
+
- [ ] Approved execution = 1 in test
|
|
26
|
+
- [ ] Replay survives restart
|
|
27
|
+
|
|
28
|
+
## Exit criteria
|
|
29
|
+
- [ ] Partner accepts production use
|
|
30
|
+
- [ ] Second workflow requested or demonstrated
|
|
31
|
+
- [ ] Evidence can support a case study
|
|
32
|
+
- [ ] No unresolved critical audit mismatch
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# AgentGate Design Partner Kit
|
|
2
|
+
|
|
3
|
+
This kit is the controlled operating package for a 3–5 partner pilot. It is not a general security or compliance program.
|
|
4
|
+
|
|
5
|
+
## 1. Select one workflow
|
|
6
|
+
|
|
7
|
+
Start with one agent and one side-effecting tool. Recommended first workflow: Support Refund Safety.
|
|
8
|
+
|
|
9
|
+
- Agent: support/operations agent
|
|
10
|
+
- Sensitive tool: `refund`
|
|
11
|
+
- Initial environment: sandbox or replay
|
|
12
|
+
- Policy Pack: `support-refund-safety`
|
|
13
|
+
|
|
14
|
+
## 2. Run the five-step pilot
|
|
15
|
+
|
|
16
|
+
1. **Intake** — document the agent, tool, side effects, identity/tenant model, and existing authorization.
|
|
17
|
+
2. **Sandbox / replay** — install AgentGate from the real package and replay representative traffic.
|
|
18
|
+
3. **Shadow** — record proposed decisions and compare them with actual execution. Do not enforce until mismatches are reviewed.
|
|
19
|
+
4. **Enforce** — enable one sensitive tool only after the acceptance gates pass.
|
|
20
|
+
5. **Evidence / exit** — preserve Run, winningRule, ruleTrace, execution result, egress findings, and Replay evidence; complete the exit report.
|
|
21
|
+
|
|
22
|
+
## 3. Acceptance gates
|
|
23
|
+
|
|
24
|
+
- `BLOCK -> handler execution = 0`
|
|
25
|
+
- `ASK -> pre-approval execution = 0`
|
|
26
|
+
- approved `ASK -> exactly 1 execution`
|
|
27
|
+
- cross-tenant access -> `0` leaks
|
|
28
|
+
- undetected egress secret findings -> `0`
|
|
29
|
+
- audit mismatch -> `0`
|
|
30
|
+
- restart/replay -> PASS
|
|
31
|
+
- first protected tool -> target under 10 minutes
|
|
32
|
+
|
|
33
|
+
## 4. Partner evidence package
|
|
34
|
+
|
|
35
|
+
Every pilot should produce:
|
|
36
|
+
|
|
37
|
+
- policy pack and test cases
|
|
38
|
+
- shadow report
|
|
39
|
+
- attack results
|
|
40
|
+
- enforcement results
|
|
41
|
+
- approval evidence
|
|
42
|
+
- replay run IDs
|
|
43
|
+
- restart verification
|
|
44
|
+
- before/after metrics
|
|
45
|
+
- limitations and unresolved controls
|
|
46
|
+
|
|
47
|
+
## 5. Exit decision
|
|
48
|
+
|
|
49
|
+
A pilot is **ready** only when every critical gate is green and the partner explicitly accepts the evidence. Otherwise keep the tool in shadow mode and resolve the failing gate.
|
|
50
|
+
|
|
51
|
+
AgentGate does not claim to prevent every prompt-injection technique and does not replace application authorization, IAM, network isolation, or provider controls.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# AgentGate Design Partner Rollout
|
|
2
|
+
|
|
3
|
+
This is the controlled path from a tested AgentGate build to a production candidate.
|
|
4
|
+
It is intentionally narrower than a general AI-security launch.
|
|
5
|
+
|
|
6
|
+
## Ten-stage rollout
|
|
7
|
+
|
|
8
|
+
1. **Freeze a tested baseline** — start from a release that passed an external consumer-project test.
|
|
9
|
+
2. **Fast first protection** — install, initialize, simulate, attack, and protect one tool without building a control plane first.
|
|
10
|
+
3. **Policy Pack** — start with one job-specific pack and declared test cases.
|
|
11
|
+
4. **Shadow Mode** — record what AgentGate would ALLOW/ASK/BLOCK while the existing tool still executes.
|
|
12
|
+
5. **Design Partner** — use 3–5 partners, one agent and one sensitive tool per partner initially.
|
|
13
|
+
6. **Enforce** — move one validated sensitive tool from shadow to enforcement.
|
|
14
|
+
7. **Evidence** — require request, decision, winning rule, rule trace, execution outcome, and replay evidence.
|
|
15
|
+
8. **Case Study** — measure before/after outcomes using partner-approved data.
|
|
16
|
+
9. **Productize** — extract repeated policy patterns, integrations, and onboarding friction into the product.
|
|
17
|
+
10. **Commercialize** — only after repeatable protection and onboarding evidence exists.
|
|
18
|
+
|
|
19
|
+
## Partner acceptance gates
|
|
20
|
+
|
|
21
|
+
- `BLOCK -> handler execution = 0`
|
|
22
|
+
- `ASK -> pre-approval execution = 0`
|
|
23
|
+
- Approved `ASK -> exactly 1 execution`
|
|
24
|
+
- Cross-tenant access -> `0` leaks
|
|
25
|
+
- Egress secret leakage -> `0` undetected test cases
|
|
26
|
+
- Audit mismatch -> `0`
|
|
27
|
+
- Restart/replay -> evidence remains available
|
|
28
|
+
- Time to first protected tool -> target under 10 minutes
|
|
29
|
+
|
|
30
|
+
## Rollout sequence
|
|
31
|
+
|
|
32
|
+
`replay/sandbox -> shadow -> low-risk enforce -> sensitive-tool enforce`
|
|
33
|
+
|
|
34
|
+
Do not start with sensitive production data. Review the partner's tool contract,
|
|
35
|
+
identity model, and existing authorization before enforcement.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# AgentGate Design Partner Program
|
|
2
|
+
|
|
3
|
+
AgentGate is introduced to a design partner around one measurable runtime boundary, not as a general-purpose AI security replacement.
|
|
4
|
+
|
|
5
|
+
## First use case: Support Refund Safety
|
|
6
|
+
|
|
7
|
+
Start with one side-effecting tool such as `refund`.
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
Agent request
|
|
11
|
+
↓
|
|
12
|
+
AgentGate
|
|
13
|
+
↓
|
|
14
|
+
ALLOW / ASK / BLOCK
|
|
15
|
+
↓
|
|
16
|
+
Tool execution
|
|
17
|
+
↓
|
|
18
|
+
Audit + Replay evidence
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The default Support Refund Safety Pack uses:
|
|
22
|
+
|
|
23
|
+
- refunds → `ASK`
|
|
24
|
+
- `> $5,000` → `BLOCK`
|
|
25
|
+
- the pack's `autoApproveAmount` remains available for policy evolution, but this conservative pack does not bypass the runtime's destructive-action approval boundary
|
|
26
|
+
- invalid amount types → fail closed through the runtime policy engine
|
|
27
|
+
- `export_all` → `BLOCK`
|
|
28
|
+
|
|
29
|
+
The pack is a starting point, not a compliance guarantee. Review thresholds and tool semantics before enforcement.
|
|
30
|
+
|
|
31
|
+
## 10-minute first protection
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install agentgate-runtime-control
|
|
35
|
+
npx agentgate pack list
|
|
36
|
+
npx agentgate pack init support-refund-safety
|
|
37
|
+
npx agentgate simulate
|
|
38
|
+
npx agentgate attack
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
For a focused demonstration:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx agentgate demo refund
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The generated configuration starts in `enforce` mode for the packaged safety default. If a team wants Shadow/Observe, it must explicitly set `mode: 'observe'` and treat that configuration as non-enforcing.
|
|
48
|
+
|
|
49
|
+
## Rollout
|
|
50
|
+
|
|
51
|
+
1. Use sandbox or replay traffic.
|
|
52
|
+
2. Start in `observe` mode.
|
|
53
|
+
3. Compare AgentGate's proposed decisions with actual tool behavior.
|
|
54
|
+
4. Run normal and adversarial cases.
|
|
55
|
+
5. Move one tool to `enforce` only after review.
|
|
56
|
+
6. Preserve replayable evidence for every sensitive decision.
|
|
57
|
+
|
|
58
|
+
## Acceptance criteria
|
|
59
|
+
|
|
60
|
+
- `BLOCK → handler execution = 0`
|
|
61
|
+
- `ASK → pre-approval execution = 0`
|
|
62
|
+
- approved action executes exactly once
|
|
63
|
+
- cross-tenant access = 0
|
|
64
|
+
- egress secret leakage = 0
|
|
65
|
+
- audit mismatch = 0
|
|
66
|
+
|
|
67
|
+
AgentGate does not claim to prevent every prompt-injection technique and does not replace application authorization, IAM, network isolation, or provider controls.
|