Skip to content

feat: authenticated multi-user hosting — per-user commit attribution (web) + Streamable HTTP MCP transport#701

Closed
tuxo83 wants to merge 2 commits into
MrLesk:mainfrom
tuxo83:feat/mcp-http-transport
Closed

feat: authenticated multi-user hosting — per-user commit attribution (web) + Streamable HTTP MCP transport#701
tuxo83 wants to merge 2 commits into
MrLesk:mainfrom
tuxo83:feat/mcp-http-transport

Conversation

@tuxo83

@tuxo83 tuxo83 commented Jun 27, 2026

Copy link
Copy Markdown

feat: authenticated multi-user hosting — per-user commit attribution (web) + Streamable HTTP MCP transport

Summary

Two complementary changes that let Backlog.md be hosted for a team behind a single OIDC
provider, with every commit attributed to the authenticated end-user:

  1. Web — commit author from auth-proxy headers. When the web server runs behind an
    authenticating reverse proxy (e.g. oauth2-proxy), the commit author is derived
    from the forwarded identity headers; the committer stays the server identity.
    Opt-in, header names configurable (oauth2-proxy defaults).
  2. MCP — Streamable HTTP transport. backlog mcp start --http serves the MCP server
    over HTTP as a single long-lived process (no per-client stdio process), behind the
    same proxy, reusing the same per-request identity → commit-author mechanism.

This PR contains two reviewable commits (web first, then the MCP transport that builds on
it). They are submitted together because the MCP transport reuses the web commit's new
commit-context.ts and the shared authorFromProxyHeaders() helper, so they share config
plumbing and cannot be opened as independent cross-fork PRs.

Commit 1 — web commits attributed to the auth-proxy user

  • New opt-in config commit_author_from_proxy_headers (default off) with configurable
    header names proxy_author_email_header / proxy_author_name_header
    (defaults x-forwarded-email / x-forwarded-preferred-username).
  • A small AsyncLocalStorage-backed commit-context.ts binds a per-request git author;
    the commit builders append --author when one is set. Concurrency-safe, no signature
    threading through Core.
  • The web server wraps mutating routes so each commit is authored by the proxied user.

Commit 2 — Streamable HTTP transport for MCP

  • backlog mcp start --http [--port 6421] [--host 127.0.0.1]; stdio stays the default.
  • Uses the SDK's WebStandardStreamableHTTPServerTransport (no new dependency), stateless
    (fresh transport + watchers-disabled server per request) → no per-client process spawned.
  • Per-request author resolution is shared with the web server via authorFromProxyHeaders()
    (extracted from the web server, which now reuses it); request handling is wrapped in
    runWithCommitAuthor().
  • AUTHENTICATED-HOSTING.md: end-to-end guide (Keycloak + oauth2-proxy, web + MCP, and the
    two MCP client auth options — Bearer token for agents, interactive OAuth for humans).

Behavior & safety

  • Both behaviours are opt-in and default off — no change for existing users.
  • The git committer always stays the server identity; only the author is overridden,
    so history remains auditable.
  • The config field is intentionally not added to needsMigration (no forced config
    rewrite on existing projects).

Testing

  • bunx tsc --noEmit → clean. npx biome check . (changed files) → clean.
  • bun test:
    • commit-author.test.ts — author from the request context, committer preserved, fallback
      to git identity.
    • mcp-http-transport.test.ts — end-to-end via the MCP SDK client over HTTP: a tool call
      with forwarded identity headers produces a commit authored by the proxied user (committer
      preserved), and falls back to the git identity without headers.
    • config-commands.test.ts — config serialize/parse roundtrip.

Related Tasks

Happy to create/associate a backlog task per the PR template, and to split the two commits
into separate PRs if preferred once the first lands.

tuxo83 added 2 commits June 27, 2026 06:52
…user

When `commitAuthorFromProxyHeaders` is enabled, the web server sets each commit's
git AUTHOR from the identity forwarded by an auth proxy; the committer stays the
server identity. So in a hosted multi-user setup, "who did it" is the authenticated
end-user while the host robot remains the committer.

- Opt-in: `commitAuthorFromProxyHeaders` (default false).
- Configurable headers: `proxyAuthorEmailHeader` (default x-forwarded-email),
  `proxyAuthorNameHeader` (default x-forwarded-preferred-username) — oauth2-proxy defaults.
- Per-request author via AsyncLocalStorage (concurrency-safe, no signature threading);
  `git commit --author` applied centrally in GitOperations.
- Covers all web content mutations (tasks, docs, decisions, milestones, drafts, reorder).
- config get/set + README + commit-author test.
…tribution

`backlog mcp start --http` serves the MCP server over Streamable HTTP (Web Standard
transport) as a single long-lived process, instead of one stdio process per client.
Intended to run behind an authenticating reverse proxy (e.g. oauth2-proxy): the proxy
forwards the end-user identity headers, and each request's resulting commit is
attributed to that user — the same `commit_author_from_proxy_headers` model as the
web UI, so web and MCP share one identity mechanism and one proxy.

- Stateless transport (fresh transport + watchers-disabled server per request), so no
  per-client process is spawned and requests are independent.
- Per-request author resolution is shared with the web server via a new
  `authorFromProxyHeaders()` helper (extracted from the web server, which now reuses
  it). The request handling is wrapped in `runWithCommitAuthor()`.
- New CLI flags: `--http`, `--port` (default 6421), `--host` (default 127.0.0.1).
  stdio stays the default — nothing changes for local clients.
- `createMcpServer`/`McpServer` gain an `enableWatchers` option (off for the
  per-request HTTP server).
- Docs: AUTHENTICATED-HOSTING.md — end-to-end multi-user hosting guide (Keycloak +
  oauth2-proxy, web + MCP, Bearer-token and interactive-OAuth client options).

Reuses the existing MCP SDK dependency (WebStandardStreamableHTTPServerTransport),
no new dependencies.

Tests: end-to-end via the MCP SDK client over HTTP — a tool call with forwarded
identity headers produces a commit authored by the proxied user (committer preserved),
and falls back to the git identity when no headers are present.
@MrLesk

MrLesk commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Alex's Agent: Thanks for the detailed PR. I’m closing this because it does not fit the current project direction: we are keeping the product small, CLI/MCP-first, and git-authorized.

This PR adds authenticated multi-user hosting semantics, a web auth/proxy trust surface, per-user hosted commit attribution, a new Streamable HTTP MCP transport, and hosted setup docs/config around Keycloak/oauth2-proxy. Even though it is opt-in, that is a broad product and security surface we are not taking into the current repository scope.

I did not find a small independent bugfix in the diff that should be split out separately. If there is a narrower local CLI/MCP bugfix behind this work, please open that as a separate focused PR.

Future PRs in this area should link an issue first, or open one before implementation so scope can be agreed.

@MrLesk MrLesk closed this Jul 1, 2026
@tuxo83 tuxo83 deleted the feat/mcp-http-transport branch July 5, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants