Table of Contents

Class SystemTeamScopes

Namespace
Tharga.Team
Assembly
Tharga.Team.dll

System-level (cross-team) scope constants for team operations. Unlike the in-team TeamScopes (which authorize only the caller's own team), these authorize across any team and are granted to system API keys or privileged roles. The toolkit defines them; the consumer applies them as they see fit (e.g. o.ConfigureSystemRoles mapping a role to the scope, or a system API key's scope list).

public static class SystemTeamScopes
Inheritance
SystemTeamScopes
Inherited Members

Fields

Delete

Authorizes deleting any team, regardless of membership and regardless of the AllowTeamCreation self-service option. The unconditional, cross-team delete path.

public const string Delete = "teams:delete"

Field Value

string

Manage

Authorizes renaming any team and setting or clearing its icon, regardless of membership. The oversight equivalent of the in-team team:manage, for the two operations that are presentational.

public const string Manage = "teams:manage"

Field Value

string

Remarks

Deliberately narrower than in-team team:manage, which also covers consent and custom roles. Those are authorization: consent is a team's own statement about what it exposes inbound, and an operator overriding it is a far larger claim than fixing a typo in a name. Rename and icon change how a team looks; consent changes who can reach it.

Extending this scope to consent would need a deliberate decision and its own name. An architecture-level rule cannot express "these two members of that scope but not those two", so a test asserts the boundary instead.

Purge

Authorizes permanently removing a soft-deleted team, including dropping its storage.

public const string Purge = "teams:purge"

Field Value

string

Remarks

Separate from Delete because it is the only irreversible one, and because it is the only one needing whatever privilege the storage adapter requires to destroy a team's data — for the MongoDB adapter in a per-team-database deployment, dropDatabase. That privilege is one most managed deployments will not grant permanently: Atlas's readWriteAnyDatabase does not include it.

Splitting it means a deployment can withhold both this scope and the database grant and still delete teams normally, which is what Eplicta FortDocs asked for in Tharga/Team#224.

Restoring needs no scope of its own. It is strictly less destructive than the delete it undoes, so Delete covers it — anyone trusted to remove a team is trusted to change their mind. A third scope would be a grant nobody has asked for and one more thing to map.

Read

Authorizes enumerating any team via ITeamService.GetAllTeamsAsync, regardless of membership — the discovery path for oversight roles (support, administration).

public const string Read = "teams:read"

Field Value

string

Remarks

Discovery only. Holding this grants no access inside a team: selecting a team the caller is not a member of still yields only the scopes that team has consented to, and none if it has consented to nothing. Contrast with the in-team Read, which authorizes reading the caller's own team.

SetOwner

Authorizes making an existing member the sole owner of any team, whatever its current owner count — none, one, or several.

public const string SetOwner = "teams:set-owner"

Field Value

string

Remarks

Why this is its own scope. The rule this codebase already applies is that a new scope is warranted when an operation is irreversible or crosses a tenant boundary. This does the second: the caller is by definition not a member of the team they are acting on. It is also privilege escalation by construction — it hands someone Owner and takes it from whoever held it — so it earns its own grant and its own audit entry rather than riding on a scope granted for something else.

Deliberately not Delete: authorizing repair with the right to destroy would mean the only way to fix a team is to hold the right to delete it. Deliberately not the in-team team:manage either, which is TeamKey-bound and so cannot fit a non-member.

This grant can depose a sitting owner, and is meant to. The two cases it exists for are a team synced from a legacy system carrying several owners, which must be reduced to one, and a handover the sitting owner cannot perform themselves — they have left, or the account is gone. ITeamService.TransferOwnershipAsync remains the in-team path and still requires the caller to be the owner; this is the operator path, and the two are not alternatives.

Renamed from teams:assign-owner (3.9.0–3.13.0), which authorized only the ownerless-repair case. The name changed with the capability rather than being widened in place, so a host that granted the old string does not silently acquire the ability to depose owners. A startup check fails loudly if the retired name is still registered — see RetiredScopeCheck — because the failure would otherwise be a silent refusal at the point of use rather than an error at boot.