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.