"use strict"; const crypto = require("node:crypto"); const fs = require("node:fs"); const path = require("node:path"); const { validateDeploymentSource } = require("./deployment-source-policy"); // A push can request deployment, but it can never execute deployment itself. // The separate resident deployment agent consumes these immutable event files. function enqueueDeploymentEvent(intent, push, queueDir, context = {}) { if (!intent) return { state: "not_requested" }; const invalid = validateIntent(intent, push, context); if (invalid) return { state: "rejected", diagnostic_code: invalid }; fs.mkdirSync(queueDir, { recursive: true, mode: 0o750 }); const requestedEventId = String(context.event_id || ""); if (requestedEventId && !/^[0-9a-f]{8}-[0-9a-f]{4}-[45][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(requestedEventId)) { return { state: "rejected", diagnostic_code: "deployment_event_id_invalid" }; } const eventId = requestedEventId || crypto.randomUUID(); const dedupeKey = String(context.dedupe_key || ""); let marker = ""; if (dedupeKey) { const dedupeDir = path.join(queueDir, ".dedupe"); fs.mkdirSync(dedupeDir, { recursive: true, mode: 0o750 }); marker = path.join(dedupeDir, `${crypto.createHash("sha256").update(dedupeKey).digest("hex")}.json`); try { fs.writeFileSync(marker, JSON.stringify({ event_id: eventId, state: "reserved" }), { mode: 0o640, flag: "wx" }); } catch (error) { if (error.code === "EEXIST") { const prior = JSON.parse(fs.readFileSync(marker, "utf8")); return { state: "duplicate", event_id: String(prior.event_id || eventId) }; } throw error; } } const event = { schema: "guanghu.deployment-event/v1", event_id: eventId, created_at: Date.now() / 1000, state: "queued_for_resident_agent", repo: push.repo, branch: push.branch, commit_sha: push.commit_sha, workorder_id: String(intent.workorder_id || ""), resource: intent.resource, action: "provision-approved-architecture", manifest: intent.manifest, authorizer_id: String(context.authorizer_id || ""), persona_id: String(context.persona_id || ""), execution_runtime_id: String(context.execution_runtime_id || ""), target: String(context.target || ""), deployment_source: intent.deployment_source || null, }; const target = path.join(queueDir, `${event.created_at}-${event.event_id}.json`); const temporary = `${target}.${process.pid}.tmp`; try { fs.writeFileSync(temporary, JSON.stringify(event), { mode: 0o640, flag: "wx" }); fs.renameSync(temporary, target); if (marker) { const markerTemporary = `${marker}.${process.pid}.tmp`; fs.writeFileSync(markerTemporary, JSON.stringify({ event_id: eventId, state: "queued", queued_at: event.created_at }), { mode: 0o640, flag: "wx" }); fs.renameSync(markerTemporary, marker); } } catch (error) { try { fs.unlinkSync(temporary); } catch {} if (marker) { try { fs.unlinkSync(marker); } catch {} } throw error; } return { state: event.state, event_id: event.event_id }; } function validateIntent(intent, push, context = {}) { if (intent.schema !== "guanghu.deployment-intent/v1") return "deployment_intent_schema_invalid"; if (String(intent.repo || "").toLowerCase() !== push.repo || intent.branch !== push.branch || String(intent.commit_sha || "").toLowerCase() !== push.commit_sha) return "deployment_intent_binding_mismatch"; const resource = String(intent.resource || "").match(/^([A-Z0-9][A-Z0-9._-]{5,119})@([0-9a-f]{40})$/); if (!resource || resource[2] !== push.commit_sha) return "deployment_intent_resource_invalid"; if (!/^deployment\/requests\/[A-Za-z0-9._/-]{1,180}\.json$/.test(String(intent.manifest || ""))) return "deployment_intent_manifest_invalid"; if (intent.manifest !== `deployment/requests/${resource[1]}.json`) return "deployment_intent_manifest_resource_mismatch"; if (context.registry) { return validateDeploymentSource({ repo: push.repo, authorizer_id: String(context.authorizer_id || ""), persona_id: String(context.persona_id || ""), execution_runtime_id: String(context.execution_runtime_id || ""), target: String(context.target || ""), deployment_source: intent.deployment_source || null, }, context.registry); } return ""; } module.exports = { enqueueDeploymentEvent, validateIntent };