# Non-Linear: Policy & Permissions Model ## Overview Non-Linear uses a granular, policy-based permission system inspired by AWS S3/IAM. The system is built on fine-grained policies from day one. Roles are named bundles of policies — convenience layers, not hardcoded ceilings. ## Core Concepts ### Policy A policy is a single permission rule with four dimensions: ``` Policy = Actor + Action + Resource Scope + Effect ``` | Dimension | Description | Examples | |-----------|-------------|---------| | **Actor** | Who the policy applies to | User, agent, or role | | **Action** | What operation is permitted/denied | `read_node`, `create_child`, `change_status` | | **Resource Scope** | Where the policy applies in the graph | Global, subtree, single node | | **Effect** | Allow or deny | `allow`, `deny` | ### Actions (Enumerated) | Action | Description | |--------|-------------| | `read_node` | View a node's title, description, status, labels | | `read_comments` | View comments on a node | | `create_child` | Create a new child node | | `edit_node` | Modify title, description | | `change_status` | Change a node's status | | `change_assignee` | Assign/unassign a user or agent | | `add_label` | Add labels to a node | | `remove_label` | Remove labels from a node | | `add_comment` | Post a comment on a node | | `create_link` | Create a lateral link (blocks, relates-to) | | `remove_link` | Remove a lateral link | | `reparent_node` | Move a node to a different parent | | `delete_node` | Delete a node (and its subtree) | | `manage_policies` | Create/edit/delete policies and roles | | `manage_members` | Add/remove project members | ### Resource Scopes | Scope | Meaning | Use Case | |-------|---------|----------| | **Global** | Entire project — all nodes | Default for most human roles | | **Subtree** | A specific node and all its descendants | Agent confined to "Backend" component | | **Single Node** | Exactly one node | Lock down a sensitive design doc | **Subtree scoping is the key feature.** Because the data model is a decomposition tree, resource scope maps naturally to graph position. The graph *is* the namespace — no separate resource-naming scheme needed. ## Roles A role is a named, reusable collection of policies. Roles are editable — the defaults are starting points, not limits. ### Default Presets #### Owner ```yaml role: owner policies: - action: "*" scope: global effect: allow ``` #### Member ```yaml role: member policies: - action: [read_node, read_comments, create_child, edit_node, change_status, change_assignee, add_label, remove_label, add_comment, create_link, remove_link, reparent_node] scope: global effect: allow ``` #### Agent Reader ```yaml role: agent-reader policies: - action: [read_node, read_comments, add_comment, change_status] scope: global effect: allow ``` ### Custom Role Examples #### Backend Decomposer Agent ```yaml role: backend-decomposer policies: - action: read_node scope: global effect: allow - action: [create_child, add_label, add_comment] scope: subtree("Backend API") effect: allow ``` #### Triage Agent ```yaml role: triage-agent policies: - action: [read_node, read_comments, add_comment, change_status, change_assignee, add_label, remove_label] scope: global effect: allow ``` ## Resolution Order 1. **Direct policy on actor** takes precedence over role-inherited policies 2. **Deny always wins** over allow at the same level 3. **Narrower scope wins** over broader scope (single node > subtree > global) 4. **Default: deny** — if no policy explicitly allows an action, it is denied ## Reparenting and Permission Changes When a node is moved to a different parent (via plan-then-apply): 1. All subtree-scoped policies are re-evaluated against the new position 2. Actors may gain or lose access depending on the new subtree 3. System warns: *"Moving this node will change who can access it"* 4. Specifically flags if any agent loses access to nodes it's currently assigned to ## Lateral Links Crossing Permission Boundaries When a lateral link connects two nodes and the actor only has access to one: - **Link visible, target opaque.** The actor can see that a link exists and its type but cannot read the target node's details. - This enables agents to reason about blockers without leaking information across permission boundaries.