10.7 Infras, bootstrap, and production boundaries

Some configuration belongs to the tenant; some belongs to whoever operates the machine. Infras keep backend credentials and locked settings on the operator side while still letting a tenant choose an approved backend by name.

Operator-managed infras

serverConfig.infrasPath (default ./infras.json) points to a map of named partial adapter configs:

{
  "orders-db": {
    "adapter": "code:postgres-data@<version>",
    "description": "Production orders database",
    "allowedTenants": ["acme"],
    "config": { "host": "db.internal", "password": "…" },
    "requires": ["database"],
    "infraOnly": ["host"]
  }
}

A tenant uses it without seeing those values:

"store": { "adapter": "infra:orders-db", "database": "acme_orders" }

At config time the runtime verifies the tenant may use the infra, requires its open fields, rejects tenant attempts to set infraOnly fields, overlays the operator config (operator values win), and then validates the real adapter. A bad reference is rejected during PUT /services/raw, not deferred until a request.

GET /services/infras lists only the infras available to that tenant, their descriptions, required/locked field names, and which keys the operator provides. It never returns values. The same mechanism supplies credential strategies to a proxy or httpOut grant through inject: "infra:<name>".

Pipeline, query, and template authoring specs can use an independent specStore:

"specStore": { "adapter": "infra:spec-store", "root": "orders-pipelines" }

That keeps instructions on an operator-approved file backend without changing a query's execution store.adapter.

Reloading infras

Edit infras.json, then reload it without restarting:

Invoke-RestMethod -Method Post -Uri http://127.0.0.1:3100/admin/reload-infras `
  -Headers @{ Authorization = "Bearer $env:RS2_ADMIN_TOKEN" }

Set the node token through RS2_ADMIN_TOKEN (preferred) or serverConfig.adminToken. With neither, the endpoint is disabled (503). A successful reload swaps the infra set and causes tenants to rebuild on their next request. Removing an infra still in use intentionally makes those tenant builds fail until their config is repointed.

Seeding the first admin at startup

In single-tenant mode, bootstrapAdmin can seed one operator directly into the configured auth dataset:

"bootstrapAdmin": { "email": "admin@example.com" }

Supply the password through RS2_ADMIN_PASSWORD; RS2_ADMIN_EMAIL can override the email too. Set both credentials or neither. The tenant must already have auth.jwtSecret, and the seeded role A must be included in operatorRoles. Seeding is if absent: an existing record is never overwritten. With the standard durable data store, the admin therefore survives restart.

For a fresh no-auth node, rs2 auth init is the HTTP-only alternative (5.1 and Appendix E). Do that while bound to loopback, because the user store and control surface are briefly open during bootstrap.

The production network boundary

rs2-server speaks plain HTTP. In production, bind it to loopback and terminate TLS at Apache, nginx, or another reverse proxy. Preserve the original Host header — multi-tenant resolution depends on it. The repo's deploy/ directory contains a systemd unit, Apache template, installer, and Ubuntu build runbook.

The release installer runs the node as a dedicated system user, keeps config in /etc/rs2, data in /var/lib/rs2, and logs in /var/log/rs2; reinstalling upgrades the binary without overwriting existing config. Choose the JS release variant when the tenant uses JavaScript code, templates, or loadable adapters.

That completes operating a node. Part 11 covers bringing an existing v1 Restspace configuration across to RS2.


Previous: 10.6 Secrets handling · Manual home · Next: Part 11 — 11.1 What rs2 migrate carries over →