Concept

Authorisation model

The two independent policies for worker calls and client calls, how they are evaluated and why they are kept separate.

Distro Current 3 min read Updated

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#

text
Effective access = ServiceAccess AND (MethodIsUnrestricted OR MethodAccess)
  1. No allowed callers on the target means allow.
  2. 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.
  3. A method rule narrows only the methods it names. It is a sparse exceptions map, not an implicit allow-list of every method.
  4. Method names are matched case-insensitively.

Client evaluation#

text
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.