Repository navigation
GCP one-token bootstrap of the keyless BMC assimilator (API-enablement + SA + IAM + WIF as .dag, proven live) - #5712
Merged
Merged
Conversation
…nly validation Models the onboarding of the operator's new Altra server (BMC 192.168.1.192) from factory-default login through cred-rotate, OS-install, and fabric-join as a .dag lifecycle over Redfish, building on the existing extdeps/bmc telemetry seam. - extdeps/bmc/types.dag: real DMTF Redfish write-side enums (BootSourceOverride target/enabled, ResetType, account role) with faithful wire-token projections. - extdeps/bmc/http.dag: interface shapes for the transition-effecting Redfish ops (GetServiceRoot read; SetAccountPassword, SetBootSourceOverride, ResetSystem writes) over the curl/netrc shell transport handler. Secrets ride a runtime request_body_file, never argv or the repo. - gunbc/bmc_onboarding.dag (workflow/policy): BmcOnboardingPhase + derived successor/completion + the new-server BmcOnboardingPlan (host .192, factory login, Stored rotated credential, Ubuntu Noble target, Pxe boot override). - gunbc/tools/bmc_onboard.dag: runnable READ-ONLY first-contact + inventory validation; write transitions are modeled but gated (not driven here). - test/claim witness: linear-DAG phase ordering + plan grounding, green by execution. Grounded against the live BMC at 192.168.1.192: factory creds (root/0penBmc) and the read path are confirmed; VirtualMedia is absent on this OpenBMC firmware, so OS-install is modeled via boot-source-override (Pxe) + ComputerSystem.Reset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bmc_onboarding_next_phase is now the sole authority for the linear successor relation; the standalone bmc_onboarding_phase_order list duplicated it. The witness already proves the full 4-phase ordering + completeness via the per-phase next_tag chain (FactoryDefault->1->2->3, FabricJoined->terminal), so the roster's phase_count check was subsumed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… name, §5) The tool only performs the read-only FactoryDefault validation (GetServiceRoot + GetSystem); it does not drive cred-rotate/OS-install/fabric-join. Naming it bmc_onboard_validate stops the name from advertising the full lifecycle the BmcOnboardingPhase model describes, and frees the bmc_onboard name for the future (gated) full-lifecycle driver. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ate) The predicate had one caller (the witness) and the witness's next_tag chain already proves completion (FabricJoined -> -1 = terminal; others -> 1/2/3). Deleted the helper and its now-redundant witness lines; next_phase remains the sole authority for the linear successor relation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…le-verified, live-gated) Assemble the credential-rotation leg of orchestration C over new_altra_onboarding_plan, now that Lane B (#5634) landed the Redfish auth-as-Secret seam on main: materialize the netrc + PATCH body via Filesystem.Write (executable file effect), SetAccountPassword (Redfish write), then reauth with the new credential to VERIFY the rotation took — fail-closed if rejected. The minted Secret is declassified to String exactly once, explicitly (the Secret type forbids accidental exposure). Verified by execution: gunbc compile --source-root dsl => 464 modules, 471 files, 0 diagnostics — the legs typecheck and compose. LIVE execution is operator-fenced (first destructive write); live-correctness of account_id/body shape is confirmed only by the gated run against .192, not this typecheck. Not a *_test.dag, so it does NOT auto-enroll as a floor witness (no false CI-coverage claim). §5 debt (named): the netrc + body files transiently hold the credential on disk at default umask with no post-run unlink; dissolution = mode-0600 file write + unlink leg. gen+store leg (entropy mint #5633 -> base64 -> GCP store) wires in once #5633 lands; os-install leg pends Lane E (#5638). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e→rotate→reauth→os-install) Assemble the full lifecycle as one .dag workflow over new_altra_onboarding_plan + srv3_os_install_plan, now that all four lanes (entropy #5633, auth-Secret #5634, OS-install #5638) landed on main: acquire — GetServiceRoot + factory-login GetSystem (read; proven live earlier) gen — mint a credential from OS entropy (extdeps.entropy Urandom), FAIL-CLOSED on the Optional (never a fabricated/empty credential — dissolves the witness scaffold's empty-string arm per cool-lynx's dissolution trigger) store — base64 of the same octets -> GCP AddVersion (durability) under the plan's secret id; token via gcloud rotate+reauth — Filesystem.Write netrc + PATCH body, SetAccountPassword, reauth with the NEW credential to verify the rotation took (fail-closed) os-install — re-materialize netrc with the NEW credential (factory netrc is now stale), SetBootSourceOverride(Pxe,Once) + ResetSystem(ForceRestart) via the Lane-E wire fns to boot srv3 into the PXE/autoinstall path Legs chain on ProcessExit so any failure short-circuits. The minted Secret is declassified to String exactly once, explicitly (the type forbids accidental leak). Verified by execution: gunbc compile --source-root dsl => 480 modules, 488 files, 0 diagnostics. The pure wire-shape builders (netrc line, Redfish PATCH/POST JSON bodies, GCP secret name) have by-execution witnesses — all 5 green via --claim-run. The live Redfish/GCP/entropy legs are OPERATOR-FENCED (destructive); their live correctness is confirmed only by the gated run against .192, not this typecheck. §5 debt (named): netrc/body files transiently hold the credential on disk at default umask with no unlink; dissolution = mode-0600 file write + unlink leg. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cred-only entry point Address review #32107: - bmc_credential_bytes: Int gets a 🟡 marker riding extdeps.entropy's entropy_count_bytes_unit_debt (same bytesize-argv-interpolation dissolution) — no longer an unmarked *_bytes-on-Int. - the concat-built Redfish PATCH/POST bodies get a 🟡 dissolve-on marker (safe for the current base64url-credential + enum-wire call sites, which the body-shape witnesses pin; dissolution = a structured JSON-object encoder authority). Also adds bmc_assimilate_srv3_credential — a credential-only entry point (acquire -> gen -> store(read-back gated) -> rotate -> reauth, STOPPING before os-install) so the live BMC credential assimilation can run while no PXE/install server exists yet (running the full bmc_onboard_srv3 would reset srv3 into a dead PXE boot). Refactors the shared store+rotate into bmc_store_and_rotate (no duplication). Compile clean (488 files, 0 diagnostics); body-shape witnesses green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The auth_input fix's call-site edit split the let-binding across two lines; rustfmt wants it on one (fits in width). cargo fmt --all --check now clean — this was the rust_tests CI failure on 311ff38 (fmt gate), not a logic issue. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…§2, review #32137)
mint_credential_octets base64_decode'd Urandom.octets_b64 to List<UInt8>, then
credential_from_octets base64_encode'd it straight back — base64_encode∘base64_decode
is identity, so the octets intermediate (and the Optional failure mode that could only
trip on a base64_decode bug, never on real Urandom output) bought nothing. Collapse to
one fn: mint_bmc_credential() = Urandom.ReadBytes(count).octets_b64 as Secret. The
credential IS the base64 entropy string directly — same string set on the BMC, stored
as the GCP payload, and compared in the read-back gate (identity preserved; the BMC
password is byte-for-byte what it was). Drops the std.encoding + std.integer{UInt8}
imports and the unused ExitFailure. Fail-closed now lives at the Urandom service call
(nonzero exit raises). Compile 488/0, body witnesses green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t + SA + IAM + WIF modeled as .dag, proven live
Collapses the entire operator-fenced WIF runbook (every manual `gcloud` block) into a
single admin-token .dag orchestration over the proven v1 REST executor. The only manual
input is the initial admin access token, acquired via the operator's existing gcloud login
(shell.GCloud.AuthPrintAccessToken). No host/.rs changes — the REST executor already
serializes list/map/record JSON bodies.
New surfaces (all extdeps interface shapes + one workflow orchestration):
- extdeps/cloud/gcp/serviceusage.dag — ServiceUsage.BatchEnableServices
- extdeps/cloud/gcp/iam_admin.dag — IamAdmin.{CreateServiceAccount,CreateWorkloadIdentityPool,CreateWorkloadIdentityPoolProvider}
- extdeps/cloud/gcp/secret_manager.dag — SecretManager.SetSecretIamPolicy (resource-level least-priv)
- gcp.dag — shared GcpOperation + GcpService.{GcpIamCredentials,GcpServiceUsage} + gcp_service_api_id
- gunbc/assimilate/bmc_bootstrap_provision.dag — enable -> SA -> bind -> pool -> provider
API enablement is a DEPENDENCY OF USAGE, not a hand-typed list: bmc_required_gcp_apis is
derived from bmc_assimilate_service_deps (the GcpService set the path uses) via
gcp_service_api_id, single-authority on the GcpService enum.
PROVEN LIVE 2026-06-24 against gunbai-secrets: all 5 ops dispatched; SA minted, both
least-priv roles bound on bmc-srv3-admin only, WIF provider ACTIVE. Witness
bmc_bootstrap_provision_witness_test.dag green by execution (least-priv + closed-API-set +
derivation, with discriminating negatives). §3 single authority preserved (federation facts).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Collapses the entire operator-fenced WIF runbook (
docs/runbooks/bmc-assimilator-wif-setup.md— six manualgcloudblocks) into one admin-token.dagorchestration over the proven v1 REST executor. The only manual input is the initial admin access token, acquired via the operator's existing gcloud login (shell.GCloud.AuthPrintAccessToken— the "one button").Zero host/
.rschanges — the REST executor already serializes list/map/record JSON bodies; this is pure.dagmodeling.New surfaces
extdeps/cloud/gcp/serviceusage.dagServiceUsage.BatchEnableServices(API enablement)extdeps/cloud/gcp/iam_admin.dagIamAdmin.{CreateServiceAccount, CreateWorkloadIdentityPool, CreateWorkloadIdentityPoolProvider}extdeps/cloud/gcp/secret_manager.dagSecretManager.SetSecretIamPolicy(resource-level least-priv)extdeps/cloud/gcp/gcp.dagGcpOperation;GcpService.{GcpIamCredentials, GcpServiceUsage};gcp_service_api_idgunbc/assimilate/bmc_bootstrap_provision.dagAPI enablement is a dependency of usage
bmc_required_gcp_apisis derived frombmc_assimilate_service_deps(theGcpServiceset the assimilate path uses) viagcp_service_api_id— single authority on theGcpServiceenum + wire label. No hand-typed.googleapis.comstrings.Proven live (2026-06-24, project
gunbai-secrets)All 5 ops dispatched through the v1 REST executor with one admin token:
batchEnable→ operation returned (list body serialized as JSON array, incl. the derived list)CreateServiceAccount→bmc-assimilator@…minted (nested body)SetSecretIamPolicy→etagreturned; both least-priv roles bound onbmc-srv3-adminonly (List<GcpBinding>records → array of objects)CreateWorkloadIdentityPool+CreateWorkloadIdentityPoolProvider→ providerACTIVEwith correctattributeMapping/condition/issuer (Map→ object)Known first-run race (documented): a freshly-created SA takes a few seconds to propagate before the IAM binding can reference it — re-run once and it converges (a 409-tolerant single-pass converge is the noted follow-up).
Witness (green by execution)
dsl/test/claim/bmc_bootstrap_provision_witness_test.dag: least-privilege (exactly 2 roles, SA-scoped), broad-role exclusion, closed API set with a discriminating negative (compute.googleapis.comrejected), and the dependency-of-usage derivation.🤖 Generated with Claude Code