non-linear-docs/04-POLICY-MODEL.md

140 lines
4.3 KiB
Markdown

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