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.