Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions dag/extdeps/http/client.dag
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,23 @@ fn http_client_get_argv(url: NonEmptyStr, max_time: Second) -> List<String> {
]
}

// The same bounded GET over a unix socket (curl --unix-socket, curl(1)): the URL names the path the
// server routes on and the connection is made to the socket file. GetWithinUnixSocket is its
// local-transport twin.
fn http_client_get_unix_socket_argv(socket: NonEmptyStr, url: NonEmptyStr, max_time: Second) -> List<String> {
[
"curl",
"-fsS",
"--unix-socket",
socket as String,
"--connect-timeout",
http_client_localhost_connect_timeout_flag(),
"--max-time",
http_client_max_time_flag(max_time: max_time) as String,
url as String,
]
}

// THE DEADLINE IS AN INPUT, NOT A LITERAL IN THIS ARGV, and the distinction is the one DESIGN
// section 3 draws between an interface fact and a policy fact. How long a caller is willing to wait
// for ITS backend is the caller's business; curl's ability to bound a request is ours. A number
Expand Down Expand Up @@ -178,6 +195,23 @@ service http.Client {
}
}

operation GetWithinUnixSocket {
requires Network
input { socket: NonEmptyStr, url: NonEmptyStr, max_seconds: NonEmptyStr }
output { body: String from "stdout", success: Bool from "exit_success" }
readonly
transport shell {
argv: ["curl", "-fsS", "--unix-socket", "{socket}", "--max-time", "{max_seconds}", "{url}"]
}
exit {
0 => Unit
nonzero => String "http client GET over a unix socket within a caller deadline failed"
}
mock_response {
0 => { body: "", success: false } "hermetic: no live socket; a bounded observation refuses rather than fabricate a body"
}
}

operation StatusWithin {
requires Network
input { url: NonEmptyStr, max_seconds: NonEmptyStr }
Expand Down
33 changes: 32 additions & 1 deletion dag/extdeps/tailscale/serve.dag
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,37 @@ fn tailscale_serve_unix_target(path: NonEmptyStr) -> NonEmptyStr {
concat(tailscale_serve_unix_target_prefix, path as String) as NonEmptyStr
}

// The socket path a unix target names, or nothing for a port backend: the inverse of
// tailscale_serve_unix_target.
fn tailscale_serve_unix_target_path(endpoint: TailscaleServeEndpoint) -> NonEmptyStr? {
let backend = endpoint.backend as String
if starts_with(s: backend, prefix: tailscale_serve_unix_target_prefix) && length(backend) > length(tailscale_serve_unix_target_prefix) {
Present { value: substring(s: backend, start: length(tailscale_serve_unix_target_prefix), end: length(backend)) as NonEmptyStr }
} else {
none
}
}

// The backend word that produces a status proxy target: the inverse of tailscale_serve_proxy_url.
// A unix target is its own word and a loopback port URL is its port; any other target (another
// host, another scheme) is not a backend this module's endpoint form can restate, and answers
// nothing rather than an approximation.
data tailscale_serve_port_proxy_prefix: String = "http://127.0.0.1:"

fn tailscale_serve_backend_of_proxy_target(target: String) -> NonEmptyStr? {
if starts_with(s: target, prefix: tailscale_serve_unix_target_prefix) && length(target) > length(tailscale_serve_unix_target_prefix) {
Present { value: target as NonEmptyStr }
} else if starts_with(s: target, prefix: tailscale_serve_port_proxy_prefix) {
let port = substring(s: target, start: length(tailscale_serve_port_proxy_prefix), end: length(target))
match parse_int(s: port) {
Present { value: n } => if n > 0 && to_string(n) == port { Present { value: port as NonEmptyStr } } else { none }
Absent => none
}
} else {
none
}
}

// THE PEER A UNIX TARGET SEES. tailscaled runs as root and it is tailscaled that dials the target,
// so a connection Serve proxies to a unix socket arrives with root as its kernel-attested peer;
// since v1.98.9 only root may configure a unix target at all (TS-2026-005).
Expand All @@ -125,7 +156,7 @@ fn tailscale_serve_backend_is_unix_target(endpoint: TailscaleServeEndpoint) -> B

fn tailscale_serve_proxy_url(endpoint: TailscaleServeEndpoint) -> String {
if starts_with(s: endpoint.backend as String, prefix: tailscale_serve_unix_target_prefix) { endpoint.backend as String }
else { concat("http://127.0.0.1:", endpoint.backend as String) }
else { concat(tailscale_serve_port_proxy_prefix, endpoint.backend as String) }
}

fn tailscale_serve_mount_status_key(mount: TailscaleServeMount) -> String {
Expand Down
5 changes: 5 additions & 0 deletions dag/gunbc/ci/ci_layer_roots.dag
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,11 @@ data witness_exclusion_frontier: List<WitnessExclusionRow> = [
classification: LocalRepoWetLane,
reason: excl_local_repo_wet_tempdir_write_reason,
dissolution: excl_local_repo_wet_dissolve},
WitnessExclusionRow {
pattern: "tailnet_door_cutover_journal_wet_witness_test.dag",
classification: LocalRepoWetLane,
reason: excl_local_repo_wet_tempdir_write_reason,
dissolution: excl_local_repo_wet_dissolve},
WitnessExclusionRow {
pattern: "workspace_allocation_write_seam_wet_witness_test.dag",
classification: LocalRepoWetLane,
Expand Down
2 changes: 2 additions & 0 deletions dag/gunbc/host/host_effect.dag
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import extdeps.llm.codex_app_server_stdio_session { CodexAppServerStdioSessionIn
import gunbc.fleet_intent { ComputeHost }
import gunbc.host_converge { HostConverge }
import gunbc.live_deploy.spec { DeploymentSpec, DeploymentDependencyStep }
import extdeps.tailscale.serve { TailscaleServeEndpoint }
import gunbc.build_cache_instance { RunnerSlotIdentity, BuildCacheInstance }
import extdeps.github.actions_runner { ActionsRunnerRelease, ActionsRunnerBinaryArtifact }
import gunbc.runner_incarnation { LocalRunnerSlotIncarnation }
Expand Down Expand Up @@ -81,6 +82,7 @@ type HostEffect =
| LiveDeployMicrovmControllerInstall { candidate: CandidateRelease }
| LiveDeployEnsureDependency { dep: DeploymentDependencyStep }
| LiveDeployDigestReadback { port: Int }
| LiveDeployServeRoute { endpoint: TailscaleServeEndpoint, enabled: Bool }
| DeployAccessPreflight {}
| EnsureActionsRunnerRelease { runner_release: ActionsRunnerRelease }
| EnsureRunnerSlotDirectory { slot: RunnerSlotIdentity, artifact: ActionsRunnerBinaryArtifact }
Expand Down
37 changes: 37 additions & 0 deletions dag/gunbc/host/host_effect_realize.dag
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import gunbc.host_effect {
LiveDeployMicrovmControllerInstall,
LiveDeployEnsureDependency,
LiveDeployDigestReadback,
LiveDeployServeRoute,
DeployAccessPreflight,
EnsureActionsRunnerRelease,
EnsureRunnerSlotDirectory,
Expand Down Expand Up @@ -161,6 +162,7 @@ import gunbc.ci_deploy_access {
deploy_access_sudo_probe_effect,
}
import gunbc.live_deploy.spec { DeploymentSpec, DeploymentDependencyStep }
import extdeps.tailscale.serve { TailscaleServeEndpoint }
import gunbc.live_deploy.member_identity { ReleaseEffectPlan }
import gunbc.host_axis_caps { CapConvergeWithheld, cap_converge_decision }
import gunbc.runner_host_deploy {
Expand Down Expand Up @@ -191,6 +193,7 @@ import gunbc.live_deploy.emit {
live_deploy_apply_script_for,
live_deploy_retract_script_for,
deployment_ensure_dependency_script,
deployment_serve_route_script,
approval_broker_dark_install_script,
microvm_controller_install_script,
}
Expand Down Expand Up @@ -455,6 +458,7 @@ type ResolvedHostEffectCell =
| LiveDeployMicrovmControllerInstallOnHost { node: ComputeHost, candidate: CandidateRelease }
| LiveDeployEnsureDependencyOnHost { node: ComputeHost, dep: DeploymentDependencyStep }
| LiveDeployDigestReadbackOnHost { node: ComputeHost, port: Int }
| LiveDeployServeRouteOnHost { node: ComputeHost, endpoint: TailscaleServeEndpoint, enabled: Bool }
| EnsureActionsRunnerReleaseOnHost { node: ComputeHost, runner_release: ActionsRunnerRelease }
| EnsureRunnerSlotDirectoryOnHost {
node: ComputeHost,
Expand Down Expand Up @@ -545,6 +549,8 @@ fn resolve_host_os_effect_cell_after_direct_arms(n: ComputeHost, effect: HostEff
LiveDeployEnsureDependencyOnHost { node: n, dep: d }
LiveDeployDigestReadback { port: p } =>
LiveDeployDigestReadbackOnHost { node: n, port: p }
LiveDeployServeRoute { endpoint: e, enabled: route_enabled } =>
LiveDeployServeRouteOnHost { node: n, endpoint: e, enabled: route_enabled }
EnsureActionsRunnerRelease { runner_release: r } =>
EnsureActionsRunnerReleaseOnHost { node: n, runner_release: r }
EnsureRunnerSlotDirectory { slot: s, artifact: a } =>
Expand Down Expand Up @@ -625,6 +631,8 @@ fn resolve_bmc_controller_effect_cell_after_direct_arms(n: ComputeHost, effect:
IncompatibleCell { reason: "host_effect: LiveDeployEnsureDependency is in-band (actuator host shell mutation) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." }
LiveDeployDigestReadback { port: _ } =>
IncompatibleCell { reason: "host_effect: LiveDeployDigestReadback is in-band (actuator host shell readback) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." }
LiveDeployServeRoute { endpoint: _, enabled: _ } =>
IncompatibleCell { reason: "host_effect: LiveDeployServeRoute is in-band (actuator host shell mutation) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." }
EnsureActionsRunnerRelease { runner_release: _ } =>
IncompatibleCell { reason: "host_effect: EnsureActionsRunnerRelease is in-band (runner lifecycle on HostOs) and cannot target BmcController; target HostOs instead. Typed mismatch via total fold, DESIGN section 5." }
EnsureRunnerSlotDirectory { slot: _, artifact: _ } =>
Expand Down Expand Up @@ -681,6 +689,7 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res
LiveDeployMicrovmControllerInstall { candidate: _ } => IncompatibleCell { reason: "host_effect: RunnerLocalHost admits only ProbeToolchainHomeInterference; fleet-addressed effects require HostOs or BmcController." }
LiveDeployEnsureDependency { dep: _ } => IncompatibleCell { reason: "host_effect: RunnerLocalHost admits only ProbeToolchainHomeInterference; fleet-addressed effects require HostOs or BmcController." }
LiveDeployDigestReadback { port: _ } => IncompatibleCell { reason: "host_effect: RunnerLocalHost admits only ProbeToolchainHomeInterference; fleet-addressed effects require HostOs or BmcController." }
LiveDeployServeRoute { endpoint: _, enabled: _ } => IncompatibleCell { reason: "host_effect: RunnerLocalHost admits only ProbeToolchainHomeInterference; fleet-addressed effects require HostOs or BmcController." }
DeployAccessPreflight {} => IncompatibleCell { reason: "host_effect: RunnerLocalHost admits only ProbeToolchainHomeInterference; fleet-addressed effects require HostOs or BmcController." }
EnsureActionsRunnerRelease { runner_release: _ } => IncompatibleCell { reason: "host_effect: RunnerLocalHost admits only ProbeToolchainHomeInterference; fleet-addressed effects require HostOs or BmcController." }
EnsureRunnerSlotDirectory { slot: _, artifact: _ } => IncompatibleCell { reason: "host_effect: RunnerLocalHost admits only ProbeToolchainHomeInterference; fleet-addressed effects require HostOs or BmcController." }
Expand Down Expand Up @@ -724,6 +733,8 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res
LiveDeployEnsureDependencyOnHost { node: n, dep: d }
LiveDeployDigestReadback { port: p } =>
LiveDeployDigestReadbackOnHost { node: n, port: p }
LiveDeployServeRoute { endpoint: e, enabled: route_enabled } =>
LiveDeployServeRouteOnHost { node: n, endpoint: e, enabled: route_enabled }
ProbeToolchainHomeInterference {} =>
IncompatibleCell { reason: "host_effect: ProbeToolchainHomeInterference is runner-local and cannot name a fleet HostOs; target RunnerLocalHost instead." }
RedfishAction { action: _ } => resolve_host_os_effect_cell_after_direct_arms(n: n, effect: effect)
Expand Down Expand Up @@ -782,6 +793,7 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res
LiveDeployMicrovmControllerInstall { candidate: _ } => resolve_bmc_controller_effect_cell_after_direct_arms(n: n, effect: effect)
LiveDeployEnsureDependency { dep: _ } => resolve_bmc_controller_effect_cell_after_direct_arms(n: n, effect: effect)
LiveDeployDigestReadback { port: _ } => resolve_bmc_controller_effect_cell_after_direct_arms(n: n, effect: effect)
LiveDeployServeRoute { endpoint: _, enabled: _ } => resolve_bmc_controller_effect_cell_after_direct_arms(n: n, effect: effect)
DeployAccessPreflight {} => resolve_bmc_controller_effect_cell_after_direct_arms(n: n, effect: effect)
EnsureActionsRunnerRelease { runner_release: _ } => resolve_bmc_controller_effect_cell_after_direct_arms(n: n, effect: effect)
EnsureRunnerSlotDirectory { slot: _, artifact: _ } => resolve_bmc_controller_effect_cell_after_direct_arms(n: n, effect: effect)
Expand Down Expand Up @@ -1625,6 +1637,7 @@ fn realize_provision_codex_runtime(
LiveDeployMicrovmControllerInstall { candidate: _ } => realize_provision_codex_runtime_wrong_effect(effect_intent: effect_intent)
LiveDeployEnsureDependency { dep: _ } => realize_provision_codex_runtime_wrong_effect(effect_intent: effect_intent)
LiveDeployDigestReadback { port: _ } => realize_provision_codex_runtime_wrong_effect(effect_intent: effect_intent)
LiveDeployServeRoute { endpoint: _, enabled: _ } => realize_provision_codex_runtime_wrong_effect(effect_intent: effect_intent)
DeployAccessPreflight {} => realize_provision_codex_runtime_wrong_effect(effect_intent: effect_intent)
EnsureActionsRunnerRelease { runner_release: _ } => realize_provision_codex_runtime_wrong_effect(effect_intent: effect_intent)
EnsureRunnerSlotDirectory { slot: _, artifact: _ } => realize_provision_codex_runtime_wrong_effect(effect_intent: effect_intent)
Expand Down Expand Up @@ -3447,6 +3460,7 @@ fn realize_provision_build_cache(
LiveDeployMicrovmControllerInstall { candidate: _ } => realize_provision_build_cache_wrong_effect(effect_intent: effect_intent)
LiveDeployEnsureDependency { dep: _ } => realize_provision_build_cache_wrong_effect(effect_intent: effect_intent)
LiveDeployDigestReadback { port: _ } => realize_provision_build_cache_wrong_effect(effect_intent: effect_intent)
LiveDeployServeRoute { endpoint: _, enabled: _ } => realize_provision_build_cache_wrong_effect(effect_intent: effect_intent)
DeployAccessPreflight {} => realize_provision_build_cache_wrong_effect(effect_intent: effect_intent)
EnsureActionsRunnerRelease { runner_release: _ } => realize_provision_build_cache_wrong_effect(effect_intent: effect_intent)
EnsureRunnerSlotDirectory { slot: _, artifact: _ } => realize_provision_build_cache_wrong_effect(effect_intent: effect_intent)
Expand Down Expand Up @@ -4581,6 +4595,24 @@ fn realize_live_deploy_ensure_dependency_on_host(
)
}

// One serve route written or turned off, as its own gated effect: the tailnet door cutover applies
// the re-point and its restore one at a time, between observations, never inside the bulk apply.
fn realize_live_deploy_serve_route_on_host(
endpoint: TailscaleServeEndpoint,
enabled: Bool,
transport: HostEffectTransport,
intent: HostEffectIntent,
) -> Reconciliation<HostEffectIntent, HostEffectEvidence> {
realize_shell_on_host(
script: retained_srvn(
body: deployment_serve_route_script(endpoint: endpoint, enabled: enabled),
reason: "live_deploy serve route",
),
transport: transport,
intent: intent,
)
}

fn realize_live_deploy_digest_readback_on_host(
port: Int,
transport: HostEffectTransport,
Expand Down Expand Up @@ -4652,6 +4684,8 @@ fn host_effect_apply(target: NodeControlPlane, effect: HostEffect, evidence: Ide
realize_live_deploy_ensure_dependency_on_host(dep: d, transport: transport, intent: intent)
LiveDeployDigestReadbackOnHost { node: _, port: p } =>
realize_live_deploy_digest_readback_on_host(port: p, transport: transport, intent: intent)
LiveDeployServeRouteOnHost { node: _, endpoint: e, enabled: route_enabled } =>
realize_live_deploy_serve_route_on_host(endpoint: e, enabled: route_enabled, transport: transport, intent: intent)
EnsureActionsRunnerReleaseOnHost { node: _, runner_release: _ } =>
runner_lifecycle_realize_ensure_actions_runner_release(intent: intent)
EnsureRunnerSlotDirectoryOnHost { node: _, slot: _, artifact: _ } =>
Expand Down Expand Up @@ -4855,6 +4889,8 @@ fn host_effect_apply_gated(
realize_live_deploy_ensure_dependency_on_host(dep: d, transport: transport, intent: intent)
LiveDeployDigestReadbackOnHost { node: _, port: p } =>
realize_live_deploy_digest_readback_on_host(port: p, transport: transport, intent: intent)
LiveDeployServeRouteOnHost { node: _, endpoint: e, enabled: route_enabled } =>
realize_live_deploy_serve_route_on_host(endpoint: e, enabled: route_enabled, transport: transport, intent: intent)
EnsureActionsRunnerReleaseOnHost { node: _, runner_release: _ } =>
runner_lifecycle_realize_ensure_actions_runner_release(intent: intent)
EnsureRunnerSlotDirectoryOnHost { node: _, slot: _, artifact: _ } =>
Expand Down Expand Up @@ -4936,6 +4972,7 @@ fn host_effect_drive_directive(r: Reconciliation<HostEffectIntent, HostEffectEvi
LiveDeployMicrovmControllerInstallOnHost { node: _, candidate: _ } => not_converged_directive(drive: host_effect_drive(intent: intent))
LiveDeployEnsureDependencyOnHost { node: _, dep: _ } => not_converged_directive(drive: host_effect_drive(intent: intent))
LiveDeployDigestReadbackOnHost { node: _, port: _ } => not_converged_directive(drive: host_effect_drive(intent: intent))
LiveDeployServeRouteOnHost { node: _, endpoint: _, enabled: _ } => not_converged_directive(drive: host_effect_drive(intent: intent))
EnsureActionsRunnerReleaseOnHost { node: _, runner_release: _ } => not_converged_directive(drive: host_effect_drive(intent: intent))
EnsureRunnerSlotDirectoryOnHost { node: _, slot: _, artifact: _ } => not_converged_directive(drive: host_effect_drive(intent: intent))
EnsureRunnerJitWrapperOnHost { node: _, host_label: _ } => not_converged_directive(drive: host_effect_drive(intent: intent))
Expand Down
Loading