Isolation and secrets

What runs where, what is wiped when a runtime stops, who can see your data, and how to hand a kernel an API key without ever pasting it into a notebook. This page describes the mechanisms; the security page and the privacy policy cover the commitments.

One machine per runtime

Every runtime is a dedicated, single-tenant virtual machine, provisioned fresh for that session on the provider, GPU and size you picked, and terminated when the session ends. The instance and its boot volume are never reused for another session or another user: the hypervisor is the isolation boundary, and no two kernels ever share a machine.

Inside the machine, your notebook kernel, the web terminal and SSH logins all run as the same unprivileged user (user, uid 1000), never as root, and start in /home/user/workspace.

No inbound ports

The machine accepts no inbound connection on the data path. The kernel, the terminal and sshd bind to localhost only, and the runtime opens one outbound encrypted tunnel to the platform's session gateway at boot; every byte between your browser (or your ssh client) and the kernel travels through that tunnel. Nothing on the VM listens on a public address. The only inbound the platform reserves for itself is the provisioning channel, restricted to the control plane's own source addresses.

The other direction is deliberately narrow too. A runtime cannot reach another runtime, the platform's database or cache, or control-plane internals. Its outbound access is the public internet, subject to the Environment's egress policy (open, blocked, or an allowlist of CIDRs), and the platform endpoint it needs to run.

A runtime holds no standing cloud credential either. The instance identity is the minimum the bootstrap needs (with IMDSv2 required); the credential that mounts your workspace is short-lived, scoped to exactly that workspace, and fetched at mount time rather than baked into any image. The kernel gateway's own authentication token is minted on the machine and never leaves it; the browser holds only a single-use, 60-second WebSocket ticket.

Each session also receives a session API key with a scope strictly narrower than a personal key: it can stream experiment metrics and fetch the session's own secrets, but cannot manage secrets, open SSH, or act as you elsewhere. It is revoked within a minute of the session stopping.

What survives a stop, and what does not

Stopping a runtime terminates the machine. The only durable thing attached to it is the workspace filesystem mounted at /home/user/workspace (~/workspace).

LocationAfter stop
~/workspace (files, notebooks, datasets, checkpoints)Kept. Your workspace filesystem, shared by every runtime of the workspace.
~/workspace/.ns-cache (pip cache, Hugging Face cache, per-Environment pip installs)Kept. Platform-managed, hidden from the Files page; see Packages.
The rest of /home/user (~/.cache, ~/.local, ~/.vscode-server, ~/.config, git credentials)Wiped.
/tmp, /scratch (local NVMe)Wiped. Scratch is the fast local disk for temporary data.
Changes to the image (sudo pip, apt-get, edits under /opt, /etc, /usr)Wiped. Put them in the Environment to keep them.
The VM, its boot volume, and every secret value it was handedDestroyed.

Data at rest on the platform side is encrypted with platform-managed keys: the workspace filesystem's object store, the database, and the secrets store.

Who can see your data

Access is enforced by the API on every request, keyed to the identity in your session token or API key, never to anything in a request body.

  • Workspace members see the workspace's notebooks, files, environments, experiment runs and runtimes, according to their role. Viewers read; editors and owners change things. Membership is the only way to write into a workspace.
  • Share grants give one named person view and fork/copy access to a single file, notebook, run or environment without adding them to the workspace. Every recipient read is brokered by the API: no storage credential, mount or filesystem path is ever issued to the recipient, a shared file never appears on their runtime, and a copy lands in their workspace under their quota, never touching the source. Revoking a grant makes the next read indistinguishable from "never shared".
  • Public links for files, notebooks and runs resolve an unguessable token to a read-only view. They are revocable, can carry an expiry, are rate-limited per IP, and attribute only the sharer's display name, never an email or account id.
  • Shared environments are redacted for anyone but the owner: the recipe shape is visible, environment-variable values are blanked, and build error text is withheld. A recipient who edits gets a fork with a fresh id; the original is never modified.
  • Someone who is not a member and holds no grant gets the same "not found" answer as for an object that does not exist, so a URL leaks nothing about what it points at.

Secrets: the place for an API key

A Hugging Face token, an OpenAI key, a private index credential. The wrong places for it are an environment spec (it would be shared, forked, and rendered into a build), a notebook cell (it would be saved, shared, and committed), and the image (it would be pulled by every runtime). The right place is Workspace Secrets: Settings → Secrets, once secrets are enabled for your deployment.

  • A secret is a name (^[A-Z][A-Z0-9_]{0,63}$) and a value, at workspace scope. The value is written to the platform's managed secrets store and never comes back: no API response, log line, database column or process argument ever carries it, and replacing a value never reveals the old one. Listing shows names and a non-secret hint.
  • Managing secrets is human-only and membership-gated: creating, replacing and deleting need an editor or owner signed in through the browser. A kernel's session key cannot enumerate or change the store.
  • An Environment references secrets by name (runtime_secret_names, and build_secret_names for builds). Names are ordinary, non-secret handles: they are visible to share recipients, copied verbatim on fork, and never rendered into the Dockerfile. This is what makes a shared environment safe to launch: the names resolve against the launching workspace's store, never the author's.
  • At launch, the runtime is handed only a pointer. After boot, the machine fetches the values itself over the tunnel, authenticated by its own session key, and writes them to a 0600 file on its ephemeral disk that the kernel and terminal source in-process. Values reach os.environ; ps shows only static command text; nothing rides the provisioning payload or a log.
  • A referenced name that does not exist in your workspace never fails the launch: the session starts without it, the notebook nudges you toward Settings → Secrets, and NS_USER_SECRETS_MISSING lists what was missing.

In a cell:

import os
import neural_studio as ns

token = ns.secrets.get("HF_TOKEN")   # a clear error if the name is not referenced or not defined
os.environ["HF_TOKEN"]               # the same value; every referenced secret is an env var

Build-time secrets (a private pip index, a git token for a post_install step) follow the same by-name pattern and reach the build only as BuildKit secret mounts on the pip install and post-install steps: tmpfs inside that one RUN, never an image layer, never an ENV, never command text. A missing build secret fails the build, deliberately the opposite of the runtime path.

The share-time secret scanner

Mistakes happen, so every sharing surface scans what it is about to expose. Before an environment is shared or made more visible, before a file or notebook grant is created, and before a public link is minted, the content is checked for credential-shaped strings: cloud key ids, sk--style API keys, GitHub, Slack, Hugging Face and Google tokens, private-key headers, webhook URLs, user:credential@ URLs, and an entropy heuristic for secret-looking environment variables.

If it finds something, the share is refused with the findings listed, and you can either remove the secret or confirm and proceed. A finding shows at most the first four characters of the match, never the value. Environment saves report the same findings as advisory warnings. The scanner is best-effort: it is a safety net, not a guarantee, and it never blocks an operation by failing.