@forgezero/agent 0.1.74 → 0.1.76
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 +126 -17
- package/dist/agent-heartbeat.d.ts +25 -1
- package/dist/agent-heartbeat.js +45 -5
- package/dist/bootstrap.js +10 -7
- package/dist/community-rehearsal-host.js +218 -13
- package/dist/credential-schema.d.ts +1 -1
- package/dist/credential-schema.js +1 -1
- package/dist/definition.js +206 -7
- package/dist/deploy-actions.d.ts +7 -0
- package/dist/deploy-compiler.js +191 -10
- package/dist/deploy-file.js +206 -7
- package/dist/deploy-plan-runner.d.ts +3 -0
- package/dist/deploy-plan-runner.js +190 -9
- package/dist/deploy-plan.js +187 -8
- package/dist/deploy-providers.d.ts +19 -0
- package/dist/deploy.d.ts +89 -3
- package/dist/deploy.js +49 -2
- package/dist/deployment-connectivity.d.ts +63 -0
- package/dist/deployment-connectivity.js +155 -0
- package/dist/deployment-pull.d.ts +2 -0
- package/dist/deployment-targets.d.ts +9 -0
- package/dist/deployment-targets.js +141 -0
- package/dist/deployment.d.ts +69 -0
- package/dist/fz-agent.js +1122 -313
- package/dist/fz.js +234 -26
- package/dist/index.d.ts +1 -0
- package/dist/metal-bootstrap.js +1 -1
- package/dist/metal-helper-socket.js +210 -11
- package/dist/metal-provision.js +210 -11
- package/dist/operator-bootstrap.js +11 -9
- package/dist/platform-bootstrap-runtime.js +209 -10
- package/dist/platform-fleet-verification.js +525 -155
- package/dist/platform-genesis.js +206 -7
- package/dist/process-input.d.ts +11 -0
- package/dist/provision.js +472 -99
- package/dist/service-supervisor.d.ts +6 -0
- package/dist/software-helper.d.ts +3 -0
- package/dist/software-helper.js +472 -98
- package/dist/software.d.ts +4 -1
- package/dist/software.js +210 -8
- package/dist/ubuntu.js +206 -7
- package/dist/version.d.ts +1 -1
- package/package.json +12 -4
package/dist/deploy-plan.js
CHANGED
|
@@ -38,6 +38,13 @@ function validateBuiltInAction(definition, context, where) {
|
|
|
38
38
|
throw new Error(`${where}.with.requirements must name existing requirements`);
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
+
if (definition.uses === "forgezero.network/ensure@1") {
|
|
42
|
+
exact(withValue, ["target"], `${where}.with`);
|
|
43
|
+
if (typeof withValue.target !== "string" || !Object.values(context.components).some((entry) => entry.target === withValue.target)) {
|
|
44
|
+
throw new Error(`${where}.with.target must name a target used by a component`);
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
41
48
|
if (definition.uses === "forgezero.exec/argv@1") {
|
|
42
49
|
const allowed = ["component", "argv", "credentials"];
|
|
43
50
|
const unknown = Object.keys(withValue).filter((key) => !allowed.includes(key));
|
|
@@ -93,6 +100,8 @@ function validateBuiltInProvider(requirement2, where) {
|
|
|
93
100
|
const expected = {
|
|
94
101
|
"forgezero.bun": { capability: "runtime.javascript", versions: ["1.3.14"], keys: ["installScope"] },
|
|
95
102
|
"forgezero.docker": { capability: "runtime.container", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "storageDriver", "dataRoot", "liveRestore", "logDriver", "defaultNetwork", "rootless"] },
|
|
103
|
+
"forgezero.containerd": { capability: "runtime.container", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "runtimeClass", "namespace", "snapshotter"] },
|
|
104
|
+
"forgezero.kata": { capability: "runtime.container", versions: ["4.0.0"], keys: ["installScope", "runtimeClass", "namespace", "hypervisor", "confidentialGuest"] },
|
|
96
105
|
"forgezero.nginx": { capability: "proxy.http", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "websocket", "maximumBodyMiB", "workerProcesses", "workerConnections"] },
|
|
97
106
|
"forgezero.arangodb": { capability: "database.arangodb", versions: ["3.11.14"], keys: ["installScope", "runtime", "dataPath", "bind"] },
|
|
98
107
|
"forgezero.cloudflared": { capability: "network.public-tunnel", versions: ["2026.7.3"], keys: ["installScope", "mode"] },
|
|
@@ -113,6 +122,10 @@ function validateBuiltInProvider(requirement2, where) {
|
|
|
113
122
|
if (row2.storageDriver !== "overlay2" || row2.dataRoot !== "/var/lib/docker" || row2.liveRestore !== true || row2.logDriver !== "local" || row2.defaultNetwork !== "bridge" || row2.rootless !== false)
|
|
114
123
|
throw new Error(`${where} Docker config is unsupported by the fixed host strategy`);
|
|
115
124
|
}
|
|
125
|
+
if (requirement2.provider === "forgezero.containerd" && (row2.runtimeClass !== "runc" || row2.namespace !== "forgezero" || row2.snapshotter !== "overlayfs"))
|
|
126
|
+
throw new Error(`${where} containerd config is unsupported by the fixed host strategy`);
|
|
127
|
+
if (requirement2.provider === "forgezero.kata" && (row2.runtimeClass !== "kata-qemu-snp" || row2.namespace !== "forgezero" || row2.hypervisor !== "qemu" || row2.confidentialGuest !== "sev-snp"))
|
|
128
|
+
throw new Error(`${where} Kata config is unsupported by the fixed host strategy`);
|
|
116
129
|
if (requirement2.provider === "forgezero.nginx" && (typeof row2.websocket !== "boolean" || !Number.isInteger(row2.maximumBodyMiB) || Number(row2.maximumBodyMiB) < 1 || Number(row2.maximumBodyMiB) > 1024 || !(row2.workerProcesses === "auto" || Number.isInteger(row2.workerProcesses)) || !Number.isInteger(row2.workerConnections)))
|
|
117
130
|
throw new Error(`${where} Nginx config is invalid`);
|
|
118
131
|
if (requirement2.provider === "forgezero.arangodb" && (row2.runtime !== "native" || !absolute(row2.dataPath) || row2.bind !== "private"))
|
|
@@ -190,7 +203,7 @@ function reference(value, where) {
|
|
|
190
203
|
const row2 = object(value, where);
|
|
191
204
|
exact3(row2, ["$ref"], where);
|
|
192
205
|
const coordinate = boundedString(row2.$ref, `${where}.$ref`, 256);
|
|
193
|
-
if (!/^(inputs\.[a-z][A-Za-z0-9-]{0,62}|steps\.[a-z][A-Za-z0-9-]{0,62}\.(status|outputs\.[a-z][A-Za-z0-9-]{0,62})|components\.[a-z][A-Za-z0-9-]{0,62}\.[a-z][A-Za-z0-9-]{0,62}|targets\.[a-z][A-Za-z0-9-]{0,62}\.[a-z][A-Za-z0-9-]{0,62}|deployment\.[a-z][A-Za-z0-9-]{0,62})$/.test(coordinate)) {
|
|
206
|
+
if (!/^(inputs\.[a-z][A-Za-z0-9-]{0,62}|steps\.[a-z][A-Za-z0-9-]{0,62}\.(status|outputs\.[a-z][A-Za-z0-9-]{0,62})|components\.[a-z][A-Za-z0-9-]{0,62}\.[a-z][A-Za-z0-9-]{0,62}|targets\.[a-z][A-Za-z0-9-]{0,62}\.[a-z][A-Za-z0-9-]{0,62}|deployment\.[a-z][A-Za-z0-9-]{0,62}|execution\.(targetName|slot|releaseExecutor|runtime|cpuCores|memoryMiB|storageGiB))$/.test(coordinate)) {
|
|
194
207
|
fail(where, "contains an unsupported reference.");
|
|
195
208
|
}
|
|
196
209
|
return { $ref: coordinate };
|
|
@@ -430,10 +443,9 @@ function validateNetwork(value, where) {
|
|
|
430
443
|
named(container.network, `${where}.container.network`);
|
|
431
444
|
}
|
|
432
445
|
}
|
|
433
|
-
function validateComponent(value, where,
|
|
446
|
+
function validateComponent(value, where, targets, requirements) {
|
|
434
447
|
const row2 = object(value, where);
|
|
435
|
-
|
|
436
|
-
fail(`${where}.target`, "must name an existing target.");
|
|
448
|
+
const selectedTarget = targets.get(String(row2.target)) ?? fail(`${where}.target`, "must name an existing target.");
|
|
437
449
|
if (row2.kind === "application") {
|
|
438
450
|
exact3(row2, ["kind", "target", "runtime", "service", "resources", "storage", "network", "rollout"], where);
|
|
439
451
|
const runtime = object(row2.runtime, `${where}.runtime`);
|
|
@@ -444,7 +456,9 @@ function validateComponent(value, where, targetNames, requirements) {
|
|
|
444
456
|
exact3(runtime, ["kind", "provider", "requirement", "argv"], `${where}.runtime`);
|
|
445
457
|
validateArgv(runtime.argv, `${where}.runtime.argv`);
|
|
446
458
|
} else if (runtime.kind === "container") {
|
|
447
|
-
exact3(runtime, ["kind", "provider", "requirement", "image", "entrypoint", "security"], `${where}.runtime`);
|
|
459
|
+
exact3(runtime, ["kind", "provider", "requirement", "runtimeClass", "image", "entrypoint", "security"], `${where}.runtime`);
|
|
460
|
+
if (runtime.runtimeClass !== undefined && !["runc", "kata-qemu-snp"].includes(String(runtime.runtimeClass)))
|
|
461
|
+
fail(`${where}.runtime.runtimeClass`, "is unsupported.");
|
|
448
462
|
const image = object(runtime.image, `${where}.runtime.image`);
|
|
449
463
|
exact3(image, ["source"], `${where}.runtime.image`);
|
|
450
464
|
const source = object(image.source, `${where}.runtime.image.source`);
|
|
@@ -478,6 +492,23 @@ function validateComponent(value, where, targetNames, requirements) {
|
|
|
478
492
|
fail(`${where}.runtime.requirement`, "must provide runtime.container.");
|
|
479
493
|
if (runtimeRequirement && runtime.kind === "native" && !runtimeRequirement.capability.startsWith("runtime."))
|
|
480
494
|
fail(`${where}.runtime.requirement`, "must provide a runtime capability.");
|
|
495
|
+
const execution = selectedTarget.allocation.execution;
|
|
496
|
+
if (execution === "native" && runtime.kind !== "native")
|
|
497
|
+
fail(`${where}.runtime.kind`, "must be native for the selected target.");
|
|
498
|
+
if (execution !== "native" && runtime.kind !== "container")
|
|
499
|
+
fail(`${where}.runtime.kind`, "must be container for the selected target.");
|
|
500
|
+
if (runtime.kind === "container" && execution === "oci-runc") {
|
|
501
|
+
if (!["forgezero.containerd", "forgezero.docker"].includes(String(runtime.provider)))
|
|
502
|
+
fail(`${where}.runtime.provider`, "must be forgezero.containerd for an OCI runc target.");
|
|
503
|
+
if (runtime.runtimeClass !== undefined && runtime.runtimeClass !== "runc")
|
|
504
|
+
fail(`${where}.runtime.runtimeClass`, "must be runc for the selected target.");
|
|
505
|
+
}
|
|
506
|
+
if (runtime.kind === "container" && execution === "oci-kata-qemu-snp") {
|
|
507
|
+
if (runtime.provider !== "forgezero.kata")
|
|
508
|
+
fail(`${where}.runtime.provider`, "must be forgezero.kata for a Kata QEMU SNP target.");
|
|
509
|
+
if (runtime.runtimeClass !== "kata-qemu-snp")
|
|
510
|
+
fail(`${where}.runtime.runtimeClass`, "must be kata-qemu-snp for the selected target.");
|
|
511
|
+
}
|
|
481
512
|
const service = object(row2.service, `${where}.service`);
|
|
482
513
|
exact3(service, ["protocol", "port", "health", "websocket", "maximumConnections"], `${where}.service`);
|
|
483
514
|
if (service.protocol !== "http")
|
|
@@ -509,6 +540,18 @@ function validateComponent(value, where, targetNames, requirements) {
|
|
|
509
540
|
const network = object(row2.network, `${where}.network`);
|
|
510
541
|
if (network.container === undefined)
|
|
511
542
|
fail(`${where}.network.container`, "must select bridge or reviewed host networking.");
|
|
543
|
+
if (selectedTarget.allocation.sharing === "shared") {
|
|
544
|
+
if (object(runtime.security, `${where}.runtime.security`).root !== "read-only")
|
|
545
|
+
fail(`${where}.runtime.security.root`, "must be read-only on a shared compute.");
|
|
546
|
+
if (network.container.mode !== "bridge")
|
|
547
|
+
fail(`${where}.network.container.mode`, "must be bridge on a shared compute.");
|
|
548
|
+
if (Array.isArray(row2.storage) && row2.storage.some((mount) => mount.class !== "ephemeral")) {
|
|
549
|
+
fail(`${where}.storage`, "may contain only size-bounded tmpfs mounts on a shared compute.");
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (runtime.kind === "native" && selectedTarget.allocation.sharing !== "exclusive") {
|
|
554
|
+
fail(`${where}.target`, "native applications require an exclusive compute.");
|
|
512
555
|
}
|
|
513
556
|
if (row2.storage !== undefined) {
|
|
514
557
|
if (!Array.isArray(row2.storage) || row2.storage.length > 32)
|
|
@@ -545,6 +588,8 @@ function validateComponent(value, where, targetNames, requirements) {
|
|
|
545
588
|
fail(`${where}.runtime.mode`, "must match the provider runtime mode.");
|
|
546
589
|
if (runtime.mode !== "native")
|
|
547
590
|
fail(`${where}.runtime.mode`, "must be native in deployment plan v1.");
|
|
591
|
+
if (selectedTarget.allocation.execution !== "native")
|
|
592
|
+
fail(`${where}.target`, "must select a native execution target for database components.");
|
|
548
593
|
const topology = object(row2.topology, `${where}.topology`);
|
|
549
594
|
if (topology.mode === "standalone")
|
|
550
595
|
exact3(topology, ["mode"], `${where}.topology`);
|
|
@@ -741,15 +786,20 @@ function compileDeployment(sourceValue) {
|
|
|
741
786
|
if (targetEntries.length < 1 || targetEntries.length > 64)
|
|
742
787
|
fail("deployment.spec.targets", "must contain 1 to 64 targets.");
|
|
743
788
|
const targetNames = new Set(targetEntries.map(([targetName]) => named(targetName, "deployment.spec.targets key")));
|
|
789
|
+
const targetDefinitions = new Map;
|
|
744
790
|
for (const [targetName, value] of targetEntries) {
|
|
745
791
|
const target = object(value, `deployment.spec.targets.${targetName}`);
|
|
746
|
-
exact3(target, ["kind", "selector", "cardinality", "placement"], `deployment.spec.targets.${targetName}`);
|
|
792
|
+
exact3(target, ["kind", "selector", "cardinality", "allocation", "provisioning", "connectivity", "placement"], `deployment.spec.targets.${targetName}`);
|
|
747
793
|
if (target.kind !== "compute")
|
|
748
794
|
fail(`deployment.spec.targets.${targetName}.kind`, "must be compute.");
|
|
749
795
|
const selector = object(target.selector, `deployment.spec.targets.${targetName}.selector`);
|
|
750
|
-
exact3(selector, ["profiles", "confidentialCompute", "labels"], `deployment.spec.targets.${targetName}.selector`);
|
|
796
|
+
exact3(selector, ["profiles", "operatingSystem", "confidentialCompute", "labels"], `deployment.spec.targets.${targetName}.selector`);
|
|
751
797
|
if (!Array.isArray(selector.profiles) || selector.profiles.length < 1 || selector.profiles.length > 32 || selector.profiles.some((profile) => typeof profile !== "string" || !NAME.test(profile)) || new Set(selector.profiles).size !== selector.profiles.length)
|
|
752
798
|
fail(`deployment.spec.targets.${targetName}.selector.profiles`, "is invalid.");
|
|
799
|
+
const operatingSystem = object(selector.operatingSystem, `deployment.spec.targets.${targetName}.selector.operatingSystem`);
|
|
800
|
+
exact3(operatingSystem, ["id", "version", "architecture"], `deployment.spec.targets.${targetName}.selector.operatingSystem`);
|
|
801
|
+
if (operatingSystem.id !== "ubuntu" || !["24.04", "26.04"].includes(String(operatingSystem.version)) || operatingSystem.architecture !== "x64")
|
|
802
|
+
fail(`deployment.spec.targets.${targetName}.selector.operatingSystem`, "is unsupported.");
|
|
753
803
|
if (selector.confidentialCompute !== undefined && !["required", "preferred", "disabled"].includes(String(selector.confidentialCompute)))
|
|
754
804
|
fail(`deployment.spec.targets.${targetName}.selector.confidentialCompute`, "is unsupported.");
|
|
755
805
|
if (selector.labels !== undefined)
|
|
@@ -762,8 +812,108 @@ function compileDeployment(sourceValue) {
|
|
|
762
812
|
integer(cardinality.desired, `deployment.spec.targets.${targetName}.cardinality.desired`, minimum, maximum);
|
|
763
813
|
else
|
|
764
814
|
reference(cardinality.desired, `deployment.spec.targets.${targetName}.cardinality.desired`);
|
|
815
|
+
const allocation = object(target.allocation, `deployment.spec.targets.${targetName}.allocation`);
|
|
816
|
+
exact3(allocation, ["execution", "sharing", "resources", "existing"], `deployment.spec.targets.${targetName}.allocation`);
|
|
817
|
+
if (!["native", "oci-runc", "oci-kata-qemu-snp"].includes(String(allocation.execution)))
|
|
818
|
+
fail(`deployment.spec.targets.${targetName}.allocation.execution`, "is unsupported.");
|
|
819
|
+
if (!["exclusive", "shared"].includes(String(allocation.sharing)))
|
|
820
|
+
fail(`deployment.spec.targets.${targetName}.allocation.sharing`, "is unsupported.");
|
|
821
|
+
if (!["prefer", "require"].includes(String(allocation.existing)))
|
|
822
|
+
fail(`deployment.spec.targets.${targetName}.allocation.existing`, "is unsupported.");
|
|
823
|
+
const capacity = object(allocation.resources, `deployment.spec.targets.${targetName}.allocation.resources`);
|
|
824
|
+
exact3(capacity, ["cpuCores", "memoryMiB", "storageGiB"], `deployment.spec.targets.${targetName}.allocation.resources`);
|
|
825
|
+
const capacityValue = (value2, where, minimumValue, maximumValue) => {
|
|
826
|
+
if (typeof value2 === "number")
|
|
827
|
+
integer(value2, where, minimumValue, maximumValue);
|
|
828
|
+
else
|
|
829
|
+
reference(value2, where);
|
|
830
|
+
};
|
|
831
|
+
capacityValue(capacity.cpuCores, `deployment.spec.targets.${targetName}.allocation.resources.cpuCores`, 1, 1024);
|
|
832
|
+
capacityValue(capacity.memoryMiB, `deployment.spec.targets.${targetName}.allocation.resources.memoryMiB`, 128, 4194304);
|
|
833
|
+
capacityValue(capacity.storageGiB, `deployment.spec.targets.${targetName}.allocation.resources.storageGiB`, 1, 1048576);
|
|
834
|
+
if (allocation.execution === "oci-kata-qemu-snp" && selector.confidentialCompute !== "required")
|
|
835
|
+
fail(`deployment.spec.targets.${targetName}.selector.confidentialCompute`, "must be required for Kata QEMU SNP.");
|
|
836
|
+
if ((allocation.execution === "oci-kata-qemu-snp" || selector.confidentialCompute === "required") && operatingSystem.version !== "26.04")
|
|
837
|
+
fail(`deployment.spec.targets.${targetName}.selector.operatingSystem.version`, "must be Ubuntu 26.04 for SEV-SNP.");
|
|
838
|
+
if (allocation.execution !== "oci-kata-qemu-snp" && selector.confidentialCompute === "required" && allocation.execution !== "native")
|
|
839
|
+
fail(`deployment.spec.targets.${targetName}.allocation.execution`, "cannot satisfy required confidential compute.");
|
|
840
|
+
if (target.provisioning !== undefined) {
|
|
841
|
+
const provisioning = object(target.provisioning, `deployment.spec.targets.${targetName}.provisioning`);
|
|
842
|
+
exact3(provisioning, ["planKey", "regionKey", "imageKey", "environmentKey", "ownership", "metalHostname", "maxMonthlySpendMinor"], `deployment.spec.targets.${targetName}.provisioning`);
|
|
843
|
+
for (const key of ["planKey", "regionKey", "imageKey", "environmentKey"])
|
|
844
|
+
named(provisioning[key], `deployment.spec.targets.${targetName}.provisioning.${key}`);
|
|
845
|
+
if (!["platform", "tenant-metal"].includes(String(provisioning.ownership)) || provisioning.ownership === "tenant-metal" !== Boolean(provisioning.metalHostname))
|
|
846
|
+
fail(`deployment.spec.targets.${targetName}.provisioning.ownership`, "must bind platform capacity or one tenant metal hostname.");
|
|
847
|
+
if (provisioning.metalHostname !== undefined && !/^(?=.{1,253}$)[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$/.test(String(provisioning.metalHostname)))
|
|
848
|
+
fail(`deployment.spec.targets.${targetName}.provisioning.metalHostname`, "is invalid.");
|
|
849
|
+
if (typeof provisioning.maxMonthlySpendMinor !== "string" || !/^\d{1,18}$/.test(provisioning.maxMonthlySpendMinor))
|
|
850
|
+
fail(`deployment.spec.targets.${targetName}.provisioning.maxMonthlySpendMinor`, "must be a bounded decimal minor-unit amount.");
|
|
851
|
+
}
|
|
852
|
+
if (allocation.existing === "prefer" && target.provisioning === undefined)
|
|
853
|
+
fail(`deployment.spec.targets.${targetName}.provisioning`, "is required when placement may provision after reuse.");
|
|
854
|
+
if (allocation.existing === "require" && target.provisioning !== undefined)
|
|
855
|
+
fail(`deployment.spec.targets.${targetName}.provisioning`, "must be omitted for reuse-only placement.");
|
|
856
|
+
if (target.connectivity !== undefined) {
|
|
857
|
+
const connectivity = object(target.connectivity, `deployment.spec.targets.${targetName}.connectivity`);
|
|
858
|
+
exact3(connectivity, ["private", "public"], `deployment.spec.targets.${targetName}.connectivity`);
|
|
859
|
+
const declaredPrivate = connectivity.private === undefined ? undefined : object(connectivity.private, `deployment.spec.targets.${targetName}.connectivity.private`);
|
|
860
|
+
if (declaredPrivate?.mode === "cloudflare-warp" && allocation.sharing !== "exclusive")
|
|
861
|
+
fail(`deployment.spec.targets.${targetName}.allocation.sharing`, "must be exclusive for a Cloudflare WARP connector.");
|
|
862
|
+
if (connectivity.private !== undefined) {
|
|
863
|
+
const privateNetwork = declaredPrivate;
|
|
864
|
+
if (privateNetwork.mode === "cloudflare-warp") {
|
|
865
|
+
exact3(privateNetwork, ["mode", "credential", "network"], `deployment.spec.targets.${targetName}.connectivity.private`);
|
|
866
|
+
if (!credentials[named(privateNetwork.credential, `deployment.spec.targets.${targetName}.connectivity.private.credential`)])
|
|
867
|
+
fail(`deployment.spec.targets.${targetName}.connectivity.private.credential`, "must name a declared credential.");
|
|
868
|
+
if (typeof privateNetwork.network === "string")
|
|
869
|
+
named(privateNetwork.network, `deployment.spec.targets.${targetName}.connectivity.private.network`);
|
|
870
|
+
else
|
|
871
|
+
reference(privateNetwork.network, `deployment.spec.targets.${targetName}.connectivity.private.network`);
|
|
872
|
+
} else {
|
|
873
|
+
exact3(privateNetwork, ["mode"], `deployment.spec.targets.${targetName}.connectivity.private`);
|
|
874
|
+
if (!["disabled", "private-lan"].includes(String(privateNetwork.mode)))
|
|
875
|
+
fail(`deployment.spec.targets.${targetName}.connectivity.private.mode`, "is unsupported.");
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
if (connectivity.public !== undefined) {
|
|
879
|
+
const publicNetwork = object(connectivity.public, `deployment.spec.targets.${targetName}.connectivity.public`);
|
|
880
|
+
if (publicNetwork.mode === "cloudflare-tunnel") {
|
|
881
|
+
exact3(publicNetwork, ["mode", "credential", "zone", "hostname"], `deployment.spec.targets.${targetName}.connectivity.public`);
|
|
882
|
+
if (!credentials[named(publicNetwork.credential, `deployment.spec.targets.${targetName}.connectivity.public.credential`)])
|
|
883
|
+
fail(`deployment.spec.targets.${targetName}.connectivity.public.credential`, "must name a declared credential.");
|
|
884
|
+
if (typeof publicNetwork.zone === "string") {
|
|
885
|
+
if (!/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(publicNetwork.zone))
|
|
886
|
+
fail(`deployment.spec.targets.${targetName}.connectivity.public.zone`, "is invalid.");
|
|
887
|
+
} else
|
|
888
|
+
reference(publicNetwork.zone, `deployment.spec.targets.${targetName}.connectivity.public.zone`);
|
|
889
|
+
const hostname = object(publicNetwork.hostname, `deployment.spec.targets.${targetName}.connectivity.public.hostname`);
|
|
890
|
+
const value2 = (coordinate, where, pattern) => {
|
|
891
|
+
if (typeof coordinate === "string") {
|
|
892
|
+
if (!pattern.test(coordinate))
|
|
893
|
+
fail(where, "is invalid.");
|
|
894
|
+
} else
|
|
895
|
+
reference(coordinate, where);
|
|
896
|
+
};
|
|
897
|
+
if (hostname.mode === "static") {
|
|
898
|
+
exact3(hostname, ["mode", "label"], `deployment.spec.targets.${targetName}.connectivity.public.hostname`);
|
|
899
|
+
value2(hostname.label, `deployment.spec.targets.${targetName}.connectivity.public.hostname.label`, /^[a-z0-9](?:[a-z0-9-]{0,48}[a-z0-9])?$/);
|
|
900
|
+
} else if (hostname.mode === "indexed") {
|
|
901
|
+
exact3(hostname, ["mode", "prefix", "startAt"], `deployment.spec.targets.${targetName}.connectivity.public.hostname`);
|
|
902
|
+
value2(hostname.prefix, `deployment.spec.targets.${targetName}.connectivity.public.hostname.prefix`, /^[a-z0-9](?:[a-z0-9-]{0,48}[a-z0-9])?$/);
|
|
903
|
+
if (hostname.startAt !== undefined)
|
|
904
|
+
integer(hostname.startAt, `deployment.spec.targets.${targetName}.connectivity.public.hostname.startAt`, 0, 1e6);
|
|
905
|
+
} else
|
|
906
|
+
fail(`deployment.spec.targets.${targetName}.connectivity.public.hostname.mode`, "is unsupported.");
|
|
907
|
+
} else {
|
|
908
|
+
exact3(publicNetwork, ["mode"], `deployment.spec.targets.${targetName}.connectivity.public`);
|
|
909
|
+
if (publicNetwork.mode !== "disabled")
|
|
910
|
+
fail(`deployment.spec.targets.${targetName}.connectivity.public.mode`, "is unsupported.");
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
765
914
|
if (target.placement !== undefined)
|
|
766
915
|
jsonValue(target.placement, `deployment.spec.targets.${targetName}.placement`);
|
|
916
|
+
targetDefinitions.set(targetName, target);
|
|
767
917
|
}
|
|
768
918
|
const requirements = object(spec.requirements ?? {}, "deployment.spec.requirements");
|
|
769
919
|
if (Object.keys(requirements).length > 64)
|
|
@@ -777,13 +927,34 @@ function compileDeployment(sourceValue) {
|
|
|
777
927
|
const key = `${requirementValue.provider}@${requirementValue.contract}:${requirementValue.capability}`;
|
|
778
928
|
requiredProviders.set(key, { provider: requirementValue.provider, contract: requirementValue.contract, capability: requirementValue.capability });
|
|
779
929
|
}
|
|
930
|
+
for (const [targetName, target] of targetDefinitions) {
|
|
931
|
+
if (target.connectivity?.public?.mode === "cloudflare-tunnel" && ![...requirementDefinitions.values()].some(({ capability }) => capability === "network.public-tunnel")) {
|
|
932
|
+
fail(`deployment.spec.targets.${targetName}.connectivity.public`, "requires one network.public-tunnel provider requirement.");
|
|
933
|
+
}
|
|
934
|
+
if (target.connectivity?.private?.mode === "cloudflare-warp" && ![...requirementDefinitions.values()].some(({ capability }) => capability === "network.private")) {
|
|
935
|
+
fail(`deployment.spec.targets.${targetName}.connectivity.private`, "requires one network.private provider requirement.");
|
|
936
|
+
}
|
|
937
|
+
}
|
|
780
938
|
const components = object(spec.components, "deployment.spec.components");
|
|
781
939
|
const componentEntries = Object.entries(components);
|
|
782
940
|
if (componentEntries.length < 1 || componentEntries.length > 128)
|
|
783
941
|
fail("deployment.spec.components", "must contain 1 to 128 components.");
|
|
784
942
|
for (const [componentName, value] of componentEntries) {
|
|
785
943
|
named(componentName, "deployment.spec.components key");
|
|
786
|
-
validateComponent(value, `deployment.spec.components.${componentName}`,
|
|
944
|
+
validateComponent(value, `deployment.spec.components.${componentName}`, targetDefinitions, requirementDefinitions);
|
|
945
|
+
}
|
|
946
|
+
for (const [targetName, target] of targetDefinitions) {
|
|
947
|
+
if (target.connectivity?.public?.mode !== "cloudflare-tunnel")
|
|
948
|
+
continue;
|
|
949
|
+
const publicApplications = componentEntries.filter(([, component2]) => {
|
|
950
|
+
const candidate = component2;
|
|
951
|
+
return candidate.kind === "application" && candidate.target === targetName && candidate.network?.public?.mode === "cloudflare-tunnel";
|
|
952
|
+
});
|
|
953
|
+
if (publicApplications.length !== 1)
|
|
954
|
+
fail(`deployment.spec.targets.${targetName}.connectivity.public`, "requires exactly one application on the target with network.public cloudflare-tunnel.");
|
|
955
|
+
const application = publicApplications[0][1];
|
|
956
|
+
if (application.kind !== "application" || !application.network?.ingress?.stablePort)
|
|
957
|
+
fail(`deployment.spec.components.${publicApplications[0][0]}.network.ingress`, "requires one stablePort for Tunnel ingress.");
|
|
787
958
|
}
|
|
788
959
|
const actionContext = {
|
|
789
960
|
components,
|
|
@@ -825,6 +996,14 @@ function compileDeployment(sourceValue) {
|
|
|
825
996
|
});
|
|
826
997
|
plannedWorkflows.push({ id: workflowName, ...workflowValue.concurrency ? { concurrency: workflowValue.concurrency } : {}, stages: plannedStages });
|
|
827
998
|
}
|
|
999
|
+
const deployWorkflow = plannedWorkflows.find(({ id }) => id === "deploy");
|
|
1000
|
+
for (const [targetName, target] of targetDefinitions) {
|
|
1001
|
+
if (!target.connectivity || target.connectivity.public?.mode !== "cloudflare-tunnel" && target.connectivity.private?.mode !== "cloudflare-warp")
|
|
1002
|
+
continue;
|
|
1003
|
+
const ensures = deployWorkflow?.stages.flatMap(({ steps }) => steps).filter((step2) => step2.uses === "forgezero.network/ensure@1" && step2.with.target === targetName && (step2.scope.kind === "each-target" || step2.scope.kind === "target-batches") && step2.scope.target === targetName) ?? [];
|
|
1004
|
+
if (ensures.length !== 1)
|
|
1005
|
+
fail(`deployment.spec.workflows.deploy`, `must contain exactly one network.ensure step scoped to connected target ${targetName}.`);
|
|
1006
|
+
}
|
|
828
1007
|
const typedSource = source;
|
|
829
1008
|
return {
|
|
830
1009
|
format: DEPLOY_PLAN_FORMAT,
|
|
@@ -11,6 +11,19 @@ export interface DockerProviderConfig {
|
|
|
11
11
|
defaultNetwork: 'bridge';
|
|
12
12
|
rootless: boolean;
|
|
13
13
|
}
|
|
14
|
+
export interface ContainerdProviderConfig {
|
|
15
|
+
installScope: 'host';
|
|
16
|
+
runtimeClass: 'runc';
|
|
17
|
+
namespace: 'forgezero';
|
|
18
|
+
snapshotter: 'overlayfs';
|
|
19
|
+
}
|
|
20
|
+
export interface KataProviderConfig {
|
|
21
|
+
installScope: 'host';
|
|
22
|
+
runtimeClass: 'kata-qemu-snp';
|
|
23
|
+
namespace: 'forgezero';
|
|
24
|
+
hypervisor: 'qemu';
|
|
25
|
+
confidentialGuest: 'sev-snp';
|
|
26
|
+
}
|
|
14
27
|
export interface NginxProviderConfig {
|
|
15
28
|
installScope: 'host';
|
|
16
29
|
websocket: boolean;
|
|
@@ -58,6 +71,12 @@ export declare const providers: {
|
|
|
58
71
|
readonly docker: {
|
|
59
72
|
readonly require: (options?: RequirementOptions<DockerProviderConfig>) => RequirementDefinition<import("./deploy").JsonObject>;
|
|
60
73
|
};
|
|
74
|
+
readonly containerd: {
|
|
75
|
+
readonly require: (options?: RequirementOptions<ContainerdProviderConfig>) => RequirementDefinition<import("./deploy").JsonObject>;
|
|
76
|
+
};
|
|
77
|
+
readonly kata: {
|
|
78
|
+
readonly require: (options?: RequirementOptions<KataProviderConfig>) => RequirementDefinition<import("./deploy").JsonObject>;
|
|
79
|
+
};
|
|
61
80
|
readonly nginx: {
|
|
62
81
|
readonly require: (options?: RequirementOptions<NginxProviderConfig>) => RequirementDefinition<import("./deploy").JsonObject>;
|
|
63
82
|
};
|
package/dist/deploy.d.ts
CHANGED
|
@@ -46,7 +46,33 @@ export type InputDefinition = {
|
|
|
46
46
|
default?: string;
|
|
47
47
|
};
|
|
48
48
|
export interface ValueReference {
|
|
49
|
-
readonly $ref: `inputs.${string}` | `steps.${string}.status` | `steps.${string}.outputs.${string}` | `components.${string}.${string}` | `targets.${string}.${string}` | `deployment.${string}`;
|
|
49
|
+
readonly $ref: `inputs.${string}` | `steps.${string}.status` | `steps.${string}.outputs.${string}` | `components.${string}.${string}` | `targets.${string}.${string}` | `deployment.${string}` | `execution.${string}`;
|
|
50
|
+
}
|
|
51
|
+
export type IntegerValue = number | ValueReference;
|
|
52
|
+
export type StringValue = string | ValueReference;
|
|
53
|
+
export interface ComputeConnectivityDefinition {
|
|
54
|
+
private?: {
|
|
55
|
+
mode: 'disabled' | 'private-lan';
|
|
56
|
+
} | {
|
|
57
|
+
mode: 'cloudflare-warp';
|
|
58
|
+
credential: string;
|
|
59
|
+
network: StringValue;
|
|
60
|
+
};
|
|
61
|
+
public?: {
|
|
62
|
+
mode: 'disabled';
|
|
63
|
+
} | {
|
|
64
|
+
mode: 'cloudflare-tunnel';
|
|
65
|
+
credential: string;
|
|
66
|
+
zone: StringValue;
|
|
67
|
+
hostname: {
|
|
68
|
+
mode: 'static';
|
|
69
|
+
label: StringValue;
|
|
70
|
+
} | {
|
|
71
|
+
mode: 'indexed';
|
|
72
|
+
prefix: StringValue;
|
|
73
|
+
startAt?: number;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
50
76
|
}
|
|
51
77
|
export type ResolvableJsonValue = JsonPrimitive | ValueReference | readonly ResolvableJsonValue[] | {
|
|
52
78
|
[key: string]: ResolvableJsonValue;
|
|
@@ -98,6 +124,11 @@ export interface TargetDefinition {
|
|
|
98
124
|
kind: 'compute';
|
|
99
125
|
selector: {
|
|
100
126
|
profiles: readonly string[];
|
|
127
|
+
operatingSystem: {
|
|
128
|
+
id: 'ubuntu';
|
|
129
|
+
version: '24.04' | '26.04';
|
|
130
|
+
architecture: 'x64';
|
|
131
|
+
};
|
|
101
132
|
confidentialCompute?: 'required' | 'preferred' | 'disabled';
|
|
102
133
|
labels?: Readonly<Record<string, string>>;
|
|
103
134
|
};
|
|
@@ -106,11 +137,62 @@ export interface TargetDefinition {
|
|
|
106
137
|
desired: number | ValueReference;
|
|
107
138
|
maximum: number;
|
|
108
139
|
};
|
|
140
|
+
/**
|
|
141
|
+
* Capacity reserved for every stable target slot. This is placement intent,
|
|
142
|
+
* not an application cgroup limit. The API first binds an eligible idle
|
|
143
|
+
* compute with at least this unreserved capacity; provisioning is allowed
|
|
144
|
+
* only when the separately approved control-plane policy permits it.
|
|
145
|
+
*/
|
|
146
|
+
allocation: {
|
|
147
|
+
execution: 'native' | 'oci-runc' | 'oci-kata-qemu-snp';
|
|
148
|
+
sharing: 'exclusive' | 'shared';
|
|
149
|
+
resources: {
|
|
150
|
+
cpuCores: IntegerValue;
|
|
151
|
+
memoryMiB: IntegerValue;
|
|
152
|
+
storageGiB: IntegerValue;
|
|
153
|
+
};
|
|
154
|
+
existing: 'prefer' | 'require';
|
|
155
|
+
};
|
|
156
|
+
/** Required only when the control plane may create capacity after reuse is exhausted. */
|
|
157
|
+
provisioning?: {
|
|
158
|
+
planKey: string;
|
|
159
|
+
regionKey: string;
|
|
160
|
+
imageKey: string;
|
|
161
|
+
environmentKey: string;
|
|
162
|
+
ownership: 'platform' | 'tenant-metal';
|
|
163
|
+
metalHostname?: string;
|
|
164
|
+
maxMonthlySpendMinor: string;
|
|
165
|
+
};
|
|
166
|
+
/** Per-slot connectivity intent. Credential names resolve through the scoped Vault, never to plan values. */
|
|
167
|
+
connectivity?: ComputeConnectivityDefinition;
|
|
109
168
|
placement?: {
|
|
110
169
|
spreadBy?: readonly ('metal' | 'region' | 'zone')[];
|
|
111
170
|
antiAffinity?: readonly string[];
|
|
112
171
|
};
|
|
113
172
|
}
|
|
173
|
+
/** Compact TypeScript input normalized into the explicit wire shape above. */
|
|
174
|
+
export interface ComputePoolDefinition {
|
|
175
|
+
profiles: readonly string[];
|
|
176
|
+
replicas: number | {
|
|
177
|
+
minimum: number;
|
|
178
|
+
desired: number | ValueReference;
|
|
179
|
+
maximum: number;
|
|
180
|
+
};
|
|
181
|
+
resources: {
|
|
182
|
+
cpuCores: IntegerValue;
|
|
183
|
+
memoryMiB: IntegerValue;
|
|
184
|
+
storageGiB: IntegerValue;
|
|
185
|
+
};
|
|
186
|
+
os?: 'ubuntu-24.04' | 'ubuntu-26.04';
|
|
187
|
+
runtime?: 'native' | 'oci-runc' | 'oci-kata-qemu-snp';
|
|
188
|
+
isolation?: 'standard' | 'sev-snp';
|
|
189
|
+
sharing?: 'exclusive' | 'shared';
|
|
190
|
+
reuse?: 'prefer' | 'require';
|
|
191
|
+
provisioning?: TargetDefinition['provisioning'];
|
|
192
|
+
connectivity?: ComputeConnectivityDefinition;
|
|
193
|
+
labels?: Readonly<Record<string, string>>;
|
|
194
|
+
placement?: TargetDefinition['placement'];
|
|
195
|
+
}
|
|
114
196
|
export interface RequirementDefinition<TConfig extends JsonObject = JsonObject> {
|
|
115
197
|
capability: string;
|
|
116
198
|
provider: string;
|
|
@@ -195,6 +277,8 @@ export type ApplicationRuntime = {
|
|
|
195
277
|
kind: 'container';
|
|
196
278
|
provider: string;
|
|
197
279
|
requirement: string;
|
|
280
|
+
/** runc shares the compute kernel; Kata creates one measured QEMU SNP workload VM. */
|
|
281
|
+
runtimeClass?: 'runc' | 'kata-qemu-snp';
|
|
198
282
|
image: {
|
|
199
283
|
source: {
|
|
200
284
|
kind: 'build';
|
|
@@ -395,6 +479,8 @@ export declare const ref: {
|
|
|
395
479
|
component: (component: string, property: string) => ValueReference;
|
|
396
480
|
target: (target: string, property: string) => ValueReference;
|
|
397
481
|
deployment: (property: string) => ValueReference;
|
|
482
|
+
/** Secret-free control-plane assignment of the compute executing this step. */
|
|
483
|
+
execution: (property: "targetName" | "slot" | "releaseExecutor" | "runtime" | "cpuCores" | "memoryMiB" | "storageGiB") => ValueReference;
|
|
398
484
|
};
|
|
399
485
|
export declare const conditions: {
|
|
400
486
|
all: (...values: Condition[]) => Condition;
|
|
@@ -425,7 +511,7 @@ export declare const credential: {
|
|
|
425
511
|
}) => CredentialDefinition;
|
|
426
512
|
};
|
|
427
513
|
export declare const target: {
|
|
428
|
-
compute: (value: Omit<TargetDefinition, "kind">) => TargetDefinition;
|
|
514
|
+
compute: (value: ComputePoolDefinition | Omit<TargetDefinition, "kind">) => TargetDefinition;
|
|
429
515
|
};
|
|
430
516
|
export declare function requirement<TConfig extends JsonObject>(value: RequirementDefinition<TConfig>): RequirementDefinition<TConfig>;
|
|
431
517
|
export declare function application(value: Omit<ApplicationComponent, 'kind'>): ApplicationComponent;
|
|
@@ -436,4 +522,4 @@ export declare function workflow(value: WorkflowDefinition): WorkflowDefinition;
|
|
|
436
522
|
export declare function defineDeployment<const T extends DeploymentSource>(definition: T): T;
|
|
437
523
|
export { actions } from './deploy-actions';
|
|
438
524
|
export { providers } from './deploy-providers';
|
|
439
|
-
export type { ArangoDbProviderConfig, BunProviderConfig, CloudflaredProviderConfig, DockerProviderConfig, NginxProviderConfig, OpenSshProviderConfig, SystemdProviderConfig, UfwProviderConfig, WarpProviderConfig } from './deploy-providers';
|
|
525
|
+
export type { ArangoDbProviderConfig, BunProviderConfig, CloudflaredProviderConfig, ContainerdProviderConfig, DockerProviderConfig, KataProviderConfig, NginxProviderConfig, OpenSshProviderConfig, SystemdProviderConfig, UfwProviderConfig, WarpProviderConfig } from './deploy-providers';
|
package/dist/deploy.js
CHANGED
|
@@ -6,6 +6,9 @@ var actions = {
|
|
|
6
6
|
software: {
|
|
7
7
|
ensure: (withValue, options) => action("forgezero.software/ensure@1", withValue, options)
|
|
8
8
|
},
|
|
9
|
+
network: {
|
|
10
|
+
ensure: (withValue, options) => action("forgezero.network/ensure@1", withValue, options)
|
|
11
|
+
},
|
|
9
12
|
exec: {
|
|
10
13
|
argv: (withValue, options) => action("forgezero.exec/argv@1", withValue, options)
|
|
11
14
|
},
|
|
@@ -49,6 +52,23 @@ var providers = {
|
|
|
49
52
|
rootless: false
|
|
50
53
|
})
|
|
51
54
|
},
|
|
55
|
+
containerd: {
|
|
56
|
+
require: (options = { version: "ubuntu-26.04" }) => define("runtime.container", "forgezero.containerd", options, {
|
|
57
|
+
installScope: "host",
|
|
58
|
+
runtimeClass: "runc",
|
|
59
|
+
namespace: "forgezero",
|
|
60
|
+
snapshotter: "overlayfs"
|
|
61
|
+
})
|
|
62
|
+
},
|
|
63
|
+
kata: {
|
|
64
|
+
require: (options = { version: "4.0.0" }) => define("runtime.container", "forgezero.kata", options, {
|
|
65
|
+
installScope: "host",
|
|
66
|
+
runtimeClass: "kata-qemu-snp",
|
|
67
|
+
namespace: "forgezero",
|
|
68
|
+
hypervisor: "qemu",
|
|
69
|
+
confidentialGuest: "sev-snp"
|
|
70
|
+
})
|
|
71
|
+
},
|
|
52
72
|
nginx: {
|
|
53
73
|
require: (options = { version: "ubuntu-26.04" }) => define("proxy.http", "forgezero.nginx", options, {
|
|
54
74
|
installScope: "host",
|
|
@@ -98,7 +118,8 @@ var ref = {
|
|
|
98
118
|
stepOutput: (step2, output) => ({ $ref: `steps.${step2}.outputs.${output}` }),
|
|
99
119
|
component: (component, property) => ({ $ref: `components.${component}.${property}` }),
|
|
100
120
|
target: (target, property) => ({ $ref: `targets.${target}.${property}` }),
|
|
101
|
-
deployment: (property) => ({ $ref: `deployment.${property}` })
|
|
121
|
+
deployment: (property) => ({ $ref: `deployment.${property}` }),
|
|
122
|
+
execution: (property) => ({ $ref: `execution.${property}` })
|
|
102
123
|
};
|
|
103
124
|
var conditions = {
|
|
104
125
|
all: (...values) => ({ all: values }),
|
|
@@ -128,7 +149,33 @@ var credential = {
|
|
|
128
149
|
})
|
|
129
150
|
};
|
|
130
151
|
var target = {
|
|
131
|
-
compute: (value) =>
|
|
152
|
+
compute: (value) => {
|
|
153
|
+
if ("selector" in value)
|
|
154
|
+
return { kind: "compute", ...value };
|
|
155
|
+
const replicas = typeof value.replicas === "number" ? { minimum: value.replicas, desired: value.replicas, maximum: value.replicas } : value.replicas;
|
|
156
|
+
const execution = value.runtime ?? "native";
|
|
157
|
+
const isolation = value.isolation ?? (execution === "oci-kata-qemu-snp" ? "sev-snp" : "standard");
|
|
158
|
+
const os = value.os ?? (isolation === "sev-snp" ? "ubuntu-26.04" : "ubuntu-24.04");
|
|
159
|
+
return {
|
|
160
|
+
kind: "compute",
|
|
161
|
+
selector: {
|
|
162
|
+
profiles: value.profiles,
|
|
163
|
+
operatingSystem: { id: "ubuntu", version: os === "ubuntu-26.04" ? "26.04" : "24.04", architecture: "x64" },
|
|
164
|
+
confidentialCompute: isolation === "sev-snp" ? "required" : "disabled",
|
|
165
|
+
...value.labels ? { labels: value.labels } : {}
|
|
166
|
+
},
|
|
167
|
+
cardinality: replicas,
|
|
168
|
+
allocation: {
|
|
169
|
+
execution,
|
|
170
|
+
sharing: value.sharing ?? "exclusive",
|
|
171
|
+
resources: value.resources,
|
|
172
|
+
existing: value.reuse ?? "prefer"
|
|
173
|
+
},
|
|
174
|
+
...value.provisioning ? { provisioning: value.provisioning } : {},
|
|
175
|
+
...value.connectivity ? { connectivity: value.connectivity } : {},
|
|
176
|
+
...value.placement ? { placement: value.placement } : {}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
132
179
|
};
|
|
133
180
|
function requirement(value) {
|
|
134
181
|
return value;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface DeploymentConnectivityIntent {
|
|
2
|
+
private?: {
|
|
3
|
+
mode: 'disabled' | 'private-lan';
|
|
4
|
+
} | {
|
|
5
|
+
mode: 'cloudflare-warp';
|
|
6
|
+
credential: string;
|
|
7
|
+
network: string;
|
|
8
|
+
};
|
|
9
|
+
public?: {
|
|
10
|
+
mode: 'disabled';
|
|
11
|
+
} | {
|
|
12
|
+
mode: 'cloudflare-tunnel';
|
|
13
|
+
credential: string;
|
|
14
|
+
hostname: string;
|
|
15
|
+
tunnelKey: string;
|
|
16
|
+
service: {
|
|
17
|
+
protocol: 'http';
|
|
18
|
+
port: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Connector-scoped capabilities delivered only inside the hybrid-sealed node claim. */
|
|
23
|
+
export interface DeploymentConnectivityCapabilities {
|
|
24
|
+
public?: {
|
|
25
|
+
tunnelId: string;
|
|
26
|
+
connectorToken: string;
|
|
27
|
+
};
|
|
28
|
+
private?: {
|
|
29
|
+
connectorId: string;
|
|
30
|
+
connectorToken: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface DeploymentConnectivityRequest {
|
|
34
|
+
key: string;
|
|
35
|
+
intent: DeploymentConnectivityIntent;
|
|
36
|
+
capabilities: DeploymentConnectivityCapabilities;
|
|
37
|
+
}
|
|
38
|
+
export interface DeploymentConnectivityEvidence {
|
|
39
|
+
key: string;
|
|
40
|
+
public?: {
|
|
41
|
+
hostname: string;
|
|
42
|
+
tunnelId: string;
|
|
43
|
+
unit: string;
|
|
44
|
+
active: true;
|
|
45
|
+
};
|
|
46
|
+
private?: {
|
|
47
|
+
network: string;
|
|
48
|
+
connectorId: string;
|
|
49
|
+
unit: string;
|
|
50
|
+
active: true;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export interface DeploymentConnectivityHost {
|
|
54
|
+
seal(name: string, path: string, value: string): Promise<void>;
|
|
55
|
+
write(path: string, content: string, mode: number): void;
|
|
56
|
+
exec(argv: readonly string[]): Promise<{
|
|
57
|
+
exitCode: number;
|
|
58
|
+
output: string;
|
|
59
|
+
}>;
|
|
60
|
+
sleep(ms: number): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
/** Apply only connector-scoped capabilities; the broad Cloudflare token never reaches this helper. */
|
|
63
|
+
export declare function applyDeploymentConnectivity(request: DeploymentConnectivityRequest, host?: DeploymentConnectivityHost): Promise<DeploymentConnectivityEvidence>;
|