@groundnuty/macf-channel-server 0.2.0-rc.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 (49) hide show
  1. package/dist/collision.d.ts +25 -0
  2. package/dist/collision.d.ts.map +1 -0
  3. package/dist/collision.js +94 -0
  4. package/dist/collision.js.map +1 -0
  5. package/dist/health.d.ts +3 -0
  6. package/dist/health.d.ts.map +1 -0
  7. package/dist/health.js +33 -0
  8. package/dist/health.js.map +1 -0
  9. package/dist/https.d.ts +25 -0
  10. package/dist/https.d.ts.map +1 -0
  11. package/dist/https.js +361 -0
  12. package/dist/https.js.map +1 -0
  13. package/dist/index.d.ts +29 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +25 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/mcp.d.ts +6 -0
  18. package/dist/mcp.d.ts.map +1 -0
  19. package/dist/mcp.js +43 -0
  20. package/dist/mcp.js.map +1 -0
  21. package/dist/notify-formatter.d.ts +19 -0
  22. package/dist/notify-formatter.d.ts.map +1 -0
  23. package/dist/notify-formatter.js +43 -0
  24. package/dist/notify-formatter.js.map +1 -0
  25. package/dist/otel.d.ts +59 -0
  26. package/dist/otel.d.ts.map +1 -0
  27. package/dist/otel.js +122 -0
  28. package/dist/otel.js.map +1 -0
  29. package/dist/server.d.ts +2 -0
  30. package/dist/server.d.ts.map +1 -0
  31. package/dist/server.js +256 -0
  32. package/dist/server.js.map +1 -0
  33. package/dist/shutdown.d.ts +15 -0
  34. package/dist/shutdown.d.ts.map +1 -0
  35. package/dist/shutdown.js +53 -0
  36. package/dist/shutdown.js.map +1 -0
  37. package/dist/startup-issues.d.ts +24 -0
  38. package/dist/startup-issues.d.ts.map +1 -0
  39. package/dist/startup-issues.js +75 -0
  40. package/dist/startup-issues.js.map +1 -0
  41. package/dist/tmux-wake.d.ts +106 -0
  42. package/dist/tmux-wake.d.ts.map +1 -0
  43. package/dist/tmux-wake.js +196 -0
  44. package/dist/tmux-wake.js.map +1 -0
  45. package/dist/tracing.d.ts +38 -0
  46. package/dist/tracing.d.ts.map +1 -0
  47. package/dist/tracing.js +68 -0
  48. package/dist/tracing.js.map +1 -0
  49. package/package.json +46 -0
@@ -0,0 +1,25 @@
1
+ import type { AgentInfo, Registry } from '@groundnuty/macf-core';
2
+ import type { Logger } from '@groundnuty/macf-core';
3
+ import { MacfError } from '@groundnuty/macf-core';
4
+ export declare class CollisionError extends MacfError {
5
+ constructor(name: string, host: string, port: number);
6
+ }
7
+ export type CollisionResult = {
8
+ readonly action: 'register';
9
+ } | {
10
+ readonly action: 'takeover';
11
+ readonly previous: AgentInfo;
12
+ } | {
13
+ readonly action: 'abort';
14
+ readonly existing: AgentInfo;
15
+ };
16
+ /**
17
+ * Check if an agent is already registered and alive.
18
+ * Returns the action to take: register (fresh), takeover (dead), or abort (alive).
19
+ */
20
+ export declare function checkCollision(name: string, registry: Registry, certPaths: {
21
+ readonly caCertPath: string;
22
+ readonly agentCertPath: string;
23
+ readonly agentKeyPath: string;
24
+ }, logger: Logger): Promise<CollisionResult>;
25
+ //# sourceMappingURL=collision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collision.d.ts","sourceRoot":"","sources":["../src/collision.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,qBAAa,cAAe,SAAQ,SAAS;gBAC/B,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAQrD;AAgED,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAC/B;IAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,GAC7D;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,CAAC;AAE/D;;;GAGG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE;IACT,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,EACD,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,CAAC,CAwC1B"}
@@ -0,0 +1,94 @@
1
+ import { request } from 'node:https';
2
+ import { readFileSync } from 'node:fs';
3
+ import { MacfError } from '@groundnuty/macf-core';
4
+ export class CollisionError extends MacfError {
5
+ constructor(name, host, port) {
6
+ super('AGENT_COLLISION', `Agent '${name}' is already running at ${host}:${port}. ` +
7
+ 'Stop the existing agent before starting another.');
8
+ this.name = 'CollisionError';
9
+ }
10
+ }
11
+ const HEALTH_PING_TIMEOUT_MS = 5000;
12
+ /**
13
+ * Ping an agent's /health endpoint via mTLS.
14
+ * Returns true if the agent responds, false if unreachable.
15
+ */
16
+ function pingHealth(host, port, caCertPath, agentCertPath, agentKeyPath, timeoutMs = HEALTH_PING_TIMEOUT_MS) {
17
+ // readFileSync on missing/unreadable cert files throws ENOENT/EACCES
18
+ // as raw Node errors with no descriptive context. During a cert-
19
+ // rotation race at startup, the agent cert/key files may be
20
+ // momentarily absent — without this guard the error propagates as
21
+ // an unhandled rejection up through main() and crashes startup.
22
+ // Treat any read error the same way we treat network errors: the
23
+ // peer is effectively unreachable for the purpose of the collision
24
+ // check. Ultrareview finding H3.
25
+ let ca;
26
+ let cert;
27
+ let key;
28
+ try {
29
+ ca = readFileSync(caCertPath);
30
+ cert = readFileSync(agentCertPath);
31
+ key = readFileSync(agentKeyPath);
32
+ }
33
+ catch {
34
+ return Promise.resolve(false);
35
+ }
36
+ return new Promise((resolve) => {
37
+ const req = request({
38
+ hostname: host,
39
+ port,
40
+ method: 'GET',
41
+ path: '/health',
42
+ ca,
43
+ cert,
44
+ key,
45
+ rejectUnauthorized: true,
46
+ timeout: timeoutMs,
47
+ }, (res) => {
48
+ // Any 2xx response means the agent is alive
49
+ resolve(res.statusCode !== undefined && res.statusCode >= 200 && res.statusCode < 300);
50
+ res.resume(); // drain response
51
+ });
52
+ req.on('error', () => resolve(false));
53
+ req.on('timeout', () => {
54
+ req.destroy();
55
+ resolve(false);
56
+ });
57
+ req.end();
58
+ });
59
+ }
60
+ /**
61
+ * Check if an agent is already registered and alive.
62
+ * Returns the action to take: register (fresh), takeover (dead), or abort (alive).
63
+ */
64
+ export async function checkCollision(name, registry, certPaths, logger) {
65
+ const existing = await registry.get(name);
66
+ if (existing === null) {
67
+ logger.info('collision_check', { result: 'fresh', agent: name });
68
+ return { action: 'register' };
69
+ }
70
+ logger.info('collision_check', {
71
+ result: 'variable_exists',
72
+ agent: name,
73
+ host: existing.host,
74
+ port: existing.port,
75
+ instance_id: existing.instance_id,
76
+ });
77
+ const alive = await pingHealth(existing.host, existing.port, certPaths.caCertPath, certPaths.agentCertPath, certPaths.agentKeyPath);
78
+ if (alive) {
79
+ logger.warn('collision_check', {
80
+ result: 'abort',
81
+ agent: name,
82
+ host: existing.host,
83
+ port: existing.port,
84
+ });
85
+ return { action: 'abort', existing };
86
+ }
87
+ logger.info('collision_check', {
88
+ result: 'takeover',
89
+ agent: name,
90
+ previous_instance: existing.instance_id,
91
+ });
92
+ return { action: 'takeover', previous: existing };
93
+ }
94
+ //# sourceMappingURL=collision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collision.js","sourceRoot":"","sources":["../src/collision.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,MAAM,OAAO,cAAe,SAAQ,SAAS;IAC3C,YAAY,IAAY,EAAE,IAAY,EAAE,IAAY;QAClD,KAAK,CACH,iBAAiB,EACjB,UAAU,IAAI,2BAA2B,IAAI,IAAI,IAAI,IAAI;YACzD,kDAAkD,CACnD,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAEpC;;;GAGG;AACH,SAAS,UAAU,CACjB,IAAY,EACZ,IAAY,EACZ,UAAkB,EAClB,aAAqB,EACrB,YAAoB,EACpB,YAAoB,sBAAsB;IAE1C,qEAAqE;IACrE,iEAAiE;IACjE,4DAA4D;IAC5D,kEAAkE;IAClE,gEAAgE;IAChE,iEAAiE;IACjE,mEAAmE;IACnE,iCAAiC;IACjC,IAAI,EAAU,CAAC;IACf,IAAI,IAAY,CAAC;IACjB,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QAC9B,IAAI,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;QACnC,GAAG,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,GAAG,GAAG,OAAO,CACjB;YACE,QAAQ,EAAE,IAAI;YACd,IAAI;YACJ,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,SAAS;YACf,EAAE;YACF,IAAI;YACJ,GAAG;YACH,kBAAkB,EAAE,IAAI;YACxB,OAAO,EAAE,SAAS;SACnB,EACD,CAAC,GAAG,EAAE,EAAE;YACN,4CAA4C;YAC5C,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,SAAS,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC;YACvF,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,iBAAiB;QACjC,CAAC,CACF,CAAC;QAEF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QACtC,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YACrB,GAAG,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC;AAOD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAY,EACZ,QAAkB,EAClB,SAIC,EACD,MAAc;IAEd,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE1C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAChC,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;QAC7B,MAAM,EAAE,iBAAiB;QACzB,KAAK,EAAE,IAAI;QACX,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,WAAW,EAAE,QAAQ,CAAC,WAAW;KAClC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,MAAM,UAAU,CAC5B,QAAQ,CAAC,IAAI,EACb,QAAQ,CAAC,IAAI,EACb,SAAS,CAAC,UAAU,EACpB,SAAS,CAAC,aAAa,EACvB,SAAS,CAAC,YAAY,CACvB,CAAC;IAEF,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;YAC7B,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,IAAI;YACX,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;SACpB,CAAC,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;QAC7B,MAAM,EAAE,UAAU;QAClB,KAAK,EAAE,IAAI;QACX,iBAAiB,EAAE,QAAQ,CAAC,WAAW;KACxC,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AACpD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { HealthState } from '@groundnuty/macf-core';
2
+ export declare function createHealthState(agentName: string, agentType: string): HealthState;
3
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAkB,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAQzE,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CA4BnF"}
package/dist/health.js ADDED
@@ -0,0 +1,33 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ function readVersion() {
4
+ const pkgPath = resolve(import.meta.dirname, '..', 'package.json');
5
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
6
+ return pkg.version;
7
+ }
8
+ export function createHealthState(agentName, agentType) {
9
+ const version = readVersion();
10
+ const startTime = Date.now();
11
+ let currentIssue = null;
12
+ let lastNotification = null;
13
+ return {
14
+ getHealth() {
15
+ return {
16
+ agent: agentName,
17
+ status: 'online',
18
+ type: agentType,
19
+ uptime_seconds: Math.floor((Date.now() - startTime) / 1000),
20
+ current_issue: currentIssue,
21
+ version,
22
+ last_notification: lastNotification,
23
+ };
24
+ },
25
+ setCurrentIssue(issueNumber) {
26
+ currentIssue = issueNumber;
27
+ },
28
+ recordNotification() {
29
+ lastNotification = new Date().toISOString();
30
+ },
31
+ };
32
+ }
33
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,SAAS,WAAW;IAClB,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;IACnE,MAAM,GAAG,GAAwB,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5E,OAAO,GAAG,CAAC,OAAO,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAAiB,EAAE,SAAiB;IACpE,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,IAAI,gBAAgB,GAAkB,IAAI,CAAC;IAE3C,OAAO;QACL,SAAS;YACP,OAAO;gBACL,KAAK,EAAE,SAAS;gBAChB,MAAM,EAAE,QAAQ;gBAChB,IAAI,EAAE,SAAS;gBACf,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;gBAC3D,aAAa,EAAE,YAAY;gBAC3B,OAAO;gBACP,iBAAiB,EAAE,gBAAgB;aACpC,CAAC;QACJ,CAAC;QAED,eAAe,CAAC,WAA0B;YACxC,YAAY,GAAG,WAAW,CAAC;QAC7B,CAAC;QAED,kBAAkB;YAChB,gBAAgB,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC9C,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { NotifyPayload, SignRequest, HealthResponse, HttpsServer, Logger } from '@groundnuty/macf-core';
2
+ export declare const PORT_RANGE_START = 8800;
3
+ export declare const PORT_RANGE_SIZE = 1000;
4
+ export declare const CLIENT_AUTH_EKU_OID = "1.3.6.1.5.5.7.3.2";
5
+ /**
6
+ * Check whether the presented peer cert carries the clientAuth EKU.
7
+ * Node's `tls.TLSSocket.getPeerCertificate()` exposes EKU as
8
+ * `ext_key_usage` — an array of OID strings. If the field is absent
9
+ * or empty, the cert carries no EKU; if present, we require the
10
+ * clientAuth OID specifically. Exported for tests.
11
+ */
12
+ export declare function peerCertHasClientAuthEKU(peerCert: {
13
+ readonly ext_key_usage?: readonly string[];
14
+ }): boolean;
15
+ export declare function randomPort(): number;
16
+ export declare function createHttpsServer(config: {
17
+ readonly caCertPath: string;
18
+ readonly agentCertPath: string;
19
+ readonly agentKeyPath: string;
20
+ readonly onNotify: (payload: NotifyPayload) => Promise<void>;
21
+ readonly onHealth: () => HealthResponse;
22
+ readonly onSign?: (request: SignRequest) => Promise<Record<string, unknown>>;
23
+ readonly logger: Logger;
24
+ }): HttpsServer;
25
+ //# sourceMappingURL=https.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"https.d.ts","sourceRoot":"","sources":["../src/https.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAK7G,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,eAAe,OAAO,CAAC;AAQpC,eAAO,MAAM,mBAAmB,sBAAsB,CAAC;AAEvD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE;IACjD,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5C,GAAG,OAAO,CAGV;AAUD,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAyDD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,cAAc,CAAC;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GAAG,WAAW,CAiUd"}
package/dist/https.js ADDED
@@ -0,0 +1,361 @@
1
+ import { createServer } from 'node:https';
2
+ import { readFileSync } from 'node:fs';
3
+ import { randomInt } from 'node:crypto';
4
+ import { context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api';
5
+ import { NotifyPayloadSchema, SignRequestSchema } from '@groundnuty/macf-core';
6
+ import { PortExhaustedError, PortUnavailableError, HttpsServerError, HttpError } from '@groundnuty/macf-core';
7
+ import { getTracer, SpanNames, Attr, GenAiAttr, operationNameForNotifyType } from './tracing.js';
8
+ const MAX_BODY_BYTES = 64 * 1024; // 64KB
9
+ export const PORT_RANGE_START = 8800;
10
+ export const PORT_RANGE_SIZE = 1000;
11
+ const MAX_PORT_ATTEMPTS = 10;
12
+ // clientAuth EKU OID — RFC 5280 §4.2.1.12. Peer certs emit this via
13
+ // generateAgentCert + signCSR (#125); the routing-action client cert
14
+ // emits it too (#119). Enforced at the server as the final step of
15
+ // DR-004 v2 EKU rollout (#121). Non-EKU certs are rejected at
16
+ // /notify + /health + /sign uniformly.
17
+ export const CLIENT_AUTH_EKU_OID = '1.3.6.1.5.5.7.3.2';
18
+ /**
19
+ * Check whether the presented peer cert carries the clientAuth EKU.
20
+ * Node's `tls.TLSSocket.getPeerCertificate()` exposes EKU as
21
+ * `ext_key_usage` — an array of OID strings. If the field is absent
22
+ * or empty, the cert carries no EKU; if present, we require the
23
+ * clientAuth OID specifically. Exported for tests.
24
+ */
25
+ export function peerCertHasClientAuthEKU(peerCert) {
26
+ return Array.isArray(peerCert.ext_key_usage)
27
+ && peerCert.ext_key_usage.includes(CLIENT_AUTH_EKU_OID);
28
+ }
29
+ // Exported for tests (#109 H1). Uses crypto.randomInt (CSPRNG)
30
+ // rather than a weak PRNG — port numbers aren't secrets, but the
31
+ // canonical defensive pattern for random in security-adjacent code
32
+ // paths is the CSPRNG.
33
+ export function randomPort() {
34
+ return PORT_RANGE_START + randomInt(PORT_RANGE_SIZE);
35
+ }
36
+ function sendJson(res, status, body) {
37
+ const json = JSON.stringify(body);
38
+ res.writeHead(status, {
39
+ 'Content-Type': 'application/json',
40
+ 'Content-Length': Buffer.byteLength(json),
41
+ });
42
+ res.end(json);
43
+ }
44
+ function readBody(req) {
45
+ return new Promise((resolve, reject) => {
46
+ const chunks = [];
47
+ let size = 0;
48
+ let settled = false;
49
+ req.on('data', (chunk) => {
50
+ size += chunk.length;
51
+ if (size > MAX_BODY_BYTES && !settled) {
52
+ settled = true;
53
+ // Destroy the underlying socket (not just req) so the half-open
54
+ // write-side (res) is also torn down. `req.destroy()` alone
55
+ // leaves res attached to a destroyed request, which can retain
56
+ // GC references under high-throughput abuse — see ultrareview
57
+ // finding H2.
58
+ req.socket.destroy();
59
+ reject(new HttpsServerError('Body too large'));
60
+ return;
61
+ }
62
+ if (!settled) {
63
+ chunks.push(chunk);
64
+ }
65
+ });
66
+ req.on('end', () => {
67
+ if (!settled) {
68
+ settled = true;
69
+ resolve(Buffer.concat(chunks).toString('utf-8'));
70
+ }
71
+ });
72
+ req.on('error', (err) => {
73
+ if (!settled) {
74
+ settled = true;
75
+ reject(err);
76
+ }
77
+ });
78
+ });
79
+ }
80
+ export function createHttpsServer(config) {
81
+ const { onNotify, onHealth, onSign, logger } = config;
82
+ const tlsOptions = {
83
+ key: readFileSync(config.agentKeyPath),
84
+ cert: readFileSync(config.agentCertPath),
85
+ ca: readFileSync(config.caCertPath),
86
+ requestCert: true,
87
+ rejectUnauthorized: true,
88
+ };
89
+ let server;
90
+ async function handleRequest(req, res) {
91
+ // Defense-in-depth: reject at HTTP level even if TLS handshake passed.
92
+ // Protects against misconfigured rejectUnauthorized during debugging.
93
+ const tlsSocket = req.socket;
94
+ if (!tlsSocket.authorized) {
95
+ sendJson(res, 401, { error: 'Unauthorized' });
96
+ return;
97
+ }
98
+ // Step 3 of the DR-004 v2 EKU rollout (#121): require the peer
99
+ // cert to carry the clientAuth EKU. Peer certs emit it via
100
+ // generateAgentCert + signCSR (#125); routing-action client cert
101
+ // emits it via generateClientCert (#119). A CA-signed cert
102
+ // WITHOUT the EKU — e.g. an old peer cert pre-#125 that hasn't
103
+ // been rotated — is rejected uniformly at /health, /notify,
104
+ // /sign. Operators who miss a rotation see 403 with a clear
105
+ // message pointing at `macf certs rotate`.
106
+ const peerCert = tlsSocket.getPeerCertificate();
107
+ if (!peerCertHasClientAuthEKU(peerCert)) {
108
+ const cn = peerCert.subject?.CN ?? 'unknown';
109
+ logger.warn('client_cert_missing_eku', {
110
+ from_cn: cn,
111
+ url: req.url ?? '',
112
+ });
113
+ sendJson(res, 403, {
114
+ error: 'Forbidden: client certificate missing clientAuth Extended Key Usage. Run `macf certs rotate` to pick up an EKU-enabled cert.',
115
+ });
116
+ return;
117
+ }
118
+ const { method, url } = req;
119
+ if (method === 'GET' && url === '/health') {
120
+ const health = onHealth();
121
+ const clientCn = req.socket
122
+ .getPeerCertificate()?.subject?.CN;
123
+ logger.info('health_pinged', { from_cn: clientCn ?? 'unknown' });
124
+ sendJson(res, 200, health);
125
+ return;
126
+ }
127
+ if (method === 'POST' && url === '/notify') {
128
+ const contentType = req.headers['content-type'] ?? '';
129
+ if (!contentType.includes('application/json')) {
130
+ sendJson(res, 415, { error: 'Content-Type must be application/json' });
131
+ return;
132
+ }
133
+ let body;
134
+ try {
135
+ body = await readBody(req);
136
+ }
137
+ catch {
138
+ sendJson(res, 413, { error: 'Body too large (max 64KB)' });
139
+ return;
140
+ }
141
+ let parsed;
142
+ try {
143
+ parsed = JSON.parse(body);
144
+ }
145
+ catch {
146
+ sendJson(res, 400, { error: 'Invalid JSON' });
147
+ return;
148
+ }
149
+ const result = NotifyPayloadSchema.safeParse(parsed);
150
+ if (!result.success) {
151
+ sendJson(res, 400, { error: `Validation failed: ${result.error.message}` });
152
+ return;
153
+ }
154
+ // macf#194: wrap onNotify in a SERVER span so child operations
155
+ // (MCP push, tmux wake) attach to it via active-context
156
+ // propagation. Parent context extracted from W3C `traceparent`
157
+ // header if the routing Action sent one; otherwise a new root.
158
+ // Span + any mcp/tmux children roll up to the same trace-id
159
+ // in Langfuse/SigNoz, giving one unified trace per coord event.
160
+ const parentCtx = propagation.extract(context.active(), req.headers);
161
+ const clientCn = req.socket
162
+ .getPeerCertificate()?.subject?.CN ?? 'unknown';
163
+ const tracer = getTracer();
164
+ await tracer.startActiveSpan(SpanNames.NotifyReceived, {
165
+ kind: SpanKind.SERVER,
166
+ attributes: {
167
+ [GenAiAttr.System]: 'macf',
168
+ [GenAiAttr.OperationName]: operationNameForNotifyType(result.data.type),
169
+ [Attr.NotifyType]: result.data.type,
170
+ [Attr.RemoteCn]: clientCn,
171
+ ...(result.data.issue_number !== undefined
172
+ ? { [Attr.IssueNumber]: result.data.issue_number }
173
+ : {}),
174
+ },
175
+ }, parentCtx, async (span) => {
176
+ try {
177
+ await onNotify(result.data);
178
+ sendJson(res, 200, { status: 'received' });
179
+ span.setStatus({ code: SpanStatusCode.OK });
180
+ }
181
+ catch (err) {
182
+ logger.error('notify_push_failed', {
183
+ error: err instanceof Error ? err.message : String(err),
184
+ });
185
+ span.recordException(err);
186
+ span.setStatus({
187
+ code: SpanStatusCode.ERROR,
188
+ message: err instanceof Error ? err.message : String(err),
189
+ });
190
+ sendJson(res, 500, { error: 'Failed to push notification' });
191
+ }
192
+ finally {
193
+ span.end();
194
+ }
195
+ });
196
+ return;
197
+ }
198
+ if (method === 'POST' && url === '/sign') {
199
+ if (!onSign) {
200
+ sendJson(res, 503, { error: 'Signing not available on this agent' });
201
+ return;
202
+ }
203
+ const contentType = req.headers['content-type'] ?? '';
204
+ if (!contentType.includes('application/json')) {
205
+ sendJson(res, 415, { error: 'Content-Type must be application/json' });
206
+ return;
207
+ }
208
+ let body;
209
+ try {
210
+ body = await readBody(req);
211
+ }
212
+ catch {
213
+ sendJson(res, 413, { error: 'Body too large (max 64KB)' });
214
+ return;
215
+ }
216
+ let parsed;
217
+ try {
218
+ parsed = JSON.parse(body);
219
+ }
220
+ catch {
221
+ sendJson(res, 400, { error: 'Invalid JSON' });
222
+ return;
223
+ }
224
+ const result = SignRequestSchema.safeParse(parsed);
225
+ if (!result.success) {
226
+ sendJson(res, 400, { error: `Validation failed: ${result.error.message}` });
227
+ return;
228
+ }
229
+ // macf#194: /sign SERVER span. Audit-trail value — every cert
230
+ // issuance gets a trace entry correlatable to the requester
231
+ // (cn + agent_name) + the trace-parent (who kicked off the
232
+ // rotation?).
233
+ const signParentCtx = propagation.extract(context.active(), req.headers);
234
+ const signClientCn = req.socket
235
+ .getPeerCertificate()?.subject?.CN ?? 'unknown';
236
+ const signTracer = getTracer();
237
+ await signTracer.startActiveSpan(SpanNames.SignCsr, {
238
+ kind: SpanKind.SERVER,
239
+ attributes: {
240
+ [GenAiAttr.System]: 'macf',
241
+ [Attr.RemoteCn]: signClientCn,
242
+ [GenAiAttr.AgentName]: result.data.agent_name,
243
+ },
244
+ }, signParentCtx, async (span) => {
245
+ try {
246
+ const response = await onSign(result.data);
247
+ sendJson(res, 200, response);
248
+ span.setStatus({ code: SpanStatusCode.OK });
249
+ }
250
+ catch (err) {
251
+ // Typed HttpError carries a specific intended status;
252
+ // anything else is an unexpected server-side failure → 500.
253
+ const status = err instanceof HttpError ? err.httpStatus : 500;
254
+ sendJson(res, status, {
255
+ error: err instanceof Error ? err.message : 'Signing failed',
256
+ });
257
+ span.recordException(err);
258
+ span.setStatus({
259
+ code: SpanStatusCode.ERROR,
260
+ message: err instanceof Error ? err.message : String(err),
261
+ });
262
+ }
263
+ finally {
264
+ span.end();
265
+ }
266
+ });
267
+ return;
268
+ }
269
+ sendJson(res, 404, { error: 'Not found' });
270
+ }
271
+ function listenOnPort(srv, port, host) {
272
+ return new Promise((resolve, reject) => {
273
+ const onError = (err) => {
274
+ srv.removeListener('error', onError);
275
+ reject(err);
276
+ };
277
+ srv.on('error', onError);
278
+ srv.listen(port, host, () => {
279
+ srv.removeListener('error', onError);
280
+ const addr = srv.address();
281
+ const actualPort = typeof addr === 'object' && addr !== null ? addr.port : port;
282
+ resolve(actualPort);
283
+ });
284
+ });
285
+ }
286
+ function requestHandler(req, res) {
287
+ handleRequest(req, res).catch((err) => {
288
+ logger.error('request_error', {
289
+ error: err instanceof Error ? err.message : String(err),
290
+ });
291
+ if (!res.headersSent) {
292
+ sendJson(res, 500, { error: 'Internal server error' });
293
+ }
294
+ });
295
+ }
296
+ return {
297
+ async start(port, host) {
298
+ server = createServer(tlsOptions, requestHandler);
299
+ // TLS-layer handshake failures (no cert, expired cert, wrong CA,
300
+ // missing clientAuth EKU per #121) never reach requestHandler.
301
+ // Without this listener, operators see a dead connection with
302
+ // no log entry explaining why. Log enough to triage — ultrareview
303
+ // finding H1.
304
+ server.on('tlsClientError', (err, tlsSocket) => {
305
+ const peerCn = tlsSocket.getPeerCertificate?.()
306
+ ?.subject?.CN ?? 'unknown';
307
+ logger.warn('tls_client_error', {
308
+ error: err.message,
309
+ code: err.code ?? 'unknown',
310
+ from_cn: peerCn,
311
+ remote_addr: tlsSocket.remoteAddress ?? 'unknown',
312
+ });
313
+ });
314
+ // Explicit port: fail immediately if busy
315
+ if (port !== 0) {
316
+ try {
317
+ const actualPort = await listenOnPort(server, port, host);
318
+ return { actualPort };
319
+ }
320
+ catch (err) {
321
+ const nodeErr = err;
322
+ if (nodeErr.code === 'EADDRINUSE') {
323
+ throw new PortUnavailableError(port);
324
+ }
325
+ throw new HttpsServerError(`Failed to start server: ${nodeErr.message}`);
326
+ }
327
+ }
328
+ // Random port: retry up to MAX_PORT_ATTEMPTS times
329
+ for (let attempt = 0; attempt < MAX_PORT_ATTEMPTS; attempt++) {
330
+ const candidatePort = randomPort();
331
+ try {
332
+ const actualPort = await listenOnPort(server, candidatePort, host);
333
+ return { actualPort };
334
+ }
335
+ catch (err) {
336
+ const nodeErr = err;
337
+ if (nodeErr.code !== 'EADDRINUSE') {
338
+ throw new HttpsServerError(`Failed to start server: ${nodeErr.message}`);
339
+ }
340
+ // Close and recreate server for retry
341
+ await new Promise((r) => server.close(() => r()));
342
+ server = createServer(tlsOptions, requestHandler);
343
+ }
344
+ }
345
+ throw new PortExhaustedError();
346
+ },
347
+ async stop() {
348
+ if (!server)
349
+ return;
350
+ return new Promise((resolve, reject) => {
351
+ server.close((err) => {
352
+ if (err)
353
+ reject(new HttpsServerError(`Failed to stop server: ${err.message}`));
354
+ else
355
+ resolve();
356
+ });
357
+ });
358
+ },
359
+ };
360
+ }
361
+ //# sourceMappingURL=https.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"https.js","sourceRoot":"","sources":["../src/https.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Multi-Agent Coordination Framework (MACF)
3
+ *
4
+ * Coordinates multiple Claude Code agents via GitHub,
5
+ * using MCP channels (HTTP/mTLS) for communication.
6
+ */
7
+ export { MacfError, ConfigError, McpChannelError, HttpsServerError, PortUnavailableError, PortExhaustedError, ValidationError } from '@groundnuty/macf-core';
8
+ export { createMcpChannel } from './mcp.js';
9
+ export { createHttpsServer } from './https.js';
10
+ export { createHealthState } from './health.js';
11
+ export { createLogger } from '@groundnuty/macf-core';
12
+ export { loadConfig } from '@groundnuty/macf-core';
13
+ export { NotifyPayloadSchema, NotifyTypeSchema, HealthResponseSchema, CiCompletionPayloadSchema, CheckSuiteConclusionSchema, } from '@groundnuty/macf-core';
14
+ export type { NotifyPayload, NotifyType, HealthResponse, AgentConfig, Logger, McpChannel, HttpsServer, HealthState, CiCompletionPayload, CheckSuiteConclusion, } from '@groundnuty/macf-core';
15
+ export { createRegistryFromConfig, createRegistry, createGitHubClient, GitHubApiError, AgentInfoSchema, RegistryConfigSchema } from '@groundnuty/macf-core';
16
+ export type { AgentInfo, Registry, RegistryConfig, GitHubVariablesClient } from '@groundnuty/macf-core';
17
+ export { checkCollision, CollisionError } from './collision.js';
18
+ export type { CollisionResult } from './collision.js';
19
+ export { registerShutdownHandler } from './shutdown.js';
20
+ export { generateToken } from '@groundnuty/macf-core';
21
+ export { checkPendingIssues } from './startup-issues.js';
22
+ export { createCA, backupCAKey, recoverCAKey, encryptCAKey, decryptCAKey, loadCA, CaError } from '@groundnuty/macf-core';
23
+ export type { CaKeyPair } from '@groundnuty/macf-core';
24
+ export { generateAgentCert, generateCSR, signCSR, AgentCertError } from '@groundnuty/macf-core';
25
+ export type { AgentCertResult } from '@groundnuty/macf-core';
26
+ export { createChallenge, verifyAndConsumeChallenge, ChallengeError } from '@groundnuty/macf-core';
27
+ export { SignRequestSchema, SignChallengeResponseSchema, SignCertResponseSchema } from '@groundnuty/macf-core';
28
+ export type { SignRequest } from '@groundnuty/macf-core';
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7J,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EACL,mBAAmB,EAAE,gBAAgB,EAAE,oBAAoB,EAC3D,yBAAyB,EAAE,0BAA0B,GACtD,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,aAAa,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,EAC9D,UAAU,EAAE,WAAW,EAAE,WAAW,EACpC,mBAAmB,EAAE,oBAAoB,GAC1C,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,wBAAwB,EAAE,cAAc,EAAE,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC5J,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACxG,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGzD,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACzH,YAAY,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAChG,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACnG,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/G,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}