Skip to content

Security & Tenant Isolation

A synthesis page. Security in Starform is not one feature — it's the sum of five mechanisms, each owned by a canonical section elsewhere. This page ties them together; the linked sections remain authoritative. (Design record: specs/2026-07-05-compute-substrate-and-isolation-design.md.)

The five pillars:

  1. Network isolation — namespace-per-project + label-selector NetworkPolicies (§20.4).
  2. Query isolation — a server-side tenant filter on all telemetry the client cannot override (v2 Read & query / FR-065).
  3. Encryption at rest — AES-256-GCM on all sensitive Postgres fields (FR-060), and Cloudflare Origin Certs in transit with no cert-manager (§5).
  4. Ingress auth — per-cluster bearer tokens on telemetry ingress, even over private VPC peering (FR-066 / FR-071).
  5. Workload & kernel isolation — gVisor sandbox for customer code + a hardened PodSpec (FR-082 / FR-083).

Network isolation — namespace + NetworkPolicy

Tenant isolation is namespace-per-project plus label-selector NetworkPoliciesnot 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.

For each environment, Shuttle creates a label-selector NetworkPolicy in the project namespace — the policy template and apply rules are owned by §20.4. The posture it enforces:

  • All external egress allowed on any port; the entire private estate denied by one except 10.0.0.0/8; the environment's own dedicated DB endpoints allowed back as per-/32 rules rendered from desired state.
  • Same environment means same environment, same cluster — there is no private networking across regions (FR-078).
  • Cross-environment traffic stays denied unless the project opts in via allow_cross_env_networking (FR-079, warned when a protected environment is involved).
  • 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, as bound parameters the client cannot supply or override. Starbase acts as the telemetry-query broker: it routes each query to the region(s) hosting the environment's services (fan-out + merge when an environment spans regions, FR-071) and injects the filter before the store ever sees the request (v2 Read & query).

  • 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 encryption for all sensitive data at rest; SC-011 requires those credentials never appear in logs or API responses.

Customer-cluster etcd (at rest). Shuttle renders Var Group values into K8s Secrets, which live in each customer cluster's etcd. Because those clusters run untrusted co-tenant code:

  • etcd is encrypted at rest via Talos LUKS2 disk encryption (STATE + EPHEMERAL, AES-XTS-256, key sealed to the node) enabled in the bootstrap machine-config — a distinct mechanism from the app-level AES-256-GCM above (which protects the Postgres source-of-truth). It closes stolen-disk / DO-volume-snapshot / offline-etcd exposure.
  • secretbox EncryptionConfiguration (encrypting the Secret objects within etcd, so even a live etcd read returns ciphertext) is pre-launch defense-in-depth (§39.2 #68).
  • Neither stops a live gVisor-escape from reading a secret mounted into a running pod — that is the sandbox's job (isolation pillar above).

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 — at MVP or after — see Networking › Traffic & TLS §5. (Custom domains arrive post-MVP via Cloudflare for SaaS custom hostnames — Cloudflare issues and renews those certs too, §39.3 #27.)

Ingress auth — per-cluster bearer tokens

Customer telemetry ships over the private network (intra-region VPC), authenticated with a per-cluster bearer token validated at the regional OTel Collector gateway — both ingest doors, app logs and access logs — even on the private link. This is defense in depth: a single compromised cluster cannot spoof another's data.

The telemetry store is VPC-private (no public IPs). Starbase reads it only through the authenticated front door — a read-only ClickHouse user (SELECT only; the single front door) — 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 telemetry ingest (v2 Collect).
  • FR-071 — serve cross-region telemetry reads over the private network through an authenticated store front-door (a read-only ClickHouse user), never a public endpoint, applying the server-side tenant filter on that read path.

Workload & kernel isolation — gVisor + hardened PodSpec

Customer code is untrusted. Two layers contain it, on the self-managed Talos substrate (Kubernetes Runtime):

gVisor sandbox (FR-082). Customer web and worker pods run under the gVisor RuntimeClass (runtimeClassName: gvisor) — a user-space kernel that intercepts syscalls, so a container escape hits gVisor, not the host kernel. Platform components and Starform-rendered Mininova database pods run under runc.

Hardened PodSpec (FR-083), applied to every customer pod as defense in depth:

securityContext · every customer pod (Shuttle-rendered)
securityContext:
  seccompProfile: { type: RuntimeDefault }
  allowPrivilegeEscalation: false
  capabilities:
    drop: ["ALL"]
    add: ["CHOWN", "SETUID", "SETGID", "FOWNER", "DAC_OVERRIDE", "NET_BIND_SERVICE"]   # NET_RAW dropped
automountServiceAccountToken: false        # pods get no Kubernetes API credentials
# no hostNetwork / hostPID / hostIPC / hostPath; root-in-container allowed (image compat, declawed by the above)

Enterprise tier — dedicated nodes. Enterprise customers get dedicated Talos node groups (taints + tolerations), so their pods share a kernel only with their own workloads (post-MVP).

Shared-kernel risk register

Even with gVisor, tenants share a host kernel. A container-escape 0-day/N-day in gVisor or runc could reach co-tenants on the same node (not the fleet — NetworkPolicy, no K8s token, and blocked metadata egress bound lateral movement). gVisor makes escape much harder; DO patches node kernels; dedicated nodes remove co-tenancy for those who pay. The public untrusted tier also depends on abuse controls (payment gate + CPU/memory limits + mining detection, FR-084) — the defense against resource abuse, which sandboxing does not address.

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, post-MVP — §39.3 #30). 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 → v2 Read & query / 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 (v2 Collect) · workload/kernel isolation (gVisor + hardened PodSpec) → Kubernetes Runtime / FR-082 / FR-083 · RBAC → §15. Canonical map: Canonical Sources.