aegis-desktop 0.8.10 → 0.8.12
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/lib/local/aegis-seal.mjs +236 -0
- package/lib/local/covenant.js +562 -0
- package/lib/local/engine.js +29 -0
- package/lib/local/prompt.js +5 -0
- package/main.js +49 -0
- package/package.json +1 -1
- package/preload.js +19 -0
- package/vendor/aegis.js +13 -1
- package/vendor/covenant/README.md +21 -0
- package/vendor/covenant/aegis.seal +177 -0
|
@@ -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
|
+
};
|
package/lib/local/engine.js
CHANGED
|
@@ -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,
|
package/lib/local/prompt.js
CHANGED
|
@@ -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.
|
|
4
|
+
"version": "0.8.12",
|
|
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
|
|
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
|