Skip to content

Host-side forge proxy: keep git-forge OAuth tokens out of workspaces - #3549

Open
runyaga wants to merge 1 commit into
mcdonc:mainfrom
runyaga:feat/token-free-gitea
Open

runyaga wants to merge 1 commit into
mcdonc:mainfrom
runyaga:feat/token-free-gitea

Conversation

@runyaga

@runyaga runyaga commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Workspaces never hold a git-forge OAuth token. klangkd runs the PKCE authorization for a proxied forge on the host, keeps the access and refresh tokens per workspace and forge host, and injects them into a reverse proxy that containers reach at /forge-proxy/<host>/... with their workspace JWT. This follows the same pattern as /llm-proxy/.

  • Host-side custody.
    • git-credential-klangk forge-auth <host> starts the flow and the browser tab approves it. klangkd consumes the authorization code; it is never relayed to the container.
    • Tokens are refreshed host-side.
    • While a forge is proxied, the browser-delegate relay refuses container-built authorization flows, PAT prompts and credential-cache reads for that forge.
  • Explicit, locked-down policy. Both settings refuse everything when empty, which is the default.
    • forge_proxy_features names the operations the proxy performs: read, git-read, git-push, issues, issue-edit, site-create, collaborators, api-passthrough, or *.
    • forge_proxy_allowed_repos names the repositories: owner/repo, owner/*, or *. A :ro suffix allows reads only; :issues allows reads plus issue work.
  • Site creation.
    • forge_proxy_template_repos and forge_proxy_template_owners allow generating a repository from a listed template.
    • forge_proxy_collaborator_accounts allows adding listed accounts as read-only collaborators.
  • Request handling.
    • Paths with percent-encoding, URL delimiters, dot segments or repeated slashes are refused, and query keys are checked per route.
    • JSON write bodies are validated field by field.
    • In api-passthrough mode, a call whose path names no repository needs an unrestricted * rule.
  • Pushes. A push's ref-update commands are read ahead of the pack. A push that deletes a ref is refused, and a compressed push is refused so the commands stay inspectable. Force-push protection is left to the forge's branch protection.
  • Responses. Forge error bodies on API routes are replaced with a local error; git routes keep the forge's message.
  • Scopes. Provider entries accept an optional OAuth scope, and the granted scope is logged at authorization (never the token).

Configuration

forge_proxy_hosts: "forge.example.org"
forge_proxy_features: "read, git-read, git-push, issues, issue-edit"
forge_proxy_allowed_repos: "team/*, *:issues"
forge_proxy_ca_cert: "/path/to/private-ca.pem"   # optional

Add a KLANGKWS_FEATURE_OAUTH_PROVIDERS entry for the host with "flow": "authorization_code_pkce" and, optionally, a scope.

Testing

  • Unit tests. The forge-proxy, API and git-credential suites pass (1203 tests). klangk/forge_proxy.py and klangk/api/forge_proxy.py have 100% line and branch coverage. Pre-commit passes, xenon included.
  • Live, against Gitea from a local workspace:
    • fetch and push work;
    • reading, creating, commenting on and assigning issues work;
    • a disallowed repository, a read-only repository's push and a ref-deleting push are refused;
    • no token appears in the container's environment or git config.

Open

  • Tokens live in klangkd memory, so a restart means re-authorizing.
  • There is no per-workspace override of the allow-list.

…paces

klangkd runs the PKCE authorization for a proxied forge on the host,
keeps the access and refresh tokens per workspace and forge host, and
injects them into a reverse proxy that containers reach at
/forge-proxy/<host>/ with their workspace JWT. The browser-delegate relay
refuses container-built authorization flows, PAT prompts and
credential-cache reads for a proxied forge, so a container cannot obtain
a forge credential through the bridge either.

Policy is explicit and refuses everything by default:
- forge_proxy_features: read, git-read, git-push, issues, issue-edit,
  site-create, collaborators, api-passthrough, or '*'.
- forge_proxy_allowed_repos: owner/repo, owner/*, or '*', with ':ro'
  (reads only) and ':issues' (reads plus issue work) suffixes.
- forge_proxy_template_repos / forge_proxy_template_owners /
  forge_proxy_collaborator_accounts for site creation from a template
  and read-only collaborators.

A push that deletes a ref is refused, compressed pushes are refused so
the ref updates stay inspectable, paths with encoding or URL delimiters
are refused, and forge error bodies on API routes are replaced with a
local error. Provider entries accept an optional OAuth scope.
@github-actions github-actions Bot added the backport/2.0 Merge also backports the squash commit to stable/2.0 (#3361) label Oct 6, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport/2.0 Merge also backports the squash commit to stable/2.0 (#3361)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant