โ† Back to Features

Multi-tenancy

Tenant scope is derived from the authenticated principal, never from client input, and a hosted deployment can refuse to let tenants reach the operator's network.

Running uterm for one team needs no tenancy at all. Running it as a hosted service does, and the difference between the two is usually one bad assumption: that the caller can be trusted to say which tenant it belongs to.

Where the tenant comes from

Principal.tenant_id is resolved from the authenticated identity โ€” a JWT claim (jwt_tenant_claim, default tenant_id), a header (x-uterm-tenant), or a cookie (uterm_tenant), depending on the auth mode. The resolved value is validated against a tenant pattern and fails closed if it does not match.

A tenant_id in a request body is not an alternative source. Send one to a create or update endpoint and it is rejected 422 tenant_managed โ€” the field is not quietly ignored, and it is not honoured. Silently dropping it would leave a caller believing it had set something.

What a scope permits

A scope is either a single tenant or the system scope, and it must be exactly one โ€” the validity check is a XOR, so a malformed scope claiming both, or neither, permits nothing rather than defaulting open. A tenant scope reaches its own resources; the system scope reaches all of them.

Reads are gated the same way writes are. A listing narrowed by scope is the reason a tenant cannot enumerate what it may not open.

Egress: the tenant’s reach into your network

uterm’s whole purpose is connecting to machines โ€” SSH into an internal server is the use case, not an attack. So block_private_connector_targets defaults off, and a single-team deployment keeps working as intended.

Turn it on for a hosted deployment and connector targets resolving to private, loopback, link-local, multicast, unspecified, or reserved addresses are refused. Without it, a tenant who can name a connector target can reach anything your server can reach.

Cloud-metadata addresses โ€” 169.254.169.254, 100.100.100.200, fd00:ec2::254 โ€” are always blocked, whatever that flag says. Credential-bearing metadata endpoints are not a thing any deployment posture makes reasonable to expose, so that one is not a toggle.

Not a bolt-on

Tenancy is enforced where resources are read and written, not at a gateway in front of them. The graphical target registry takes a scope on every operation; API keys carry a validated tenant; the same principal resolution feeds both. C# is canonical for the multi-tenancy model, with the other implementations held to it.