Skip to content

Managing Tenants ​

A tenant is a customer organization inside a project. Each tenant has its own members, its own settings layer, and its own set of enabled applications. A tenant is not an environment: dev, qas and run are carried by the project's Environments, and applications are defined once per project and switched on per tenant.

What a tenant has ​

  • Members with a configurable tenant role. Roles are defined per project over a fixed permission 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.
  • Enabled applications: a Tenant2Application row per (tenant, application) pair. It carries enabled, forwardRole, notifyMembershipChanges and its own settings layer.
  • Tenant settings: a layer of the configuration cascade between project and the per-(tenant, application) leaf.
  • Deployment topology: shared (default) or dedicated hosts and stacks.

Creating a tenant ​

  1. Open a project and click Tenants in the sidebar.
  2. Click New tenant.
  3. In the Create tenant dialog, enter a Name (up to 100 characters).
  4. Enter a Slug: lowercase letters, digits and single hyphens (^[a-z0-9]+(?:-[a-z0-9]+)*$), up to 50 characters, unique within the project. The dialog auto-fills upper-case initials from the name; replace them with a lowercase slug or the server rejects the save.
  5. Click Create.

You become a member of the new tenant with the project's seeded owner tenant role. Next, enable the applications this customer should have (see Enabling applications).

The tenants list ​

ColumnMeaning
#Tenant slug
NameDisplay name
Created at / Created byCreation metadata
Updated at / Updated byLast change metadata

Click #, Name, Created at or Updated at to sort. There is no search box and no edit or delete action in the list.

The tenant page ​

Click a row to open the tenant (/<project>/<tenant>). The page shows, top to bottom:

  • The tenant dashboard.
  • A metadata table: ID, Created by, Created at, Editor, Updated at.
  • Deployment topology: Shared or Dedicated, with Dedicate / Return to shared actions.
  • Applications: every project application with its status for this tenant and the actions Enable, Disable and Configure. The note under the table says that the API reports the aggregate count; a row's current state is shown only for applications you changed in this session.
  • Tenant members: the members list with a role selector per row, a remove action and an Add User button.

The sidebar section Tenant links to Tenant details, Tenant settings (/<project>/<tenant>/settings) and Machine Users (machine users are scoped to an environment; the page has an environment selector).

Managing members needs a tenant permission, not a project role

The member actions call addUserToTenant, updateUserTenantRole and removeUserFromTenant, which require the tenant.members.manage permission. That permission comes only from your own membership in the tenant. A project maintainer or owner who is not a member sees the panel read-only with a banner explaining why.

The tenant-admin shell ​

Tenant members who hold at least one tenant permission, with or without any project membership, get a scoped admin area at /tenant-admin/<tenantId> with the sections Dashboard, Members, Role definitions, Applications, Tenant profile and Pending invites. Each section is gated on its permission on the server. In Members, the role selector offers every role the project defines; a role you cannot grant (one carrying a permission you do not hold, or one this version of the platform does not recognise) is shown disabled with the reason beside it when you may read the role definitions, and is refused by the server otherwise.

Enabling applications for a tenant ​

Applications are defined once at the project level; a tenant gains access by enablement.

UI: on the tenant page, click Enable next to the application. (Equivalent: on the application page, the Tenant access card lists tenants with the same buttons.) Configure opens the Per-tenant configuration dialog for the (tenant, application) settings leaf: Frontend and Backend tabs of key-value pairs, saved with Save.

CLI:

bash
pfy application enable  --project <project-slug> --app <app-slug> --tenant <tenant-slug>
pfy application disable --project <project-slug> --app <app-slug> --tenant <tenant-slug>
pfy application list-tenants --project <project-slug> --app <app-slug>

API:

graphql
mutation {
  setApplicationEnabledForTenant(
    tenantId: "tenant-uuid"
    applicationId: "app-uuid"
    enabled: true
  ) {
    id
    enabled
    forwardRole
  }
}

setApplicationEnabledForTenant creates the join row if it does not exist and otherwise flips enabled, including on enabled: false, so disabling a pair that has no row brings the offer into existence. updateTenant2Application(id, input: { enabled, forwardRole, notifyMembershipChanges }) edits the other flags, withdrawApplicationFromTenant(tenant2ApplicationId) removes the row entirely, and tenant2Applications(tenantId: ...) or tenant2Applications(applicationId: ...) lists the join rows. All of these need project maintainer or owner (or the system administrator role). Every one of them writes an audit row.

To take an application away from a customer for good rather than switching it off, see Withdrawing an application from a tenant.

When an application is not enabled for a tenant (no join row, or enabled: false), the tenant-scoped frontend configuration request for that application returns 404.

Renaming a tenant ​

Renaming needs project membership, the tenant.lifecycle.manage permission in the tenant, or the system administrator role, not bare tenant membership. It writes an audit row carrying the old and new name.

There is no edit form in the UI. Use the CLI or the API:

bash
pfy tenant update <tenant-slug> --project <project-slug> --name "New name"
graphql
mutation {
  updateTenant(id: "tenant-uuid", input: { name: "New name" }) {
    id
    name
    slug
  }
}

UpdateTenantInput accepts name and slug. The deployment topology is changed with pfy tenant update --deployment-mode shared|dedicated or the Deployment topology card.

Deleting a tenant ​

The delete is refused while an application offer or a dedicated deployment exists

Deleting a tenant is not offered in the UI. pfy tenant delete <tenant-slug> --project <project-slug> and deleteTenant(id) are refused while the tenant has any Tenant2Application row, whether or not it is enabled, or, for a dedicated tenant, its own deployment rows. Each blocker is named with a count and its own remedy: cannot delete tenant: it still has 2 application offer(s); withdraw each application from the tenant first: setApplicationEnabledForTenant(enabled: false), then withdrawApplicationFromTenant.

withdrawApplicationFromTenant is the call that clears the row.

When the delete does go through, the tenant row is removed and every membership in it ends; the delete is not blocked by live members. Project applications are never touched: they belong to the project. Live comms websockets for that tenant are closed. The delete writes its audit row before it removes anything, recording the tenant's name, slug and member count.

The delete needs project membership, the tenant.lifecycle.manage permission in the tenant, or the system administrator role.

Common tasks ​

Onboard a customer to an application ​

  1. Create the tenant if it does not exist.
  2. Enable the application for it (UI Enable, or pfy application enable ...).
  3. Optionally click Configure and add per-(tenant, application) key-value overrides.

The cross-tenant promoteApplication / copyApplication mutations and the pfy application promote / copy commands still exist but always return an error. Both mutations carry @deprecated directives, so introspection, generated clients and schema tooling flag them before the call is made. To copy per-tenant configuration between two enablements use pfy application config-copy --from <joinId> --to <joinId> (find the join IDs with pfy application list-tenants).

Copy tenant settings to another tenant ​

  1. Open the source tenant's Tenant settings, switch to JSON Editor, copy the Backend and Frontend JSON.
  2. Open the target tenant's Tenant settings, paste, Save changes.

Add a member ​

  1. On the tenant page, click Add User under Tenant members.
  2. Use Search for user to find the user in Pocket ID.
  3. Pick a Role, one of the project's tenant role names. You can only assign a role whose permissions are a subset of your own.
  4. Click Add.

Troubleshooting ​

cannot delete tenant: it still has ... application offer(s) ​

See Deleting a tenant. Disabling is not enough: the join row itself blocks the delete, and disabling a pair that had no row creates one. Withdraw each application instead.

A setting is not applied for this tenant ​

The (tenant, application) leaf overrides tenant settings, and the frontend cascade also takes module fields from the application layer. Open Tenant settings or the application's Settings and check the Configuration Layers panel.

A user cannot open the tenant ​

Either they are not a member of the tenant (or of its project), or the application they expect is not enabled for the tenant. Add the membership with a suitable role, and check the Applications table on the tenant page.

See also ​