Service Roles
Per-service capability roles that gate access to individual pyck services
Audience: Programmer
Overview
pyck gates access to individual backend services with a set of fixed per-service capability roles. This is a thin layer on top of the unchanged reader/writer/admin ladder: the ladder still governs what a user may do once past the gate, while a service role governs whether the user may reach the service at all.
The roles are sourced from ZITADEL project roles (seeded at bootstrap); pyck holds no separate database copy.
Gated and open services
inventory
yes
inventory_service
picking
yes
picking_service
receiving
yes
receiving_service
file
yes
file_service
main-data
yes
main_data_service
management
no
—
workflow
no
—
A user who does not hold the relevant service role is denied with an HTTP 403 Forbidden before the request reaches the gated service. management (including the role-assignment API below) and workflow are intentionally not gated.
How the gate resolves which tenants it checks
The gate evaluates the service role against the tenant(s) the request operates on, which are derived from the X-Pyck-Tenant-Id header exactly as every other request is:
A request without the header defaults to
all, which expands to every tenant the user holds a ladder role in.A request with the header operates on the tenant(s) it names.
The gate then requires the service role in every tenant in that set (an AND, not an OR).
This has a consequence worth calling out explicitly:
A user who holds, for example,
inventory_servicein only some of their tenants is denied on the default (header-less) request, because that request resolves to all of their tenants and the role is missing in at least one of them. To proceed, the caller must scope the request to a qualifying tenant withX-Pyck-Tenant-Id: <tenant-uuid>.
This is fail-closed but can be surprising — if a default request returns 403 where you expected access, narrow X-Pyck-Tenant-Id to the specific tenant where the user holds the service role.
System users and service tokens bypass the gate.
Exception: the public file-alias route
One route is intentionally outside the gate and is therefore not subject to the per-service role restriction:
This is the pre-existing public-alias handler. It serves only files that have an explicit public alias, scoped to the tenantId in the path, and redirects to a short-lived pre-signed URL. It does not require the file_service role — its only access control is knowing a valid (tenantId, alias) pair. Do not assume the file service is fully gated: GraphQL traffic to the file service is gated, but this alias route is public by design.
Managing service roles
Service roles are assigned and inspected through the management GraphQL API (tenant-admin only):
serviceRoles— list the assignable per-service roles.userServiceRoles— list the roles a user currently holds in a tenant.assignRoles— add one or more service roles to a user (additive).removeRoles— remove one or more service roles from a user (idempotent).
Access is opt-in with no backfill: existing users are denied on the gated services until a tenant admin assigns them the relevant role.
Related
HTTP Headers —
X-Pyck-Tenant-Idand tenant context.Authentication — the reader/writer/admin/system ladder.
Last updated
Was this helpful?
