/** * Workflow v3 daemon-mutation IPC authentication. * * This protocol deliberately has its own domain and headers. The historical * dashboard CLI HMAC signs only `ts:nonce`; accepting it here would let a * credential captured for another local route be replayed against workflow * start/retry/grant/cancel. A v1 credential is instead bound to the exact * request bytes and one daemon boot: * * [domain, ts, nonce, METHOD, raw req.url, sha256(body), appId, port, bootId] * * The JSON-array encoding is unambiguous even if a future field contains a * newline. `bootInstanceId` is public audience data, not a secret; including * it makes every credential invalid immediately after a daemon restart even * when the process reuses the same port and the nonce cache starts empty. * The HMAC key remains the established 0600 `.dashboard-secret`: putting a * per-boot key in the public discovery descriptor would hand the capability to * the exact port-only local process this boundary is intended to reject. */ import type { IncomingMessage } from 'node:http'; export declare const WORKFLOW_DAEMON_IPC_DOMAIN = "botmux-workflow-daemon-ipc/v1"; export declare const WORKFLOW_DAEMON_IPC_RESPONSE_DOMAIN = "botmux-workflow-daemon-ipc/v1/response"; /** Dedicated namespace unknown to pre-v1 daemons; never reuse legacy mutation paths. */ export declare const WORKFLOW_DAEMON_IPC_ROUTE_PREFIX = "/__workflow-ipc/v1/runs"; export declare const WORKFLOW_DAEMON_IPC_TS_WINDOW_MS = 60000; export declare const WORKFLOW_DAEMON_IPC_NONCE_TTL_MS: number; export declare const WORKFLOW_DAEMON_IPC_BODY_LIMIT_BYTES: number; export declare const WORKFLOW_DAEMON_IPC_BODY_READ_TIMEOUT_MS = 5000; export declare const WORKFLOW_DAEMON_IPC_HEADERS: { readonly timestamp: "x-botmux-workflow-ipc-ts"; readonly nonce: "x-botmux-workflow-ipc-nonce"; readonly signature: "x-botmux-workflow-ipc-signature"; readonly responseSignature: "x-botmux-workflow-ipc-response-signature"; }; export interface WorkflowDaemonIpcTarget { larkAppId: string; ipcPort: number; bootInstanceId: string; } export interface WorkflowDaemonIpcClock { now(): number; } export interface WorkflowDaemonIpcNonceStore { has(nonce: string): boolean; add(nonce: string, expiresAt: number): void; size(): number; } export type WorkflowDaemonIpcVerifyReason = 'remote_not_loopback' | 'missing_or_malformed_header' | 'timestamp_out_of_window' | 'target_identity_unavailable' | 'body_too_large' | 'body_length_mismatch' | 'body_read_timeout' | 'body_read_failed' | 'body_not_utf8' | 'signature_mismatch' | 'replay'; export type WorkflowDaemonIpcVerifyResult = { ok: true; bodyRaw: string; nonce: string; target: WorkflowDaemonIpcTarget; } | { ok: false; reason: WorkflowDaemonIpcVerifyReason; httpStatus: number; }; export interface WorkflowDaemonIpcSignInput { secret: string; timestamp: string; nonce: string; method: string; pathWithQuery: string; body: string | Uint8Array; target: WorkflowDaemonIpcTarget; } export interface WorkflowDaemonIpcResponseSignInput { secret: string; requestNonce: string; method: string; pathWithQuery: string; status: number; body: string | Uint8Array; target: WorkflowDaemonIpcTarget; } export interface WorkflowDaemonIpcVerifyOptions { secret: string; target: Omit; nonceStore: WorkflowDaemonIpcNonceStore; clock?: WorkflowDaemonIpcClock; maxBodyBytes?: number; bodyReadTimeoutMs?: number; } export declare const workflowDaemonIpcRealClock: WorkflowDaemonIpcClock; /** Canonical bytes covered by the v1 HMAC. Exported for golden tests/audits. */ export declare function canonicalWorkflowDaemonIpcMaterial(input: Omit): string; export declare function signWorkflowDaemonIpcRequest(input: WorkflowDaemonIpcSignInput): string; export declare function canonicalWorkflowDaemonIpcResponseMaterial(input: Omit): string; export declare function signWorkflowDaemonIpcResponse(input: WorkflowDaemonIpcResponseSignInput): string; export declare function verifyWorkflowDaemonIpcResponse(input: WorkflowDaemonIpcResponseSignInput & { signature: string | null | undefined; }): boolean; export declare function createWorkflowDaemonIpcNonceStore(clock?: WorkflowDaemonIpcClock): WorkflowDaemonIpcNonceStore; /** * Authenticate one inbound Workflow daemon mutation. The request body is read * exactly once here and returned to the route; handlers must never read `req`. */ export declare function verifyWorkflowDaemonIpcRequest(req: IncomingMessage, options: WorkflowDaemonIpcVerifyOptions): Promise; export declare function generateWorkflowDaemonBootInstanceId(): string; export declare function generateWorkflowDaemonIpcNonce(): string; export declare function defaultWorkflowDaemonIpcSecretPath(): string; export declare function loadWorkflowDaemonIpcSecret(secretPath?: string): string; export declare function workflowDaemonIpcHeaders(input: { secret: string; method: string; pathWithQuery: string; bodyRaw: string; target: WorkflowDaemonIpcTarget; timestamp?: string; nonce?: string; }): Record; //# sourceMappingURL=daemon-ipc-auth.d.ts.map