10.4 Editing config safely (PUT /services/raw, dry-build, hot-swap)

On a running node you edit a tenant through the services self-config API, not by hand-editing files and restarting. It validates, dry-builds, and hot-swaps atomically — an invalid edit never touches the running tenant.

The services mount

{ "path": "/services", "service": "services",
  "config": { "access": { "read": "all", "write": "A" } } }

Protect it tightly — it's the tenant's control surface, and only operators may change authorization config (5.0). Lock writes with write: "A" and set the tenant's operatorRoles accordingly.

Reading and writing the config

Request Behavior
GET /services/catalogue Available service types with their config schemas
GET /services/catalogues Registered external catalogues and whether the operator permits their hosts
GET /services/catalogue/available Built-in and registered services/adapters available to install
POST /services/catalogue/install Fetch, hash-verify, compile-check, and deploy one catalogue item (8.11)
GET /services/services Mount summary {mounts: [{path, service, access}]}
GET /services/infras Operator-managed backends this tenant may reference, with values and secrets redacted (10.7)
GET /services/raw The full tenant config + an ETag (version)
PUT /services/raw Replace the config: parse → dry-build every mount → persist → hot-swap atomically → 204 + new ETag

How a write is made safe

PUT /services/raw doesn't just save bytes. It:

  1. Parses the new config.
  2. Dry-builds the whole tenant — every mount is validated as if loaded.
  3. Only if that succeeds, persists and hot-swaps the live tenant atomically.

An invalid config is rejected with 400 and the reasons, and the running tenant is left untouched. You cannot break a live tenant with a bad edit — the worst case is the edit is refused.

Optimistic concurrency with If-Match

To avoid clobbering a concurrent change, send the ETag you read:

PUT /services/raw
If-Match: "<etag from GET /services/raw>"

A mismatch is 409 conflict — someone else changed the config since you read it. Re-GET, reapply your change, retry. This is the standard read-modify-write loop.

Secrets are write-only

GET /services/raw reads back secrets as the marker "<secret>"auth.jwtSecret and everything under a top-level secrets block. On PUT, a "<secret>" marker is replaced with the stored value, so a normal GET → edit → PUT round-trip never destroys a secret. To rotate a secret, supply a real value in its place. (A marker with no stored counterpart is a 400.) See 10.6.

File edits vs. API edits

  • Editing tenants/<name>.json on disk takes effect only on a reload (restart, or any PUT /services/raw round-trip).
  • Editing through PUT /services/raw validates and hot-swaps with no restart — the preferred path on a running server.

For a versioned or multi-file change, rs2 pull mirrors the tenant config and every discovered spec subtree into a local rs2/ directory; edit it in git and use rs2 push. Push uses the same If-Match/If-None-Match guarantees for config and specs, aborting on a remote edit instead of clobbering it. Code bytes and application data are intentionally outside that mirror.

Next: deploying and rotating custom code on a running node.


Previous: 10.3 Logging & observability · Manual home · Next: 10.5 Deploying & rotating code →