Concept
Authorisation model
The two independent policies for worker calls and client calls, how they are evaluated and why they are kept separate.
Note
Kiwi Distro is currently in private preview. Interfaces and behaviour described here can change before general availability. Contact Kiwify Labs for access.
Kiwi Distro deliberately has two separate policies, not one generalised permission system.
| Worker calls (service to service) | Client calls | |
|---|---|---|
| Direction | Target-centric: the called service says who may call it | Source-centric: the client's own record says what it may call |
| Identity | The caller's owning service | The client itself |
| Stored on | The target service: allowed caller services, and method restrictions | The client: allowed services, allowed service groups, client-group membership |
| Refinement | Method-level restrictions | None (service-level only) |
| Grouping | None | Service groups (grant targets) and client groups (grant once, every member inherits) |
A call is checked only by the policy for its origin. Neither policy can widen or narrow the other.
Shared rule: unrestricted by default#
No configuration means unrestricted, exactly as before authorisation existed. The presence of any grant is what flips a target or caller to allow-list only, and an empty list is always treated as absent, never as "deny everyone".
Worker-to-worker evaluation#
Effective access = ServiceAccess AND (MethodIsUnrestricted OR MethodAccess)
- No allowed callers on the target means allow.
- Otherwise the caller's service id must be in the list, or the call is denied before any method rule is consulted. A method-level allow can never override a service-level deny.
- A method rule narrows only the methods it names. It is a sparse exceptions map, not an implicit allow-list of every method.
- Method names are matched case-insensitively.
Client evaluation#
effective service ids = client.AllowedServiceIds union each member group's AllowedServiceIds
effective service-group ids = client.AllowedServiceGroupIds union each member group's AllowedServiceGroupIds
allow if:
both effective sets are empty (unrestricted)
OR the target service id is in the effective service ids
OR "all-services" is in the effective service-group ids
OR the target service is a member of one of the effective service groups
The reserved all-services group is computed, not stored: it always contains every service that exists, and it cannot be edited or deleted. Any client with the superadmin role keeps this grant irrevocably, so administrators cannot lock themselves out.
Enforcement and caching#
Enforcement happens in the middleware before a worker is fetched. A denied call becomes a generic 403 with no policy detail. Because client tokens are stateless JWTs, client, service-group and client-group records are served from a cache (in-memory or Redis) so authorisation costs a cache hit rather than a database read per request.