Expand description
@yah:relay(R040, “yah-cloud Phase 1: mirror bootstrap (Hetzner driver, Podman runtime, yah-yubaba)”) @yah:status(review) @yah:parent(Q058) @yah:next(“yah-A track (8 tickets) — substrate for noisetable cloud + future managed camps; gates noisetable C6 (E2E) and D1–D5 (corpus migration)”) @yah:next(“Adjacent relays: R019 (SSH-RPC remote camps), R032 (yah-agentd), R034 (identity registry — hostkeys overlap)”) @yah:next(“Reparented under Q058 cloud quest 2026-05-07. Largely superseded by R091 (yubaba orchestration + integration testing) and R092 (managed-camps CLI + .yah/cloud/ schema) — net new work should land on those relays. Remaining unique R040 scope (Cloudflare Tunnel cookbook F15, pg-on-mesh recipe F16, mesh-promote phasing F18-F19) stays here as historical/operational reference; consider archiving once R091+R092 reach review.”) @yah:next(“Refinement open questions: subcommand framework alignment with yah arch/board, where hostkey fingerprints live, reverse proxy choice. Crate-layout question soft-answered by this stub being a sibling crate; A1 may still refactor into a sub-module of an existing crate if desired (move the //! block + delete this stub).”) @arch:see(architecture/PHASE_1_MIRROR_BOOTSTRAP.md) @arch:see(architecture/yah-managed-camps-topology.md)
@yah:ticket(R040-F1, “A1: .yah/cloud/ schema + parser (MachineConfig, MirrorConfig, ServiceConfig)”)
@yah:at(2026-05-05T00:29:11Z)
@yah:assignee(agent:claude)
@yah:status(review)
@yah:phase(P1)
@yah:parent(R040)
@yah:next(“Per-file TOML layout: machines/
@yah:ticket(R040-F3, “A3: Hetzner driver — MachineProvider trait + hcloud impl (server, bucket, status, destroy)”) @yah:at(2026-05-05T00:29:11Z) @yah:assignee(agent:claude) @yah:status(review) @yah:phase(P1) @yah:parent(R040) @yah:next(“API token from HETZNER_API_TOKEN env; document in .yah/cloud/SECRETS.md”) @yah:next(“Gotcha: Hetzner has TWO APIs (Cloud and Robot) — Phase 1 is Cloud only; Object Storage is S3-compat, separate surface”) @yah:next(“Verify before A6: Hetzner Hillsboro + Ashburn Object Storage both GA; CPX-22 available in PDX/IAD/FSN”) @yah:verify(“cargo test -p cloud provider::hetzner – –ignored (gated; needs token)”) @arch:see(architecture/PHASE_1_MIRROR_BOOTSTRAP.md) @yah:handoff(“MachineProvider trait + HetznerDriver impl complete in crates/yah/cloud/src/provider/. Cloud API (create_server, server_status, destroy_server) uses HETZNER_API_TOKEN via reqwest+bearer. create_bucket uses Hetzner Object Storage S3-compat with AWS Sig V4 signing (HETZNER_S3_ACCESS_KEY + HETZNER_S3_SECRET_KEY). Location enum maps pdx/iad/fsn to hetzner cloud IDs and S3 endpoints. 9 unit tests pass; 2 integration tests behind #[ignore] gate. .yah/cloud/SECRETS.md created documenting all three token types.”) @yah:next(“A2: wire yah cloud subcommand stub into CLI (yah board/arch pattern). All commands no-op except machine status + mirror show which dump parsed .yah/cloud/ config. Verify: yah cloud mirror show noisetable prints declared regions/services.”)
@yah:ticket(R040-F4, “A4: cloud-init YAML + yah cloud machine provision (Debian + Podman + Tailscale + yah-yubaba)”)
@yah:at(2026-05-05T00:29:11Z)
@yah:status(review)
@yah:phase(P1)
@yah:parent(R040)
@yah:next(“Template at .yah/cloud/cloud-init/mirror.yml; renderer substitutes YAH_WARDEN_BASE64 + HEADSCALE_PREAUTH_KEY + TAGS per machine”)
@yah:next(“Hostkey generated ON the machine (ssh-keygen in cloud-init); private half never touches dev machines”)
@yah:next(“Provision command polls cloud-init done, reads back hostkey fingerprint via yah-yubaba /identity, writes back to machine.toml”)
@yah:next(“Surface /var/log/cloud-init.log and /var/log/cloud-init-output.log on failure”)
@yah:verify(“yah cloud machine provision noisetable-pdx-1 –dry-run prints rendered cloud-init YAML”)
@arch:see(architecture/PHASE_1_MIRROR_BOOTSTRAP.md)
@yah:handoff(“Cloud-init template + renderer + provision command landed. Template at .yah/cloud/cloud-init/mirror.yml (canonical) with embedded copy at crates/yah/cloud/templates/mirror.yml (drift-test in cloud_init::tests::embedded_template_matches_workspace_canonical). Renderer at crates/yah/cloud/src/cloud_init.rs substitutes {{KEY}} (no-spaces) for MACHINE_NAME, YAH_WARDEN_BASE64, HEADSCALE_PREAUTH_KEY, TAGS; doc comments use {{ KEY }} (with spaces) so they survive rendering. find_unsubstituted() trips on any unsubstituted real placeholder. Provision orchestrator at crates/yah/cloud/src/provision.rs decoupled from concrete provider (takes &dyn MachineProvider). CLI wired in app/yah/cli/src/cloud.rs: yah cloud machine provision
@yah:ticket(R040-T5, “A5: yah cloud machine status — drift report (declared vs Hetzner vs agent health)”)
@yah:at(2026-05-05T00:29:11Z)
@yah:status(review)
@yah:phase(P2)
@yah:parent(R040)
@yah:next(“Drift categories: missing-server, wrong-machine-type, missing-bucket, hostkey-fingerprint-mismatch, missing-co-tenant, agent-unreachable, service-unhealthy”)
@yah:next(“Exit non-zero on drift unless –quiet; bare ‘yah cloud machine status’ dumps all declared machines”)
@yah:next(“Can land any time after A4 (parallel-safe with A6/A7/A8)”)
@yah:verify(“Pre-A6: reports ‘not provisioned’ for each declared machine”)
@yah:verify(“Post-A6+A7+A8: all three report ‘in sync’”)
@arch:see(architecture/PHASE_1_MIRROR_BOOTSTRAP.md)
@yah:handoff(“Drift module landed at crates/yah/cloud/src/status.rs (DriftFinding + MachineReport + collect_machine_report). MachineProvider trait gained find_server_by_name (GET /servers?name=) + bucket_exists (S3 HEAD) — Hetzner impls share a refactored sign_s3_empty_body helper for PUT/HEAD signing. CLI: yah cloud machine status [name] [--quiet] queries Hetzner, prints declared+live state with per-finding [drift]/[note ] tags, and bails non-zero on real drift unless –quiet. Soft findings (missing creds, A8-not-landed, bucket-uncheckable) are surfaced as notes only. Pre-A6 verify: smoke-tested against this workspace’s noisetable-pdx-1 declaration with no token — reports ‘unknown’ + provider-error note, exit 0. 7 new drift unit tests pass via in-memory FakeProvider; 27 total pass in crates/yah/cloud.”)
@yah:next(“A8 will plug in real agent-ping (yah-yubaba /health + /identity). The HostkeyFingerprintMismatch + AgentUnreachable variants are wired through the report rendering — A8 just needs to flip the AgentUnreachable emit-site in collect_machine_report() from the always-emit pre-A8 stub to a live probe.”)
@yah:ticket(R040-T6, “A6: provision noisetable-{pdx,iad,fsn}-1 (operational; yah dogfoods on noisetable’s mirror, no yah-cloud SaaS)”)
@yah:at(2026-05-05T00:29:11Z)
@yah:status(review)
@yah:phase(P2)
@yah:parent(R040)
@yah:verify(“yah cloud machine status –path /Users/user/ss/noisetable — all three ‘in sync’”)
@yah:verify(“aws s3 ls –endpoint-url yah cloud machine status --path /Users/user/ss/noisetable → all three ‘in sync’) which is gated on the release-tag + provision steps.”)
@yah:ticket(R040-F7, “A7: yah cloud service deploy — Podman compose generation + Cloudflare front + tier isolation”)
@yah:at(2026-05-05T00:29:11Z)
@yah:assignee(agent:claude)
@yah:status(review)
@yah:phase(P3)
@yah:parent(R040)
@yah:next(“Resolves service configs + mirror declarations → per-machine compose.yml → push via yah-yubaba (A8) → systemctl restart yah-cloud-services”)
@yah:next(“Tier isolation at SOFTWARE layer: separate PG roles per service set, distinct Headscale tags (tier:t0 yah meta vs tier:t2 noisetable assets), mesh_only flag controls Cloudflare exposure”)
@yah:next(“Cloudflare DNS records (manual, in SECRETS.md): pdx.cloud.noisetable.example etc. → public IPs via orange-cloud”)
@yah:next(“Gotcha: Cloudflare free tier proxies HTTP/HTTPS but NOT raw TCP/UDP — Headscale mesh uses direct WireGuard between machines”)
@yah:next(“Assumes: Cloudflare account exists + noisetable.example DNS delegated to it (else spawns Cloudflare-bootstrap sub-ticket)”)
@yah:next(“Open question (refinement): Caddy vs nginx vs direct-to-Cloudflare for per-machine reverse proxy”)
@yah:verify(“curl https://pdx.cloud.noisetable.example/healthz (asset-registry) returns 200”)
@yah:verify(“curl https://pdx.cloud.yah.example/healthz (yah meta-directory) returns 200”)
@yah:verify(“yah cloud service status –all — all green on all 3 machines”)
@arch:see(architecture/PHASE_1_MIRROR_BOOTSTRAP.md)
@yah:handoff(“Compose generation + yubaba service deploy + CLI wiring landed. New file: crates/yah/cloud/src/compose.rs (generate_compose_bundle: tier-named networks, Caddy service for mesh_only:false, Caddyfile with hostname when cloud_domain is set in mirrors/*.toml). MirrorConfig gained cloud_domain: Option
@yah:ticket(R040-F8, “A8: yah-yubaba (Rust binary on machine) + yah-cloud-client + desktop Cloud panel”)
@yah:at(2026-05-05T00:29:11Z)
@yah:status(review)
@yah:phase(P3)
@yah:parent(R040)
@yah:verify(“cargo test -p yubaba -p cloud -p cloud-client + cargo test -p yah –bin yah cloud:: — 18 + 47 + 9 + 17 tests pass”)
@yah:verify(“cargo run -p yubaba – serve –bind 127.0.0.1:7450 –state /tmp/i.json then curl POST /register-hostkey with a real ssh-ed25519 pubkey, then yah cloud machine attach <machine> --host 127.0.0.1:7450 --wait 5 --path <camp> prints attached: <machine> (fingerprint recorded: SHA256:…) and writes the line into .yah/cloud/machines/yah cloud machine attach <name> (the hostkey write-back follow-on). app/yah/cli/src/cloud.rs::handle_attach loads MachineConfig, polls cloud-client GET /identity until success or –wait seconds elapse (default 180s, retry every 3s on NotRegistered or Transport errors), then calls MachineConfig::save() with the fingerprint. Decision logic in decide_attach_action covers all four states: Write (no fingerprint on file), Idempotent (match — no-op write), Mismatch (bail with –force hint), Overwrite (mismatch + –force). Subcommand args harmonized with agent ping: –host (default http://next: hostkey fingerprint write-back lands with A8) replaced with next: once cloud-init finishes, run yah cloud machine attach <name>. End-to-end smoke verified all four AttachAction paths against a live yubaba on 127.0.0.1:7450 — pre-registration polling + bail, fresh-write, idempotent re-run, mismatch refusal, –force overwrite. 5 new unit tests (decide_attach_action_{writes_when_no_declared,idempotent_on_match,bails_on_mismatch_without_force,overwrites_with_force,force_is_idempotent_when_match}) — total 9 cloud:: tests in the yah bin. Cumulative R040-F8 deliverables across sessions 1–3: yubaba binary (12 tests), cloud-client crate (6 tests + doctest), AgentProbe trait + drift wire-up (4 new in cloud, 29 total), yah cloud agent {ping,services,logs} CLI, yah cloud machine attach, provision next-step hint correctly points at attach. Out of scope still: Tailscale-mesh discovery, desktop Cloud panel, mTLS hardening.”)
@yah:next(“Tailscale-mesh discovery for –host: yah cloud agent ping/services/attach defaults to http://tailscale status --json for the machine’s mesh IP. Until then, operators pass –host explicitly. Add a TS_DEVICES env shortcut or yah cloud agent ip <machine> helper if the manual path stays painful.”)
@yah:next(“Desktop Cloud panel (out of session, larger): packages/yah/ui/src/cloud/ — machines list, per-machine service list, log tail. Tauri command bridges in app/yah/desktop/src/. Pulls on the cloud-client crate (already in workspace). Defer until at least Hostkey write-back lands so the panel has something useful to display per-machine.”)
@yah:next(“Hardening parking lot (post-Phase-1): mTLS (server cert from machine hostkey, client cert from desktop user identity); /metrics endpoint (Prometheus); streaming /logs from journald with heartbeat. None of these gate Phase 1 verify; cloud-client’s CloudClient::with_timeout exists so log streaming has a hook.”)
@yah:gotcha(“yubaba binds 0.0.0.0:7443 by default; cloud-init template adds ufw rules to restrict to tailscale0. If a future deployment skips ufw, public IP exposure is a real risk. Better long-term: bind to tailscale0 IP directly (cloud-init systemd unit can compute it via ip -4 addr show tailscale0).”)
@yah:gotcha(“Re-running register-hostkey with a different pubkey replaces the stored identity (idempotent for same key, swaps for different). Probably what you want, but worth flagging if someone designs an audit log around ‘first registration wins’.”)
@yah:gotcha(“Default URL resolution (http://<machine.name>:7443) is shared between WardenHealthProbe (status drift), agent ping/services, and machine attach — outside Tailscale mesh every default-host call against a real Hetzner machine will fail. Status emits AgentUnreachable as a soft note; agent/attach surface the transport error directly. Once mesh discovery lands, all three call sites should resolve via Tailscale before falling back to plain hostname.”)
@yah:ticket(R040-F18, “Phase 1a — yah mesh start: camp bootstrap with stable URL from day 1”)
@yah:at(2026-05-05T00:29:11Z)
@yah:status(review)
@yah:assignee(agent:claude)
@yah:parent(R040)
@yah:phase(P1)
@yah:next(“Implements Phase 1a from .yah/docs/architecture/A041-yah-mesh-bootstrap.md — design committed 2026-05-04. Phase 1b lands as R040-F19, Phase 2 (HA) as R040-F20+F21.”)
@yah:next(“Subcommand surface: yah mesh start brings up Headscale locally (yah-desktop or camp daemon); auto-detects direct vs. cloudflared reachability; configures DNS for mesh.mesh-url so provision picks it up.”)
@yah:next(“Cloudflared bundling: yah-desktop should manage the cloudflared binary so the operator doesn’t need a separate install. Same daemon can carry yah serve ingress + Headscale tunnel under one install — that’s the user-acceptable story.”)
@yah:next(“Stable URL contract: mesh.<your-domain> set ONCE here and never changes across Phase 1b promotion or Phase 2 leader changes. Nodes embed this URL in their tailscale config; downstream phases re-point DNS, not nodes.”)
@yah:next(“Headscale binary provisioning: download + manage like cloudflared (precedent: yah already shells out to cloudflared/tailscale/cargo). Config template per arch doc ‘Headscale config’ section. Initial ACL writes a permissive tag:* policy.”)
@yah:next(“Provision integration: when mesh-url is set, yah cloud machine provision calls Headscale API for a single-use preauth key and embeds {MESH_URL, PREAUTH_KEY, TAGS} into cloud-init’s tailscale up line. Static headscale-preauth-key in vault becomes optional.”)
@yah:next(“Also lands yah mesh status (coordinator location, connected nodes, preauth count). yah mesh backup/restore can be Phase-1a stubs; full litestream comes with R040-F21.”)
@yah:verify(“yah mesh start on a fresh camp brings up Headscale + DNS, prints stable URL, stores in vault”)
@yah:verify(“yah cloud machine provision
@yah:ticket(R040-F19, “Phase 1b — yah mesh promote: migrate Headscale from camp to first cluster machine”)
@yah:at(2026-05-05T00:29:11Z)
@yah:assignee(agent:claude)
@yah:status(review)
@yah:phase(P2)
@yah:parent(R040)
@yah:next(“Implements Phase 1b from .yah/docs/architecture/A041-yah-mesh-bootstrap.md. Depends on R040-F18 (Phase 1a) shipping the stable-URL contract and camp Headscale.”)
@yah:next(“Subcommand: yah mesh promote
@yah:relay(R085, “Infra-info MCP — yubaba / machine / mirror status read surface”) @yah:assignee(agent:claude) @yah:status(review) @yah:parent(Q082) @yah:handoff(“Four read-only cloud.* MCP tools landed in crates/yah/agent-tools/src/cloud_tools.rs. Registration: KgToolRegistry::with_cloud() in tools.rs (same builder pattern as with_board/with_scryer/with_task). cloud-client dep added to agent-tools Cargo.toml. Local-config tools (cloud.machines, cloud.mirror_state) read .yah/cloud/{machines,mirrors,services}/.toml from ctx.camp_root with lightweight serde structs — no cloud crate dep, no network. Network tools (cloud.warden_status, cloud.service_ports) probe yubaba HTTP API via cloud_client::CloudClient and return reachable:false rather than error when unreachable. 14 unit tests, all pass. Total agent-tools tests: 140.”) @yah:next(“Wire with_cloud() into the agent session startup (camp.rs or agent.rs in app/yah/cli) alongside with_board() and with_scryer() so the tools appear in the agent’s tool list automatically.”) @yah:next(“Feed cloud.warden_status into the drift detection surface (status.rs AgentProbe) so yubaba reachability populates machine status reports rather than being a separate probe path.”) @yah:verify(“cargo test -p agent-tools – cloud 2>&1 | grep ‘test result’ shows 14 passed; 0 failed”) @yah:verify(“KgToolRegistry::standard_read_only(ctx).with_cloud().schemas() returns 4 tools all named cloud.”) @arch:see(.yah/quests.md)
@yah:relay(R168, “yah-cloud TOML config schema + validation”)
@yah:assignee(agent:claude)
@yah:at(2026-05-13T19:07:07Z)
@yah:status(review)
@yah:parent(Q156)
@yah:handoff(“Schema doc written at .yah/docs/architecture/A031-yah-cloud-config-shape.md. MirrorConfig.camp renamed to .camp with serde alias ‘camp’ for migration compat (R137 rename). load_mirrors() added to handle both flat mirrors/
@yah:relay(R260, “OpenRouter model almanac — track top-weekly models for backend selection”)
@yah:assignee(agent:claude)
@yah:at(2026-05-20T22:23:52Z)
@yah:kind(spike)
@yah:status(in-progress)
@arch:see(architecture/yah-cloud-config-shape.md)
@yah:next(“Unique signal: popularity + freshness + capability tags. Pricing is already covered by crates/yah/probe/data/pricing.json (LiteLLM snapshot) — almanac does NOT duplicate $/token; it adds rank, :free flag, modality, capability tags (code / vision / tool-use / long-context), and a last_seen_at so consumers can tell when an entry has gone stale.”)
@yah:next(“Primary consumer: the OpenRouter character bundle at crates/yah/kg/preroll/openrouter/{agents,subclasses}.json. Currently a single preroll agent ("Quick Hand") pinned to qwen/qwen3-coder:free — this WILL silently break when OpenRouter rotates the free-tier list. Almanac’s first job is to keep that subclass pointed at a currently-available free model (and let the bundle expand into a roster of currently-hot free models, not just one).”)
@yah:next(“Free-model rotation is the load-bearing constraint. Refresh cadence has to match how often OpenRouter shuffles the :free set (empirically weekly-ish). Storage options ranked by how well they handle rotation: (1) scheduled daemon refresh writing back into crates/yah/kg/preroll/openrouter/ — auto-heals; (2) on-demand fetch+cache in the runner — never stale at session start but offline-fragile; (3) build-time embedded snapshot like probe/data/pricing.json — simplest, goes stale between releases (probably wrong shape for free-tier).”)
@yah:next(“Two approaches to evaluate side-by-side: (A) straight call to https://openrouter.ai/api/v1/models (free, no key, structured JSON — already returns context_length, pricing.prompt/pricing.completion, architecture.modality; crates/yah/runner/src/resolver/openrouter.rs already talks to OpenRouter so a fetch_models() sibling to its existing GET /api/v1/credits is the natural home); (B) inference-based tool that distills https://openrouter.ai/models?order=top-weekly HTML into our schema. Confirm (A) first — if the JSON exposes the top-weekly ordering and :free flag, (B) is unnecessary; if not, (B) becomes the rotation-detector.”)
@yah:next(“Query-resolvable subclass (the real primitive): AgentSubclass.model at crates/yah/kg/src/party.rs:520 is a hard provider:model string today. Promote it to a sum type — literal provider:model OR ModelQuery { tier, capability_tags, min_context_tokens, max_price_per_token, … } — resolved by the almanac at session start. Slots into the existing fallbacks: Vec<FallbackRule> machinery at party.rs:540: emit ConfigSwitch{ModelResolved} (sibling of FallbackTriggered) when the query lands, so the UI can show "Quick Hand → qwen3-coder:free (current pick, refreshed 2h ago)". The preroll bundle then declares intent ("Thief-shaped, free, code-tuned") instead of a brittle pin.”)
@yah:next(“Constraint vocabulary — the hard numbers behind "Thief-shaped, free, code-tuned", layered by cost-to-implement so we ship something useful at T0 and can grow. T0 (already free from /models): context_length, pricing.prompt/pricing.completion, architecture.modality come back in the same fetch. MVP heuristic = Pareto frontier of (context_window, price_per_token); at :free tier price collapses to 0 so it cleanly reduces to "max context with matching capability tag". This is enough to pick a non-broken Quick Hand. T1 (external quality scores): join on canonical model id against artificialanalysis.ai’s quality index (general + code subscore), Aider’s code leaderboard, or LiveBench. Cheap to add once the T0 pipe exists; lets queries express min_quality or prefer_code_subscore. T2 (almanac-as-evaluator): spend a few credits running a small fixed corpus (lint-fix, summarize, ack-and-route) through each new free candidate, score against a reference, persist scores into the almanac. The dogfood option — most signal, most expensive, most interesting. Out of scope for the spike; file as a follow-up child once T0/T1 are in flight.”)
@yah:next(“Recommendation surface: popularity-ranked picker entries in packages/yah/ui/src/components/agent/Picker/toPickerAgents.ts + AgentProvidersPanel.tsx so adding an OpenRouter backend surfaces the currently-hot models first; query-resolved subclasses display their resolved pick + a freshness chip; stale literal-pinned :free references show a warning.”)
@yah:next(“Spike output: a design note at .yah/docs/working/W091-openrouter-almanac.md + a working (A) prototype printing the top 10 models in our T0 schema (id, context_length, price, :free flag, capability tags, popularity rank). From there, refine into child tickets for: (i) refresh mechanism, (ii) ModelSpec::Literal | ModelSpec::Query sum type + T0 resolver, (iii) preroll-bundle migration from pinned-model to ModelQuery, (iv) UI recommendation + freshness chips, (v) T1 external-benchmark join, (vi) T2 self-eval as a stretch/follow-up.”)
@yah:handoff(“Spike output delivered: working prototype + design note + 6 child tickets. Prototype lives in crates/yah/runner/src/resolver/openrouter.rs (AlmanacEntry struct + fetch_models() against /api/frontend/models/find?order=top-weekly + derive_capability_tags) plus app/yah/cli/src/agent.rs (yah agent almanac –top N –free-only –tag code –json). Design note at .yah/docs/working/W091-openrouter-almanac.md. Six follow-ups filed: R260-F1 (refresh scheduler), R260-F2 (ModelSpec sum type + T0 resolver), R260-T3 (preroll bundle migration → ModelSpec::Query), R260-F4 (UI freshness chips + stale-pin warning), R260-F5 (T1 benchmark join), R260-S6 (T2 self-eval, stretch). T0 prototype confirms the rotation problem: yah agent almanac --top 10 --free-only --tag code shows qwen/qwen3-coder:free (Quick Hand’s current pin) is NO LONGER in the top-10 free code models — current leaders are openrouter/owl-alpha, nvidia/nemotron-3-super, poolside/laguna-m.1.”)
@yah:verify(“cargo run -p yah – agent almanac –top 10 — prints OpenRouter top-10 weekly models in T0 schema”)
@yah:verify(“cargo run -p yah – agent almanac –top 10 –free-only –tag code — shows the currently-hot free-tier code-tuned roster (the Quick Hand replacement set)”)
@yah:verify(“cargo check -p runner -p yah — clean (note: cargo test -p runner –lib is blocked by pre-existing R258-F3 test breakage on AgentSession; tracked as gotcha)”)
@yah:gotcha(“Pre-existing R258-F3 test breakage in crates/yah/runner/src/{anthropic.rs:833, sessions.rs} — cargo test -p runner --lib fails with E0063 (AgentSession initialiser missing last_text + slot_id fields). R258-F3 is currently in review. R260’s new resolver::openrouter::tests entries are byte-perfect but un-runnable until R258-F3’s test fixtures are updated.”)
@yah:gotcha(“OpenRouter’s /api/frontend/models/find?order=top-weekly is undocumented. The almanac treats it as best-effort and the design (see openrouter-almanac.md) calls for graceful degradation to public /api/v1/models alone when the join fails. ?order= is silently ignored on the documented endpoint (it always returns newest-first); approach (B) HTML scraping is unnecessary and was ruled out.”)
@yah:relay(R414, “yah-cloud pill sync + publish + Pills panel”) @yah:assignee(bundle-anthropic-miravel) @yah:at(2026-06-03T07:07:03Z) @yah:status(review) @yah:phase(P2) @yah:parent(Q410) @arch:see(.yah/docs/working/W141-pill-rings.md) @arch:see(.yah/docs/architecture/A031-yah-cloud-config-shape.md) @yah:depends_on(R411) @yah:handoff(“P2 delivered: resolve_pill_catalog (chips crate) + party_resolve_pill_catalog Tauri command + env wiring + PillsPanel in FullCharacterEditor. Pills panel shows all categorized pills grouped by category with per-character tri-state toggle (always-on/discoverable/off); edits agent.persona.chips.pill_state map via patchAgent.”) @yah:next(“P3: local pills.toml loader — add ~/.yah/pills.toml and .yah/pills.toml as new resolution layers (separate from chips.toml) per W141 layer stack”) @yah:next(“P3: MCP URL trust display — when a content-pill with mcp[] is toggled always-on, show one-time approval dialog listing the URLs before activating”) @yah:next(“P4: cloud sync stub — ‘Share to my account’ button in PillsPanel (disabled until account system exists)”) @yah:verify(“cargo test -p chips — 84 passed (3 new resolve_pill_catalog tests)”) @yah:verify(“bun run typecheck — no new errors (6 pre-existing unchanged)”)
@arch:see(.yah/docs/working/W193-asset-dependency-status-surface.md)
Re-exports§
pub use almanac_dispatch::dispatch_on_change;pub use asset_journal::AssetState;pub use asset_journal::AssetStatusEvent;pub use asset_journal::AssetStatusJournal;pub use capability::Capability;pub use compose::generate_compose_bundle;pub use compose::ComposeBundle;pub use config::BucketLogEntry;pub use config::CampCloudDbs;pub use config::CloudConfig;pub use config::CloudDb;pub use config::ConnectSpec;pub use config::DbCatalog;pub use config::DevDb;pub use config::GitSource;pub use config::IngressProvider;pub use config::LegacyMirrorConfig;pub use config::LegacyServiceConfig;pub use config::MachineConfig;pub use config::MirrorAssignment;pub use config::MirrorConfig;pub use config::MirrorProviderSlot;pub use config::MirrorShape;pub use config::PondDb;pub use config::PondDbKind;pub use config::Provider;pub use config::ProviderConfig;pub use config::ServiceComponent;pub use config::ServiceConfig;pub use config::TopologyConfig;pub use config::WorkloadConfig;pub use config::WorkloadConfigError;pub use local_driver_glue::local_container_spec_from_provider;pub use provider::on_ingress_owner_changed;pub use provider::reconcile_assignment;pub use provider::BucketAcl;pub use provider::BucketRef;pub use provider::CfAccountInfo;pub use provider::CloudflareClient;pub use provider::CloudflareEnvoy;pub use provider::CreateR2BucketResult;pub use provider::CreateTokenResult;pub use provider::CreateTunnelResult;pub use provider::DigitalOceanEnvoy;pub use provider::FloatingIpAssignOutcome;pub use provider::FloatingIpProvider;pub use provider::FloatingIpState;pub use provider::FloatingIpTarget;pub use provider::GrantScope;pub use provider::HetznerDriver;pub use provider::HetznerEnvoy;pub use provider::HetznerFloatingIp;pub use provider::Location;pub use provider::MachineProvider;pub use provider::OvhFloatingIp;pub use provider::ProjectId;pub use provider::R2BucketInfo;pub use provider::R2CustomDomain;pub use provider::ServerId;pub use provider::ServerSpec;pub use provider::ServerStatus;pub use provider::ServerSummary;pub use provider::TokenGrant;pub use provider::TunnelConnState;pub use provider::TunnelDnsRecord;pub use provider::TunnelDriftRow;pub use provider::TunnelDriftState;pub use provider::VultrFloatingIp;pub use provider::WorkerDeployResult;pub use provider::MESOFACT_STATIC_GRANTS;pub use reconciler::collect_live_derive_hashes;pub use reconciler::compute_derive_cache_candidates;pub use reconciler::compute_live_set;pub use reconciler::compute_prune_candidates;pub use reconciler::compute_service;pub use reconciler::derive_minio_key;pub use reconciler::execute_derive_cache_prune;pub use reconciler::execute_prune;pub use reconciler::load_service_and_mirror;pub use reconciler::mesofact_static::WORKER_SCRIPT;pub use reconciler::new_sync_id;pub use reconciler::pond::MINIFLARE_SIM_SCRIPT;pub use reconciler::publish_to_pond;pub use reconciler::summarize;pub use reconciler::CellStatus;pub use reconciler::CloudflareWorkerReconciler;pub use reconciler::ContainerOptions;pub use reconciler::ContainerReconciler;pub use reconciler::DeriveCacheLiveHashes;pub use reconciler::DerivePruneCandidate;pub use reconciler::DriftEntry;pub use reconciler::HealthState;pub use reconciler::LocalProcessReconciler;pub use reconciler::LocalStaticOptions;pub use reconciler::MesofactStaticReconciler;pub use reconciler::MirrorObservation;pub use reconciler::PondOptions;pub use reconciler::PondPublishReport;pub use reconciler::PondState;pub use reconciler::ProviderScope;pub use reconciler::PruneCandidate;pub use reconciler::PruneOutcome;pub use reconciler::PruneReport;pub use reconciler::ReconcileCtx;pub use reconciler::Reconciler;pub use reconciler::RunningWorkload;pub use reconciler::RunningWorkloadSummary;pub use reconciler::Runtime;pub use reconciler::ServiceStatus;pub use reconciler::StaticAssetReconciler;pub use reconciler::StatusSummary;pub use reconciler::SyncHistoryEntry;pub use reconciler::SyncOutcome;pub use reconciler::SyncState;pub use reconciler::WireContainerStatus;pub use status::collect_machine_report;pub use status::AgentProbe;pub use status::DriftFinding;pub use status::MachineReport;
Modules§
- almanac_
dispatch - Dispatch [
almanac::OnChangeConfig] actions after a feed run (R330-F4). - app_
manifest yah-app.tomlschema, parser, closed-setrequired_whenevaluator,.yah/apps.tomlregistry, and workspace discovery (R470-T7).- asset_
journal - Append-only JSONL journal for static-asset reconciler decisions (R470-T1).
- asset_
status - Build the W193 workspace asset-status report as a
serde_json::Valuesuitable forcloud.statusRPC responses (R470-T4). - capability
- Service capabilities and the mirror-side driver bindings that satisfy them (W265).
- cloud_
init - Cloud-init template renderer for mirror-machine bootstrap (R040-F4).
- compose
- Podman Compose file generation for yah-cloud service deployments (R040-F7).
- config
- @yah:ticket(R040-F16, “pg-on-mesh service recipe: bind tailscale0 + pg_hba.conf snippet + ufw rules”)
@yah:at(2026-05-05T00:32:34Z)
@yah:assignee(agent:claude)
@yah:status(review)
@yah:parent(R040)
@yah:handoff(“Companion to R040-F15. Inter-node TCP (Postgres primary↔replica, NATS clusters, anything raw-protocol) lives on the Headscale mesh, not on Hetzner public IPs. Each node has a stable 100.64.x.x mesh IP that survives replacement of the underlying box, so DNS / config / pg_hba never churn when a CPX-11 is rebuilt. WireGuard already encrypts the wire — TLS becomes defense-in-depth, not load-bearing. This ticket carries the concrete pg-shaped recipe so the first stateful service deploy doesn’t have to re-derive the pattern; subsequent services (redis, NATS, etc.) cargo-cult from it.”)
@yah:next(“ServiceConfig gains a
bind_interface: Option<String>field (e.g.Some(\"tailscale0\")for mesh-only services). The cloud-init/podman compose renderer translates this into either--network host+pg listen_addresses = '<mesh-ip>'OR a podman macvlan/host-binding pattern that achieves the same.”) @yah:next(“Generated pg_hba.conf snippet: allow the mesh subnet (100.64.0.0/10) for replication + app users. Postgres binds to the node’s tailscale0 mesh IP only —listen_addressesis templated from the node’stailscale ip --4at first boot.”) @yah:next(“Generated ufw rules:ufw allow in on tailscale0 to any port 5432; ufw deny 5432— mirrors the existing yah-yubaba 7443 pattern in mirror.yml. Same shape works for any mesh-only port.”) @yah:next(“Replica connection string uses primary’s mesh IP, NOT its public IP. Stable across box replacement.”) @yah:next(“Out of scope: pg_basebackup orchestration, failover, WAL archiving — those belong in noisetable’s domain; this ticket only standardizes the binding/firewall/auth shape so noisetable’s pg deployment doesn’t reinvent it.”) - envoy
- Envoy framework types —
Tier,AdapterFlavor,VerbCategory,InternalVerb,VerbDescriptor. - identities
- Stub local identity registry:
.yah/cloud/identities/<machine>.json. - local_
driver_ glue - Adapter between cloud’s TOML-driven
ProviderConfigshape andlocal_driver::LocalContainerSpec. - mesh
- Headscale API client and configuration helpers for
yah meshoperations. - mesh_
service - Recipe helpers for stateful services bound exclusively to the Headscale mesh (R040-F16).
- multi_
root - Multi-root cloud config — union sibling
.X/config trees (W206 layout (b)). - paths
- On-disk path resolver for camp deployment manifests.
- provider
- @yah:relay(R409, “Envoy — providers and internal verb catalog (W144)”) @yah:at(2026-06-02T20:58:35Z) @yah:status(open) @arch:see(.yah/docs/working/W144-envoy-providers-and-tiering.md)
- provision
- Provisioning orchestrator: config → cloud-init render → MachineProvider call.
- reconciler
- Reconciler abstraction — bring a workload up against a mirror’s provider slots.
- release_
manifest - Yubaba release-manifest fetch + per-triple resolution (R330-F21).
- state
- Reconciler-written sidecar state for declared machines.
- status
- Drift detection between declared
.yah/cloud/config and live cloud state. - validate
- Workspace-wide lint checks for the
.yah/services/tree (R470-T3).
Structs§
- Container
RunSpec - Run-time spec for a single container managed by
LocalRuntime::run. - Custom
Docker Host Provider RuntimeProviderfor a rawDOCKER_HOSTstring supplied by the operator (e.g."tcp://localhost:2375"or"unix:///path/to/custom.sock"). Always reported as available — the operator opted in explicitly; failures surface as docker CLI errors rather than probe misses.- Local
Container Spec - Probe spec lifted from a
kind = "local-container"provider TOML. - Local
Docker Runtime WorkloadRuntimeimplementation backed by the docker CLI.- Local
Runtime - Reachable local container daemon — the winning provider from the cascade.
- Owned
Container - A container managed by this module, as observed via
LocalRuntime::list_owned. - Socket
Runtime Provider RuntimeProviderbacked by a Unix socket path. Available iff the tilde-expanded path exists on disk.
Enums§
- Container
State - Container State.Status from
docker inspect. Anything the docs don’t enumerate lands in [Unknown]. - Detected
Runtime - Which runtime answered the probe cascade.
- Runtime
Pref - Operator-declared runtime preference from the provider TOML’s top-level
runtimefield.autowalks the full cascade; any pinned value probes only that runtime.
Constants§
- LABEL_
KEY - Docker label key applied to every container this module owns. The value
is
<service>:<env>:<slot>sodocker ps --filter label=yah.pond=<v>scopes orphan cleanup to a specific mirror. - NAME_
PREFIX - Canonical name prefix for every container managed by this module.
Traits§
- Runtime
Provider - Probe contract for a single Docker-compatible runtime back-end.
Each implementation knows its socket path (or raw
DOCKER_HOST) and can report whether it is currently reachable.
Functions§
- canonical_
label - Build the canonical label value for the same triple.
- canonical_
name - Build the canonical container name for a (service, env, slot) triple.
- warden_
container_ label - Canonical label of the camp’s pond yubaba container:
camp:<env>:yubaba. - warden_
container_ name - Canonical name of the camp’s pond yubaba container for
env:yah-pond-camp-<env>-yubaba. The camp (which starts it) and the desktop probes (which inspect it) MUST agree on this — always go through here.