1.1 What Restspace v2 is
Restspace v2 (RS2) is a runtime for building backends out of small, composable services — without writing a traditional web server. You describe what you want as configuration, mount it at a URL path, and the runtime handles the HTTP, the routing, the storage, the auth, and the sandboxing for you.
The whole system rests on one idea:
A service is a function on an HTTP message.
A request comes in as a message — a method, a URL, headers, and a body. A service transforms it and returns another message — a status, headers, and a body. That is the entire contract. A file store is such a function. A schema-validated database is such a function. A piece of your own JavaScript is such a function. Because they all share the same shape, they all compose.
Why that matters
In a conventional backend you wire up a framework, write route handlers, pull in a database client, bolt on auth middleware, add a caching layer, and so on — each with its own conventions. RS2 collapses that into a uniform model:
- Everything is reachable over HTTP at a path you choose. There is no hidden internal API; the way your own code talks to the data store is the same way a browser does.
- Cross-cutting concerns live in the runtime, not in each service. Authentication, role checks, rate limits, caching headers, idempotency, retries, tracing, and CORS are applied by the host around every service, so you configure them once instead of re-implementing them everywhere.
- Custom code is a first-class citizen, but always sandboxed. You can drop in a JavaScript module (or a compiled WebAssembly component) and mount it like any built-in service. It runs under hard resource limits and can only reach the capabilities you explicitly grant it. There is no unsandboxed path.
What's in the box
RS2 ships a handful of prebuilt services that cover the common backend needs. You mount the ones you want:
| Service | What it does |
|---|---|
file |
Streamed file storage; also serves static sites / SPAs |
data |
Schema-validated JSON record store |
query |
Stored, parameterized queries over your data |
template |
Renders stored JSX templates to HTML |
pipeline |
Composes other services into multi-step flows |
wrapper |
Runs one fixed inline pipeline as a facade over a mount |
auth |
Login, sessions, and role-based access control |
proxy |
Forwards to a fixed external API with host-injected credentials |
sms |
Stable send/status API over a swappable provider adapter |
services |
The self-configuration API (edit the running tenant) |
log |
Reads back the node's structured logs, scoped to your tenant |
When the prebuilt services aren't enough, you write a custom service — a single JavaScript file, or a Rust/WebAssembly component — and deploy it to the same runtime. It gets the same HTTP front door, the same auth and limits, and talks to the other services through granted capabilities.
The shape of a running system
A single RS2 node (the running process) hosts one or more tenants. Each tenant is an isolated world: its own mounts, its own data, its own users, its own config. Within a tenant you declare mounts — each one places a service at a URL prefix:
{
"mounts": [
{ "path": "/files", "service": "file", "config": { "access": "open" } },
{ "path": "/data", "service": "data",
"config": { "access": { "read": "all", "write": "A" }, "enforceSchema": true } },
{ "path": "/auth", "service": "auth",
"config": { "access": { "read": "authenticated", "invoke": "all" } } }
]
}
That config alone gives you a streamed file store at /files, a validated JSON
database at /data, and the auth endpoints at /auth — no code written. (Auth
also needs the tenant-level signing/user settings covered in Part 5.) Every
mount's access is explicit because an omitted policy fails closed.
From there you layer on queries, pipelines, and custom services as your backend
grows.
The next section unpacks the mental model — tenants, mounts, services, and pipelines — in a bit more detail.