Security & Tenant Isolation¶
A synthesis page. Security in Starform is not one feature — it's the sum of four mechanisms, each owned by a canonical section elsewhere. This page ties them together; the linked sections remain authoritative.
The four pillars:
- Network isolation — namespace-per-project + label-selector NetworkPolicies (§20.4).
- Query isolation — a server-side tenant filter on all telemetry the client cannot override (§35.2 / FR-065).
- Encryption at rest — AES-256-GCM on all sensitive Postgres fields (FR-060), and Cloudflare Origin Certs in transit with no cert-manager (§5).
- Ingress auth — per-cluster bearer tokens on telemetry ingress, even over private VPC peering (FR-066 / FR-071).
Network isolation — namespace + NetworkPolicy¶
Tenant isolation is namespace-per-project plus label-selector NetworkPolicies — not the VPC.
Clusters in a region share one /16 VPC (§4.4); isolation
is enforced inside the cluster, so sharing the regional VPC is safe.
K8s NetworkPolicies support podSelector.matchLabels — policies reference pods by label rather than by
namespace. For each environment, Shuttle creates a policy in the project namespace
(§20.4):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: env-isolation-production
namespace: proj-acme-api
spec:
podSelector:
matchLabels:
starform.io/environment: production
policyTypes: [Ingress, Egress]
ingress:
- from:
- podSelector:
matchLabels:
starform.io/environment: production # same env only
- from:
- namespaceSelector:
matchLabels:
starform.io/namespace-role: gateway # Envoy Gateway namespace
egress:
- to:
- podSelector:
matchLabels:
starform.io/environment: production
- to: [] # all external traffic allowed (egress policing is at Gateway)
ports:
- protocol: TCP
port: 443
This enforces production pods can only reach production pods, and staging pods cannot reach production
pods, within the same namespace. The starform.io/environment label is therefore load-bearing for
isolation — see the label catalog §24.1.
Failure mode → loud, not silent (pre-launch-blocking)
If Shuttle fails to apply the correct starform.io/environment label on a pod, that pod loses its
NetworkPolicy protection. Mitigation: a Kyverno admission policy (post-MVP) rejects any pod without
the required label set, turning a silent failure into a loud one
(§20.4; enforcement tracked in
§39.2).
Query isolation — the server-side telemetry filter¶
Every customer metric and log query is scoped by a tenant filter injected server-side from the authenticated session. The client cannot supply or override it. Starbase acts as the telemetry-query broker: it routes each query to the project's region and injects the filter before the store ever sees the request (§35.2).
- FR-065 — all customer metric/log queries MUST be scoped by a server-side tenant filter; the client MUST NOT supply or override it.
- SC-016 — customer queries never return another tenant's data (verified by an isolation test in CI).
The filter keys off the tenant tuple — project_id + environment + service_id, with workspace_id
as the billing boundary — the same identity every series and log line is attributed to
(§24.1).
Encryption — at rest and in transit¶
At rest (AES-256-GCM). All sensitive data in Postgres is encrypted with AES-256-GCM using a single
key from the ENCRYPTION_KEY env var; key rotation is a post-MVP batch job. The encryption-at-rest
catalog (§39.1 item 18) covers every encrypted field:
| Encrypted field | Requirement |
|---|---|
| Var Group entry values (§38.2) | FR-036 |
| Database credentials | FR-028 |
| Storage (bucket) credentials | FR-033 |
| GitHub installation tokens | FR-060 |
| Cloud-provider credentials / kubeconfigs Starform holds for its own clusters | FR-060 |
FR-060 mandates AES-256-GCM for all sensitive data at rest; SC-011 requires those credentials never appear in logs or API responses.
In transit (Cloudflare, no cert-manager). TLS terminates at the Cloudflare edge; Cloudflare → origin
is encrypted with a free Cloudflare Origin Certificate (15-year validity, stored as a K8s Secret on
the Gateway listener). *.starform.app subdomains use Cloudflare's wildcard edge cert.
No cert-manager is needed at MVP — see Networking › Traffic & TLS §5.
(cert-manager arrives post-MVP only when customer-domain Let's Encrypt issuance lands,
§39.)
Ingress auth — per-cluster bearer tokens¶
Customer telemetry ships over the private network (DO VPC peering), authenticated with a
per-cluster bearer token at the metrics ingress (vmauth) and the logs ingress (the Vector
aggregator's fluent source) — even over the private link. This is defense in depth: a single
compromised cluster cannot spoof another's data.
The telemetry stores are VPC-private (no public IPs). Starbase reads them only through the authenticated front door — vmauth (VictoriaMetrics) + a read-only ClickHouse user — over the peered link, and always injects the server-side tenant filter on that read path.
- FR-066 — ship customer telemetry over the private network, authenticated with a per-cluster bearer token at the metrics and logs ingress (§35.4).
- FR-071 — serve cross-region telemetry reads over the private network through an authenticated store front-door (vmauth + read-only ClickHouse user), never a public endpoint, applying the server-side tenant filter on that read path.
Authentication & authorization (who can act)¶
Dashboard login is SSO via GitHub + Google at MVP — distinct from the deferred customer auth primitive (a feature Starform offers customers' apps, out of scope at MVP). Authorization within the dashboard is the two-tier RBAC model §15: workspace roles + project roles + the environment-protection flag.
Cross-references
Network isolation → §20.4 · isolation labels → §24.1 · server-side query filter → §35.2 / FR-065 / SC-016 · encryption-at-rest catalog → §39.1 #18 / FR-060 · TLS / Origin Certs → §5 · ingress + cross-region read auth → FR-066 / FR-071 (§35.4) · RBAC → §15. Canonical map: Canonical Sources.