diff --git a/.github/workflows/fleet-converge.yml b/.github/workflows/fleet-converge.yml index 27b68eadd5e..bc9ca33550d 100644 --- a/.github/workflows/fleet-converge.yml +++ b/.github/workflows/fleet-converge.yml @@ -13,7 +13,7 @@ on: mode: description: "plan shows membership hunks; apply executes a prior plan artifact; org_actions_observe validates the org credential and refuses on runner-group drift; org_runner_roster_observe reads the organization's self-hosted runner registrations to exhaustion under the same token and refuses unless the roster is complete; app_control_plane_observe reads the gunbai-ci App registration and webhook config with a short-lived App JWT and refuses on hook-config drift; app_key_version_verify reads the gunbai-ci App private key at the EXACT Secret Manager version named by app_key_version, mints an installation token with it, and refuses unless GitHub accepts it and the key's rotation deadline has not passed -- no add, disable or destroy; runner_browser_toolchain_converge installs the declared Playwright/Chromium toolchain (apt host libraries as the administrator, digest-pinned node, Playwright and Chromium archives into the job user's root) on the selected host, which must be in the pool that runs the floor job, and refuses unless every digest, version and host library reads back and headless Chromium renders a local page; microvm_host_converge installs the cited Firecracker release on the selected host and reads the kvm grant back, refusing by name when the grant has not landed (the grant itself is applied by the full-host apply spine); microvm_network_apply stages the slot and host network files the model renders at the named expected_revision as root:root 0600 in a root-only directory over the fleet SSH edge as the host's ADMINISTRATOR, installs them with the modeled operations, reloads networkd, systemd-sysctl and the nft loader unit, and reads the ruleset back -- the job user is granted none of it, because install plus systemctl over content that principal can write is arbitrary root for any pull request; guest_image_observe reports the micro-VM base artifacts and their measured digests on the selected host and builds nothing; guest_image_converge builds the runner guest image there; microvm_boot_probe boots that image and reads its serial console; spark_grants and spark_bootstrap prepare the selected Spark; spark_serving_apply applies the promoted fabric groups' pair vLLM units over the password session, workers before heads; spark_native_serving_apply relaunches group B's native four-rank arm as ONE BOUNDED TRANSACTION -- an all-host preflight that mutates nothing and refuses on any unresolved artifact from an earlier run, the incumbent UNIT preserved (plus a diagnostic inspect of the incumbent container -- the container itself cannot outlive its unit, because these run --rm in the foreground under systemd), the head applied BEFORE the workers so every rank joins the head's new rendezvous store, then a readback of the COMPLETE realization through each container's own inspect -- every field the create spec states, the effective --node-rank off that container's argv, and NCCL's typed transport verdict on that incarnation -- plus the head's front door answering with this arm's served model, and then commit or a rollback of the whole arm -- and it is a separate mode because it reloads a 328 GB arm and nobody should reach it by asking for the pair units; spark_runtime_image_probe pulls the pinned runtime image on the selected Spark and reads its capabilities from inside its own digest, changing nothing; spark_wireless_link_converge reads the selected Spark's Wi-Fi power save (runtime via iw, persisted via the NetworkManager profile), link and kernel disconnect count, sets whichever realization differs from the declared intent, and refuses unless the readback decides Noop; spark_grants_observe reads every procured Spark's sudo grant listing as gunbc-automation and the bootstrap principal, and its sudoers drop-in shape, and writes nothing; spark_v41_checkpoint_materialize fetches the admitted published DeepSeek V4.1 files onto the selected Group A Spark (about 510 GB; spark_v41_row_store_encode encodes the eight Engram row stores from the verified shards on the selected Group A Spark and reads each store's sha256; spark_v41_row_store_readback reads those stores back at their header, first and last record and every rank seam, with the published source rows at the same rows, and writes nothing; spark_v41_engram_differential compares upstream's Engram lookup kernel with the design-B file-backed lookup over sampled real rows of every row store, byte for byte, and writes nothing; it states the requirement and refuses before fetching when the disk cannot hold it), publishes each only after its sha256 matches the manifest, leaves a present file with the right digest alone and refuses one with the wrong digest, and reads the storage-backed Engram spans from the verified shards; a transfer runs detached and a rerun reattaches; spark_v41_runtime_image_build PRODUCES the DeepSeek V4.1 image on the selected Spark -- it verifies the candidate's three FlashInfer wheels against the digests the candidate keys, converges the patched source tree, builds from it, reads the produced configuration digest back from inside the image through the probe route, and admits that digest against the candidate's own recipe, refusing a digest that does not recompute from it -- and it is a separate mode from the probe because it occupies one host for hours where the probe occupies it for minutes; spark_v41_runtime_image_distribute moves that produced image, named by the configuration digest its production receipt read back, from the host that receipt names to the selected Group A Spark -- save into its fabric blob root, pulled by the target straight over the fabric rail, load -- after stating its size against every filesystem a copy lands on, leaves a target already holding the digest untouched, refuses a target holding a different image under the tag, and refuses unless the target's image inspect Id reads back as that digest; spark_v41_group_a_launch reads the production image back under its tag on every Group A host, reads its registry inside its digest and every host's occupancy, and only when Group A is suspended for this candidate with every host held and vacant stages the Engram manifest and applies the V4.1 four-rank arm as one transaction at the capacity measurement's shape -- the target names only the session host; spark_v41_serving_load runs the shared serving-load runner against the V4.1 head (target must be srv6): one vllm bench serve step per capacity-measurement concurrency, metrics scraped around each, host pressure read on every Group A rank, and the staircase stop rules applied over the worst rank; it changes no unit and writes only its receipt; host_reset_return drives a rostered reset subject through its controller and measures the return FROM A PEER, and takes reset_observer rather than host because the observer must not be the subject; runner_host_file_observe reads the runner teardown drop-in, the needrestart deferral and the loaded teardown on the selected host as its administrator and writes nothing; runner_host_file_converge writes whichever of those two files differs, reloads systemd only if the drop-in changed, and refuses unless the manager then reports the declared teardown; microvm_controller_install writes the microVM slot controller's root-owned release locus (gunbc + sources + Firecracker + jailer) and the gunbc-microvm-slot@ template unit and the gunbc-microvm-slot-reserve broker unit on srv1 and starts neither; microvm_slot_reserve starts that broker unit once on srv1, which reserves the shakedown cell through the fabric broker route from inside the root process (slot, demand and offer derived from the model, never inputs), and uploads that invocation's reservation receipt, failing unless the reservation committed; microvm_slot_start starts the shakedown slot's controller unit once on srv1 (the instance is derived from the model, never an input), waits for it bounded by the unit's own stop timeout, and uploads that invocation's controller receipt; microvm_runner_group_ensure (srv1 only) reads the organization's runner groups and, only when the microvm-shakedown group is absent, files ONE operator approval, creates it restricted to the shakedown workflow on the default branch, and refuses unless the readback holds that restriction; mtcollins1_census_qemu_host_observe reads the selected host's KVM device and, under the job user's census QEMU root, the qemu-system-aarch64 build, its ldd libraries and the AAVMF images against their pins, and writes nothing; mtcollins1_census_qemu_toolchain_converge places that root as the job user (digest-pinned noble .debs unpacked with dpkg-deb -x, no apt, no Recommends) and refuses unless the same observe reads it ready; workspace_source_pack (host=srv1) enumerates the operator workspace as the job user, drops every path the credential exclusion row names and every build output, tars exactly the remainder, reads the archive back and refuses if any member is excluded, and puts it into the workspace bucket under its SHA-256, refusing by name when the runner cannot read a source root; workspace_checkpoint_measure (host=srv3, workspace_source_object = that SHA-256) refuses unless the job user's home is on btrfs, then times a read-only subvolume snapshot, a pinned kopia scan of it, the content-addressed chunk upload and the gated revision commit, and receipts every time with the 10 s / 60 s / 10 GB draft verdicts; workspace_checkpoint_restore (host=srv3) commits a small authored workspace, puts one chunk with no head advance, destroys the directory, restores it from the head closure and requires byte-equality with the uncommitted chunk absent -- both srv3 modes refuse their commit while the R2 head's CAS ground is uncited" required: true - options: [plan, launch_environment_plan, allocation_store_plan, workspace_commissioning_plan, apply, org_actions_observe, app_control_plane_observe, microvm_host_converge, microvm_network_observe, microvm_network_apply, guest_image_observe, guest_image_converge, microvm_boot_probe, spark_grants, spark_bootstrap, spark_serving_apply, spark_native_serving_apply, spark_runtime_image_probe, spark_wireless_link_converge, spark_grants_observe, spark_v41_checkpoint_materialize, spark_v41_row_store_encode, spark_v41_row_store_readback, spark_v41_engram_differential, spark_runtime_image_produce, spark_runtime_image_distribute, spark_arm_group_load, spark_arm_checkpoint_materialize, spark_arm_group_observe, spark_arm_group_launch_plan, spark_arm_group_launch, spark_v41_serving_load, dashboard_deploy, approval_broker_dark_install, microvm_controller_install, microvm_slot_reserve, microvm_slot_start, rlm_launch_deployment_receipt, host_reset_return, runner_host_file_observe, runner_host_file_converge, site_pxe_edge_observe, site_pxe_edge_converge, runner_password_session_tool_converge, runner_browser_toolchain_converge, r2_mint_preflight, r2_object_write_mint, org_runner_roster_observe, approval_keyring_converge, approval_device_enrolment_code_issue, mtcollins1_boot, mtcollins1_fan_observe, mtcollins1_ui_bundle_observe, mtcollins1_kvm_observer_observe, mtcollins1_census_image_publish, mtcollins1_census_member_readback, mtcollins1_census_qemu_host_observe, mtcollins1_census_qemu_toolchain_converge, host_credential_custody_converge, app_key_version_verify, r2_bucket_ensure, r2_bucket_admin_mint, r2_workspace_object_read_mint, r2_workspace_object_write_mint, r2_conditional_put_race_probe, workspace_source_pack, workspace_checkpoint_measure, workspace_checkpoint_restore, fabric_writer_identity_observe, pair_serving_d0, microvm_runner_group_ensure, gcp_iam_converge, namecheap_observe] + options: [plan, launch_environment_plan, allocation_store_plan, workspace_commissioning_plan, apply, org_actions_observe, app_control_plane_observe, microvm_host_converge, microvm_network_observe, microvm_network_apply, guest_image_observe, guest_image_converge, microvm_boot_probe, spark_grants, spark_bootstrap, spark_serving_apply, spark_native_serving_apply, spark_runtime_image_probe, spark_wireless_link_converge, spark_grants_observe, spark_v41_checkpoint_materialize, spark_v41_row_store_encode, spark_v41_row_store_readback, spark_v41_engram_differential, spark_runtime_image_produce, spark_runtime_image_distribute, spark_arm_group_load, spark_arm_checkpoint_materialize, spark_arm_group_observe, spark_arm_group_launch_plan, spark_arm_group_launch, spark_v41_serving_load, dashboard_deploy, approval_broker_dark_install, microvm_controller_install, microvm_slot_reserve, microvm_slot_start, rlm_launch_deployment_receipt, host_reset_return, runner_host_file_observe, runner_host_file_converge, site_pxe_edge_observe, site_pxe_edge_converge, runner_password_session_tool_converge, runner_browser_toolchain_converge, r2_mint_preflight, r2_object_write_mint, org_runner_roster_observe, approval_keyring_converge, approval_device_enrolment_code_issue, mtcollins1_boot, mtcollins1_fan_observe, mtcollins1_ui_bundle_observe, mtcollins1_kvm_observer_observe, mtcollins1_census_image_publish, mtcollins1_census_member_readback, mtcollins1_census_qemu_host_observe, mtcollins1_census_qemu_toolchain_converge, host_credential_custody_converge, app_key_version_verify, r2_bucket_ensure, r2_bucket_admin_mint, r2_workspace_object_read_mint, r2_workspace_object_write_mint, r2_cache_object_read_mint, r2_cache_object_write_mint, r2_conditional_put_race_probe, workspace_source_pack, workspace_checkpoint_measure, workspace_checkpoint_restore, fabric_writer_identity_observe, pair_serving_d0, microvm_runner_group_ensure, gcp_iam_converge, namecheap_observe] type: choice target: description: "Spark target host for the spark_* modes, naming the administrator credential the run materializes (any rostered Spark: srv5, srv6, srv7, srv8, srv9, srv10, srv11, srv12); apply reads the subject off the plan artifact" @@ -1415,6 +1415,44 @@ jobs: retention-days: 30 if: always() && github.event.inputs.mode == 'r2_workspace_object_write_mint' timeout-minutes: 10 + - name: R2 cache-blobs object-read token mint (AccountTokens.Create + Secret Manager custody) + id: r2_cache_object_read_mint + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/cloudflare/r2_token_mint_run.dag --function run_cache_object_read + env: + WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} + if: github.event.inputs.mode == 'r2_cache_object_read_mint' + timeout-minutes: 5 + - name: Upload R2 cache-blobs object-read mint receipt (token id + custody version resource; no secret) + id: r2_cache_object_read_mint_receipt_upload + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + with: + name: r2-cache-object-read-mint-receipt + path: /tmp/r2-mint-receipts/${{ github.run_id }}-${{ github.run_attempt }}/r2-*-object-read-mint-receipt.txt + if-no-files-found: warn + retention-days: 30 + if: always() && github.event.inputs.mode == 'r2_cache_object_read_mint' + timeout-minutes: 10 + - name: R2 cache-blobs object-write token mint (AccountTokens.Create + Secret Manager custody) + id: r2_cache_object_write_mint + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/cloudflare/r2_token_mint_run.dag --function run_cache_object_write + env: + WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} + if: github.event.inputs.mode == 'r2_cache_object_write_mint' + timeout-minutes: 5 + - name: Upload R2 cache-blobs object-write mint receipt (token id + custody version resource; no secret) + id: r2_cache_object_write_mint_receipt_upload + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + with: + name: r2-cache-object-write-mint-receipt + path: /tmp/r2-mint-receipts/${{ github.run_id }}-${{ github.run_attempt }}/r2-*-object-write-mint-receipt.txt + if-no-files-found: warn + retention-days: 30 + if: always() && github.event.inputs.mode == 'r2_cache_object_write_mint' + timeout-minutes: 10 - name: R2 If-Match race probe (two concurrent conditional PUTs per round on one entity tag) id: r2_conditional_put_race_probe run: |- diff --git a/dag/gunbc/ci/ci_spec.dag b/dag/gunbc/ci/ci_spec.dag index bda1d62f674..531f1776459 100644 --- a/dag/gunbc/ci/ci_spec.dag +++ b/dag/gunbc/ci/ci_spec.dag @@ -824,6 +824,16 @@ data gunbc_ci_r2_workspace_object_write_mint_target: GunbcRunStepTarget = GunbcR function: "run_workspace_object_write", } +data gunbc_ci_r2_cache_object_read_mint_target: GunbcRunStepTarget = GunbcRunStepTarget { + entry: "dag/gunbc/cloudflare/r2_token_mint_run.dag", + function: "run_cache_object_read", +} + +data gunbc_ci_r2_cache_object_write_mint_target: GunbcRunStepTarget = GunbcRunStepTarget { + entry: "dag/gunbc/cloudflare/r2_token_mint_run.dag", + function: "run_cache_object_write", +} + data gunbc_ci_r2_conditional_put_race_probe_target: GunbcRunStepTarget = GunbcRunStepTarget { entry: "dag/gunbc/cloudflare/r2_conditional_put_race_probe.dag", function: "run_r2_conditional_put_race_probe", @@ -1374,6 +1384,8 @@ fn gunbc_run_step_targets() -> List { gunbc_ci_r2_object_write_mint_target, gunbc_ci_r2_workspace_object_read_mint_target, gunbc_ci_r2_workspace_object_write_mint_target, + gunbc_ci_r2_cache_object_read_mint_target, + gunbc_ci_r2_cache_object_write_mint_target, gunbc_ci_r2_conditional_put_race_probe_target, gunbc_ci_workspace_source_pack_target, gunbc_ci_workspace_checkpoint_measure_target, @@ -2683,6 +2695,26 @@ fn gunbc_ci_r2_workspace_object_write_mint_invoke() -> String { ) } +fn gunbc_ci_r2_cache_object_read_mint_invoke() -> String { + gunbc_run_step_script( + source_roots: witness_layer_roots, + entry: gunbc_ci_r2_cache_object_read_mint_target.entry, + function: gunbc_ci_r2_cache_object_read_mint_target.function, + claim_run: false, + receipt_rel: none + ) +} + +fn gunbc_ci_r2_cache_object_write_mint_invoke() -> String { + gunbc_run_step_script( + source_roots: witness_layer_roots, + entry: gunbc_ci_r2_cache_object_write_mint_target.entry, + function: gunbc_ci_r2_cache_object_write_mint_target.function, + claim_run: false, + receipt_rel: none + ) +} + fn gunbc_ci_r2_conditional_put_race_probe_invoke() -> String { gunbc_run_step_script( source_roots: witness_layer_roots, diff --git a/dag/gunbc/cloudflare/r2_mint_secret_access.dag b/dag/gunbc/cloudflare/r2_mint_secret_access.dag index 156c4d3ae09..0ebef7ca5ff 100644 --- a/dag/gunbc/cloudflare/r2_mint_secret_access.dag +++ b/dag/gunbc/cloudflare/r2_mint_secret_access.dag @@ -61,6 +61,8 @@ import gunbc.cloudflare.r2_origin { cloudflare_r2_bucket_admin_secret_id, fabric_workspace_read_secret_id, fabric_workspace_write_secret_id, + fabric_cache_blobs_read_secret_id, + fabric_cache_blobs_write_secret_id, } import gunbc.secret_provision { fleet_secret_ref } import std.types { List } @@ -175,6 +177,11 @@ fn r2_workspace_read_grant_for(secret_id: NonEmptyStr) -> SecretAccessGrant { secret_accessor_grant(target: r2_workspace_secret_target(secret_id: secret_id)) } +// THE CACHE-BLOBS CONTAINERS TAKE THE SAME THREE CELLS ON EACH OF THEIR TWO SECRETS (run_cache_object_read +// and run_cache_object_write perform the same three secret effects as the workspace mints, against their own +// container), so the cell constructors above are reused and the roster below names the two secrets. +// The bootstrap derives its secret population from this roster: a container missing here is never created. +// // THE ROSTER, ONE ROW PER CELL, IN EFFECT ORDER. gunbc.auth.gcp_iam_converge reads it as a grant set, // and the custody secret population (what the bootstrap creates and binds) is derived from its // targets rather than listed a second time. @@ -193,5 +200,11 @@ fn r2_mint_custody_grants() -> List { r2_workspace_container_read_grant_for(secret_id: fabric_workspace_write_secret_id), r2_workspace_version_add_grant_for(secret_id: fabric_workspace_write_secret_id), r2_workspace_read_grant_for(secret_id: fabric_workspace_write_secret_id), + r2_workspace_container_read_grant_for(secret_id: fabric_cache_blobs_read_secret_id), + r2_workspace_version_add_grant_for(secret_id: fabric_cache_blobs_read_secret_id), + r2_workspace_read_grant_for(secret_id: fabric_cache_blobs_read_secret_id), + r2_workspace_container_read_grant_for(secret_id: fabric_cache_blobs_write_secret_id), + r2_workspace_version_add_grant_for(secret_id: fabric_cache_blobs_write_secret_id), + r2_workspace_read_grant_for(secret_id: fabric_cache_blobs_write_secret_id), ] } diff --git a/dag/gunbc/fleet/fleet_converge_workflow.dag b/dag/gunbc/fleet/fleet_converge_workflow.dag index 2a66a050a1f..f47ec149dcc 100644 --- a/dag/gunbc/fleet/fleet_converge_workflow.dag +++ b/dag/gunbc/fleet/fleet_converge_workflow.dag @@ -164,6 +164,8 @@ import gunbc.ci_spec { gunbc_ci_r2_object_write_mint_invoke, gunbc_ci_r2_workspace_object_read_mint_invoke, gunbc_ci_r2_workspace_object_write_mint_invoke, + gunbc_ci_r2_cache_object_read_mint_invoke, + gunbc_ci_r2_cache_object_write_mint_invoke, gunbc_ci_r2_conditional_put_race_probe_invoke, gunbc_ci_workspace_source_pack_invoke, gunbc_ci_workspace_checkpoint_measure_invoke, @@ -346,6 +348,8 @@ type FleetConvergeWorkflowMode | R2ObjectWriteMint | R2WorkspaceObjectReadMint | R2WorkspaceObjectWriteMint + | R2CacheObjectReadMint + | R2CacheObjectWriteMint | R2ConditionalPutRaceProbe | WorkspaceSourcePack | WorkspaceCheckpointMeasure @@ -422,6 +426,8 @@ fn fleet_converge_workflow_mode_wire(mode: FleetConvergeWorkflowMode) -> String R2ObjectWriteMint => "r2_object_write_mint" R2WorkspaceObjectReadMint => "r2_workspace_object_read_mint" R2WorkspaceObjectWriteMint => "r2_workspace_object_write_mint" + R2CacheObjectReadMint => "r2_cache_object_read_mint" + R2CacheObjectWriteMint => "r2_cache_object_write_mint" R2ConditionalPutRaceProbe => "r2_conditional_put_race_probe" WorkspaceSourcePack => "workspace_source_pack" WorkspaceCheckpointMeasure => "workspace_checkpoint_measure" @@ -466,7 +472,7 @@ fn fleet_converge_spark_target_description() -> String { ], "") } -data fleet_converge_workflow_modes: List = [FullHostPlan, LaunchEnvironmentPlan, AllocationStorePlan, WorkspaceCommissioningPlan, FleetApply, OrgActionsObserve, AppControlPlaneObserve, MicrovmHostConverge, MicrovmNetworkObserve, MicrovmNetworkApply, GuestImageObserve, GuestImageConverge, MicrovmBootProbe, SparkGrants, SparkBootstrap, SparkServingApply, SparkNativeServingApply, SparkRuntimeImageProbe, SparkWirelessLinkConverge, SparkGrantsObserve, SparkV41CheckpointMaterialize, SparkV41RowStoreEncode, SparkV41RowStoreReadback, SparkV41EngramDifferential, SparkRuntimeImageProduce, SparkRuntimeImageDistribute, SparkArmGroupLoad, SparkArmCheckpointMaterialize, SparkArmGroupObserve, SparkArmGroupLaunchPlan, SparkArmGroupLaunch, SparkV41ServingLoad, DashboardDeploy, ApprovalBrokerDarkInstall, MicrovmControllerInstall, MicrovmSlotReserve, MicrovmSlotStart, RlmLaunchDeploymentReceipt, HostResetReturn, RunnerHostFileObserve, RunnerHostFileConverge, SitePxeEdgeObserve, SitePxeEdgeConverge, RunnerPasswordSessionToolConverge, RunnerBrowserToolchainConverge, R2MintPreflight, R2ObjectWriteMint, OrgRunnerRosterObserve, ApprovalKeyringConverge, ApprovalDeviceEnrolmentCodeIssue, MtCollins1Boot, MtCollins1FanObserve, MtCollins1UiBundleObserve, MtCollins1KvmObserverObserve, MtCollins1CensusImagePublish, MtCollins1CensusMemberReadback, MtCollins1CensusQemuHostObserve, MtCollins1CensusQemuToolchainConverge, HostCredentialCustodyConverge, AppKeyVersionVerify, R2BucketEnsure, R2BucketAdminMint, R2WorkspaceObjectReadMint, R2WorkspaceObjectWriteMint, R2ConditionalPutRaceProbe, WorkspaceSourcePack, WorkspaceCheckpointMeasure, WorkspaceCheckpointRestore, FabricWriterIdentityObserve, PairServingD0, MicrovmRunnerGroupEnsure, GcpIamConverge, NamecheapObserve] +data fleet_converge_workflow_modes: List = [FullHostPlan, LaunchEnvironmentPlan, AllocationStorePlan, WorkspaceCommissioningPlan, FleetApply, OrgActionsObserve, AppControlPlaneObserve, MicrovmHostConverge, MicrovmNetworkObserve, MicrovmNetworkApply, GuestImageObserve, GuestImageConverge, MicrovmBootProbe, SparkGrants, SparkBootstrap, SparkServingApply, SparkNativeServingApply, SparkRuntimeImageProbe, SparkWirelessLinkConverge, SparkGrantsObserve, SparkV41CheckpointMaterialize, SparkV41RowStoreEncode, SparkV41RowStoreReadback, SparkV41EngramDifferential, SparkRuntimeImageProduce, SparkRuntimeImageDistribute, SparkArmGroupLoad, SparkArmCheckpointMaterialize, SparkArmGroupObserve, SparkArmGroupLaunchPlan, SparkArmGroupLaunch, SparkV41ServingLoad, DashboardDeploy, ApprovalBrokerDarkInstall, MicrovmControllerInstall, MicrovmSlotReserve, MicrovmSlotStart, RlmLaunchDeploymentReceipt, HostResetReturn, RunnerHostFileObserve, RunnerHostFileConverge, SitePxeEdgeObserve, SitePxeEdgeConverge, RunnerPasswordSessionToolConverge, RunnerBrowserToolchainConverge, R2MintPreflight, R2ObjectWriteMint, OrgRunnerRosterObserve, ApprovalKeyringConverge, ApprovalDeviceEnrolmentCodeIssue, MtCollins1Boot, MtCollins1FanObserve, MtCollins1UiBundleObserve, MtCollins1KvmObserverObserve, MtCollins1CensusImagePublish, MtCollins1CensusMemberReadback, MtCollins1CensusQemuHostObserve, MtCollins1CensusQemuToolchainConverge, HostCredentialCustodyConverge, AppKeyVersionVerify, R2BucketEnsure, R2BucketAdminMint, R2WorkspaceObjectReadMint, R2WorkspaceObjectWriteMint, R2CacheObjectReadMint, R2CacheObjectWriteMint, R2ConditionalPutRaceProbe, WorkspaceSourcePack, WorkspaceCheckpointMeasure, WorkspaceCheckpointRestore, FabricWriterIdentityObserve, PairServingD0, MicrovmRunnerGroupEnsure, GcpIamConverge, NamecheapObserve] // WHICH RELEASE PRODUCT A MODE'S JOB RUNS, NAMED PER MODE WITH NO WILDCARD (as the scope and fleet-key // matches above name theirs). A mode whose steps run only gunbc and claim_executor declares the // compiler pair (gunbc.fleet_release_bins_key compiler_pair_release_product): its build job then @@ -526,6 +532,8 @@ fn fleet_converge_mode_release_product(mode: FleetConvergeWorkflowMode) -> Fleet R2ObjectWriteMint => SixteenBinaryPack R2WorkspaceObjectReadMint => SixteenBinaryPack R2WorkspaceObjectWriteMint => SixteenBinaryPack + R2CacheObjectReadMint => SixteenBinaryPack + R2CacheObjectWriteMint => SixteenBinaryPack R2ConditionalPutRaceProbe => SixteenBinaryPack WorkspaceSourcePack => CompilerPairOnly WorkspaceCheckpointMeasure => CompilerPairOnly @@ -638,6 +646,8 @@ fn fleet_converge_mode_scope(mode: FleetConvergeWorkflowMode) -> FleetConvergeSc R2ObjectWriteMint => none R2WorkspaceObjectReadMint => none R2WorkspaceObjectWriteMint => none + R2CacheObjectReadMint => none + R2CacheObjectWriteMint => none R2ConditionalPutRaceProbe => none WorkspaceSourcePack => none WorkspaceCheckpointMeasure => none @@ -739,6 +749,8 @@ fn fleet_converge_mode_fleet_ssh_key_demand(mode: FleetConvergeWorkflowMode) -> R2BucketAdminMint => FleetSshKeyNotConsumed R2WorkspaceObjectReadMint => FleetSshKeyNotConsumed R2WorkspaceObjectWriteMint => FleetSshKeyNotConsumed + R2CacheObjectReadMint => FleetSshKeyNotConsumed + R2CacheObjectWriteMint => FleetSshKeyNotConsumed R2ConditionalPutRaceProbe => FleetSshKeyNotConsumed WorkspaceSourcePack => FleetSshKeyNotConsumed WorkspaceCheckpointMeasure => FleetSshKeyNotConsumed @@ -1458,6 +1470,8 @@ fn fleet_converge_mode_mutation_domain(mode: FleetConvergeWorkflowMode) -> Fleet R2ObjectWriteMint => ExecutorDomain R2WorkspaceObjectReadMint => ExecutorDomain R2WorkspaceObjectWriteMint => ExecutorDomain + R2CacheObjectReadMint => ExecutorDomain + R2CacheObjectWriteMint => ExecutorDomain R2ConditionalPutRaceProbe => SubjectDomain { group: "r2-conditional-put-race-probe" } WorkspaceSourcePack => ExecutorDomain WorkspaceCheckpointMeasure => ExecutorDomain @@ -2282,6 +2296,8 @@ data fleet_converge_r2_bucket_admin_mint_step_if: String = fleet_converge_mode_s data fleet_converge_r2_object_write_mint_step_if: String = fleet_converge_mode_step_if(mode: R2ObjectWriteMint) data fleet_converge_r2_workspace_object_read_mint_step_if: String = fleet_converge_mode_step_if(mode: R2WorkspaceObjectReadMint) data fleet_converge_r2_workspace_object_write_mint_step_if: String = fleet_converge_mode_step_if(mode: R2WorkspaceObjectWriteMint) +data fleet_converge_r2_cache_object_read_mint_step_if: String = fleet_converge_mode_step_if(mode: R2CacheObjectReadMint) +data fleet_converge_r2_cache_object_write_mint_step_if: String = fleet_converge_mode_step_if(mode: R2CacheObjectWriteMint) data fleet_converge_r2_conditional_put_race_probe_step_if: String = fleet_converge_mode_step_if(mode: R2ConditionalPutRaceProbe) // ── THE APP-KEY VERSION VERIFIER ───────────────────────────────────────────────────────────── @@ -3233,6 +3249,80 @@ fn fleet_converge_r2_workspace_object_write_mint_receipt_upload_step() -> Step { } } +fn fleet_converge_r2_cache_object_read_mint_step() -> Step { + RunStep { + name: Present { value: "R2 cache-blobs object-read token mint (AccountTokens.Create + Secret Manager custody)" }, + id: Present { value: "r2_cache_object_read_mint" }, + run: gunbc_ci_r2_cache_object_read_mint_invoke(), + shell: none, + env: Present { value: [ + kv(key: "WIF_ACCESS_TOKEN", value: yaml_string(s: "${{ steps.wif_auth.outputs.access_token }}")), + ] }, + working_directory: none, + if_condition: Present { value: fleet_converge_r2_cache_object_read_mint_step_if }, + continue_on_error: none, + timeout_minutes: Present { value: gunbc_ci_aux_step_timeout_minutes } + } +} + +fn fleet_converge_r2_cache_object_read_mint_receipt_upload_step() -> Step { + UsesStep { + name: Present { value: "Upload R2 cache-blobs object-read mint receipt (token id + custody version resource; no secret)" }, + id: Present { value: "r2_cache_object_read_mint_receipt_upload" }, + uses: upload_artifact_action, + with: Present { value: [ + kv(key: "name", value: yaml_string(s: "r2-cache-object-read-mint-receipt")), + kv(key: "path", value: yaml_string(s: r2_mint_receipt_glob_in( + scope: fleet_converge_r2_mint_receipt_scope(), + profile: R2OriginObjectRead, + ))), + kv(key: "if-no-files-found", value: yaml_string(s: "warn")), + kv(key: "retention-days", value: yaml_int(n: 30)), + ] }, + env: none, + if_condition: Present { value: join(["always() && ", fleet_converge_r2_cache_object_read_mint_step_if], "") }, + continue_on_error: none, + timeout_minutes: Present { value: gunbc_ci_artifact_transfer_step_timeout_minutes } + } +} + +fn fleet_converge_r2_cache_object_write_mint_step() -> Step { + RunStep { + name: Present { value: "R2 cache-blobs object-write token mint (AccountTokens.Create + Secret Manager custody)" }, + id: Present { value: "r2_cache_object_write_mint" }, + run: gunbc_ci_r2_cache_object_write_mint_invoke(), + shell: none, + env: Present { value: [ + kv(key: "WIF_ACCESS_TOKEN", value: yaml_string(s: "${{ steps.wif_auth.outputs.access_token }}")), + ] }, + working_directory: none, + if_condition: Present { value: fleet_converge_r2_cache_object_write_mint_step_if }, + continue_on_error: none, + timeout_minutes: Present { value: gunbc_ci_aux_step_timeout_minutes } + } +} + +fn fleet_converge_r2_cache_object_write_mint_receipt_upload_step() -> Step { + UsesStep { + name: Present { value: "Upload R2 cache-blobs object-write mint receipt (token id + custody version resource; no secret)" }, + id: Present { value: "r2_cache_object_write_mint_receipt_upload" }, + uses: upload_artifact_action, + with: Present { value: [ + kv(key: "name", value: yaml_string(s: "r2-cache-object-write-mint-receipt")), + kv(key: "path", value: yaml_string(s: r2_mint_receipt_glob_in( + scope: fleet_converge_r2_mint_receipt_scope(), + profile: R2OriginObjectWrite, + ))), + kv(key: "if-no-files-found", value: yaml_string(s: "warn")), + kv(key: "retention-days", value: yaml_int(n: 30)), + ] }, + env: none, + if_condition: Present { value: join(["always() && ", fleet_converge_r2_cache_object_write_mint_step_if], "") }, + continue_on_error: none, + timeout_minutes: Present { value: gunbc_ci_artifact_transfer_step_timeout_minutes } + } +} + // THE R2 IF-MATCH RACE PROBE (gunbc.cloudflare.r2_conditional_put_race_probe). It touches no host: it // writes and deletes one probe key in the workspace bucket with that bucket's pinned write credential. // The dispatch is the operator's approval; the receipt names every round's statuses and the verdict, @@ -4384,6 +4474,10 @@ fn fleet_converge_job() -> Job { fleet_converge_r2_workspace_object_read_mint_receipt_upload_step(), fleet_converge_r2_workspace_object_write_mint_step(), fleet_converge_r2_workspace_object_write_mint_receipt_upload_step(), + fleet_converge_r2_cache_object_read_mint_step(), + fleet_converge_r2_cache_object_read_mint_receipt_upload_step(), + fleet_converge_r2_cache_object_write_mint_step(), + fleet_converge_r2_cache_object_write_mint_receipt_upload_step(), fleet_converge_r2_conditional_put_race_probe_step(), fleet_converge_r2_conditional_put_race_probe_receipt_upload_step(), fleet_converge_workspace_source_pack_step(), @@ -4648,6 +4742,8 @@ fn fleet_converge_mode_job_id(mode: FleetConvergeWorkflowMode) -> NonEmptyStr { R2BucketAdminMint => fleet_converge_shared_job_id R2WorkspaceObjectReadMint => fleet_converge_shared_job_id R2WorkspaceObjectWriteMint => fleet_converge_shared_job_id + R2CacheObjectReadMint => fleet_converge_shared_job_id + R2CacheObjectWriteMint => fleet_converge_shared_job_id R2ConditionalPutRaceProbe => fleet_converge_shared_job_id WorkspaceSourcePack => fleet_converge_shared_job_id WorkspaceCheckpointMeasure => fleet_converge_shared_job_id diff --git a/dag/test/claim/r2_mint_secret_access_witness_test.dag b/dag/test/claim/r2_mint_secret_access_witness_test.dag index dfd2ceba3d3..48b32103538 100644 --- a/dag/test/claim/r2_mint_secret_access_witness_test.dag +++ b/dag/test/claim/r2_mint_secret_access_witness_test.dag @@ -25,11 +25,13 @@ import gunbc.cloudflare.r2_mint_secret_access { r2_mint_write_version_add_grant, r2_mint_write_readback_grant, r2_bucket_admin_container_read_grant, r2_bucket_admin_version_add_grant, r2_bucket_admin_read_grant, r2_workspace_container_read_grant_for, r2_workspace_version_add_grant_for, r2_workspace_read_grant_for, + r2_mint_custody_grants, } import gunbc.cloudflare.r2_origin { cloudflare_bootstrap_custody_secret_id, fabric_durable_origin_write_secret_id, cloudflare_r2_bucket_admin_secret_id, fabric_workspace_read_secret_id, fabric_workspace_write_secret_id, + fabric_cache_blobs_read_secret_id, fabric_cache_blobs_write_secret_id, } import std.types { String, NonEmptyStr } import gunbc.gcp_estate_observation { fleet_cloud_convergence_sa_email } @@ -174,3 +176,14 @@ test fn each_workspace_secret_gets_container_read_version_add_and_read_on_its_ow && (r2_workspace_read_grant_for(secret_id: fabric_workspace_read_secret_id).target.secret as String) != (cloudflare_bootstrap_custody_secret_id as String) && (r2_workspace_read_grant_for(secret_id: fabric_workspace_write_secret_id).target.secret as String) != (fabric_durable_origin_write_secret_id as String) } + +// THE CACHE-BLOBS CONTAINERS GET THE SAME THREE UNCONDITIONED CELLS EACH, AND THE ROSTER CARRIES ALL SIX +// (the bootstrap derives its containers from the roster, so a missing row is a container never created). +test fn each_cache_blobs_secret_gets_three_cells_on_its_own_container_and_the_roster_carries_them() -> Bool { + let targets = map(r2_mint_custody_grants(), g => g.target.secret as String) + workspace_cells_hold(id: fabric_cache_blobs_read_secret_id) + && workspace_cells_hold(id: fabric_cache_blobs_write_secret_id) + && (fabric_cache_blobs_read_secret_id as String) != (fabric_cache_blobs_write_secret_id as String) + && count(filter(targets, t => t == (fabric_cache_blobs_read_secret_id as String))) == 3 + && count(filter(targets, t => t == (fabric_cache_blobs_write_secret_id as String))) == 3 +}