@rightkit/release 0.2.134 → 0.2.136

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 (86) hide show
  1. package/README.md +62 -0
  2. package/addon-command.mjs +53 -34
  3. package/apple-profile-renew.mjs +30 -12
  4. package/artifact-storage.mjs +53 -12
  5. package/background-process.mjs +18 -0
  6. package/build-release.mjs +4 -3
  7. package/cache-command.mjs +4 -3
  8. package/cargo-contract.mjs +1 -1
  9. package/cargo-guard.mjs +4 -3
  10. package/cargo-registry-readback.mjs +51 -0
  11. package/cargo-target.mjs +3 -1
  12. package/cli/right-release.mjs +26 -0
  13. package/codesign-argv.mjs +64 -0
  14. package/codesign-environment.mjs +58 -0
  15. package/codesign-shim.mjs +51 -0
  16. package/committed-source.mjs +3 -3
  17. package/development.mjs +11 -9
  18. package/direct-bootstrap.mjs +19 -9
  19. package/dmg-build.mjs +117 -0
  20. package/dmg-layout.mjs +123 -0
  21. package/github-release.mjs +74 -37
  22. package/heavy-command.mjs +3 -2
  23. package/install-app.mjs +358 -0
  24. package/inventory-check.mjs +980 -0
  25. package/macos-notarization.mjs +24 -8
  26. package/macos-signing-session.mjs +50 -2
  27. package/managed-cargo.mjs +23 -19
  28. package/managed-host-secrets.mjs +30 -0
  29. package/managed-roots.mjs +39 -0
  30. package/model-promote.mjs +31 -37
  31. package/notarize-macos.mjs +5 -5
  32. package/notary-auth.mjs +4 -0
  33. package/osascript-guard.mjs +24 -0
  34. package/ownership-approval-command.mjs +15 -4
  35. package/ownership-approval.mjs +114 -49
  36. package/ownership-availability.mjs +25 -0
  37. package/ownership-catalog-config.json +18 -0
  38. package/ownership-catalog.mjs +94 -0
  39. package/ownership-command.mjs +17 -10
  40. package/ownership-declaration.json +15 -0
  41. package/ownership-default-policy.json +1 -11
  42. package/ownership-evidence.mjs +10 -8
  43. package/ownership-git-inventory.mjs +29 -1
  44. package/ownership-helper-paths.json +4 -4
  45. package/ownership-hooks.mjs +107 -0
  46. package/ownership-internal-owners.json +44 -0
  47. package/ownership-key-authority.mjs +112 -7
  48. package/ownership-key-command.mjs +76 -19
  49. package/ownership-key-trust.mjs +97 -9
  50. package/ownership-lockfiles.mjs +311 -0
  51. package/ownership-report-command.mjs +12 -3
  52. package/ownership-report.mjs +20 -17
  53. package/ownership-repositories.json +19 -0
  54. package/ownership-scan-worker.mjs +96 -11
  55. package/ownership-source.mjs +39 -0
  56. package/ownership-staged.mjs +193 -0
  57. package/ownership-verdict.mjs +74 -0
  58. package/ownership.mjs +215 -107
  59. package/package.json +1 -1
  60. package/protected-credentials.mjs +111 -0
  61. package/prune-r2.mjs +4 -3
  62. package/pub-upload.mjs +3 -85
  63. package/publication-artifacts.mjs +108 -0
  64. package/publication-gate.mjs +28 -7
  65. package/publish-cargo.mjs +47 -16
  66. package/publish-swift.mjs +46 -17
  67. package/publish-update.mjs +61 -39
  68. package/r2-verified-upload.mjs +41 -0
  69. package/registry-parity.mjs +5 -3
  70. package/release-state.mjs +1 -0
  71. package/release-token.mjs +2 -0
  72. package/release.mjs +2 -1
  73. package/rightkit-drift.mjs +3 -2
  74. package/rightkit-ownership.json +2028 -139
  75. package/rightkit-signing-agent.mjs +161 -19
  76. package/rightkit-sync.mjs +6 -2
  77. package/rightkit-versions.json +220 -106
  78. package/runtime-artifact-manifest.mjs +20 -6
  79. package/sign-updater.mjs +5 -0
  80. package/signing-agent-client.mjs +78 -22
  81. package/signing-agent-command.mjs +5 -4
  82. package/signing-agent-install.mjs +31 -4
  83. package/standalone-clone-verify.mjs +3 -2
  84. package/tauri-bundle-marker.mjs +3 -2
  85. package/upload-large.mjs +36 -82
  86. package/verifier.mjs +5 -1
@@ -1,26 +1,31 @@
1
1
  #!/usr/bin/env node
2
+ import { resolveManagedRoots } from './managed-roots.mjs';
2
3
  import * as fs from "node:fs";
3
4
  import net from "node:net";
4
5
  import os from "node:os";
5
6
  import path from "node:path";
6
7
  import { randomBytes, timingSafeEqual, randomUUID } from "node:crypto";
7
8
  import { fileURLToPath } from "node:url";
8
- import { spawnSync } from "./background-process.mjs";
9
+ import { spawn, spawnSync } from "./background-process.mjs";
10
+ import { parseCodesignArgv } from "./codesign-argv.mjs";
9
11
 
10
12
  export const AGENT_LABEL = "dev.rightkit.signing-agent";
11
13
  export const MAX_FRAME = 1024 * 1024;
14
+ export const NOTARIZATION_TIMEOUT_MS = 1800000;
15
+ export const MAX_NOTARIZATION_TIMEOUT_MS = 7200000;
12
16
  export class SigningAgentError extends Error {
13
17
  constructor(code, message) { super(message); this.name = "SigningAgentError"; this.code = code; }
14
18
  }
15
19
  const fail = (code, message) => { throw new SigningAgentError(code, message); };
16
20
  export function signingAgentPaths(home = os.userInfo().homedir) {
17
- const directory = path.join(home, "Library/Application Support/Orthic Labs/shared/signing-agent");
21
+ const directory = resolveManagedRoots({ platform: "darwin", home }).signingAgent;
18
22
  return { directory, socket: path.join(directory, "agent.sock"), token: path.join(directory, "token"),
19
23
  config: path.join(directory, "config.json"), runtime: path.join(directory, "runtime"),
20
24
  log: path.join(directory, "requests.jsonl"), plist: path.join(home, "Library/LaunchAgents", `${AGENT_LABEL}.plist`) };
21
25
  }
22
- export function defaultSigningRoots(home = os.userInfo().homedir, managedRoot = "/Volumes/D/.rightkit-managed") {
23
- return ["/Volumes/D/claude", managedRoot, "/Volumes/D/rightkit-build-control", path.join(home, "Library/Caches/RightSuite/release")];
26
+ export function defaultSigningRoots(home = os.userInfo().homedir, managedRoot = resolveManagedRoots({ home }).dataRoot, cargoTargetDirs = []) {
27
+ return [...new Set(["/Volumes/D/claude", managedRoot, "/Volumes/D/rightkit-build-control",
28
+ path.join(home, "Library/Caches/RightSuite/release"), "/tmp", "/private/tmp", os.tmpdir(), ...cargoTargetDirs])];
24
29
  }
25
30
  export const within = (root, file) => file === root || file.startsWith(`${root}${path.sep}`);
26
31
  export function privatePath(file, mode, { io = fs, uid = process.getuid?.(), kind = "file" } = {}) {
@@ -55,15 +60,19 @@ function absolute(value) {
55
60
  return path.resolve(value);
56
61
  }
57
62
  export function validateSigningRequest(request, { roots, stagingRoot, io = fs } = {}) {
63
+ // Node's JS realpath normalizes .. before following links. Native realpath
64
+ // follows the same path traversal as codesign & catches link/.. escapes.
65
+ const realpath = io.realpathSync.native ?? io.realpathSync;
58
66
  const common = ["schema", "token", "type", "allowedRoots", "cwd", "timeoutMs"];
59
67
  if (request?.type === "health") {
60
68
  shape(request, ["schema", "token", "type"]);
61
69
  if (request.schema !== 1) fail("MAC_SIGNING_AGENT_PROTOCOL", "Unsupported protocol schema");
62
70
  return request;
63
71
  }
64
- if (!["codesign", "updater"].includes(request?.type)) fail("MAC_SIGNING_AGENT_PROTOCOL", "Unsupported signing request type");
65
- shape(request, [...common, request.type === "codesign" ? "items" : "files"]);
66
- if (request.schema !== 1 || !Number.isInteger(request.timeoutMs) || request.timeoutMs < 1 || request.timeoutMs > 180000) {
72
+ const notary = ["notarize", "staple"].includes(request?.type);
73
+ if (!["codesign", "codesign-argv", "updater", "notarize", "staple"].includes(request?.type)) fail("MAC_SIGNING_AGENT_PROTOCOL", "Unsupported signing request type");
74
+ shape(request, [...common, ...(notary ? ["path", "profile", "staple", "app", "validateOnly"] : request.type === "codesign-argv" ? ["argv", "targets"] : [request.type === "codesign" ? "items" : "files"])]);
75
+ if (request.schema !== 1 || !Number.isInteger(request.timeoutMs) || request.timeoutMs < 1 || request.timeoutMs > (notary ? MAX_NOTARIZATION_TIMEOUT_MS : 180000)) {
67
76
  fail("MAC_SIGNING_AGENT_PROTOCOL", "Invalid schema or signing deadline");
68
77
  }
69
78
  if (!Array.isArray(request.allowedRoots) || !request.allowedRoots.length || request.allowedRoots.length > 32) fail("MAC_SIGNING_AGENT_PATH_DENIED", "Allowed roots required");
@@ -73,15 +82,16 @@ export function validateSigningRequest(request, { roots, stagingRoot, io = fs }
73
82
  const allowed = request.allowedRoots.map((root) => {
74
83
  const lexical = absolute(root);
75
84
  if (![...installed, stagingRoot].filter(Boolean).some((base) => within(base, lexical))) fail("MAC_SIGNING_AGENT_PATH_DENIED", "Caller root is outside installed signing policy");
76
- const canonical = io.realpathSync(lexical);
85
+ const canonical = realpath(root);
77
86
  if (![...installed, stagingRoot].filter(Boolean).some((base) => {
78
- try { return within(io.realpathSync(base), canonical); } catch { return false; }
87
+ try { return within(realpath(base), canonical); } catch { return false; }
79
88
  })) fail("MAC_SIGNING_AGENT_PATH_DENIED", "Allowed root escapes installed policy");
80
89
  return { lexical, canonical };
81
90
  });
82
91
  function checked(file, { directory = false, entitlements = false } = {}) {
83
92
  const lexical = absolute(file);
84
- const canonical = io.realpathSync(lexical);
93
+ let canonical;
94
+ try { canonical = realpath(file); } catch { fail("MAC_SIGNING_AGENT_PATH_DENIED", "Signing path is unavailable"); }
85
95
  if (!allowed.some((root) => within(root.lexical, lexical) && within(root.canonical, canonical))
86
96
  || (!entitlements && stagingRoot && within(stagingRoot, canonical))) fail("MAC_SIGNING_AGENT_PATH_DENIED", "Signing path escapes allowed roots");
87
97
  const info = io.statSync(canonical);
@@ -89,6 +99,33 @@ export function validateSigningRequest(request, { roots, stagingRoot, io = fs }
89
99
  return canonical;
90
100
  }
91
101
  const cwd = checked(request.cwd, { directory: true });
102
+ if (notary) {
103
+ const file = checked(request.path);
104
+ if (request.type === "staple") {
105
+ if (request.profile !== undefined || request.app !== undefined || request.staple !== undefined
106
+ || (request.validateOnly !== undefined && typeof request.validateOnly !== "boolean")) fail("MAC_SIGNING_AGENT_PROTOCOL", "Invalid stapling options");
107
+ if (!/\.(dmg|pkg|app)$/i.test(file) || (/\.app$/i.test(file) ? !io.statSync(file).isDirectory() : !io.statSync(file).isFile())) fail("MAC_SIGNING_AGENT_PATH_DENIED", "Invalid staple target");
108
+ return { ...request, cwd, path: file };
109
+ }
110
+ const profile = request.profile ?? "apple-dev-notary";
111
+ if (typeof profile !== "string" || !/^[A-Za-z0-9][A-Za-z0-9_. -]{0,127}$/.test(profile)
112
+ || request.staple !== true || request.validateOnly !== undefined) fail("MAC_SIGNING_AGENT_PROTOCOL", "Notarization requires a profile name & staple: true");
113
+ if (!/\.(dmg|zip|pkg)$/i.test(file) || !io.statSync(file).isFile()) fail("MAC_SIGNING_AGENT_PATH_DENIED", "Notarization requires DMG, ZIP, or PKG");
114
+ const app = request.app === undefined ? undefined : checked(request.app, { directory: true });
115
+ if (app && !/\.app$/i.test(app)) fail("MAC_SIGNING_AGENT_PATH_DENIED", "Stapling app must be an app bundle");
116
+ if (/\.zip$/i.test(file) && !app) fail("MAC_SIGNING_AGENT_PROTOCOL", "ZIP submission requires app bundle for stapling");
117
+ return { ...request, cwd, path: file, profile, ...(app ? { app } : {}) };
118
+ }
119
+ if (request.type === "codesign-argv") {
120
+ const parsed = parseCodesignArgv(request.argv, request.cwd);
121
+ if (!Array.isArray(request.targets) || request.targets.length !== parsed.targets.length
122
+ || request.targets.some((target, index) => target !== parsed.targets[index])) fail("MAC_SIGNING_AGENT_PROTOCOL", "Codesign target paths do not match argv");
123
+ for (const target of parsed.targets) checked(target);
124
+ for (const file of parsed.entitlements) checked(file, { entitlements: true });
125
+ // Preserve argv & cwd spelling: relative arguments must resolve exactly as
126
+ // they would for the original caller, including symlink/.. semantics.
127
+ return { ...request };
128
+ }
92
129
  if (request.type === "updater") {
93
130
  if (!Array.isArray(request.files) || !request.files.length || request.files.length > 4096) fail("MAC_SIGNING_AGENT_PROTOCOL", "Updater files required");
94
131
  const files = request.files.map((file) => {
@@ -119,9 +156,17 @@ export function authenticateSigningToken(candidate, token) {
119
156
  }
120
157
  export function executeSigningRequest(request, { run = spawnSync, home = os.userInfo().homedir,
121
158
  username = os.userInfo().username, pnpm, environmentPath, now = Date.now } = {}) {
122
- if (request.type === "health") return { schema: 1, ok: true, service: AGENT_LABEL, pid: process.pid };
159
+ if (request.type === "health") return { schema: 1, ok: true, service: AGENT_LABEL, pid: process.pid, capabilities: ["codesign", "codesign-argv", "updater", "notarize", "staple"] };
123
160
  const deadline = now() + request.timeoutMs;
124
161
  const env = { HOME: home, PATH: environmentPath, TMPDIR: path.join(signingAgentPaths(home).directory, "tmp") };
162
+ if (request.type === "codesign-argv") {
163
+ const r = run("/usr/bin/codesign", request.argv, { cwd: request.cwd, env, encoding: "utf8", stdio: "pipe", shell: false,
164
+ windowsHide: true, timeout: request.timeoutMs, killSignal: "SIGKILL", maxBuffer: MAX_FRAME });
165
+ if (r.error || !Number.isInteger(r.status)) fail(r.error?.code === "ETIMEDOUT" ? "MAC_SIGNING_AGENT_TIMEOUT" : "MAC_SIGNING_AGENT_JOB_FAILED", "Codesign execution failed");
166
+ const report = { schema: 1, ok: true, results: [{ status: r.status, stdout: String(r.stdout ?? ""), stderr: String(r.stderr ?? "") }] };
167
+ if (Buffer.byteLength(`${JSON.stringify(report)}\n`) > MAX_FRAME) fail("MAC_SIGNING_AGENT_PROTOCOL", "Codesign output exceeds protocol limit");
168
+ return report;
169
+ }
125
170
  const results = [];
126
171
  const outputLimit = Math.min(4096, Math.floor(MAX_FRAME / (4 * (request.items?.length ?? request.files.length))));
127
172
  const invoke = (command, args, sensitive = false) => {
@@ -158,6 +203,92 @@ export function executeSigningRequest(request, { run = spawnSync, home = os.user
158
203
  throw error;
159
204
  } finally { delete env.TAURI_SIGNING_PRIVATE_KEY; delete env.TAURI_SIGNING_PRIVATE_KEY_PASSWORD; }
160
205
  }
206
+ // No notarytool/stapler output is forwarded verbatim: diagnostics can contain
207
+ // account details. Only typed status, submission UUID, counts & issue codes leave Aqua.
208
+ export async function executeNotarizationRequest(request, { launch = spawn, home = os.userInfo().homedir,
209
+ environmentPath = "/usr/bin:/bin", now = Date.now, id = randomUUID(), progress = () => {}, signal } = {}) {
210
+ const deadline = now() + request.timeoutMs;
211
+ const env = { HOME: home, PATH: environmentPath, TMPDIR: path.join(signingAgentPaths(home).directory, "tmp") };
212
+ const report = { schema: 1, ok: false, id, notarization: { id: null, status: "Unknown", logSummary: null }, steps: [], stapling: [] };
213
+ const invoke = async (step, args, file) => {
214
+ progress({ schema: 1, event: "status", id, step });
215
+ const remaining = deadline - now();
216
+ if (remaining <= 0 || signal?.aborted) throw new SigningAgentError("MAC_SIGNING_AGENT_TIMEOUT", "Notarization deadline expired");
217
+ const result = await new Promise((resolve) => {
218
+ let child, output = "", bytes = 0, timedOut = false, overflow = false, failed = false;
219
+ let timer;
220
+ const kill = () => { try { child?.kill("SIGKILL"); } catch {} };
221
+ const cancel = () => { timedOut = true; kill(); };
222
+ const finish = (exitCode) => {
223
+ clearTimeout(timer); signal?.removeEventListener("abort", cancel);
224
+ resolve({ exitCode: Number.isInteger(exitCode) ? exitCode : null, output, timedOut, overflow, failed });
225
+ };
226
+ try {
227
+ child = launch("/usr/bin/xcrun", args, { cwd: request.cwd, env, stdio: ["ignore", "pipe", "pipe"], shell: false });
228
+ timer = setTimeout(cancel, remaining);
229
+ signal?.addEventListener("abort", cancel, { once: true });
230
+ child.stdout.on("data", (chunk) => {
231
+ bytes += chunk.length;
232
+ if (bytes > MAX_FRAME) { overflow = true; kill(); } else output += chunk.toString();
233
+ });
234
+ child.stderr.on("data", () => {}); // Drain, never disclose.
235
+ child.once("error", () => { failed = true; finish(null); });
236
+ child.once("close", finish);
237
+ } catch { failed = true; finish(null); }
238
+ });
239
+ report.steps.push({ step, ...(file ? { file } : {}), exitCode: result.exitCode });
240
+ if (result.timedOut) throw new SigningAgentError("MAC_SIGNING_AGENT_TIMEOUT", "Notarization deadline expired");
241
+ if (result.failed || result.overflow) throw new SigningAgentError("MAC_SIGNING_AGENT_JOB_FAILED", `${step} execution failed`);
242
+ return result;
243
+ };
244
+ const parse = (output) => { try { return JSON.parse(output); } catch { return {}; } };
245
+ try {
246
+ if (request.type === "notarize") {
247
+ const submitted = await invoke("submit", ["notarytool", "submit", request.path, "--keychain-profile", request.profile ?? "apple-dev-notary", "--wait", "--output-format", "json"]);
248
+ const result = parse(submitted.output);
249
+ const submissionId = result.id ?? result.submissionId;
250
+ report.notarization.id = typeof submissionId === "string" && /^[a-f0-9-]{36}$/i.test(submissionId) ? submissionId : null;
251
+ report.notarization.status = ["Accepted", "Invalid", "In Progress", "Rejected"].includes(result.status) ? result.status : "Unknown";
252
+ if (report.notarization.status === "Invalid" && report.notarization.id) {
253
+ const logged = await invoke("log", ["notarytool", "log", report.notarization.id, "--keychain-profile", request.profile ?? "apple-dev-notary", "--output-format", "json"]);
254
+ if (logged.exitCode === 0) {
255
+ const log = parse(logged.output);
256
+ const issues = Array.isArray(log.issues) ? log.issues : [];
257
+ report.notarization.logSummary = { statusCode: Number.isInteger(log.statusCode) ? log.statusCode : null,
258
+ errors: issues.filter((issue) => issue.severity === "error").length,
259
+ warnings: issues.filter((issue) => issue.severity === "warning").length,
260
+ issues: issues.slice(0, 100).map((issue) => ({ severity: ["error", "warning"].includes(issue.severity) ? issue.severity : "unknown",
261
+ code: Number.isInteger(issue.code) ? issue.code : null })) };
262
+ }
263
+ }
264
+ if (submitted.exitCode !== 0 || report.notarization.status !== "Accepted" || !report.notarization.id) {
265
+ throw new SigningAgentError("MAC_SIGNING_NOTARIZATION_FAILED", `Notarization ${report.notarization.status}`);
266
+ }
267
+ }
268
+ const targets = request.type === "staple" ? [request.path]
269
+ : [...new Set([...( /\.(dmg|pkg)$/i.test(request.path) ? [request.path] : []), ...(request.app ? [request.app] : [])])];
270
+ for (const file of targets) {
271
+ let alreadyStapled = false;
272
+ if (request.type === "staple" && !request.validateOnly) {
273
+ const probe = await invoke("probe", ["stapler", "validate", file], file);
274
+ alreadyStapled = probe.exitCode === 0;
275
+ }
276
+ if (!request.validateOnly && !alreadyStapled) {
277
+ const stapled = await invoke("staple", ["stapler", "staple", file], file);
278
+ if (stapled.exitCode !== 0) throw new SigningAgentError("MAC_SIGNING_STAPLE_FAILED", "Stapling failed");
279
+ }
280
+ const validated = await invoke("validate", ["stapler", "validate", file], file);
281
+ if (validated.exitCode !== 0) throw new SigningAgentError("MAC_SIGNING_STAPLE_FAILED", "Staple validation failed");
282
+ report.stapling.push({ file, alreadyStapled, validated: true });
283
+ }
284
+ report.ok = true;
285
+ } catch (error) {
286
+ report.code = error.code?.startsWith("MAC_SIGNING_") ? error.code : "MAC_SIGNING_AGENT_JOB_FAILED";
287
+ report.message = report.code === "MAC_SIGNING_AGENT_TIMEOUT" ? "Notarization deadline expired" : "Notarization or stapling failed inside signing agent";
288
+ }
289
+ return report;
290
+ }
291
+
161
292
  export function managedSigningLog(file, event, { io = fs } = {}) {
162
293
  if (io.existsSync(file)) {
163
294
  privatePath(file, 0o600, { io });
@@ -170,7 +301,7 @@ export function managedSigningLog(file, event, { io = fs } = {}) {
170
301
  // Only service-authored metadata is logged, never request/token/key/argv/output.
171
302
  io.appendFileSync(file, `${JSON.stringify({ at: new Date().toISOString(), ...event })}\n`, { mode: 0o600 });
172
303
  }
173
- export async function startSigningAgent({ paths = signingAgentPaths(), config, io = fs, run = spawnSync,
304
+ export async function startSigningAgent({ paths = signingAgentPaths(), config, io = fs, run = spawnSync, launch = spawn, heartbeatMs = 10000,
174
305
  log = (event) => managedSigningLog(paths.log, event), createServer = net.createServer } = {}) {
175
306
  privatePath(paths.directory, 0o700, { io, kind: "directory" });
176
307
  privatePath(paths.config, 0o600, { io });
@@ -211,9 +342,14 @@ export async function startSigningAgent({ paths = signingAgentPaths(), config, i
211
342
  return;
212
343
  }
213
344
  const receivedAt = Date.now();
345
+ const notary = ["notarize", "staple"].includes(request.type);
346
+ const controller = new AbortController();
347
+ const progress = (frame) => { if (!socket.destroyed) socket.write(`${JSON.stringify(frame)}\n`); };
348
+ const heartbeat = notary ? setInterval(() => progress({ schema: 1, event: "heartbeat", id }), heartbeatMs) : null;
349
+ if (notary) socket.on("close", () => { clearInterval(heartbeat); controller.abort(); });
214
350
  pending++;
215
- log({ event: "request.accepted", id, type: request.type, count: request.items?.length ?? request.files?.length ?? 0 });
216
- queue = queue.then(() => {
351
+ log({ event: "request.accepted", id, type: request.type, count: notary ? 1 : request.targets?.length ?? request.items?.length ?? request.files?.length ?? 0 });
352
+ queue = queue.then(async () => {
217
353
  if (socket.destroyed) { log({ event: "request.cancelled", id, type: request.type }); return; }
218
354
  log({ event: "request.started", id, type: request.type });
219
355
  try {
@@ -223,15 +359,20 @@ export async function startSigningAgent({ paths = signingAgentPaths(), config, i
223
359
  // Revalidate immediately before effects, not merely on admission.
224
360
  request = validateSigningRequest(request, { roots: config.roots, stagingRoot: path.join(paths.directory, "requests"), io });
225
361
  }
226
- const report = executeSigningRequest(request, { run, home: config.home, username: config.username, pnpm: config.pnpm, environmentPath: config.environmentPath });
362
+ const report = notary
363
+ ? await executeNotarizationRequest(request, { launch, home: config.home, environmentPath: config.environmentPath, id, progress, signal: controller.signal })
364
+ : executeSigningRequest(request, { run, home: config.home, username: config.username, pnpm: config.pnpm, environmentPath: config.environmentPath });
227
365
  respond(report);
228
- log({ event: "request.completed", id, type: request.type });
366
+ log({ event: report.ok ? "request.completed" : "request.failed", id, type: request.type,
367
+ ...(report.code ? { code: report.code } : {}),
368
+ ...(notary ? { status: report.notarization.status, steps: report.steps.map(({ step, exitCode }) => ({ step, exitCode })) } : {}),
369
+ ...(request.type === "codesign-argv" ? { status: report.results[0].status } : {}) });
229
370
  } catch (error) {
230
371
  const code = error.code?.startsWith("MAC_SIGNING_") ? error.code : "MAC_SIGNING_AGENT_JOB_FAILED";
231
- respond({ schema: 1, ok: false, code, message: request.type === "updater" ? "Updater signing failed" : error.message });
372
+ respond({ schema: 1, ok: false, ...(notary ? { id } : {}), code, message: request.type === "updater" ? "Updater signing failed" : notary ? "Notarization job failed" : error.message });
232
373
  log({ event: "request.failed", id, type: request.type, code });
233
374
  }
234
- }).finally(() => { pending--; }).catch(() => { socket.destroy(); });
375
+ }).finally(() => { clearInterval(heartbeat); pending--; }).catch(() => { socket.destroy(); });
235
376
  });
236
377
  });
237
378
  await new Promise((resolve, reject) => { server.once("error", reject); server.listen(paths.socket, resolve); });
@@ -240,7 +381,8 @@ export async function startSigningAgent({ paths = signingAgentPaths(), config, i
240
381
  server.on("error", () => { log({ event: "service.failed", code: "MAC_SIGNING_AGENT_SOCKET" }); });
241
382
  return server;
242
383
  }
243
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
384
+ const realOrSelf = (file) => { try { return fs.realpathSync(file); } catch { return path.resolve(file); } };
385
+ if (process.argv[1] && realOrSelf(process.argv[1]) === realOrSelf(fileURLToPath(import.meta.url))) {
244
386
  process.umask(0o077);
245
387
  startSigningAgent().then((server) => {
246
388
  const stop = () => { managedSigningLog(signingAgentPaths().log, { event: "service.stopping" }); server.close(() => process.exit(0)); setTimeout(() => process.exit(0), 2000).unref(); };
package/rightkit-sync.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  // Every publishable crate under crates/ and package under packages/ is looked up; a newer
4
4
  // published version becomes the staged (latest) version and the one it replaces stays allowed
5
5
  // in legacyCargo / legacyNpm, so apps pinned to it keep validating until they adopt.
6
- import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
6
+ import { existsSync, readFileSync, readdirSync, writeFileSync, realpathSync } from "node:fs";
7
7
  import path from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
9
 
@@ -189,6 +189,9 @@ export function syncConsumerCopies(root = rightkitRoot) {
189
189
  ["rightkit-versions.json", path.join(root, "packages", "build-control-policy", "src", "ownership-session-versions.json")],
190
190
  ["rightkit-drift.mjs", path.join(root, "packages", "build-control-policy", "src", "rightkit-drift.mjs")],
191
191
  ["background-process.mjs", path.join(root, "packages", "build-control-policy", "src", "background-process.mjs")],
192
+ ["ownership-source.mjs", path.join(root, "packages", "build-control-policy", "src", "ownership-source.mjs")],
193
+ ["ownership-availability.mjs", path.join(root, "packages", "build-control-policy", "src", "ownership-availability.mjs")],
194
+ ["ownership-availability.mjs", path.join(root, "packages", "build-control-clients", "src", "ownership-availability.mjs")],
192
195
  ];
193
196
  const updated = [];
194
197
  for (const [source, target] of targets) {
@@ -201,7 +204,8 @@ export function syncConsumerCopies(root = rightkitRoot) {
201
204
  return updated;
202
205
  }
203
206
 
204
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
207
+ const realOrSelf = (file) => { try { return realpathSync(file); } catch { return path.resolve(file); } };
208
+ if (process.argv[1] && realOrSelf(process.argv[1]) === realOrSelf(fileURLToPath(import.meta.url))) {
205
209
  const main = async () => {
206
210
  const args = process.argv.slice(2);
207
211
  if (args[0] === "stage") {