feat: dark-orchestrator provisioning foundation + live Yandex Cloud adapter #1

Merged
arento merged 10 commits from feat/provisioning-yc into main 2026-07-01 09:28:42 +00:00
Owner

Establishes dark-orchestrator's provisioning control plane (hexagonal ports & adapters) and the tenant-VM lifecycle up through credentials init — validated end to end against real Yandex Cloud.

What's in it

  • Architecture — domain-inward ports as traits (CloudProvider, SecretStore, DarkAgent); Orchestrator depends only on them via Arc<dyn …>; adapters at the edges; main is the only wiring point. Minimal axum API surface holds only a control handle.
  • Provisioning (NKS #749/#757)CloudProvider port + in-memory mock + a real Yandex Cloud REST adapter (YcCloudProvider): IAM-token JWT (PS256), create → poll operation → read address, bounded health wait, idempotent delete. Config from env or a key.json (YC_SA_KEY_FILE); public IP optional (YC_PUBLIC_IP).
  • Init dance (NKS #750)DarkAgent credentials port + mock; the control loop runs first-deploy init → claude → export → vault vs respawn import → claude.
  • Vault (#758)SecretStore port + mock; secret-bearing types redact under Debug.
  • Config & ops — verstak AGENTS.md + session hooks, Gitea Actions CI (fmt + clippy pedantic + test), .env/dotenvy, docs/yc-provisioning.md.

Verification

  • Gate green: cargo fmt --check, cargo clippy --all-targets -- -D warnings -W clippy::pedantic, cargo test (unit).
  • Live: the yc_live integration test creates and destroys a real YC VM (private-only) — passing.
  • Passed a craft Rust review (Approve).

Accepted (sandbox) / follow-ups

  • Provider/vault/dark-agent mocks where a real backend isn't wired yet; real dark-agent HTTP adapter pending.
  • Durable bundle is stage-1 plaintext (orchestrator-side encryption mandated later — #628); dark-agent API unauthenticated (#632); tenant-secret onboarding stubbed (#740); public IP needs the SA vpc.publicAdmin role / tenant network model (#735); image publish to YC (#588); transactional rollback-teardown on provision failure (#767).
Establishes dark-orchestrator's provisioning control plane (hexagonal ports & adapters) and the tenant-VM lifecycle up through credentials init — validated end to end against real Yandex Cloud. ## What's in it - **Architecture** — domain-inward ports as traits (`CloudProvider`, `SecretStore`, `DarkAgent`); `Orchestrator` depends only on them via `Arc<dyn …>`; adapters at the edges; `main` is the only wiring point. Minimal axum API surface holds only a control handle. - **Provisioning (NKS #749/#757)** — `CloudProvider` port + in-memory mock + a **real Yandex Cloud REST adapter** (`YcCloudProvider`): IAM-token JWT (PS256), create → poll operation → read address, bounded health wait, idempotent delete. Config from env or a `key.json` (`YC_SA_KEY_FILE`); public IP optional (`YC_PUBLIC_IP`). - **Init dance (NKS #750)** — `DarkAgent` credentials port + mock; the control loop runs first-deploy `init → claude → export → vault` vs respawn `import → claude`. - **Vault (#758)** — `SecretStore` port + mock; secret-bearing types redact under `Debug`. - **Config & ops** — verstak `AGENTS.md` + session hooks, Gitea Actions CI (fmt + clippy pedantic + test), `.env`/dotenvy, `docs/yc-provisioning.md`. ## Verification - Gate green: `cargo fmt --check`, `cargo clippy --all-targets -- -D warnings -W clippy::pedantic`, `cargo test` (unit). - **Live**: the `yc_live` integration test creates and destroys a real YC VM (private-only) — passing. - Passed a craft Rust review (Approve). ## Accepted (sandbox) / follow-ups - Provider/vault/dark-agent mocks where a real backend isn't wired yet; real dark-agent HTTP adapter pending. - Durable bundle is stage-1 plaintext (orchestrator-side encryption mandated later — #628); dark-agent API unauthenticated (#632); tenant-secret onboarding stubbed (#740); public IP needs the SA `vpc.publicAdmin` role / tenant network model (#735); image publish to YC (#588); transactional rollback-teardown on provision failure (#767).
AGENTS.md (verstak standard) + CLAUDE.md symlink; committed .claude/settings.json (SessionStart/post-push hooks + permissions allow-list); standard Rust .gitignore; Gitea Actions CI running fmt + clippy pedantic + test.
feat: provisioning control plane with Yandex Cloud adapter
Some checks failed
ci / gate (push) Has been cancelled
b9c9ffa316
Hexagonal ports/adapters: CloudProvider and SecretStore traits, Orchestrator over Arc<dyn>, minimal axum API surface. Adapters: in-memory mock + Yandex Cloud REST (JWT/IAM token, create -> poll operation -> get NAT IP, bounded health wait, idempotent delete). Reference: docs/yc-provisioning.md.
feat: select provider from env and add live YC integration test
Some checks failed
ci / gate (push) Has been cancelled
9bf90a1121
YcConfig::from_env (12-factor; private key from a file path, never an env var or log). main picks mock vs Yandex Cloud via DARK_PROVIDER. tests/yc_live.rs exercises create->destroy against real YC, #[ignore]'d (opt-in, needs creds). Env vars documented in docs/yc-provisioning.md.
refactor: split VM shape out of YcConfig into domain VmShape
Some checks failed
ci / gate (push) Has been cancelled
06b4242761
Vm size (cores/memory/disk) is provider-agnostic, so it moves into a domain VmShape carried by InstanceSpec; YcConfig keeps only Yandex placement + auth + operational knobs. Orchestrator holds the VmShape (VmShape::default() for now). Adapters translate VmShape into provider terms.
chore: load .env via dotenvy and add .env.example template
Some checks failed
ci / gate (push) Has been cancelled
ef9c466bfc
main loads a local .env on startup (dev convenience; real envs set vars directly). .env.example documents every var with placeholders; .env, .env.*, *.pem and /secrets/ are gitignored so credentials never reach the repo.
feat: load YC auth from a key.json via YC_SA_KEY_FILE
Some checks failed
ci / gate (push) Has been cancelled
19a90381c4
YcConfig::from_env prefers YC_SA_KEY_FILE (a whole 'yc iam key create' JSON: service_account_id, id->key_id, private_key PEM) over the three separate vars. The PEM is taken from the first -----BEGIN marker so YC's preamble line is tolerated. Validated live: IAM token issued against real YC.
New dark_agent port (init/claude_login/export_bundle/import_bundle) + in-memory mock. Orchestrator runs the credentials dance after provisioning: first deploy = init -> claude -> export -> seal -> vault; respawn/fork = import -> claude. Vault read errors no longer fall through to overwrite the durable bundle.
fix: craft review fixes, optional YC public IP (#735), mod.rs module layout
Some checks failed
ci / gate (push) Has been cancelled
ci / gate (pull_request) Has been cancelled
0c172f1e6c
Redacting Debug on secret-bearing InstanceSpec/DurableBundle; API returns a generic error and logs detail server-side (#632); YC create errors on a missing operation id and refreshes the IAM token a minute early; fresh per-call idempotency keys (uuid v4). YC public IP is optional via YC_PUBLIC_IP (a public NAT needs the SA vpc.publicAdmin role; private-only otherwise, validated live). Module layout standardized to single-file or foo/mod.rs (AGENTS.md); cloud_provider and dark_agent migrated.
ci: run Forgejo Actions on the factory runner with dark-agent-style lints
All checks were successful
ci / test (push) Successful in 1m17s
ci / test (pull_request) Successful in 37s
415c73a164
Forgejo reads .forgejo/workflows (not .gitea), so the workflow moves there and runs on the shared factory-ci-rust:1.94.1 image — same as dark-agent: cargo fmt --check, clippy --all-targets -D warnings, test --all-targets. Lint levels move into Cargo [lints] (clippy all=warn, unused_must_use=deny) instead of a CLI pedantic flag. Pin the toolchain to 1.94.1 (rust-toolchain.toml) to match the image and drop Duration::from_mins (1.95-only) accordingly. AGENTS.md updated; old .gitea workflow removed.
Reviewed-on: #2
arento merged commit f5efa6d439 into main 2026-07-01 09:28:42 +00:00
arento deleted branch feat/provisioning-yc 2026-07-01 09:28:42 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
projects/dark-orchestrator!1
No description provided.