web-agent-bridge 3.3.0 → 3.4.0
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/LICENSE +12 -0
- package/README.ar.md +18 -0
- package/README.md +198 -1664
- package/bin/wab-init.js +223 -0
- package/examples/azure-dns-wab.js +83 -0
- package/examples/cloudflare-wab-dns.js +121 -0
- package/examples/cpanel-wab-dns.js +114 -0
- package/examples/dns-discovery-agent.js +166 -0
- package/examples/gcp-dns-wab.js +76 -0
- package/examples/governance-agent.js +169 -0
- package/examples/plesk-wab-dns.js +103 -0
- package/examples/route53-wab-dns.js +144 -0
- package/examples/safe-mode-agent.js +96 -0
- package/examples/wab-sign.js +74 -0
- package/examples/wab-verify.js +60 -0
- package/package.json +5 -5
- package/public/.well-known/wab.json +28 -0
- package/public/activate.html +368 -0
- package/public/adoption-metrics.html +188 -0
- package/public/api.html +1 -1
- package/public/azure-dns-integration.html +289 -0
- package/public/cloudflare-integration.html +380 -0
- package/public/cpanel-integration.html +398 -0
- package/public/css/styles.css +28 -0
- package/public/dashboard.html +1 -0
- package/public/dns.html +101 -172
- package/public/docs.html +1 -0
- package/public/gcp-dns-integration.html +318 -0
- package/public/growth.html +4 -2
- package/public/index.html +227 -31
- package/public/integrations.html +1 -1
- package/public/js/activate.js +145 -0
- package/public/js/auth-nav.js +34 -0
- package/public/js/dns.js +438 -0
- package/public/openapi.json +89 -0
- package/public/plesk-integration.html +375 -0
- package/public/premium.html +1 -1
- package/public/provider-onboarding.html +172 -0
- package/public/provider-sandbox.html +134 -0
- package/public/providers.html +359 -0
- package/public/registrar-integrations.html +141 -0
- package/public/robots.txt +12 -0
- package/public/route53-integration.html +531 -0
- package/public/shieldqr.html +231 -0
- package/public/sitemap.xml +6 -0
- package/public/wab-trust.html +200 -0
- package/public/wab-vs-protocols.html +210 -0
- package/public/whitepaper.html +449 -0
- package/sdk/auto-discovery.js +288 -0
- package/sdk/governance.js +262 -0
- package/sdk/index.js +13 -0
- package/sdk/package.json +2 -2
- package/sdk/safe-mode.js +221 -0
- package/server/index.js +144 -5
- package/server/migrations/007_governance.sql +106 -0
- package/server/migrations/008_plans.sql +144 -0
- package/server/migrations/009_shieldqr.sql +30 -0
- package/server/migrations/010_extended_trust.sql +33 -0
- package/server/models/adapters/mysql.js +1 -1
- package/server/models/adapters/postgresql.js +1 -1
- package/server/models/db.js +60 -1
- package/server/routes/admin-plans.js +76 -0
- package/server/routes/admin-premium.js +4 -2
- package/server/routes/admin-shieldqr.js +90 -0
- package/server/routes/admin-trust-monitor.js +83 -0
- package/server/routes/admin.js +289 -1
- package/server/routes/billing.js +16 -4
- package/server/routes/discovery.js +1933 -2
- package/server/routes/governance.js +208 -0
- package/server/routes/plans.js +33 -0
- package/server/routes/providers.js +650 -0
- package/server/routes/shieldqr.js +88 -0
- package/server/services/email.js +29 -0
- package/server/services/governance.js +466 -0
- package/server/services/plans.js +214 -0
- package/server/services/premium.js +1 -1
- package/server/services/provider-clients.js +740 -0
- package/server/services/shieldqr.js +322 -0
- package/server/services/ssl-inspector.js +42 -0
- package/server/services/ssl-monitor.js +167 -0
- package/server/services/stripe.js +18 -5
- package/server/services/vision.js +1 -1
- package/server/services/wab-crypto.js +178 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WAB Agent Governance Routes
|
|
3
|
+
* Mounted at /api/governance
|
|
4
|
+
*
|
|
5
|
+
* Auth model:
|
|
6
|
+
* - Agent endpoints: Bearer <agent_token> in Authorization header
|
|
7
|
+
* OR ?agent_id=...&agent_token=... query params (for SDKs without headers)
|
|
8
|
+
* - Owner/human endpoints (kill, decide approval): same agent token works
|
|
9
|
+
* for the owner of that agent. (Future: owner-level user auth.)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const express = require('express');
|
|
15
|
+
const gov = require('../services/governance');
|
|
16
|
+
|
|
17
|
+
const router = express.Router();
|
|
18
|
+
|
|
19
|
+
// ───────────────────────── auth middleware ────
|
|
20
|
+
function requireAgent(req, res, next) {
|
|
21
|
+
const auth = req.headers.authorization || '';
|
|
22
|
+
const headerToken = auth.startsWith('Bearer ') ? auth.slice(7).trim() : null;
|
|
23
|
+
const agentId = req.params.agentId || req.body?.agent_id || req.query?.agent_id;
|
|
24
|
+
const token = headerToken || req.body?.agent_token || req.query?.agent_token;
|
|
25
|
+
const a = gov.authAgent(agentId, token);
|
|
26
|
+
if (!a) return res.status(401).json({ error: 'invalid_agent_credentials' });
|
|
27
|
+
req.agent = a;
|
|
28
|
+
next();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ───────────────────────── lifecycle ────
|
|
32
|
+
// Register a new agent. Returns the one-time token. Public — anyone can create
|
|
33
|
+
// an agent identity for themselves; rate-limited by the parent app.
|
|
34
|
+
router.post('/agents', (req, res) => {
|
|
35
|
+
try {
|
|
36
|
+
const { agent_id, owner_id, display_name, metadata } = req.body || {};
|
|
37
|
+
const out = gov.registerAgent({
|
|
38
|
+
agentId: agent_id, ownerId: owner_id,
|
|
39
|
+
displayName: display_name, metadata,
|
|
40
|
+
});
|
|
41
|
+
res.status(201).json({
|
|
42
|
+
agent_id: out.agentId,
|
|
43
|
+
agent_token: out.agentToken, // shown ONCE — caller must store it
|
|
44
|
+
message: 'Save the agent_token now; it cannot be retrieved later.',
|
|
45
|
+
});
|
|
46
|
+
} catch (e) {
|
|
47
|
+
if (String(e.message).includes('UNIQUE')) {
|
|
48
|
+
return res.status(409).json({ error: 'agent_id_exists' });
|
|
49
|
+
}
|
|
50
|
+
res.status(500).json({ error: 'register_failed', detail: e.message });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
router.get('/agents/:agentId/status', requireAgent, (req, res) => {
|
|
55
|
+
const s = gov.getStatus(req.agent.agent_id);
|
|
56
|
+
res.json(s);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Kill switch — agent self-kill or external trigger with valid token.
|
|
60
|
+
router.post('/agents/:agentId/kill', requireAgent, (req, res) => {
|
|
61
|
+
const reason = req.body?.reason || 'manual';
|
|
62
|
+
const ok = gov.killAgent(req.agent.agent_id, reason);
|
|
63
|
+
res.json({ ok, status: 'killed', reason });
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
router.post('/agents/:agentId/revive', requireAgent, (req, res) => {
|
|
67
|
+
const reason = req.body?.reason || 'manual_revive';
|
|
68
|
+
const ok = gov.reviveAgent(req.agent.agent_id, reason);
|
|
69
|
+
res.json({ ok, status: 'alive', reason });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ───────────────────────── policies ────
|
|
73
|
+
router.get('/agents/:agentId/policies', requireAgent, (req, res) => {
|
|
74
|
+
res.json({ policies: gov.listPolicies(req.agent.agent_id) });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
router.post('/agents/:agentId/policies', requireAgent, (req, res) => {
|
|
78
|
+
const b = req.body || {};
|
|
79
|
+
if (!b.resource || !b.action) {
|
|
80
|
+
return res.status(400).json({ error: 'missing_fields', need: ['resource', 'action'] });
|
|
81
|
+
}
|
|
82
|
+
const r = gov.definePolicy({
|
|
83
|
+
agentId: req.agent.agent_id,
|
|
84
|
+
resource: String(b.resource),
|
|
85
|
+
action: String(b.action),
|
|
86
|
+
scope: b.scope || null,
|
|
87
|
+
maxAmount: b.max_amount,
|
|
88
|
+
currency: b.currency,
|
|
89
|
+
dailyCap: b.daily_cap,
|
|
90
|
+
perCallRate: b.per_call_rate,
|
|
91
|
+
requiresApproval: !!b.requires_approval,
|
|
92
|
+
effect: b.effect === 'deny' ? 'deny' : 'allow',
|
|
93
|
+
expiresAt: b.expires_at || null,
|
|
94
|
+
});
|
|
95
|
+
res.status(201).json({ ok: true, id: r.id });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
router.delete('/agents/:agentId/policies/:id', requireAgent, (req, res) => {
|
|
99
|
+
const ok = gov.deletePolicy(req.agent.agent_id, Number(req.params.id));
|
|
100
|
+
res.json({ ok });
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ───────────────────────── core check ────
|
|
104
|
+
// Pre-action authorisation. Agent calls this BEFORE executing.
|
|
105
|
+
router.post('/agents/:agentId/check', requireAgent, (req, res) => {
|
|
106
|
+
const { resource, action, scope, amount, currency } = req.body || {};
|
|
107
|
+
if (!resource || !action) {
|
|
108
|
+
return res.status(400).json({ error: 'missing_fields', need: ['resource', 'action'] });
|
|
109
|
+
}
|
|
110
|
+
const r = gov.check({
|
|
111
|
+
agentId: req.agent.agent_id, resource, action, scope, amount, currency,
|
|
112
|
+
});
|
|
113
|
+
// Audit the check itself (no params persisted by default).
|
|
114
|
+
gov.appendAudit({
|
|
115
|
+
agentId: req.agent.agent_id, eventType: 'check',
|
|
116
|
+
resource, action, scope, amount, currency,
|
|
117
|
+
decision: r.decision, reason: r.reason,
|
|
118
|
+
});
|
|
119
|
+
res.json({
|
|
120
|
+
decision: r.decision,
|
|
121
|
+
reason: r.reason,
|
|
122
|
+
policy_id: r.policy?.id || null,
|
|
123
|
+
extra: r.extra || null,
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Agent reports an executed action (for audit + spend tracking).
|
|
128
|
+
router.post('/agents/:agentId/execute', requireAgent, (req, res) => {
|
|
129
|
+
const b = req.body || {};
|
|
130
|
+
if (!b.resource || !b.action) {
|
|
131
|
+
return res.status(400).json({ error: 'missing_fields', need: ['resource', 'action'] });
|
|
132
|
+
}
|
|
133
|
+
const audit = gov.appendAudit({
|
|
134
|
+
agentId: req.agent.agent_id, eventType: 'execute',
|
|
135
|
+
resource: b.resource, action: b.action, scope: b.scope,
|
|
136
|
+
amount: b.amount, currency: b.currency,
|
|
137
|
+
decision: b.decision || 'allow', reason: b.reason || null,
|
|
138
|
+
paramsJson: b.params ? JSON.stringify(gov._internals.redact(b.params)) : null,
|
|
139
|
+
resultJson: b.result ? JSON.stringify(gov._internals.redact(b.result)) : null,
|
|
140
|
+
});
|
|
141
|
+
if (b.amount && Number(b.amount) > 0) {
|
|
142
|
+
gov.recordSpend(req.agent.agent_id, b.resource, Number(b.amount),
|
|
143
|
+
b.currency || 'USD', String(audit.id));
|
|
144
|
+
}
|
|
145
|
+
res.json({ ok: true, audit_id: audit.id, hash: audit.hash });
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// ───────────────────────── approvals ────
|
|
149
|
+
router.post('/agents/:agentId/approvals', requireAgent, (req, res) => {
|
|
150
|
+
const b = req.body || {};
|
|
151
|
+
if (!b.resource || !b.action) {
|
|
152
|
+
return res.status(400).json({ error: 'missing_fields', need: ['resource', 'action'] });
|
|
153
|
+
}
|
|
154
|
+
const r = gov.requestApproval({
|
|
155
|
+
agentId: req.agent.agent_id,
|
|
156
|
+
resource: b.resource, action: b.action, scope: b.scope,
|
|
157
|
+
amount: b.amount, currency: b.currency,
|
|
158
|
+
params: b.params, reason: b.reason, ttlMs: b.ttl_ms,
|
|
159
|
+
});
|
|
160
|
+
res.status(201).json(r);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
router.get('/agents/:agentId/approvals/pending', requireAgent, (req, res) => {
|
|
164
|
+
const limit = Math.min(500, Number(req.query.limit) || 100);
|
|
165
|
+
res.json({ pending: gov.listPendingApprovals(req.agent.agent_id, limit) });
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
router.get('/approvals/:requestId', (req, res) => {
|
|
169
|
+
const r = gov.getApproval(req.params.requestId);
|
|
170
|
+
if (!r) return res.status(404).json({ error: 'not_found' });
|
|
171
|
+
res.json(r);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Human decision endpoint. Requires the agent's token (treated as owner-grade).
|
|
175
|
+
// Future hardening: separate owner-level user auth.
|
|
176
|
+
router.post('/approvals/:requestId/decide', (req, res) => {
|
|
177
|
+
const r = gov.getApproval(req.params.requestId);
|
|
178
|
+
if (!r) return res.status(404).json({ error: 'not_found' });
|
|
179
|
+
// Authenticate as the owner-of-the-agent.
|
|
180
|
+
const auth = req.headers.authorization || '';
|
|
181
|
+
const headerToken = auth.startsWith('Bearer ') ? auth.slice(7).trim() : null;
|
|
182
|
+
const token = headerToken || req.body?.agent_token || req.query?.agent_token;
|
|
183
|
+
const a = gov.authAgent(r.agent_id, token);
|
|
184
|
+
if (!a) return res.status(401).json({ error: 'invalid_credentials' });
|
|
185
|
+
const decision = req.body?.decision === 'approved' ? 'approved' : 'rejected';
|
|
186
|
+
const out = gov.decideApproval(req.params.requestId, {
|
|
187
|
+
decision, decidedBy: req.body?.decided_by || a.owner_id || null,
|
|
188
|
+
note: req.body?.note || null,
|
|
189
|
+
});
|
|
190
|
+
if (!out.ok) return res.status(409).json(out);
|
|
191
|
+
res.json(out);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// ───────────────────────── audit ────
|
|
195
|
+
router.get('/agents/:agentId/audit', requireAgent, (req, res) => {
|
|
196
|
+
const limit = Math.min(1000, Number(req.query.limit) || 200);
|
|
197
|
+
const since = req.query.since || null;
|
|
198
|
+
const eventType = req.query.event || null;
|
|
199
|
+
res.json({
|
|
200
|
+
audit: gov.getAudit(req.agent.agent_id, { limit, since, eventType }),
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
router.get('/agents/:agentId/audit/verify', requireAgent, (req, res) => {
|
|
205
|
+
res.json(gov.verifyAuditChain(req.agent.agent_id));
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
module.exports = router;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public Plans API — feeds the landing-page pricing section.
|
|
3
|
+
* No authentication; returns only public, non-archived plans.
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const express = require('express');
|
|
8
|
+
const router = express.Router();
|
|
9
|
+
const plansService = require('../services/plans');
|
|
10
|
+
|
|
11
|
+
router.get('/', (req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
const plans = plansService.listPlans({ publicOnly: true });
|
|
14
|
+
const features = plansService.listFeatures();
|
|
15
|
+
res.json({
|
|
16
|
+
plans,
|
|
17
|
+
features,
|
|
18
|
+
generated_at: new Date().toISOString(),
|
|
19
|
+
});
|
|
20
|
+
} catch (err) {
|
|
21
|
+
res.status(500).json({ error: err.message });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
router.get('/:id', (req, res) => {
|
|
26
|
+
const plan = plansService.getPlan(req.params.id);
|
|
27
|
+
if (!plan || plan.is_archived || !plan.is_public) {
|
|
28
|
+
return res.status(404).json({ error: 'plan not found' });
|
|
29
|
+
}
|
|
30
|
+
res.json({ plan });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
module.exports = router;
|