PORTFORK

Back to blog

The Portfork isolation architecture

How Portfork Cloud runs each app in its own hardware-isolated microVM, keeps tenants separated, and injects secrets at runtime. The boundaries, the threat model, and the trade-offs we made.

By
Portfork
Published
Tagged
engineering architecture security

When we say each app runs isolated, we mean a specific thing. We mean a real virtualization boundary — a per-app microVM — not a shared container runtime where a kernel bug or a noisy neighbor reaches across the fence.

This post is the honest description of how that is achieved: what the boundary is, what crosses it, what an attacker would have to do to break it, and which trade-offs we chose along the way.

The isolation invariant

Two sentences. The rest of this post explains them.

Every app on Portfork Cloud runs in its own hardware-isolated microVM. One tenant’s apps can never see or reach another’s.

The boundary is the microVM, not a namespace or a cgroup. Each app gets its own guest kernel and its own slice of virtualized hardware. The blast radius of a compromised app is that app’s microVM and nothing else.

What runs where

Three things make up a hosted app on Portfork Cloud.

The app artifact. The same artifact you run locally on Open Source Portfork. On Cloud it is scheduled into a fresh microVM, given a stable URL, and kept reachable around the clock — even with your computer off.

The microVM. A hardware-isolated guest with its own kernel. It wakes on the first request (lazy-start), serves traffic, and scales to zero when idle (idle-reap). Nothing is co-tenanted inside it.

The control plane. The managed orchestration layer that schedules microVMs, routes URLs, injects secrets, and handles billing and account lifecycle. It is the part of Portfork that is commercial; the runtime inside the microVM is the open-source engine.

The flow, end to end

flowchart LR
    A[Request to<br/>app URL] --> B[(Portfork Cloud<br/>control plane)]
    B -- lazy-start --> C[Per-app<br/>microVM]
    C -- serve --> D[Response]
    C -- idle-reap --> E[Scaled to zero]
    style C fill:#1c1e1b,stroke:#484d47,color:#efefe7

The control plane is the only shared component, and it is deliberately narrow: it schedules and routes. It does not run your code. Your code runs inside the microVM, behind a virtualization boundary, scoped to your account.

Per-tenant separation

Your nodes and apps are scoped to your account. Separation is enforced at the platform, not by convention:

  • No shared runtime. Each app has its own microVM. There is no shared interpreter, no shared process tree, no shared filesystem between tenants.
  • Network scoping. An app can reach the network paths you grant it and nothing belonging to another tenant. Cross-tenant reachability is not a setting you can flip on by accident; it does not exist.
  • Account-scoped URLs. Stable URLs route to your microVMs only.

Secrets

App secrets are stored encrypted in AWS and injected into the microVM at runtime. They are never baked into the artifact and never written to logs. When the app scales to zero, the running secret material goes with the microVM; the next start re-injects from the encrypted store.

You also bring your own model access. An API key or a claude setup-token subscription token is handled as a secret on the same path: encrypted at rest, injected at runtime, never in an artifact or a log. There is no inference markup — the model call goes from your app to your provider directly.

What we did not isolate — and why

Two things are deliberately shared, and we documented the choice.

  • The control plane. Scheduling, routing, and billing run as a multi-tenant service. Pushing those into per-tenant infrastructure would multiply operational cost for a marginal security gain — the control plane never executes your code. If your threat model needs the whole data plane in-house, Open Source Portfork runs the same apps on your own hardware.
  • Cold-start timing. A lazy-started app takes longer to answer its first request than a warm one. We accept that an observer can tell a cold start from a warm one. Padding every start to a uniform latency would make the product slower for everyone to hide very little.

The threat model, briefly

What does Portfork’s design protect against?

  • A compromised app. Code execution inside one microVM stays inside that microVM. It cannot reach another tenant’s apps or the host.
  • A noisy neighbor. Resource contention is bounded by the microVM’s allocation. One tenant’s spike does not starve another’s app.
  • Secret leakage through artifacts or logs. Secrets are injected at runtime, never baked in, never logged.

What does it not protect against?

  • A vulnerability in your own app. Portfork isolates apps from each other; it does not patch your code. Treat your app the way you treat any internet-facing service.
  • Loss of your own model credentials. You bring your own model access. If your provider key leaks from somewhere outside Portfork, that is outside our boundary.

Why we built it this way

There is a simpler model that many hosts use: pack many tenants into shared containers on a shared kernel and rely on namespaces for separation. That model is cheaper to operate. It also means a single kernel bug is a cross-tenant incident.

We took the other path. Portfork’s value is running a lot of small, infrequently used apps cheaply and safely. Scale-to-zero makes the economics work; per-app microVMs make the isolation real. We could not in good conscience ship a host we ourselves would not run untrusted agent-built code on.

So that is the design.

See it for yourself

The runtime that hosts apps locally is the same engine that runs inside the microVM on Cloud, and its source is on GitHub today. If you find a problem, our security disclosure policy is written down and we honour it.

We will publish a longer threat-model document with GA. This post is the headline; the details follow.

Try Portfork today. The Open Source edition is free.

Install in under a minute. Host the apps your agents build, on your own machine or on Portfork Cloud.