Skip to content

Your App VM ​

Some projects deploy to a full server instead of static files. This page explains how that server, your App VM, works: what runs there, what Stasho does on your behalf, and what you can check yourself.

Prerequisite: none, but Your data on-chain covers the encryption and verification model this page builds on.

The model in one paragraph ​

Your App VM is one machine that Stasho creates, runs and pays for on your behalf, on a confidential host: its disk is encrypted and only unlocked once the host proves it runs the expected image, which protects it from the node operator. Each server app you deploy to it runs in its own container, watched over by the supervisor: a small always-on program that installs each server app, restarts it if it crashes, and reports its state back to the dashboard. Your usage stats (live CPU and memory per server app) are encrypted to your wallet's key before they leave the VM. The VM is included: you pay nothing for it while it exists.

What's public, what's encrypted ​

Public by protocolEncrypted
IPv6 address, and the IPv4 host and port used for SSH (a NAT mapping)Your project names
Allocation: vCPU, RAM, diskYour usage stats: live CPU and memory per server app
VM image version and supervisor version
SSH public keys and fingerprints
Each server app's project ID and the hash of the bundle it runs

The public column above is provisioning detail that's already visible to anyone who reads the underlying on-chain messages or asks the host running your VM directly; none of it is a Stasho decision to disclose, and deleting the VM doesn't retroactively hide what was public while it ran. What stays encrypted is exactly what would otherwise leak your project identity or activity: the project's own name, and how hard it's actually working. See Your data on-chain for the same split applied to projects and deployments.

How code reaches the VM ​

Stasho holds the keys that unlock your VM and vouch for its supervisor, so you trust Stasho with it. The node operator, who runs the physical host, cannot read its memory or its disks: your VM is protected from the node operator.

Code reaches the VM by exactly one path: the supervisor, running on the VM itself, pulls your bundle from the public network, checks it, and runs it. There is no push path from our backend, and no remote-exec channel into your VM.

You talk to the Stasho backend, which publishes your deployment to a public network. Your App VM is a separate box: inside it, the supervisor independently pulls, verifies, and runs your bundle as your server app. There is no push path or remote-exec channel from the backend into the VM.You talk to the Stasho backend, which publishes your deployment to a public network. Your App VM is a separate box: inside it, the supervisor independently pulls, verifies, and runs your bundle as your server app. There is no push path or remote-exec channel from the backend into the VM.
The supervisor inside the VM pulls and runs your code. There's no push path in.

You can check what the supervisor runs. Before it runs a single line of your code, it works through a chain of checks, and every one of them is either an on-chain fact or a cryptographic proof, never a claim from Stasho:

  1. Resolve your deployment's on-chain storage reference and fetch the encrypted bundle from the storage network.
  2. Hash the fetched bytes and compare them against the hash your deployment recorded on-chain. A mismatch is rejected before anything is decrypted.
  3. Decrypt the bundle using the VM's own private key, a key nobody (not Stasho, not you) holds a copy of anywhere else. A wrong key or tampered ciphertext fails this step outright.
  4. Validate the unpacked manifest, including a check that the app was built for the exact Node.js major version the VM runs.
  5. Re-hash the unpacked application code against the hash recorded inside the bundle itself.
  6. Only after every step above passes does the supervisor start your app's container.

This is the same verify-what-runs principle described in Your data on-chain, applied to a running server instead of a static file. The exact wire format each step checks against is documented in the storage schema.

Filesystem layout ​

The supervisor and your server apps use a fixed set of paths on the VM, all on its encrypted state disk:

PathWhat's there
/var/lib/stasho/vm-keyThe VM's own private key, generated the first time it boots. Never leaves the VM.
/var/lib/stasho/state.jsonThe supervisor's own record of what it has already applied: assigned ports, deployed bundles, its own version.
/srv/stasho/apps/<projectId>/<deploymentId>/Your unpacked application code, one directory per deployment, mounted read-only into the container. The supervisor keeps the newest 3 per project and removes older ones.
/srv/stasho/apps/<projectId>/data/Your app's /data (see What your app can write).

Each server app runs as the container stasho-app-<projectId>.

Everyday operations ​

SSH into your VM with one of the keys you added when you created it (the App VM home lists them on the Access card), and use podman to inspect your app:

bash
# Watch a project's logs live
podman logs -f stasho-app-<projectId>

# Check whether it's currently running
podman ps --filter name=stasho-app-<projectId>

The supervisor owns these containers

The supervisor starts stasho-app-<projectId> fresh on every deploy. Anything you change by hand is gone the next time you push. Treat SSH access as a window for inspection and debugging, not a place to configure your app permanently: changes belong in your repo.

Ports and the firewall ​

The VM's firewall denies all inbound traffic by default. Only these are open:

PortWhat it's for
22SSH
80, 443Your apps, served over HTTPS on their default subdomains and custom domains
8443The public status endpoint (below)

Your apps listen on the VM's loopback interface only; a reverse proxy on the VM serves them on 80 and 443 for their domains, so an app's own port is never reachable from outside. The firewall also accepts ICMPv6, which is what makes IPv6 itself work.

The VM key ​

At first boot, the VM generates its own private key at /var/lib/stasho/vm-key and never sends it anywhere. It exists purely so the VM can decrypt bundles addressed to it.

Never copy this key off the VM, and never share it. Losing the VM (a move to a new host, a delete) is not the same as losing your deployments: every bundle you deploy is also encrypted to a key derived from your own wallet, so you can always recover your deployment history and app code with nothing but your wallet, even if the VM that ran it is long gone. See Recover your data.

What your app can write ​

Your server app runs as a Next.js app (standalone output) on one Node runtime. It can write to two places, and everything else on the VM is read-only:

  • /data (also available as process.env.DATA_DIR). Files here are kept on this VM across restarts and updates: a redeploy replaces the app container, not /data. It is not backed up and it does not move with the VM. When the VM is replaced or re-created, the files in /data are gone.
  • /tmp. It is memory-backed and cleared on every restart.

Keep durable data in a hosted database, not in /data. Treat /data as a local cache or scratch space that you can afford to lose.

When your VM moves ​

If the host your VM runs on stops responding, Stasho replaces the VM on a new host and redeploys your apps there. The timeline from a real replacement:

  1. A watchdog waits 15 minutes 30 seconds to confirm the host is really down.
  2. The VM then shows as moving to a new host. The replacement takes about 5 minutes to come up and be endorsed.
  3. Your apps are back about 10 minutes after the move starts, which is about 26 minutes after the host stopped.

Until the automatic redeploy lands, an app whose last bundle cannot be opened on the fresh VM reads "Waiting for redeploy". The old VM's /data is gone, so anything your app kept there has to be rebuilt or reloaded from your database.

You get one of two emails, sent to the address you gave when you created the VM, or to your sign-in email:

  • "Your App VM moved to a new host" is sent when the replacement is up and your apps are redeployed.
  • "Your App VM is down" is sent when a replacement failed after several attempts. The App VM home then offers "Create a new VM".

The status card on the App VM home shows the same states: running on a confidential host, moving to a new host (about 10 minutes), could not move after several attempts, and deleting.

Environment variables ​

Your project's environment variables travel inside the encrypted bundle itself, the same one the supervisor verifies and decrypts before running your app. There's no separate channel for them, and no way to change a running app's environment without redeploying: rotating a variable means redeploying. The next deploy bakes the new values into a new bundle, and the supervisor replaces the running container with one built from it.

Updates ​

Two different things update independently, on different schedules:

  • The supervisor updates itself in place. It checks a signed, on-chain registry, and when a genuinely newer, hash-verified release is available, it swaps itself in and restarts, without disturbing your running server apps.
  • The VM image (the OS, Node.js, the firewall rules) is fixed for the life of a VM. A new VM, or a VM moved to a new host, starts from the latest image.

Crashes ​

If your app keeps crashing, its container is restarted automatically, up to a point. Past that point, your deployment card in the dashboard shows:

App crashed on your App VM after N restarts. Check logs over SSH.

That message is deliberately generic. We don't ship your logs anywhere; they stay on the VM. SSH in and run podman logs -f stasho-app-<projectId> (see Everyday operations, above) to see what actually happened.

When your VM will not start ​

Creating an App VM takes about 6 minutes, and the App VM page shows each stage while it runs. If a stage fails, the page says "We could not prepare your VM." with one sentence naming the stage. When the failure happened before the VM existed, Try again reopens the setup with your SSH keys filled in; otherwise Delete clears it so you can create a new one. If preparing runs past 20 minutes, the page offers to delete the VM and start over.

The status endpoint ​

Port 8443 on your VM serves a small, public JSON document, no authentication, by design: it's what our health checks read, and anyone else can read it too. It exposes exactly:

  • The supervisor's own version, and a hash identifying this VM
  • For each deployed server app: its project ID, the hash of the bundle it's running, its current state (downloading, verifying, starting, serving, crashed, or verify-failed, which means the last bundle cannot be opened on this VM and a redeploy is on its way), its restart count, and its port
  • An encrypted blob of that server app's live CPU and memory use, sealed to your wallet's encryption key. The endpoint carries only the ciphertext; it never sees, and never serves, the plaintext numbers.

It never exposes environment variables, keys, logs, file paths, project names, or anything else about your account beyond what's already public on-chain. The dashboard's App VM home decrypts the stats blob in your browser to show live CPU and memory; if your encryption key is locked, it shows "Unlock to view stats" instead.

Deleting your VM ​

Delete App VM on the App VM home removes the VM for good: every server app on it stops, its address is gone, and the files in /data and /tmp go with it. A new App VM gets a different address. Your deployments stay on-chain: when you create a new VM, Stasho redeploys your server apps on it once it comes up.