feat: authenticated multi-user hosting — per-user commit attribution (web) + Streamable HTTP MCP transport#701
feat: authenticated multi-user hosting — per-user commit attribution (web) + Streamable HTTP MCP transport#701tuxo83 wants to merge 2 commits into
Conversation
…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.
|
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. |
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:
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).
backlog mcp start --httpserves the MCP serverover 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.tsand the sharedauthorFromProxyHeaders()helper, so they share configplumbing and cannot be opened as independent cross-fork PRs.
Commit 1 — web commits attributed to the auth-proxy user
commit_author_from_proxy_headers(default off) with configurableheader names
proxy_author_email_header/proxy_author_name_header(defaults
x-forwarded-email/x-forwarded-preferred-username).AsyncLocalStorage-backedcommit-context.tsbinds a per-request git author;the commit builders append
--authorwhen one is set. Concurrency-safe, no signaturethreading through
Core.Commit 2 — Streamable HTTP transport for MCP
backlog mcp start --http [--port 6421] [--host 127.0.0.1]; stdio stays the default.WebStandardStreamableHTTPServerTransport(no new dependency), stateless(fresh transport + watchers-disabled server per request) → no per-client process spawned.
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 thetwo MCP client auth options — Bearer token for agents, interactive OAuth for humans).
Behavior & safety
so history remains auditable.
needsMigration(no forced configrewrite 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, fallbackto git identity.
mcp-http-transport.test.ts— end-to-end via the MCP SDK client over HTTP: a tool callwith 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