Phase 7 — VM allocation + PoolAssignmentProvisioner #8
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Tracks the Phase 7 work in hero_onboarding#1 — the first product the user can actually buy with their accumulated credit balance: a VM allocated from a pre-provisioned operator-managed pool.
Phase 6 / Phase 7 naming note: the meta-issue §Phase 6 bundles three things — Idenfy KYC integration +
PoolAssignmentProvisioner+ production keys. In practice this split across two sessions:360a942, D-15)PoolAssignmentProvisioner+ VM allocation, this issue (s2-009)The "production keys" half stays deferred until both sandbox loops are operator-validated end-to-end.
Scope (v0)
A single in-house
PoolAssignmentProvisionerimpl behind aProvisionerasync-trait. v1 (TfchainAutoDeployProvisioner) is a deliberate scope cut from this session — it depends on hero_compute#116 gap closure (Mahmoud'swait_vm_ready,vm_execstreaming clarification, metadata field, deployer auth model) and on Kristof'shero_os_tfgrid_deployerarc landing first. The v0 ships the trait surface + an impl that operates against an operator-pre-provisioned pool, so v1 slots in as a pure additional impl without touching the trait or the gate logic.Locked trait shape
Two deliberate deviations from the meta-issue §Provisioning sketch:
user_sid: &str, not&User. Mirrors the two existing provider traits (PaymentProvider::create_top_up(user_sid, ...)incrates/hero_onboarding_server/src/payment.rs#L92,KycProvider::start_session(user_sid, ...)incrates/hero_onboarding_server/src/kyc.rs#L108). Keeps the trait file decoupled from the schema crate; if an impl needs richer user data it fetches from OSIS.status()included now, not deferred. v0 PoolAssignment status is a trivial OSIS read, but v1 Tfchainallocate()returns early and needs polling — pre-baking the surface means v1 is a pure impl-side change.Decision lock + revisability notes will be captured in D-17 at session close.
v0
PoolAssignmentProvisionerReads the operator's pre-provisioned VM pool from a JSON source. Two modes:
VM_POOL_JSONhero_proc secret (contextonboarding) containingVec<PoolVm { vm_id, ssh_address, mycelium_address?, status }>. Operator pre-populates; assignments mutate via OSIS (the JSON source remains the operator's truth for VM identity, OSIS is the assignment ledger).PROVISIONER_DEMO=trueloads an in-memory 3-row pool — same escape-hatch pattern asIDENFY_DEV_MODE=true/DEMO_KYC=true(s2-008). Keepsscripts/smoke_vm_allocate.shself-contained.Allocation algorithm: linear scan for first
status=availablerow, markassigned, persistVmAllocationrow in OSIS, return.New routes (server)
/vm/allocateBilling.credit_balance_cents >= plan.cost_cents→ 402 if not) →provisioner.allocate()→billing.credit_balance_cents = saturating_sub(cost_cents)in same OSIS write. Returns 303 →/dashboard./vm/release/{sid}/vm/list/admin/list-allocations/admin/list-kyc-sessions)./admin/release/{sid}Credit deduction (load-bearing): this is the FIRST request-time decrement of
Billing.credit_balance_cents. Until now the balance has only been incremented via Stripe/ClickPesa webhooks (s2-004/5) and decremented via the aggregator's batched cron-time saturating_sub (s2-007usage_aggregate.rs). Phase 7 is the first user-action-time decrement — smoke must exercise the live decrement path (the s2-007 deferred Stripe-sandbox-seed assertion lands here).Schema additions
New
crates/hero_onboarding_schema/schemas/onboarding/vm_allocation.oschema:AllocationStatusenum:requested | active | suspended | releasedVmAllocationrootobject:sid+user_sid @index+provisioner_kind+status: AllocationStatus+assigned_pool_vm_id?+tfchain_contract_id?(v1 placeholder) +cost_cents+ssh_pub_key?+expires_at: u64+created_at: u64User schema unchanged. 7 trigger stubs (
*_trigger_new_post/get_pre/get_post/list_pre/save_pre/save_post/delete_pre) added inrpc.rsper s2-007 Phase B finding 1. Avoidupdated_at: otime/created_at: otimefield names per s2-007 Phase B finding 2 + s2-008 Phase B finding 2 collision rule.Dashboard surface
Flip the placeholder "Active services" card into a real per-allocation table with
Allocate VMbutton whencredit_balance_cents >= default_plan.cost_cents. Side-effects insufficient-credit →/dashboard?error=insufficient_creditand KYC-required →/kyc/start?reason=vm_allocateredirect cycles.Admin surface
5th view in
hero_onboarding_admin(nav: Overview / Users / Payments / Aggregator / KYC / Allocations). Per-allocation table reading via reqwest from server's/admin/list-allocations(no second OSIS handle on admin process per the s2-007 architectural rule). Manual force-release button.Acceptance
Mirrors prior phases:
cargo checkworkspace +cargo test(expect ~45/45 unit tests = 39 carry-over + ~5 provisioner + 1 schema CRUD auto for VmAllocation) +lab build --release --install --workspaceVICTORY 3/3 +lab infocheck3/3 clean +cargo fmt --check+cargo clippy --workspace --all-targets -- -D warningsclean +scripts/smoke_vm_allocate.sh~25/25 GREEN againstPROVISIONER_DEMO=true+IDENFY_DEV_MODE=true.Smoke checks: allocate-happy / insufficient-credit-402 / KYC-required-303 / pool-exhausted-503 / double-allocate-blocked / release-then-re-allocate / admin-list-allocations / admin-force-release / live-balance-decrement (Stripe-sandbox webhook self-sign seeds the balance — addresses the s2-007 deferred live-balance-decrement assertion).
Open questions surfaced to Kristof/Emre
release()does NOT credit cost back to balance — this is a deliberate Phase 7 scoping choice; refund logic lives in a future ops-tooling phase. Smoke documents this behavior.Cross-references
payment_intent_post)Edit 2026-05-21 (post-Track-A-s135 race resolution): All references to D-16 in this issue body have been re-numbered to D-17. Track A's s135 minted D-16 (cockpit-byok-user-forge-token-namespacing) 40 minutes before this issue was filed; per CLAUDE.md ID-NN race rule (first-minted wins), Track A keeps D-16 and this trait-shape decision lock is now D-17. The decision file is decisions/D-17-provisioner-trait-shape.md in the workspace (the file was renamed by Track A at /stop after the squash-merge race was resolved).