@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 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
+ }