Zanzibar And Core Auth
Coil authorisation is Zanzibar-inspired.
That means access is modeled as relationships between subjects and resources, not as one global role table.
Examples of relationship facts:
- a user is a member of a group
- a group administers a site
- a site editor may edit a page
- a merchandiser may featured-edit a product
What This Means In Practice
If you have used role-based systems before, the important shift is:
- you are not asking "what global role does this user have?"
- you are asking "what relationship chain connects this subject to this resource and permission?"
That is a better fit for Coil's target workloads because sites, pages, products, bookings, memberships, and admin surfaces do not all share one flat ownership model.
Why Coil Uses This Model
Coil needs one auth system that can cover:
- multi-site storefronts
- editorial publishing
- memberships and entitlements
- events and bookings
- support and finance operations
- customer-specific organization structure
A flat role table becomes brittle fast in that environment.
When To Use This Mental Model
Use the Zanzibar model when you need to reason about:
- cross-site admin and editorial access
- inherited permissions from site to page or collection to product
- group-based access instead of only direct user grants
- customer-specific roles that should not leak into module code
What Core Owns
Core auth owns the engine-level pieces:
- tuple storage
- schema parsing and validation
- check/list/lookup style APIs
- recursive graph evaluation
- explanation tooling
- caching and invalidation
Core does not own one universal set of relation names.
That is the job of the active auth package.
What Coil Adds On Top
Coil does not expose Zanzibar-style relations directly as the module contract.
It adds:
- capability names as the stable module boundary
- package-selected schema semantics
- explain tooling for operational debugging
That is the main difference between "graph auth exists internally" and "graph auth is safe to build a framework on."
The Three-Layer Model
Think about auth in three layers:
Tuple storage- persistent relationship facts
Auth schema- resource types, relations, and derived permissions
Capability bindings- stable contracts that modules ask for
That split is the point. It lets the engine stay stable while semantics remain replaceable.
Capabilities Over Roles
Official modules ask questions like:
cms.page.publishcatalog.product.editorder.refund.issue
They do not ask:
- is this user
site#admin? - does this actor have relation
editoron this page?
The current auth package decides which schema relation satisfies the capability.
Concrete Example
A CMS publish screen does not need to know the customer's custom org chart.
It asks for a capability like:
cms.page.publish
Then the active auth package decides whether that resolves through:
page#publishersite#admin- a customer-specific approval group
- some other derived permission path
That separation is what keeps official modules reusable.
How This Maps To Zanzibar Thinking
In Zanzibar-style terms:
subject: the actor being checkedresource: the object being accessedrelationorpermission: the semantic being evaluatedtuple: one stored relationship fact
Coil adds a stable capability layer on top so official modules do not couple themselves to one relation graph.
Full Implementation
The Zanzibar-inspired engine and package boundary show up in these repo areas:
crates/coil-auth/apps/shoppr/auth/shoppr-auth/package.tomlapps/shoppr/auth/shoppr-auth/model.authapps/shoppr/auth/shoppr-auth/capabilities.toml
Why This Matters For Customer Apps
This model is what makes the following possible without forking modules:
- adding customer-specific operator roles
- adding domain-specific resources
- tightening approval flows
- replacing the default organization model
If official modules depended directly on relation names, "custom auth schema" would be fake.
Common Developer Mistake
A common mistake is trying to model auth as if every action needs a new module-side role.
The better Coil pattern is:
- keep the module capability stable
- change the auth package semantics behind it
That keeps customer policy flexible without turning every official module into a customer-specific fork.
Common Mistakes
- Explaining Zanzibar only as "graph auth." The important boundary is not just graph traversal; it is the separation of engine, schema, and capability contracts.
- Treating capability names as equivalent to stored tuple relations. They are not.
- Assuming customer code or WASM should read raw auth tables directly. Coil is designed to keep authorisation decisions inside the core auth service.