feat(ops): add workspace MCP deployment overlays
This commit is contained in:
@@ -0,0 +1,280 @@
|
||||
# Architecture: NODE.DC Tasker Codex API
|
||||
|
||||
Last updated: 2026-07-18.
|
||||
|
||||
## Goal
|
||||
|
||||
Give selected NODE.DC users a controlled way to connect their local Codex or another AI coding agent to Tasker so the agent can maintain project work items according to NODE.DC task-card rules.
|
||||
|
||||
The agent can create projects inside an explicitly granted workspace, create and
|
||||
update cards, move them through states, write structured task documentation,
|
||||
update checkers, add comments and bounded file attachments, assign existing
|
||||
workspace users to project cards when permitted, and apply labels. It cannot
|
||||
delete cards or projects, manage workspace settings, bypass workspace/project
|
||||
grants, or call arbitrary Tasker endpoints.
|
||||
|
||||
## Product boundary
|
||||
|
||||
This module is a standalone service:
|
||||
|
||||
```text
|
||||
Launcher / Hub
|
||||
owns platform entitlement: can this user/client use Codex agents?
|
||||
|
||||
Tasker / Operational Core
|
||||
owns workspaces, projects, issues, labels, states, members, comments
|
||||
|
||||
Agent Gateway
|
||||
owns agents, tokens, scopes, grants, MCP tools, audit, rate limits
|
||||
```
|
||||
|
||||
The service is deployed as its own Docker container and has its own repository and database. It may call Launcher and Tasker through internal APIs, but it does not read or write either database directly.
|
||||
|
||||
## Runtime shape
|
||||
|
||||
```text
|
||||
Local Codex
|
||||
-> independent Ops and read-only Ontology MCP servers over HTTPS
|
||||
-> Agent Gateway public endpoints (/mcp and /ontology-mcp)
|
||||
-> scope and grant checks
|
||||
-> Tasker internal adapter
|
||||
-> Tasker domain models
|
||||
```
|
||||
|
||||
```text
|
||||
User UI
|
||||
-> Launcher entitlement
|
||||
-> Tasker workspace feature settings
|
||||
-> Agent Gateway agent/token management
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### Launcher integration
|
||||
|
||||
Launcher decides whether a user can use the module at all.
|
||||
|
||||
Required Launcher concepts:
|
||||
|
||||
- service: `operational-core`
|
||||
- module entitlement: `codex_agents`
|
||||
- scope owner: client/user/access matrix
|
||||
- open contour support
|
||||
- enterprise contour support
|
||||
|
||||
Launcher should not create low-level Tasker cards or tokens. It only projects entitlement and global lifecycle state.
|
||||
|
||||
### Tasker integration
|
||||
|
||||
Tasker shows the feature in `Workspace settings -> Features` only when:
|
||||
|
||||
- the current user has Tasker access;
|
||||
- Launcher access check says the user has `codex_agents`;
|
||||
- the workspace policy allows this workspace to host agents.
|
||||
|
||||
Tasker remains the owner of issues, states, labels, project members, comments, and structured blocks.
|
||||
|
||||
### Agent Gateway
|
||||
|
||||
Agent Gateway owns:
|
||||
|
||||
- agent profiles;
|
||||
- pairing codes;
|
||||
- opaque access tokens;
|
||||
- token hashes;
|
||||
- grants;
|
||||
- scopes;
|
||||
- audit events;
|
||||
- idempotency keys;
|
||||
- rate limits;
|
||||
- MCP tools exposed to local Codex.
|
||||
|
||||
It should not execute user code and should not run Codex itself.
|
||||
|
||||
Agent lifecycle management is not public. Tasker/Launcher-facing management calls use `NODEDC_AGENT_GATEWAY_INTERNAL_TOKEN`, while external Codex calls use only the opaque agent token issued for one agent. The owner-scoped internal routes verify that the requested agent belongs to the requested `owner_user_id`.
|
||||
|
||||
### Tasker internal adapter
|
||||
|
||||
The adapter is a narrow Tasker API layer for Agent Gateway. It exists because the current Plane REST API is broad and includes operations the agent must not receive directly.
|
||||
|
||||
The adapter should expose intent-level operations:
|
||||
|
||||
- list allowed workspaces/projects;
|
||||
- read project states, labels, members;
|
||||
- create a project within a granted workspace after Tasker admin revalidation;
|
||||
- search/list issues;
|
||||
- read one issue with full comments and attachment metadata;
|
||||
- create issue;
|
||||
- patch allowed issue fields;
|
||||
- append comment;
|
||||
- attach a bounded base64 file through Tasker-owned object storage;
|
||||
- update NODE.DC structured blocks;
|
||||
- add existing workspace member to project when explicitly allowed;
|
||||
- publish audit/realtime events.
|
||||
|
||||
It must reject delete, archive, workspace settings, project deletion, invite
|
||||
creation, arbitrary path/URL attachments, and arbitrary path proxying.
|
||||
|
||||
## Data model
|
||||
|
||||
Initial Agent Gateway entities:
|
||||
|
||||
```text
|
||||
agent
|
||||
id
|
||||
owner_user_id
|
||||
owner_email
|
||||
display_name
|
||||
avatar_url
|
||||
status: active | disabled | revoked
|
||||
created_at
|
||||
updated_at
|
||||
|
||||
agent_token
|
||||
id
|
||||
agent_id
|
||||
token_hash
|
||||
name
|
||||
purpose: ops | ontology
|
||||
status: active | revoked | expired
|
||||
expires_at
|
||||
last_used_at
|
||||
created_at
|
||||
|
||||
agent_grant
|
||||
id
|
||||
agent_id
|
||||
workspace_slug
|
||||
project_id (empty string internally means workspace-level grant)
|
||||
scopes[]
|
||||
mode: voluntary | reporting
|
||||
created_by_user_id
|
||||
created_at
|
||||
updated_at
|
||||
|
||||
pairing_code
|
||||
id
|
||||
agent_id
|
||||
code_hash
|
||||
status: active | used | expired | revoked
|
||||
expires_at
|
||||
created_at
|
||||
used_at
|
||||
|
||||
agent_audit_event
|
||||
id
|
||||
agent_id
|
||||
event_type
|
||||
actor_user_id
|
||||
metadata
|
||||
created_at
|
||||
|
||||
idempotency_key
|
||||
key
|
||||
agent_id
|
||||
request_hash
|
||||
response_body
|
||||
created_at
|
||||
expires_at
|
||||
```
|
||||
|
||||
## Actor model
|
||||
|
||||
Writes should be visible as agent actions, not anonymous platform automation.
|
||||
|
||||
Recommended Tasker-side identity:
|
||||
|
||||
```text
|
||||
display_name: Codex Agent: <name>
|
||||
email: agent+<agent_id>@agents.nodedc.local
|
||||
is_bot: true
|
||||
owner_user_id: <human user id>
|
||||
```
|
||||
|
||||
For audit and UI, every write must preserve both:
|
||||
|
||||
- `agent_id`;
|
||||
- `owner_user_id`.
|
||||
|
||||
If Tasker cannot yet represent owned bot users cleanly, the adapter can initially use a system actor and store the agent metadata in audit payloads, but this should be treated as a migration step, not the final model.
|
||||
|
||||
## Capability model
|
||||
|
||||
Allowed initial scopes:
|
||||
|
||||
```text
|
||||
workspace:read
|
||||
project:read
|
||||
project:create
|
||||
project:member:add_existing
|
||||
issue:read
|
||||
issue:create
|
||||
issue:update
|
||||
issue:move
|
||||
issue:comment
|
||||
issue:label
|
||||
issue:assign
|
||||
issue:structured_blocks:write
|
||||
issue:attachment:write
|
||||
```
|
||||
|
||||
Explicitly denied for MVP:
|
||||
|
||||
```text
|
||||
issue:delete
|
||||
issue:archive
|
||||
comment:delete
|
||||
label:delete
|
||||
state:create
|
||||
state:delete
|
||||
project:delete
|
||||
workspace:settings
|
||||
workspace:member:invite
|
||||
workspace:member:remove
|
||||
raw_tasker_api
|
||||
```
|
||||
|
||||
Deleting or archiving a card remains a human-only operation.
|
||||
|
||||
## Two operating modes
|
||||
|
||||
### Voluntary mode
|
||||
|
||||
The user creates an agent and gives its token to their local Codex. Codex updates Tasker when the user asks it to.
|
||||
|
||||
This mode is user-owned and best for personal development workflow.
|
||||
|
||||
### Reporting mode
|
||||
|
||||
The enterprise/project admin requires agent reporting for a project. The developer still runs local Codex, but the expected workflow is:
|
||||
|
||||
- start work session;
|
||||
- update active issue/checker;
|
||||
- append implementation notes;
|
||||
- finish work session.
|
||||
|
||||
This mode is policy-visible but cannot be fully enforced if Codex runs entirely on the developer machine outside a managed wrapper. The system can enforce token scope and show stale/missing reports, but it cannot force an arbitrary local agent to report unless the organization distributes a managed Codex config or wrapper.
|
||||
|
||||
## Deployment
|
||||
|
||||
Initial local target:
|
||||
|
||||
```text
|
||||
ops-agents.local.nodedc -> Agent Gateway
|
||||
```
|
||||
|
||||
Production-like domains should follow platform conventions:
|
||||
|
||||
```text
|
||||
ops-agents.nodedc.ru or ops-agents.<deployment-domain>
|
||||
```
|
||||
|
||||
The service should support:
|
||||
|
||||
- local `.env`;
|
||||
- staging `.env.staging`;
|
||||
- production secret store;
|
||||
- Docker image build;
|
||||
- container startup migrations;
|
||||
- health endpoint;
|
||||
- preflight script validating URLs/secrets.
|
||||
+418
@@ -0,0 +1,418 @@
|
||||
# MCP Tools Contract
|
||||
|
||||
Last updated: 2026-07-18.
|
||||
|
||||
## Position
|
||||
|
||||
The current Tasker / Plane fork does not expose a dedicated MCP server. It exposes REST endpoints and NODE.DC internal endpoints. The new module should expose MCP tools externally and translate those tool calls into validated Tasker adapter calls.
|
||||
|
||||
Codex should not call generic Tasker REST directly.
|
||||
|
||||
Current implementation status:
|
||||
|
||||
- Agent Gateway exposes `/mcp` as JSON-RPC over HTTP.
|
||||
- Implemented MCP methods: `initialize`, `ping`, `tools/list`, `tools/call`.
|
||||
- `tools/list` returns only tools allowed by the authenticated agent session scopes.
|
||||
- `tools/call` uses the same product runtime as REST tool endpoints.
|
||||
- Server-sent event streaming is intentionally not required for the first product slice.
|
||||
|
||||
## Authentication
|
||||
|
||||
MCP clients authenticate to Agent Gateway with an opaque agent token.
|
||||
|
||||
Recommended transport options:
|
||||
|
||||
- HTTPS MCP endpoint for general clients;
|
||||
- local stdio connector later if useful;
|
||||
- REST fallback for non-MCP clients.
|
||||
|
||||
Current route:
|
||||
|
||||
```text
|
||||
POST /mcp
|
||||
POST /ontology-mcp
|
||||
```
|
||||
|
||||
`/mcp` accepts only an Ops-purpose token. `/ontology-mcp` accepts only the
|
||||
separately minted Ontology-purpose token and proxies the read-only Ontology MCP.
|
||||
The two endpoints, credentials, and MCP server entries remain independent.
|
||||
|
||||
Required request headers for authenticated tool calls:
|
||||
|
||||
```text
|
||||
Authorization: Bearer <agent-token>
|
||||
Accept: application/json
|
||||
MCP-Protocol-Version: 2025-06-18
|
||||
```
|
||||
|
||||
Token rules:
|
||||
|
||||
- token is opaque;
|
||||
- server stores only token hash;
|
||||
- token is scoped to one agent;
|
||||
- token can be revoked immediately;
|
||||
- token expires;
|
||||
- owner blocked/annulled disables tokens.
|
||||
|
||||
## Tool list
|
||||
|
||||
### `tasker_get_agent_instructions`
|
||||
|
||||
Returns operating rules, allowed projects, card guide, and reporting expectations.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
workspace:read
|
||||
```
|
||||
|
||||
### `tasker_list_projects`
|
||||
|
||||
Lists projects granted to the agent.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
project:read
|
||||
```
|
||||
|
||||
### `tasker_get_project_context`
|
||||
|
||||
Returns project states, labels, members, and card-writing rules.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
project:read
|
||||
```
|
||||
|
||||
### `tasker_search_issues`
|
||||
|
||||
Searches existing work items within granted projects.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:read
|
||||
```
|
||||
|
||||
### `tasker_get_issue`
|
||||
|
||||
Returns one issue with description, structured blocks, labels, state, assignees,
|
||||
full comment bodies, and attachment metadata. The Tasker adapter caps comments at
|
||||
1000 and reports truncation explicitly.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:read
|
||||
```
|
||||
|
||||
### `tasker_create_project`
|
||||
|
||||
Creates or idempotently resolves a project inside an explicitly granted
|
||||
workspace. Only a long-lived agent-scoped session can call it; token-scoped run
|
||||
grants cannot expand themselves. Tasker revalidates that the human owner is an
|
||||
active workspace administrator before creating the project. The new project is
|
||||
then added to the same agent's grants so subsequent card tools can address it.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
project:create
|
||||
```
|
||||
|
||||
Allowed fields:
|
||||
|
||||
```text
|
||||
workspace_slug
|
||||
name
|
||||
identifier (optional)
|
||||
description (optional)
|
||||
idempotency_key
|
||||
```
|
||||
|
||||
### `tasker_create_issue`
|
||||
|
||||
Creates a work item.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:create
|
||||
```
|
||||
|
||||
Allowed fields:
|
||||
|
||||
```text
|
||||
project_id
|
||||
name
|
||||
description_html
|
||||
detail_layout
|
||||
state_id
|
||||
priority
|
||||
label_ids
|
||||
assignee_ids
|
||||
start_date
|
||||
target_date
|
||||
parent_id
|
||||
```
|
||||
|
||||
Validation:
|
||||
|
||||
- project must be granted;
|
||||
- state must belong to project;
|
||||
- labels must belong to project;
|
||||
- assignees must be active project members;
|
||||
- detail layout must match NODE.DC structured block schema.
|
||||
|
||||
### `tasker_update_issue`
|
||||
|
||||
Patches allowed issue fields.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:update
|
||||
```
|
||||
|
||||
Allowed fields:
|
||||
|
||||
```text
|
||||
name
|
||||
description_html
|
||||
detail_layout
|
||||
priority
|
||||
start_date
|
||||
target_date
|
||||
parent_id
|
||||
```
|
||||
|
||||
Deletion, archive, and project transfer are not allowed.
|
||||
|
||||
### `tasker_move_issue`
|
||||
|
||||
Changes issue state and optional sort order.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:move
|
||||
```
|
||||
|
||||
Allowed fields:
|
||||
|
||||
```text
|
||||
state_id
|
||||
sort_order
|
||||
```
|
||||
|
||||
Validation:
|
||||
|
||||
- target state must belong to the same project;
|
||||
- cancelled/completed moves are allowed only if the grant permits them.
|
||||
|
||||
### `tasker_set_issue_labels`
|
||||
|
||||
Sets or merges labels.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:label
|
||||
```
|
||||
|
||||
Validation:
|
||||
|
||||
- labels must already exist in project for MVP;
|
||||
- creating new labels can be added later under `label:create`.
|
||||
|
||||
### `tasker_assign_issue`
|
||||
|
||||
Sets assignees.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:assign
|
||||
```
|
||||
|
||||
Validation:
|
||||
|
||||
- assignees must be active project members;
|
||||
- if the user asks to add a workspace member to the project, use `tasker_add_existing_project_member` first.
|
||||
|
||||
### `tasker_add_existing_project_member`
|
||||
|
||||
Adds an existing workspace member to a granted project.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
project:member:add_existing
|
||||
```
|
||||
|
||||
Validation:
|
||||
|
||||
- target user must already be an active workspace member;
|
||||
- target user role cannot exceed workspace role;
|
||||
- launcher-managed workspace rules must be respected;
|
||||
- the request should be traceable to an explicit human instruction or reporting policy.
|
||||
|
||||
### `tasker_append_comment`
|
||||
|
||||
Adds a comment to an issue.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:comment
|
||||
```
|
||||
|
||||
Allowed fields:
|
||||
|
||||
```text
|
||||
comment_html
|
||||
```
|
||||
|
||||
### `tasker_attach_file`
|
||||
|
||||
Uploads a bounded file to an issue through Tasker's existing FileAsset and
|
||||
S3/MinIO storage path. The MCP accepts base64 content only: it never reads a
|
||||
client or server filesystem path and never fetches a remote URL. Raw content is
|
||||
limited to 5 MiB and Tasker rechecks MIME allowlists, workspace storage limits,
|
||||
project quota, and content deduplication.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:attachment:write
|
||||
```
|
||||
|
||||
Allowed fields:
|
||||
|
||||
```text
|
||||
project_id
|
||||
issue_id
|
||||
file_name
|
||||
content_type
|
||||
content_base64
|
||||
idempotency_key
|
||||
```
|
||||
|
||||
### `tasker_update_structured_blocks`
|
||||
|
||||
Replaces or patches NODE.DC structured blocks in `detail_layout`.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:structured_blocks:write
|
||||
```
|
||||
|
||||
Supported blocks:
|
||||
|
||||
```text
|
||||
text
|
||||
checker
|
||||
```
|
||||
|
||||
Canonical key:
|
||||
|
||||
```text
|
||||
nodedc_structured_blocks
|
||||
```
|
||||
|
||||
The tool should support high-level patch actions:
|
||||
|
||||
```text
|
||||
append_text_block
|
||||
append_checker
|
||||
update_checker_item
|
||||
replace_blocks
|
||||
append_implementation_note
|
||||
```
|
||||
|
||||
### `tasker_start_work_session`
|
||||
|
||||
Starts a reporting session for a project or issue.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:comment
|
||||
```
|
||||
|
||||
This is mainly for enterprise reporting mode.
|
||||
|
||||
### `tasker_finish_work_session`
|
||||
|
||||
Finishes a reporting session and appends summary/validation notes.
|
||||
|
||||
Required scope:
|
||||
|
||||
```text
|
||||
issue:comment
|
||||
issue:structured_blocks:write
|
||||
```
|
||||
|
||||
## Idempotency
|
||||
|
||||
All write tools must accept:
|
||||
|
||||
```text
|
||||
idempotency_key
|
||||
```
|
||||
|
||||
Agent Gateway requires this value for all write tools. The key is scoped by agent, hashed together with the tool name and normalized arguments, and stored with a processing/completed state.
|
||||
|
||||
Behavior:
|
||||
|
||||
- first successful request stores the tool result for 24 hours;
|
||||
- duplicate key with identical arguments returns the stored result and does not call Tasker again;
|
||||
- duplicate key with different arguments returns `idempotency_key_conflict`;
|
||||
- duplicate key while the first request is still processing returns `idempotency_key_in_progress`;
|
||||
- failed writes release the key so the same operation can be retried.
|
||||
|
||||
## Denied tools
|
||||
|
||||
These should not exist in MVP:
|
||||
|
||||
```text
|
||||
tasker_delete_issue
|
||||
tasker_archive_issue
|
||||
tasker_delete_comment
|
||||
tasker_delete_label
|
||||
tasker_delete_project
|
||||
tasker_invite_workspace_member
|
||||
tasker_raw_api_request
|
||||
tasker_attach_server_path
|
||||
tasker_attach_remote_url
|
||||
```
|
||||
|
||||
## Instruction pack
|
||||
|
||||
`tasker_get_agent_instructions` should include the effective card guide. The local Codex setup file should instruct the agent to call this tool before planning Tasker changes.
|
||||
|
||||
Agent Gateway also exposes:
|
||||
|
||||
```text
|
||||
GET /api/v1/agent-session/setup
|
||||
```
|
||||
|
||||
This endpoint is authenticated by the same bearer token and returns:
|
||||
|
||||
- MCP server config template with `<agent-token>` placeholder;
|
||||
- tools available to the current agent grants;
|
||||
- generated AGENTS.md content with NODE.DC Tasker operating rules;
|
||||
- no raw token echo.
|
||||
|
||||
Minimum instruction:
|
||||
|
||||
```text
|
||||
Before creating or updating Tasker cards, call tasker_get_agent_instructions.
|
||||
Use Tasker for durable project planning, current architecture, planned architecture, stage checkers, implementation notes, and validation results.
|
||||
Do not create multiple top-level cards for substeps of one product topic.
|
||||
Do not delete or archive cards.
|
||||
Do not operate outside granted projects.
|
||||
```
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
# Tasker API Audit
|
||||
|
||||
Last updated: 2026-05-14.
|
||||
|
||||
## Summary
|
||||
|
||||
Current Tasker / Plane fork is partially ready for the Codex Agent API use case through existing REST endpoints and NODE.DC structured blocks. It is not ready as a direct external API for agents because it has broad routes, session-oriented permissions, delete/archive endpoints, and no MCP layer.
|
||||
|
||||
The correct approach is to add a narrow internal Tasker adapter for Agent Gateway instead of exposing raw Plane API to local Codex.
|
||||
|
||||
## Existing useful API surface
|
||||
|
||||
### Issues
|
||||
|
||||
Routes exist for issue list/create/update/retrieve:
|
||||
|
||||
```text
|
||||
GET /api/workspaces/:slug/projects/:project_id/issues/
|
||||
POST /api/workspaces/:slug/projects/:project_id/issues/
|
||||
GET /api/workspaces/:slug/projects/:project_id/issues/:issue_id/
|
||||
PATCH /api/workspaces/:slug/projects/:project_id/issues/:issue_id/
|
||||
```
|
||||
|
||||
The same route also supports `DELETE`, but Agent Gateway must never expose it.
|
||||
|
||||
Existing serializers already support:
|
||||
|
||||
- name;
|
||||
- state;
|
||||
- priority;
|
||||
- dates;
|
||||
- labels;
|
||||
- assignees;
|
||||
- parent issue;
|
||||
- description HTML;
|
||||
- `detail_layout`.
|
||||
|
||||
Validation already checks:
|
||||
|
||||
- assignees are active project members with sufficient role;
|
||||
- labels belong to project;
|
||||
- state belongs to project;
|
||||
- parent belongs to workspace/project;
|
||||
- description HTML is sanitized.
|
||||
|
||||
### Structured blocks
|
||||
|
||||
NODE.DC structured task content lives in:
|
||||
|
||||
```text
|
||||
Issue.detail_layout["nodedc_structured_blocks"]
|
||||
```
|
||||
|
||||
Known block types:
|
||||
|
||||
```text
|
||||
text
|
||||
checker
|
||||
```
|
||||
|
||||
This is the right storage layer for:
|
||||
|
||||
- current architecture;
|
||||
- planned architecture;
|
||||
- stages;
|
||||
- checkers;
|
||||
- implementation notes.
|
||||
|
||||
Tasker already computes checker progress from this structure.
|
||||
|
||||
### Comments
|
||||
|
||||
Routes exist:
|
||||
|
||||
```text
|
||||
GET /api/workspaces/:slug/projects/:project_id/issues/:issue_id/comments/
|
||||
POST /api/workspaces/:slug/projects/:project_id/issues/:issue_id/comments/
|
||||
PATCH /api/workspaces/:slug/projects/:project_id/issues/:issue_id/comments/:comment_id/
|
||||
DELETE /api/workspaces/:slug/projects/:project_id/issues/:issue_id/comments/:comment_id/
|
||||
```
|
||||
|
||||
Agent Gateway should expose comment creation and possibly own-comment edit later, but not comment deletion.
|
||||
|
||||
### Labels
|
||||
|
||||
Routes exist:
|
||||
|
||||
```text
|
||||
GET /api/workspaces/:slug/projects/:project_id/issue-labels/
|
||||
POST /api/workspaces/:slug/projects/:project_id/issue-labels/
|
||||
PATCH /api/workspaces/:slug/projects/:project_id/issue-labels/:label_id/
|
||||
DELETE /api/workspaces/:slug/projects/:project_id/issue-labels/:label_id/
|
||||
```
|
||||
|
||||
MVP should let agents apply existing labels. Creating labels can be added later under an explicit admin scope.
|
||||
|
||||
### States
|
||||
|
||||
Routes exist:
|
||||
|
||||
```text
|
||||
GET /api/workspaces/:slug/projects/:project_id/states/
|
||||
PATCH /api/workspaces/:slug/projects/:project_id/states/:state_id/
|
||||
```
|
||||
|
||||
Agent Gateway should allow moving issues to existing states. It should not allow state creation/deletion in MVP.
|
||||
|
||||
### Project members
|
||||
|
||||
Routes exist:
|
||||
|
||||
```text
|
||||
GET /api/workspaces/:slug/projects/:project_id/members/
|
||||
POST /api/workspaces/:slug/projects/:project_id/members/
|
||||
PATCH /api/workspaces/:slug/projects/:project_id/members/:member_id/
|
||||
DELETE /api/workspaces/:slug/projects/:project_id/members/:member_id/
|
||||
```
|
||||
|
||||
Current code checks workspace membership and blocks launcher-managed workspace self-management.
|
||||
|
||||
Agent Gateway may expose `add_existing_project_member` only with an explicit scope and only for existing workspace members.
|
||||
|
||||
## Missing MCP layer
|
||||
|
||||
No dedicated MCP server exists in Tasker today. References to MCP are documentation/guideline-oriented, not an operational API server.
|
||||
|
||||
Required new layer:
|
||||
|
||||
```text
|
||||
Agent Gateway MCP tools -> Agent Gateway service -> Tasker internal adapter
|
||||
```
|
||||
|
||||
Tasker should not become the MCP host. Keeping MCP in Agent Gateway preserves standalone Tasker and keeps the external agent surface outside Plane.
|
||||
|
||||
## Required Tasker adapter additions
|
||||
|
||||
Add internal endpoints under a namespace such as:
|
||||
|
||||
```text
|
||||
/api/internal/nodedc/agent/...
|
||||
```
|
||||
|
||||
Current implemented adapter routes:
|
||||
|
||||
```text
|
||||
POST /api/internal/nodedc/agent/projects/resolve
|
||||
POST /api/internal/nodedc/agent/projects
|
||||
GET /api/internal/nodedc/agent/projects/:project_id/context
|
||||
GET /api/internal/nodedc/agent/issues?project_id=...
|
||||
POST /api/internal/nodedc/agent/issues
|
||||
GET /api/internal/nodedc/agent/issues/:issue_id
|
||||
PATCH /api/internal/nodedc/agent/issues/:issue_id
|
||||
POST /api/internal/nodedc/agent/issues/:issue_id/attachments
|
||||
POST /api/internal/nodedc/agent/issues/:issue_id/move
|
||||
POST /api/internal/nodedc/agent/issues/:issue_id/comments
|
||||
PUT /api/internal/nodedc/agent/issues/:issue_id/labels
|
||||
PUT /api/internal/nodedc/agent/issues/:issue_id/assignees
|
||||
```
|
||||
|
||||
The implemented adapter uses NODE.DC internal bearer auth and receives normalized agent metadata in headers:
|
||||
|
||||
```text
|
||||
X-NODEDC-Agent-Id
|
||||
X-NODEDC-Agent-Owner-User-Id
|
||||
X-NODEDC-Agent-Token-Id
|
||||
```
|
||||
|
||||
The current adapter creates or reuses a dedicated bot actor with email `agent+<agent_id>@agents.nodedc.local` and `bot_type=nodedc_codex_agent`.
|
||||
|
||||
Issue detail returns full comment bodies plus attachment metadata. Project
|
||||
creation is workspace-admin gated and idempotent by agent external id.
|
||||
Attachment upload uses Plane's existing FileAsset, S3/MinIO, quota, MIME, and
|
||||
deduplication implementation; it accepts no server path or remote URL.
|
||||
|
||||
These endpoints must use `NODEDC_INTERNAL_ACCESS_TOKEN` / `PLANE_NODEDC_ACCESS_TOKEN` style auth and must be callable only from Agent Gateway.
|
||||
|
||||
Suggested adapter endpoints:
|
||||
|
||||
```text
|
||||
POST /api/internal/nodedc/agent/context/
|
||||
POST /api/internal/nodedc/agent/issues/search/
|
||||
POST /api/internal/nodedc/agent/issues/
|
||||
PATCH /api/internal/nodedc/agent/issues/:issue_id/
|
||||
POST /api/internal/nodedc/agent/issues/:issue_id/comments/
|
||||
POST /api/internal/nodedc/agent/issues/:issue_id/structured-blocks/
|
||||
POST /api/internal/nodedc/agent/projects/:project_id/members/add-existing/
|
||||
```
|
||||
|
||||
The adapter should receive normalized Agent Gateway metadata:
|
||||
|
||||
```text
|
||||
agent_id
|
||||
owner_user_id
|
||||
owner_email
|
||||
workspace_slug
|
||||
project_id
|
||||
scopes
|
||||
idempotency_key
|
||||
```
|
||||
|
||||
The adapter should validate Tasker domain rules and return stable errors rather than leaking raw Plane serializer details.
|
||||
|
||||
## Why raw Plane API is not enough
|
||||
|
||||
Raw Plane API is too broad for external agents:
|
||||
|
||||
- it includes delete/archive routes;
|
||||
- it assumes a session user, not an external agent identity;
|
||||
- it has more fields than agents should control;
|
||||
- it does not know Launcher module entitlements;
|
||||
- it does not have agent idempotency;
|
||||
- it does not produce agent-specific audit by default;
|
||||
- it is not MCP-native.
|
||||
|
||||
## Compatibility note
|
||||
|
||||
Tasker must remain standalone-capable. All agent-specific behavior should be disabled when NODE.DC Agent Gateway env vars are absent.
|
||||
@@ -0,0 +1,157 @@
|
||||
# Threat Model
|
||||
|
||||
Last updated: 2026-07-18.
|
||||
|
||||
## Security objective
|
||||
|
||||
Let external local Codex agents maintain Tasker cards without turning Tasker into an open automation surface.
|
||||
|
||||
## Main threats
|
||||
|
||||
### Raw Tasker access leakage
|
||||
|
||||
Risk: a user copies a broad Tasker token or cookie into local Codex, allowing arbitrary API calls.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- never issue Plane session cookies to agents;
|
||||
- never expose raw Tasker API tokens;
|
||||
- use opaque Agent Gateway tokens;
|
||||
- only expose allowlisted MCP tools.
|
||||
|
||||
### Project scope escape
|
||||
|
||||
Risk: an agent writes to another project or workspace.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- Agent Gateway grants are project-scoped;
|
||||
- Tasker adapter revalidates workspace/project membership;
|
||||
- every tool requires explicit `project_id`;
|
||||
- gateway rejects projects outside grant set.
|
||||
- project creation requires `project:create` on a workspace grant, an
|
||||
agent-scoped token, and a second workspace-admin check inside Tasker;
|
||||
- token-scoped AI Workspace sessions cannot add projects to their own grants.
|
||||
|
||||
### Attachment exfiltration or storage abuse
|
||||
|
||||
Risk: an agent reads arbitrary host files, fetches a remote payload, or bypasses
|
||||
Tasker storage quotas through the attachment tool.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- MCP accepts explicit base64 bytes only, never filesystem paths or remote URLs;
|
||||
- Agent Gateway limits decoded content to 5 MiB and never logs the base64 body;
|
||||
- Tasker enforces MIME allowlists, workspace storage limits, project quota,
|
||||
S3/MinIO ownership, and content deduplication.
|
||||
|
||||
### Cross-service token confusion
|
||||
|
||||
Risk: an Ops token is replayed against Ontology or an Ontology token gains Tasker
|
||||
write capabilities.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- setup redemption mints two independent opaque token records;
|
||||
- token purpose is persisted and checked on every endpoint;
|
||||
- `/mcp` accepts only `ops`, `/ontology-mcp` only `ontology`;
|
||||
- Ontology upstream access token remains server-side.
|
||||
|
||||
### Destructive action
|
||||
|
||||
Risk: an agent deletes or archives cards, labels, comments, projects, or members.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- no delete/archive MCP tools in MVP;
|
||||
- adapter rejects delete/archive intents;
|
||||
- raw API proxy is forbidden.
|
||||
|
||||
### Privilege confusion
|
||||
|
||||
Risk: an agent acts as the human user and hides automation history.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- create dedicated agent identity;
|
||||
- store owner user separately;
|
||||
- every audit event includes both `agent_id` and `owner_user_id`;
|
||||
- UI displays agent-originated changes.
|
||||
|
||||
### Prompt injection
|
||||
|
||||
Risk: text inside a card tells Codex to exfiltrate token or call forbidden tools.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- MCP tools enforce server-side scopes;
|
||||
- instruction pack says Tasker content is untrusted;
|
||||
- Gateway never exposes secrets through read tools;
|
||||
- deny arbitrary HTTP fetch/proxy tools.
|
||||
|
||||
### Token theft
|
||||
|
||||
Risk: local token leaks from developer machine.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- token hash storage;
|
||||
- expiry;
|
||||
- immediate revoke;
|
||||
- last used metadata;
|
||||
- rate limits;
|
||||
- optional IP/device binding later.
|
||||
|
||||
### Lifecycle API exposure
|
||||
|
||||
Risk: an external caller creates agents, grants projects, or mints tokens without going through Launcher/Tasker entitlement.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- lifecycle routes require `NODEDC_AGENT_GATEWAY_INTERNAL_TOKEN`;
|
||||
- owner-scoped routes verify `owner_user_id` against the stored agent owner;
|
||||
- external Codex tokens can call only agent-session, setup, tool, and MCP routes;
|
||||
- raw agent token is returned only once on token creation.
|
||||
|
||||
### Owner lifecycle bypass
|
||||
|
||||
Risk: blocked/annulled user keeps active agent token.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- Gateway checks Launcher owner status;
|
||||
- blocked/annulled owner disables agent tokens;
|
||||
- periodic sync plus request-time access check.
|
||||
|
||||
### Replay and duplicate writes
|
||||
|
||||
Risk: network retry creates duplicate cards/comments.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- required idempotency keys for write tools;
|
||||
- store operation result by agent and idempotency key;
|
||||
- reject same key with different arguments;
|
||||
- release failed writes so safe retries can run again.
|
||||
|
||||
### Reporting mode false confidence
|
||||
|
||||
Risk: enterprise admin assumes local Codex must report, but the developer bypasses the managed config.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- UI distinguishes `connected`, `stale`, `never connected`;
|
||||
- reporting mode is visibility and policy, not hard enforcement, unless a managed wrapper is used;
|
||||
- CI/workflow checks can require Tasker session updates later.
|
||||
|
||||
## Hard rules
|
||||
|
||||
- No database access from Agent Gateway to Tasker DB.
|
||||
- No arbitrary Tasker HTTP proxy.
|
||||
- No user session cookie reuse.
|
||||
- No delete/archive tools in MVP.
|
||||
- No secrets in generated markdown instruction files.
|
||||
- No token logging.
|
||||
- No frontend access to service secrets.
|
||||
- No cross-use between Ops and Ontology bearer tokens.
|
||||
- No attachment from a filesystem path or remote URL.
|
||||
Reference in New Issue
Block a user