summary:"Every deployable project declares its infrastructure in a root .infra.yaml (single `service`); `service.host` must be a host in net-tools mesh-hosts.json. A PRODUCER root may also carry a .infra.yaml describing shared-infra TOPOLOGY via `droplets` — physical hosts each running many co-located services (e.g. @quinn/.infra.yaml — one services droplet for all forges + registries + DNS + edge, plus an MCP droplet). The infra-net reconciler reads every .infra*.yaml; a future infra-apply renders the DO parts."
text:A project needing a database declares its own logical DB + dedicated user on the shared managed cluster (data-sourced), never reusing another service's creds.
text:"Standing cloud hosts run on DigitalOcean (region nyc3 by default — operator-local; fra1/ams3 only if EU PII residency wins the GDPR call), managed by the uvlava terraform at @ct/infra/uvlava/terraform/do/. `provider: digitalocean` in the manifest. Today all droplets share ONE DO account (PATs ~/.vault/do_pat_*); per-producer DO accounts are the target, not yet real."
rationale:One declared cloud provider keeps IaC, billing, and the mesh reconciler coherent; nyc3 co-locates droplets + managed PG + Spaces.
text:"DO droplets are named reverse-DNS. TWO tiers: (1) GLOBAL shared services with NO producer segment — `com.uvlava.<role>` (e.g. com.uvlava.dns = DNS authority/resolver, com.uvlava.wg = WG mesh hub); (2) PRODUCER hosts — `com.uvlava.<producer>.<role>`, `<producer>` ∈ {ct, mc, quinn}, `<role>` is the function (services, artifacts, redroid, gpu). Operator-shared producer infra is `quinn.*` (com.uvlava.quinn.artifacts = forges+registries); per-producer app/data hosts are `<producer>.*` (com.uvlava.ct.services, com.uvlava.ct.redroid). The DO `name` is ForceNew in the provider: set it once at create, rename LIVE via `doctl compute droplet-action rename`, and keep `lifecycle.ignore_changes = [name]` so a label change never destroys the box."
text:"`service.host` is a host name from net-tools mesh-hosts.json (lime, fennel, redroid, …) — the infra-net reconciler validates this and regenerates the mesh-hosts services map from all .infra.yaml."
text:"Shared metal owned by the operator is declared once at the producer root (@quinn/.infra.yaml) via `droplets` — each droplet lists the co-located services it runs (forges, npm/pypi/swift registries, DNS, reverse-proxy, MCP). Logical per-producer forges (ct/mc/quinn) co-locate on one services droplet rather than one droplet each; tag each service with its `producer`. On provision, register each droplet's `hosts` in mesh-hosts.json."
rationale:One services droplet (forges + registries + DNS + edge) + one MCP droplet is cheaper and simpler than a droplet per producer, while keeping forges logically per-producer.
text:"Default manifest is `.infra.yaml` (prod, environment defaults to prod). A distinct non-prod deployment lives in a sibling `.infra.<env>.yaml` (currently only `.infra.dev.yaml`) with the same schema + `environment` set. One project may thus appear as multiple services (e.g. prod on a DO droplet + a local mac instance). Keep run-only/access config (passcodes, bind addresses) out of the manifest — it is not mesh infra."
text:"`@quinn/manage-apps` (~/Code/@quinn/@packages/manage-apps) is the canonical service orchestrator — it AUTO-DISCOVERS every `.infra.yaml` by walking the producer tree (no central registry) and drives start/stop/status/deploy. A new deployable service = drop a `.infra.yaml`; never hand-roll start/deploy ssh scripts or a per-app `app.manifest.yaml` (that legacy format is retired in favour of `.infra.yaml`)."
rationale:One declarative manifest, one orchestrator, zero registration — the same `.infra.yaml` the net-tools infra-net reconciler reads for mesh/DNS.
- id:systemd_supervision
level:must
text:"Standing services on cloud hosts run as **systemd units** (declared via `service.systemd_unit`), never as foreground ssh or /tmp PID-tracked processes — so they survive host restarts and crash-restart. The `service.deploy` script installs/enables the unit; manage-apps drives it via `ssh <host> systemctl …`. PID/background mode is for local-mac dev only."
rationale:systemd is the supervisor; PID files die on restart. Matches the global rule 'long-running jobs → systemd, not foreground ssh'.
- id:mesh_host_resolution
level:should
text:"`service.host` resolves to an ssh alias from net-tools `host-apply` (~/.ssh/config rendered from mesh-hosts.json) — manage-apps runs `ssh <host> …`, it does NOT embed IPs or `-i <key>`. Internal service-to-service traffic rides the WireGuard mesh (10.9.0.0/24); on-mesh peers skip auth, so no app port is publicly exposed."
rationale:net-tools owns SSH config + the mesh; manage-apps owns runtime. One source of truth for host addressing; the mesh is the private plane.
provider:{type: string, enum: [digitalocean, mac, bare-metal, local], description:"Where it physically runs: digitalocean droplet, a mac (e.g. fennel), bare-metal, or local."}
systemd_unit:{type: string, description:"systemd unit name. manage-apps drives it via `ssh <host> systemctl …` (start/stop/status); the host resolves as an ssh alias from host-apply's ~/.ssh/config."}
deploy:{type: string, description:"Repo-relative deploy script (ships + builds + installs/enables the unit). manage-apps `deploy` runs it locally; the script handles ssh/rsync."}
description:"Producer-level shared-infra topology: physical droplets each hosting MANY co-located services. Used by a producer-root manifest (e.g. @quinn/.infra.yaml) that owns shared metal — distinct from a single project's `service`. Logical per-producer endpoints (ct-forge/mc-forge/quinn-forge) may co-locate on one droplet."
name:{type: string, pattern:"^com\\.uvlava\\.((ct|mc|quinn)\\.)?[a-z0-9-]+$", description:"Reverse-DNS droplet name: global com.uvlava.<role> (e.g. com.uvlava.dns) OR producer com.uvlava.<producer>.<role> (see rule droplet_naming). Rename live via doctl; name is ForceNew in terraform."}