feat: add X-Docker-Agent-Version and X-Docker-Desktop-Version headers to built-in tools#2795
feat: add X-Docker-Agent-Version and X-Docker-Desktop-Version headers to built-in tools#2795dgageot wants to merge 7 commits into
Conversation
Introduce SetIdentity() to centralize HTTP identity headers (User-Agent, X-Docker-Agent-Version, X-Docker-Desktop-Version) used across built-in tools. Add pkg/desktop/version.go to fetch Docker Desktop version from the backend socket's /update endpoint once per process (memoized, 2s timeout).
Update api tool to use the new SetIdentity() helper, ensuring consistent identity header formatting across all built-in tools. Add test coverage for identity headers.
Update fetch tool to use SetIdentity(), including robots.txt requests. Remove redundant userAgent parameter from fetchRobots(). Add test coverage for identity headers.
Update openapi tool to use SetIdentity(), establishing unified identity header handling. Add test coverage for identity headers.
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟡 NEEDS ATTENTION
One medium confirmed finding in the new code.
| CurrentVersion string `json:"currentVersion"` | ||
| } | ||
| _ = ClientBackend.Get(ctx, "/update", &info) | ||
| return info.CurrentVersion, nil |
There was a problem hiding this comment.
[MEDIUM] Empty Desktop version cached for full 5-minute TTL when Desktop is unavailable at first call
GetVersion always returns (info.CurrentVersion, nil) — even when the HTTP call to Desktop's /update endpoint fails (Desktop not running, socket not present, timeout, etc.). In that case info.CurrentVersion is the zero value "", and since go-memoize only caches results where error == nil, the empty string gets cached for the full 5-minute TTL.
This contradicts the PR description's claim that the lookup "recovers automatically if Desktop starts after docker-agent": if GetVersion is first called before Desktop is ready, the X-Docker-Desktop-Version header will be silently omitted for up to 5 minutes even after Desktop becomes available — rather than recovering on the next request.
Fix: return a non-nil error when the lookup fails so go-memoize does not cache the failure:
if err := ClientBackend.Get(ctx, "/update", &info); err != nil {
return "", err // not cached; next caller retries
}
return info.CurrentVersion, nilThis way a transient failure (Desktop not yet up) never poisons the cache.
What
Adds two HTTP identity headers to every outbound request made by the
api,fetch, andopenapibuilt-in tools, on top of the existingUser-Agent:User-AgentCagent/<version> (<goos>; <goarch>)X-Docker-Agent-Version<version>(e.g.dev,1.2.3)X-Docker-Desktop-Version4.74.0Operator-supplied headers in the agent config still win over these defaults —
SetIdentityis called first, then caller headers.Why
Backends that receive built-in-tool traffic (Docker Hub APIs, Hub gateway, ...) want to attribute requests to a specific docker-agent install and the Docker Desktop version it's running alongside, the same way other Docker traffic is already attributed.
User-Agentwas the only signal so far and is brittle to parse.How
pkg/useragent.SetIdentity(req)is the single source of truth for these headers. All three built-in tools route through it, including therobots.txtfetch in thefetchtool.pkg/desktop.GetVersion(ctx)readscurrentVersionfrom Docker Desktop'sbackend.sock/updateendpoint, with:memoize.Call[string]) — recovers automatically if Desktop starts after docker-agent or is upgraded mid-session, while still costing one socket call per tool request at most;""when Desktop isn't running, in which case the header is omitted.No new dependencies (
go-memoizeandgo-cachewere already vendored).Privacy / security note
X-Docker-Agent-Versionadds no information thatUser-Agentdidn't already broadcast.X-Docker-Desktop-Versionis genuinely new data sent to whichever host the operator configured. It's no more leaky thanUser-Agentis for<goos>; <goarch>, but if a privacy/opt-out mode is desired we can layer one on top later. Not adding one now keeps this PR focused.Tests
pkg/useragent: pins User-Agent +X-Docker-Agent-Versionon a fresh request.api,fetch,openapi: each existing header test now also asserts the two identity headers reach the test server.task lintis clean.Commits
feat: add identity headers for docker-agent and Docker Desktop trackingrefactor: use SetIdentity() in api toolrefactor: use SetIdentity() in fetch toolrefactor: use SetIdentity() in openapi toolfix(desktop): make Desktop version lookup TTL-based and context-independentrefactor: simplify Desktop version lookup and useragent docsrefactor(desktop): plumb context through GetVersion