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 |
| Cluster infrastructure | LB size units | Starbase (from capacity monitoring) | Shuttle (patches annotation) |
| 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.
Desired state payload is the contract between Starbase and Shuttle. It's a JSON document that Shuttle pulls every 30 seconds (see §25.1). If the payload changes, Shuttle applies the diff on the next tick. If the payload is unchanged, Shuttle does nothing (no-op 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 (§35.2). 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.