test: add runtime boundary guard

This commit is contained in:
HWLAB Code Queue
2026-05-21 15:28:13 +00:00
parent edd613d208
commit 3f6451ae91
3 changed files with 507 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
# HWLAB Runtime Boundary Guard
The HWLAB runtime may use UniDesk CI/CD infrastructure and Code Queue
scheduling, but it must not use UniDesk backend, provider-gateway, or
microservice proxy as replacement runtime services. Those systems are external
supporting infrastructure only.
This guard is intentionally static. It reads repository files and does not
contact the DEV endpoint, deploy to DEV or PROD, read secrets, or restart any
UniDesk or HWLAB runtime process.
## Boundary Rule
- HWLAB runtime service identity is the frozen `hwlab-*` service ID set.
- DEV is the only accepted runtime environment for this guard.
- The fixed DEV endpoint is `http://74.48.78.17:6667`.
- The DEV namespace is `hwlab-dev`.
- PROD deployment and PROD acceptance remain disabled.
- UniDesk backend, provider-gateway, and microservice proxy are forbidden
runtime substitutes.
UniDesk may remain in the control-plane path for CI, CD, and Code Queue
scheduling. It must not terminate HWLAB health, API, gateway, router, tunnel,
agent, simulator, patch-panel, CLI, or skills runtime contracts on behalf of
HWLAB.
## Checked Artifacts
The machine-readable fixture is
`protocol/examples/runtime-boundary/guard.dev.json`. It records the expected
DEV-only boundary, frozen service IDs, forbidden substitutes, and paths checked
by `scripts/validate-runtime-boundary.mjs`.
The validator checks:
- `deploy/deploy.json` keeps `environment: dev`, `hwlab-dev`, the fixed DEV
endpoint, the complete HWLAB service set, HWLAB image names, and a disabled
PROD profile.
- `deploy/master-edge/health-contract.json` keeps the `frp` D601-to-master
reverse link, HWLAB contract service IDs, and the forbidden substitute list.
- `deploy/k8s/base/workloads.yaml` keeps workload names, labels, container
names, and images under HWLAB service IDs.
- `deploy/k8s/base/services.yaml` keeps Kubernetes service names and labels
under HWLAB service IDs.
- `deploy/k8s/dev/*` keeps the DEV overlay and runtime substitute policy.
- `deploy/k8s/prod/*` keeps PROD as a disabled placeholder only.
## Validation
Run:
```sh
node --check scripts/validate-runtime-boundary.mjs
node scripts/validate-runtime-boundary.mjs
```
The script is lightweight and has no package dependency. It fails if a checked
artifact drifts toward a UniDesk runtime substitute, a non-DEV environment, an
unexpected namespace, an unexpected endpoint, or an incomplete HWLAB service
boundary.
@@ -0,0 +1,104 @@
{
"guard": "hwlab-runtime-boundary",
"schemaVersion": "v1",
"environment": "dev",
"endpoint": "http://74.48.78.17:6667",
"namespace": "hwlab-dev",
"scope": "static-runtime-boundary",
"policy": {
"runtimeSubstitution": "forbidden",
"prodDeployment": "forbidden",
"realDevDeployment": "out_of_scope",
"allowedUniDeskRoles": ["ci", "cd", "code_queue_scheduling"],
"forbiddenRuntimeSubstitutes": [
"unidesk-backend",
"provider-gateway",
"microservice-proxy"
]
},
"requiredManifest": {
"path": "deploy/deploy.json",
"environment": "dev",
"namespace": "hwlab-dev",
"prodProfileEnabled": false,
"requiredServiceIds": [
"hwlab-cloud-api",
"hwlab-cloud-web",
"hwlab-agent-mgr",
"hwlab-agent-worker",
"hwlab-gateway",
"hwlab-gateway-simu",
"hwlab-box-simu",
"hwlab-patch-panel",
"hwlab-router",
"hwlab-tunnel-client",
"hwlab-edge-proxy",
"hwlab-cli",
"hwlab-agent-skills"
]
},
"requiredHealthContract": {
"path": "deploy/master-edge/health-contract.json",
"reverseLink": {
"mode": "frp",
"direction": "d601-to-master",
"client": "hwlab-tunnel-client"
},
"requiredContractServiceIds": [
"hwlab-edge-proxy",
"hwlab-router",
"hwlab-tunnel-client",
"hwlab-cloud-api",
"hwlab-cloud-web",
"hwlab-agent-mgr",
"hwlab-agent-worker",
"hwlab-gateway-simu",
"hwlab-box-simu",
"hwlab-patch-panel"
]
},
"requiredK3sSkeleton": {
"workloadsPath": "deploy/k8s/base/workloads.yaml",
"servicesPath": "deploy/k8s/base/services.yaml",
"devKustomizationPath": "deploy/k8s/dev/kustomization.yaml",
"devHealthContractPath": "deploy/k8s/dev/health-contract.yaml",
"prodKustomizationPath": "deploy/k8s/prod/kustomization.yaml",
"prodDisabledPath": "deploy/k8s/prod/prod-disabled.yaml",
"requiredWorkloadServiceIds": [
"hwlab-cloud-api",
"hwlab-cloud-web",
"hwlab-agent-mgr",
"hwlab-agent-worker",
"hwlab-gateway",
"hwlab-gateway-simu",
"hwlab-box-simu",
"hwlab-patch-panel",
"hwlab-router",
"hwlab-tunnel-client",
"hwlab-edge-proxy",
"hwlab-cli",
"hwlab-agent-skills"
],
"requiredServiceServiceIds": [
"hwlab-cloud-api",
"hwlab-cloud-web",
"hwlab-gateway",
"hwlab-cli",
"hwlab-agent-skills",
"hwlab-agent-mgr",
"hwlab-gateway-simu",
"hwlab-box-simu",
"hwlab-patch-panel",
"hwlab-router",
"hwlab-tunnel-client",
"hwlab-edge-proxy"
]
},
"validation": {
"script": "scripts/validate-runtime-boundary.mjs",
"commands": [
"node --check scripts/validate-runtime-boundary.mjs",
"node scripts/validate-runtime-boundary.mjs"
]
}
}
+343
View File
@@ -0,0 +1,343 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { DEV_ENDPOINT, ENVIRONMENT_DEV, SERVICE_IDS } from "../internal/protocol/index.mjs";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const serviceIdLabel = "hwlab.pikastech.local/service-id";
const namespace = "hwlab-dev";
const forbiddenRuntimeSubstitutes = Object.freeze([
"unidesk-backend",
"provider-gateway",
"microservice-proxy"
]);
async function readText(relativePath) {
return readFile(path.join(repoRoot, relativePath), "utf8");
}
async function readJSON(relativePath) {
const raw = await readText(relativePath);
return JSON.parse(raw);
}
function assertObject(value, label) {
assert.ok(value && typeof value === "object" && !Array.isArray(value), `${label} must be an object`);
}
function assertArray(value, label) {
assert.ok(Array.isArray(value), `${label} must be an array`);
}
function assertUnique(values, label) {
assert.equal(new Set(values).size, values.length, `${label} must be unique`);
}
function assertSameMembers(actual, expected, label) {
assert.deepEqual([...actual].sort(), [...expected].sort(), `${label} must match expected members`);
}
function assertServiceId(serviceId, label) {
assert.ok(SERVICE_IDS.includes(serviceId), `${label} uses unknown serviceId ${JSON.stringify(serviceId)}`);
assert.match(serviceId, /^hwlab-/, `${label} must be an HWLAB service id`);
assertNoForbiddenSubstitute(serviceId, label);
}
function assertDevOnlyEnvironment(value, label) {
assert.equal(value, ENVIRONMENT_DEV, `${label} must be dev`);
}
function assertDevNamespace(value, label) {
assert.equal(value, namespace, `${label} must be ${namespace}`);
}
function assertForbiddenList(actual, label) {
assertArray(actual, label);
assertSameMembers(actual, forbiddenRuntimeSubstitutes, label);
}
function splitCsv(value) {
assert.equal(typeof value, "string", "CSV value must be a string");
return value
.split(",")
.map((item) => item.trim())
.filter(Boolean);
}
function assertNoForbiddenSubstitute(value, label) {
if (typeof value !== "string") {
return;
}
const lower = value.toLowerCase();
for (const forbidden of forbiddenRuntimeSubstitutes) {
assert.equal(
lower.includes(forbidden),
false,
`${label} must not use forbidden runtime substitute ${forbidden}`
);
}
}
function assertNoForbiddenSubstitutesDeep(value, label) {
if (
label.endsWith(".HWLAB_RUNTIME_SUBSTITUTE_FORBIDDEN") ||
label.endsWith(".forbiddenRuntimeSubstitutes")
) {
return;
}
if (typeof value === "string") {
assertNoForbiddenSubstitute(value, label);
return;
}
if (Array.isArray(value)) {
value.forEach((item, index) => assertNoForbiddenSubstitutesDeep(item, `${label}[${index}]`));
return;
}
if (value && typeof value === "object") {
if (value.name === "HWLAB_RUNTIME_SUBSTITUTE_FORBIDDEN") {
return;
}
for (const [key, item] of Object.entries(value)) {
assertNoForbiddenSubstitutesDeep(item, `${label}.${key}`);
}
}
}
function containerList(workload, label) {
const template = workload.kind === "Job" ? workload.spec?.template : workload.spec?.template;
const containers = template?.spec?.containers;
assertArray(containers, `${label}.containers`);
return containers;
}
function labelsFor(resource, label) {
const labels = resource.metadata?.labels;
assertObject(labels, `${label}.metadata.labels`);
return labels;
}
function assertMetadataServiceId(resource, expectedServiceId, label) {
assert.equal(resource.metadata?.name, expectedServiceId, `${label}.metadata.name must match service id`);
assertDevNamespace(resource.metadata?.namespace, `${label}.metadata.namespace`);
const labels = labelsFor(resource, label);
assert.equal(labels[serviceIdLabel], expectedServiceId, `${label} service-id label`);
assert.equal(labels["app.kubernetes.io/name"], expectedServiceId, `${label} app name label`);
}
function assertTemplateServiceId(workload, expectedServiceId, label) {
const templateLabels = workload.spec?.template?.metadata?.labels;
assertObject(templateLabels, `${label}.spec.template.metadata.labels`);
assert.equal(templateLabels[serviceIdLabel], expectedServiceId, `${label} template service-id label`);
assert.equal(templateLabels["app.kubernetes.io/name"], expectedServiceId, `${label} template app name label`);
}
function assertHwlabImage(image, serviceId, label) {
assert.equal(
typeof image,
"string",
`${label}.image must be a string`
);
assert.match(
image,
new RegExp(`^ghcr\\.io/pikastech/${serviceId}:[a-f0-9]{7,40}$`),
`${label}.image must be the HWLAB image for ${serviceId}`
);
}
function assertHealthProbe(container, serviceId, label) {
if (serviceId === "hwlab-agent-worker" || serviceId === "hwlab-cli") {
return;
}
for (const probeName of ["readinessProbe", "livenessProbe"]) {
assert.equal(
container[probeName]?.httpGet?.path,
"/health/live",
`${label}.${probeName} must use /health/live`
);
}
}
function assertNoExternalNameSubstitute(service, label) {
assert.notEqual(service.spec?.type, "ExternalName", `${label} must not be an ExternalName substitute`);
assert.equal(service.spec?.type, "ClusterIP", `${label} must be a ClusterIP skeleton service`);
}
function assertKustomizationDevOnly(kustomization, label) {
assert.equal(kustomization.kind, "Kustomization", `${label}.kind`);
assertDevNamespace(kustomization.namespace, `${label}.namespace`);
assert.equal(kustomization.commonLabels?.["hwlab.pikastech.local/profile"], "dev", `${label} profile label`);
assert.ok(kustomization.resources?.includes("../base"), `${label} must include ../base`);
}
function assertRuntimePolicyText(value, label) {
assert.equal(typeof value, "string", `${label} must be a string`);
assert.ok(value.includes("HWLAB runtime"), `${label} must name HWLAB runtime`);
assert.ok(value.includes("UniDesk backend"), `${label} must name UniDesk backend`);
assert.ok(value.includes("provider-gateway"), `${label} must name provider-gateway`);
assert.ok(value.includes("microservice proxy"), `${label} must name microservice proxy`);
}
function assertGuardFixture(guard) {
assert.equal(guard.guard, "hwlab-runtime-boundary");
assert.equal(guard.schemaVersion, "v1");
assertDevOnlyEnvironment(guard.environment, "guard.environment");
assert.equal(guard.endpoint, DEV_ENDPOINT, "guard.endpoint");
assertDevNamespace(guard.namespace, "guard.namespace");
assert.equal(guard.policy?.runtimeSubstitution, "forbidden", "guard runtime substitution policy");
assert.equal(guard.policy?.prodDeployment, "forbidden", "guard prod policy");
assertForbiddenList(guard.policy?.forbiddenRuntimeSubstitutes, "guard forbidden substitutes");
assertSameMembers(guard.requiredManifest?.requiredServiceIds, SERVICE_IDS, "guard manifest service ids");
assertSameMembers(guard.requiredK3sSkeleton?.requiredWorkloadServiceIds, SERVICE_IDS, "guard workload service ids");
assert.ok(
guard.validation?.commands?.includes("node scripts/validate-runtime-boundary.mjs"),
"guard must record executable validation command"
);
}
function assertDeployManifest(manifest, guard) {
assert.equal(manifest.manifestVersion, "v1");
assertDevOnlyEnvironment(manifest.environment, "deploy manifest environment");
assertDevNamespace(manifest.namespace, "deploy manifest namespace");
assert.equal(manifest.endpoint, DEV_ENDPOINT, "deploy manifest endpoint");
assert.ok(manifest.profiles?.dev?.enabled, "deploy manifest dev profile must be enabled");
assertDevNamespace(manifest.profiles.dev.namespace, "deploy manifest dev profile namespace");
assert.equal(manifest.profiles.dev.endpoint, DEV_ENDPOINT, "deploy manifest dev profile endpoint");
assert.equal(manifest.profiles?.prod?.enabled, false, "deploy manifest prod profile must stay disabled");
assertArray(manifest.services, "deploy manifest services");
const serviceIds = manifest.services.map((service) => service.serviceId);
assertUnique(serviceIds, "deploy manifest service ids");
assertSameMembers(serviceIds, guard.requiredManifest.requiredServiceIds, "deploy manifest service ids");
assertSameMembers(serviceIds, SERVICE_IDS, "deploy manifest service ids");
for (const service of manifest.services) {
assertServiceId(service.serviceId, `deploy service ${service.serviceId}`);
assertHwlabImage(service.image, service.serviceId, `deploy service ${service.serviceId}`);
assertDevNamespace(service.namespace, `${service.serviceId}.namespace`);
assert.equal(service.profile, "dev", `${service.serviceId}.profile must be dev`);
assert.match(service.healthPath, /^\//, `${service.serviceId}.healthPath must be absolute`);
assertNoForbiddenSubstitutesDeep(service.env ?? {}, `${service.serviceId}.env`);
}
const cloudApi = manifest.services.find((service) => service.serviceId === "hwlab-cloud-api");
assertForbiddenList(
splitCsv(cloudApi?.env?.HWLAB_RUNTIME_SUBSTITUTE_FORBIDDEN),
"hwlab-cloud-api HWLAB_RUNTIME_SUBSTITUTE_FORBIDDEN"
);
}
function assertMasterHealthContract(contract, guard) {
assertDevOnlyEnvironment(contract.environment, "master health contract environment");
assert.equal(contract.endpoint, DEV_ENDPOINT, "master health contract endpoint");
assert.equal(contract.prodAcceptance, false, "master health contract prodAcceptance");
assert.equal(contract.reverseLink?.mode, guard.requiredHealthContract.reverseLink.mode, "reverse link mode");
assert.equal(
contract.reverseLink?.direction,
guard.requiredHealthContract.reverseLink.direction,
"reverse link direction"
);
assert.equal(contract.reverseLink?.client, "hwlab-tunnel-client", "reverse link client");
assert.equal(contract.reverseLink?.publicPort, 6667, "reverse link public port");
assertForbiddenList(contract.forbiddenRuntimeSubstitutes, "master forbidden substitutes");
assertArray(contract.contracts, "master health contracts");
const serviceIds = contract.contracts.map((item) => item.serviceId);
assertUnique(serviceIds, "master health contract service ids");
assertSameMembers(
serviceIds,
guard.requiredHealthContract.requiredContractServiceIds,
"master health contract service ids"
);
for (const item of contract.contracts) {
assertServiceId(item.serviceId, `master health contract ${item.serviceId}`);
}
}
function assertWorkloads(workloads, guard) {
assert.equal(workloads.kind, "List", "workloads kind");
assertArray(workloads.items, "workloads items");
const serviceIds = workloads.items.map((item) => item.metadata?.labels?.[serviceIdLabel]);
assertUnique(serviceIds, "workload service ids");
assertSameMembers(serviceIds, guard.requiredK3sSkeleton.requiredWorkloadServiceIds, "workload service ids");
for (const workload of workloads.items) {
const serviceId = workload.metadata?.labels?.[serviceIdLabel];
assertServiceId(serviceId, `workload ${workload.metadata?.name}`);
const expectedName = workload.kind === "Job" ? `${serviceId}-template` : serviceId;
assert.equal(workload.metadata?.name, expectedName, `${serviceId} workload name`);
assertDevNamespace(workload.metadata?.namespace, `${serviceId} workload namespace`);
assert.equal(labelsFor(workload, serviceId)["app.kubernetes.io/name"], serviceId, `${serviceId} workload app label`);
assertTemplateServiceId(workload, serviceId, `workload ${serviceId}`);
const containers = containerList(workload, `workload ${serviceId}`);
assert.equal(containers.length, 1, `${serviceId} workload must have one container`);
const container = containers[0];
assert.equal(container.name, serviceId, `${serviceId} container name`);
assertHwlabImage(container.image, serviceId, `${serviceId} container`);
assertHealthProbe(container, serviceId, `${serviceId} container`);
assertNoForbiddenSubstitutesDeep(container.env ?? [], `${serviceId} container env`);
}
}
function assertServices(services, guard) {
assert.equal(services.kind, "List", "services kind");
assertArray(services.items, "services items");
const serviceIds = services.items.map((item) => item.metadata?.labels?.[serviceIdLabel]);
assertUnique(serviceIds, "k8s service ids");
assertSameMembers(serviceIds, guard.requiredK3sSkeleton.requiredServiceServiceIds, "k8s service ids");
for (const service of services.items) {
const serviceId = service.metadata?.labels?.[serviceIdLabel];
assertServiceId(serviceId, `k8s service ${service.metadata?.name}`);
assertMetadataServiceId(service, serviceId, `k8s service ${serviceId}`);
assertNoExternalNameSubstitute(service, `k8s service ${serviceId}`);
assert.equal(service.spec?.selector?.["app.kubernetes.io/name"], serviceId, `${serviceId} selector`);
}
}
function assertDevHealthConfig(configMap) {
assert.equal(configMap.kind, "ConfigMap", "dev health contract kind");
assertDevNamespace(configMap.metadata?.namespace, "dev health contract namespace");
assert.equal(configMap.data?.endpoint, DEV_ENDPOINT, "dev health contract endpoint");
assertRuntimePolicyText(configMap.data?.["runtime-substitute-policy"], "dev runtime substitute policy");
}
function assertProdDisabled(prodKustomization, prodDisabled) {
assert.equal(prodKustomization.kind, "Kustomization", "prod kustomization kind");
assert.deepEqual(prodKustomization.resources, ["prod-disabled.yaml"], "prod kustomization resources");
assert.equal(prodDisabled.kind, "ConfigMap", "prod disabled kind");
assert.equal(prodDisabled.metadata?.labels?.["hwlab.pikastech.local/profile"], "prod", "prod disabled profile");
assert.equal(
prodDisabled.metadata?.labels?.["hwlab.pikastech.local/acceptance"],
"disabled",
"prod disabled acceptance"
);
assert.equal(prodDisabled.data?.status, "disabled", "prod disabled status");
}
const guard = await readJSON("protocol/examples/runtime-boundary/guard.dev.json");
assertGuardFixture(guard);
assertDeployManifest(await readJSON(guard.requiredManifest.path), guard);
assertMasterHealthContract(await readJSON(guard.requiredHealthContract.path), guard);
assertWorkloads(await readJSON(guard.requiredK3sSkeleton.workloadsPath), guard);
assertServices(await readJSON(guard.requiredK3sSkeleton.servicesPath), guard);
assertKustomizationDevOnly(await readJSON(guard.requiredK3sSkeleton.devKustomizationPath), "dev kustomization");
assertDevHealthConfig(await readJSON(guard.requiredK3sSkeleton.devHealthContractPath));
assertProdDisabled(
await readJSON(guard.requiredK3sSkeleton.prodKustomizationPath),
await readJSON(guard.requiredK3sSkeleton.prodDisabledPath)
);
console.log(
`validated HWLAB runtime boundary guard for ${SERVICE_IDS.length} services; forbidden substitutes: ${forbiddenRuntimeSubstitutes.join(", ")}`
);