diff --git a/programming_general/infra_manifest.yaml b/programming_general/infra_manifest.yaml index 211b0e0..c69afbe 100644 --- a/programming_general/infra_manifest.yaml +++ b/programming_general/infra_manifest.yaml @@ -1,12 +1,12 @@ apiVersion: conventions/v1 -version: 0.3.0 +version: 0.4.0 updated: "2026-06-29" name: infra_manifest -title: Per-project infra manifest (.infra.yaml) +title: Infra manifest (.infra.yaml — per-project + producer-level shared infra) scope: general status: draft -summary: Every deployable project declares its infrastructure in a root .infra.yaml; `service.host` must be a host in net-tools mesh-hosts.json, so the infra-net reconciler can build the live picture. A project with a distinct non-prod deployment may add a sibling .infra.dev.yaml (same schema, environment:dev) — e.g. a local operator instance on a mac host. The reconciler reads every .infra*.yaml. A future infra-apply renders the DO parts. -appliesTo: ["@applications/*", "@projects/@cocottetech", "@projects/@magic-civilization"] +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." +appliesTo: ["@ct/**", "@mc/**", "@quinn/**", "@*/.infra.yaml"] rules: - id: own_db level: must @@ -21,6 +21,10 @@ rules: - id: host_in_mesh level: must 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." + - id: shared_infra_topology + level: should + 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. - id: env_variants level: should text: "Default manifest is `.infra.yaml` (prod, environment defaults to prod). A distinct non-prod deployment lives in a sibling `.infra..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." @@ -63,3 +67,27 @@ providesFile: type: array items: { type: string } description: Other services consumed over HTTP. + droplets: + type: array + 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." + items: + type: object + additionalProperties: false + required: [name, services] + properties: + name: { type: string } + role: { type: string } + provider: { type: string, enum: [digitalocean, mac, bare-metal, local] } + hosts: { type: array, items: { type: string }, description: "mesh-hosts.json names this droplet registers on provision." } + services: + type: array + items: + type: object + additionalProperties: false + required: [name, kind] + properties: + name: { type: string } + kind: { type: string, description: "forgejo | npm-registry | pypi-registry | swiftpm-registry | dns | reverse-proxy | mcp | ..." } + producer: { type: string, description: "Which producer this service belongs to (ct/mc/quinn), when shared host serves multiple." } + port: { type: integer } + domain: { type: string }