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
15 changes: 14 additions & 1 deletion dag/extdeps/bmc/http.dag
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module extdeps.bmc.http

import std.types { NonEmptyStr, Secret }
import std.types { NonEmptyStr, Secret, String }
import std.disposition { Disposition, Scaffold, RealizationDispatch }
import std.decl_ref { DeclarationRef, WholeDeclaration }

import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }
Expand All @@ -12,6 +14,17 @@ data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
}
}

data redfish_http_hardwired_transport_dissolution_trigger: Disposition = Scaffold {
dissolves_to: RealizationDispatch,
bind: DeclarationRef {
module_path: "extdeps.bmc.http",
decl_name: "redfish.Http",
field: WholeDeclaration
}
}

data redfish_http_hardwired_transport_note: String = "§3(b) declared debt: every operation below re-spells the shell/curl transport handler per row (the hardwired-single-transport trap; the DMTF-owned interface facts are only the method x path x auth-mode axes). Dissolves when the operations become declarative transport rows realized by the shared HTTP-client machinery - i.e. when `transport rest` (or a derived-curl realization of the extdeps.http.client shape) supports the three axes these argv rows carry by hand: netrc/basic auth, insecure-TLS (self-signed BMC certs), and request-body-from-file. Until then the argv rows stay - live BMC consumers (bmc_onboard*, bmc_read_telemetry, bmc_first_contact, srv3_boot_once_cd) execute them as-is."

service redfish.Http {

operation GetServiceRoot {
Expand Down
59 changes: 59 additions & 0 deletions dag/extdeps/http/client.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module extdeps.http.client

import std.types { NonEmptyStr, String, Bool }
import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }

data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
uri: Uri {
scheme: Https
locator: "www.rfc-editor.org/rfc/rfc9110"
}
}

data http_client_handler_layering_note: String = "§3 shape/transport split: this module is the agnostic HTTP *client* interface (the client half of extdeps.http; extdeps.http.server is the serving half). The shell/curl argv rows below are ONE bound transport handler of the shape (the tool authority is extdeps.tools.curl); the declarative `transport rest` machinery (config modeled in extdeps.transports.rest) is the peer handler - N handlers, one shape, dispatch peripheral. REST itself is a behavioral refinement of HTTP (method semantics honored: GET safe/readonly, PUT idempotent; stateless requests over URI-named resources), not a different client - `transport rest` today carries nothing REST-specific and is honestly the native HTTP-client transport."

service http.Client {
operation Get {
input { url: NonEmptyStr }
output { body: String from "stdout", success: Bool from "exit_success" }
readonly
transport shell {
argv: ["curl", "-fsS", "{url}"]
}
exit {
0 => Unit
nonzero => String "http client GET failed"
}
}

operation PostJsonFromFile {
input { url: NonEmptyStr, request_body_file: NonEmptyStr }
output { body: String from "stdout", success: Bool from "exit_success" }
readonly
transport shell {
argv: [
"curl",
"-sk",
"-X", "POST",
"{url}",
"-H", "Content-Type: application/json",
"--data", "@{request_body_file}",
]
}
exit {
0 => Unit
nonzero => String "curl POST JSON from file failed"
}
}

operation DownloadToFile {
input { url: NonEmptyStr, destination: NonEmptyStr }
output { success: Bool from "exit_success" }
transport shell { argv: ["curl", "-fL", "{url}", "-o", "{destination}"] }
exit {
0 => Unit
nonzero => String "curl download failed"
}
}
}
6 changes: 3 additions & 3 deletions dag/extdeps/os/ubuntu_install_media_fetch.dag
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import std.types { NonEmptyStr, List, String, Bool, FilePath }
import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }
import extdeps.shell
import extdeps.tools.curl
import extdeps.http.client
import extdeps.tools.sha256sum { sha256sum_verify_via_shell, Sha256Matches, Sha256Mismatch, Sha256VerifyInfraFailure }
import extdeps.os.ubuntu_install_media {
UbuntuInstallMediaArtifactRow,
Expand All @@ -24,7 +24,7 @@ data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
}

data install_media_fetch_bash_emit_dissolution_trigger: Disposition = Terminal {
reason: "in-process typed reconcile (shell.Test.IsFile, sha256sum_verify_via_shell, curl.Http.DownloadToFile, shell.Move.File) replaces ShellProgram/serialize_bash - no shell text is built or serialized"
reason: "in-process typed reconcile (shell.Test.IsFile, sha256sum_verify_via_shell, http.Client.DownloadToFile, shell.Move.File) replaces ShellProgram/serialize_bash - no shell text is built or serialized"
}

data install_media_fetch_hash_convergence_note: String = "§3 convergence note: this file verifies fetched content against Canonical's published cryptographic SHA-256 digest (extdeps_external_authority_anchor above) via the Sha256Verify shape - a genuine external-authority digest check, distinct from std.content_hash's fnv1a64 internal structural fingerprint (dag/std/content_hash.dag, used for our own generated-artifact freshness, e.g. srv3_seeded_install_media's remaster sidecar). Do not unify the two: one is a cryptographic authority citation, the other is an internal identity fingerprint with no external-authority meaning."
Expand Down Expand Up @@ -70,7 +70,7 @@ fn install_media_fetch_try_mirror(
final_path: NonEmptyStr,
expected_hash: NonEmptyStr,
) -> MirrorAttempt {
let downloaded = curl.Http.DownloadToFile(url: url, destination: part_path)
let downloaded = http.Client.DownloadToFile(url: url, destination: part_path)
if downloaded.success == false {
MirrorUnreachable
} else {
Expand Down
33 changes: 0 additions & 33 deletions dag/extdeps/tools/curl.dag
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
module extdeps.tools.curl

import std.types { NonEmptyStr, String, Bool }
import extdeps.version { VersionConstraint }
import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }
Expand All @@ -21,35 +20,3 @@ data curl_cli_tool: CliTool = CliTool {
min_version: Present { value: curl_pinned_version },
installable_via: [SourceApt { package: package_curl }],
}

service curl.Http {
operation PostJsonFromFile {
input { url: NonEmptyStr, request_body_file: NonEmptyStr }
output { body: String from "stdout", success: Bool from "exit_success" }
readonly
transport shell {
argv: [
"curl",
"-sk",
"-X", "POST",
"{url}",
"-H", "Content-Type: application/json",
"--data", "@{request_body_file}",
]
}
exit {
0 => Unit
nonzero => String "curl POST JSON from file failed"
}
}

operation DownloadToFile {
input { url: NonEmptyStr, destination: NonEmptyStr }
output { success: Bool from "exit_success" }
transport shell { argv: ["curl", "-fL", "{url}", "-o", "{destination}"] }
exit {
0 => Unit
nonzero => String "curl download failed"
}
}
}
7 changes: 5 additions & 2 deletions dag/gunbc/ci_layer_roots.dag
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ data witness_exclusion_substrings: List<String> = [
"test/claim/emit/logic_ground_truth_test.dag",
"test/claim/manual/english_emit_add_test.dag",
"srv3_install_media_fetch_real_execution_witness_test.dag",
"srv3_seeded_install_media_real_execution_witness_test.dag"
"srv3_seeded_install_media_real_execution_witness_test.dag",
"http_client_get_real_execution_witness_test.dag"
]

data long_lane_exclusion_note: String = "test/claim/long/ is the LONG-test home (operator 5-second rule, 2026-07-12; authority + enforcement: gunbc_ci_fast_lane_rule_note / gunbc_ci_fast_lane_witness_eval_budget in v2.workflow.ci_floor_plan): a witness whose own eval reaches 5s does not run in per-PR discovery — the executor's cooperative eval deadline REDs it by name (EvalBudgetExceeded), and the fix is moving its file here (dag/test/claim/long/, src/v2/test/claim/long/), where discovery excludes it at dir grain and it runs via the local recipe (claim_batch --source-root dag --source-root src/v2 --entry <file> --functions <csv>) or the scheduled lane's future enrollment. First residents (receipted): s1_closure_receipt_test (whole-closure re-parse through the interpreted v2 frontend, 90+min eval, the run-29183446733 wedge class) and self_host_module_emit_derisk_test (~31s/witness). Unlike the roster exclusions above, membership here is a HOME, not a per-file ruling: the 5s deadline makes an over-budget fast-lane witness unwritable-in-place, so the roster cannot silently grow stale (§5 construction over validation). Dissolve-on: per-witness declared cost envelopes (witness-cost-locality admission law) subsume the dir grain."
Expand Down Expand Up @@ -121,7 +122,9 @@ data bin_witness_wet_entries: List<ScheduleWitnessEntry> = [
bin_wet(entry: "dag/test/claim/srv3_install_media_fetch_real_execution_witness_test.dag", f: "install_media_hash_matches_true_by_real_execution"),
bin_wet(entry: "dag/test/claim/srv3_install_media_fetch_real_execution_witness_test.dag", f: "install_media_hash_matches_false_on_wrong_hash_by_real_execution"),
bin_wet(entry: "dag/test/claim/srv3_install_media_fetch_real_execution_witness_test.dag", f: "observe_install_media_fetch_covers_absent_verified_and_mismatch_by_real_execution"),
bin_wet(entry: "dag/test/claim/srv3_seeded_install_media_real_execution_witness_test.dag", f: "install_media_remaster_ensure_grub_cmdline_inserts_by_real_execution")
bin_wet(entry: "dag/test/claim/srv3_seeded_install_media_real_execution_witness_test.dag", f: "install_media_remaster_ensure_grub_cmdline_inserts_by_real_execution"),
bin_wet(entry: "dag/test/claim/http_client_get_real_execution_witness_test.dag", f: "http_client_get_roundtrip_by_real_execution"),
bin_wet(entry: "dag/test/claim/http_client_get_real_execution_witness_test.dag", f: "http_client_get_missing_target_red_control_by_real_execution")
]

data floor_skip_control_fixture_exclusion_note: String = "test/fixture/floor_skip/ is excluded from tree-wide discovery at DIR grain (parent ruling 2026-07-11: a fixture that WANTS witness enrollment is mis-homed by definition, and file-grain exclusion invites the next control fixture to repeat the landmine). These fixtures exist to be driven by floor_skip_discovery_witness's explicit rosters, not as corpus witnesses: falsifier_divergence_control_test.dag carries a BY-DESIGN red row (falsifier_red_control_holds returns false to prove the divergence attribution channel), so any completed full-corpus run - the nightly falsifier, or a PR whose diff touches its import closure - would red on a row whose red is the control's specification, and a permanent by-design divergence would also make the falsifier's 8-clean-window retirement criterion unsatisfiable; live_tree_declared_test.dag declares ReadsLiveTree as a control, so as a corpus row it would be a permanent never-skip cost with no enforcement value. Genuine corpus witnesses that were mis-homed here have been re-homed under test/claim/ (provenance_fail_closed_contract_test, live_tree_collision_test). Explicit-roster runs (the floor_skip bin) are unaffected: witness exclusions apply to discovery only."
Expand Down
31 changes: 31 additions & 0 deletions dag/test/claim/http_client_get_real_execution_witness_test.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module test.claim.http_client_get_real_execution

import std.logic { Bool }
import std.types { String, NonEmptyStr }
import extdeps.http.client
import extdeps.shell
import extdeps.filesystem.filesystem_io
import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly }

data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly

data real_execution_home_doc: String = "These witnesses drive the real shell/curl transport of http.Client.Get (extdeps/http/client.dag) - the op has no mock_response, so the hermetic discovery corpus refuses it by construction. Excluded from discovery (gunbc.ci_layer_roots witness_exclusion_substrings) and re-enrolled as declared Wet explicit entries (bin_witness_wet_entries), same lane as the srv3 install-media real-execution witnesses. Network-free: the GET targets a file:// URL over a mktemp fixture, so what is proven by execution is the client shape's transport row - argv realization, stdout-to-body projection, exit-to-success mapping - plus a discriminating RED (missing target refuses, success=false, no fabricated body)."

data http_client_get_fixture_body: String = "gunbc-http-client-get-witness-body"

test fn http_client_get_roundtrip_by_real_execution() -> Bool {
let dir = shell.Mktemp.Dir()
let path = concat(dir.path as String, "/fixture")
let wrote = Filesystem.Write(path: path, content: http_client_get_fixture_body)
let got = http.Client.Get(url: concat("file://", path) as NonEmptyStr)
let cleaned = shell.Remove.RecursiveForce(path: dir.path)
dir.success && wrote.success && got.success && got.body == http_client_get_fixture_body
}

test fn http_client_get_missing_target_red_control_by_real_execution() -> Bool {
let dir = shell.Mktemp.Dir()
let missing = concat(dir.path as String, "/does-not-exist")
let got = http.Client.Get(url: concat("file://", missing) as NonEmptyStr)
let cleaned = shell.Remove.RecursiveForce(path: dir.path)
dir.success && got.success == false && got.body == ""
}
Loading