feat(server): build via BuildKit client when buildkitd address is set - #409
Merged
Merged
Conversation
Release artifacts are built by shelling out to the docker CLI (buildx create/build/rm), which fails with "executable file not found" when the plugin runs in an environment without the docker binary, e.g. compiled into a process shipped in a distroless image. Follow up on the configurable buildx driver (#398), which made the builder configurable but still requires the CLI. Introduce an alternative build path that talks to an already running buildkitd directly through github.com/moby/buildkit/client: the per-build buildx builder provisioning and removal disappear, and the build maps to a single Solve with the dockerfile.v0 frontend (context tar streamed via the session upload provider, secrets via secretsprovider, tar exporter into the same pipe the buildx path writes to). The buildkitd address is set per project via the configure endpoint (buildkitd_address) or, as a process-wide fallback, via the TRDL_BUILDKITD_ADDRESS env var; the per-project value wins. Supported address schemes: unix://, tcp:// (direct gRPC), docker-container://, kube-pod:// (via docker/kubectl exec). With no address set the docker CLI path stays byte-for-byte unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Vasily Marmer <vasily.marmer@flant.com>
govulncheck reports five advisories against the v2.2.4 buildkit pulls in transitively (GO-2026-5064/5338/5475/5622/5758), all fixed in v2.2.5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Vasily Marmer <vasily.marmer@flant.com>
The kubernetes-driver notes added in #398 said rootless BuildKit requires the `baseline` PodSecurity level. It does not fit `baseline` either: buildx's rootless pod spec sets `seccompProfile: Unconfined` and the `unconfined` AppArmor annotation (driver/kubernetes/manifest/manifest.go), and Unconfined is rejected by both the Seccomp and the AppArmor control at `baseline`. The builder namespace has to be `privileged` or exempt from PodSecurity admission. This also removes the contradiction with the external-buildkitd section added by this PR, which already states that BuildKit needs a relaxed seccomp/AppArmor profile even when rootless. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Vasily Marmer <vasily.marmer@flant.com>
v1.18.6, pulled in by buildkit, carries GO-2026-5841 (out-of-bounds read in the s2 decoder), fixed in v1.18.7. The symbol is not reachable from trdl, but it was the only module-level advisory this branch added over main. With the bump, govulncheck reports exactly the same module-level set as main -- GO-2026-5932 (x/crypto/openpgp, unmaintained) and GO-2022-0646 / GO-2022-0635 (aws-sdk-go v1 S3 crypto), none of which have a fixed version. The plugin binary is byte-identical in size. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Vasily Marmer <vasily.marmer@flant.com>
… BuildKit mode The BuildKit client path attached only the context uploader and the secrets provider to the Solve session, so no registry credentials ever reached buildkitd: with image-resolve-mode=pull and no cache, a private base image could not be resolved at all, while the docker CLI path receives those credentials from the docker config through buildx. Attach the same docker-config auth provider. The goroutine streaming the build context blocked on write forever whenever a build stopped consuming it: the upload provider closes the reader only once buildkitd pulls the context, so a Solve failing before that leaked the goroutine and its 64 MiB buffer on every failed release. Close the reader when the build returns, which covers the exec path too. Alongside that, fail closed on an address whose scheme carries no endpoint instead of failing at build time, wait for the build log to drain instead of dropping its tail and leaking the exec path scanner goroutine, pass a context to the exported address validation, and rename ContextPath after what it actually holds. github.com/docker/cli becomes a direct dependency and adds docker/docker-credential-helpers as an indirect one; both were already in go.sum. Signed-off-by: Aleksei Igrychev <aleksei.igrychev@palark.com>
The tests around the BuildKit path asserted the maps the very same helpers had just built, so removing the context uploader from the Solve session — which breaks every build — left the whole suite green. Assert instead that the session serves the build context, the secrets and the registry credentials, that a failed build unblocks the context producer, and that the log writer drains before it returns. Add server:test:ai, since tests behind the ai_tests tag were never executed by any task. Signed-off-by: Aleksei Igrychev <aleksei.igrychev@palark.com>
Tests behind the ai_tests build tag never ran anywhere, which left the only test exercising the BuildKit Solve path a no-op. Start buildkitd in a job and point the smoke test at it. Signed-off-by: Aleksei Igrychev <aleksei.igrychev@palark.com>
The buildkitd address example used a plaintext tcp:// endpoint without saying that the build context, the build secrets and the mac signing credentials all travel over that connection, that the client neither encrypts it nor authenticates the daemon, and that one daemon is shared by every project pointed at it. Make securing the channel and isolating the daemon the administrator's stated responsibility. Signed-off-by: Aleksei Igrychev <aleksei.igrychev@palark.com>
The e2e module consumes the server module through `replace … => ../server`, so the BuildKit dependency chain added to the server module left its go.mod incomplete: both `task lint` and `task e2e:test:e2e` failed with "updates to go.mod needed" before compiling a single test. Run go mod tidy, which also carries over the go directive bump to 1.25.9 that buildkit requires. Signed-off-by: Aleksei Igrychev <aleksei.igrychev@palark.com>
alexey-igrychev
force-pushed
the
feat/server/buildkit-client-builder
branch
from
August 5, 2026 15:50
f365285 to
72df4a6
Compare
Nothing exercised the chain from `release` through the BuildKit client to the published artifact: the unit smoke test covers a bare Solve, and the e2e suites all build through the docker CLI. The flow_vault suite already switches build backends by environment for the buildx driver check, so give it the same hook for a buildkitd address and run it in CI against a buildkitd container. The suite's fixture reads two build secrets inside the build, so the run also proves the secrets reach the build through the session rather than only that their ids map. Signed-off-by: Aleksei Igrychev <aleksei.igrychev@palark.com>
This was referenced Aug 5, 2026
`ai_server` read as "AI server" rather than as the kind of tests the job runs, which is what `unit_server` and `unit_client` express. Rename it to `ai_tests_server`, and add the new BuildKit e2e job to the coverage job's needs so its artifact is not uploaded in a race with the job producing it. Signed-off-by: Aleksei Igrychev <aleksei.igrychev@palark.com>
vmrm
approved these changes
Aug 5, 2026
vmrm
left a comment
Contributor
There was a problem hiding this comment.
Approving. I read the seven commits on top of my four from #408 (73a6cbf9..0aac05e5) — they close real gaps, and I'd rather this PR go in than mine.
What the delta fixes, and why each one matters:
- Registry credentials (
buildkitSessionAttachables). The buildx path gets them from the docker config through the CLI; the client path had no auth attachable at all, soimage-resolve-mode=pullcould only reach public registries. Every private base image would have failed at resolve time, and my e2e never covered that because it pulls public images. - Draining the build context (
defer contextReader.Close()inBuilder.Build). A build that fails before the context is fully read left the goroutine feeding the pipe blocked on write forever. This is a leak on the error path of both builders, not just the BuildKit one. - Log tail (
logWriterreturning a wait function). Closing the pipe writer did not guarantee the scanner goroutine had drained it, so the last lines of a build log — including the interesting ones on failure — could be dropped. Both call sites now wait. - Empty endpoint after the scheme.
tcp://alone passed the scheme allowlist and then failed deep inside the client. Rejecting it atconfiguretime is the right place, consistent with the #398 driver allowlist. - CI against a real buildkitd —
End-to-end tests (BuildKit client)running the full release cycle overtcp://, plus theai_tests_serversmoke job. This is what #408 was missing: everything there was verified against unit tests and my own stand, and there was no positive marker in CI that the BuildKit path had actually built anything. WithTRDL_TEST_BUILDKITD_ADDRESSthreaded into the e2e configure, the same flow-vault suite now covers both backends. - QUICKSTART security section. Correct, and worth stating explicitly: the plugin ships the whole context and every build secret (including the mac-signing certificate, its password and the notary key) over that connection,
tcp://is plaintext and unauthenticated, and write access to<project>/configuretherefore becomes equivalent to access to the release secrets. The daemon-is-shared point is the one people will miss.
All checks are green on 0aac05e5, including the two new jobs.
One nit, not blocking: ValidateBuildkitdAddress and resolveBuildkitdAddress now take a context.Context that neither of them uses — it only ripples out into path_configure.go and the tests. Either use it or drop the parameter; if it's there for a planned reachability check against the address, a // TODO naming that would help.
#408 is closed in favour of this PR.
alexey-igrychev
marked this pull request as ready for review
August 5, 2026 18:40
This was referenced Aug 5, 2026
alexey-igrychev
added a commit
that referenced
this pull request
Aug 5, 2026
… certificate (#416) ## Summary Two release-build defects that predate #409 and affect both build paths: an oversized build log line hangs the release task, and a passwordless mac signing certificate fails the signer stage. ## Why Both turn an ordinary situation into a broken release, and neither reports a cause the operator can act on. `bufio.Scanner` stops at `bufio.ErrTooLong` on a line above 64 KB, after which the goroutine reading the pipe returns. The build keeps writing into that pipe, so the next write blocks forever — `cmd.Run` on the docker CLI path, the progress display on the BuildKit client path — and the release task hangs with no error, until the plugin process is restarted. The generated service Dockerfile mounts `certificate_password` unconditionally and reads it with `cat` under `set -e -o pipefail`, while both build paths served that secret only when the stored password was non-empty. `--mount=type=secret` defaults to `required=false`, so BuildKit mounts nothing and the `cat` takes the whole `RUN` down. A passwordless p12 is legitimate. ## Key changes - `server/pkg/docker/builder.go` — `logWriter` raises the line limit to 1 MiB and drains the pipe to `io.Discard` in a deferred call, so parsing that stops for any reason can no longer block the writer. An oversized line now costs the rest of the build log, reported through the logger, instead of the release. - `server/pkg/docker/mac_signing.go`, `server/pkg/docker/buildkit.go` — the password secret is always served, with an empty value when there is none, so quill receives an empty `QUILL_SIGN_PASSWORD`. Both paths mount the same ids and are changed together. - `server/pkg/docker/buildkit_test.go` — `TestBuildkitSecretsData_NoPasswordNoCredentials` asserted the defect (`NotContains` the password id); the passwordless half is replaced by the new test below, and what remains covers the no-credentials case. ## Verification - Mutations run, each reverted after the run, each killing exactly the test that covers it: - removing the drain from `logWriter` → `TestAI_LogWriter_OversizedLineDoesNotBlockTheBuild` fails after its 30s timeout, reporting the blocked writer; - restoring `if Password != ""` in `buildkitSecretsData` → `TestAI_MacSigningSecrets_PasswordlessCertificateIsStillServed` fails; - restoring the same condition in `GetMacSigningCommandMounts` → the same test fails, so both halves of it discriminate. - Not run: a release with a passwordless certificate through the mac-signing e2e suite. Its quill stub validates the five `QUILL_*` variables, so accepting an empty password there is a change to the fixture rather than a run of the existing one. ## Review focus / risks - The 1 MiB limit is a judgement call: below it a line is buffered whole, above it the remaining log is dropped. The hang is gone either way, and the drop is logged. - An empty `QUILL_SIGN_PASSWORD` reaches quill for a passwordless certificate. This is the intended reading of "no password", but it is a behaviour change for anyone who stored an empty password expecting the release to fail. ## After merge - [ ] Close #410 and #411 (linked by `Fixes` in the commits, so a squash merge closes them automatically — verify it did). --------- Signed-off-by: Aleksei Igrychev <aleksei.igrychev@palark.com>
alexey-igrychev
added a commit
that referenced
this pull request
Aug 5, 2026
`docs/_includes/reference/cli/trdl_use.md` still described the `trdl use` help text from before #391, so `task docs:gen` produced a diff on a clean checkout of `main` and every PR touching the generator picked up this unrelated change. Regenerated on its own, no hand edits. Noticed while working on #409, where the same diff appeared and was reverted to keep that PR scoped. Signed-off-by: Aleksei Igrychev <aleksei.igrychev@palark.com>
alexey-igrychev
added a commit
that referenced
this pull request
Aug 5, 2026
## Summary A project can now choose its buildx driver and driver options through `configure`, instead of only through the environment of the Vault process. That is the only route available when the secret engine is compiled into a host process whose environment the administrator cannot set, where the `kubernetes` driver was unreachable until now. Continues #412 by @vmrm, rebased onto `main` after #409. ## What - `configure` accepts `buildx_driver` — `docker-container` or `kubernetes`, empty by default — and it wins over `TRDL_BUILDX_DRIVER`. - `configure` accepts `buildx_driver_opts` — a list, one `--driver-opt` per element, empty by default — and it wins over `TRDL_BUILDX_DRIVER_OPTS_*`. Elements are passed through verbatim, so `nodeselector=disktype=ssd,zone=a` stays one option. - Each of the two settings resolves on its own: configuration, then environment, then the previous default of `docker-container` with no options. A field omitted or set to a blank value means "not configured" and falls back to the environment instead of clearing it, so building with no options while the environment defines some requires unsetting those variables. - An unsupported driver is rejected whatever its source: writing `buildx_driver=docker` fails `configure` and stores nothing, while an unsupported `TRDL_BUILDX_DRIVER` still fails when the builder is created. The error names the setting the value came from. - `configure` rejects `buildx_driver` or `buildx_driver_opts` written together with `buildkitd_address`, and leaves an already stored configuration untouched: that address replaces the buildx path entirely, so the driver settings would have no effect. Blank values count as unset on both sides of that check. - When the address comes from `TRDL_BUILDKITD_ADDRESS` instead, the combination cannot be refused at write time — the variable is process-wide and can change after a project is configured — so the release reports `the configured buildx driver settings are not used` in both the release task log and the plugin log. - An update that omits the buildx fields clears the stored ones: `configure` replaces the whole document. - With neither field set, `docker buildx create` is invoked exactly as before, down to the argument list, and a configuration stored before these fields existed reads back with both fields empty and builds as it did. - UNVERIFIED: that `buildx_driver_opts` and `buildkitd_address` reach the release build at all — deleting their assignment in `pathRelease` leaves every suite green. The driver now has an end-to-end guard; these two would need one job each, because the value has to break the build when it is lost. ## Why Everything else a project needs already lives in `configure` — the Git repository, the S3 credentials, the signature quorum — because Vault has no other per-project channel. The build backend was the exception: it could only be set through the environment of the process that hosts the plugin, and a host process that ships the engine as a built-in plugin may expose no way to set one, which left such an installation on the default driver permanently. Environment-only was the alternative, and #409 shows why it does not hold: the same argument produced `buildkitd_address` as a `configure` field, so keeping the driver out of `configure` would have split one decision across two mechanisms.
alexey-igrychev
pushed a commit
that referenced
this pull request
Sep 2, 2026
…422) ## Summary A project can now build releases with no `docker` binary next to the plugin and no long-lived BuildKit daemon: `buildkitd_driver=kubernetes` has the plugin create a buildkitd Pod for one build, talk to it with the BuildKit client over `pods/exec`, and delete it afterwards. Until now those two properties were mutually exclusive — the buildx drivers shell out to the `docker` CLI, while `buildkitd_address` needs a daemon somebody else keeps running and shares between projects and builds. Continues #409 and #419. ## What - `configure` accepts `buildkitd_driver` — `kubernetes`, empty by default — and `buildkitd_driver_opts`, a list of `name=value` pairs. - With `buildkitd_driver=kubernetes` a release creates one Pod `trdl-builder-<build id>` in the target namespace, waits for its readiness probe, streams the build through the API server's exec channel, and deletes the Pod when the build ends. No external binary is executed on any part of that path. - The permissions needed in that namespace are `create`, `get` and `delete` on `pods`, plus `create` on `pods/exec`. A namespaced Role is enough and no `apps` API group is touched. The plugin needs no network route to the builder pod. - The Pod is deleted on every exit path the plugin controls: build success, build failure, a canceled release, a builder that never becomes ready, and a create whose response was lost after the API server had already persisted the pod. VERIFIED: a release through this driver against a kind cluster left no pod behind, and each cleanup path is held by a mutation rather than by a passing test alone — see the comment. - The Pod is not deleted if the plugin's own process dies outright. The `deadline` option caps how long an abandoned builder keeps running, via `activeDeadlineSeconds`; it is a whole number of seconds of at least `1s`, because anything smaller truncates to zero and anything fractional loses its remainder. UNVERIFIED: that the kubelet enforces it as expected here — a dispatch of the opt-in workflow with a short `deadline` would settle it. - Driver options are validated when the configuration is written: `buildkitd_driver_opts=replicas=3`, a negative quantity such as `requests.cpu=-1`, and a truncating `deadline=500ms` all fail `configure` and store nothing, instead of failing a release hours later. Accepted names are `annotations`, `deadline`, `image`, `labels`, `limits.cpu`, `limits.ephemeral-storage`, `limits.memory`, `namespace`, `nodeselector`, `requests.cpu`, `requests.ephemeral-storage`, `requests.memory`, `rootless`, `serviceaccount` and `timeout`. - `configure` rejects `buildkitd_driver` written together with `buildkitd_address`, `buildx_driver` or `buildx_driver_opts`, and a rejected write leaves an already stored configuration untouched. - With `TRDL_BUILDKITD_ADDRESS` set on the Vault process the combination cannot be refused at write time, so the build states that the buildkitd driver settings are unused, in the release log and the plugin log — the treatment `buildx_driver` already gets. - The builder is a bare Pod with `restartPolicy: Never`, not a Deployment: a replacement pod would be a builder the release is not connected to, and `apps/v1` cannot carry `activeDeadlineSeconds` at all, so a Deployment builder survives a crashed plugin forever. - The builder container runs `privileged` by default, because BuildKit needs it; `rootless=true` trades that for seccomp `Unconfined` and the `unconfined` AppArmor annotation. Either way the namespace has to admit what `baseline` PodSecurity refuses, and whoever can write a project's `configure` chooses the namespace, the ServiceAccount and the image of that pod — `configure` write access is pod-create access wherever the plugin's Role reaches. Both QUICKSTART locales state this as the administrator's responsibility. - Defaults: image `moby/buildkit:buildx-stable-1`, or its `-rootless` variant when `rootless=true`; readiness wait `2m` (`timeout`), the same default buildx uses for the same wait; no deadline unless configured. - The two fields have no `TRDL_*` counterpart, unlike every other build-backend setting. That is deliberate: an environment twin is what makes a value impossible to reject at write time, and rejecting bad options at write time is the point of a separate vocabulary. - New dependencies: `k8s.io/api`, `k8s.io/apimachinery`, `k8s.io/client-go`. Measured on this branch as unique modules in `server/go.sum` after `go mod tidy` and a successful `go build ./...`: 240 → 278, so 38 new modules, 117 new linked packages, and the plugin binary goes 48.8 MB → 61.1 MB. - Nothing changes for a project that sets neither field: the docker CLI path and `buildkitd_address` behave exactly as before, and `buildx_driver`/`buildx_driver_opts` keep meaning what they meant. ## Why `buildkitd_address` was added in #409 for a plugin embedded in a distroless image, where `docker` cannot exist. It removes the binary but not the daemon: trdl connects to a buildkitd somebody else runs, so that daemon outlives the build and is shared by whatever else points at it. Where the build executes untrusted instructions, a daemon that survives the build and reaches the next project is the thing being designed against — and the buildx `kubernetes` driver, which does provision one builder per build, is unreachable there because it goes through the `docker` CLI. The ephemeral-builder property and the no-docker property could not be had together. They are separable, because the CLI is not what talks to Kubernetes. The buildx kubernetes driver is pure Go: it creates its objects with `client-go` and dials the builder with `remotecommand` over `pods/exec`, executing no external process anywhere on that path. The `docker` requirement comes only from trdl driving that driver through the CLI wrapper. **Which leaves a choice this PR should not make alone.** *Import `github.com/docker/buildx/driver/kubernetes`.* Least new code, and behaviour already proven in the wild. Two things argue against it, both measured rather than estimated. Cost: v0.31.0 adds 88 modules to `server/go.sum` and takes the plugin binary to 97.6 MB; v0.36.1 adds 183, forces `go 1.26.3` on the module, and pulls buildkit, docker/cli and containerd up with it. For scale, #409 accepted 46 modules and +8.9 MB — that is a precedent, not a rule, but 88 is twice it. Blocker: buildx's kubernetes factory cannot be handed a `rest.Config`. `driver.Register(&factory{})` registers a zero value and its `cc ClientConfig` field is unexported, so the in-cluster path is reachable only through an undocumented `EndpointAddr = "kubernetes://"` with a nil `ContextStore`, and any other endpoint nil-panics inside `ConfigFromEndpoint`. *A minimal implementation on `client-go`,* which is what this PR does: 38 modules, +12.3 MB, and roughly 400 lines. The pod calls go through a plain REST client rather than the generated typed client, which reaches every API group and its apply configurations — the same trim buildx made for itself in v0.33.0 (`5a7f7c28`, "kubernetes: trim client-go dependency surface"), and worth 23 MB of binary here. The cost is that the Deployment/StatefulSet manifest matrix, the pod chooser and the driver-opt surface are now trdl's to maintain, and the option vocabulary is a documented subset rather than all of buildx's. If the answer is the first one, the conversation is worth having about driver factories generally rather than the kubernetes one alone: buildx's `remote` driver would come with it, and it already implements TLS to an external buildkitd through `cacert`/`cert`/`key`/`servername` driver-opts — which `buildkitd_address` has no answer for today. The honest caveat is that it reads that material from files by absolute path, which a plugin holding its configuration in Vault does not have. On naming: `buildx_driver_mode=in-process` reusing `buildx_driver_opts` was the first shape tried and was dropped. It keeps the buildx name for a path with no buildx in it; ten of buildx's documented kubernetes driver-opts would be accepted by `configure` and rejected only at release time, which is what `ff76158` refused to allow for this exact field pair; and a mode field valid for exactly one value of a sibling field encodes three states in four cells. A `buildkitd_*` field also follows the precedent already in this surface — an externally managed daemon was not modelled as `buildx_driver=remote` either. --------- Signed-off-by: Vasily Marmer <vasily.marmer@flant.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an alternative release-build path that talks to an already running
buildkitdthrough the BuildKit Go client instead of shelling out todocker buildx. The address is set per project viaconfigure(buildkitd_address) or process-wide viaTRDL_BUILDKITD_ADDRESS; with no address set, thedocker buildxpath is unchanged.Continues #408 by @vmrm — their four commits are taken as-is, with four commits on top that close the findings from reviewing them.
Why
The secret engine is also compiled into host processes shipped in distroless images that contain no
dockerbinary and no docker socket. Thereexec.CommandContext(ctx, "docker", …)fails withexecutable file not foundbefore the buildx driver choice from #398 can matter at all. Pointing the plugin at an external buildkitd removes the CLI dependency forunix:///tcp://while keeping the docker CLI as the default for every existing installation.The address lives in per-project
configurerather than only in the environment because module-based deployments have no way to inject env vars into the Vault pod, andconfigureis already the per-project channel for s3/git/quorum settings.Key changes
From #408:
server/pkg/docker/buildkit.go— the release build mapped onto a singleSolveagainst an external buildkitd: frontenddockerfile.v0, in-context service Dockerfile viafilename,no-cache,image-resolve-mode=pull, the context tar streamed through the session upload provider, build secrets and mac-signing credentials served bysecretsprovider.FromMapunder the ids the generated Dockerfile mounts, tar exporter writing into the same pipe the buildx path writes to, progress through the existing logger.server/pkg/docker/builder.go—NewBuilderreturns a BuildKit-mode builder when an address resolves: nobuildx create, andRemoveis a no-op since nothing is provisioned per build.server/path_configure.go— optionalconfigurefieldbuildkitd_address, validated at configure time against a scheme allowlist (unix,tcp,docker-container,kube-pod), fail-closed like the feat(server): make buildx driver configurable via env #398 driver allowlist. Per-project config wins over the env fallback.github.com/moby/buildkit v0.31.2direct,golang.org/x/syncpromoted from indirect, 46 new modules overall, plugin binary 42.6 MB → 51.5 MB per the measurements in feat(server): build via BuildKit client when buildkitd address is set #408 (not re-measured here).containerd/v2pinned to v2.2.5 andklauspost/compressto v1.18.7 so the module-level govulncheck advisory set matchesmain.On top of that:
fix(server): the Solve session now also attaches the docker-config auth provider — without it no registry credentials reached buildkitd, so withimage-resolve-mode=pulland no cache a private base image could not be resolved at all, while the CLI path gets those credentials through buildx.Builder.Buildcloses the context reader on return: the upload provider closes it only once buildkitd pulls the context, so a Solve failing earlier left the goroutine streaming the context blocked on write forever with its 64 MiB buffer. An address whose scheme carries no endpoint (unix://) is now rejected at configure time instead of failing on the next release.logWriterreturns a wait function, so the tail of a build log is not dropped and the exec path stops leaking its scanner goroutine.test(server): the session wiring, the context release and the log drain are asserted against behaviour rather than against the maps the same helpers build.server:test:airuns everything behind theai_teststag, which no task ran before.ci: jobai_serverstartsmoby/buildkit:v0.31.2and points the smoke test at it, so the only test that exercises the Solve path stops being a no-op.docs: the buildkitd section states that the build context, the build secrets and the mac-signing credentials travel over that connection, that the client neither encryptstcp://nor authenticates the daemon, that the address is a trust boundary for whoever can writeconfigure, and that one daemon is shared by every project pointed at it — all of it the administrator's responsibility.github.com/docker/clibecomes a direct dependency (already in the graph via the connhelpers) and addsdocker/docker-credential-helpersas an indirect one; both were already ingo.sum.Verification
tcp://, using the same recipe the new CI job uses: streamed tar context in,.trdl/Dockerfilepicked byfilename, a secret mount read back out of the exported artifacts tar (TestAI_BuildkitSmoke, 2s).defer contextReader.Close()(the producer test then reports the producer still blocked); removing<-donefrom the log wait; removing the empty-endpoint check. Before these tests existed, removing the context uploader from the session — which breaks every build — left the whole suite green.docker-container://andkube-pod://transports live (onlytcp://was exercised); binary size after thedocker/cli/cli/configaddition.Review focus / risks
server/pkg/docker/buildkit.go— the Solve mapping and the session attachables.tcp://is plaintext and unauthenticated: the client has no TLS options, so an operator who ignores the documented requirement ships build secrets and the mac-signing notary key in the clear. Adding mTLS options is deliberately left to a separate change; the current mitigation is documentation only.go.sumchurn: MVS bumps of existing indirect deps alongside the new modules.certificate_passwordunconditionally, so a passwordless mac-signing certificate fails in the signing stage on both paths; a build log line above 64 KB kills thebufio.ScannerinlogWriterand blocks the release.After merge
license/snykandsecurity/snykchecks; their findings are only visible inside the werf Snyk org.docs/_includes/reference/cli/trdl_use.md, stale onmainsince feat(client): add support for pinning explicit versions in main commands #391 and deliberately left out of this PR.