@connexum/ai-governance 1.0.0-beta.21 → 1.0.0-beta.23
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/dist/cli/agent-dir-scanner.d.ts +32 -0
- package/dist/cli/agent-dir-scanner.d.ts.map +1 -1
- package/dist/cli/agent-dir-scanner.js +52 -0
- package/dist/cli/agent-dir-scanner.js.map +1 -1
- package/dist/cli/governance-md-renderer.d.ts +50 -0
- package/dist/cli/governance-md-renderer.d.ts.map +1 -0
- package/dist/cli/governance-md-renderer.js +185 -0
- package/dist/cli/governance-md-renderer.js.map +1 -0
- package/dist/cli/governance-projection-writer.d.ts +88 -0
- package/dist/cli/governance-projection-writer.d.ts.map +1 -0
- package/dist/cli/governance-projection-writer.js +291 -0
- package/dist/cli/governance-projection-writer.js.map +1 -0
- package/dist/cli/index.d.ts +85 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +343 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/per-folder-identity.d.ts +79 -0
- package/dist/cli/per-folder-identity.d.ts.map +1 -0
- package/dist/cli/per-folder-identity.js +321 -0
- package/dist/cli/per-folder-identity.js.map +1 -0
- package/dist/cli/sync.d.ts +193 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +1094 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/cli/wrap-shim-generator.d.ts +33 -0
- package/dist/cli/wrap-shim-generator.d.ts.map +1 -1
- package/dist/cli/wrap-shim-generator.js +93 -8
- package/dist/cli/wrap-shim-generator.js.map +1 -1
- package/dist/esm/cli/agent-dir-scanner.js +52 -0
- package/dist/esm/cli/agent-dir-scanner.js.map +1 -1
- package/dist/esm/cli/governance-md-renderer.js +182 -0
- package/dist/esm/cli/governance-md-renderer.js.map +1 -0
- package/dist/esm/cli/governance-projection-writer.js +253 -0
- package/dist/esm/cli/governance-projection-writer.js.map +1 -0
- package/dist/esm/cli/index.js +343 -3
- package/dist/esm/cli/index.js.map +1 -1
- package/dist/esm/cli/per-folder-identity.js +283 -0
- package/dist/esm/cli/per-folder-identity.js.map +1 -0
- package/dist/esm/cli/sync.js +1054 -0
- package/dist/esm/cli/sync.js.map +1 -0
- package/dist/esm/cli/wrap-shim-generator.js +92 -8
- package/dist/esm/cli/wrap-shim-generator.js.map +1 -1
- package/dist/esm/governance/governance-projection-canonical.js +101 -0
- package/dist/esm/governance/governance-projection-canonical.js.map +1 -0
- package/dist/governance/governance-projection-canonical.d.ts +104 -0
- package/dist/governance/governance-projection-canonical.d.ts.map +1 -0
- package/dist/governance/governance-projection-canonical.js +141 -0
- package/dist/governance/governance-projection-canonical.js.map +1 -0
- package/dist/hooks/audit-logger.sh +108 -10
- package/package.json +1 -1
- package/src/hooks/audit-logger.sh +108 -10
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.resolveGuardedAgentFolder = resolveGuardedAgentFolder;
|
|
37
|
+
exports.buildPerFolderIdentityInputs = buildPerFolderIdentityInputs;
|
|
38
|
+
exports.writePerFolderIdentities = writePerFolderIdentities;
|
|
39
|
+
/**
|
|
40
|
+
* B1 — Per-folder agent identity writer (per-agent identity-file injection RFC §5).
|
|
41
|
+
*
|
|
42
|
+
* Writes each agent's server identity + signed passport into its OWN folder:
|
|
43
|
+
* <agent>/.connexum/identity.json secret (serviceToken); mode 0600; git-ignored
|
|
44
|
+
* <agent>/.connexum/passport.json public verifiable credential (Invariant 4)
|
|
45
|
+
*
|
|
46
|
+
* Standalone + side-effect-scoped so it is unit-testable with mocked inputs and does NOT
|
|
47
|
+
* touch the register-fleet handler in index.ts (held by another workstream). A thin wiring
|
|
48
|
+
* layer builds PerFolderIdentityInput[] from register-fleet's registered[] + the local scan
|
|
49
|
+
* and calls writePerFolderIdentities().
|
|
50
|
+
*
|
|
51
|
+
* Invariant 2 (advisory, never block): never throws on a write/guard failure — records a
|
|
52
|
+
* skip + advisory and continues. Malformed existing files are tolerated (Invariant 5 echo).
|
|
53
|
+
*/
|
|
54
|
+
const fs = __importStar(require("fs"));
|
|
55
|
+
const path = __importStar(require("path"));
|
|
56
|
+
const os = __importStar(require("os"));
|
|
57
|
+
const IDENTITY_SCHEMA_VERSION = '1.0';
|
|
58
|
+
const GITIGNORE_RULE = '**/.connexum/identity.json';
|
|
59
|
+
/** Secret-file fields persisted into <agent>/.connexum/identity.json. */
|
|
60
|
+
const IDENTITY_FIELDS = [
|
|
61
|
+
'localId', 'agentId', 'passportId', 'installationId', 'orgId',
|
|
62
|
+
'signingKeyId', 'govServerUrl', 'serviceToken', 'serviceTokenJti',
|
|
63
|
+
'issuedAt', 'expiresAt',
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* SHARED traversal guard (Shield S-W2: the write-back F2 governance-projection
|
|
67
|
+
* writer must REUSE this guard, never re-implement it). Resolves the agent
|
|
68
|
+
* folder for a scanned filePath and confirms the REAL path (symlinks followed
|
|
69
|
+
* — Shield HIGH-1, PR #1707 review) is the project root or a child of it.
|
|
70
|
+
* realpathSync throws on a not-yet-existing path — there is no symlink to
|
|
71
|
+
* follow in that case, so falling back to the lexical path is safe.
|
|
72
|
+
*/
|
|
73
|
+
function resolveGuardedAgentFolder(projectDir, filePath) {
|
|
74
|
+
const projectRoot = path.resolve(projectDir);
|
|
75
|
+
const realProjectRoot = (() => {
|
|
76
|
+
try {
|
|
77
|
+
return fs.realpathSync(projectRoot);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return projectRoot;
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
83
|
+
const folder = path.dirname(path.resolve(projectRoot, filePath));
|
|
84
|
+
const realFolder = (() => {
|
|
85
|
+
try {
|
|
86
|
+
return fs.realpathSync(folder);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return folder;
|
|
90
|
+
}
|
|
91
|
+
})();
|
|
92
|
+
const rel = path.relative(realProjectRoot, realFolder);
|
|
93
|
+
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
94
|
+
return { error: 'path-traversal: folder outside project root' };
|
|
95
|
+
}
|
|
96
|
+
return { folder };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Pure Pass-1 wiring helper (identity RFC §5 thin wiring layer): join the
|
|
100
|
+
* register-fleet `registered[]` entries to the local scan's filePaths and the
|
|
101
|
+
* A1 fleet-shared response metadata, producing the inputs
|
|
102
|
+
* writePerFolderIdentities() lands on disk. Entries whose localId has no
|
|
103
|
+
* local filePath (e.g. server-side adoptions of agents not in this scan) are
|
|
104
|
+
* dropped — there is no folder to write into.
|
|
105
|
+
*/
|
|
106
|
+
function buildPerFolderIdentityInputs(registered, localRefs, meta, issuedAt) {
|
|
107
|
+
const filePathByLocalId = new Map(localRefs.map((r) => [r.agentId, r.filePath]));
|
|
108
|
+
const inputs = [];
|
|
109
|
+
for (const r of registered) {
|
|
110
|
+
const localId = r.localId ?? r.agentId;
|
|
111
|
+
const filePath = filePathByLocalId.get(localId);
|
|
112
|
+
if (!filePath)
|
|
113
|
+
continue;
|
|
114
|
+
inputs.push({
|
|
115
|
+
localId,
|
|
116
|
+
agentId: r.agentId,
|
|
117
|
+
filePath,
|
|
118
|
+
passportId: r.passportId,
|
|
119
|
+
// Pass 1 (pre-activation): serviceToken is null by design — delivered at
|
|
120
|
+
// Pass 2 (sync after Confirm+pay). The field-merge in the writer means a
|
|
121
|
+
// later Pass-1 re-run can never clobber a delivered token.
|
|
122
|
+
serviceToken: r.serviceToken,
|
|
123
|
+
installationId: meta.installationId ?? null,
|
|
124
|
+
signingKeyId: meta.signingKeyId ?? null,
|
|
125
|
+
govServerUrl: meta.govServerUrl ?? null,
|
|
126
|
+
orgId: meta.orgId ?? null,
|
|
127
|
+
issuedAt,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return inputs;
|
|
131
|
+
}
|
|
132
|
+
function writePerFolderIdentities(projectDir, inputs, opts = {}) {
|
|
133
|
+
const warn = opts.warn ?? ((m) => console.warn(m));
|
|
134
|
+
const result = { written: [], skipped: [] };
|
|
135
|
+
if (inputs.length === 0)
|
|
136
|
+
return result;
|
|
137
|
+
const projectRoot = path.resolve(projectDir);
|
|
138
|
+
// Pass-2 wiring fix: the writer root is the SCAN root (--agent-dir), which
|
|
139
|
+
// is usually a SUBDIRECTORY of the repo — `.git` lives at an ancestor. Walk
|
|
140
|
+
// up so a token write inside a real repo isn't wrongly refused; the
|
|
141
|
+
// .gitignore rule itself is still written at projectRoot (a nested
|
|
142
|
+
// .gitignore covers everything beneath it).
|
|
143
|
+
// Shield #1720 F2: a `.git` directly at the system temp dir or the
|
|
144
|
+
// filesystem root is a stray (shared-host artifact), not a repo that governs
|
|
145
|
+
// this project — never let it satisfy the secret-write precondition.
|
|
146
|
+
const STRAY_GIT_HOLDERS = new Set([os.tmpdir(), path.parse(projectRoot).root]);
|
|
147
|
+
const isGitRepo = (() => {
|
|
148
|
+
let dir = projectRoot;
|
|
149
|
+
for (;;) {
|
|
150
|
+
if (!STRAY_GIT_HOLDERS.has(dir) && fs.existsSync(path.join(dir, '.git')))
|
|
151
|
+
return true;
|
|
152
|
+
const parent = path.dirname(dir);
|
|
153
|
+
if (parent === dir)
|
|
154
|
+
return false;
|
|
155
|
+
dir = parent;
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
for (const input of inputs) {
|
|
159
|
+
// 1. Traversal guard — the resolved REAL folder must be the project root or a child of it.
|
|
160
|
+
const guarded = resolveGuardedAgentFolder(projectRoot, input.filePath);
|
|
161
|
+
if (!guarded.folder) {
|
|
162
|
+
result.skipped.push({ localId: input.localId, reason: guarded.error ?? 'path-traversal: folder outside project root' });
|
|
163
|
+
warn(`[connexum] identity skipped for ${input.localId}: resolved folder escapes project root (${input.filePath})`);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const folder = guarded.folder;
|
|
167
|
+
const hasSecret = input.serviceToken != null && input.serviceToken !== '';
|
|
168
|
+
// 2. gitignore-first HARD precondition when a bearer token will be written.
|
|
169
|
+
if (hasSecret) {
|
|
170
|
+
if (!isGitRepo && !opts.allowNoGit) {
|
|
171
|
+
result.skipped.push({ localId: input.localId, reason: 'no-git-repo: secret write needs allowNoGit opt-in' });
|
|
172
|
+
warn(`[connexum] identity skipped for ${input.localId}: project is not a git repo; refusing to write a bearer token without allowNoGit (it would not be git-ignored).`);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (!ensureGitignoreRule(projectRoot, warn)) {
|
|
176
|
+
result.skipped.push({ localId: input.localId, reason: 'gitignore-write-failed: aborted before writing secret' });
|
|
177
|
+
warn(`[connexum] identity skipped for ${input.localId}: could not write .gitignore rule; refusing to write the bearer token (a committed token is worse than none).`);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// Best-effort gitignore on metadata-only (Pass 1) — does not gate the write.
|
|
183
|
+
ensureGitignoreRule(projectRoot, () => { });
|
|
184
|
+
}
|
|
185
|
+
// 3. Write the .connexum/ files (field-merged identity, atomic; public passport).
|
|
186
|
+
try {
|
|
187
|
+
const dir = path.join(folder, '.connexum');
|
|
188
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
189
|
+
writeIdentityMerged(dir, input);
|
|
190
|
+
if (input.passport != null && input.passport !== '') {
|
|
191
|
+
// Shield MED-2 (PR #1707 review): passport.json is PUBLIC (deliberately not
|
|
192
|
+
// git-ignored), so structurally validate the caller-supplied string before it
|
|
193
|
+
// touches disk — a caller that accidentally passes identity material here must
|
|
194
|
+
// not publish a bearer token. Validation is local (no trust-api import: the CLI
|
|
195
|
+
// ships standalone): parsed JSON must be a plain object carrying a signature and
|
|
196
|
+
// must not contain a serviceToken field at any depth.
|
|
197
|
+
if (isPlausiblePublicPassport(input.passport)) {
|
|
198
|
+
// Plain write (not tmp→rename): public data, no mode requirement; a crash
|
|
199
|
+
// mid-write yields a malformed file the next sync overwrites. Deliberate
|
|
200
|
+
// asymmetry vs identity.json (Stern OBS-3).
|
|
201
|
+
fs.writeFileSync(path.join(dir, 'passport.json'), input.passport);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
warn(`[connexum] passport for ${input.localId} failed the public-credential structural check; skipping passport.json write`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
result.written.push(input.localId);
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
result.skipped.push({ localId: input.localId, reason: `write-failed: ${err.message}` });
|
|
211
|
+
warn(`[connexum] identity write failed for ${input.localId}: ${err.message}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Write identity.json with field-level merge + atomic replace.
|
|
218
|
+
* - Merge: an incoming null/undefined field NEVER clobbers a present stored value
|
|
219
|
+
* (so a Pass-1 re-register with serviceToken:null cannot wipe a delivered token).
|
|
220
|
+
* - Atomic: tmp → chmod 0600 → rename. Closes the write-then-chmod window where a kill
|
|
221
|
+
* between writeFileSync and chmodSync would leave the token at 0644 (Stern BLOCK-3).
|
|
222
|
+
*/
|
|
223
|
+
function writeIdentityMerged(dir, input) {
|
|
224
|
+
const file = path.join(dir, 'identity.json');
|
|
225
|
+
let existing = {};
|
|
226
|
+
if (fs.existsSync(file)) {
|
|
227
|
+
try {
|
|
228
|
+
// Stern CONDITION-1 (PR #1707 review): valid JSON that is not a plain object
|
|
229
|
+
// (string/number/array) would spread index keys ("0","1",...) into the merge.
|
|
230
|
+
// Tolerate-and-start-clean, same as the parse-failure path.
|
|
231
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
232
|
+
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
233
|
+
existing = parsed;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
existing = {}; // tolerate a corrupt prior file
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const merged = { ...existing };
|
|
241
|
+
merged.schemaVersion = IDENTITY_SCHEMA_VERSION;
|
|
242
|
+
merged.passportRef = 'passport.json';
|
|
243
|
+
for (const f of IDENTITY_FIELDS) {
|
|
244
|
+
const v = input[f];
|
|
245
|
+
if (v != null)
|
|
246
|
+
merged[f] = v; // present incoming value wins
|
|
247
|
+
else if (!(f in merged))
|
|
248
|
+
merged[f] = null; // record absence on first write only
|
|
249
|
+
}
|
|
250
|
+
const tmp = `${file}.tmp`;
|
|
251
|
+
fs.writeFileSync(tmp, `${JSON.stringify(merged, null, 2)}\n`, { mode: 0o600 });
|
|
252
|
+
try {
|
|
253
|
+
fs.chmodSync(tmp, 0o600);
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
if (process.platform !== 'win32') {
|
|
257
|
+
try {
|
|
258
|
+
fs.unlinkSync(tmp);
|
|
259
|
+
}
|
|
260
|
+
catch { /* ignore */ }
|
|
261
|
+
throw new Error(`chmod 0600 failed on identity file: ${err.message}`);
|
|
262
|
+
}
|
|
263
|
+
// Windows: 0600 semantics differ; proceed best-effort.
|
|
264
|
+
}
|
|
265
|
+
fs.renameSync(tmp, file); // atomic replace; preserves the tmp inode's 0600 mode
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Structural gate for the PUBLIC passport.json write (Shield MED-2): the string must
|
|
269
|
+
* parse to a plain JSON object that looks like a signed passport (has a `signature`)
|
|
270
|
+
* and must NOT carry a `serviceToken` key at any depth — the one secret that could
|
|
271
|
+
* plausibly be mis-routed here, and the file is deliberately not git-ignored.
|
|
272
|
+
* Local check only; the CLI cannot import trust-api's deserializePassport (standalone
|
|
273
|
+
* packaging). False ⇒ the caller skips the write with an advisory, never throws.
|
|
274
|
+
*/
|
|
275
|
+
function isPlausiblePublicPassport(serialized) {
|
|
276
|
+
try {
|
|
277
|
+
const parsed = JSON.parse(serialized);
|
|
278
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
279
|
+
return false;
|
|
280
|
+
if (typeof parsed['signature'] !== 'string')
|
|
281
|
+
return false;
|
|
282
|
+
const containsServiceToken = (v) => {
|
|
283
|
+
if (v === null || typeof v !== 'object')
|
|
284
|
+
return false;
|
|
285
|
+
for (const [k, child] of Object.entries(v)) {
|
|
286
|
+
if (k === 'serviceToken')
|
|
287
|
+
return true;
|
|
288
|
+
if (containsServiceToken(child))
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
};
|
|
293
|
+
return !containsServiceToken(parsed);
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Ensure the project .gitignore ignores the secret identity file (and NOT the public
|
|
301
|
+
* passport). Returns false if the rule could not be persisted — the caller then refuses
|
|
302
|
+
* to write the bearer token.
|
|
303
|
+
*/
|
|
304
|
+
function ensureGitignoreRule(projectRoot, warn) {
|
|
305
|
+
const file = path.join(projectRoot, '.gitignore');
|
|
306
|
+
try {
|
|
307
|
+
let content = '';
|
|
308
|
+
if (fs.existsSync(file))
|
|
309
|
+
content = fs.readFileSync(file, 'utf-8');
|
|
310
|
+
if (content.split(/\r?\n/).some((l) => l.trim() === GITIGNORE_RULE))
|
|
311
|
+
return true;
|
|
312
|
+
const prefix = content.length > 0 && !content.endsWith('\n') ? '\n' : '';
|
|
313
|
+
fs.appendFileSync(file, `${prefix}# Connexum per-agent identity (bearer serviceToken) — never commit\n${GITIGNORE_RULE}\n`);
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
warn(`[connexum] could not update .gitignore: ${err.message}`);
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=per-folder-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"per-folder-identity.js","sourceRoot":"","sources":["../../src/cli/per-folder-identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-governance sync — pull the server's signed governance bundle and
|
|
3
|
+
* re-materialize the local .governance.json to match.
|
|
4
|
+
*
|
|
5
|
+
* Design (Thomas, locked):
|
|
6
|
+
* - SAFE BY DEFAULT: no arguments = dry-run. Shows diff, writes nothing.
|
|
7
|
+
* - --apply (or interactive confirm) writes the new governance to disk.
|
|
8
|
+
* - NOT at runtime, NOT at init — a deliberate standalone verb.
|
|
9
|
+
* - Client PULLS from server. Server never calls out to the client (Invariant 10).
|
|
10
|
+
* - Server unreachable → graceful message, local config unchanged (Invariant 2/3).
|
|
11
|
+
* - Unsigned / tampered bundle → REJECTED, nothing written.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* ai-governance sync Dry-run: show diff, no writes
|
|
15
|
+
* ai-governance sync --apply Apply the bundle to .governance.json
|
|
16
|
+
* ai-governance sync --agent <id> Sync a specific agent only
|
|
17
|
+
* ai-governance sync --gov-server-url Override gov server URL
|
|
18
|
+
*
|
|
19
|
+
* Source of per-agent identities: .governance.json agents[] written by TS-002
|
|
20
|
+
* (register-fleet + writePerAgentIdentities). Each entry has:
|
|
21
|
+
* { localId, agentId, serviceToken, passportId, filePath }
|
|
22
|
+
* Plus top-level runtime block:
|
|
23
|
+
* { govServerUrl, orgId, agentId, serviceToken }
|
|
24
|
+
*
|
|
25
|
+
* Follow-up slices (historic seams — see feat/effective-governance-projection-2026-06-08):
|
|
26
|
+
* F1: serviceToken TTL reduced to 30d — DONE
|
|
27
|
+
* F2: JTI added to minted tokens, persisted on metadata — DONE
|
|
28
|
+
* F3: per-agent audit-logger.sh push rewiring (CXNI_AGENT_FILE resolver) — DONE
|
|
29
|
+
* F4: serviceToken moved off curl argv via tmpfile — DONE (this file + audit-logger.sh)
|
|
30
|
+
* F5: narrower AGENT_SELF role on server for tighter RBAC — DEFERRED (architectural)
|
|
31
|
+
*
|
|
32
|
+
* @connexum/ai-governance TS-010 sync verb
|
|
33
|
+
*/
|
|
34
|
+
import { fetchAgentGovernance } from './governance-projection-writer.js';
|
|
35
|
+
/** Governance section as materialised from the bundle into .governance.json. */
|
|
36
|
+
export interface SyncedGovernance {
|
|
37
|
+
/** Compliance pack IDs bound by the server. */
|
|
38
|
+
packs: string[];
|
|
39
|
+
/** Raw rule overrides from the bundle. */
|
|
40
|
+
ruleOverrides: unknown[];
|
|
41
|
+
/** Declared approval gates. */
|
|
42
|
+
declaredApprovalGates: unknown[];
|
|
43
|
+
/** Declared forbidden capabilities. */
|
|
44
|
+
declaredForbiddenCapabilities: string[];
|
|
45
|
+
/** ISO timestamp the bundle was issued. */
|
|
46
|
+
bundleIssuedAt: string;
|
|
47
|
+
/** hex content hash of the bundle payload (for audit). */
|
|
48
|
+
contentHash: string;
|
|
49
|
+
/** Ed25519 public key of the org (base64url) that signed this bundle. */
|
|
50
|
+
signerPublicKeyB64: string;
|
|
51
|
+
}
|
|
52
|
+
/** The shape stored under .governance.json `lastSync` for one agent. */
|
|
53
|
+
export interface AgentLastSync {
|
|
54
|
+
agentId: string;
|
|
55
|
+
syncedAt: string;
|
|
56
|
+
governance: SyncedGovernance;
|
|
57
|
+
}
|
|
58
|
+
/** Per-agent diff result. */
|
|
59
|
+
export interface AgentSyncDiff {
|
|
60
|
+
agentId: string;
|
|
61
|
+
filePath?: string;
|
|
62
|
+
/** null = failed to fetch bundle */
|
|
63
|
+
bundle: Record<string, unknown> | null;
|
|
64
|
+
/** null = bundle fetch/verify failed */
|
|
65
|
+
newGovernance: SyncedGovernance | null;
|
|
66
|
+
/**
|
|
67
|
+
* null = no previous sync snapshot (treat as no previous state).
|
|
68
|
+
* Diff shows "+ <key>: <value>" for all fields on first sync.
|
|
69
|
+
*/
|
|
70
|
+
previousGovernance: SyncedGovernance | null;
|
|
71
|
+
diffLines: string[];
|
|
72
|
+
/** Human-readable error when bundle could not be fetched or verified. */
|
|
73
|
+
error?: string;
|
|
74
|
+
}
|
|
75
|
+
/** Outcome of runSyncCommand. */
|
|
76
|
+
export interface SyncResult {
|
|
77
|
+
dryRun: boolean;
|
|
78
|
+
diffs: AgentSyncDiff[];
|
|
79
|
+
applied: number;
|
|
80
|
+
errors: number;
|
|
81
|
+
}
|
|
82
|
+
/** One delivered per-agent token (POST /api/v1/cli/agent-tokens response item). */
|
|
83
|
+
export interface FetchedAgentToken {
|
|
84
|
+
agentId: string;
|
|
85
|
+
serviceToken: string;
|
|
86
|
+
passportId: string | null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Fetch the org's Ed25519 public key from the governance server and write it
|
|
90
|
+
* into .governance.json under `orgPublicKey`.
|
|
91
|
+
*
|
|
92
|
+
* SECURITY: this must be called at `ai-governance init` time, BEFORE the first
|
|
93
|
+
* sync. The key is fetched over authenticated TLS from the same gov-server that
|
|
94
|
+
* issued the serviceToken. Subsequent syncs verify bundle signatures against
|
|
95
|
+
* this pinned key — they NEVER trust the key embedded in the bundle itself.
|
|
96
|
+
*
|
|
97
|
+
* The endpoint `GET /api/v1/orgs/:orgId/public-key` is unauthenticated (public
|
|
98
|
+
* keys are public, per Locked Invariant 4). We call it over the same
|
|
99
|
+
* govServerUrl + orgId that the exchange just returned, giving us a trusted
|
|
100
|
+
* source: the same TLS channel that authenticated the exchange.
|
|
101
|
+
*
|
|
102
|
+
* Returns the pinned key (base64url) on success, or null on failure.
|
|
103
|
+
* NEVER throws — on failure the caller logs the advisory and continues;
|
|
104
|
+
* the subsequent sync will fail closed (no pinned key = bundle rejected).
|
|
105
|
+
*
|
|
106
|
+
* DEPENDENCY NOTE (2026-06-08 prod incident): the org signing key is currently
|
|
107
|
+
* in-memory and regenerates on every gov-server deploy. With key-pinning, every
|
|
108
|
+
* post-deploy sync hits the rotation path until the key is made durable. That
|
|
109
|
+
* durability fix is the separate incident runbook — this function is still
|
|
110
|
+
* correct and required (rotation-as-explicit-event is the safe behavior).
|
|
111
|
+
*/
|
|
112
|
+
export declare function fetchAndPinOrgPublicKey(govServerUrl: string, orgId: string, configPath: string, timeoutSec?: number): string | null;
|
|
113
|
+
/**
|
|
114
|
+
* Verify a governance bundle's Ed25519 signature.
|
|
115
|
+
*
|
|
116
|
+
* The signed payload is the JCS-canonical serialization of:
|
|
117
|
+
* { agentId, bundleVersion, governance, issuedAt, orgId }
|
|
118
|
+
* The contentHash is SHA-256(canonical_bytes), hex-encoded.
|
|
119
|
+
* The signature is Ed25519(raw_sha256_digest_bytes) by the org signing key.
|
|
120
|
+
*
|
|
121
|
+
* KEY-TRUST SECURITY (Shield TS-010 remediation):
|
|
122
|
+
* `pinnedPublicKeyB64` is the PINNED org public key read from the LOCAL
|
|
123
|
+
* .governance.json (written at trusted init time). It is the SOLE key used
|
|
124
|
+
* for signature verification. bundle['publicKeyB64'] is NEVER used as the
|
|
125
|
+
* verification key — doing so would allow a MITM/rogue server to substitute
|
|
126
|
+
* their own key alongside a tampered bundle and have it pass verification.
|
|
127
|
+
*
|
|
128
|
+
* When `pinnedPublicKeyB64` is undefined (no pinned key on disk), the bundle
|
|
129
|
+
* is REJECTED (fail closed). Re-running `ai-governance init` pins the key.
|
|
130
|
+
*
|
|
131
|
+
* KEY ROTATION: if bundle['publicKeyB64'] differs from the pinned key, the
|
|
132
|
+
* bundle is REJECTED with a rotation message. Key rotation is an EXPLICIT
|
|
133
|
+
* operator event — re-run `ai-governance init` to accept the new key over an
|
|
134
|
+
* authenticated TLS channel and re-pin it.
|
|
135
|
+
*
|
|
136
|
+
* Returns the verified SyncedGovernance on success, or an error string.
|
|
137
|
+
*
|
|
138
|
+
* SAFE BY DEFAULT: any failure (missing fields, bad sig, tampered, missing/
|
|
139
|
+
* mismatched pinned key) returns an error string; the caller MUST NOT apply.
|
|
140
|
+
*/
|
|
141
|
+
export declare function verifyBundle(bundle: Record<string, unknown>, pinnedPublicKeyB64?: string): {
|
|
142
|
+
ok: true;
|
|
143
|
+
governance: SyncedGovernance;
|
|
144
|
+
} | {
|
|
145
|
+
ok: false;
|
|
146
|
+
error: string;
|
|
147
|
+
};
|
|
148
|
+
/** Produce a human-readable unified-style diff between two SyncedGovernance objects. */
|
|
149
|
+
export declare function computeGovernanceDiff(previous: SyncedGovernance | null, next: SyncedGovernance): string[];
|
|
150
|
+
/**
|
|
151
|
+
* Apply the synced governance to the local .governance.json.
|
|
152
|
+
*
|
|
153
|
+
* Rules:
|
|
154
|
+
* 1. NEVER silently clobber a hand-edited file. If the local `packs` field
|
|
155
|
+
* diverges from what `lastSync[agentId].governance.packs` recorded (i.e.
|
|
156
|
+
* the user hand-edited it since the last sync), surface that in the diff
|
|
157
|
+
* output. The --apply write still proceeds but the divergence is logged.
|
|
158
|
+
* 2. Write mode 0o600 — serviceTokens are present in the same file.
|
|
159
|
+
* 3. The `packs` top-level field is updated only when it is the first sync
|
|
160
|
+
* or when --apply is passed. It is the union of all agents' packs.
|
|
161
|
+
* 4. `lastSync[agentId]` is updated unconditionally on --apply.
|
|
162
|
+
*
|
|
163
|
+
* Returns a list of warnings (non-fatal) about hand-edit divergences.
|
|
164
|
+
*/
|
|
165
|
+
export declare function applyGovernanceToLocal(configPath: string, agentId: string, newGovernance: SyncedGovernance): {
|
|
166
|
+
warnings: string[];
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Parse the args array and run the sync command.
|
|
170
|
+
*
|
|
171
|
+
* @param args Slice of process.argv after 'sync'
|
|
172
|
+
* @param projectDir Absolute path to the project directory (default: cwd)
|
|
173
|
+
* @param options.fetchBundleFn Override the fetch function for testing
|
|
174
|
+
* @returns exit code: 0 = success (or dry-run with no errors), 1 = some errors
|
|
175
|
+
*/
|
|
176
|
+
export declare function runSyncCommand(args: string[], projectDir?: string, options?: {
|
|
177
|
+
/** Injectable fetch function for testing (default: fetchBundle via curl). */
|
|
178
|
+
fetchBundleFn?: (govServerUrl: string, agentId: string, serviceToken: string) => {
|
|
179
|
+
bundle: Record<string, unknown> | null;
|
|
180
|
+
error?: string;
|
|
181
|
+
};
|
|
182
|
+
/** Injectable per-agent token fetch for testing (default: fetchAgentTokens via curl). */
|
|
183
|
+
fetchAgentTokensFn?: (govServerUrl: string, installToken: string) => {
|
|
184
|
+
tokens: FetchedAgentToken[] | null;
|
|
185
|
+
installationId?: string | null;
|
|
186
|
+
error?: string;
|
|
187
|
+
};
|
|
188
|
+
/** Injectable governance-projection fetch for testing (write-back F2). */
|
|
189
|
+
fetchAgentGovernanceFn?: typeof fetchAgentGovernance;
|
|
190
|
+
/** Suppress all stdout/stderr for testing. */
|
|
191
|
+
silent?: boolean;
|
|
192
|
+
}): Promise<SyncResult>;
|
|
193
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/cli/sync.ts"],"names":[],"mappings":"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"}
|