User Management
Users sign in through Pocket ID; the Manager stores a mirror of each user and decides what they may do through four separate role systems.
The four role systems
| Level | Where it lives | Values | What it controls |
|---|---|---|---|
| System role | User.role | normal, audit, administrator | Install-wide operator powers |
| Project role | User2Project.role | readonly, tester, developer, maintainer, owner | Who can open and manage a project in the Manager |
| Tenant role | User2Tenant.role → a project-defined TenantRole | project-configurable names (seeded: member, admin, owner) | Tenant self-service permissions |
| Application role | User2Application.role | readonly, normal | Access inside your application's own UI, not Manager access |
System role
administrator is the platform operator. It passes the "operator override" in project-scoped gates, and it alone can read the whole audit log export, run access reviews, push proxy configuration, list every user, and grant or revoke system roles. audit is stored but nothing reads it; it grants nothing beyond normal.
Administrators manage system roles on the System roles page (/system-roles; the sidebar entry is shown only to administrators). The server refuses to demote the last administrator. Every change writes an audit row.
mutation {
setUserSystemRole(userId: "user-uuid", role: administrator) {
id
externalEmail
role
}
}systemRoleDirectory { users { id externalName externalEmail role } administratorCount } lists everyone with their system role. Both are gated on administrator.
Project roles
Any project role lets a user open the project and read its tenants, applications, endpoints and settings. Managing (members, environments, endpoints, triggers, machine users, pilets, settings and application enablement) needs maintainer or owner, and so does renaming or deleting the project itself and any of its applications. The server does not treat tester and developer differently from readonly; they only differ in rank.
The one thing a readonly member can still write is a new tenant, application or language pack; those three creates are gated on bare membership. See Access Model.
Rank, lowest to highest: readonly < normal < tester < developer < maintainer < owner (one ladder shared with application roles, which use readonly and normal). You can assign only a role at or below your own (cannot assign role higher than your own); a system administrator overrides the rank check, which is the recovery path for a project that has locked itself out.
removeUserFromProject refuses to remove you from a project. Separately, and for every caller including an administrator, a removal or demotion that would leave a project with no owner is refused; the guard runs on the raw deleteUser2Project mutation too.
Tenant roles
Tenant roles are permission sets, not a fixed list. Each project defines its own roles over the vocabulary tenant.members.read, tenant.members.manage, tenant.invites.manage, tenant.roles.manage, tenant.apps.read, tenant.apps.configure, tenant.lifecycle.manage; new projects are seeded with member, admin and owner. When you assign a tenant role, its permissions must be a subset of your own permissions in that tenant. A membership whose role cannot be resolved grants nothing (shown as No role in the UI). See Access Model.
Application roles
readonly and normal are forwarded to your application for its own authorization decisions. The banner on the members card says it: Application roles control external UI access only. Manager access is controlled by tenant/project roles. Assigning them needs project maintainer or owner.
Adding users
To a project
- Open the project page (
/<project>) and scroll to Project members. - Click Add User.
- Search for user: the search runs against Pocket ID, so the user must exist there.
- Pick a Role (only roles at or below your own are offered).
- Click Add.
mutation {
addUserToProject(projectID: "project-uuid", userID: "user-uuid", role: developer) {
id
role
user {
externalUsername
externalEmail
}
}
}To a tenant
- Open the tenant page (
/<project>/<tenant>) and scroll to Tenant members. - Click Add User, search, pick a Role (one of the project's tenant role names), click Add.
You need the tenant.members.manage permission in that tenant. A project maintainer or owner who is not a member of the tenant sees the panel read-only. The tenant's creator is made an owner member automatically.
mutation {
addUserToTenant(tenantID: "tenant-uuid", userID: "user-uuid", roleName: "admin") {
id
roleName
}
}To an application
- Open the application page (
/<project>/applications/<app>) and scroll to Application members. - Click Add User, search, pick
readonlyornormal, click Add.
mutation {
addUserToApplication(applicationID: "app-uuid", userID: "user-uuid", role: readonly) {
id
role
}
}Changing a role
In any of the three members lists, change the Role dropdown on the user's row. The change is saved immediately and confirmed with a notification.
mutation {
updateUserProjectRole(id: "user2project-uuid", role: maintainer) { id role }
}mutation {
updateUserTenantRole(id: "user2tenant-uuid", roleName: "admin") { id roleName }
}mutation {
updateUserApplicationRole(id: "user2application-uuid", role: normal) { id role }
}The id is the membership row, not the user.
Removing a user
Click the trash icon (Remove user) on the row and confirm.
mutation {
removeUserFromProject(id: "user2project-uuid")
}removeUserFromTenant(id) and removeUserFromApplication(id) work the same way.
Memberships are independent
Removing a project membership deletes only that row. Tenant and application memberships the user holds in the same project stay in place and must be removed separately.
Looking up users
Search Pocket ID for users who have not yet appeared in the Manager. The query needs an authority that lets you add a member somewhere: maintainer or owner on any project, tenant.members.manage in any tenant, or the system administrator role. Each match is upserted into the Manager's users table, so the search is also a write.
query {
userProviderSearch(name: "john") {
id
username
email
displayName
disabled
}
}The authenticated user:
query {
me {
id
externalName
externalUsername
externalEmail
role
}
}Audit trail
Membership changes on tenants, projects and applications (add, re-role, remove), invites, role definitions and system-role changes write AuditLog rows: entities User2Tenant, User2Project and User2Application respectively, carrying actor, target and the before/after role. The generated updateUser2* / deleteUser2* mutations write the same rows as the named ones, so a grant cannot be changed off the audited path.
Filter fields: entityType, entityID, action, description, ipAddress; sort fields: createdAt, action, entityType. entityType matches exactly (case-insensitively), so User2Project returns grants and Project returns project lifecycle rows; asking for one will not return the other.
target is not always the user
On User2Project and User2Application rows written by these mutations, metadata.target is the granted user and metadata.resource is the project or application. On User2Tenant rows it is the grant row's own id. See Audit Logs.
query {
auditLogs(
projectID: "project-uuid"
filters: [{ field: "entityType", value: "User2Tenant" }]
order: { field: "createdAt", direction: DESC }
pagination: { limit: 50, offset: 0 }
) {
id
action
description
user {
externalUsername
}
createdAt
}
}See Audit Logs.
Pocket ID configuration
The Manager reads Pocket ID's API to search users:
pocket_id:
host: http://pocketid:1411
api_key: your-api-keypocket_id.api_key is required in production and can also be set with the PFY_POCKET_ID_API_KEY environment variable. Login itself (OIDC) is handled by the Proxy; see Proxy Configuration and Configuration.
Troubleshooting
The user is not found when searching
They do not exist in Pocket ID, or the Manager cannot reach Pocket ID (pocket_id.host / pocket_id.api_key).
cannot assign role higher than your own
Project and application roles are ranked; you can only hand out your own rank or lower. Tenant roles must be a permission subset of yours.
The tenant member controls are read-only for me
You hold a project role but no membership in the tenant. tenant.members.manage comes only from a tenant membership; ask a tenant member with that permission to add you.
A user cannot open a project
They have no project role. Any role (including readonly) is enough to open it.
API reference
- Add User to Project, Update User Project Role, Remove User from Project
- Add User to Tenant, Add User to Application
- Search Provider Users
See also: Access Model, Managing Tenants, Managing Projects.