import { type BigIntStats } from "node:fs"; export declare const CODEX_NATIVE_POLICY_CAPABILITY = "host-path-allowlist-v1"; export declare const CODEX_NATIVE_POLICY_RECEIPT_SCHEMA = "skills.codex-native-policy-acceptance/v1"; /** Loop safety for the parent walk. It is not a policy bound on launch depth: * Unix hooks run through the configured shell (`-lc`, new session), which may * exec into the command or keep wrappers, so the real chain length varies. */ export declare const CODEX_NATIVE_POLICY_ANCESTRY_SAFETY_HOPS = 64; export type CodexNativeHookEvent = "SessionStart" | "UserPromptSubmit"; /** Exactly the fields the native hook emits (hook_runtime.rs, native_skill_policy_for_hook). */ export interface CodexNativeSkillPolicy { capability: typeof CODEX_NATIVE_POLICY_CAPABILITY; mode: "restricted"; allowedHostPaths: [string]; nonHostSources: "disabled"; processId: number; effectiveConfigDigest: string; } export interface CodexNativeHookEnvelope { event: CodexNativeHookEvent; policy: unknown; sessionId: unknown; /** Present on UserPromptSubmit only; SessionStart input has no turn id. */ turnId?: unknown; /** SHA-256 of the exact hook stdin bytes, for the authenticated channel binding. */ hookInputSha256?: unknown; /** Inherited read end of the per-hook socketpair Codex passes to the hook * child, for the authenticated channel binding. Not yet named by the native * contract, so the hook does not supply it today. */ inheritedFd?: unknown; } /** Native Codex sets this only on the current hook child; it names the * inherited read end of the per-hook socketpair (draft binding contract). */ export declare const CODEX_NATIVE_POLICY_FD_ENV = "CODEX_NATIVE_SKILL_POLICY_FD"; /** Map one native hook input object to the adapter envelope, exactly as the * Codex `hook user-prompt` path does. Undefined means the input carries no * native policy or is not a SessionStart/UserPromptSubmit input, so the guard * keeps today's behaviour. `inputBytes` are the exact raw stdin bytes, hashed * before any decoding; `input` must have been parsed from those same bytes. */ export declare function codexNativeHookEnvelopeFromInput(input: Record, event: string, inputBytes: Buffer, inheritedFd?: string): CodexNativeHookEnvelope | undefined; /** Injectable so tests can supply fake lineages; production uses the OS. */ export interface ProcessInspector { platform: string; arch: string; /** The hook process itself. */ pid: number; parentOf(pid: number): number | null; /** Opaque start identity; two equal reads mean the same process incarnation. */ startTime(pid: number): string | null; executablePath(pid: number): string | null; } /** Reviewed operator trust from the managed policy (`bridge.codexNativePolicy`). */ export interface CodexNativePolicyTrust { executableDigests: Readonly>; } /** What the native helper attests over the inherited channel (draft contract). */ export interface CodexNativePolicyAttestation { schema: typeof CODEX_NATIVE_POLICY_PEER_SCHEMA; peerProcessId: number; } export interface CodexNativePolicyVerification { event: CodexNativeHookEvent; sessionId: string; turnId: string | null; policy: CodexNativeSkillPolicy; platform: string; ancestryHops: number; processStartTime: string; executablePath: string; executableSha256: string; /** No-follow identity of the qualified executable taken before hashing; re-taken after the helper. */ executableWitness: ExecutableWitness; bridge: { path: string; sha256: string; }; attestation?: CodexNativePolicyAttestation; } export interface CodexNativePolicyAcceptedSkill { path: string; treeSha256: string; } export interface CodexNativePolicyAcceptance { schema: typeof CODEX_NATIVE_POLICY_RECEIPT_SCHEMA; acceptedAt: string; event: CodexNativeHookEvent; sessionId: string; turnId: string | null; process: { id: number; startTime: string; executablePath: string; executableSha256: string; platform: string; }; effectiveConfigDigest: string; bridge: { path: string; sha256: string; }; acceptedCache: CodexNativePolicyAcceptedSkill[]; attestation?: CodexNativePolicyAttestation; } /** Draft native helper transport: argv, bounded run, JSON attestation. */ export declare const CODEX_NATIVE_POLICY_PEER_SCHEMA = "native-hook-policy-peer-v1"; export declare const CODEX_NATIVE_POLICY_HELPER_TIMEOUT_MS = 5000; export declare const CODEX_NATIVE_POLICY_HELPER_MAX_OUTPUT_BYTES: number; /** Hook-deadline bounds: the helper gets min(5 s, remaining minus the margin); * a first-run executable hash needs this much budget; below the minimum the * adapter refuses fast instead of spawning anything. */ export declare const CODEX_NATIVE_POLICY_DEADLINE_MARGIN_MS = 1000; export declare const CODEX_NATIVE_POLICY_HELPER_MIN_TIMEOUT_MS = 500; export declare const CODEX_NATIVE_POLICY_FIRST_HASH_BUDGET_MS = 3000; export interface NativePolicyHelperRequest { executablePath: string; fd: number; expectedProcessId: number; inputSha256: string; timeoutMs: number; maxOutputBytes: number; } export interface NativePolicyHelperResult { exitCode: number | null; stdout: Buffer; timedOut: boolean; oversized: boolean; } /** Injectable so tests can fake the helper; production spawns the verified binary. */ export type NativePolicyHelperRunner = (request: NativePolicyHelperRequest) => NativePolicyHelperResult; export declare const CODEX_NATIVE_POLICY_EXECUTABLE_CACHE_SCHEMA = "skills.codex-native-policy-executable-cache/v1"; /** No-follow identity of the qualified executable file; the only thing cached. */ export interface ExecutableWitness { dev: string; ino: string; size: string; mtimeNs: string; ctimeNs: string; uid: string; } /** Absent trust is an empty set: every platform is unqualified and refuses. * The pinned digests are reviewed operator configuration, never a package default. */ export declare function parseCodexNativePolicyTrust(value: unknown): CodexNativePolicyTrust; /** Strict envelope parsing. Unknown keys, unknown capability, unrestricted mode * and anything but the exact bridge document refuse. */ export declare function parseCodexNativeHookEnvelope(envelope: CodexNativeHookEnvelope, bridgeDocument: string): { policy: CodexNativeSkillPolicy; sessionId: string; turnId: string | null; hookInputSha256: string | null; inheritedFd: number | null; }; /** The owned bridge document: a regular file, no link in any component, its * real path equal to its lexical path, and exactly the bytes the guard installs. */ export declare function verifyCodexNativeBridgeDocument(path: string, expectedContent: string, expectedSha256: string): { path: string; sha256: string; }; /** Walk the real parent chain from the hook process to the root. The claimed * consumer must appear as a strict ancestor. Only a cycle/loop guard bounds * the walk; the expected launch-chain shape (which wrappers may sit between * Codex and this hook) is pending from the native author and plugs in here as * a predicate over the walked chain. */ export declare function verifyCodexNativeAncestry(inspector: ProcessInspector, processId: number): { hops: number; chain: number[]; startTime: string; }; export declare function hashExecutableFile(path: string): string; /** Accept only a regular file owned by root or the current user that neither * group nor world can write. Pure over the no-follow stat so it is testable * with synthetic owners. */ export declare function assertExecutableWitnessStat(stat: Pick, currentUid: number): ExecutableWitness; /** No-follow witness of the path proc_pidpath reported: no component may be a * link, and the file must satisfy assertExecutableWitnessStat. */ export declare function executableWitness(path: string): ExecutableWitness; export declare function executableCachePath(dataDir: string): string; /** Digest of the qualified executable through the artifact-identity cache. * Only (path, dev, ino, size, mtimeNs, ctimeNs, uid) -> sha256 is cached, in * process and optionally in a 0600 file under the Skills data dir. A witness * that changes between before and after, or differs from the cached key, is * re-hashed. Policy, attestation, session and turn results are never cached. */ export declare function qualifiedExecutableSha256(path: string, options?: { dataDir?: string; hasher?: (path: string) => string; deadlineMs?: number; /** Test seam: the uid the cache file must be owned by (besides root). */ cacheOwnerUid?: number; }): { sha256: string; witness: ExecutableWitness; cached: boolean; }; /** Bind the claimed process to its start identity and a pinned executable * digest. The pin set is reviewed operator trust; an absent or empty set for * this platform means the platform is unqualified and refuses. Linux stays * unqualified until its artifact is reviewed. The helper cannot attest that it * is itself reviewed, so this trust stays in the managed policy. */ export declare function verifyCodexNativeExecutable(inspector: ProcessInspector, processId: number, trust: CodexNativePolicyTrust, cache?: { dataDir?: string; hasher?: (path: string) => string; deadlineMs?: number; }, expectations?: { ancestryStartTime?: string; }): { platform: string; startTime: string; executablePath: string; executableSha256: string; witness: ExecutableWitness; }; /** The Skills data directory is the operator trust root: the managed policy * that carries the reviewed executable digests, and the executable identity * cache, must each be a regular file reached through no symlink, owned by the * current user or root and writable by neither group nor world. Anything else * fails the adapter closed. A writer with the same uid is outside this * boundary. */ export declare function assertOperatorTrustRoot(dataDir: string): void; /** Production helper runner: the verified binary, no shell, an allowlisted * empty environment, cwd `/`, the inherited descriptor forwarded as fd 3, and * bounded time and output. Bun.spawnSync accepts a fourth stdio entry, which * dup2s that descriptor to 3 in the child (measured on bun 1.3.14). */ export declare function runCodexNativePolicyHelper(request: NativePolicyHelperRequest): NativePolicyHelperResult; /** Authenticated channel binding, against the frozen native source * (native-hook-policy-peer-v1; "line" numbers below index the native * producer's hook-policy-peer patch as reviewed): * - the hook runner creates a Unix socketpair per invocation (line 296), * forwards the receiver to the hook child (line 171) and names it in * CODEX_NATIVE_SKILL_POLICY_FD (lines 172-175, constant at line 264); * - the verifier is ` debug verify-hook-policy --fd * --expected-process-id --input-sha256 ` (lines 88-97, dispatch * lines 102-103), dispatched in main before config, auth-home or session * setup (lines 55-58); it requires fd > 2 (line 449), a positive pid * (line 450) and a lowercase 64-hex digest (line 456); * - success writes one compact JSON object to stdout followed by a newline and * exits 0 (lines 56-58): {"schema":"native-hook-policy-peer-v1", * "peerProcessId":,"stdinSha256":, * "policy":} (lines 546-550, * schema at line 263); any failure returns an error from main, so the * process exits non-zero with the message on stderr and nothing on stdout; * - the producer hashes the exact bytes it wrote to the hook's stdin, * lowercase hex (lines 286-288), and the verifier compares it with * --input-sha256 (line 536), so the hook must hash its raw stdin bytes; * - the verifier checks the kernel peer before and after a fresh random * challenge (lines 466-469, 490-491, 517-519, 526-528); Linux also * authenticates every response chunk's writer through SCM_CREDENTIALS. * A creator-pid-only binding was rejected (NO_GO: pre-exec forgery * reproduced); the fresh challenge plus actual-writer verification is the * correction. * The hook forwards the descriptor to the QUALIFIED ancestor's own executable * (the digest-verified path, never PATH) as fd 3, with no shell, an empty * environment, cwd `/` and bounded time and output, and accepts the * attestation only when every field matches. The proof is never cached: * once per invocation, SessionStart included. The channel pre-check here is * fstat only; the socket semantics are enforced by the verifier itself. */ export declare function verifyCodexNativeChannelBinding(options: { executablePath: string; inheritedFd: number | null; expectedProcessId: number; expectedStartTime: string; expectedWitness?: ExecutableWitness; inputSha256: string | null; emittedPolicy: unknown; inspector: ProcessInspector; runner?: NativePolicyHelperRunner; timeoutMs?: number; maxOutputBytes?: number; deadlineMs?: number; }): CodexNativePolicyAttestation; /** Required acceptance gate: the authenticated channel binding of the native * contract, run with the qualified ancestor's verified executable and its * witness, its start time, the raw stdin digest, the inherited descriptor, the * emitted policy and the remaining hook budget. */ export declare function assertAuthenticatedChannelBinding(binding: { processId: number; hookInputSha256: string | null; inheritedFd: number | null; verification: CodexNativePolicyVerification; emittedPolicy: unknown; inspector: ProcessInspector; runner?: NativePolicyHelperRunner; deadlineMs?: number; }): CodexNativePolicyAttestation; /** Full adapter verification. Every step refuses with a reason. Order: trust * configuration, envelope, bridge, ancestry, executable digest (reviewed trust), * then the authenticated channel binding, once per invocation. */ export declare function verifyCodexNativeSkillPolicy(options: { envelope: CodexNativeHookEnvelope; bridgeDocument: string; expectedBridgeContent: string; expectedBridgeSha256: string; trust?: unknown; inspector?: ProcessInspector; dataDir?: string; executableHasher?: (path: string) => string; helperRunner?: NativePolicyHelperRunner; deadlineMs?: number; }): CodexNativePolicyVerification; /** Darwin: libproc through bun:ffi. proc_pidinfo(PROC_PIDTBSDINFO) yields the * parent pid and start time; proc_pidpath yields the executable path. Layout * from the SDK's sys/proc_info.h (struct proc_bsdinfo, 136 bytes): pbi_pid at * 12, pbi_ppid at 16, pbi_start_tvsec at 120, pbi_start_tvusec at 128. */ export declare function darwinProcessInspector(): ProcessInspector; /** Linux: procfs. The parent pid and start time (clock ticks since boot) come * from /proc//stat after the bracketed command name; the executable from * /proc//exe. Linux remains unqualified until its artifact is reviewed. */ export declare function linuxProcessInspector(): ProcessInspector; export declare function defaultProcessInspector(): ProcessInspector; export declare function codexNativePolicyReceiptPath(dataDir: string): string; /** Write the provenance receipt for one accepted hook: mode 0600, exclusive * no-follow temporary file, then rename. Any failure throws, and the caller * treats that as a refusal. Reachable only after the channel binding passes. */ export declare function recordCodexNativePolicyAcceptance(dataDir: string, verification: CodexNativePolicyVerification, accepted: readonly CodexNativePolicyAcceptedSkill[]): string;