// Responsibility: expose the YAML-owned sentinel internal publish capability without claiming provenance. import { rootPath } from "./config"; import type { CicdDeliveryAuthority } from "./cicd-delivery-authority"; import type { WebProbeSentinelOptions } from "./hwlab-node-web-sentinel-cicd-shared"; import { readYamlRecord } from "./platform-infra-ops-library"; import { materializeYamlComposition } from "./yaml-composition"; const configPath = "config/platform-infra/pipelines-as-code.yaml"; const configRef = `${configPath}#capabilities.sentinelInternalPublish`; export interface SentinelPacInternalPublishCapability { readonly enabled: boolean; readonly admissionProvenance: "unavailable"; readonly trackingIssue: string; readonly configRef: string; readonly valuesPrinted: false; } export type SentinelPacInternalPublishAuthorization = { readonly allowed: false; readonly mode: "sentinel-pac-internal-publish-blocked"; readonly reason: | "operator-entrypoint" | "delivery-authority-not-pac" | "capability-disabled" | "admission-provenance-unavailable"; readonly capability: SentinelPacInternalPublishCapability; readonly valuesPrinted: false; }; export function readSentinelPacInternalPublishCapability(): SentinelPacInternalPublishCapability { const raw = readYamlRecord>(rootPath(...configPath.split("/")), "platform-infra-pipelines-as-code"); const composed = materializeYamlComposition(raw, { label: configPath }).value; const capabilities = record(composed.capabilities, `${configPath}#capabilities`); const capability = record(capabilities.sentinelInternalPublish, configRef); const enabled = booleanField(capability.enabled, `${configRef}.enabled`); const admissionProvenance = stringField(capability.admissionProvenance, `${configRef}.admissionProvenance`); if (admissionProvenance !== "unavailable") { throw new Error(`${configRef}.admissionProvenance must remain unavailable until the admission contract is implemented`); } const trackingIssue = stringField(capability.trackingIssue, `${configRef}.trackingIssue`); if (!/^https:\/\/github\.com\/pikasTech\/unidesk\/issues\/\d+$/u.test(trackingIssue)) { throw new Error(`${configRef}.trackingIssue must reference the owning GitHub issue`); } return { enabled, admissionProvenance, trackingIssue, configRef, valuesPrinted: false, }; } export function authorizeSentinelPacInternalPublish( options: WebProbeSentinelOptions, authority: CicdDeliveryAuthority, capability = readSentinelPacInternalPublishCapability(), ): SentinelPacInternalPublishAuthorization { if (options.kind !== "publish" || options.internalExecution !== "pac-controller") { return denied("operator-entrypoint", capability); } if (authority.kind !== "pac-pr-merge") return denied("delivery-authority-not-pac", capability); if (!capability.enabled) return denied("capability-disabled", capability); return denied("admission-provenance-unavailable", capability); } function denied( reason: SentinelPacInternalPublishAuthorization["reason"], capability: SentinelPacInternalPublishCapability, ): SentinelPacInternalPublishAuthorization { return { allowed: false, mode: "sentinel-pac-internal-publish-blocked", reason, capability, valuesPrinted: false, }; } function record(value: unknown, path: string): Record { if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${path} must be an object`); return value as Record; } function booleanField(value: unknown, path: string): boolean { if (typeof value !== "boolean") throw new Error(`${path} must be a boolean`); return value; } function stringField(value: unknown, path: string): string { if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${path} must be a non-empty string`); return value.trim(); }