# 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 three 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. "Backend API subtree" means "this node and everything decomposed beneath it." 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 Full control over the project. ```yaml role: owner policies: - action: "*" scope: global effect: allow ``` #### Member Standard team member. Can do everything except manage policies and delete nodes. ```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 Default agent role. Read-only plus comments and status changes. ```yaml role: agent-reader policies: - action: [read_node, read_comments, add_comment, change_status] scope: global effect: allow ``` ### Custom Role Examples #### Backend Decomposer Agent An AI agent that can break down backend components into tasks, but only within the Backend subtree. ```yaml role: backend-decomposer policies: - action: read_node scope: global effect: allow - action: [create_child, add_label, add_comment] scope: subtree("Backend API") # scoped to node ID in practice effect: allow ``` #### Triage Agent An agent that can reprioritize and reassign, but can't create or delete. ```yaml role: triage-agent policies: - action: [read_node, read_comments, add_comment, change_status, change_assignee, add_label, remove_label] scope: global effect: allow ``` #### External Reviewer A stakeholder who can view and comment on high-level nodes only. ```yaml role: external-reviewer policies: - action: [read_node, read_comments, add_comment] scope: subtree("root") # top two levels only (enforced by depth limit) effect: allow ``` ## Resolution Order When multiple policies apply to an actor+action+resource combination: 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 ### Resolution Examples ``` Scenario: Agent "deploy-bot" has role "agent-reader" (global read + comment + status) AND a direct policy: deny change_status on "Production Deploy" node Action: deploy-bot tries to change status on "Production Deploy" Result: DENIED — direct deny on single node overrides role's global allow Action: deploy-bot tries to change status on "Backend API / Auth" Result: ALLOWED — role's global allow applies, no deny in scope ``` ``` Scenario: Agent "decomposer" has role "backend-decomposer" (global read, create_child on Backend subtree) Action: decomposer tries to create a child under "Frontend / Charts" Result: DENIED — create_child only allowed in Backend subtree Action: decomposer tries to read "Frontend / Charts" Result: ALLOWED — read_node is global scope ``` ## Reparenting and Permission Changes When a node is moved to a different parent: 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. The system should warn the user: *"Moving this node will change who can access it"* 4. Specifically flag 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 ("blocked by node X") but cannot read the target node's details. - This enables agents to reason about blockers without leaking information across permission boundaries. ## Implementation Notes for v0.1 - Build the full granular policy engine underneath - Ship the three default presets (owner, member, agent-reader) - Expose custom role creation through a settings panel (can be minimal UI) - Store policies as structured data (JSON/YAML), not code - Policy evaluation should be centralized — every API call goes through the policy engine - Audit log: record every policy evaluation for debugging and compliance