Skip to content

Managed-host cut 5 (re-derived): fan and KVM observations over ManagedHost - #13689

Merged
gunbai-bot[bot] merged 1 commit into
mainfrom
session/still-swift-683
Oct 10, 2026
Merged

gunbai-bot[bot] merged 1 commit into
mainfrom
session/still-swift-683

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Re-derives closed gunbc#13420 (session/neat-pike-256; history, not a base) on current main: fan observation is host-generic over ManagedHost, KVM observer is MegaRAC-scoped over AdmittedMegaRacHost.
  • Inhabits gunbc.fleet_fan_converge_routes fan_observation_authority_for_family (same peripheral family dispatch as the existing converge routes). A host whose BMC family was never observed refuses FanHostFamilyUnobserved and is never defaulted to either policy. ManagedHostFoundUnderDeclaredDrop is matched, not dropped (current ManagedHostLookup).
  • Fleet-converge modes mtcollins1_fan_observe and mtcollins1_kvm_observer_observe stay as thin unit bindings (workflow surface is cut 7). Credential source is unchanged (GUNBC_MTCOLLINS1_BMC_CREDENTIAL_FILE).
  • Not carried: served-UI / MegaRacServedUiRead / megarac_ui_bundle_observe. Public gunbc deleted AMI-bundle-derived content in f956099fcbe (moved to gunbc-private). Restoring it would re-introduce that content.

Conformance

  • observability: receipts stay in the observing product/workflow layer (gunbc.machine_intake_fan_observe, gunbc.machine_intake_megarac_kvm_observer_observe); no Unobserved authored in an upstream module.
  • external facts: MegaRAC vs phosphor policy authorities stay in their extdeps modules; this change only dispatches among them from an observed family.
  • leasing/grants: unit-hold acquire/release admit_callers now name the generic probe (megarac_kvm_observer_observe / megarac_kvm_observer_release).
  • consumption: new declarations are consumed by the kept wet entries and the enrolled claims.

Test plan

  • claim_batch dag/test/claim/machine_intake/fan_observe_witness_test.dag — 4/4 PASS, exit 0 (seed claim_batch built from this branch, CTRL_BUILD_MODE=local, CARGO_TARGET_DIR=/tmp/cargo-target-still-swift-683)
  • claim_batch dag/test/claim/machine_intake/megarac_kvm_observer_observe_witness_test.dag — 14/14 PASS, exit 0
  • claim_batch dag/test/claim/machine_intake/bmc_stack_observation_forged_probe_witness_test.dag — 4/4 PASS, exit 0 (sole-constructor + outside-caller seals; no served-UI probe)
  • required witnesses job on this PR
  • Operator-only (not this lane): wet mtcollins1_fan_observe / mtcollins1_kvm_observer_observe on srv1; no fleet-converge dispatch from this PR

Made with Cursor

Move the BMC-stack observation bodies off the mtcollins1 procedure root onto
ManagedHost (fan, family-selected) and AdmittedMegaRacHost (KVM). Keep the
fleet-converge entries as thin unit bindings. Do not restore served-UI: that
AMI-bundle-derived path left public gunbc in f956099.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gunbai-bot

gunbai-bot Bot commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

Required gate receipt on this head (5bb9355236be7a0b87f8f62f24b0d7f41775452d):

  • witnesses (run 38075780681) — SUCCESS, exit 0

Dashboard review 78449 (cursor, dashboard-only) approved this head; no REQUEST_CHANGES. Operator lands; this lane does not merge.

@gunbai-bot
gunbai-bot Bot merged commit d853aec into main Oct 10, 2026
1 check passed
@gunbai-bot
gunbai-bot Bot deleted the session/still-swift-683 branch October 10, 2026 19:01
gunbai-bot Bot pushed a commit that referenced this pull request Oct 15, 2026
…nericized on explicit subject + executor params

Subject/medium/attempt/receipts become explicit parameters; the mtcollins1_
measured readings stay as mtcollins1_ receipt rows. Executor side keyed by
executor host per #13416. census_boot_medium computes artifact identity only.

DONE: the 5 census modules' procedure roots genericized (census_image,
census_image_publish, census_medium_readback, census_member_readback); the
mtcollins1_ receipt rows retained; discriminating controls wired (a mismatched
medium-vs-image binding refuses at census_medium_qualification before any
effect).
NOT DONE: 14 consumer files (ci_spec, fleet_converge_workflow, mtcollins1_boot_run,
runner_throughput_qualification_route, 10 witness tests) still reference the old
names and must be re-pointed at the genericized roots + new param surfaces; the
type-check (remote gunbc loop) has not been run on the whole tree; terminal
receipt (witness runs + red-same-path + corpus sweep) outstanding.

Refs: managed-host-untangle cut 4b-ii; precedents #13248 (4a), #13416 (4b-i),
#13261 (3), #13689 (5).
gunbai-bot Bot added a commit that referenced this pull request Oct 16, 2026
…s genericized (#14065)

* Managed-host cut 4b-ii (WIP): census image/medium/member readbacks genericized on explicit subject + executor params

Subject/medium/attempt/receipts become explicit parameters; the mtcollins1_
measured readings stay as mtcollins1_ receipt rows. Executor side keyed by
executor host per #13416. census_boot_medium computes artifact identity only.

DONE: the 5 census modules' procedure roots genericized (census_image,
census_image_publish, census_medium_readback, census_member_readback); the
mtcollins1_ receipt rows retained; discriminating controls wired (a mismatched
medium-vs-image binding refuses at census_medium_qualification before any
effect).
NOT DONE: 14 consumer files (ci_spec, fleet_converge_workflow, mtcollins1_boot_run,
runner_throughput_qualification_route, 10 witness tests) still reference the old
names and must be re-pointed at the genericized roots + new param surfaces; the
type-check (remote gunbc loop) has not been run on the whole tree; terminal
receipt (witness runs + red-same-path + corpus sweep) outstanding.

Refs: managed-host-untangle cut 4b-ii; precedents #13248 (4a), #13416 (4b-i),
#13261 (3), #13689 (5).

* census: re-point consumers to the genericized mtcollins1_census_* params

- census_capture_program(machine, console_device, halt_command, stages)
- census_seeded_image_input(image_stem, volume_id, user_data_body, grub_kernel_cmdline, nocloud_dir, program_file, program_content)
- census_medium_readback(medium, image); census_medium_readback_from_answers(+image)
- census_member_readback(medium, image_stem, volume_id); census_pin_gate(+image_stem, volume_id); census_member_readback_from_readings(+image_stem, volume_id)
- mtcollins1_boot_run passes mtcollins1_census_image to the two readback gates

* census member readback wet entry: restore mtcollins1_ receipt name

The wet entry is the mtcollins1 RECEIPT producer (reads mtcollins1_boot_medium,
mtcollins1_census_image_stem, mtcollins1_census_volume_id; writes the
mtcollins1 receipt path) -- not a generic derivation root. Keep the
mtcollins1_ prefix for consistency with mtcollins1_census_image_publish_wet
and with the fleet-converge.yml:1250 + ci_spec.dag:1093 consumers (the .yml
consumer is outside the required v2-bootstrap gate).

* census medium readback: restore module declaration to the file's rule position

gunbc requires module name = file path (the rule position). The genericization
this cut applies to the FUNCTIONS (census_*), not the module/file: the plan
defers module naming until after cuts 2-6 expose the axes, and the scope
adjustment only removes mtcollins1 constants + makes params explicit. Restoring
the declaration so the file path and module name agree again.

* census readback: executor+subject params and two executed discriminating controls

Addresses review 80603 (REQUEST_CHANGES) + manager msg 3fb13889:
- census_readback_refusal + before_filing/before_attach take (subject, host)
  params; the refusal text now names the SUBJECT and the EXECUTOR that took the
  leg, not one baked-in host (DESIGN 3 meaning fork, DESIGN 5 no fabricated
  output).
- census_medium_readback takes (medium, image, host, attempt): the recorded host
  and the SSH attempt identity are parameters, not the srv2 / mtcollins1
  constants; boot_run + the witness thread the mtcollins1 labels.
- Two EXECUTED discriminating-control test fns on a constructed second subject
  (supplied values, not mtcollins1's rows): control_a a non-mtcollins1 subject
  derives its own image+medium plan (own stem/digest, distinct build key);
  control_b a medium whose stem does not match its image refuses at
  census_medium_qualification before any leg.
- Each control was run RED once against a deliberately broken variant (a
  derivation that ignores the supplied stem; a gate with the stem check
  dropped): both DID-NOT-HOLD outcome=Fail, while the controls themselves HELD.
  Scratch reds removed; only the two green controls are committed.

Verified on the remote runner (gunbc @ da13ad3 + this diff, 28GiB cgroup,
24GiB budget): //test/claim/machine_intake/mtcollins1_census_medium_readback_witness:all
= 19 held, 0 did not hold (control_a + control_b both HELD).

* machine_intake: parameterize the pinned-image expected digest in census_regression_on

Close the meaning fork in census_regression_on (review 80729): the generic
subject input (pinned_image) was judged against mtcollins1's hardcoded
expected digest, so a non-mtcollins1 pinned image would be judged against
the wrong digest. The expected digest is now an explicit parameter, and the
mtcollins1 wet entry passes its own mtcollins1_census_hermetic_pinned_rebuild_digest.

The generic function now takes only its subject and the expected digest,
keeping the mtcollins1-specific fact (the pinned rebuild digest) as the
caller's receipt row.

* census image: the seed's early command and seed file cannot disagree

Close the silent medium review 80811 names (DESIGN §5: correctness by
construction, not validation; §4b: silent wrongness is not a rung).

census_image_derivation took live_commands and nocloud_dir/program_file as
independent parameters. The user-data's early command (where the installer
runs the program from) came from live_commands; the seed file (where the
program actually rides) came from nocloud_dir/program_file. Those could
disagree and still yield CensusImageDerived -- a runnable image whose
autoinstall invokes a path that is not the seeded program. On main that
mismatch was unwritable: derivation always used mtcollins1_census_live_commands,
which closed over the same module constants as the seeded input.

The early command is now BUILT inside the derivation from the seed's
location (census_program_on_medium(nocloud_dir, program_file)), so the
command the installer runs and the file the seed carries are one fact, not
two parameters a caller can cross. A path mismatch is unwritable, not a
validation. mtcollins1_census_live_commands is kept as the measured
receipt row (the payload the derivation builds for mt. collins' seed dir
and program file); it no longer feeds the derivation.

* census readback: the refusal names the host that ran the leg, from the receipt

Close review 80825 (DESIGN 5: correctness by construction, not validation;
no plausible output is fabricated).

census_readback_refusal took its own host parameter, untied from the
readback's. Only CensusMediumReadbackAgreed recorded which host it ran on;
the two unreadable variants the renderer prints a host for --
CensusMediumReadbackRecordUnreadable and CensusMediumReadbackImageUnreadable
-- did not, so the renderer printed whatever host the caller handed it. A
caller that passes different hosts to the two functions gets a refusal
blaming a host that never ran the leg; the "no fabricated output" comment
above the renderer was prose the code did not enforce.

The executor host is now stored in the two unreadable readback variants,
populated at construction (census_record_standing / census_measure_join /
the producer's refused SSH-context arm), where the producer's host is the
single source that also names the executor the leg ran on.
census_readback_refusal and its before_filing / before_attach wrappers take
the host from the receipt, not as a second input a caller can cross --
one fact, not two parameters. census_record_standing takes the host so the
record-stage refusal names the same executor the measure stage would.
Witnesses re-pointed: the refusal checks now exercise the host recorded in
the receipt.

* control (a): route the second subject through the real census_image_derivation

Close review 80832 (DESIGN 5: done means a real consumer green by execution
plus a discriminating input that goes red when the behavior is wrong; DESIGN 3
pairing: assert the route, not only the answer).

control_a claimed a non-mtcollins1 subject "derives" its own image, but
second_subject_image hand-built CensusImageDerived via census_seeded_image_input
and never called census_image_derivation. A derivation that ignored image_stem
(or the other subject params) and always used the mtcollins1 constants would
have left the control green -- it tested a re-derivation of the same input, not
the genericized derivation it claimed to prove.

The second subject's image is now the REAL census_image_derivation applied to
its own facts (its stem and volume id name the artifact; the envelope and
seed location are the shared census shape). The control asserts the derived
input carries the second stem (not mtcollins1's), its own image name, and a
build key distinct from mtcollins1's -- so a derivation that ignored the
subject's stem goes RED here.

* census image: the seed's early commands have one author

Close review 80838 (DESIGN 2/3: authored duplication is a meaning fork; minimization
holds only if each fact lives in exactly one place).

The 80811 fix built the user-data payload from (nocloud_dir, program_file) inside
census_image_derivation so the early command and seed file cannot cross, but it
left an independently authored twin with the same UbuntuAutoinstallLiveCommands
shape at the mtcollins1_census_live_commands receipt row. Two sources that can
drift; the inhabitance witness only detected disagreement after the fact.

census_live_commands(nocloud_dir, program_file) is now the single helper that
builds the payload, consumed by both the derivation and the receipt row, so the
early-command fact stays single-authority and the two cannot drift.

* census readback: the receipt's host is derived from the leg's target

Close review 80844 (DESIGN 5: no plausible output is fabricated; DESIGN 3:
a materially different contract needs a materially different name).

census_medium_readback took a free host parameter and stamped it into the
receipt and the unreadable arms, while the legs actually ran on
export.target. A caller could pass a host the producer never contacted and
get a refusal naming that host -- contradicting the module's own contract
that the receipt names the host that actually took the leg, one fact, not two
inputs a caller can cross.

The effectful producer no longer takes host: it derives it from the qualified
medium's own exporter target address (known_hosts_host_field(export.target.address)),
which is the SSH target the legs run on. The receipt and the unreadable arms
now name the host the leg actually ran on -- it cannot name a different one.
The host parameter stays only on the supplied-answers fold
(census_medium_readback_from_answers), where it is a witness boundary value the
witness supplies and asserts. Producer call sites (boot_run, witness) re-pointed.

* census readback: control (c) -- the refusal names the subject and the host its receipt recorded

Close review 80853 (DESIGN 5: done means a real consumer green by execution plus a
discriminating input that goes red when the behavior is wrong).

The refusal subject/host parameterization (the answer to baked-in mtcollins1/srv2
naming) had no discriminating control: every refusal claim called with
mtcollins1_census_machine and never asserted that the second subject's label -- or a
non-srv2 receipt host -- appears in the rendered text. A renderer that ignored its
subject parameter and hardcoded mtcollins1, or ignored the receipt host and hardcoded
srv2, would have stayed green.

control_c is the discriminating control: a not-census refusal for the second subject
names that subject (and not mtcollins1), and a record-unreadable refusal with a
non-srv2 receipt host renders that host (and not srv2) -- so the text names the
subject it was given and the host its receipt recorded. It is pure (renders a
constructed readback), so a renderer that drops the parameters goes RED here.

* census image / boot_run: cite the symbol, drop the unused import

Close review 80870.

- census_image.dag: the census_live_commands comment cited "the derivation
  (line 219)" -- a positional pointer that any edit above the line silently
  invalidates (DESIGN 3: cite the symbol, not the position). Replaced with
  the symbol census_image_derivation.
- mtcollins1_boot_run.dag: the import line added operator_host_srv2, but
  nothing in the file uses it (grep -c returns 1, the import itself). Left
  over from moving the executor host into the readback receipt, where the
  host is now derived from the export target. Removed the unused import.

* machine_intake: census_member_readback takes the attempt identity as an input

Side-chat HOLD on #14065 (b220363): the generic census_member_readback and
census_member_mapped_attributes called prepare_fleet_ssh_agent_context under the
captured mtcollins1 row (census_member_readback_attempt_raw) and owned a
mtcollins1-named scratch prefix. The fleet-SSH attempt identity owns the paths that
materialize the public selector and the verified known_hosts anchor, so a second or
concurrent subject would have run under mtcollins1's attempt identity: two subjects
alias one attempt-owned materialization, and a receipt cannot say whose attempt
prepared the SSH context. The medium readback already takes its attempt; the member
readback did not.

DESIGN §3/§5: one fact, not two inputs a caller can cross, and no fabricated receipt.

- census_member_readback and census_member_mapped_attributes take `attempt: NonEmptyStr`
  and prepare their context under it.
- The scratch prefix is a fact OF the attempt: census_member_workspace_prefix_of /
  _template_of derive it, replacing the two mtcollins1 prefix data rows (deleted).
- CensusMemberReadbackAttempt now carries `attempt`, and the receipt renders it, so a
  receipt names the attempt whose context it answers for. census_member_attempt_of,
  render_workspace_outcome and render_census_member_attempt thread it.
- The mtcollins1 wet wrapper supplies census_member_readback_attempt_raw, and the
  mtcollins1 receipt output path stays there.

Witness controls (each demonstrated RED once against a broken variant, see PR):
- a_second_subject_attempt_owns_its_receipt_and_its_namespace: a second subject under
  its own attempt stores that attempt, admits its own disjoint namespace, and renders
  neither mtcollins1's attempt identity nor mtcollins1's namespace.
- an_unsafe_attempt_identity_refuses_at_admission_before_any_agent_or_remote_work: the
  producer admits the caller's attempt before the agent; a non-safe-segment identity
  refuses with admission's own words and requests no workspace, and the report entry
  refuses the same way.

member witness: 21 claim(s), 21 held, 0 did not hold.

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants