Add policy-model.md

This commit is contained in:
oberon 2026-05-05 00:50:12 +03:00
parent c3ef169a6c
commit aa5e1408cf

196
policy-model.md Normal file
View File

@ -0,0 +1,196 @@
# 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