Background
We want to reuse Cachew for private Git repositories hosted outside GitHub. Authentication for these repositories is not based on a GitHub App, so the existing GitHub-specific integration cannot provide the required credentials.
Cachew should support repository-scoped external credential providers without embedding provider-specific authentication logic in the main process. Providers should return a complete HTTP authorization value so repositories can use schemes such as Basic or Bearer authentication.
Proposed configuration
Add a repeatable git-credential-command block:
git-credential-command "private-git" {
command = [
"/usr/local/bin/private-git-credential",
"--provider-option", "value",
]
remotes = [
"https://git.example.com/project/repository",
]
timeout = "5s"
refresh-before = "5m"
}
Configured repository URLs are canonicalized and matched exactly. A provider's credential must not be applied to a repository that was not assigned to it.
Proposed internal interface
type Credential struct {
Authorization string
URLScope string
}
type Provider interface {
Credential(
ctx context.Context,
repositoryURL string,
) (credential Credential, matched bool, err error)
}
Authorization is the complete HTTP authorization value, for example Basic ... or Bearer .... URLScope is the canonical repository URL to which it may be applied.
Providers can be composed in order, stopping at the first match. Existing in-process authentication integrations can be adapted to this interface without invoking a plugin binary.
A matched provider failure should fail closed rather than falling back to unauthenticated access. Unmatched repositories retain the existing authentication behavior.
Plugin interface
A plugin implements credential acquisition for one canonical repository URL:
type CommandResult struct {
Authorization string
ExpiresAt time.Time
}
type CommandHandler interface {
Credential(ctx context.Context, remoteURL string) (CommandResult, error)
}
A helper library can expose ServeCommand, request/response encoding functions, and an optional CLI entry point for Go implementations. Plugins may be written in any language as long as they implement the protocol.
Main process and plugin protocol
Cachew executes the configured command directly without a shell and applies the configured timeout. One request and one response are exchanged per invocation.
Cachew writes one newline-terminated JSON object to stdin:
{"version":1,"remote_url":"https://git.example.com/project/repository"}
The plugin writes one newline-terminated JSON object to stdout:
{"version":1,"authorization":"Bearer example-token","expires_at":"2026-08-10T12:00:00Z"}
Proposed schemas:
const ProtocolVersion = 1
type Request struct {
Version int `json:"version"`
RemoteURL string `json:"remote_url"`
}
type Response struct {
Version int `json:"version"`
Authorization string `json:"authorization"`
ExpiresAt time.Time `json:"expires_at"`
}
Protocol requirements:
remote_url is the canonical upstream repository URL.
authorization contains the complete HTTP Authorization header value.
expires_at is a future RFC 3339 timestamp.
- Requests and responses are size-limited and strictly decoded.
- Unknown fields, extra JSON values, unsupported versions, invalid authorization values, and expired credentials are rejected.
- Plugin failures, timeouts, unsuccessful exits, and invalid responses fail the matched operation.
Credential lifecycle
Successful credentials are cached only in memory:
- Canonicalize the requested repository URL and select its exact provider match.
- Return an unexpired cached credential when available.
- Refresh credentials within the configured
refresh-before window.
- Coalesce concurrent refreshes for the same provider and repository.
- Scope the returned authorization to the canonical repository URL.
- Discard credentials after expiration; never persist them.
Applying credentials
The authorization should be applied to Git as repository-scoped HTTP configuration through GIT_CONFIG_* environment entries:
GIT_CONFIG_KEY_<n>=http.<canonical-repository-url>.extraHeader
GIT_CONFIG_VALUE_<n>=Authorization: <provider-value>
This supports Basic and Bearer credentials without a shell-form credential helper or credentials in Git subprocess arguments. This part is related to #402.
The same provider should authenticate:
- Background clone, fetch, and mirror operations.
- Direct upstream Git HTTP requests.
- Cache-miss and stale-reference fallbacks.
- Push requests.
- Git LFS requests.
Security properties
- Exact canonical repository matching and URL scoping.
- No shell execution for provider commands.
- No credentials in subprocess command-line arguments.
- In-memory caching only.
- Credentials are never logged or written to stderr.
- Strict protocol and authorization-value validation.
- Fail-closed behavior for matched provider failures.
Background
We want to reuse Cachew for private Git repositories hosted outside GitHub. Authentication for these repositories is not based on a GitHub App, so the existing GitHub-specific integration cannot provide the required credentials.
Cachew should support repository-scoped external credential providers without embedding provider-specific authentication logic in the main process. Providers should return a complete HTTP authorization value so repositories can use schemes such as Basic or Bearer authentication.
Proposed configuration
Add a repeatable
git-credential-commandblock:Configured repository URLs are canonicalized and matched exactly. A provider's credential must not be applied to a repository that was not assigned to it.
Proposed internal interface
Authorizationis the complete HTTP authorization value, for exampleBasic ...orBearer ....URLScopeis the canonical repository URL to which it may be applied.Providers can be composed in order, stopping at the first match. Existing in-process authentication integrations can be adapted to this interface without invoking a plugin binary.
A matched provider failure should fail closed rather than falling back to unauthenticated access. Unmatched repositories retain the existing authentication behavior.
Plugin interface
A plugin implements credential acquisition for one canonical repository URL:
A helper library can expose
ServeCommand, request/response encoding functions, and an optional CLI entry point for Go implementations. Plugins may be written in any language as long as they implement the protocol.Main process and plugin protocol
Cachew executes the configured command directly without a shell and applies the configured timeout. One request and one response are exchanged per invocation.
Cachew writes one newline-terminated JSON object to stdin:
{"version":1,"remote_url":"https://git.example.com/project/repository"}The plugin writes one newline-terminated JSON object to stdout:
{"version":1,"authorization":"Bearer example-token","expires_at":"2026-08-10T12:00:00Z"}Proposed schemas:
Protocol requirements:
remote_urlis the canonical upstream repository URL.authorizationcontains the complete HTTPAuthorizationheader value.expires_atis a future RFC 3339 timestamp.Credential lifecycle
Successful credentials are cached only in memory:
refresh-beforewindow.Applying credentials
The authorization should be applied to Git as repository-scoped HTTP configuration through
GIT_CONFIG_*environment entries:This supports Basic and Bearer credentials without a shell-form credential helper or credentials in Git subprocess arguments. This part is related to #402.
The same provider should authenticate:
Security properties