@cogenta/fleet 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/agent/assert.d.ts +9 -0
  2. package/dist/agent/assert.d.ts.map +1 -0
  3. package/dist/agent/assert.js +64 -0
  4. package/dist/agent/assert.js.map +1 -0
  5. package/dist/agent/build.d.ts +21 -0
  6. package/dist/agent/build.d.ts.map +1 -0
  7. package/dist/agent/build.js +43 -0
  8. package/dist/agent/build.js.map +1 -0
  9. package/dist/agent/commands.d.ts +39 -0
  10. package/dist/agent/commands.d.ts.map +1 -0
  11. package/dist/agent/commands.js +43 -0
  12. package/dist/agent/commands.js.map +1 -0
  13. package/dist/agent/rollback-handler.d.ts +28 -0
  14. package/dist/agent/rollback-handler.d.ts.map +1 -0
  15. package/dist/agent/rollback-handler.js +52 -0
  16. package/dist/agent/rollback-handler.js.map +1 -0
  17. package/dist/agent/sign.d.ts +18 -0
  18. package/dist/agent/sign.d.ts.map +1 -0
  19. package/dist/agent/sign.js +19 -0
  20. package/dist/agent/sign.js.map +1 -0
  21. package/dist/agent/types.d.ts +86 -0
  22. package/dist/agent/types.d.ts.map +1 -0
  23. package/dist/agent/types.js +2 -0
  24. package/dist/agent/types.js.map +1 -0
  25. package/dist/alerts/alerts.d.ts +42 -0
  26. package/dist/alerts/alerts.d.ts.map +1 -0
  27. package/dist/alerts/alerts.js +98 -0
  28. package/dist/alerts/alerts.js.map +1 -0
  29. package/dist/alerts/conditions.d.ts +25 -0
  30. package/dist/alerts/conditions.d.ts.map +1 -0
  31. package/dist/alerts/conditions.js +28 -0
  32. package/dist/alerts/conditions.js.map +1 -0
  33. package/dist/alerts/tables.d.ts +13 -0
  34. package/dist/alerts/tables.d.ts.map +1 -0
  35. package/dist/alerts/tables.js +27 -0
  36. package/dist/alerts/tables.js.map +1 -0
  37. package/dist/control/commands.d.ts +38 -0
  38. package/dist/control/commands.d.ts.map +1 -0
  39. package/dist/control/commands.js +51 -0
  40. package/dist/control/commands.js.map +1 -0
  41. package/dist/control/identity.d.ts +13 -0
  42. package/dist/control/identity.d.ts.map +1 -0
  43. package/dist/control/identity.js +5 -0
  44. package/dist/control/identity.js.map +1 -0
  45. package/dist/control/ingest.d.ts +46 -0
  46. package/dist/control/ingest.d.ts.map +1 -0
  47. package/dist/control/ingest.js +56 -0
  48. package/dist/control/ingest.js.map +1 -0
  49. package/dist/control/risk.d.ts +66 -0
  50. package/dist/control/risk.d.ts.map +1 -0
  51. package/dist/control/risk.js +130 -0
  52. package/dist/control/risk.js.map +1 -0
  53. package/dist/control/state.d.ts +19 -0
  54. package/dist/control/state.d.ts.map +1 -0
  55. package/dist/control/state.js +67 -0
  56. package/dist/control/state.js.map +1 -0
  57. package/dist/control/tables.d.ts +18 -0
  58. package/dist/control/tables.d.ts.map +1 -0
  59. package/dist/control/tables.js +58 -0
  60. package/dist/control/tables.js.map +1 -0
  61. package/dist/enrollment/store.d.ts +84 -0
  62. package/dist/enrollment/store.d.ts.map +1 -0
  63. package/dist/enrollment/store.js +81 -0
  64. package/dist/enrollment/store.js.map +1 -0
  65. package/dist/enrollment/tables.d.ts +20 -0
  66. package/dist/enrollment/tables.d.ts.map +1 -0
  67. package/dist/enrollment/tables.js +55 -0
  68. package/dist/enrollment/tables.js.map +1 -0
  69. package/dist/enrollment/tokens.d.ts +4 -0
  70. package/dist/enrollment/tokens.d.ts.map +1 -0
  71. package/dist/enrollment/tokens.js +20 -0
  72. package/dist/enrollment/tokens.js.map +1 -0
  73. package/dist/index.d.ts +42 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +25 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/inventory/drift.d.ts +73 -0
  78. package/dist/inventory/drift.d.ts.map +1 -0
  79. package/dist/inventory/drift.js +91 -0
  80. package/dist/inventory/drift.js.map +1 -0
  81. package/dist/reporting/report.d.ts +65 -0
  82. package/dist/reporting/report.d.ts.map +1 -0
  83. package/dist/reporting/report.js +137 -0
  84. package/dist/reporting/report.js.map +1 -0
  85. package/dist/reporting/schedule.d.ts +10 -0
  86. package/dist/reporting/schedule.d.ts.map +1 -0
  87. package/dist/reporting/schedule.js +34 -0
  88. package/dist/reporting/schedule.js.map +1 -0
  89. package/dist/reporting/tables.d.ts +11 -0
  90. package/dist/reporting/tables.d.ts.map +1 -0
  91. package/dist/reporting/tables.js +22 -0
  92. package/dist/reporting/tables.js.map +1 -0
  93. package/dist/rollout/campaign.d.ts +103 -0
  94. package/dist/rollout/campaign.d.ts.map +1 -0
  95. package/dist/rollout/campaign.js +258 -0
  96. package/dist/rollout/campaign.js.map +1 -0
  97. package/dist/rollout/rollback.d.ts +51 -0
  98. package/dist/rollout/rollback.d.ts.map +1 -0
  99. package/dist/rollout/rollback.js +60 -0
  100. package/dist/rollout/rollback.js.map +1 -0
  101. package/dist/rollout/tables.d.ts +19 -0
  102. package/dist/rollout/tables.d.ts.map +1 -0
  103. package/dist/rollout/tables.js +65 -0
  104. package/dist/rollout/tables.js.map +1 -0
  105. package/package.json +45 -0
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Throws if `payload` — already serialized to a plain object, exactly as it
3
+ * would be before signing and sending — contains any key from the forbidden
4
+ * list at any depth. Never silently drops the offending field: a payload
5
+ * that could only be made "safe" by quietly deleting data is not a payload
6
+ * this function considers safe to send at all.
7
+ */
8
+ export declare function assertNoForbiddenFields(payload: unknown): void;
9
+ //# sourceMappingURL=assert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../../src/agent/assert.ts"],"names":[],"mappings":"AAgDA;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAW9D"}
@@ -0,0 +1,64 @@
1
+ import { CogentaError } from '@cogenta/core';
2
+ /**
3
+ * "Vérification exhaustive de ce qui sort d'un site" — the security test
4
+ * `docs/lots/L8-flotte.md` names explicitly. `TelemetryPayload`'s type
5
+ * already makes the forbidden fields unrepresentable for any caller that
6
+ * goes through TypeScript's own checking — this function is the
7
+ * defense-in-depth half: a real, runtime inspection of the object about to
8
+ * be signed and sent, catching a forbidden key smuggled in past a loosely
9
+ * typed call site (an unchecked cast, an object spread, a JSON round-trip
10
+ * from an untyped source) that the type system alone would not stop.
11
+ */
12
+ const FORBIDDEN_KEYS = [
13
+ 'content',
14
+ 'media',
15
+ 'visitorpii',
16
+ 'visitordata',
17
+ 'apikey',
18
+ 'apikeys',
19
+ 'secret',
20
+ 'secrets',
21
+ 'agentmemory',
22
+ 'memory',
23
+ 'rawlogs',
24
+ 'logs',
25
+ 'log',
26
+ ];
27
+ /** Recurses into plain objects and arrays only — a class instance or a function is not what a JSON-serializable telemetry payload should ever contain in the first place. */
28
+ function walk(value, path, hits) {
29
+ if (value === null || typeof value !== 'object')
30
+ return;
31
+ if (Array.isArray(value)) {
32
+ value.forEach((entry, index) => {
33
+ walk(entry, `${path}[${index}]`, hits);
34
+ });
35
+ return;
36
+ }
37
+ for (const [key, entry] of Object.entries(value)) {
38
+ const keyPath = path === '' ? key : `${path}.${key}`;
39
+ if (FORBIDDEN_KEYS.includes(key.toLowerCase())) {
40
+ hits.push(keyPath);
41
+ }
42
+ walk(entry, keyPath, hits);
43
+ }
44
+ }
45
+ /**
46
+ * Throws if `payload` — already serialized to a plain object, exactly as it
47
+ * would be before signing and sending — contains any key from the forbidden
48
+ * list at any depth. Never silently drops the offending field: a payload
49
+ * that could only be made "safe" by quietly deleting data is not a payload
50
+ * this function considers safe to send at all.
51
+ */
52
+ export function assertNoForbiddenFields(payload) {
53
+ const hits = [];
54
+ walk(payload, '', hits);
55
+ if (hits.length > 0) {
56
+ throw new CogentaError({
57
+ code: 'FLEET_TELEMETRY_FORBIDDEN_FIELD',
58
+ message: `Telemetry payload contains forbidden field(s): ${hits.join(', ')}.`,
59
+ hint: 'Only the fields named in "## Ce qui remonte" (docs/lots/L8-flotte.md) may leave a site. Remove the offending field before sending.',
60
+ details: { fields: hits },
61
+ });
62
+ }
63
+ }
64
+ //# sourceMappingURL=assert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert.js","sourceRoot":"","sources":["../../src/agent/assert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAE5C;;;;;;;;;GASG;AACH,MAAM,cAAc,GAAG;IACrB,SAAS;IACT,OAAO;IACP,YAAY;IACZ,aAAa;IACb,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,SAAS;IACT,aAAa;IACb,QAAQ;IACR,SAAS;IACT,MAAM;IACN,KAAK;CACG,CAAA;AAEV,6KAA6K;AAC7K,SAAS,IAAI,CAAC,KAAc,EAAE,IAAY,EAAE,IAAc;IACxD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAM;IAEvD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC7B,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,IAAI,CAAC,CAAA;QACxC,CAAC,CAAC,CAAA;QACF,OAAM;IACR,CAAC;IAED,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,MAAM,OAAO,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,CAAA;QACpD,IAAI,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAqC,CAAC,EAAE,CAAC;YAClF,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACpB,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;IAC5B,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;IACvB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,iCAAiC;YACvC,OAAO,EAAE,kDAAkD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAC7E,IAAI,EAAE,oIAAoI;YAC1I,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;SAC1B,CAAC,CAAA;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { SbomEntry } from '@cogenta/agents-builtin';
2
+ import type { CredentialStore, UserStore } from '@cogenta/auth';
3
+ import type { AdminAccountsSummary } from './types.js';
4
+ /**
5
+ * A SHA-256 hex digest of the site's real SBOM (`buildSbom`,
6
+ * `@cogenta/agents-builtin`) — reuses the same deterministic, sorted-key
7
+ * canonicalization `@cogenta/plugins`' signing primitive already relies on
8
+ * (`canonicalizeContent`), so two sites with the identical dependency set
9
+ * always fingerprint identically regardless of `package.json` insertion
10
+ * order.
11
+ */
12
+ export declare function fingerprintSbom(entries: readonly SbomEntry[]): string;
13
+ /**
14
+ * Real counts, never identities — `docs/lots/L8-flotte.md`'s own words:
15
+ * "comptes administrateurs (nombre et état MFA, pas les identités)". Reads
16
+ * every admin-role user's real credential kinds (`CredentialStore.kinds`)
17
+ * and counts one as MFA-enabled the moment it has any kind beyond a bare
18
+ * password — `password` alone is not a second factor.
19
+ */
20
+ export declare function summarizeAdminAccounts(users: UserStore, credentials: CredentialStore, adminRole?: string): Promise<AdminAccountsSummary>;
21
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/agent/build.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE/D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEtD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAarE;AAKD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,eAAe,EAC5B,SAAS,SAAU,GAClB,OAAO,CAAC,oBAAoB,CAAC,CAW/B"}
@@ -0,0 +1,43 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { canonicalizeContent } from '@cogenta/plugins';
3
+ /**
4
+ * A SHA-256 hex digest of the site's real SBOM (`buildSbom`,
5
+ * `@cogenta/agents-builtin`) — reuses the same deterministic, sorted-key
6
+ * canonicalization `@cogenta/plugins`' signing primitive already relies on
7
+ * (`canonicalizeContent`), so two sites with the identical dependency set
8
+ * always fingerprint identically regardless of `package.json` insertion
9
+ * order.
10
+ */
11
+ export function fingerprintSbom(entries) {
12
+ // `canonicalizeContent` sorts each object's own keys, but an array's
13
+ // ELEMENT order is exactly what it was given — `buildSbom`'s own order
14
+ // follows `Object.entries()` on a plain `dependencies` record, which is
15
+ // insertion order, not a stable sort. Two sites with the identical
16
+ // dependency set built from a `package.json`/lockfile whose keys simply
17
+ // appear in a different order must fingerprint identically, so the
18
+ // entries are sorted here, by name then ecosystem, before canonicalizing.
19
+ const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name) || a.ecosystem.localeCompare(b.ecosystem));
20
+ const canonical = canonicalizeContent(sorted);
21
+ return createHash('sha256').update(canonical, 'utf8').digest('hex');
22
+ }
23
+ /** `CredentialStore.kinds()`'s real return values beyond a bare password — `@cogenta/auth`'s own `CREDENTIAL_KINDS` (`'password' | 'totp' | 'webauthn'`), not re-exported publicly, so named here as the literal strings its real API returns. */
24
+ const MFA_KINDS = ['totp', 'webauthn'];
25
+ /**
26
+ * Real counts, never identities — `docs/lots/L8-flotte.md`'s own words:
27
+ * "comptes administrateurs (nombre et état MFA, pas les identités)". Reads
28
+ * every admin-role user's real credential kinds (`CredentialStore.kinds`)
29
+ * and counts one as MFA-enabled the moment it has any kind beyond a bare
30
+ * password — `password` alone is not a second factor.
31
+ */
32
+ export async function summarizeAdminAccounts(users, credentials, adminRole = 'admin') {
33
+ const all = await users.list();
34
+ const admins = all.filter((user) => user.roles.includes(adminRole));
35
+ let mfaEnabledCount = 0;
36
+ for (const admin of admins) {
37
+ const kinds = await credentials.kinds(admin.id);
38
+ if (kinds.some((kind) => MFA_KINDS.includes(kind)))
39
+ mfaEnabledCount += 1;
40
+ }
41
+ return { count: admins.length, mfaEnabledCount };
42
+ }
43
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/agent/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAGtD;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,OAA6B;IAC3D,qEAAqE;IACrE,uEAAuE;IACvE,wEAAwE;IACxE,mEAAmE;IACnE,wEAAwE;IACxE,mEAAmE;IACnE,0EAA0E;IAC1E,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAC9B,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CACjF,CAAA;IACD,MAAM,SAAS,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAA;IAC7C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACrE,CAAC;AAED,kPAAkP;AAClP,MAAM,SAAS,GAAsB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;AAEzD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,KAAgB,EAChB,WAA4B,EAC5B,SAAS,GAAG,OAAO;IAEnB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;IAC9B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAA;IAEnE,IAAI,eAAe,GAAG,CAAC,CAAA;IACvB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC/C,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAAE,eAAe,IAAI,CAAC,CAAA;IAC1E,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,CAAA;AAClD,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { type FleetCommand, type FleetCommandAction, type SignedFleetCommand } from '../control/commands.js';
2
+ export type FleetCommandVerification = {
3
+ readonly ok: true;
4
+ readonly command: FleetCommand;
5
+ } | {
6
+ readonly ok: false;
7
+ readonly reason: 'action_not_whitelisted' | 'invalid_signature';
8
+ };
9
+ /**
10
+ * The real security boundary "## Appairage" describes: "exécutables
11
+ * uniquement dans une liste blanche d'actions." Whitelist checked first — a
12
+ * command naming an action outside `FLEET_COMMAND_ACTIONS` is refused before
13
+ * its signature is even inspected, since signature validity alone must never
14
+ * be sufficient to authorize an action this site doesn't recognise at all.
15
+ * Verifies against the CONTROL PLANE'S public key (`../control/identity.js`,
16
+ * learned once at pairing — `../enrollment/store.js`'s
17
+ * `PairingToken.controlPlanePublicKey`), the reverse direction from
18
+ * `../agent/sign.js`'s telemetry signing, which uses the site's own key.
19
+ */
20
+ export declare function verifyFleetCommand(signed: SignedFleetCommand, controlPlanePublicKeyBase64: string): FleetCommandVerification;
21
+ export type FleetCommandHandlers = {
22
+ readonly [K in FleetCommandAction]?: (payload: unknown) => Promise<void>;
23
+ };
24
+ export type FleetCommandDispatchResult = {
25
+ readonly ok: true;
26
+ readonly executed: true;
27
+ } | {
28
+ readonly ok: false;
29
+ readonly reason: 'action_not_whitelisted' | 'invalid_signature' | 'no_handler_registered';
30
+ };
31
+ /**
32
+ * Verifies, then — and only then — looks up a real handler for the
33
+ * command's action. A rejected command never reaches a handler: this is the
34
+ * one real call site a site deployment should ever use to act on a fleet
35
+ * command, so "verify, then dispatch" is not something a caller can
36
+ * accidentally skip by calling a handler directly.
37
+ */
38
+ export declare function dispatchFleetCommand(signed: SignedFleetCommand, controlPlanePublicKeyBase64: string, handlers: FleetCommandHandlers): Promise<FleetCommandDispatchResult>;
39
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/agent/commands.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,wBAAwB,CAAA;AAE/B,MAAM,MAAM,wBAAwB,GAChC;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,wBAAwB,GAAG,mBAAmB,CAAA;CAAE,CAAA;AAM3F;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,kBAAkB,EAC1B,2BAA2B,EAAE,MAAM,GAClC,wBAAwB,CAW1B;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,CAAC,IAAI,kBAAkB,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC;CACzE,CAAA;AAED,MAAM,MAAM,0BAA0B,GAClC;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAA;CAAE,GAC9C;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAA;IAClB,QAAQ,CAAC,MAAM,EAAE,wBAAwB,GAAG,mBAAmB,GAAG,uBAAuB,CAAA;CAC1F,CAAA;AAEL;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,kBAAkB,EAC1B,2BAA2B,EAAE,MAAM,EACnC,QAAQ,EAAE,oBAAoB,GAC7B,OAAO,CAAC,0BAA0B,CAAC,CASrC"}
@@ -0,0 +1,43 @@
1
+ import { verifyContentSignature } from '@cogenta/plugins';
2
+ import { FLEET_COMMAND_ACTIONS, } from '../control/commands.js';
3
+ function isWhitelistedAction(action) {
4
+ return FLEET_COMMAND_ACTIONS.includes(action);
5
+ }
6
+ /**
7
+ * The real security boundary "## Appairage" describes: "exécutables
8
+ * uniquement dans une liste blanche d'actions." Whitelist checked first — a
9
+ * command naming an action outside `FLEET_COMMAND_ACTIONS` is refused before
10
+ * its signature is even inspected, since signature validity alone must never
11
+ * be sufficient to authorize an action this site doesn't recognise at all.
12
+ * Verifies against the CONTROL PLANE'S public key (`../control/identity.js`,
13
+ * learned once at pairing — `../enrollment/store.js`'s
14
+ * `PairingToken.controlPlanePublicKey`), the reverse direction from
15
+ * `../agent/sign.js`'s telemetry signing, which uses the site's own key.
16
+ */
17
+ export function verifyFleetCommand(signed, controlPlanePublicKeyBase64) {
18
+ if (!isWhitelistedAction(signed.command.action)) {
19
+ return { ok: false, reason: 'action_not_whitelisted' };
20
+ }
21
+ const valid = verifyContentSignature(signed.command, signed.signatureBase64, controlPlanePublicKeyBase64);
22
+ if (!valid)
23
+ return { ok: false, reason: 'invalid_signature' };
24
+ return { ok: true, command: signed.command };
25
+ }
26
+ /**
27
+ * Verifies, then — and only then — looks up a real handler for the
28
+ * command's action. A rejected command never reaches a handler: this is the
29
+ * one real call site a site deployment should ever use to act on a fleet
30
+ * command, so "verify, then dispatch" is not something a caller can
31
+ * accidentally skip by calling a handler directly.
32
+ */
33
+ export async function dispatchFleetCommand(signed, controlPlanePublicKeyBase64, handlers) {
34
+ const verification = verifyFleetCommand(signed, controlPlanePublicKeyBase64);
35
+ if (!verification.ok)
36
+ return verification;
37
+ const handler = handlers[verification.command.action];
38
+ if (handler === undefined)
39
+ return { ok: false, reason: 'no_handler_registered' };
40
+ await handler(verification.command.payload);
41
+ return { ok: true, executed: true };
42
+ }
43
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/agent/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AACzD,OAAO,EACL,qBAAqB,GAItB,MAAM,wBAAwB,CAAA;AAM/B,SAAS,mBAAmB,CAAC,MAAc;IACzC,OAAQ,qBAA2C,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AACtE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAA0B,EAC1B,2BAAmC;IAEnC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAA;IACxD,CAAC;IACD,MAAM,KAAK,GAAG,sBAAsB,CAClC,MAAM,CAAC,OAAO,EACd,MAAM,CAAC,eAAe,EACtB,2BAA2B,CAC5B,CAAA;IACD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAA;IAC7D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAA;AAC9C,CAAC;AAaD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAA0B,EAC1B,2BAAmC,EACnC,QAA8B;IAE9B,MAAM,YAAY,GAAG,kBAAkB,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAA;IAC5E,IAAI,CAAC,YAAY,CAAC,EAAE;QAAE,OAAO,YAAY,CAAA;IAEzC,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IACrD,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAA;IAEhF,MAAM,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;AACrC,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { InventoryComponentKind } from '../inventory/drift.js';
2
+ /**
3
+ * The real shape of a `rollback` command's payload, per
4
+ * `../rollout/rollback.js`'s `triggerRollback` — validated here rather than
5
+ * trusted, since a command's `payload` field is `unknown` at the type level
6
+ * until a handler actually inspects it (`../agent/commands.js`'s
7
+ * `FleetCommandHandlers`).
8
+ */
9
+ export interface RollbackIntent {
10
+ readonly componentKind: InventoryComponentKind;
11
+ readonly componentName: string;
12
+ readonly targetVersion: string;
13
+ }
14
+ /**
15
+ * The real, honest scope of this task's site-side rollback handling:
16
+ * validate the intent a signed, whitelisted `rollback` command carries, then
17
+ * hand it to a caller-supplied callback — never a claim that this package
18
+ * itself reverts installed code. No mechanism exists anywhere in this
19
+ * codebase today to revert a site's installed plugin/theme/CMS code to a
20
+ * prior version (no package-manager integration, no `@cogenta/plugins`
21
+ * uninstall/downgrade path) — that is a real, documented gap, not something
22
+ * papered over with a handler that pretends to act. `recordIntent` is where
23
+ * a real deployment wires whatever it actually has (an operator alert, a
24
+ * queued manual task, or — once real reversion infrastructure exists — the
25
+ * genuine mechanism) without this package inventing one it cannot back.
26
+ */
27
+ export declare function createRollbackIntentHandler(recordIntent: (intent: RollbackIntent) => Promise<void>): (payload: unknown) => Promise<void>;
28
+ //# sourceMappingURL=rollback-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollback-handler.d.ts","sourceRoot":"","sources":["../../src/agent/rollback-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAEnE;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,aAAa,EAAE,sBAAsB,CAAA;IAC9C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAqCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CACzC,YAAY,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,GACtD,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAKrC"}
@@ -0,0 +1,52 @@
1
+ import { CogentaError } from '@cogenta/core';
2
+ const COMPONENT_KINDS = ['cms', 'plugin', 'theme'];
3
+ function parseRollbackPayload(payload) {
4
+ if (payload === null || typeof payload !== 'object') {
5
+ throw new CogentaError({
6
+ code: 'FLEET_ROLLBACK_NO_PRIOR_VERSION',
7
+ message: 'Rollback command payload is not an object.',
8
+ hint: 'This indicates a control-plane bug — triggerRollback always sends a real object payload.',
9
+ details: { payload },
10
+ });
11
+ }
12
+ const record = payload;
13
+ const { componentKind, componentName, targetVersion } = record;
14
+ if (typeof componentKind !== 'string' ||
15
+ !COMPONENT_KINDS.includes(componentKind) ||
16
+ typeof componentName !== 'string' ||
17
+ componentName.trim() === '' ||
18
+ typeof targetVersion !== 'string' ||
19
+ targetVersion.trim() === '') {
20
+ throw new CogentaError({
21
+ code: 'FLEET_ROLLBACK_NO_PRIOR_VERSION',
22
+ message: 'Rollback command payload is missing or has malformed fields.',
23
+ hint: 'Expected { componentKind: "cms"|"plugin"|"theme", componentName: string, targetVersion: string }.',
24
+ details: { payload },
25
+ });
26
+ }
27
+ return {
28
+ componentKind: componentKind,
29
+ componentName,
30
+ targetVersion,
31
+ };
32
+ }
33
+ /**
34
+ * The real, honest scope of this task's site-side rollback handling:
35
+ * validate the intent a signed, whitelisted `rollback` command carries, then
36
+ * hand it to a caller-supplied callback — never a claim that this package
37
+ * itself reverts installed code. No mechanism exists anywhere in this
38
+ * codebase today to revert a site's installed plugin/theme/CMS code to a
39
+ * prior version (no package-manager integration, no `@cogenta/plugins`
40
+ * uninstall/downgrade path) — that is a real, documented gap, not something
41
+ * papered over with a handler that pretends to act. `recordIntent` is where
42
+ * a real deployment wires whatever it actually has (an operator alert, a
43
+ * queued manual task, or — once real reversion infrastructure exists — the
44
+ * genuine mechanism) without this package inventing one it cannot back.
45
+ */
46
+ export function createRollbackIntentHandler(recordIntent) {
47
+ return async (payload) => {
48
+ const intent = parseRollbackPayload(payload);
49
+ await recordIntent(intent);
50
+ };
51
+ }
52
+ //# sourceMappingURL=rollback-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollback-handler.js","sourceRoot":"","sources":["../../src/agent/rollback-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAgB5C,MAAM,eAAe,GAAsC,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;AAErF,SAAS,oBAAoB,CAAC,OAAgB;IAC5C,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QACpD,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,iCAAiC;YACvC,OAAO,EAAE,4CAA4C;YACrD,IAAI,EAAE,0FAA0F;YAChG,OAAO,EAAE,EAAE,OAAO,EAAE;SACrB,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,OAAkC,CAAA;IACjD,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,MAAM,CAAA;IAC9D,IACE,OAAO,aAAa,KAAK,QAAQ;QACjC,CAAC,eAAe,CAAC,QAAQ,CAAC,aAAuC,CAAC;QAClE,OAAO,aAAa,KAAK,QAAQ;QACjC,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE;QAC3B,OAAO,aAAa,KAAK,QAAQ;QACjC,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,EAC3B,CAAC;QACD,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,iCAAiC;YACvC,OAAO,EAAE,8DAA8D;YACvE,IAAI,EAAE,mGAAmG;YACzG,OAAO,EAAE,EAAE,OAAO,EAAE;SACrB,CAAC,CAAA;IACJ,CAAC;IACD,OAAO;QACL,aAAa,EAAE,aAAuC;QACtD,aAAa;QACb,aAAa;KACd,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,2BAA2B,CACzC,YAAuD;IAEvD,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,MAAM,MAAM,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAA;QAC5C,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;IAC5B,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { TelemetryPayload } from './types.js';
2
+ export interface SignedTelemetry {
3
+ readonly payload: TelemetryPayload;
4
+ /** Base64 Ed25519 signature over the payload's canonical content — `@cogenta/plugins`' generalized signing primitive (task 9/12), the same one this project's control-plane pairing already uses (L8 task 1). */
5
+ readonly signatureBase64: string;
6
+ }
7
+ /**
8
+ * Signs a telemetry payload with the site's own paired private key
9
+ * (`@cogenta/fleet`'s enrollment, task 1) before it ever leaves the
10
+ * process. Runs the real, defense-in-depth forbidden-field check first —
11
+ * signing a payload does not make an accidentally-forbidden field safe to
12
+ * send, so this refuses to sign one at all rather than signing something
13
+ * that then has to be caught later, closer to the network boundary.
14
+ */
15
+ export declare function signTelemetryPayload(payload: TelemetryPayload, sitePrivateKeyBase64: string): SignedTelemetry;
16
+ /** Verifies a signed telemetry payload against the site's registered public key — the control plane's real half of this primitive (ingestion is task 3's job, not this one's). */
17
+ export declare function verifyTelemetrySignature(signed: SignedTelemetry, sitePublicKeyBase64: string): boolean;
18
+ //# sourceMappingURL=sign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../../src/agent/sign.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAA;IAClC,iNAAiN;IACjN,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CACjC;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,gBAAgB,EACzB,oBAAoB,EAAE,MAAM,GAC3B,eAAe,CAGjB;AAED,kLAAkL;AAClL,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,eAAe,EACvB,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAET"}
@@ -0,0 +1,19 @@
1
+ import { signContent, verifyContentSignature } from '@cogenta/plugins';
2
+ import { assertNoForbiddenFields } from './assert.js';
3
+ /**
4
+ * Signs a telemetry payload with the site's own paired private key
5
+ * (`@cogenta/fleet`'s enrollment, task 1) before it ever leaves the
6
+ * process. Runs the real, defense-in-depth forbidden-field check first —
7
+ * signing a payload does not make an accidentally-forbidden field safe to
8
+ * send, so this refuses to sign one at all rather than signing something
9
+ * that then has to be caught later, closer to the network boundary.
10
+ */
11
+ export function signTelemetryPayload(payload, sitePrivateKeyBase64) {
12
+ assertNoForbiddenFields(payload);
13
+ return { payload, signatureBase64: signContent(payload, sitePrivateKeyBase64) };
14
+ }
15
+ /** Verifies a signed telemetry payload against the site's registered public key — the control plane's real half of this primitive (ingestion is task 3's job, not this one's). */
16
+ export function verifyTelemetrySignature(signed, sitePublicKeyBase64) {
17
+ return verifyContentSignature(signed.payload, signed.signatureBase64, sitePublicKeyBase64);
18
+ }
19
+ //# sourceMappingURL=sign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign.js","sourceRoot":"","sources":["../../src/agent/sign.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AASrD;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAyB,EACzB,oBAA4B;IAE5B,uBAAuB,CAAC,OAAO,CAAC,CAAA;IAChC,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,CAAC,OAAO,EAAE,oBAAoB,CAAC,EAAE,CAAA;AACjF,CAAC;AAED,kLAAkL;AAClL,MAAM,UAAU,wBAAwB,CACtC,MAAuB,EACvB,mBAA2B;IAE3B,OAAO,sBAAsB,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAA;AAC5F,CAAC"}
@@ -0,0 +1,86 @@
1
+ import type { CruxMetrics, Urgency } from '@cogenta/agents-builtin';
2
+ /**
3
+ * What a site is allowed to send to the control plane — the closed
4
+ * enumeration of "## Ce qui remonte, et ce qui ne remonte pas"
5
+ * (`docs/lots/L8-flotte.md`), and nothing else. No field on this type, or
6
+ * any type it references, can hold content, media, visitor PII, an API key,
7
+ * agent memory, or a raw log line — that is a property of the SHAPE, not a
8
+ * filter applied after the fact: there is no `content`/`media`/`raw`/`logs`
9
+ * key anywhere on this type for a caller to even attempt to fill in.
10
+ *
11
+ * Some fields are wired to a real, existing data source in this codebase
12
+ * today (see each field's own doc comment); others are a real, honest SHAPE
13
+ * with no data source built yet — `docs/lots/L8-flotte.md`'s own "remonte"
14
+ * list names them regardless, and building the shape now means a later
15
+ * task's real data source slots in without a breaking payload change.
16
+ */
17
+ export interface TelemetryPayload {
18
+ readonly siteId: string;
19
+ readonly collectedAt: string;
20
+ /** Shape only — no real CMS/plugin/theme version inventory exists anywhere in this codebase yet (`cogenta doctor` reports only the Node runtime version). */
21
+ readonly installedVersions: InstalledVersions;
22
+ /**
23
+ * A SHA-256 hex digest of the site's real SBOM (`buildSbom`,
24
+ * `@cogenta/agents-builtin`'s security agent) — the fingerprint travels,
25
+ * never the SBOM's actual dependency list, which would itself be more
26
+ * detail than "## Ce qui remonte" asks for.
27
+ */
28
+ readonly sbomFingerprint: string;
29
+ /** Real — one entry per open CVE the security agent's real OSV/EPSS pipeline (`assessExploitability`) has assessed for this site's dependencies. */
30
+ readonly openCves: readonly OpenCveSummary[];
31
+ /** Real — the performance agent's real, aggregated CrUX field-data metrics (`CruxMetrics`, `@cogenta/agents-builtin`). `null` when no CrUX data exists for this site (not enough real-user traffic — not an error). */
32
+ readonly coreWebVitalsAggregate: CruxMetrics | null;
33
+ /** Shape only — no uptime/availability monitoring exists anywhere in this codebase yet. */
34
+ readonly availability: AvailabilitySummary;
35
+ /** Shape only — no real backup dump/restore mechanism exists anywhere in this codebase yet (confirmed absent, L9 task 9's own honest CLI scoping). */
36
+ readonly backups: BackupSummary;
37
+ /** Shape only, and nullable — no TLS certificate expiry check exists anywhere in this codebase yet. */
38
+ readonly certificateExpiry: CertificateExpirySummary | null;
39
+ /**
40
+ * Real — `count` from `@cogenta/auth`'s real `UserStore.list()` filtered
41
+ * to admin-role users; `mfaEnabledCount` from `CredentialStore.kinds()`
42
+ * reporting a real second factor (`totp` or `webauthn`, never `password`
43
+ * alone) for each. Never an identity — no email, no user id, only counts.
44
+ */
45
+ readonly adminAccounts: AdminAccountsSummary;
46
+ /** Shape only — no structured error-aggregation sink exists anywhere in this codebase yet (the logger is real and structured, but nothing aggregates its output for fleet reporting). */
47
+ readonly aggregatedErrors: AggregatedErrorsSummary;
48
+ }
49
+ export interface InstalledVersions {
50
+ readonly cms: string | null;
51
+ readonly plugins: readonly {
52
+ readonly name: string;
53
+ readonly version: string;
54
+ }[];
55
+ readonly themes: readonly {
56
+ readonly name: string;
57
+ readonly version: string;
58
+ }[];
59
+ }
60
+ export interface OpenCveSummary {
61
+ readonly id: string;
62
+ readonly urgency: Urgency;
63
+ readonly status: 'open' | 'patched' | 'ignored';
64
+ }
65
+ export interface AvailabilitySummary {
66
+ /** 0..1 over the reporting window. `null` when no measurement exists yet. */
67
+ readonly uptimeRatio: number | null;
68
+ }
69
+ export interface BackupSummary {
70
+ readonly lastBackupAt: string | null;
71
+ readonly lastResult: 'success' | 'failure' | 'unknown';
72
+ }
73
+ export interface CertificateExpirySummary {
74
+ readonly domain: string;
75
+ readonly expiresAt: string;
76
+ }
77
+ export interface AdminAccountsSummary {
78
+ readonly count: number;
79
+ readonly mfaEnabledCount: number;
80
+ }
81
+ export interface AggregatedErrorsSummary {
82
+ readonly count: number;
83
+ readonly windowStart: string;
84
+ readonly windowEnd: string;
85
+ }
86
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agent/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAEnE;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAE5B,6JAA6J;IAC7J,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAA;IAE7C;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAEhC,oJAAoJ;IACpJ,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAA;IAE5C,uNAAuN;IACvN,QAAQ,CAAC,sBAAsB,EAAE,WAAW,GAAG,IAAI,CAAA;IAEnD,2FAA2F;IAC3F,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAA;IAE1C,sJAAsJ;IACtJ,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;IAE/B,uGAAuG;IACvG,QAAQ,CAAC,iBAAiB,EAAE,wBAAwB,GAAG,IAAI,CAAA;IAE3D;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,oBAAoB,CAAA;IAE5C,yLAAyL;IACzL,QAAQ,CAAC,gBAAgB,EAAE,uBAAuB,CAAA;CACnD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAChF,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAChF;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;CAChD;AAED,MAAM,WAAW,mBAAmB;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,QAAQ,CAAC,UAAU,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;CACvD;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CACjC;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agent/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,42 @@
1
+ import { type AlertChannelMessage } from '@cogenta/channels';
2
+ import type { SiteRisk } from '../control/risk.js';
3
+ import type { TelemetrySnapshot } from '../control/state.js';
4
+ import type { SiteRegistration } from '../enrollment/store.js';
5
+ import type { CampaignRecord } from '../rollout/campaign.js';
6
+ import type { AlertConditionStore } from './conditions.js';
7
+ /**
8
+ * A site crossing into `critical` risk (task 5's real `computeSiteRisk`) —
9
+ * "un site avec une CVE critique passe devant" on the dashboard is the same
10
+ * real signal an operator needs pushed to them immediately, not only seen
11
+ * next time they open it.
12
+ */
13
+ export declare function detectCriticalRiskAlert(risk: SiteRisk): AlertChannelMessage | null;
14
+ /**
15
+ * A rollout campaign transitioning to `halted` (task 7's real
16
+ * `CampaignRecord`) — "un échec arrête toute la campagne" is exactly the
17
+ * kind of event an operator needs to know about the moment it happens, not
18
+ * discover by noticing an update never finished.
19
+ */
20
+ export declare function detectCampaignHaltedAlert(campaign: CampaignRecord, siteNameById: ReadonlyMap<string, string>): AlertChannelMessage | null;
21
+ export declare function detectSiteSilentAlert(site: SiteRegistration, latestSnapshot: TelemetrySnapshot | null, now?: () => number): AlertChannelMessage | null;
22
+ /**
23
+ * Wraps any detection function with the real de-duplication layer
24
+ * (`./conditions.js`): the message is only returned — and thus only ever
25
+ * sent — on the check where the condition genuinely transitions from
26
+ * inactive to active. Every subsequent check while it remains active
27
+ * returns `null`, even though the underlying condition still holds.
28
+ */
29
+ export declare function raiseIfNew(conditionStore: AlertConditionStore, siteId: string, conditionType: 'critical-risk' | 'campaign-halted' | 'site-silent', message: AlertChannelMessage | null, now?: () => number): Promise<AlertChannelMessage | null>;
30
+ /**
31
+ * The real dispatch integration point — a structural interface, never a
32
+ * hard dependency on any specific `@cogenta/channels` adapter instance
33
+ * (matches `../reporting/report.js`'s own boundary: depend on the real
34
+ * message-CONSTRUCTOR types, never on a live adapter). No live control-plane
35
+ * deployment exists anywhere in this lot; a real deployment plugs a real
36
+ * `ChannelAdapter.send` (or a registry's dispatch function) in here.
37
+ */
38
+ export interface AlertSender {
39
+ send(message: AlertChannelMessage): Promise<void>;
40
+ }
41
+ export declare function dispatchAlert(sender: AlertSender, message: AlertChannelMessage): Promise<void>;
42
+ //# sourceMappingURL=alerts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/alerts/alerts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAc,MAAM,mBAAmB,CAAA;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAa1D;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ,GAAG,mBAAmB,GAAG,IAAI,CAUlF;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,cAAc,EACxB,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GACxC,mBAAmB,GAAG,IAAI,CAY5B;AAmBD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,gBAAgB,EACtB,cAAc,EAAE,iBAAiB,GAAG,IAAI,EACxC,GAAG,GAAE,MAAM,MAAiB,GAC3B,mBAAmB,GAAG,IAAI,CAgB5B;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,cAAc,EAAE,mBAAmB,EACnC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,eAAe,GAAG,iBAAiB,GAAG,aAAa,EAClE,OAAO,EAAE,mBAAmB,GAAG,IAAI,EACnC,GAAG,CAAC,EAAE,MAAM,MAAM,GACjB,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAOrC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClD;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAEf"}