Skip to content

Desired State Model

Core principle: Starbase decides, Shuttle applies.

Everything in a customer cluster flows through desired state — customer workloads, LB scaling, HTTPRoutes, future SecurityPolicies. Shuttle is a dumb applier. It doesn't make decisions; it makes the cluster match what Starbase says.

What desired state includes

Category Examples Who decides Who applies
Customer workloads Deployments, Services, ConfigMaps, Secrets Starbase (from user config + build output) Shuttle
Multi-tenancy isolation Namespaces, RBAC, NetworkPolicy, Quotas Starbase (from customer tier) Shuttle
Traffic routing HTTPRoutes Starbase (from service config) Shuttle
Autoscaling policy HPA min/max + CPU/memory targets Starbase (from service config) Shuttle (renders the HPA; the HPA owns replicas§20.3)
Placement Which cluster each (service, environment) and database runs on (FR-077) Starbase (project default region + customer overrides) Shuttle (each cluster's payload contains only what is placed there)
Cluster infrastructure Gateway Service annotations (LB sizing) Starbase (region adapter renders cloud-specific keys) Shuttle (applies the map opaquely)
Auth policies (post-MVP) SecurityPolicies Starbase (from auth-config) Shuttle

Single pattern, no exceptions. If it's a Kubernetes resource in a customer cluster, it goes through desired state. Starbase never directly calls the K8s API in customer clusters — it has no persistent K8s API access. All cluster mutations flow through Shuttle.

Deletion is a decision, not an inference (July 2026). Entities leave a cluster only via an explicit tombstone (deleted: true) in the payload, confirmed back on the status report (§25.4); absence never deletes, and a delete breaker plus a monotonic sequence guard bound the blast radius of a control-plane bug (§20.3, FR-073). Autoscaling follows the same shape: the decision to autoscale (bounds, targets) flows through desired state; the live replica count is delegated to the HPA within those bounds.

Desired state payload is the contract between Starbase and Shuttle. It's a JSON document that Shuttle pulls every 30 seconds (see §25.1) via a conditional GET keyed on sequence — an unchanged payload answers 304 Not Modified, so a no-op tick transfers nothing. If the payload changes, Shuttle applies the diff on the next tick.

Identity in the payload (v1.9)

Each service entry in the desired-state payload (§25.1) carries the canonical tenant key — workspace_id, project_id, environment, and service_id — matching the load-bearing label set (§24.1). workspace_id is the billing-boundary label; project_id + environment + service_id is the identity tuple every metric and log line is attributed to (v2 Collect). Shuttle stamps these as labels on every resource it creates and encodes project_id + service_id + environment into each HTTPRoute name (§20.2) so Envoy metrics resolve to the same key. There is no customer_id field — it was removed in v1.9; identity flows workspace → project → environment → service throughout.

Where computation lives

Desired state computation — the code that transforms Postgres rows into the JSON payload above — lives in Starbase at internal/service/desiredstate.go (per §9's package layout). Its job is a query-and-serialize: SELECT across projects, environments, services, deployments, var_groups, service_var_groups, and related tables; fold the rows into the payload shape defined in §25.1; return over the HTTP endpoint defined there. The contract (JSON shape, versioning, field semantics) is architecture and lives in this design hub. The computation (SQL, joins, serialization) is implementation-time — it gets designed and reviewed as code against the concrete DB schema, not upfront. Changes to the contract require a design update; changes to the queries do not.


Cross-references

Payload contract & 30s pull → §25.1 · the agent that applies it → Shuttle · level-driven reconciliation → Shuttle › Architecture · tenant key carried in the payload → §24.1 · HTTPRoute name encoding → §20.2 · LB size-unit patching → Networking › Load Balancer. Canonical map: Canonical Sources.