RFDs/0022

Operator API

State
discussion
Authors
Darwin D Wu
Discussion
renga-dcim/renga#176
Labels
rengaapioperationsorganizationssecuritysoftware
Implemented
0%Checklist

Goal

Let whoever runs a Renga deployment manage it from their own automation: create organizations and invite their owners, set limits and locked settings, manage deployment identity providers, suspend an organization, read usage, and move an organization in or out. Today an operator can do some of this only with release commands on the server, and some of it not at all.

The API is for operating a deployment, not for using it. Except for an explicitly authorized transfer, it never reads or changes an organization’s inventory. It never signs in as a person or returns a reusable application credential or user session; an authorized export may return the short-lived download capability defined below. Organization owners keep control of organization-owned configuration, such as their own identity providers. A token with transfer authority is consequently as sensitive as tenant-owner access, not an ordinary provisioning credential.

Operator tokens

An operator token authenticates the API. It belongs to the deployment, not to an organization or a user.

  • An operator creates a token with a release command, renga operator-tokens create --name <name> --scopes <scopes>, which prints it once. Renga stores only a hash, as it does for RFD 3 intake keys.

  • A token has an immutable ID, a unique display name, scopes, an optional expiry, and an optional list of source addresses it may be used from.

  • Tokens are revoked with a release command. Any authenticated token can also revoke itself, but cannot list, create, or revoke another token through the API.

  • Without any token the API is off; no request reaches it.

Authentication checks the stored hash, expiry, revocation, and source address on every request through a credential path separate from user and agent authentication. The API requires HTTPS outside development. Source restrictions use the connection’s peer address unless the deployment explicitly names trusted reverse proxies; forwarding headers are accepted only from those proxies and are resolved with the same trusted-hop rules as the rest of the deployment.

Scopes keep a token to what its automation needs. A write scope includes the matching read operation needed to obtain versions and observe results:

Scope Allows

organizations:read

Listing organizations, their status, and their memberships

organizations:write

Creating, renaming, suspending, resuming, scheduling or cancelling deletion of organizations, and inviting owners

policy:read

Reading effective policy and its version

policy:write

Reading and setting limits, quotas, locked settings, and feature entitlements

providers:read

Reading deployment provider configuration with secrets omitted

providers:write

Reading, testing, and managing deployment identity providers

usage:read

Reading usage counters

transfers:read

Reading transfer status, but not downloading an export

transfers:write

Starting and cancelling transfers, downloading exports, and importing archives; this grants explicit access to transferred inventory

Self-revocation requires authentication but no additional scope. Every other operation requires the scope named above; having one scope never implies an unrelated scope.

Listener

The API is served under /operator/v1 with an OpenAPI description, like the agent API, but always on its own listener and port, never on the listener that serves people and agents. The port may be public: a control plane that runs in another region or at another provider has no private network to a deployment, and this RFD does not assume one. Keeping the API on its own listener lets an operator apply rules to the whole port that a path prefix on a shared port cannot carry, such as a client certificate requirement, a firewall rule, or a separate reverse proxy.

The listener has two settings:

  • Mutual TLS. The operator configures a certificate authority, and the listener completes a TLS handshake only with a client that presents a certificate from it. A token presented without a certificate is refused before it is read. This is recommended whenever the port is reachable from the internet, because a leaked token alone is then not enough to call the API, and it works across providers without any network between them. Certificates are checked against the configured authority and its revocation data, and never against the public web PKI.

  • Bind address. By default the listener binds to the same address as the main endpoint. An operator with a private network may bind it to a private address instead; the deployment must also restrict network exposure to its operator network, because a second port is not an access boundary on its own.

Tokens, scopes, and source address lists apply on every request regardless of these settings.

Organizations

  • Create an organization with a name and slug. It starts with no members.

  • Invite an owner by email address. The invitation is a purpose-specific, expiring, revocable, single-use secret bound to the organization and intended email. Acceptance atomically verifies that the signed-in or newly registered account owns that email before granting ownership. Closed registration and provider-only sign-in still apply: the invitation does not create a password or bypass the deployment’s allowed sign-in methods. The operator receives neither the invitation nor a sign-in link. organizations:write may invite an owner to an existing organization, so it can deliberately grant full tenant access to an address the operator controls; deployments that need provisioning without that authority use a token without this scope.

  • Suspend an organization. Its members cannot open it, its agents' intake is refused with 403, and its jobs are not dispatched. The 403 deliberately replaces intake’s current 401 for a valid credential belonging to a disabled organization. Suspension is checked on every authenticated HTTP request, LiveView or Channel event, data delivery, job lease and renewal, and agent request. Renga disconnects existing sockets; terminal termination follows RFD 13, including PTY process-group termination. It stops new dispatch and authorization renewal and requests cooperative cancellation of active leases. This does not imply that an external effect was undone. Started work retains RFD 9 and RFD 14 outcome and uncertainty semantics and durable execution evidence; a rejected or stale report cannot authorize more execution or publish an ordinary successful result. Nothing is deleted. Resume undoes the access block, but never automatically reruns cancelled or uncertain work.

  • Schedule deletion. The organization is suspended at once and deleted after 30 days unless the deletion is cancelled. The pending-deletion record holds one deadline; repeating the request returns that record without extending it. An operator with organizations:write may cancel it, and resuming an organization atomically cancels pending deletion. Until purge begins, an owner can sign in to a restricted deletion page to cancel through the normal owner authorization path or request and download an export, without access to the ordinary organization UI. Owners are notified when deletion is scheduled or cancelled.

  • Read organizations, their status, their members' email addresses and roles, and whether support access is on and until when, so automation can confirm an owner accepted or granted access.

Policy

An operator sets an organization’s policy as one document, replacing it whole:

  • Limits and quotas: RFD 21's named request limits, database time budgets, work limits, and capacity quotas. The policy document and renga limits commands read and write the same persisted organization overrides. They do not replace RFD 21's admission counters, transactional quota ledger, reservations, or defined exhaustion behavior.

  • Locked settings: any RFD 19 organization security setting fixed to a value, except Allow support access, which no policy can set, default, or lock (Support access). A locked setting shows as read-only in organization settings, marked as set by the deployment, and owners cannot change it.

  • Entitlements: which optional features the organization may turn on, such as organization identity providers, terminals (RFD 13), and VM operations (RFD 14). An entitlement only allows a feature; owners still decide whether to use it. Removing an entitlement turns the feature off and keeps its configuration for when it is allowed again. The effective entitlement is checked before every request and authorization renewal. Removing one applies suspension’s revocation rules to that feature: it disconnects affected channels, prevents new work and renewals, requests cooperative cancellation, and preserves execution evidence and uncertainty. Restoring it does not resume cancelled or uncertain work.

SCIM remains available under RFD 16's first-release rules rather than becoming entitlement-gated. If an entitlement disables an organization provider, Renga uses RFD 15's provider-disable behavior and completes RFD 16's source-bound membership and grant reductions before acknowledging the change. Ordinary RFD 19 security-setting changes keep RFD 19's own effective-time rules; they are not feature withdrawal and do not terminate running work.

Deployment-wide defaults for all three come from the environment, and an organization’s policy overrides them. A setting that neither locks stays the owners' choice.

Deployment providers

The API exposes RFD 15's existing deployment-provider lifecycle; it does not create a second provider store or mutation contract. Stored configuration, encrypted secrets, immutable provider identity, authoritative revisions, fail-closed cache freshness, pending revisions, and purpose-bound test callbacks all follow RFD 15. Test and activation remain one atomic transition for the exact pending revision. Responses omit saved secrets entirely.

Environment providers remain read-only. They override a stored deployment provider only when RFD 15 permits the collision because all immutable properties match; every incompatible collision refuses boot. Ordinary API and release-command changes use RFD 15's serialized guard that preserves a usable deployment sign-in method. They add no per-user guarantee and preserve the distinction between sign-in, RFD 19's account re-check, and RFD 13 terminal step-up. RFD 15's recover-local escape hatch remains release-command-only and is never exposed to a bearer token. Organization providers are not reachable through this API; they belong to their organization’s owners.

Usage

The API reports each organization’s usage for capacity planning and internal cost allocation:

  • current counts: members, resources, agents, intake keys, and recording storage;

  • daily totals: intake requests, intake bytes, jobs, and terminal session minutes.

Daily totals are kept for 400 days.

Counters use UTC calendar days. Intake requests and bytes count accepted requests once after deduplication; jobs count a job once when created; terminal minutes are the sum of connected seconds rounded up only when the daily value is presented. Failed and retried attempts do not inflate totals. These are operational measurements, not billing records, until a later RFD defines a billing contract.

Support access

An operator sometimes needs to look inside an organization to diagnose a problem. Renga has no impersonation, and nothing in this API reads inventory or acts as a user, so that access is a grant the organization’s owners make, visible to them, and revocable by them at any moment. The operator’s part is to say who its support people are; it can never grant itself access.

Support identities

The operator declares the deployment’s support identities as one deployment provider (RFD 15) flagged as the support source. Such a provider must have an email-domain allowlist and the enforces MFA mark (RFD 19), and it is hidden from the sign-in page: it is reached only by its direct URL, /users/log-in?provider=<id>. Every account created through it is a support identity. A support identity is always its own account, never linked to an existing account or to any other provider, can hold only the built-in support role (RFD 17) in any organization, and cannot be invited, mapped, or provisioned as an ordinary member. A deployment has at most one support source; a deployment that declares none has no support access anywhere, and the setting below does not appear.

The support source is managed with providers:write through the deployment-provider endpoints and with renga support enable, which takes a provider definition or a signed support license file that carries one, and renga support disable. Disabling or deleting the support source ends every support membership and session in the deployment at once.

Granting and ending access

An organization owner turns on Allow support access in Renga’s security settings (RFD 19), optionally until a chosen time. While it is on, Renga itself gives every support identity the support role in that organization, shows them in the members list marked as support, and records their every action in Activity under their own names. The operator API can read whether the setting is on and until when, through organizations:read; no scope, policy document, default, or lock can turn it on, extend it, or keep it on.

When the owner turns the setting off, or its expiry passes, Renga removes the support memberships, closes those identities' sessions and channels in that organization with the revocation path RFD 16 uses for source-bound access, and revokes any API tokens they created there. Support identities never hold explicit permissions, so there is no running terminal or VM operation to preserve; a read in progress simply fails. Turning it off is recorded in Activity like turning it on.

Support people use the normal web interface and API with the support role. Nothing they do goes through this API, so it carries the organization’s own audit trail, and the operator API’s own audit log records only the operator’s changes to the support source.

Transfers

These endpoints use RFD 20's archive and export/import engine, including its validation, principal mapping, historical-only jobs, pending security state, destination policy, and atomic import. The API can start, inspect, and cancel an export, then authorize a download. It can also create an empty organization and import an archive into it. This is the sole exception to the ordinary inventory boundary and requires transfers:write for every start, cancellation, download authorization, and import; transfers:read alone reveals only status and non-secret metadata.

An export omits passwords, session material, API and intake keys, provider and integration secrets, pending invitation secrets, and other credentials. A download authorization is a short-lived, single-use capability bound to the requesting token and is never returned in a status response. It is separate from RFD 20's owner-authorized seven-day artifact lifetime. Revoking the token or cancelling the transfer invalidates it. Import verifies and stages the archive before atomically checking that the destination is still empty and making its inventory visible. Security approvals return pending, imported users receive no access without an independently valid membership, and no credential from the source becomes usable in the destination.

Writes

Writes are safe to retry. Every create, invitation, transfer start, and other non-idempotent action requires an Idempotency-Key. Its namespace is the immutable token ID, HTTP method, and canonical route. Renga stores a digest of the normalized request and the completed response for at least 24 hours and returns the retention deadline. A retry with the same request returns the stored result; a different request in the same namespace returns 409. Concurrent requests serialize on the key, persist one operation before any side effect is queued, and converge on that operation and result.

Authentication and current scope authorization run before a stored result is returned, so expiry or revocation cannot be bypassed by replay. Retries of an asynchronous operation return the same operation ID. Cancellation, suspension, resume, and deletion scheduling also define the already-requested target state as success rather than repeating a side effect.

Policy and provider documents are replaced whole with PUT. They require If-Match with the version from the last read: a missing precondition returns 428, a stale one returns 412, and comparison and update happen atomically. Provider responses omit the client-secret field. Omitting it from a write preserves the current secret, an explicit replacement object changes it, and explicit null clears it only while the provider is disabled. A redacted read can therefore be written back without erasing a secret.

This lets automation hold the desired state and apply it repeatedly until the deployment matches.

Release commands call the same mutation functions and obey the same preconditions. For a non-idempotent action they accept or generate an operation key and print it, so rerunning a command can recover the same operation.

Deletion

Deletion has distinct active, deletion_pending, and purging states. The purge worker locks and changes deletion_pending to purging only when the stored deadline has passed. Cancellation or resume locks the same record, so one of cancellation and purge wins atomically; once purging begins it cannot be cancelled.

Purge removes organization-owned inventory, settings, memberships, pending invitations, and transfer artifacts. It does not delete shared user accounts. Deployment audit records retain their immutable organization ID and display name snapshot under the deployment’s audit-retention policy, while the organization Activity disappears with the organization. Export capabilities expire no later than purge.

Audit

Every attempted mutation is recorded in a deployment audit log with an allowlisted, secret-free summary: immutable actor ID and display-name snapshot, operation, target IDs, request ID, time, outcome, and changed field names. It never stores request bodies or authorization headers, secrets, invitation or idempotency keys, archive contents, or download capabilities. A change to an organization is also recorded in that organization’s Activity with the same sanitized summary, attributed to the operator token’s immutable ID and name snapshot, so owners can see what their deployment changed and when.

A successful mutation and its audit event commit in one transaction, or the mutation commits with a durable transactional outbox record that must become the audit event before success is returned. Release commands have the same audit and Activity guarantees and use an explicit local-operator actor with a command invocation ID; they are not anonymous or exempt because no API token is involved.

Release commands

Every API operation has a matching release command using the same domain function, validation, lockout prevention, idempotency, and auditing, so a deployment without network access to the API can be operated the same way. renga support enable|disable|status manage the support source (Support access); like the API, they never turn an organization’s support access on.

Decisions

  1. Separate port. The API always has its own listener and port, which may be public (Listener). The reason is not that a port is a boundary, but that a whole listener can carry rules such as mutual TLS that a path prefix on the shared endpoint cannot; the earlier proposal to serve the API on the main endpoint unless an operator chose otherwise is withdrawn.

  2. Deletion through the API. Allowed, as Deletion describes. This lets an external control plane schedule deletion without running a release command on the deployment. A compromised token with organizations:write can schedule a deletion that becomes irreversible when purge begins. The 30-day grace period, owner notification, and owner cancellation and export path provide an opportunity to recover, not a guarantee against data loss. When mutual TLS is enabled, a leaked token without a valid client certificate is insufficient to connect; deployments without it rely on token authentication and their configured source-address and network restrictions.

  3. Support access. Defined in Support access as an owner-granted, owner-revocable membership for operator-declared identities, rather than impersonation or an API that reads inventory. The one invariant that makes it safe is that the operator declares who, and only the owner decides whether; this RFD therefore exempts the setting from policy defaults and locks.

Amendments

  • RFD 15: a deployment provider may be hidden from the sign-in page and flagged as the deployment’s support source; identities from it are always new accounts that hold only the support role.

  • RFD 17: a fifth built-in role, support, holding inventory.read only, granted and removed by Renga rather than by members.manage.

  • RFD 19: the Allow support access organization setting, off by default, with an optional expiry, exempt from operator defaults and locks.

Future work

  • Webhooks for events an operator’s automation cares about, such as an owner accepting an invitation or a quota reaching 80%.

  • Short-lived tokens issued through workload identity instead of stored operator tokens.