@xshieldai/aegis-suite 0.2.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 +31 -0
- package/README.md +276 -0
- package/package.json +76 -0
- package/src/bus.ts +167 -0
- package/src/index.ts +43 -0
- package/src/wire.ts +145 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 19 November 2007
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2026 ANKR Labs / Capt. Anil Sharma
|
|
5
|
+
|
|
6
|
+
This program is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
This program is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Affero General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
The full text of the GNU Affero General Public License v3 is available at:
|
|
22
|
+
https://www.gnu.org/licenses/agpl-3.0.txt
|
|
23
|
+
|
|
24
|
+
ADDITIONAL TERMS (permitted under AGPL §7):
|
|
25
|
+
|
|
26
|
+
If you run a modified version of this software as a network service,
|
|
27
|
+
you must make the complete source code of the modified version available
|
|
28
|
+
to all users of that service under the terms of this license.
|
|
29
|
+
|
|
30
|
+
Commercial use, including SaaS deployments and enterprise integrations,
|
|
31
|
+
requires a separate commercial license. Contact: captain@ankr.in
|
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# @rocketlang/aegis-suite
|
|
2
|
+
|
|
3
|
+
**Meta-package.** Installs the full open-source AEGIS / KavachOS / xShieldAI agent governance stack in one shot.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @rocketlang/aegis-suite
|
|
7
|
+
# or
|
|
8
|
+
bun add @rocketlang/aegis-suite
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
That's it. You now have all six primitives. Import from each sub-package by name (this meta-package does not re-export — keep your imports honest about which primitive you're using).
|
|
12
|
+
|
|
13
|
+
## What's bundled (6 packages)
|
|
14
|
+
|
|
15
|
+
| Package | Role | Phase |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| [`@rocketlang/aegis`](https://www.npmjs.com/package/@rocketlang/aegis) | Agent **spend** governance: budget caps, kill-switch, DAN gate, HanumanG 7-axis spawn check | v2.1.0 stable |
|
|
18
|
+
| [`@rocketlang/kavachos`](https://www.npmjs.com/package/@rocketlang/kavachos) | Agent **behavior**: seccomp-bpf, Falco, syscall mediation, exec allowlist, egress firewall | v2.0.2 stable |
|
|
19
|
+
| [`@rocketlang/aegis-guard`](https://www.npmjs.com/package/@rocketlang/aegis-guard) | Five Locks SDK: approval-token, nonce, idempotency, SENSE, quality-evidence | v0.1.0 |
|
|
20
|
+
| [`@rocketlang/chitta-detect`](https://www.npmjs.com/package/@rocketlang/chitta-detect) | Memory poisoning detection: trust / imperative / tool-output / capability-expansion / fingerprint scanners | v0.1.0 |
|
|
21
|
+
| [`@rocketlang/lakshmanrekha`](https://www.npmjs.com/package/@rocketlang/lakshmanrekha) | LLM endpoint probe suite: 8 deterministic attack probes + replayable refusal classifier + multi-provider runner | v0.1.0 |
|
|
22
|
+
| [`@rocketlang/hanumang-mandate`](https://www.npmjs.com/package/@rocketlang/hanumang-mandate) | Mudrika delegation-credential verifier + 7-axis posture scorer | v0.1.0 |
|
|
23
|
+
|
|
24
|
+
## Deliberately NOT bundled
|
|
25
|
+
|
|
26
|
+
| Package | Why excluded |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `@rocketlang/n8n-nodes-kavachos` | n8n-specific integration — `npm install @rocketlang/n8n-nodes-kavachos` separately if you use n8n. Excluded so non-n8n users don't pull n8n-shaped deps. |
|
|
29
|
+
| `@rocketlang/kavachos-ee` | BSL-1.1 Enterprise Edition (PRAMANA Merkle ledger, HanumanG EE posture registry, dual-control approvals, multi-tenant isolation). Not published to npm. Contact [captain@ankr.in](mailto:captain@ankr.in) for design partner access. |
|
|
30
|
+
|
|
31
|
+
## Why a meta-package and not a fused single package?
|
|
32
|
+
|
|
33
|
+
The 6 primitives are **deliberately separate** — each addresses a different governance moment (spend / behavior / approval / memory / LLM probing / delegation). Forcing them into one fused package would:
|
|
34
|
+
|
|
35
|
+
- Multiply per-primitive deps for users who only need one
|
|
36
|
+
- Bundle SQLite + Postgres + fastify + bun-specific code for users running in browsers/edge
|
|
37
|
+
- Conflate the "stop bad spend" concern with the "verify mandate credentials" concern when neither needs the other
|
|
38
|
+
|
|
39
|
+
This meta-package is the **convenience installer**, not a re-architected mono-product. You get them all at once; you still import from each by name.
|
|
40
|
+
|
|
41
|
+
## The unified workflow (after install)
|
|
42
|
+
|
|
43
|
+
### Day-1: Aegis CLI sets up the spend gate
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Installed globally via @rocketlang/aegis bin
|
|
47
|
+
aegis init
|
|
48
|
+
aegis-monitor &
|
|
49
|
+
aegis-dashboard &
|
|
50
|
+
# → http://localhost:4850 (KAVACH DAN Gate dashboard)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Day-1: Probe your LLM endpoint with lakshmanrekha
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { runAllProbes, computeRefusalRate } from '@rocketlang/lakshmanrekha';
|
|
57
|
+
|
|
58
|
+
const results = await runAllProbes(
|
|
59
|
+
'https://api.openai.com/v1',
|
|
60
|
+
process.env.OPENAI_API_KEY!,
|
|
61
|
+
'openai',
|
|
62
|
+
{ model: 'gpt-4o-mini' }
|
|
63
|
+
);
|
|
64
|
+
console.log(`Refusal rate: ${computeRefusalRate(results.map(r => r.verdict))}%`);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Day-2: Scan persistent memory writes with chitta-detect
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { scan } from '@rocketlang/chitta-detect';
|
|
71
|
+
|
|
72
|
+
const result = scan.evaluate(suspiciousMemoryContent, { agent_id: 'agent-001' });
|
|
73
|
+
if (result.verdict === 'BLOCK') {
|
|
74
|
+
throw new Error(`memory write blocked: ${result.rules_fired.join(', ')}`);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Day-3: Verify agent mandates with hanumang-mandate
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { verifyMudrika, scoreAxis, computePostureScore } from '@rocketlang/hanumang-mandate';
|
|
82
|
+
|
|
83
|
+
const mandate = verifyMudrika(receivedMudrika, expectedAgentId);
|
|
84
|
+
if (mandate.outcome !== 'PASS') {
|
|
85
|
+
throw new Error(`mandate rejected: ${mandate.failure_reason}`);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Day-4: Wire Five Locks with aegis-guard
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { verifyApprovalToken, emitAegisSenseEvent, checkIdempotency } from '@rocketlang/aegis-guard';
|
|
93
|
+
|
|
94
|
+
// LOCK_1 — verify approval token before irreversible action
|
|
95
|
+
const payload = verifyApprovalToken(token, 'my-service', 'settle', 'record_settle');
|
|
96
|
+
|
|
97
|
+
// LOCK_3 — emit SENSE event with before/after delta
|
|
98
|
+
emitAegisSenseEvent({ event_type: 'allowance.settle', /* ... */ });
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Day-N: Kernel-enforce behavior with kavachos (when ready)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Installed via @rocketlang/kavachos bin (note: bin name collision with aegis's
|
|
105
|
+
# bundled kavachos shim — use whichever is on your PATH first; both point at
|
|
106
|
+
# the same kernel-enforcement primitives)
|
|
107
|
+
kavachos audit ./my-agent.config.json
|
|
108
|
+
kavachos generate seccomp ./policy.bpf
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Why this matters — the open primitive vs the hosted product
|
|
112
|
+
|
|
113
|
+
[Fin Operator](https://www.fin.ai/) launched 2026-05-15 as a Pro-tier subscription product whose "proposal system" puts a human gate between AI agents and the systems they change. The same primitives — pull-request-shaped intercepts, agent-managing-agent, attestation chains — are open and self-hostable here. `aegis` was born 17 April 2026, about a month before Fin Operator's launch, from a real $200 incident with an unmonitored Claude Code session.
|
|
114
|
+
|
|
115
|
+
| | Fin Operator (2026-05-15) | @rocketlang/aegis-suite (2026-05-16) |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| Distribution | Pro-tier subscription, vendor-hosted | `npm install @rocketlang/aegis-suite`, self-hosted |
|
|
118
|
+
| License | Proprietary | AGPL-3.0-only (suite) + BSL-1.1 → AGPL-3.0 in 4 years (EE) |
|
|
119
|
+
| Scope | Bound to the Fin platform | Vendor-neutral (Claude Code, OpenAI Codex, Cursor, custom) |
|
|
120
|
+
| Self-host | No | Yes — local-first by default |
|
|
121
|
+
| Audit | Trust the vendor | `grep -rn "fetch(" node_modules/@rocketlang/*/src/` |
|
|
122
|
+
| Pricing | Pro tier + usage blocks | $0 OSS · BSL-1.1 EE free up to 3 concurrent sessions |
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
AGPL-3.0-only (the meta-package itself + all 6 bundled packages). Any modified version run as a network service must publish source per AGPL clause 13.
|
|
127
|
+
|
|
128
|
+
EE packages (BSL-1.1) are separate — see boundary doc at [OPEN-CORE-BOUNDARY.md](https://github.com/rocketlang/aegis/blob/main/OPEN-CORE-BOUNDARY.md).
|
|
129
|
+
|
|
130
|
+
For commercial dual-licensing: [captain@ankr.in](mailto:captain@ankr.in).
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## v0.2.0 — `wireAllToBus()` + Agentic Control Center event bus
|
|
135
|
+
|
|
136
|
+
Added 2026-05-17. One call wires all 4 OSS primitives (aegis-guard,
|
|
137
|
+
chitta-detect, lakshmanrekha, hanumang-mandate) to a single event bus
|
|
138
|
+
+ persists every event to SQLite at `~/.aegis/acc-events.db`. The
|
|
139
|
+
aegis dashboard (v2.2.0+, ships same release wave) reads from this
|
|
140
|
+
file to render the **Agentic Control Center** page at
|
|
141
|
+
`http://localhost:4850/control-center`.
|
|
142
|
+
|
|
143
|
+
### Quick start
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { wireAllToBus } from '@rocketlang/aegis-suite';
|
|
147
|
+
|
|
148
|
+
// One call — wires all 4 primitives + sets up SQLite writer
|
|
149
|
+
const handle = wireAllToBus();
|
|
150
|
+
|
|
151
|
+
console.log('Events persisting to:', handle.sqlitePath);
|
|
152
|
+
// → /home/you/.aegis/acc-events.db
|
|
153
|
+
|
|
154
|
+
// Now use any of the @rocketlang primitives normally — every operation
|
|
155
|
+
// emits a receipt that lands in the SQLite file:
|
|
156
|
+
import { verifyApprovalToken } from '@rocketlang/aegis-guard';
|
|
157
|
+
import { scan } from '@rocketlang/chitta-detect';
|
|
158
|
+
|
|
159
|
+
verifyApprovalToken(token, 'svc', 'cap', 'op'); // → emits lock.approval.verified
|
|
160
|
+
scan.evaluate(content, { agent_id: 'agent-1' }); // → emits scan.evaluated
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### View live in the Agentic Control Center
|
|
164
|
+
|
|
165
|
+
If you also have `@rocketlang/aegis` v2.2.0+ installed and the dashboard
|
|
166
|
+
running (`aegis-dashboard &`), visit:
|
|
167
|
+
|
|
168
|
+
- **`http://localhost:4850/control-center`** — single-page grid with 6
|
|
169
|
+
zones (one per primitive + PRAMANA panel)
|
|
170
|
+
- **`http://localhost:4850/agent/:id`** — per-agent timeline across all
|
|
171
|
+
primitives, ordered by emission time
|
|
172
|
+
- **`http://localhost:4850/api/acc/events`** — JSON query API
|
|
173
|
+
- **`http://localhost:4850/api/acc/health`** — counts by primitive
|
|
174
|
+
|
|
175
|
+
All routes are gated by the dashboard's session auth when
|
|
176
|
+
`dashboard.auth.enabled: true` in `~/.aegis/config.json` (the default
|
|
177
|
+
after `aegis init`).
|
|
178
|
+
|
|
179
|
+
### Subscribe to events live (custom processing)
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const handle = wireAllToBus();
|
|
183
|
+
const unsub = handle.subscribe!((receipt) => {
|
|
184
|
+
if (receipt.verdict === 'BLOCK' || receipt.verdict === 'FAIL') {
|
|
185
|
+
notifyOps(receipt); // your custom alerting
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
// later: unsub() to detach
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Bring your own bus
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import type { EventBus, AccReceipt } from '@rocketlang/aegis-suite';
|
|
195
|
+
|
|
196
|
+
const myBus: EventBus = {
|
|
197
|
+
emit: (r: AccReceipt) => sendToRedis(r), // your transport
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
wireAllToBus({ bus: myBus }); // no SQLite, no in-memory fan-out — fully delegated
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Detach when done
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import { unwireAll } from '@rocketlang/aegis-suite';
|
|
207
|
+
unwireAll(); // all 4 primitives revert to v0.1.0 (no emission)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Architecture
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
consumer process
|
|
214
|
+
├─ wireAllToBus() ──┬─ setEventBus on aegis-guard
|
|
215
|
+
│ ├─ setEventBus on chitta-detect
|
|
216
|
+
│ ├─ setEventBus on lakshmanrekha
|
|
217
|
+
│ └─ setEventBus on hanumang-mandate
|
|
218
|
+
│
|
|
219
|
+
├─ InMemoryBus ──┬─ fan-out to subscribers (your live listeners)
|
|
220
|
+
│ └─ SqliteEventWriter ──> ~/.aegis/acc-events.db
|
|
221
|
+
│
|
|
222
|
+
└─ (your code calls primitives normally)
|
|
223
|
+
|
|
224
|
+
aegis-dashboard (separate process, v2.2.0+)
|
|
225
|
+
└─ reads ~/.aegis/acc-events.db
|
|
226
|
+
└─ /control-center, /agent/:id, /api/acc/*
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Phase-1 limits (v0.2.0)
|
|
230
|
+
|
|
231
|
+
- **Same-process vs cross-process visibility.** When the consumer
|
|
232
|
+
process and dashboard process are different (typical: your app + the
|
|
233
|
+
aegis-dashboard service), the consumer writes to SQLite via WAL.
|
|
234
|
+
Reader-side visibility lags slightly because WAL pages are not
|
|
235
|
+
automatically checkpointed back to the main DB file until
|
|
236
|
+
~1000 writes accumulate. **If you want immediate cross-process
|
|
237
|
+
visibility, call `handle.checkpoint!()`** after a batch of activity
|
|
238
|
+
(e.g., end-of-request handler) or periodically (e.g., every 30s).
|
|
239
|
+
Single-process (consumer == dashboard) needs no checkpointing.
|
|
240
|
+
- **`@rocketlang/aegis` v2.2.0 required for the cockpit UI.** The
|
|
241
|
+
dashboard at port 4850 needs aegis v2.2.0 (which ships in this same
|
|
242
|
+
release wave) to expose the `/control-center` route. Events still
|
|
243
|
+
persist to SQLite with any aegis version; only the rendering layer
|
|
244
|
+
needs v2.2.0.
|
|
245
|
+
- **Default bus is in-process only.** Multi-process buses (Redis,
|
|
246
|
+
Kafka, NATS) are a consumer choice — implement the `EventBus`
|
|
247
|
+
interface and pass via `wireAllToBus({ bus: yourBus })`.
|
|
248
|
+
- **WAL files (`-wal`, `-shm`) accompany `acc-events.db`.** If you
|
|
249
|
+
move/copy the SQLite file, take all three together or call
|
|
250
|
+
`checkpoint()` first to consolidate into the main file.
|
|
251
|
+
- **`@rocketlang/n8n-nodes-kavachos` is NOT wired.** It's an n8n
|
|
252
|
+
integration, not a primitive — its event-bus story is the consumer's
|
|
253
|
+
n8n workflow, not `wireAllToBus`.
|
|
254
|
+
|
|
255
|
+
### SQLite schema (for direct query access)
|
|
256
|
+
|
|
257
|
+
```sql
|
|
258
|
+
CREATE TABLE acc_events (
|
|
259
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
260
|
+
receipt_id TEXT NOT NULL,
|
|
261
|
+
primitive TEXT NOT NULL, -- 'aegis-guard' | 'chitta-detect' | etc.
|
|
262
|
+
event_type TEXT NOT NULL, -- 'lock.approval.verified' | 'scan.evaluated' | etc.
|
|
263
|
+
emitted_at TEXT NOT NULL, -- ISO 8601
|
|
264
|
+
agent_id TEXT,
|
|
265
|
+
verdict TEXT, -- PASS | FAIL | BLOCK | etc.
|
|
266
|
+
rules_fired TEXT, -- JSON array, e.g. '["AEG-E-016"]'
|
|
267
|
+
summary TEXT,
|
|
268
|
+
payload TEXT, -- JSON object
|
|
269
|
+
ingested_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
270
|
+
);
|
|
271
|
+
-- Indexes on (primitive, emitted_at), (agent_id, emitted_at), event_type, id
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Schema is forward-compatible-additive only (ACC-YK-006) — fields added,
|
|
275
|
+
never removed or renamed. Direct SQL queries from external tools
|
|
276
|
+
(Grafana, datadog-agent, custom scripts) are supported and stable.
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xshieldai/aegis-suite",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Meta-package — installs the full xShieldAI Posture Suite (AEGIS + Agent Kernel + 4 primitives) in one shot + wireAllToBus() helper that connects all 4 primitives to a single SQLite event bus for the Agentic Control Center. Open-source counter to Fin Operator's hosted governance plane.",
|
|
5
|
+
"license": "AGPL-3.0-only",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"author": "Capt. Anil Sharma <capt.anil.sharma@powerpbox.org>",
|
|
8
|
+
"homepage": "https://github.com/rocketlang/aegis/tree/main/packages/aegis-suite",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/rocketlang/aegis.git",
|
|
12
|
+
"directory": "packages/aegis-suite"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/rocketlang/aegis/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"aegis",
|
|
19
|
+
"aegis-suite",
|
|
20
|
+
"rocketlang",
|
|
21
|
+
"xshieldai",
|
|
22
|
+
"ai-governance",
|
|
23
|
+
"ai-agent-safety",
|
|
24
|
+
"agent-spend",
|
|
25
|
+
"agent-behavior",
|
|
26
|
+
"memory-poisoning",
|
|
27
|
+
"llm-redteam",
|
|
28
|
+
"agent-delegation",
|
|
29
|
+
"open-source",
|
|
30
|
+
"fin-operator-alternative"
|
|
31
|
+
],
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"import": "./src/index.ts",
|
|
35
|
+
"types": "./src/index.ts"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"main": "./src/index.ts",
|
|
39
|
+
"files": [
|
|
40
|
+
"src/",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@xshieldai/aegis": "^2.2.0",
|
|
46
|
+
"@xshieldai/agent-kernel": "^2.0.2",
|
|
47
|
+
"@xshieldai/aegis-guard": "^0.2.0",
|
|
48
|
+
"@xshieldai/chitta-detect": "^0.2.0",
|
|
49
|
+
"@xshieldai/lakshmanrekha": "^0.2.0",
|
|
50
|
+
"@xshieldai/hanumang-mandate": "^0.2.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"typecheck": "tsc --noEmit"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"typescript": "^5.4.0",
|
|
57
|
+
"@types/node": "^20.0.0"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"bun": ">=1.0.0",
|
|
61
|
+
"node": ">=18.0.0"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
65
|
+
},
|
|
66
|
+
"aegis_suite": {
|
|
67
|
+
"purpose": "single-install bundle of the OSS half of the xShieldAI Posture Suite (AEGIS + Agent Kernel + primitives)",
|
|
68
|
+
"bundled_packages": 6,
|
|
69
|
+
"deliberately_excluded": [
|
|
70
|
+
"@xshieldai/n8n-nodes (n8n-specific integration, install separately if using n8n)"
|
|
71
|
+
],
|
|
72
|
+
"not_bundled_because_ee": [
|
|
73
|
+
"@xshieldai/agent-kernel-ee (BSL-1.1, not on npm — contact captain@ankr.in)"
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
}
|
package/src/bus.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 Capt. Anil Sharma (rocketlang). All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// @rocketlang/aegis-suite — InMemoryBus + SqliteEventWriter (v0.2.0)
|
|
5
|
+
//
|
|
6
|
+
// Self-contained default bus implementation for wireAllToBus(). Writes to
|
|
7
|
+
// ~/.aegis/acc-events.db using a forward-compatible-additive schema. The
|
|
8
|
+
// aegis dashboard (v2.2.0+) reads from the same file using its own copy
|
|
9
|
+
// of the same schema — both use CREATE IF NOT EXISTS so the file is
|
|
10
|
+
// schema-idempotent regardless of which process writes first.
|
|
11
|
+
//
|
|
12
|
+
// @rule:ACC-005 — Black-box separation: separate from aegis.db / turn-store.db
|
|
13
|
+
// @rule:ACC-011 — In-process / single-process default. Multi-process bus is
|
|
14
|
+
// consumer choice.
|
|
15
|
+
// @rule:ACC-YK-006 — Schema forward-compatible-additive only.
|
|
16
|
+
|
|
17
|
+
import { Database } from 'bun:sqlite';
|
|
18
|
+
import { mkdirSync, existsSync } from 'fs';
|
|
19
|
+
import { dirname, join } from 'path';
|
|
20
|
+
import { homedir } from 'os';
|
|
21
|
+
import type { AccReceipt, EventBus } from './wire';
|
|
22
|
+
|
|
23
|
+
export function defaultAccEventsDbPath(): string {
|
|
24
|
+
const aegisDir = process.env.AEGIS_DIR ?? join(homedir(), '.aegis');
|
|
25
|
+
if (!existsSync(aegisDir)) mkdirSync(aegisDir, { recursive: true });
|
|
26
|
+
return join(aegisDir, 'acc-events.db');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ── InMemoryBus — fans events out to N subscribers in-process ────────────────
|
|
30
|
+
|
|
31
|
+
export type Subscriber = (r: AccReceipt) => void;
|
|
32
|
+
|
|
33
|
+
export class InMemoryBus implements EventBus {
|
|
34
|
+
private subscribers: Set<Subscriber> = new Set();
|
|
35
|
+
|
|
36
|
+
emit(receipt: AccReceipt): void {
|
|
37
|
+
for (const sub of Array.from(this.subscribers)) {
|
|
38
|
+
try {
|
|
39
|
+
sub(receipt);
|
|
40
|
+
} catch {
|
|
41
|
+
// never let one bad subscriber break others (ACC-YK-003)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
subscribe(fn: Subscriber): () => void {
|
|
47
|
+
this.subscribers.add(fn);
|
|
48
|
+
return () => this.subscribers.delete(fn);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
subscriberCount(): number {
|
|
52
|
+
return this.subscribers.size;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── SQLite schema — must match aegis core's reader schema ────────────────────
|
|
57
|
+
// If you change this, also change /root/aegis/src/acc/bus.ts SCHEMA in aegis
|
|
58
|
+
// core. Schema is CREATE IF NOT EXISTS so additions are safe; never remove or
|
|
59
|
+
// rename columns (ACC-YK-006 forward-compatible-additive only).
|
|
60
|
+
|
|
61
|
+
const SCHEMA = `
|
|
62
|
+
CREATE TABLE IF NOT EXISTS acc_events (
|
|
63
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
64
|
+
receipt_id TEXT NOT NULL,
|
|
65
|
+
primitive TEXT NOT NULL,
|
|
66
|
+
event_type TEXT NOT NULL,
|
|
67
|
+
emitted_at TEXT NOT NULL,
|
|
68
|
+
agent_id TEXT,
|
|
69
|
+
verdict TEXT,
|
|
70
|
+
rules_fired TEXT,
|
|
71
|
+
summary TEXT,
|
|
72
|
+
payload TEXT,
|
|
73
|
+
ingested_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
74
|
+
);
|
|
75
|
+
CREATE INDEX IF NOT EXISTS idx_acc_primitive_emitted ON acc_events (primitive, emitted_at);
|
|
76
|
+
CREATE INDEX IF NOT EXISTS idx_acc_agent_emitted ON acc_events (agent_id, emitted_at);
|
|
77
|
+
CREATE INDEX IF NOT EXISTS idx_acc_event_type ON acc_events (event_type);
|
|
78
|
+
CREATE INDEX IF NOT EXISTS idx_acc_id ON acc_events (id);
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
export interface SqliteEventWriterOpts {
|
|
82
|
+
path?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class SqliteEventWriter {
|
|
86
|
+
private db: Database;
|
|
87
|
+
private insertStmt: ReturnType<Database['query']>;
|
|
88
|
+
public readonly path: string;
|
|
89
|
+
|
|
90
|
+
constructor(opts: SqliteEventWriterOpts = {}) {
|
|
91
|
+
this.path = opts.path ?? defaultAccEventsDbPath();
|
|
92
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
93
|
+
this.db = new Database(this.path);
|
|
94
|
+
this.db.exec('PRAGMA journal_mode = WAL;');
|
|
95
|
+
this.db.exec(SCHEMA);
|
|
96
|
+
this.insertStmt = this.db.query(`
|
|
97
|
+
INSERT INTO acc_events
|
|
98
|
+
(receipt_id, primitive, event_type, emitted_at, agent_id, verdict, rules_fired, summary, payload)
|
|
99
|
+
VALUES
|
|
100
|
+
(?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
101
|
+
`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
write(receipt: AccReceipt): void {
|
|
105
|
+
try {
|
|
106
|
+
this.insertStmt.run(
|
|
107
|
+
receipt.receipt_id,
|
|
108
|
+
receipt.primitive,
|
|
109
|
+
receipt.event_type,
|
|
110
|
+
receipt.emitted_at,
|
|
111
|
+
receipt.agent_id ?? null,
|
|
112
|
+
receipt.verdict ?? null,
|
|
113
|
+
receipt.rules_fired ? JSON.stringify(receipt.rules_fired) : null,
|
|
114
|
+
receipt.summary ?? null,
|
|
115
|
+
receipt.payload ? JSON.stringify(receipt.payload) : null,
|
|
116
|
+
);
|
|
117
|
+
} catch {
|
|
118
|
+
// never throw — preserves ACC-YK-003 stateless-primitive contract
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Force a WAL checkpoint so writes from this process are visible to readers
|
|
123
|
+
* in other processes (e.g., the aegis dashboard in a separate process). */
|
|
124
|
+
checkpoint(): void {
|
|
125
|
+
try {
|
|
126
|
+
this.db.exec('PRAGMA wal_checkpoint(TRUNCATE);');
|
|
127
|
+
} catch {
|
|
128
|
+
// checkpoint failure non-fatal
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Total event count — for health checks. */
|
|
133
|
+
totalCount(): number {
|
|
134
|
+
try {
|
|
135
|
+
const row = this.db.query('SELECT COUNT(*) AS n FROM acc_events').get() as { n: number };
|
|
136
|
+
return row.n;
|
|
137
|
+
} catch {
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
close(): void {
|
|
143
|
+
try { this.db.close(); } catch { /* */ }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── Convenience constructor ──────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
export interface DefaultBusOpts {
|
|
150
|
+
sqlitePath?: string;
|
|
151
|
+
persist?: boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface DefaultBusHandle {
|
|
155
|
+
bus: InMemoryBus;
|
|
156
|
+
writer: SqliteEventWriter | null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function createDefaultBus(opts: DefaultBusOpts = {}): DefaultBusHandle {
|
|
160
|
+
const bus = new InMemoryBus();
|
|
161
|
+
let writer: SqliteEventWriter | null = null;
|
|
162
|
+
if (opts.persist !== false) {
|
|
163
|
+
writer = new SqliteEventWriter({ path: opts.sqlitePath });
|
|
164
|
+
bus.subscribe((r) => writer!.write(r));
|
|
165
|
+
}
|
|
166
|
+
return { bus, writer };
|
|
167
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 Capt. Anil Sharma (rocketlang). All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// @rocketlang/aegis-suite — meta-package. The value of this package is its
|
|
5
|
+
// dependency list (installs all 6 OSS @rocketlang governance primitives in
|
|
6
|
+
// one shot). Import from the sub-packages by name:
|
|
7
|
+
//
|
|
8
|
+
// import { runProbe } from '@rocketlang/lakshmanrekha';
|
|
9
|
+
// import { trust, scan } from '@rocketlang/chitta-detect';
|
|
10
|
+
// import { verifyMudrika, scoreAxis } from '@rocketlang/hanumang-mandate';
|
|
11
|
+
// import { verifyApprovalToken } from '@rocketlang/aegis-guard';
|
|
12
|
+
//
|
|
13
|
+
// The `aegis` and `kavachos` CLIs ship as bin entries in those packages.
|
|
14
|
+
// See README for the unified workflow.
|
|
15
|
+
|
|
16
|
+
export const AEGIS_SUITE_VERSION = '0.2.0';
|
|
17
|
+
export const AEGIS_SUITE_BUNDLED_PACKAGES = [
|
|
18
|
+
'@rocketlang/aegis',
|
|
19
|
+
'@rocketlang/kavachos',
|
|
20
|
+
'@rocketlang/aegis-guard',
|
|
21
|
+
'@rocketlang/chitta-detect',
|
|
22
|
+
'@rocketlang/lakshmanrekha',
|
|
23
|
+
'@rocketlang/hanumang-mandate',
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
export interface SuiteManifest {
|
|
27
|
+
version: typeof AEGIS_SUITE_VERSION;
|
|
28
|
+
bundled_packages: typeof AEGIS_SUITE_BUNDLED_PACKAGES;
|
|
29
|
+
excluded: { package: string; reason: string }[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// @rule:ACC-003 — wireAllToBus helper (v0.2.0+)
|
|
33
|
+
export { wireAllToBus, unwireAll, getWiredHandle } from './wire.js';
|
|
34
|
+
export type { EventBus, AccReceipt, WireHandle, WireAllOpts } from './wire.js';
|
|
35
|
+
|
|
36
|
+
export const SUITE_MANIFEST: SuiteManifest = {
|
|
37
|
+
version: AEGIS_SUITE_VERSION,
|
|
38
|
+
bundled_packages: AEGIS_SUITE_BUNDLED_PACKAGES,
|
|
39
|
+
excluded: [
|
|
40
|
+
{ package: '@rocketlang/n8n-nodes-kavachos', reason: 'n8n-specific integration — install separately if using n8n' },
|
|
41
|
+
{ package: '@rocketlang/kavachos-ee', reason: 'BSL-1.1 EE, not on npm — contact captain@ankr.in' },
|
|
42
|
+
],
|
|
43
|
+
};
|
package/src/wire.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 Capt. Anil Sharma (rocketlang). All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// @rocketlang/aegis-suite — wireAllToBus helper (v0.2.0)
|
|
5
|
+
//
|
|
6
|
+
// One-call setup that wires all 4 OSS primitive packages to a single
|
|
7
|
+
// EventBus + persists to ~/.aegis/acc-events.db. The Agentic Control
|
|
8
|
+
// Center (in aegis dashboard v2.2.0+) reads from the same SQLite file.
|
|
9
|
+
//
|
|
10
|
+
// @rule:ACC-003 — Opt-in. Without wireAllToBus, primitives behave
|
|
11
|
+
// identically to their v0.1.0 (no emission).
|
|
12
|
+
// @rule:ACC-005 — SQLite file at ~/.aegis/acc-events.db, separate from
|
|
13
|
+
// aegis.db and turn-store.db.
|
|
14
|
+
|
|
15
|
+
import { setEventBus as setAegisGuardBus } from '@rocketlang/aegis-guard';
|
|
16
|
+
import { setEventBus as setChittaBus } from '@rocketlang/chitta-detect';
|
|
17
|
+
import { setEventBus as setLakshmanBus } from '@rocketlang/lakshmanrekha';
|
|
18
|
+
import { setEventBus as setHanumangBus } from '@rocketlang/hanumang-mandate';
|
|
19
|
+
import { createDefaultBus, type InMemoryBus, type SqliteEventWriter } from './bus.js';
|
|
20
|
+
|
|
21
|
+
// ── Canonical receipt + bus types ────────────────────────────────────────────
|
|
22
|
+
//
|
|
23
|
+
// Defined locally to avoid pulling any primitive's copy as the canonical one.
|
|
24
|
+
// All 4 primitives have structurally compatible local copies; TypeScript
|
|
25
|
+
// structural typing makes the wiring work.
|
|
26
|
+
|
|
27
|
+
export interface AccReceipt {
|
|
28
|
+
receipt_id: string;
|
|
29
|
+
primitive: string;
|
|
30
|
+
event_type: string;
|
|
31
|
+
emitted_at: string;
|
|
32
|
+
agent_id?: string;
|
|
33
|
+
verdict?: string;
|
|
34
|
+
rules_fired?: string[];
|
|
35
|
+
summary?: string;
|
|
36
|
+
payload?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface EventBus {
|
|
40
|
+
emit(receipt: AccReceipt): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── State ────────────────────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
let _busHandle: WireHandle | null = null;
|
|
46
|
+
|
|
47
|
+
export interface WireHandle {
|
|
48
|
+
bus: EventBus;
|
|
49
|
+
/** Subscribe to live events. Returns an unsubscribe function. */
|
|
50
|
+
subscribe?: (fn: (r: AccReceipt) => void) => () => void;
|
|
51
|
+
/** SQLite path actually in use, or undefined if persistence disabled. */
|
|
52
|
+
sqlitePath?: string;
|
|
53
|
+
/** Total receipts written so far (cumulative across this process). */
|
|
54
|
+
receivedCount(): number;
|
|
55
|
+
/** Force WAL checkpoint so writes become visible to readers in other
|
|
56
|
+
* processes (the aegis dashboard reads from the same file). */
|
|
57
|
+
checkpoint?: () => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface WireAllOpts {
|
|
61
|
+
/** Provide your own bus instead of the default in-memory + SQLite. */
|
|
62
|
+
bus?: EventBus;
|
|
63
|
+
/** Override SQLite path (default ~/.aegis/acc-events.db). */
|
|
64
|
+
sqlitePath?: string;
|
|
65
|
+
/** Set false to skip SQLite persistence (memory only). Default true. */
|
|
66
|
+
persist?: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ── wireAllToBus ─────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Wire all 4 OSS primitive packages (aegis-guard, chitta-detect,
|
|
73
|
+
* lakshmanrekha, hanumang-mandate) to a single event bus.
|
|
74
|
+
*
|
|
75
|
+
* Without `opts.bus`, creates a default in-memory bus that persists
|
|
76
|
+
* every event to SQLite at ~/.aegis/acc-events.db. The aegis dashboard
|
|
77
|
+
* (v2.2.0+) reads from this file to render the Agentic Control Center
|
|
78
|
+
* page at `/control-center`.
|
|
79
|
+
*
|
|
80
|
+
* Returns a handle so the caller can subscribe to live events or force
|
|
81
|
+
* WAL checkpoints for cross-process visibility.
|
|
82
|
+
*/
|
|
83
|
+
export function wireAllToBus(opts: WireAllOpts = {}): WireHandle {
|
|
84
|
+
if (opts.bus) {
|
|
85
|
+
// Caller supplied their own bus
|
|
86
|
+
let count = 0;
|
|
87
|
+
const userBus = opts.bus;
|
|
88
|
+
const wrappedBus: EventBus = {
|
|
89
|
+
emit: (r) => {
|
|
90
|
+
count++;
|
|
91
|
+
userBus.emit(r);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
setAegisGuardBus(wrappedBus as unknown as Parameters<typeof setAegisGuardBus>[0]);
|
|
95
|
+
setChittaBus(wrappedBus as unknown as Parameters<typeof setChittaBus>[0]);
|
|
96
|
+
setLakshmanBus(wrappedBus as unknown as Parameters<typeof setLakshmanBus>[0]);
|
|
97
|
+
setHanumangBus(wrappedBus as unknown as Parameters<typeof setHanumangBus>[0]);
|
|
98
|
+
_busHandle = {
|
|
99
|
+
bus: wrappedBus,
|
|
100
|
+
receivedCount: () => count,
|
|
101
|
+
};
|
|
102
|
+
return _busHandle;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Default path — in-memory bus + SQLite writer
|
|
106
|
+
const { bus, writer } = createDefaultBus({
|
|
107
|
+
sqlitePath: opts.sqlitePath,
|
|
108
|
+
persist: opts.persist !== false,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
let count = 0;
|
|
112
|
+
const wrappedBus: EventBus = {
|
|
113
|
+
emit: (r) => {
|
|
114
|
+
count++;
|
|
115
|
+
bus.emit(r);
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
setAegisGuardBus(wrappedBus as unknown as Parameters<typeof setAegisGuardBus>[0]);
|
|
120
|
+
setChittaBus(wrappedBus as unknown as Parameters<typeof setChittaBus>[0]);
|
|
121
|
+
setLakshmanBus(wrappedBus as unknown as Parameters<typeof setLakshmanBus>[0]);
|
|
122
|
+
setHanumangBus(wrappedBus as unknown as Parameters<typeof setHanumangBus>[0]);
|
|
123
|
+
|
|
124
|
+
_busHandle = {
|
|
125
|
+
bus: wrappedBus,
|
|
126
|
+
subscribe: (fn) => (bus as InMemoryBus).subscribe(fn),
|
|
127
|
+
sqlitePath: writer?.path,
|
|
128
|
+
receivedCount: () => count,
|
|
129
|
+
checkpoint: writer ? () => (writer as SqliteEventWriter).checkpoint() : undefined,
|
|
130
|
+
};
|
|
131
|
+
return _busHandle;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Detach the bus from all 4 primitives. Primitives revert to v0.1.0 behaviour. */
|
|
135
|
+
export function unwireAll(): void {
|
|
136
|
+
setAegisGuardBus(null);
|
|
137
|
+
setChittaBus(null);
|
|
138
|
+
setLakshmanBus(null);
|
|
139
|
+
setHanumangBus(null);
|
|
140
|
+
_busHandle = null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function getWiredHandle(): WireHandle | null {
|
|
144
|
+
return _busHandle;
|
|
145
|
+
}
|