@connexum/ai-governance 1.0.0-beta.21 → 1.0.0-beta.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,189 @@
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
+ /** Governance section as materialised from the bundle into .governance.json. */
35
+ export interface SyncedGovernance {
36
+ /** Compliance pack IDs bound by the server. */
37
+ packs: string[];
38
+ /** Raw rule overrides from the bundle. */
39
+ ruleOverrides: unknown[];
40
+ /** Declared approval gates. */
41
+ declaredApprovalGates: unknown[];
42
+ /** Declared forbidden capabilities. */
43
+ declaredForbiddenCapabilities: string[];
44
+ /** ISO timestamp the bundle was issued. */
45
+ bundleIssuedAt: string;
46
+ /** hex content hash of the bundle payload (for audit). */
47
+ contentHash: string;
48
+ /** Ed25519 public key of the org (base64url) that signed this bundle. */
49
+ signerPublicKeyB64: string;
50
+ }
51
+ /** The shape stored under .governance.json `lastSync` for one agent. */
52
+ export interface AgentLastSync {
53
+ agentId: string;
54
+ syncedAt: string;
55
+ governance: SyncedGovernance;
56
+ }
57
+ /** Per-agent diff result. */
58
+ export interface AgentSyncDiff {
59
+ agentId: string;
60
+ filePath?: string;
61
+ /** null = failed to fetch bundle */
62
+ bundle: Record<string, unknown> | null;
63
+ /** null = bundle fetch/verify failed */
64
+ newGovernance: SyncedGovernance | null;
65
+ /**
66
+ * null = no previous sync snapshot (treat as no previous state).
67
+ * Diff shows "+ <key>: <value>" for all fields on first sync.
68
+ */
69
+ previousGovernance: SyncedGovernance | null;
70
+ diffLines: string[];
71
+ /** Human-readable error when bundle could not be fetched or verified. */
72
+ error?: string;
73
+ }
74
+ /** Outcome of runSyncCommand. */
75
+ export interface SyncResult {
76
+ dryRun: boolean;
77
+ diffs: AgentSyncDiff[];
78
+ applied: number;
79
+ errors: number;
80
+ }
81
+ /** One delivered per-agent token (POST /api/v1/cli/agent-tokens response item). */
82
+ export interface FetchedAgentToken {
83
+ agentId: string;
84
+ serviceToken: string;
85
+ passportId: string | null;
86
+ }
87
+ /**
88
+ * Fetch the org's Ed25519 public key from the governance server and write it
89
+ * into .governance.json under `orgPublicKey`.
90
+ *
91
+ * SECURITY: this must be called at `ai-governance init` time, BEFORE the first
92
+ * sync. The key is fetched over authenticated TLS from the same gov-server that
93
+ * issued the serviceToken. Subsequent syncs verify bundle signatures against
94
+ * this pinned key — they NEVER trust the key embedded in the bundle itself.
95
+ *
96
+ * The endpoint `GET /api/v1/orgs/:orgId/public-key` is unauthenticated (public
97
+ * keys are public, per Locked Invariant 4). We call it over the same
98
+ * govServerUrl + orgId that the exchange just returned, giving us a trusted
99
+ * source: the same TLS channel that authenticated the exchange.
100
+ *
101
+ * Returns the pinned key (base64url) on success, or null on failure.
102
+ * NEVER throws — on failure the caller logs the advisory and continues;
103
+ * the subsequent sync will fail closed (no pinned key = bundle rejected).
104
+ *
105
+ * DEPENDENCY NOTE (2026-06-08 prod incident): the org signing key is currently
106
+ * in-memory and regenerates on every gov-server deploy. With key-pinning, every
107
+ * post-deploy sync hits the rotation path until the key is made durable. That
108
+ * durability fix is the separate incident runbook — this function is still
109
+ * correct and required (rotation-as-explicit-event is the safe behavior).
110
+ */
111
+ export declare function fetchAndPinOrgPublicKey(govServerUrl: string, orgId: string, configPath: string, timeoutSec?: number): string | null;
112
+ /**
113
+ * Verify a governance bundle's Ed25519 signature.
114
+ *
115
+ * The signed payload is the JCS-canonical serialization of:
116
+ * { agentId, bundleVersion, governance, issuedAt, orgId }
117
+ * The contentHash is SHA-256(canonical_bytes), hex-encoded.
118
+ * The signature is Ed25519(raw_sha256_digest_bytes) by the org signing key.
119
+ *
120
+ * KEY-TRUST SECURITY (Shield TS-010 remediation):
121
+ * `pinnedPublicKeyB64` is the PINNED org public key read from the LOCAL
122
+ * .governance.json (written at trusted init time). It is the SOLE key used
123
+ * for signature verification. bundle['publicKeyB64'] is NEVER used as the
124
+ * verification key — doing so would allow a MITM/rogue server to substitute
125
+ * their own key alongside a tampered bundle and have it pass verification.
126
+ *
127
+ * When `pinnedPublicKeyB64` is undefined (no pinned key on disk), the bundle
128
+ * is REJECTED (fail closed). Re-running `ai-governance init` pins the key.
129
+ *
130
+ * KEY ROTATION: if bundle['publicKeyB64'] differs from the pinned key, the
131
+ * bundle is REJECTED with a rotation message. Key rotation is an EXPLICIT
132
+ * operator event — re-run `ai-governance init` to accept the new key over an
133
+ * authenticated TLS channel and re-pin it.
134
+ *
135
+ * Returns the verified SyncedGovernance on success, or an error string.
136
+ *
137
+ * SAFE BY DEFAULT: any failure (missing fields, bad sig, tampered, missing/
138
+ * mismatched pinned key) returns an error string; the caller MUST NOT apply.
139
+ */
140
+ export declare function verifyBundle(bundle: Record<string, unknown>, pinnedPublicKeyB64?: string): {
141
+ ok: true;
142
+ governance: SyncedGovernance;
143
+ } | {
144
+ ok: false;
145
+ error: string;
146
+ };
147
+ /** Produce a human-readable unified-style diff between two SyncedGovernance objects. */
148
+ export declare function computeGovernanceDiff(previous: SyncedGovernance | null, next: SyncedGovernance): string[];
149
+ /**
150
+ * Apply the synced governance to the local .governance.json.
151
+ *
152
+ * Rules:
153
+ * 1. NEVER silently clobber a hand-edited file. If the local `packs` field
154
+ * diverges from what `lastSync[agentId].governance.packs` recorded (i.e.
155
+ * the user hand-edited it since the last sync), surface that in the diff
156
+ * output. The --apply write still proceeds but the divergence is logged.
157
+ * 2. Write mode 0o600 — serviceTokens are present in the same file.
158
+ * 3. The `packs` top-level field is updated only when it is the first sync
159
+ * or when --apply is passed. It is the union of all agents' packs.
160
+ * 4. `lastSync[agentId]` is updated unconditionally on --apply.
161
+ *
162
+ * Returns a list of warnings (non-fatal) about hand-edit divergences.
163
+ */
164
+ export declare function applyGovernanceToLocal(configPath: string, agentId: string, newGovernance: SyncedGovernance): {
165
+ warnings: string[];
166
+ };
167
+ /**
168
+ * Parse the args array and run the sync command.
169
+ *
170
+ * @param args Slice of process.argv after 'sync'
171
+ * @param projectDir Absolute path to the project directory (default: cwd)
172
+ * @param options.fetchBundleFn Override the fetch function for testing
173
+ * @returns exit code: 0 = success (or dry-run with no errors), 1 = some errors
174
+ */
175
+ export declare function runSyncCommand(args: string[], projectDir?: string, options?: {
176
+ /** Injectable fetch function for testing (default: fetchBundle via curl). */
177
+ fetchBundleFn?: (govServerUrl: string, agentId: string, serviceToken: string) => {
178
+ bundle: Record<string, unknown> | null;
179
+ error?: string;
180
+ };
181
+ /** Injectable per-agent token fetch for testing (default: fetchAgentTokens via curl). */
182
+ fetchAgentTokensFn?: (govServerUrl: string, installToken: string) => {
183
+ tokens: FetchedAgentToken[] | null;
184
+ error?: string;
185
+ };
186
+ /** Suppress all stdout/stderr for testing. */
187
+ silent?: boolean;
188
+ }): Promise<SyncResult>;
189
+ //# 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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"}