Skip to content

Railway hosting

Self-hosting is the default model, but not everyone wants to run their own box. Railway hosting is Subspace’s managed offering: a real, dedicated instance per tenant, run by a fleet operator instead of by you, without giving up the trust properties that make Subspace worth self-hosting in the first place. It’s the same server, the same embedded Postgres, the same data model, just running on infrastructure someone else watches.

Each tenant gets its own tenant cell: one Railway service, one volume-backed instance, running the same production image you’d run yourself. There’s no shared database, no shared process, no row-level tenant isolation to get subtly wrong. A tenant’s data lives in a Postgres cluster only that tenant’s cell process can reach, which is the same isolation model a self-hosted box gives you, just provisioned on demand instead of by hand.

A fleet is only a managed offering if operating it is cheap, so the control plane ships alongside the fleet rather than as an afterthought:

  • Tenant registry: one row per tenant, tracking the Railway service, the volume, the domain, the plan, and the deployed version.
  • Idempotent provisioning: a new tenant is created through Railway’s API from a template, a fresh service, a fresh volume, SUBSPACE_PROFILE=cloud, a claim token, and a KMS-wrapped key reference. A Railway-generated domain is available immediately; a custom slug.subspace.app subdomain attaches asynchronously (certificate issuance is not instant). Provisioning is zero-touch: request a tenant, get a claim link.
  • Fleet operations: staged upgrades roll out to a canary tenant first, then cohorts, gated on workflow replay-compatibility checks so an in-flight agent run never gets stranded mid-upgrade by a bundle change. A Postgres major-version bump rides the same pg_upgrade orchestration a self-hosted instance uses, forced through a pre-upgrade backup first.
  • Monitoring: every cell exposes a status endpoint (version, migration state, backup freshness, disk usage, queue depth), polled by the control plane so a silently-stopped backup job is caught by monitoring, not by needing a restore.

Cloud hosting changes the right trust story for anything that executes on a machine: terminals, coding agents, os.exec, Jupyter kernels, and macOS automation don’t run on the tenant cell in the cloud. They run on your paired device, the same laptop or desktop you’d run Subspace on yourself, with the cell acting as the durable coordinator rather than the executor.

This is the authorization inversion that makes hosted execution safe: a cloud process holding your shell is a different, much larger blast radius than a cloud process holding your notes. So the execution capability moves to the device you already trust, and the cell only ever dispatches a job to it.

  • A durable job (queued, offered, awaiting device approval, running, and a terminal state including succeeded, failed, cancelled, expired, or unknown) is the unit of work between the cell and your device. unknown matters as much as the success states: if a device’s acknowledgment of a non-idempotent command is lost, the job goes to unknown rather than being silently retried, so a command that might have already run never runs twice by accident.
  • Every dispatch passes a seven-point authorization check: the run’s tenant matches the target tenant, the run’s owner is an active member, the target device belongs to that same owner (an admin’s elevated status does not satisfy this), the device has advertised the capability the job needs, the agent definition grants that capability, cloud policy allows it, and your device-local consent policy (ask every time, allow a signed action, or deny while locked) allows it too.
  • Terminals and coding agents keep running on your device even if the socket to the cell drops, including Railway’s periodic WebSocket rotation described below; the local process survives the disconnect and a reconnect resumes streaming from where it left off, rather than the job dying with the socket.
  • Server-host execution (running these same capabilities on the tenant cell itself) stays available, but only in local, self-hosted mode. On a hosted tenant, it simply isn’t an option, by design, not by policy toggle.

This is the same reason coding agent dispatch already distinguishes where code actually executes: hosted Subspace pushes that distinction all the way through the product, not just for agents.

A tenant cell’s data lives on its Railway volume at $SUBSPACE_HOME, exactly the home directory layout a self-hosted instance uses. On top of that:

  • Per-tenant encryption key escrow: each tenant has its own data-encryption key. The control plane escrows only a KMS-wrapped copy of it, never the plaintext key, so a control-plane compromise alone can’t decrypt tenant data.
  • WAL-G and blob backups: the same continuous WAL archiving and blob replication described in backup and restore run per tenant, writing to a per-tenant storage prefix.

A tenant cell is, unlike a self-hosted instance, reachable from the public internet by design, so it runs under a dedicated cloud capability profile (SUBSPACE_PROFILE=cloud) that closes the gaps a loopback-only deployment doesn’t need to think about: claim-token provisioning is mandatory rather than optional, internal broker and workflow endpoints bind on a separate loopback-only listener that never rides the public port, and the profile disables every capability that only makes sense with a trusted local operator at the keyboard, server-host os.exec, container execution, server-host PTY spawn, Jupyter kernel spawn, and local MLOps launchers, in favor of the device execution lane above.

All four clients, web, desktop, mobile, and the browser extension, pair against a hosted, claimed tenant the same way they pair against a self-hosted instance. Because Railway recycles public WebSocket connections roughly every 15 minutes and doesn’t offer sticky sessions, every satellite socket, the desktop PTY relay, the extension’s job queue, mobile, proactively reconnects a few minutes ahead of that rotation, with jitter so a fleet’s sockets don’t all reconnect in the same instant, and resumes from a cursor rather than losing anything mid-stream. A forced reconnect on a hosted tenant should never be visible as lost terminal output or a dropped job.

Google OAuth restricted-scope verification

Section titled “Google OAuth restricted-scope verification”

Gmail and Google Calendar connectors need Google’s restricted-scope verification, a security assessment separate from ordinary OAuth app review, before mainstream users can connect their accounts on a hosted tenant. Because a hosted deployment serves a public origin (unlike a self-hosted instance, where each operator registers their own OAuth credentials), the verification and the redirect-URI design are centralized: Google doesn’t allow wildcard redirect URIs, so the control plane runs one registered OAuth callback and relays the result to the correct tenant rather than each tenant subdomain registering its own.

Hosted tenants run under plan-based quota enforcement wired to the control plane (suspension and hard caps on overage), with LLM usage metered at the same broker seam that already mediates every model call, so usage-based limits don’t require a second accounting path. Documented recovery-point and recovery-time objectives, published data-retention and deletion policies, and supply-chain practices (image signing, SBOM, vulnerability scanning) round out what makes this a sellable managed service rather than just “self-hosting, but we run the box.”