Security model
What Platinum trusts, where the isolation boundaries sit, and how credentials stay out of sandboxes.
Platinum treats code inside a sandbox as hostile. The hypervisor is the main boundary, and the host adds network, process and credential controls around it. The full threat model, including known gaps, is in SECURITY.md at the repository root.
Know who is trusted
| Party | Trust |
|---|---|
| Host kernel and KVM | Trusted. You run the machine. |
| Control plane and host agent | Trusted. Root-equivalent. |
| Edge proxy | Trusted, with minimal privilege. |
| API callers | Untrusted. They can do anything inside their own organization. |
| Code inside a sandbox | Hostile. |
The boundaries that must hold:
| Boundary | What must hold |
|---|---|
| Sandbox to host | Code in a sandbox cannot reach the host or another sandbox. |
| Tenant to tenant | One organization cannot see or affect another's sandboxes, files, secrets or traffic. |
| Public to sandbox | A preview URL reaches only the port it was signed for. |
Isolate with the hypervisor
- One VM per sandbox. Each sandbox runs its own kernel in its own
cloud-hypervisorprocess on KVM. - Seccomp. Every launch, restores included, passes
--seccomp true. - Unprivileged VMM. The host installer's fleet profile runs Cloud Hypervisor as the
platinum-vmmuser (PT_CH_UID) with every capability dropped (PT_CH_PRIVDROP=1). That user cannot read/etc/platinum/host.env. - Resource limits. Each VM runs in its own systemd scope under
pt-vms.slice, with memory, CPU and task limits. The slice caps all VMs together at 90% of host memory. - No TCP control path. The in-VM agent listens only on vsock port
18047.
If a host cannot apply per-VM cgroup limits, it starts VMs without them and logs itself as degraded.
PT_CH_UID_PER_VM=1 goes further and gives each VM launch a uid of its own, so one escaped VMM cannot open another sandbox's files.
Network isolation is covered in Networking.
Harden the host
infra/01-bootstrap.sh applies these at install.
- Kernel command line.
nosmtandlockdown=integrity, applied on the installer's reboot. - Sysctls.
/etc/sysctl.d/99-platinum-kspp.conf, includingkernel.kptr_restrict=2,kernel.unprivileged_bpf_disabled=1andkernel.yama.ptrace_scope=2. - No core dumps or swap for the host agent. Its unit sets
LimitCORE=0andMemorySwapMax=0.
The same sysctl file sets kernel.unprivileged_userns_clone=1 and vm.unprivileged_userfaultfd=1. On-demand memory restore under an unprivileged VMM needs the second one.
Authenticate callers and hosts
| Credential | Power |
|---|---|
API key (pt_live_…) | Its scopes, inside one organization. Stored as a SHA-256 hash. |
| Session | The user's role in the active organization. |
ADMIN_TOKEN | Super-admin across all organizations. |
Bootstrap token (htok_…) | Registers one host. |
| Host API key | Only the routes on the host allowlist (apps/api/src/hostConfinement.ts). |
- A host key is confined to a positive allowlist. A new route is closed to hosts until it is added there.
- With
PT_PRODUCTION=1, the control plane refuses to start with the developmentADMIN_TOKEN, and the edge proxy refuses to start with a developmentEDGE_HMAC_SECRET.
Reach a guest only through the host
The control plane reaches a remote guest through the host agent's :18048 proxy. It checks, in order: an nftables allowlist of the control plane's address, TLS with the host's certificate pinned on the control plane, a client certificate when PT_VSOCK_RPC_CLIENT_CA is set, and a bearer token. Details are in Networking.
Keep secrets out of the guest
An organization secret is never placed inside the sandbox. There is no environment variable, placeholder or file for it.
- In use. The host's egress proxy writes the value into an allowlisted request header, on outbound HTTPS to an allowlisted hostname (
apps/api/src/api/secrets.ts). The proxy terminates TLS only for that destination. - Allowlist. Hostnames are exact. Wildcards are refused.
- At rest. Values are encrypted with AES-256-GCM under a keyring (
PT_SECRETS_ACTIVE_KID,PT_SECRETS_KEY_<KID>). Changing a secret's allowlist or headers re-seals it. - To the host. Each delivery is sealed to one host's X25519 key with HKDF-SHA256 and AES-256-GCM (
apps/api/src/orgSecrets/seal.ts). - On the host. Values live in locked memory excluded from core dumps (
hosts/host-agent/cmd/host-agent/secretmem_linux.go).
Sign what crosses a boundary
| Artifact | Signed with |
|---|---|
| Preview token | HMAC-SHA256, EDGE_HMAC_SECRET |
| Route hint to the edge proxy | HMAC-SHA256, EDGE_HINT_SECRET |
| Snapshot | Ed25519, PT_SIG_PRIVATE_KEY |
Know the guarantees
- Exec and files have no network path. Nothing on the bridge or the internet can reach the in-VM agent.
- A host's API key reaches only the host routes. But every host holds its database URL, bucket credentials and
EDGE_HMAC_SECRET, so treat a compromised host as a compromised deployment. - Metadata endpoints are counted, not blocked, until you set
PT_EGRESS_GUARD_MODE.
See also: Networking · Components