1.2 The mental model: tenants, mounts, services, pipelines
Four nouns describe almost everything you do with RS2. Once they click, the rest of the manual is detail. We'll go from the outside in: node → tenant → mount → service, then add pipelines as the way services combine.
Node
A node is the running process — what you start with rs2 dev or
rs2-server. It listens on an address, holds operator-level settings (where
files go, where logs go), and hosts one or more tenants. You rarely think about
the node when building; it matters when you operate (Part 10).
Tenant
A tenant is a self-contained application: its own mounts, data, users, and configuration, fully isolated from every other tenant on the node. One node can serve a single tenant or many.
- In single-tenant mode every request belongs to the one configured tenant.
- In multi-tenant mode the tenant is resolved from the request's
Hostheader (an explicit domain map, then{tenant}.{maindomain}subdomains).
Everything a tenant is lives in its configuration plus its stored data. That
config is a single JSON document (tenants/<name>.json, or edited live through
the services API). This is a deliberate design point we return to in
1.3 and 3.6:
a tenant is declarative, so it can be version-controlled, diffed, and rebuilt.
Mount
A mount places one service at a URL prefix within a tenant. It is the unit of configuration you'll write most:
{ "path": "/data", "service": "data",
"config": { "access": { "read": "all", "write": "A" },
"enforceSchema": true } }
path— the URL prefix this service answers on. Routing is longest-prefix on segment boundaries, so/data/ordersbeats/dataif both are mounted.service— which service to run: a prebuilt name (file,data,pipeline,wrapper,query,template,auth,proxy,sms,services,log) or a custom-code reference (code:<name>@<version>).config— service-specific settings, plus a set of standard keys that work on any mount:access(who may call it; absent means nobody),retry(retry policy),caching(cache headers),schedule, and agent-surface metadata (x-agent,description, …).
A key idea: a service's variants are configuration, not separate services.
There is no "static-site service" — there's a file mount with static-site
config. There is no "schema database" and "plain database" — there's a data
mount with enforceSchema on or off. You shape behavior through config, not by
choosing among near-duplicate services.
Service
A service is the function-on-a-message itself (see 1.1). It receives the incoming message and returns a response. Services come in two origins:
- Prebuilt — the host-native services listed above, written for you.
- Custom — your own JavaScript or WebAssembly, sandboxed and mounted just like a prebuilt one.
Many services share a conversation shape called the store pattern: file
and data both behave like a tree of containers and resources you GET, PUT,
POST, and DELETE in a uniform way. Learn that shape once (Part 4) and you
can drive any store — including future ones — with the same client code.
Pipeline
A pipeline is how services combine. Because every service speaks the same message contract, the output of one can be the input of the next. A pipeline is a small declared flow — a sequence (or parallel fan-out, or conditional branch) of steps, where each step typically calls another mount:
[
"GET /data/orders/${id} :$order",
"if ($order.status == 'open') POST /payments/charge",
{ "receipt": "$order.id", "charged": "$_ok" }
]
This terse string DSL — one line per step — is the most readable way to
author a pipeline, and is carried over from Restspace v1. The runtime accepts it
and canonicalizes it into an equivalent typed spec (a JSON object with a
mode and an array of steps), which is what it stores, reads back, and
introspects. You author in either form; Part 7 covers both in full.
Crucially, a pipeline step's call re-enters the full runtime — the same routing, auth, limits, and idempotency that apply to an external request apply to an internal hop. A pipeline isn't a backdoor around your rules; it's a first-class caller that obeys them. Pipelines get a full treatment in Part 7.
Putting it together
node
└── tenant "acme"
├── mount /files → file service
├── mount /data → data service (schema-validated)
├── mount /auth → auth service
├── mount /q → query service ─┐ read your data
├── mount /orders → pipeline service ─┘ compose the above
└── mount /pay → code:stripe-wrapper@… (your sandboxed JS)
Requests arrive at the node, get routed to a tenant, matched to a mount by longest prefix, and handed to that mount's service — with auth, limits, caching, and tracing applied by the host on the way in and out. That single path is the backbone of everything that follows.
← Previous: 1.1 What Restspace v2 is · Manual home · Next: 1.3 What you can build →