d0ugal / d0ugal/graith

feat: generalise orchestrator privileges into attenuating session capabilities

Open
#1,609 0 comments 0 reactions 0 assignees View on GitHub
cli daemon enhancement scenario size: XL
Dominant language
Go
Stars
2
Forks
0
Avg merge
5h 49m
Merged PRs (30d)
189

Description

## Problem

Graith currently has an implicit authorization hierarchy. Ordinary agent sessions receive one set of powers, while the system orchestrator is recognized through `SystemKindOrchestrator` and granted extra authority through scattered `roleOrchestrator` / `isOrchestrator` branches.

Examples include targeting unrelated sessions, orphaning/reparenting sessions, managing triggers, sending notifications, releasing jailed comments, and global todo operations. This works for one privileged system session, but it is not a general policy model.

Workflow prompts also cannot reliably restrict a delegated reviewer. A reviewer may invoke another fan-out workflow and recursively create many sessions. #1605 protects the ownership tree during deletion, but does not prevent unintended fan-out.

Adding a one-off `leaf` boolean or a separate `sessions.create` check would create another special case. Instead, generalise the existing orchestrator privilege model into persisted, named capabilities for every authenticated principal/session.

## Proposal

Separate identity from authority:

- Authentication roles continue to identify a local human, remote human, guest, ordinary session, or system session and enforce transport boundaries.
- A versioned capability registry describes Graith control-plane operations.
- Each authenticated principal has an effective capability set.
- `SystemKindOrchestrator` may continue to control orchestrator lifecycle/UX, but must no longer imply blanket authorization. The configured orchestrator receives an explicit capability profile through the same machinery as other sessions.
- Authorization checks ask for a capability plus any target/scope constraint instead of branching on `isOrchestrator`.

The first user-facing restriction should be removal of `sessions.create` for non-orchestrating reviewers, but the migration must absorb existing orchestrator-only privileges rather than layering a second system beside them.

## Capability attenuation

Capability delegation must never amplify authority.

For any created or updated session:

```text
effective capabilities = requested/default capabilities
intersect issuer effective capabilities
intersect configured policy ceiling
```

Consequences:

- A caller cannot create a session with a capability the caller does not possess.
- A restricted session cannot grant itself a missing capability.
- A session-created child cannot be more capable than its authenticated creator.
- Reparenting a session does not implicitly change or expand its capabilities.
- Orchestrator mediation must preserve the original initiator. Routing a request through a more privileged orchestrator cannot launder authority; the grant is bounded by both the initiator and the mediating authority.
- Daemon-authored triggers and other automation need an explicit service authority/profile rather than silently borrowing unrestricted daemon power.
- Unknown capabilities and ambiguous issuers fail closed.

Capability expansion should require a controller that both has the capability being granted and is authorized to manage the target. A useful explicit capability may be `sessions.capabilities.manage`; possessing it permits delegation, not minting powers outside the controller effective set. Capability removal can be more permissive because it only narrows authority, but must still respect target ownership rules.

## Initial capability inventory

The implementation should inventory current authorization branches and give them stable names. Likely starting points include:

- `sessions.create`
- `sessions.manage.self`
- `sessions.manage.descendants`
- `sessions.manage.any`
- `sessions.reparent`
- `sessions.capabilities.manage`
- `scenarios.manage`
- `triggers.manage`
- `notifications.send`
- `messages.jail.read`
- `messages.jail.release`
- `todos.manage.any`

The final taxonomy should avoid encoding every role combination as a capability. Target scope may be represented separately (self, descendants, any) if that produces a smaller and clearer model.

## Configuration and UX

Possible CLI:

```console
gr new review-correctness --remove-capability sessions.create ...
gr update review-correctness --add-capability sessions.create
gr update review-correctness --remove-capability sessions.create
```

Possible scenario member syntax:

```toml
[[sessions]]
name = "correctness"
capabilities.remove = ["sessions.create"]
```

The exact spelling is open to refinement. Requirements:

- Session state persists the effective set and its policy/profile source across stop, resume, restart, and upgrade.
- Agent defaults, scenario definitions, and the orchestrator configuration can select a capability profile and add/remove capabilities within the issuer ceiling.
- Structured session output and inspection expose effective capabilities.
- Mutations are explicit and idempotent.
- Denials name the authenticated caller, required capability, target/scope, and attempted operation.
- Capability grants, removals, and denials are audit-logged without credentials.
- Generated agent instructions summarize relevant restrictions. A reviewer without `sessions.create` should be told to perform the task itself because Graith will deny agent creation.

## `sessions.create` enforcement

Removing `sessions.create` must deny every path capable of producing a new session, not only `gr new`:

- direct creation;
- fork into a new session;
- scenario start/add operations that create members;
- trigger or orchestrator-mediated creation initiated by the restricted session;
- future creation commands added to the daemon.

Put the authoritative check at a shared daemon boundary and retain handler checks only for clearer errors. A rejection must perform no partial mutation.

## Migration

- Inventory every `roleOrchestrator`, `isOrchestrator`, and `SystemKindOrchestrator` authorization branch.
- Define a default orchestrator capability profile preserving intended current behavior.
- Convert authorization checks to capabilities while keeping `SystemKindOrchestrator` only where lifecycle behavior genuinely differs.
- Keep remote transport policy as a separate outer boundary; passing it does not grant a missing operation capability.
- Migrate existing persisted sessions/config deterministically and document the compatibility behavior.
- Add a completeness test so new mutating protocol operations cannot omit capability classification silently.

## Acceptance criteria

- [ ] A versioned capability registry covers Graith control-plane operations.
- [ ] The current orchestrator-specific authorization behavior is expressed through an explicit capability profile rather than blanket role exemptions.
- [ ] Ordinary sessions, the orchestrator, humans, guests, and daemon automation resolve to explicit effective capabilities or a documented root/service authority.
- [ ] Session creation and capability updates enforce strict attenuation: no caller can grant more authority than it has.
- [ ] Self-granting, reparenting, child creation, and orchestrator mediation cannot escalate authority.
- [ ] CLI creation/update and scenario members support capability additions/removals within the issuer ceiling.
- [ ] Effective capabilities are persisted and visible in structured output.
- [ ] `sessions.create` is enforced across direct create, fork, scenario, trigger, and mediated creation paths.
- [ ] Existing orchestrator actions continue to work through capabilities, with regression tests for each migrated privilege.
- [ ] Denials and mutations are atomic, actionable, and audit-logged.
- [ ] Unknown capabilities and unclassified new mutating operations fail closed.
- [ ] Documentation distinguishes authentication roles, runtime capabilities, target scope, labels, OS sandbox policy, and agent-native permissions.

## Follow-up possibilities

A configurable fan-out budget or maximum descendant depth remains useful as a fleet-wide circuit breaker. It is separate from authorization: capabilities answer whether a caller may create sessions, while budgets limit how much permitted creation may occur.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by inventorying every roleOrchestrator, isOrchestrator, and SystemKindOrchestrator authorization branch, then trace the shared daemon boundary for direct creation, fork, scenario, trigger, and mediated creation. Define the capability registry and attenuation rules alongside regression and completeness tests. Done means existing orchestrator behavior is preserved through explicit capabilities, all creation paths enforce sessions.create, and persistence, output, denials, and audit logging meet the acceptance criteria.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend, cli, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.