Remote-MCP 03: Replace per-process profile binding with per-request principal resolution #932

Open
opened 2026-07-26 00:30:25 -05:00 by jcwalker3 · 0 comments
Owner

Parent epic: #929.
Depends on: #930, #931.

Problem

Role separation today is physical. Five processes run, each launched with a different GITEA_MCP_PROFILE, and a caller gets author permissions because it connected to the pipe belonging to the author process. The role is a property of the process, so gitea_whoami reports one profile for the lifetime of that process and the capability resolver answers for that one profile.

A remote endpoint serves many callers through one listener. If the role stays a process property, then either every remote caller inherits whichever single role the host was started with, or five separate hosts must be deployed and the capacity problem is unchanged. Worse, the current arrangement means a caller that reaches the wrong endpoint silently gets the wrong permission set, because nothing in the request itself carries the role.

Scope

Move the principal from the process to the request.

  • Define a session principal carrying at minimum: profile name, role kind, Gitea identity, remote, organization, and repository.
  • Resolve the principal per request, from the authenticated session, and pass it explicitly through capability resolution and every gate that currently reads process-level profile state.
  • Keep GITEA_MCP_PROFILE working as the source of the principal when the transport is stdio, so the local fleet is unaffected.
  • Make the permission check consult the request principal. A caller must not be able to obtain a role's permissions without presenting that role's credential.
  • Ensure gitea_whoami, gitea_resolve_task_capability, and the session-context audit all report the request principal rather than a process-wide value.
  • Ensure concurrent requests carrying different principals cannot observe or overwrite each other's resolved capability state.

Acceptance criteria

  • Capability resolution takes the principal as an explicit input; no gate reads process-level profile state directly.
  • Under stdio, the resolved principal is identical to today's process profile, and existing behavior is unchanged.
  • Two concurrent requests with different principals each resolve their own role, with no cross-contamination in either direction.
  • A request presenting a reviewer principal is refused for an author-only operation, with the existing permission reason code.
  • A request with no resolvable principal fails closed and performs no mutation.
  • The session-context audit reflects the request principal on every call.

Verification

  • Tests cover: stdio principal derivation, explicit per-request principal, concurrent differing principals, missing principal, and a role-mismatch refusal.
  • A concurrency test asserts that interleaved requests do not leak resolved capability state between principals.
  • Full suite compared against the recorded baseline, run from a branches/ worktree.

Non-goals

  • Deciding how the principal is authenticated on the wire. Child 9 owns the endpoint and its authentication.
  • Changing the permission sets attached to each role.
  • Removing the local fleet.
Parent epic: #929. Depends on: #930, #931. ## Problem Role separation today is physical. Five processes run, each launched with a different `GITEA_MCP_PROFILE`, and a caller gets author permissions because it connected to the pipe belonging to the author process. The role is a property of the process, so `gitea_whoami` reports one profile for the lifetime of that process and the capability resolver answers for that one profile. A remote endpoint serves many callers through one listener. If the role stays a process property, then either every remote caller inherits whichever single role the host was started with, or five separate hosts must be deployed and the capacity problem is unchanged. Worse, the current arrangement means a caller that reaches the wrong endpoint silently gets the wrong permission set, because nothing in the request itself carries the role. ## Scope Move the principal from the process to the request. - Define a session principal carrying at minimum: profile name, role kind, Gitea identity, remote, organization, and repository. - Resolve the principal per request, from the authenticated session, and pass it explicitly through capability resolution and every gate that currently reads process-level profile state. - Keep `GITEA_MCP_PROFILE` working as the source of the principal when the transport is stdio, so the local fleet is unaffected. - Make the permission check consult the request principal. A caller must not be able to obtain a role's permissions without presenting that role's credential. - Ensure `gitea_whoami`, `gitea_resolve_task_capability`, and the session-context audit all report the request principal rather than a process-wide value. - Ensure concurrent requests carrying different principals cannot observe or overwrite each other's resolved capability state. ## Acceptance criteria - Capability resolution takes the principal as an explicit input; no gate reads process-level profile state directly. - Under stdio, the resolved principal is identical to today's process profile, and existing behavior is unchanged. - Two concurrent requests with different principals each resolve their own role, with no cross-contamination in either direction. - A request presenting a reviewer principal is refused for an author-only operation, with the existing permission reason code. - A request with no resolvable principal fails closed and performs no mutation. - The session-context audit reflects the request principal on every call. ## Verification - Tests cover: stdio principal derivation, explicit per-request principal, concurrent differing principals, missing principal, and a role-mismatch refusal. - A concurrency test asserts that interleaved requests do not leak resolved capability state between principals. - Full suite compared against the recorded baseline, run from a `branches/` worktree. ## Non-goals - Deciding how the principal is authenticated on the wire. Child 9 owns the endpoint and its authentication. - Changing the permission sets attached to each role. - Removing the local fleet.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#932