55 lines
3.7 KiB
Markdown
55 lines
3.7 KiB
Markdown
# R4.2 P0-A TaskPlane Scope Isolation Design
|
|
|
|
Status: approved candidate design. This document is not evidence of a protected-main merge or production deployment.
|
|
|
|
## Boundary
|
|
|
|
- Task: `r42-p0a-taskplane-scope-isolation-20260816`
|
|
- Governed Job: `job-3fd743b51971c6007923699c1233f84c`
|
|
- Repository: `aios/aios`
|
|
- Base: `0eb6e1a864edfdaeef37b438991707a110e21558`
|
|
- Target branch: `agent/r42-p0a-taskplane-scope-isolation-20260816`
|
|
- Generation: `1`
|
|
|
|
The implementation reuses this exact boundary. It does not create a replacement Task, Job, branch, generation, or idempotency key.
|
|
|
|
## Model and invariants
|
|
|
|
`TaskPlan` gains first-class `scope_type`, `tenant_id`, `user_id`, `project_id`, `conversation_id`, and `run_id` fields. The fields participate in accepted-key validation, parsing, serialization, persistence, and authorization.
|
|
|
|
Historical rows without scope data normalize to `scope_type=legacy`; no tenant is inferred. Tenant-scoped tasks require a non-empty `tenant_id`. System/global resources use an explicit `scope_type=system` invariant and do not receive a fabricated tenant. A task may add user, project, conversation, and run ownership within its tenant.
|
|
|
|
## Trusted ActorContext
|
|
|
|
TaskPlane defines a small immutable `TaskActorContext` used only for authorization. The HTTP authenticator returns an identity. `PolicyEngine` resolves that identity through server-owned policy configuration into the effective context and capabilities. Request fields such as `tenant_id`, `user_id`, `group_id`, and `role` are declarations only and never expand authority.
|
|
|
|
Public create, list, and get paths always use the resolved context. Internal compatibility calls may omit a context and retain explicit legacy behavior, but the HTTP adapter never uses that path.
|
|
|
|
## Persistence
|
|
|
|
SQLite remains the current runtime. `_create_schema()` performs an idempotent forward migration that adds the six scope columns and indexes, then backfills only missing `scope_type` values to `legacy`.
|
|
|
|
New writes persist scope in both the canonical `task_json` representation and first-class columns. Reads accept historical JSON without scope, using legacy columns, but reject a new-row mismatch between JSON and columns. SQL list/get predicates are parameterized and applied before row hydration.
|
|
|
|
PostgreSQL remains a separate migration target and is not claimed by this change.
|
|
|
|
## Authorization flow
|
|
|
|
1. HTTP bearer authentication yields an identity.
|
|
2. `PolicyEngine` verifies capability and resolves the identity to `TaskActorContext`.
|
|
3. Create binds the requested scope to the trusted context and rejects mismatches.
|
|
4. List/get pass the trusted context to `TaskPlaneStore`.
|
|
5. Store applies tenant and ownership predicates in SQL; privileged capabilities may explicitly cross scope.
|
|
|
|
Tenant actors cannot read legacy tasks, another tenant, or mismatched owned resources. Legacy actors can only read legacy tasks. System actors require explicit privileged capability for cross-scope access.
|
|
|
|
## Failure behavior
|
|
|
|
Missing tenant scope, forged scope, unknown scope types, JSON/column divergence, and unauthorized list/get operations fail closed with canonical TaskPlane errors. Public get does not reveal whether an out-of-scope task exists.
|
|
|
|
## Verification
|
|
|
|
Tests exercise real model round trips, SQLite migration/restart, Service/Policy/Store wiring, tenant A versus tenant B list/get denial, ownership filtering, forged create payload rejection, legacy isolation, privileged access, and SQL parameterization. Existing TaskPlane API, RBAC, recovery/security, and security-contract suites remain mandatory.
|
|
|
|
The candidate is complete only after real `TASK_CLAIMED`, `TASK_HEARTBEAT`, exact WriterLease, governed commit, OID convergence, result submission, and `TASK_VERIFIED`. Protected main and production remain unchanged.
|