@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.
- package/README.md +62 -0
- package/addon-command.mjs +53 -34
- package/apple-profile-renew.mjs +30 -12
- package/artifact-storage.mjs +53 -12
- package/background-process.mjs +18 -0
- package/build-release.mjs +4 -3
- package/cache-command.mjs +4 -3
- package/cargo-contract.mjs +1 -1
- package/cargo-guard.mjs +4 -3
- package/cargo-registry-readback.mjs +51 -0
- package/cargo-target.mjs +3 -1
- package/cli/right-release.mjs +26 -0
- package/codesign-argv.mjs +64 -0
- package/codesign-environment.mjs +58 -0
- package/codesign-shim.mjs +51 -0
- package/committed-source.mjs +3 -3
- package/development.mjs +11 -9
- package/direct-bootstrap.mjs +19 -9
- package/dmg-build.mjs +117 -0
- package/dmg-layout.mjs +123 -0
- package/github-release.mjs +74 -37
- package/heavy-command.mjs +3 -2
- package/install-app.mjs +358 -0
- package/inventory-check.mjs +980 -0
- package/macos-notarization.mjs +24 -8
- package/macos-signing-session.mjs +50 -2
- package/managed-cargo.mjs +23 -19
- package/managed-host-secrets.mjs +30 -0
- package/managed-roots.mjs +39 -0
- package/model-promote.mjs +31 -37
- package/notarize-macos.mjs +5 -5
- package/notary-auth.mjs +4 -0
- package/osascript-guard.mjs +24 -0
- package/ownership-approval-command.mjs +15 -4
- package/ownership-approval.mjs +114 -49
- package/ownership-availability.mjs +25 -0
- package/ownership-catalog-config.json +18 -0
- package/ownership-catalog.mjs +94 -0
- package/ownership-command.mjs +17 -10
- package/ownership-declaration.json +15 -0
- package/ownership-default-policy.json +1 -11
- package/ownership-evidence.mjs +10 -8
- package/ownership-git-inventory.mjs +29 -1
- package/ownership-helper-paths.json +4 -4
- package/ownership-hooks.mjs +107 -0
- package/ownership-internal-owners.json +44 -0
- package/ownership-key-authority.mjs +112 -7
- package/ownership-key-command.mjs +76 -19
- package/ownership-key-trust.mjs +97 -9
- package/ownership-lockfiles.mjs +311 -0
- package/ownership-report-command.mjs +12 -3
- package/ownership-report.mjs +20 -17
- package/ownership-repositories.json +19 -0
- package/ownership-scan-worker.mjs +96 -11
- package/ownership-source.mjs +39 -0
- package/ownership-staged.mjs +193 -0
- package/ownership-verdict.mjs +74 -0
- package/ownership.mjs +215 -107
- package/package.json +1 -1
- package/protected-credentials.mjs +111 -0
- package/prune-r2.mjs +4 -3
- package/pub-upload.mjs +3 -85
- package/publication-artifacts.mjs +108 -0
- package/publication-gate.mjs +28 -7
- package/publish-cargo.mjs +47 -16
- package/publish-swift.mjs +46 -17
- package/publish-update.mjs +61 -39
- package/r2-verified-upload.mjs +41 -0
- package/registry-parity.mjs +5 -3
- package/release-state.mjs +1 -0
- package/release-token.mjs +2 -0
- package/release.mjs +2 -1
- package/rightkit-drift.mjs +3 -2
- package/rightkit-ownership.json +2028 -139
- package/rightkit-signing-agent.mjs +161 -19
- package/rightkit-sync.mjs +6 -2
- package/rightkit-versions.json +220 -106
- package/runtime-artifact-manifest.mjs +20 -6
- package/sign-updater.mjs +5 -0
- package/signing-agent-client.mjs +78 -22
- package/signing-agent-command.mjs +5 -4
- package/signing-agent-install.mjs +31 -4
- package/standalone-clone-verify.mjs +3 -2
- package/tauri-bundle-marker.mjs +3 -2
- package/upload-large.mjs +36 -82
- 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 =
|
|
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 =
|
|
23
|
-
return ["/Volumes/D/claude", managedRoot, "/Volumes/D/rightkit-build-control",
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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 =
|
|
85
|
+
const canonical = realpath(root);
|
|
77
86
|
if (![...installed, stagingRoot].filter(Boolean).some((base) => {
|
|
78
|
-
try { return within(
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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") {
|