@gajae-code/natives 0.18.5 → 0.18.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ {
2
+ "schema": "gjc.diagnostic-artifact",
3
+ "version": "0.18.7",
4
+ "artifacts": {
5
+ "pi_natives.darwin-arm64.node": "3d074ffc8128980c3a8cbe1d62c99cc1f3a6dc1e4c86dfee3a8fd721b00a0717",
6
+ "pi_natives.darwin-x64-baseline.node": "68eda6a629816cf9ddfedb26afb3e105b7fb1455bfde52e47b41adeda17b007a",
7
+ "pi_natives.linux-arm64.node": "260992679d66e4f3de25ed77a0b99a494fe577bf2e2dabf3fd74178782bfebb4",
8
+ "pi_natives.linux-x64-baseline.node": "d1be8531cffa10bc77660e955cb889c5e95e68951bf966f79a878c7b32245743",
9
+ "pi_natives.linux-x64-modern.node": "259f0d4b1e99e6766924c823dfd246d8a5df4382b3167a24da2ecf1f37fd09c2",
10
+ "pi_natives.win32-x64-baseline.node": "9aa8311f1457009fdfc9b3098aedb0fa0c3e770119a6eae9caf58b57d0186357"
11
+ }
12
+ }
@@ -0,0 +1,143 @@
1
+ /** Bytes copied out of one read-only broker publication observation. */
2
+ export interface DiagnosticSnapshotRead {
3
+ ok: boolean;
4
+ reason: string | null;
5
+ bytes: Uint8Array | null;
6
+ }
7
+
8
+ /** Outcome of one read-only publication revalidation. */
9
+ export interface DiagnosticSnapshotStatus {
10
+ ok: boolean;
11
+ reason: string | null;
12
+ }
13
+
14
+ /**
15
+ * Exclusive read-only lease over one already published broker document. It
16
+ * exposes no descriptor, path, token or write method; `close` is idempotent and
17
+ * every use after close reports `unsafe_discovery`.
18
+ */
19
+ export interface DiagnosticSnapshotLease {
20
+ readonly ok: boolean;
21
+ readonly reason: string | null;
22
+ read(): DiagnosticSnapshotRead;
23
+ revalidate(): DiagnosticSnapshotStatus;
24
+ close(): void;
25
+ }
26
+
27
+ export interface DiagnosticNativeEntry {
28
+ openDiagnosticSnapshot(agentDir: string, budgetMs: number): DiagnosticSnapshotLease;
29
+ }
30
+
31
+ export type DiagnosticNativeLoad =
32
+ | { ok: true; value: DiagnosticNativeEntry }
33
+ | { ok: false; reason: "unsupported" };
34
+
35
+ /**
36
+ * Load the read-only diagnostic entry point from the fixed package-owned
37
+ * artifact. Never extracts, repairs, scans candidates or initializes the
38
+ * ordinary native loader; any unsupported runtime, missing or mismatched
39
+ * artifact resolves to `unsupported`.
40
+ */
41
+ export interface DiagnosticRuntimeTuple {
42
+ platform?: string;
43
+ arch?: string;
44
+ bunVersion?: string;
45
+ }
46
+
47
+ /** True only for the single admitted tuple: darwin/arm64 on Bun 1.4.0. */
48
+ export declare function supportedRuntime(runtime: DiagnosticRuntimeTuple): boolean;
49
+
50
+ /**
51
+ * An opaque, single-use verification authority. It carries no descriptor and no
52
+ * path: either `activateVerifiedArtifact` or `closeVerifiedArtifact` consumes it,
53
+ * after which the token holds no authority at all.
54
+ */
55
+ export interface VerifiedDiagnosticArtifact {
56
+ ok: true;
57
+ readonly token: string;
58
+ }
59
+
60
+ export type DiagnosticArtifactVerification = VerifiedDiagnosticArtifact | { ok: false; reason: "unsupported" };
61
+
62
+ /**
63
+ * Classify one ACL probe. Pure and platform-independent: absence requires a null
64
+ * handle with the platform's "no such ACL" errno, a first entry means an ACE exists,
65
+ * and everything else -- including `acl_get_entry` returning -1 -- is unproven.
66
+ */
67
+ export declare function classifyAclProbe(probe: {
68
+ handle: number;
69
+ errno: number | null;
70
+ firstEntry: number | null;
71
+ }): "absent" | "present" | "unproven";
72
+
73
+ /**
74
+ * Classify the extended ACL of an open descriptor through the platform's read-only
75
+ * ACL API, bound from the absolute system library path. Refuses before `dlopen` when
76
+ * a dyld override is in effect, and reports `unproven` whenever the platform cannot
77
+ * answer.
78
+ */
79
+ export declare function inspectAclOnDescriptor(descriptor: number): "absent" | "present" | "unproven";
80
+
81
+ /** One parsed entry of the bounded external ACL representation. */
82
+ export interface ParsedAclEntry {
83
+ guid: Uint8Array;
84
+ flags: number;
85
+ rights: number;
86
+ }
87
+
88
+ export type ParsedAclRepresentation =
89
+ | { ok: true; count: number; headerFlags: number; entries: ParsedAclEntry[] }
90
+ | { ok: false };
91
+
92
+ /** Parse and structurally validate the bounded external ACL representation. */
93
+ export declare function parseAclRepresentation(bytes: Uint8Array): ParsedAclRepresentation;
94
+
95
+ /**
96
+ * Decide a parsed representation by full-field equality: exactly one entry, ACL header
97
+ * flags entirely zero, entry flags exactly the deny tag, rights exactly DELETE, and the
98
+ * everyone GUID.
99
+ */
100
+ export declare function classifyAclRepresentation(
101
+ parsed: unknown,
102
+ ): "approved-single-deny-delete" | "not-allowlisted" | "unproven";
103
+
104
+ /**
105
+ * Classify a trusted ancestor's ACL above the artifact namespace anchor: absent, the
106
+ * approved single non-inheriting everyone DENY DELETE entry, understood-but-refused, or
107
+ * unproven.
108
+ */
109
+ export declare function inspectAncestorAclOnDescriptor(
110
+ descriptor: number,
111
+ ): "absent" | "approved-single-deny-delete" | "not-allowlisted" | "unproven";
112
+
113
+ /** Admission for one component of the artifact path: owner, mode, shape. */
114
+ export declare function namespaceComponentAdmitted(
115
+ component: { uid: number; gid: number; mode: number; isSymbolicLink: boolean; isDirectory: boolean },
116
+ euid: number,
117
+ ): boolean;
118
+
119
+ /** The single applicable package-owned artifact path, or null when none applies. */
120
+ export declare function selectArtifactLayout(require: NodeRequire): string | null;
121
+
122
+ /**
123
+ * Prove the namespace and the bytes of one artifact and retain the descriptor they
124
+ * were read from. The handle owns an open descriptor: pass it to
125
+ * `activateVerifiedArtifact` or release it with `closeVerifiedArtifact`.
126
+ */
127
+ export declare function verifyDiagnosticArtifact(file: string): DiagnosticArtifactVerification;
128
+
129
+ /**
130
+ * Release a verified handle without activating it. A fabricated, mutated or already
131
+ * consumed handle is ignored rather than closing an unrelated descriptor.
132
+ */
133
+ export declare function closeVerifiedArtifact(handle: unknown): void;
134
+
135
+ /**
136
+ * Activate the verified bytes through their retained descriptor, so a pathname
137
+ * replaced after verification cannot redirect the load. Always closes the handle.
138
+ */
139
+ export declare function activateVerifiedArtifact(handle: unknown): DiagnosticNativeLoad;
140
+
141
+ export declare function validateDiagnosticBinding(binding: unknown, expectedVersion: string): boolean;
142
+
143
+ export declare function loadDiagnosticNativeReadOnly(runtimeOverride?: DiagnosticRuntimeTuple): DiagnosticNativeLoad;
@@ -0,0 +1,815 @@
1
+ /**
2
+ * Read-only diagnostic addon loader (private subpath).
3
+ *
4
+ * Deliberately NOT the ordinary loader: importing the package entry runs
5
+ * `loadNative()` plus crash-diagnostics initialization, and the ordinary loader
6
+ * may consider embedded payloads, cache directories and self-healing steps. A
7
+ * read-only broker observation must not do any of that, so this module:
8
+ *
9
+ * - admits exactly one runtime tuple (darwin/arm64 on Bun 1.4.0) and reports a
10
+ * fixed `unsupported` outcome for everything else, before touching the
11
+ * filesystem;
12
+ * - selects exactly ONE package-owned layout — the versioned cache slot when
13
+ * running from a compiled binary, otherwise the addon beside this file (build
14
+ * checkout) or, when that is absent, the `os`/`cpu`-matched optional platform
15
+ * package that ships it in a published install. There is no candidate walk: a
16
+ * selected layout that fails verification is `unsupported`, never a reason to
17
+ * try another location. No cwd, environment, agent directory or search path is
18
+ * ever consulted;
19
+ * - proves the trusted namespace before reading: every component is owned by the
20
+ * current user or by root, is not a symlink, and grants no group/other write,
21
+ * so an account that owns none of the path cannot swap the artifact out;
22
+ * - proves the bytes on a retained descriptor: SHA-256 equal to the digest this
23
+ * package records in `diagnostic-artifact.json`, which is part of the module
24
+ * graph so a compiled binary carries it too. An addon's own
25
+ * `nativeBuildInfo()` is self-reported by code that has already executed, so
26
+ * it is only a consistency check afterwards, never the trust anchor;
27
+ * - activates the verified bytes through that same retained descriptor
28
+ * (`process.dlopen` on `/dev/fd/<fd>`), so a pathname replaced between
29
+ * verification and activation cannot change what is loaded. On this runtime the
30
+ * descriptor path reopens the descriptor's own vnode; that was verified
31
+ * experimentally, and the loader refuses to activate at all if that route is
32
+ * unavailable;
33
+ * - creates nothing and changes nothing on disk: no embedded-payload unpacking,
34
+ * no staging, no directory creation, no permission change, no lock, and no
35
+ * crash-diagnostics initialization.
36
+ *
37
+ * Boundaries stated exactly, because the loader must not claim what it cannot prove:
38
+ *
39
+ * - ACL authority IS inspected, through the platform's own read-only ACL API
40
+ * (`acl_get_fd_np`/`acl_get_entry`/`acl_free`), bound from the ABSOLUTE system
41
+ * library path with Bun's existing FFI. The absolute path matters: a basename
42
+ * bootstrap loads and runs the initializer of a same-named library found through
43
+ * the dynamic loader's search, which was reproduced on this runtime. Apple's own
44
+ * `acl_get_fd` is a wrapper for `acl_get_fd_np(fd, ACL_TYPE_EXTENDED)`; the `_np`
45
+ * form is used here only to state the ACL type explicitly.
46
+ * - A dyld override environment (`DYLD_LIBRARY_PATH`, `DYLD_INSERT_LIBRARIES`,
47
+ * `DYLD_FRAMEWORK_PATH`, the `DYLD_FALLBACK_*` variants) cannot be proven safe
48
+ * from inside the process, so the loader refuses BEFORE it calls `dlopen`,
49
+ * without deleting or rewriting anyone's environment.
50
+ * - The addon under verification is never imported for this, no helper process is
51
+ * spawned and nothing is installed. The artifact file and its own directory -- the
52
+ * artifact namespace anchor, which is NOT `agentDir` -- must have NO extended ACL
53
+ * entry. A trusted ancestor STRICTLY ABOVE that anchor may instead carry exactly one
54
+ * fully validated, non-inheriting `everyone DENY DELETE` entry (the user-approved
55
+ * exception); anything else, including ALLOW, mixed, inherited, extra rights, a
56
+ * different principal or more than one entry, fails closed. An ACL the platform
57
+ * cannot answer for counts as
58
+ * unproven and is refused: `st_mode` alone cannot show an ACE that grants another
59
+ * account write access. An `acl_get_entry` result of -1 is end-of-iteration OR a
60
+ * failure (an invalid ACL reports EINVAL the same way), so it is never read as
61
+ * "this file has no ACL"; only a null handle with the platform's "no such ACL"
62
+ * errno proves absence. Where the API is unavailable the loader reports
63
+ * `unsupported` instead of guessing.
64
+ * - A rewrite of the same inode by the owner or root stays an explicit non-goal.
65
+ * What the loader does enforce is that any write which lands after verification
66
+ * — by anyone, including a byte-identical rewrite — invalidates the handle,
67
+ * because activation re-proves the descriptor's metadata before `dlopen`.
68
+ *
69
+ * The lease returned by `openDiagnosticSnapshot` exposes no descriptor, path or
70
+ * publication authority: only bounded bytes, revalidation and close.
71
+ */
72
+
73
+ import { createHash, randomUUID } from "node:crypto";
74
+ import * as fs from "node:fs";
75
+ import { createRequire } from "node:module";
76
+ import * as os from "node:os";
77
+ import * as nodePath from "node:path";
78
+ import trustedArtifacts from "./diagnostic-artifact.json" with { type: "json" };
79
+ import packageManifest from "../package.json" with { type: "json" };
80
+
81
+ /** SPEC AC-B3: the first supported runtime is exactly Darwin arm64 on Bun 1.4.0. */
82
+ const SUPPORTED_BUN_VERSION = "1.4.0";
83
+ const ARTIFACT_BASENAME = "pi_natives.darwin-arm64.node";
84
+ const PLATFORM_PACKAGE = "@gajae-code/natives-darwin-arm64";
85
+ const DIAGNOSTIC_EXPORT = "diagnosticSnapshotOpen";
86
+ const READ_CHUNK_BYTES = 1 << 20;
87
+
88
+ const unsupported = { ok: false, reason: "unsupported" };
89
+
90
+ /**
91
+ * The one admitted runtime tuple. Node, another Bun build or another OS/arch is
92
+ * unsupported: the observation contract was only established for this tuple.
93
+ *
94
+ * @param {{ platform?: string; arch?: string; bunVersion?: string }} runtime
95
+ * @returns {boolean}
96
+ */
97
+ export function supportedRuntime(runtime) {
98
+ if (runtime.platform !== "darwin") return false;
99
+ if (runtime.arch !== "arm64") return false;
100
+ return runtime.bunVersion === SUPPORTED_BUN_VERSION;
101
+ }
102
+
103
+ /**
104
+ * A compiled binary has no package directory to read from, so the artifact can
105
+ * only come from the existing versioned cache location.
106
+ *
107
+ * @returns {boolean}
108
+ */
109
+ function runningFromCompiledBinary() {
110
+ if (process.env.PI_COMPILED) return true;
111
+ const url = import.meta.url;
112
+ return url.includes("$bunfs") || url.includes("~BUN") || url.includes("%7EBUN");
113
+ }
114
+
115
+ /**
116
+ * The existing fixed trusted cache location for this package version.
117
+ *
118
+ * @returns {string}
119
+ */
120
+ function cachedArtifactPath() {
121
+ const dataHome = process.env.XDG_DATA_HOME;
122
+ const base =
123
+ dataHome && fs.existsSync(nodePath.join(dataHome, "gjc"))
124
+ ? nodePath.join(dataHome, "gjc", "natives")
125
+ : nodePath.join(os.homedir(), ".gjc", "natives");
126
+ return nodePath.join(base, packageManifest.version, ARTIFACT_BASENAME);
127
+ }
128
+
129
+ /**
130
+ * The package-owned expected digest for this artifact, or null when this package
131
+ * ships no trusted record for it.
132
+ *
133
+ * @returns {string | null}
134
+ */
135
+ function trustedDigest() {
136
+ if (trustedArtifacts?.version !== packageManifest.version) return null;
137
+ const digest = trustedArtifacts?.artifacts?.[ARTIFACT_BASENAME];
138
+ return typeof digest === "string" && /^[0-9a-f]{64}$/.test(digest) ? digest : null;
139
+ }
140
+
141
+ /**
142
+ * Select the single applicable layout. Exactly one distribution shape applies per
143
+ * install, and a failure of the selected layout is never a reason to try another.
144
+ *
145
+ * @param {NodeRequire} require
146
+ * @returns {string | null}
147
+ */
148
+ export function selectArtifactLayout(require) {
149
+ if (runningFromCompiledBinary()) return cachedArtifactPath();
150
+ const besideLoader = nodePath.join(
151
+ nodePath.dirname(require.resolve("./diagnostic-loader.js")),
152
+ ARTIFACT_BASENAME,
153
+ );
154
+ if (fs.existsSync(besideLoader)) return besideLoader;
155
+ try {
156
+ // Published installs ship the addon in the os/cpu-matched optional package,
157
+ // resolved through package metadata only.
158
+ const platformManifest = require.resolve(`${PLATFORM_PACKAGE}/package.json`);
159
+ return nodePath.join(nodePath.dirname(platformManifest), "native", ARTIFACT_BASENAME);
160
+ } catch {
161
+ return null;
162
+ }
163
+ }
164
+
165
+ /** Lazily bound read-only ACL symbols from the platform's own system library. */
166
+ let aclBinding;
167
+
168
+ /** The absolute system library that owns the platform ACL API. */
169
+ const SYSTEM_LIBRARY_PATH = "/usr/lib/libSystem.B.dylib";
170
+
171
+ /**
172
+ * Dynamic-loader overrides that can introduce another image into this process. They
173
+ * are applied before any of this code runs, so their presence cannot be undone here;
174
+ * the loader refuses instead, and never edits the environment.
175
+ */
176
+ const DYLD_OVERRIDE_VARIABLES = [
177
+ "DYLD_INSERT_LIBRARIES",
178
+ "DYLD_LIBRARY_PATH",
179
+ "DYLD_FRAMEWORK_PATH",
180
+ "DYLD_FALLBACK_LIBRARY_PATH",
181
+ "DYLD_FALLBACK_FRAMEWORK_PATH",
182
+ "DYLD_VERSIONED_LIBRARY_PATH",
183
+ "DYLD_VERSIONED_FRAMEWORK_PATH",
184
+ ];
185
+
186
+ /**
187
+ * Whether this process runs with a dynamic-loader override in effect.
188
+ *
189
+ * @returns {boolean}
190
+ */
191
+ function dyldOverridePresent() {
192
+ return DYLD_OVERRIDE_VARIABLES.some(name => {
193
+ const value = process.env[name];
194
+ return typeof value === "string" && value !== "";
195
+ });
196
+ }
197
+
198
+ /**
199
+ * Bind the read-only macOS ACL API.
200
+ *
201
+ * Only `acl_get_fd`, `acl_get_entry` and `acl_free` are bound, all of them
202
+ * read-only: nothing here can set or change an ACL. The library is the system's own
203
+ * `libSystem.B.dylib`, resolved by the dynamic loader, which is the same trust
204
+ * anchor the ordinary native loader already relies on for platform calls.
205
+ *
206
+ * @returns {{ aclGetFd: (fd: number) => number | bigint, aclGetEntry: (acl: number | bigint, id: number, out: unknown) => number, aclFree: (acl: number | bigint) => number } | null}
207
+ */
208
+ function aclSymbols() {
209
+ if (aclBinding !== undefined) return aclBinding;
210
+ aclBinding = null;
211
+ if (process.platform !== "darwin" || typeof Bun === "undefined") return aclBinding;
212
+ // Refuse before any dlopen: an override could have introduced another image, and
213
+ // inspecting the library after loading it cannot undo an initializer.
214
+ if (dyldOverridePresent()) return aclBinding;
215
+ try {
216
+ const require = createRequire(import.meta.url);
217
+ const { dlopen, FFIType, ptr, read, toArrayBuffer } = require("bun:ffi");
218
+ const library = dlopen(SYSTEM_LIBRARY_PATH, {
219
+ // `acl_get_fd` is Apple's wrapper for this call; the `_np` form is used so the
220
+ // ACL type is explicit at the call site.
221
+ acl_get_fd_np: { args: [FFIType.i32, FFIType.i32], returns: FFIType.ptr },
222
+ acl_get_entry: { args: [FFIType.ptr, FFIType.i32, FFIType.ptr], returns: FFIType.i32 },
223
+ acl_free: { args: [FFIType.ptr], returns: FFIType.i32 },
224
+ // Read-only entry introspection for the approved single-ACE exception.
225
+ acl_valid: { args: [FFIType.ptr], returns: FFIType.i32 },
226
+ // Full permission mask and the bounded external representation: these are what
227
+ // make "nothing but DELETE" and "exactly one entry" complete statements rather
228
+ // than a walk over the bits we happened to enumerate.
229
+ acl_get_permset_mask_np: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.i32 },
230
+ acl_size: { args: [FFIType.ptr], returns: FFIType.i64 },
231
+ acl_copy_ext_native: { args: [FFIType.ptr, FFIType.ptr, FFIType.i64], returns: FFIType.i64 },
232
+ __error: { args: [], returns: FFIType.ptr },
233
+ });
234
+ aclBinding = {
235
+ aclGetFd: fd => library.symbols.acl_get_fd_np(fd, ACL_TYPE_EXTENDED),
236
+ aclGetEntry: (acl, id, out) => library.symbols.acl_get_entry(acl, id, ptr(out)),
237
+ aclFree: acl => library.symbols.acl_free(acl),
238
+ aclValid: acl => library.symbols.acl_valid(acl),
239
+ readPointer: view => Number(view[0]),
240
+ aclGetPermsetMask: (entry, out) => library.symbols.acl_get_permset_mask_np(entry, ptr(out)),
241
+ aclSize: acl => Number(library.symbols.acl_size(acl)),
242
+ aclCopyExtNative: (buffer, acl, size) =>
243
+ Number(library.symbols.acl_copy_ext_native(ptr(buffer), acl, size)),
244
+ errno: () => {
245
+ const pointer = library.symbols.__error();
246
+ return pointer ? read.i32(pointer, 0) : null;
247
+ },
248
+ resetErrno: () => {
249
+ const pointer = library.symbols.__error();
250
+ if (!pointer) return false;
251
+ // Clearing errno at the call boundary keeps a stale value from a previous,
252
+ // unrelated call out of the classification below.
253
+ new DataView(toArrayBuffer(pointer, 0, 4)).setInt32(0, 0, true);
254
+ return true;
255
+ },
256
+ };
257
+ } catch {
258
+ aclBinding = null;
259
+ }
260
+ return aclBinding;
261
+ }
262
+
263
+ /** First-entry selector of the platform ACL iteration API. */
264
+ const ACL_FIRST_ENTRY = 0;
265
+
266
+ /** macOS ACL type selector for the extended (NFSv4-style) ACL. */
267
+ const ACL_TYPE_EXTENDED = 0x00000100;
268
+
269
+ /** `errno` value the platform reports when a file simply has no extended ACL. */
270
+ const ACL_ERRNO_NO_ACL = 2;
271
+
272
+ /** Selector for the entry after the first one. */
273
+ const ACL_NEXT_ENTRY = -1;
274
+
275
+ /** Selector for the last entry. */
276
+ const ACL_LAST_ENTRY = -2;
277
+
278
+ /** `errno` the platform reports when iteration has reached the end. */
279
+ const ACL_ERRNO_END_OF_ITERATION = 22;
280
+
281
+ /**
282
+ * Bounded external ACL representation (`kauth_filesec`, magic `0x012cc16d`).
283
+ *
284
+ * Layout from the platform SDK (`sys/kauth.h`): `u32 magic`, `guid_t owner`,
285
+ * `guid_t group`, then `struct kauth_acl { u32 entrycount; u32 flags; kauth_ace ace[] }`
286
+ * with `struct kauth_ace { guid_t applicable; u32 flags; u32 rights }`. A one-entry ACL
287
+ * is therefore exactly 68 bytes, which was confirmed against a real ACL on this runtime
288
+ * before this parser was relied on.
289
+ */
290
+ const KAUTH_FILESEC_MAGIC = 0x012cc16d;
291
+ const KAUTH_FILESEC_HEADER_BYTES = 44;
292
+ const KAUTH_ACE_BYTES = 24;
293
+ const KAUTH_ACL_MAX_ENTRIES = 128;
294
+ const KAUTH_ACE_DENY_ONLY = 2;
295
+ const KAUTH_VNODE_DELETE_ONLY = 1 << 4;
296
+
297
+ /**
298
+ * Parse the bounded external representation. Every structural fact is validated: total
299
+ * length, magic, entry count against the byte length and the platform maximum. Nothing
300
+ * is reinterpreted beyond that documented layout.
301
+ *
302
+ * @param {Uint8Array} bytes
303
+ * @returns {{ ok: true, count: number, headerFlags: number, entries: { guid: Uint8Array, flags: number, rights: number }[] } | { ok: false }}
304
+ */
305
+ export function parseAclRepresentation(bytes) {
306
+ if (!(bytes instanceof Uint8Array)) return { ok: false };
307
+ if (bytes.byteLength < KAUTH_FILESEC_HEADER_BYTES) return { ok: false };
308
+ const payload = bytes.byteLength - KAUTH_FILESEC_HEADER_BYTES;
309
+ if (payload % KAUTH_ACE_BYTES !== 0) return { ok: false };
310
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
311
+ if (view.getUint32(0, true) !== KAUTH_FILESEC_MAGIC) return { ok: false };
312
+ const count = view.getUint32(36, true);
313
+ if (count !== payload / KAUTH_ACE_BYTES) return { ok: false };
314
+ if (count > KAUTH_ACL_MAX_ENTRIES) return { ok: false };
315
+ const entries = [];
316
+ for (let index = 0; index < count; index += 1) {
317
+ const base = KAUTH_FILESEC_HEADER_BYTES + index * KAUTH_ACE_BYTES;
318
+ entries.push({
319
+ guid: bytes.slice(base, base + 16),
320
+ flags: view.getUint32(base + 16, true),
321
+ rights: view.getUint32(base + 20, true),
322
+ });
323
+ }
324
+ return { ok: true, count, headerFlags: view.getUint32(40, true), entries };
325
+ }
326
+
327
+ /**
328
+ * Decide a parsed representation against the approved exception. Full-field equality,
329
+ * never a walk over known bits: the ACL header flags must be entirely zero, the entry's
330
+ * flags must be exactly the deny tag, and its rights must be exactly DELETE.
331
+ *
332
+ * @param {unknown} parsed
333
+ * @returns {"approved-single-deny-delete" | "not-allowlisted" | "unproven"}
334
+ */
335
+ export function classifyAclRepresentation(parsed) {
336
+ if (typeof parsed !== "object" || parsed === null) return "unproven";
337
+ const record = /** @type {{ ok?: unknown; count?: unknown; headerFlags?: unknown; entries?: unknown }} */ (parsed);
338
+ if (record.ok !== true) return "unproven";
339
+ if (typeof record.count !== "number" || !Array.isArray(record.entries)) return "unproven";
340
+ if (record.count !== record.entries.length) return "unproven";
341
+ if (record.count !== 1) return "not-allowlisted";
342
+ if (record.headerFlags !== 0) return "not-allowlisted";
343
+ const entry = record.entries[0];
344
+ if (entry.flags !== KAUTH_ACE_DENY_ONLY) return "not-allowlisted";
345
+ if (entry.rights !== KAUTH_VNODE_DELETE_ONLY) return "not-allowlisted";
346
+ if (entry.guid.length !== EVERYONE_GUID.length) return "unproven";
347
+ return entry.guid.every((byte, index) => byte === EVERYONE_GUID[index])
348
+ ? "approved-single-deny-delete"
349
+ : "not-allowlisted";
350
+ }
351
+
352
+ /** Raw well-known everyone GUID (`ABCDEFAB-CDEF-ABCD-EFAB-CDEF0000000C`). */
353
+ const EVERYONE_GUID = Uint8Array.from([
354
+ 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x0c,
355
+ ]);
356
+
357
+ /**
358
+ * Classify one ACL probe. Pure, so it behaves identically on every platform and can
359
+ * be tested without the platform API.
360
+ *
361
+ * Absence must be proven positively: a null handle counts only with the platform's
362
+ * "no such ACL" errno. A first entry means an ACE exists. `acl_get_entry` returning
363
+ * -1 is end-of-iteration OR a failure -- an invalid ACL reports EINVAL the same way
364
+ * -- so it is `unproven`, never "empty".
365
+ *
366
+ * @param {{ handle: number; errno: number | null; firstEntry: number | null }} probe
367
+ * @returns {"absent" | "present" | "unproven"}
368
+ */
369
+ export function classifyAclProbe(probe) {
370
+ if (!probe.handle) return probe.errno === ACL_ERRNO_NO_ACL ? "absent" : "unproven";
371
+ if (probe.firstEntry === 0) return "present";
372
+ return "unproven";
373
+ }
374
+
375
+ /**
376
+ * Classify the extended ACL of one open descriptor.
377
+ *
378
+ * `absent` requires a positive observation that the ACL holds no entry; a first
379
+ * entry means an ACE exists and the artifact is refused; anything the API cannot
380
+ * answer is `unproven`, never silently treated as clean.
381
+ *
382
+ * @param {number} fd
383
+ * @returns {"absent" | "present" | "unproven"}
384
+ */
385
+ export function inspectAclOnDescriptor(fd) {
386
+ const symbols = aclSymbols();
387
+ if (symbols === null) return "unproven";
388
+ let acl;
389
+ try {
390
+ // Reset at the call boundary, then capture immediately after the call, so the
391
+ // value classified below belongs to this call and not to an earlier one.
392
+ if (!symbols.resetErrno()) return "unproven";
393
+ acl = symbols.aclGetFd(fd);
394
+ } catch {
395
+ return "unproven";
396
+ }
397
+ const handle = typeof acl === "bigint" ? Number(acl) : acl;
398
+ if (!handle) {
399
+ return classifyAclProbe({ handle: 0, errno: symbols.errno(), firstEntry: null });
400
+ }
401
+ try {
402
+ const entry = new BigUint64Array(1);
403
+ if (!symbols.resetErrno()) return "unproven";
404
+ const result = symbols.aclGetEntry(acl, ACL_FIRST_ENTRY, entry);
405
+ return classifyAclProbe({ handle, errno: symbols.errno(), firstEntry: result });
406
+ } catch {
407
+ return "unproven";
408
+ } finally {
409
+ try {
410
+ symbols.aclFree(acl);
411
+ } catch {
412
+ // Nothing to recover; the allocation is owned by the platform call above.
413
+ }
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Classify a trusted ancestor's ACL: absent, the single approved deny-delete ACE, or
419
+ * fail closed.
420
+ *
421
+ * @param {number} fd
422
+ * @returns {"absent" | "approved-single-deny-delete" | "not-allowlisted" | "unproven"}
423
+ */
424
+ export function inspectAncestorAclOnDescriptor(fd) {
425
+ const classification = inspectAclOnDescriptor(fd);
426
+ if (classification === "absent") return "absent";
427
+ if (classification === "unproven") return "unproven";
428
+ const symbols = aclSymbols();
429
+ if (symbols === null) return "unproven";
430
+ let acl;
431
+ try {
432
+ if (!symbols.resetErrno()) return "unproven";
433
+ acl = symbols.aclGetFd(fd);
434
+ } catch {
435
+ return "unproven";
436
+ }
437
+ const handle = typeof acl === "bigint" ? Number(acl) : acl;
438
+ if (!handle) return "unproven";
439
+ try {
440
+ if (symbols.aclValid(acl) !== 0) return "unproven";
441
+
442
+ // 1. Authoritative completeness: the bounded external representation carries the
443
+ // exact entry count, the whole ACL header flag field and each entry's whole
444
+ // flag and rights fields.
445
+ if (!symbols.resetErrno()) return "unproven";
446
+ const size = symbols.aclSize(acl);
447
+ if (!Number.isInteger(size) || size <= 0 || size > 1 << 20) return "unproven";
448
+ const buffer = new Uint8Array(size);
449
+ if (!symbols.resetErrno()) return "unproven";
450
+ const copied = symbols.aclCopyExtNative(buffer, acl, size);
451
+ if (copied !== size) return "unproven";
452
+ const decision = classifyAclRepresentation(parseAclRepresentation(buffer));
453
+ if (decision !== "approved-single-deny-delete") return decision;
454
+
455
+ // 2. Independent cardinality cross-check on the live ACL. Each failable call
456
+ // resets errno immediately before and captures it immediately after, before
457
+ // any other FFI call or free, so a stale value can never stand in for a real
458
+ // one. `-1` alone proves nothing: the terminator must also report the expected
459
+ // errno, and FIRST and LAST must be the very same entry.
460
+ const firstOut = new BigUint64Array(1);
461
+ if (!symbols.resetErrno()) return "unproven";
462
+ const first = symbols.aclGetEntry(acl, ACL_FIRST_ENTRY, firstOut);
463
+ const firstErrno = symbols.errno();
464
+ if (first !== 0 || firstErrno === null) return "unproven";
465
+ const firstEntry = symbols.readPointer(firstOut);
466
+ if (!firstEntry) return "unproven";
467
+
468
+ const lastOut = new BigUint64Array(1);
469
+ if (!symbols.resetErrno()) return "unproven";
470
+ const last = symbols.aclGetEntry(acl, ACL_LAST_ENTRY, lastOut);
471
+ const lastErrno = symbols.errno();
472
+ if (last !== 0 || lastErrno === null) return "unproven";
473
+ const lastEntry = symbols.readPointer(lastOut);
474
+ if (!lastEntry || lastEntry !== firstEntry) return "unproven";
475
+
476
+ const nextOut = new BigUint64Array(1);
477
+ if (!symbols.resetErrno()) return "unproven";
478
+ const next = symbols.aclGetEntry(acl, ACL_NEXT_ENTRY, nextOut);
479
+ const nextErrno = symbols.errno();
480
+ if (next === 0) return "not-allowlisted";
481
+ if (next !== -1 || nextErrno !== ACL_ERRNO_END_OF_ITERATION) return "unproven";
482
+
483
+ // 3. Full permission mask from the platform's own mask getter, as a second,
484
+ // independent statement that nothing but DELETE is granted or denied.
485
+ const maskOut = new BigUint64Array(1);
486
+ if (!symbols.resetErrno()) return "unproven";
487
+ const maskResult = symbols.aclGetPermsetMask(firstEntry, maskOut);
488
+ const maskErrno = symbols.errno();
489
+ if (maskResult !== 0 || maskErrno === null) return "unproven";
490
+ if (maskOut[0] !== BigInt(KAUTH_VNODE_DELETE_ONLY)) return "not-allowlisted";
491
+
492
+ return "approved-single-deny-delete";
493
+ } catch {
494
+ return "unproven";
495
+ } finally {
496
+ try {
497
+ symbols.aclFree(acl);
498
+ } catch {
499
+ // The allocation belongs to the call above; nothing else to release.
500
+ }
501
+ }
502
+ }
503
+
504
+ /**
505
+ * Classify the extended ACL of a path component without following symlinks.
506
+ *
507
+ * @param {string} target
508
+ * @param {boolean} isDirectory
509
+ * @returns {"absent" | "present" | "unproven"}
510
+ */
511
+ function inspectAclOnPath(target, isDirectory) {
512
+ let fd = -1;
513
+ try {
514
+ const flags =
515
+ fs.constants.O_RDONLY |
516
+ (fs.constants.O_NOFOLLOW ?? 0) |
517
+ (isDirectory ? (fs.constants.O_DIRECTORY ?? 0) : 0);
518
+ fd = fs.openSync(target, flags);
519
+ return inspectAclOnDescriptor(fd);
520
+ } catch {
521
+ return "unproven";
522
+ } finally {
523
+ if (fd >= 0) closeDescriptor(fd);
524
+ }
525
+ }
526
+
527
+ /**
528
+ * Admission for one component of the artifact path.
529
+ *
530
+ * Ownership matters as much as the permission bits: a directory owned by another
531
+ * account can be rewritten by that account whatever its mode says, so only the
532
+ * current user or root may own any component. Group/other write is refused, and a
533
+ * symlink anywhere on the path is refused because it can be repointed.
534
+ *
535
+ * @param {{ uid: number; gid: number; mode: number; isSymbolicLink: boolean; isDirectory: boolean }} component
536
+ * @param {number} euid
537
+ * @returns {boolean}
538
+ */
539
+ export function namespaceComponentAdmitted(component, euid) {
540
+ if (component.isSymbolicLink) return false;
541
+ if (component.uid !== euid && component.uid !== 0) return false;
542
+ if ((component.mode & 0o022) !== 0) return false;
543
+ return component.isDirectory;
544
+ }
545
+
546
+ /**
547
+ * Prove the namespace that leads to the artifact: a regular-file leaf, and every
548
+ * ancestor owned by the current user or root with no ambient write and no symlink.
549
+ *
550
+ * @param {string} file
551
+ * @returns {boolean}
552
+ */
553
+ function trustedNamespace(file) {
554
+ // Read-only inspection through the OS stat and ACL interfaces only.
555
+ //
556
+ // The artifact file and its own directory -- the artifact namespace ANCHOR, which is
557
+ // a different path from `agentDir` -- stay strict. Only trusted ancestors strictly
558
+ // above the anchor may carry the approved single everyone DENY DELETE entry.
559
+ const euid = typeof process.geteuid === "function" ? process.geteuid() : -1;
560
+ if (euid < 0) return false;
561
+ const anchor = nodePath.dirname(file);
562
+ let current = file;
563
+ for (;;) {
564
+ const stat = fs.lstatSync(current);
565
+ const component = {
566
+ uid: stat.uid,
567
+ gid: stat.gid,
568
+ mode: stat.mode,
569
+ isSymbolicLink: stat.isSymbolicLink(),
570
+ isDirectory: current === file ? true : stat.isDirectory(),
571
+ };
572
+ if (current === file && !stat.isFile()) return false;
573
+ if (!namespaceComponentAdmitted(component, euid)) return false;
574
+ // An ACE can grant another account write access that `st_mode` never shows.
575
+ const strict = current === file || current === anchor;
576
+ if (strict) {
577
+ if (inspectAclOnPath(current, current !== file) !== "absent") return false;
578
+ } else if (!ancestorAclAdmitted(current)) {
579
+ return false;
580
+ }
581
+ const parent = nodePath.dirname(current);
582
+ if (parent === current) return true;
583
+ current = parent;
584
+ }
585
+ }
586
+
587
+ /**
588
+ * Whether a trusted ancestor above the anchor is admissible: no ACL, or exactly one
589
+ * validated non-inheriting everyone DENY DELETE entry.
590
+ *
591
+ * @param {string} directory
592
+ * @returns {boolean}
593
+ */
594
+ function ancestorAclAdmitted(directory) {
595
+ let fd = -1;
596
+ try {
597
+ fd = fs.openSync(
598
+ directory,
599
+ fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0) | (fs.constants.O_DIRECTORY ?? 0),
600
+ );
601
+ const decision = inspectAncestorAclOnDescriptor(fd);
602
+ return decision === "absent" || decision === "approved-single-deny-delete";
603
+ } catch {
604
+ return false;
605
+ } finally {
606
+ if (fd >= 0) closeDescriptor(fd);
607
+ }
608
+ }
609
+
610
+ /**
611
+ * @param {number} fd
612
+ * @returns {string}
613
+ */
614
+ function digestOfDescriptor(fd) {
615
+ const hash = createHash("sha256");
616
+ const buffer = Buffer.allocUnsafe(READ_CHUNK_BYTES);
617
+ let position = 0;
618
+ for (;;) {
619
+ const read = fs.readSync(fd, buffer, 0, buffer.length, position);
620
+ if (read <= 0) break;
621
+ hash.update(buffer.subarray(0, read));
622
+ position += read;
623
+ }
624
+ return hash.digest("hex");
625
+ }
626
+
627
+ /**
628
+ * Live verification authority, keyed by an unguessable token. The descriptor never
629
+ * leaves this module, and consuming an entry removes the authority: a handle is
630
+ * single use, so a stale token can neither activate a recycled descriptor number
631
+ * nor close a descriptor that now belongs to something else.
632
+ *
633
+ * @type {Map<string, { fd: number; file: string; identity: string }>}
634
+ */
635
+ const verifiedArtifacts = new Map();
636
+
637
+ /**
638
+ * @param {import("node:fs").Stats} stat
639
+ * @returns {string}
640
+ */
641
+ function identityOfStat(stat) {
642
+ // Content- and authority-affecting fields only. `nlink` and `ctime` are
643
+ // deliberately excluded: unlinking or renaming the PATH away leaves the verified
644
+ // bytes untouched, and refusing that would break the pinned-descriptor guarantee
645
+ // this loader exists to provide. A write to the bytes bumps `mtime`, and a
646
+ // permission or ownership change shows up in `mode`/`uid`/`gid`.
647
+ return JSON.stringify([stat.dev, stat.ino, stat.size, stat.mtimeMs, stat.uid, stat.gid, stat.mode]);
648
+ }
649
+
650
+ /**
651
+ * Take the authority for a token, if any. The entry is removed, so every handle is
652
+ * consumed exactly once.
653
+ *
654
+ * @param {unknown} handle
655
+ * @returns {{ fd: number; file: string; identity: string } | null}
656
+ */
657
+ function consumeHandle(handle) {
658
+ if (typeof handle !== "object" || handle === null) return null;
659
+ const token = /** @type {{ token?: unknown }} */ (handle).token;
660
+ if (typeof token !== "string") return null;
661
+ const entry = verifiedArtifacts.get(token);
662
+ if (entry === undefined) return null;
663
+ verifiedArtifacts.delete(token);
664
+ return entry;
665
+ }
666
+
667
+ /**
668
+ * Verify one artifact and retain the descriptor the verified bytes came from.
669
+ *
670
+ * The returned handle is opaque and single use: it carries no descriptor, and
671
+ * either `activateVerifiedArtifact` or `closeVerifiedArtifact` consumes it.
672
+ *
673
+ * @param {string} file
674
+ * @returns {{ ok: true, token: string } | { ok: false, reason: "unsupported" }}
675
+ */
676
+ export function verifyDiagnosticArtifact(file) {
677
+ const expectedDigest = trustedDigest();
678
+ if (expectedDigest === null) return unsupported;
679
+ let fd = -1;
680
+ try {
681
+ if (!trustedNamespace(file)) return unsupported;
682
+ fd = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
683
+ const stat = fs.fstatSync(fd);
684
+ if (!stat.isFile() || stat.size === 0) return unsupported;
685
+ if ((stat.mode & 0o022) !== 0) return unsupported;
686
+ if (stat.uid !== process.geteuid?.() && stat.uid !== 0) return unsupported;
687
+ if (inspectAclOnDescriptor(fd) !== "absent") return unsupported;
688
+ if (digestOfDescriptor(fd) !== expectedDigest) return unsupported;
689
+ const token = createHash("sha256")
690
+ .update(`${file}:${randomUUID()}:${stat.ino}`)
691
+ .digest("hex");
692
+ verifiedArtifacts.set(token, { fd, file, identity: identityOfStat(fs.fstatSync(fd)) });
693
+ fd = -1;
694
+ return { ok: true, token };
695
+ } catch {
696
+ return unsupported;
697
+ } finally {
698
+ if (fd >= 0) closeDescriptor(fd);
699
+ }
700
+ }
701
+
702
+ /**
703
+ * @param {number} fd
704
+ * @returns {void}
705
+ */
706
+ function closeDescriptor(fd) {
707
+ try {
708
+ fs.closeSync(fd);
709
+ } catch {
710
+ // Nothing to recover: the descriptor is already gone.
711
+ }
712
+ }
713
+
714
+ /**
715
+ * Release a verified handle without activating it. Unknown, fabricated or already
716
+ * consumed handles are ignored: they hold no authority and must not be able to
717
+ * close a descriptor number that now belongs to something else.
718
+ *
719
+ * @param {unknown} handle
720
+ * @returns {void}
721
+ */
722
+ export function closeVerifiedArtifact(handle) {
723
+ const entry = consumeHandle(handle);
724
+ if (entry === null) return;
725
+ closeDescriptor(entry.fd);
726
+ }
727
+
728
+ /**
729
+ * Activate the verified bytes through their retained descriptor.
730
+ *
731
+ * The handle's authority is consumed first, so it cannot be replayed. Before the
732
+ * descriptor is handed to the runtime loader its metadata is re-proved against the
733
+ * snapshot taken during verification: any write that landed in between — including
734
+ * a byte-identical rewrite, which leaves the digest intact — invalidates it.
735
+ *
736
+ * `process.dlopen` is given `/dev/fd/<fd>`, which on this runtime reopens the
737
+ * descriptor's own vnode, so replacing the original pathname cannot redirect the
738
+ * load. The descriptor is always closed before returning.
739
+ *
740
+ * @param {unknown} handle
741
+ * @returns {{ ok: true, value: { openDiagnosticSnapshot: (agentDir: string, budgetMs: number) => unknown } } | { ok: false, reason: "unsupported" }}
742
+ */
743
+ export function activateVerifiedArtifact(handle) {
744
+ const entry = consumeHandle(handle);
745
+ if (entry === null) return unsupported;
746
+ try {
747
+ if (typeof process.dlopen !== "function") return unsupported;
748
+ if (identityOfStat(fs.fstatSync(entry.fd)) !== entry.identity) return unsupported;
749
+ const module = { exports: {} };
750
+ process.dlopen(module, `/dev/fd/${entry.fd}`);
751
+ const binding = module.exports;
752
+ if (!validateDiagnosticBinding(binding, packageManifest.version)) return unsupported;
753
+ return {
754
+ ok: true,
755
+ value: {
756
+ openDiagnosticSnapshot: (agentDir, budgetMs) => binding[DIAGNOSTIC_EXPORT](agentDir, budgetMs),
757
+ },
758
+ };
759
+ } catch {
760
+ return unsupported;
761
+ } finally {
762
+ closeDescriptor(entry.fd);
763
+ }
764
+ }
765
+
766
+ /**
767
+ * Accept a loaded binding only when it carries the diagnostic entry point AND
768
+ * reports this package's exact build version. This is a consistency check on top
769
+ * of the trusted-digest proof, never the trust anchor itself.
770
+ *
771
+ * @param {unknown} binding
772
+ * @param {string} expectedVersion
773
+ * @returns {boolean}
774
+ */
775
+ export function validateDiagnosticBinding(binding, expectedVersion) {
776
+ if (typeof binding !== "object" || binding === null) return false;
777
+ const candidate = /** @type {Record<string, unknown>} */ (binding);
778
+ if (typeof candidate[DIAGNOSTIC_EXPORT] !== "function") return false;
779
+ if (typeof candidate.nativeBuildInfo !== "function") return false;
780
+ try {
781
+ const info = /** @type {() => { version?: unknown }} */ (candidate.nativeBuildInfo)();
782
+ return info?.version === expectedVersion;
783
+ } catch {
784
+ return false;
785
+ }
786
+ }
787
+
788
+ /**
789
+ * Load the read-only diagnostic native entry point.
790
+ *
791
+ * @param {{ platform?: string; arch?: string; bunVersion?: string }} [runtimeOverride] optional
792
+ * additional runtime assertion; it can only narrow admission.
793
+ * @returns {{ ok: true, value: { openDiagnosticSnapshot: (agentDir: string, budgetMs: number) => unknown } } | { ok: false, reason: "unsupported" }}
794
+ */
795
+ export function loadDiagnosticNativeReadOnly(runtimeOverride) {
796
+ const actualRuntime = {
797
+ platform: process.platform,
798
+ arch: process.arch,
799
+ bunVersion: typeof Bun === "undefined" ? undefined : Bun.version,
800
+ };
801
+ // The decision always uses the ACTUAL runtime; an optional descriptor can only
802
+ // narrow admission further (tests inject unsupported tuples), never widen it.
803
+ if (!supportedRuntime(actualRuntime)) return unsupported;
804
+ if (runtimeOverride !== undefined && !supportedRuntime(runtimeOverride)) return unsupported;
805
+ try {
806
+ const require = createRequire(import.meta.url);
807
+ const layout = selectArtifactLayout(require);
808
+ if (layout === null) return unsupported;
809
+ const verified = verifyDiagnosticArtifact(layout);
810
+ if (!verified.ok) return unsupported;
811
+ return activateVerifiedArtifact(verified);
812
+ } catch {
813
+ return unsupported;
814
+ }
815
+ }
package/native/index.d.ts CHANGED
@@ -113,6 +113,22 @@ export declare class MacAppearanceObserver {
113
113
  stop(): void
114
114
  }
115
115
 
116
+ /**
117
+ * Exclusive read-only lease over one already published broker document.
118
+ *
119
+ * The lease exposes no descriptor, no path, no write method and no publication
120
+ * authority: it can only copy bounded bytes, re-prove the retained descriptor
121
+ * and name edges, and close. `close` is idempotent and never throws; every use
122
+ * after close reports `unsafe_discovery`.
123
+ */
124
+ export declare class NativeDiagnosticSnapshot {
125
+ get ok(): boolean
126
+ get reason(): string | null
127
+ read(): NativeDiagnosticSnapshotRead
128
+ revalidate(): NativeDiagnosticSnapshotStatus
129
+ close(): void
130
+ }
131
+
116
132
  /** Retained no-follow authority for the SDK publication namespace. */
117
133
  export declare class NativeRetainedBrokerPublication {
118
134
  /**
@@ -615,8 +631,7 @@ export declare class TtyWriter {
615
631
  * Enqueue terminal output; never blocks. Returns the total bytes now
616
632
  * pending (including this chunk).
617
633
  *
618
- * Reads the JS string as UTF-16 and transcodes it with `xutf` straight into
619
- * the shared back buffer.
634
+ * Reads the JS string as UTF-8 directly into the shared back buffer.
620
635
  */
621
636
  write(data: string): number
622
637
  /** Bytes accepted but not yet written to the terminal. */
@@ -672,7 +687,7 @@ export declare function __piNativesPublishOutcomeV1(): void
672
687
  * `packages/natives/native/index.js` (which derives the name from
673
688
  * `package.json#version`).
674
689
  */
675
- export declare function __piNativesV0_18_5(): void
690
+ export declare function __piNativesV0_18_7(): void
676
691
 
677
692
  /**
678
693
  * Apply conservative pre-execution rewrites to a bash command.
@@ -990,6 +1005,14 @@ export interface DependentIdleDeliveryResult {
990
1005
  */
991
1006
  export declare function detectMacOSAppearance(): MacOSAppearance | null
992
1007
 
1008
+ /**
1009
+ * Open a read-only lease over the fixed broker publication under `agentDir`.
1010
+ *
1011
+ * The caller supplies only the agent directory: the publication name, the
1012
+ * ancestor policy and the read budget are fixed by this adapter.
1013
+ */
1014
+ export declare function diagnosticSnapshotOpen(agentDir: string, budgetMs: number): NativeDiagnosticSnapshot
1015
+
993
1016
  /** One jsdiff change object: a run of added, removed, or common tokens. */
994
1017
  export interface DiffChange {
995
1018
  /** Joined token text for this run (lines keep their `
@@ -2079,6 +2102,19 @@ export type NativeCanonicalDirectoryIdentity =
2079
2102
  code: "not_found" | "not_directory" | "not_utf8" | "network_unsupported" | "identity_unavailable" | "io_error";
2080
2103
  }
2081
2104
 
2105
+ /** Bytes copied out of one read-only broker publication observation. */
2106
+ export interface NativeDiagnosticSnapshotRead {
2107
+ ok: boolean
2108
+ reason?: string
2109
+ bytes?: Uint8Array
2110
+ }
2111
+
2112
+ /** Outcome of a read-only publication revalidation. */
2113
+ export interface NativeDiagnosticSnapshotStatus {
2114
+ ok: boolean
2115
+ reason?: string
2116
+ }
2117
+
2082
2118
  export interface NativeDirectoryParentIdentity {
2083
2119
  dev: bigint
2084
2120
  ino: bigint
package/native/index.js CHANGED
@@ -21,6 +21,7 @@ export const ComputerController = nativeBindings.ComputerController;
21
21
  export const DiffStream = nativeBindings.DiffStream;
22
22
  export const DoctorJournalAuthority = nativeBindings.DoctorJournalAuthority;
23
23
  export const MacAppearanceObserver = nativeBindings.MacAppearanceObserver;
24
+ export const NativeDiagnosticSnapshot = nativeBindings.NativeDiagnosticSnapshot;
24
25
  export const NativeRetainedBrokerPublication = nativeBindings.NativeRetainedBrokerPublication;
25
26
  export const NotificationServer = nativeBindings.NotificationServer;
26
27
  export const PowerAssertion = nativeBindings.PowerAssertion;
@@ -34,7 +35,7 @@ export const TtyWriter = nativeBindings.TtyWriter;
34
35
  // functions
35
36
  export const __gjcInstallTokioRuntime = nativeBindings.__gjcInstallTokioRuntime;
36
37
  export const __piNativesPublishOutcomeV1 = nativeBindings.__piNativesPublishOutcomeV1;
37
- export const __piNativesV0_18_5 = nativeBindings.__piNativesV0_18_5;
38
+ export const __piNativesV0_18_7 = nativeBindings.__piNativesV0_18_7;
38
39
  export const applyBashFixups = nativeBindings.applyBashFixups;
39
40
  export const applyOwnerOnlyFdSecurity = nativeBindings.applyOwnerOnlyFdSecurity;
40
41
  export const applyOwnerOnlyPathSecurity = nativeBindings.applyOwnerOnlyPathSecurity;
@@ -45,6 +46,7 @@ export const computerScreenshot = nativeBindings.computerScreenshot;
45
46
  export const copyToClipboard = nativeBindings.copyToClipboard;
46
47
  export const currentExecutablePath = nativeBindings.currentExecutablePath;
47
48
  export const detectMacOSAppearance = nativeBindings.detectMacOSAppearance;
49
+ export const diagnosticSnapshotOpen = nativeBindings.diagnosticSnapshotOpen;
48
50
  export const diffLineRuns = nativeBindings.diffLineRuns;
49
51
  export const diffLines = nativeBindings.diffLines;
50
52
  export const diffWords = nativeBindings.diffWords;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gajae-code/natives",
3
- "version": "0.18.5",
3
+ "version": "0.18.7",
4
4
  "description": "Native Rust bindings for grep, clipboard, image processing, syntax highlighting, PTY, and shell operations via N-API",
5
5
  "type": "module",
6
6
  "homepage": "https://gajae-code.com",
@@ -58,21 +58,29 @@
58
58
  "native/index.d.ts",
59
59
  "native/loader-state.js",
60
60
  "native/loader-state.d.ts",
61
+ "native/diagnostic-loader.d.ts",
62
+ "native/diagnostic-loader.js",
63
+ "native/diagnostic-artifact.json",
61
64
  "native/embedded-addon.js",
62
65
  "README.md"
63
66
  ],
64
67
  "optionalDependencies": {
65
- "@gajae-code/natives-darwin-arm64": "0.18.5",
66
- "@gajae-code/natives-darwin-x64": "0.18.5",
67
- "@gajae-code/natives-linux-arm64": "0.18.5",
68
- "@gajae-code/natives-linux-x64": "0.18.5",
69
- "@gajae-code/natives-win32-x64": "0.18.5"
68
+ "@gajae-code/natives-darwin-arm64": "0.18.7",
69
+ "@gajae-code/natives-darwin-x64": "0.18.7",
70
+ "@gajae-code/natives-linux-arm64": "0.18.7",
71
+ "@gajae-code/natives-linux-x64": "0.18.7",
72
+ "@gajae-code/natives-win32-x64": "0.18.7"
70
73
  },
71
74
  "exports": {
72
75
  ".": {
73
76
  "types": "./native/index.d.ts",
74
77
  "import": "./native/index.js",
75
78
  "default": "./native/index.js"
79
+ },
80
+ "./diagnostic-loader": {
81
+ "types": "./native/diagnostic-loader.d.ts",
82
+ "import": "./native/diagnostic-loader.js",
83
+ "default": "./native/diagnostic-loader.js"
76
84
  }
77
85
  }
78
86
  }