# Permissions Model

Resolves `../ux/04-open-questions.md` #5: staff permissions must be merchant-legible, not a checkbox per backend module. This document defines the permission vocabulary and the default roles built from it.

## The permission vocabulary matches the merchant-facing IA, not the 20 modules

Permission keys are scoped to the **7 sections** from `../ux/01-information-architecture.md` (Home, Orders, Products, Customers, Marketing, Reports, Settings), not to the 20 backend modules from `../architecture/04-domain-modules.md`. A staff member granted access to "Products" gets both Catalog and Inventory actions (since the merchant experiences those as one thing, per `../ux/01-information-architecture.md`) without ever seeing that distinction. This is the same invisibility rule from `../architecture/06-open-questions.md` #1, applied one layer deeper — into the permission system a staff member's *role* is built from, not just the navigation a regular merchant sees.

## Permission key catalog

| Permission key | Grants |
|---|---|
| `orders.view` | View order list and detail |
| `orders.manage` | Fulfill, update status, manage shipments |
| `orders.refund` | Issue refunds and process returns — **kept separate from `orders.manage`** because it moves money; a fulfillment-focused role shouldn't automatically get refund authority |
| `products.view` | View catalog and stock levels |
| `products.manage` | Create/edit products, adjust stock, manage categories/collections/bundles |
| `customers.view` | View customer list and profiles |
| `customers.manage` | Edit customer info, manage segments |
| `marketing.view` | View campaigns and discount performance |
| `marketing.manage` | Create/launch campaigns and discounts, accept/dismiss suggested campaigns |
| `reports.view` | View and export reports |
| `settings.manage` | Store details, payments, shipping, tax, notifications, content |
| `staff.manage` | Invite/remove staff, assign roles — **kept separate from `settings.manage`** to prevent privilege escalation (a staff member trusted with general settings shouldn't automatically be able to grant themselves or others broader access) |

`Home` and its Store Health Score require no dedicated permission — every staff account that can log in sees Home, scoped to whatever the rest of their permissions let them act on.

## Default roles

Opinionated, pre-built roles cover the common cases without requiring a merchant to design a permission scheme from scratch, per "opinionated defaults over endless customization":

| Role | Permissions | Typical use |
|---|---|---|
| **Owner** | All permissions, including `staff.manage` | The account that created the store. At least one Owner must always exist — the last Owner cannot be demoted or removed, a hard rule enforced at the interface level, not just a UI nicety. |
| **Store Manager** | `orders.*`, `products.manage`, `customers.manage`, `marketing.manage`, `reports.view`, `settings.manage` (excludes `staff.manage`) | Day-to-day operator trusted with the whole business except adding/removing other staff |
| **Order Fulfillment** | `orders.view`, `orders.manage`, `products.view` | Warehouse/fulfillment staff — can see stock to check availability, cannot edit products, cannot refund |
| **Marketing** | `marketing.*`, `customers.view`, `reports.view` | Runs campaigns and discounts, can see customer segments and performance, cannot touch orders or settings |
| **Support** | `orders.view`, `orders.refund`, `customers.*` | Customer-facing staff who need to look up orders, issue refunds, and manage customer records, without broader store access |

These five cover the common merchant team shapes. A merchant is not required to create custom roles to get started — assigning "Store Manager" or "Order Fulfillment" to a new staff member is a one-click default, consistent with `../ux/01-information-architecture.md`'s Settings > Staff & permissions screen offering role selection from this list first, with custom permission combinations available as a secondary, progressively-disclosed option per `../ux/03-ux-patterns-and-conventions.md` rule 4.

## Enforcement

Every module's public interface (`04-module-interfaces.md`) that performs a state-changing command checks the calling staff session's permissions before executing — this is a cross-cutting concern implemented once (a shared authorization check invoked at the start of any command handler that requires it), not re-implemented per module. `users.audit_log_entry` (`02-module-schemas.md`) records every permission-gated action taken, which is what makes "who issued this refund" or "who changed this setting" answerable later.
