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 Host header (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/orders beats /data if 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 →