DEV Community

Cover image for Docker just shipped the agent wall I wanted. It's off by default.

Docker just shipped the agent wall I wanted. It's off by default.

Sam LABBE on October 09, 2026

If you updated Docker Desktop recently — version 4.63 or later — a new command is already installed on your machine: docker agent. No setup, no ann...
Collapse
 
koda2026 profile image
Harun - solo dev •

sam, this is a brilliant and necessary teardown of the new docker agent architecture. your point that "defaults are policy" is the most critical takeaway. a sandbox that exists but defaults to off is just a false sense of security waiting for a copy-paste error.

as someone building a secure js sandbox for ai code execution (koda), the "fail closed" (default-deny) philosophy is non-negotiable. if an agent can't be trusted, the environment must assume hostility from line one. on constrained hardware like a $150 phone, a runaway agent making unauthorized network calls or consuming resources is catastrophic, making strict isolation even more vital.

your insight that "the tool supply chain is context supply chain" is spot on. even in a perfect vm, if an mcp server's tool description contains a prompt injection, the model will execute it. isolation contains the blast radius, but it doesn't fix the poisoned context.

given that pinning by digest is the only real fix for mutable tags, do you think orchestrators should eventually enforce strict digest pinning at the parser level for any remote kit/mcp reference, rather than just recommending it in the docs?

fantastic write-up. the seatbelt thread continues! 🐯

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
koda2026 profile image
Harun - solo dev •

sam, the "two-writer rule" is a massive insight. you’re absolutely right: parser-level enforcement fixes mutability, but it doesn't fix provenance. separating the proposal (the config) from the approval (a distinct, non-agent process) is the exact separation of duties needed to prevent self-attestation loops.

and i have to say, reading that my "$150 phone is a genuinely good adversarial lab for that boundary" is one of the best compliments i’ve received as a builder. when you have zero margin for error or wasted resources, every single control truly has to earn its place. it forces ruthless architectural discipline.

thank you for taking the time to break this down so clearly. the "warn-and-record vs. hard-fail" grading by blast radius is going straight into my mental model for koda’s sandbox. 🐯🛡️

Thread Thread
 
slabb profile image
Sam LABBE •

That discipline — every control earns its place — is the thing most teams only learn after an incident, and you're starting with it, which is the unfair advantage. One habit to keep as KODA grows: break your own sandbox before anyone else does, and write down what fell out — the failures you publish become the spec everyone else trusts. Build well, Harun. 🐯

Thread Thread
 
koda2026 profile image
Harun - solo dev •

sam, "break your own sandbox before anyone else does" is going straight into the koda engineering handbook.

you’re absolutely right: publishing the failures is what actually builds the spec and the trust. treating the sandbox like a chaos engineering target from day one, rather than waiting for a post-incident review, is the only way to stay ahead of the curve.

thank you for the mentorship-level advice and for taking the time to engage so deeply on this. it means a lot coming from someone with your experience.

build well! 🐯

Thread Thread
 
slabb profile image
Sam LABBE •

Building the handbook entry is the real move — written rules outlive the motivation that wrote them. And the $150 phone stays my favorite adversarial lab for exactly the reason you landed on: zero margin makes every control argue for its place. Come back with what the chaos runs shake loose; that list will be the best part.

Collapse
 
samii profile image
Waqas Ahmed Waseer •

The "wall that never turned on is invisible to diffs" thread is where the real fix lives, and I'd push it one layer further. CI can lint the YAML for runtime: sandbox: true, but that's still a declaration, not a fact — the only ground truth is what actually ran on the machine. The primitive missing here is runtime attestation: the sandbox already spins up a fresh VM per run, so the cheapest durable control is an emit-at-start receipt — sandbox on/off, image digest, allowlist version — written somewhere the agent can't edit, keyed to the run ID.

That also answers the unsigned-traces gap. Sam is right that OpenTelemetry gives you what happened, not provably what happened — but you don't need attested auditing for the whole agent to get value. A signed "sandbox state at start" record plus the egress proxy's allow/deny log covers the exfiltration question, which is the threat the wall exists for. Everything downstream of a sealed start-state is explainable; everything upstream of it is theater.

For Chris's question on a recommended team default: don't rely on the YAML flag alone — wrap docker agent in a launcher that injects --sandbox unconditionally and pins the toolset list, then treat any run that bypasses the launcher as an incident. The flag is a seatbelt; the launcher is the interlock.

Collapse
 
slabb profile image
Sam LABBE •

This is the convergence that makes the thread more than a thread: the emit-at-start receipt — sandbox state, image digest, allowlist version, keyed to the run, outside the agent's write path — is the exact primitive NoireBox ships (run_witness plus the sealed attestation), and "the flag is a seatbelt; the launcher is the interlock" is the deployment story I was missing. Everything downstream of a sealed start-state is explainable — that's the line the README should carry. Credit where the design lands: an attestation nobody can edit, keyed to the run ID, is what turns the wall from a feature into a fact.

Collapse
 
glenallen profile image
Glen Allen •

That’s an important distinction: passing CI proves what was reviewed, not necessarily what the runtime eventually loads. Binding the reviewed configuration to a hash-identified artifact would close part of that gap, but I’d also want the runtime to verify the identity before execution. Otherwise, a correct artifact can still be replaced or overridden after the check. The stronger invariant is that the exact configuration approved by policy is the one the agent actually runs with.

Collapse
 
slabb profile image
Sam LABBE •

That's the invariant — and it's the one that just shipped in the receipt layer as state_binding: the receipt declares the state version it was judged against, and divergence fails loudly. Your phrasing is the general form: the exact configuration approved by policy is the one the agent actually runs with — approved, hashed, verified at execution, with the hash of the reviewed artifact bound into the run's own receipt so the CI-passing config and the booting config can never silently diverge. The hash binding closes half the gap; your runtime-side verification closes the other — both halves needed, neither sufficient alone.

Collapse
 
glenallen profile image
Glen Allen •

That makes the invariant much more concrete. I like that divergence fails loudly instead of leaving the receipt looking valid against a state the agent never actually used. One follow-up question is how that failure is surfaced operationally: can the system distinguish a stale-state rejection from a policy denial or an execution failure? Keeping those outcomes separate would make recovery much easier, because each points to a different fix, refresh the state, review the permissions, or investigate the runtime.

Thread Thread
 
slabb profile image
Sam LABBE •

Yes — and that's what the finding statuses are for: the divergence, the denial, and the failure are three different sealed outcomes, not three flavors of "error." Stale state ships as its own finding (receipt_expired_by_state_change in receipt v0.2.2) — the fix it routes to is refresh-and-re-run: new state version, new gate pass, new receipt. Policy denial is the gate's own sealed decision with the policy version cited — routes to permission review. Execution failure is the runtime's observation — exit codes and the transport attempt, routes to the runtime. The statuses are the recovery routing table: when each sealed outcome names its fix in the note, the operator reads the next action instead of reverse-engineering it. Your three-way split is exactly why the taxonomy is flat and boring on purpose.

Collapse
 
analista_83 profile image
Sammi De Blas •

Good catch - a wall that never turned on is invisible to diffs, so I would treat sandbox enforcement as a runtime assertion: the launcher verifies the effective sandbox state (container config or cgroup namespace) and fails closed if it differs from what the YAML declares. On the chicken-and-egg edge, telemetry from the teams that opted in becomes the proof for the fleet that opted out - which is exactly why the default should flip only once that data shows the permission model held.

Collapse
 
slabb profile image
Sam LABBE •

That's the runtime-assertion shape, and it's the same invariant that just shipped in the receipt layer: state_binding — the receipt declares the state version it was judged against, and divergence fails loudly instead of silently. Declared YAML versus effective sandbox is the same gap as declared predicate versus executed bytes: the launcher verifying the cgroup namespace before the run is that receipt's prerequisite move. And your telemetry point solves the chicken-and-egg the honest way: the opt-in fleet isn't the market for the default, it's the evidence pipeline for it — the default flips when the data says the wall held, not when the docs say it should have.

Collapse
 
analista_83 profile image
Sammi De Blas •

Two things on state_binding. First, version granularity: if the state version is a global epoch, divergence detection is either too coarse or too noisy - a receipt judged against v417 stays valid while unrelated fields churn, and any change invalidates everything. Scope the binding to the field set the predicate actually read (dependency-set invalidation, not a global epoch), otherwise you get silent staleness on one side and constant false divergence on the other. Second, the fail-loud path needs its own receipt: when the launcher aborts on divergence, that event gets logged with the declared-versus-effective diff in the same log as successful runs - otherwise "the wall held" is unprovable, you can only show it never fired, not that it fired and blocked.

On flipping the default from evidence: agreed, with one design rule - pre-register the flip criterion (divergence rate below X over N runs, zero silent escapes) before the fleet data lands, or "the data says the wall held" becomes post-hoc storytelling. And the opt-in fleet is biased toward clean workloads, so pair it with shadow mode on a non-opt-in sample: compute the effective sandbox and diff without enforcing, to get an unbiased divergence rate before the default flips.

Thread Thread
 
slabb profile image
Sam LABBE •

Both land, and the field-set scoping is the sharper half: a global epoch is the state_binding equivalent of a SWE-bench score — one number that hides which fields the predicate actually depended on. Dependency-set invalidation is the transport attempt at the state layer: the receipt binds to the specific fields the check read, and only those fields' changes trigger the divergence. The fail-loud receipt is the same inversion as everywhere else — a wall that held is a claim unless it's in the journal, and the divergence event beside the successful runs is what makes "the wall held" provable instead of absent. The pre-registered flip criterion and the shadow mode are the controls that separate evidence from story: you can't measure the wall from the fleet that opted in, and you can't claim "the data says the wall held" without pre-registering what "held" means before the data lands.

Collapse
 
salparvez profile image
Sal Parvez | ML Systems •

"A trace is a debug story. An audit needs a record you can prove was never edited" is the line I'd keep from this. I hit the same thing from a different side. The rules my agents run under are a file too, and if the process that loads them can also rewrite them, they drift and nobody notices. So I hash that file when it loads and check it again before every pass. A changed rule shows up as a finding instead of a quiet change in behavior. Your reply to Carlos about the baseline being sealed by whoever reviewed it, not by the connector, is the same idea one layer over.

Collapse
 
chrissellers profile image
Chris Sellers •

The line that stuck with me is "Defaults are policy." Shipping a well built sandbox behind --sandbox false means the wall exists for people who already read the docs, and the host run path stays the path of least resistance. That is the same shape as every "my agent deleted the containers" post on this site.

What I like about the design itself is the pairing of an allowlisted toolset with default-deny egress. The YAML review catches what you meant to attach. The proxy catches the shell tool that then curls something you never listed. I would keep both; treating the VM as a substitute for a tool allowlist leaves the host key and catalog attach problems untouched.

Curious whether you have a recommended default for teams that adopt docker agent: force sandbox true in the shared YAML template, or refuse to run unless the alias already pins it.

Collapse
 
slabb profile image
Sam LABBE •

Both, honestly — they fail differently. The shared template is policy (it catches the team that forgets); the alias pin is the interlock (it catches the run that bypasses the template — a local override, a copied command, a CI job built from the wrong file). @samii's launcher framing below is the version I'd ship: the template sets the default, the wrapper enforces it unconditionally, and any run that bypasses the launcher is an incident rather than config drift. And your point about the VM not substituting for the allowlist is the one I'd underline — the proxy catches egress, the allowlist catches attachment, the sandbox catches blast radius. Three walls, three different leaks; the team that keeps all three is the one that survives the copy-pasted run.

Collapse
 
chrissellers profile image
Chris Sellers •

Three walls, three different leaks is a better summary than mine. I also like treating a run that bypasses the launcher as an incident instead of drift, because that turns a config question into something someone has to explain.

One follow up on the interlock: make the wrapper write a small record for every run it starts, with the template hash and sandbox flag. Then the incident check is a diff between runs the platform saw and runs the launcher recorded, and the copy pasted run shows up on its own instead of waiting for someone to notice.

How would you detect a bypass in practice today: the launcher logs runs and you reconcile, or the runtime refuses anything without a launcher token?

Thread Thread
 
slabb profile image
Sam LABBE •

Both, but sequenced — and your record makes the first one shippable this week. Reconcile first: the launcher writes the per-run record before the run starts, not on success — template hash plus sandbox flag — because the incident you can't see is the run nobody logged. Then the check is the diff between two streams the platform already has: runs the runtime saw, runs the launcher recorded. A copy-pasted run exists in one stream and not the other, and the missing pairing is the incident — mechanical, no one has to notice anything. The template hash is the load-bearing half: it commits the record to what was actually launched, so a legit-looking record can't be reused as cover for a different run.

The refusal is the endgame, but it carries a condition: it's only as strong as the token's arming path. If the launcher token lives in an env var or a config file the agent can read, the wall degrades silently — the runtime keeps refusing, the agent keeps passing a minted token, and the dashboard shows the wall holding. That's the canary lesson: a gate armed through the same control plane the agent can reach isn't grade-1, whatever it calls itself. So the runtime-side refusal wants the token checked by a process on the far side of a boundary the agent doesn't write — a uid-gated socket that fail-closes where the platform can't identify the peer is the cheapest version I know that survives an audit.

The sequencing is the honest part: reconcile detects on stock platforms today and turns a bypass into an incident someone explains; the runtime refusal prevents once the token is out of the agent's reach. Ship the first, gate the second on that condition — a refusal without it is the same three walls one more time, just quieter.

Thread Thread
 
chrissellers profile image
Chris Sellers •

Reconcile first, then gate refusal on a token the agent cannot mint, is the sequencing I was missing. Treating a missing pairing as the incident is what makes that first step shippable without new infrastructure.

One follow up on the arming path: if the launcher token still lives where the agent can read it, what is the cheapest move that puts the check on the far side of that boundary without rebuilding the runtime?

Are you running a uid-gated socket pattern in production yet, or is that still the design you would reach for once reconcile is in place?

Thread Thread
 
slabb profile image
Sam LABBE •

Running code as of this week, not a design sketch: the receipt tool's keyholder is exactly that pattern — a daemon loads the operator key once into memory, and every signature request is a unix-socket round trip; --allow-uid gates the connecting peer by uid (the kernel answers SO_PEERCRED out-of-band on Linux), and it fails closed where the platform cannot identify the peer — on macOS the refusal is itself the verifiable answer, readable from the client as unavailable(<daemon platform>). After start, the PEM file leaves the agent-reachable volume entirely: the material exists in one process and nowhere else.

The cheapest move for your launcher, without touching the runtime, is the same shape in three pieces: a sidecar process that loads the launch credential once; a socket file created 0600 on a path outside the agent's mounts — reachability replaces readability; and a peer-credential check answered by the kernel, not by anything the client sends. Your launcher's signer call becomes a round trip on that socket — .sign(bytes) and nothing else crosses. A token in an env var is a fact the agent can copy; a socket path is a capability — copying it grants nothing, because the authority question ("which process connected") is answered about the connection, not asserted inside it.

The honest boundary is deployment, not crypto: the sidecar runs as a different uid than the agent, and the socket path must live where the agent cannot create or replace it — that premise is priced in the docs rather than hidden. Sequence it the way we said: reconcile first, the sidecar bolted on when a bypass has to become impossible instead of visible — it's an afternoon, not a rebuild.

Collapse
 
panthpatel profile image
Panth Patel •

"Defaults are policy" matches what I saw on our side. Our agent runs per ticket inside a stack that has no other mode: its own branch in every repo, its own services, a copy of the dev database. There is no host run to forget a flag on, and the steps that leave the box (merge, deploy, rollback) are scripts, not the agent. Your tool-description point is the one that still gets through that. Do you review MCP tool definitions in the same PR as the YAML?

Collapse
 
slabb profile image
Sam LABBE •

Yes — and that's the pattern that shipped in v0.11 (toolset-pin): capture the MCP server's listTools response at registration, hash it, seal the hash as the reviewed baseline — then the diff runs at every session start, and a changed description is a finding rather than a surprise. The PR review is exactly where the baseline earns its seal: the reviewer sees the tool surface and the pin together, and an upstream MCP update that shifts the surface fails the diff instead of the agent. Your stack — per-ticket branch, scripts for every leave-the-box step — is the architecture that makes the flag unnecessary; the rest of us need the pin because the run path is where our defaults go to die.

Collapse
 
panthpatel profile image
Panth Patel •

Sealing the listTools hash at registration turns an upstream MCP update into a review event instead of a runtime surprise. When the diff fails at session start, what happens to the run: does the session refuse to start, or drop the changed tool and carry on with the rest of the toolset?

Thread Thread
 
slabb profile image
Sam LABBE •

Both, tiered by what the tool can touch — the same grading the thread gave digest pinning. A changed description on a read-only tool is a finding printed at session start (toolset_drift: changed tool dropped, baseline version cited, diff sealed) and the session carries on with the witnessed remainder — the sealed baseline means the dropped tool is a record, not a silence. A changed tool in the egress-relevant set — anything that can reach the network, the filesystem, or credentials — fails the session: that's the SCAR-45 lesson at the toolset layer, a surface that changed under a receipt that vouches for the old one is exactly the drift the pin exists to catch. And the third case your question implies — the MCP server rolls a breaking change that moves a tool's whole semantics — is the baseline-update PR: the drift finding IS the review event, and the new hash gets sealed after the same human eyes that reviewed the YAML. Refuse-to-start stays reserved for the run that can't witness its toolset at all — no sealed baseline is the "wall that never turned on" of the toolset layer.

Collapse
 
ascendra_ventures_92c21f2 profile image
Ascendra Ventures •

The point that matters most here is the gap between what an agent can read and what it is allowed to do. A sandbox that exists but ships switched off protects nobody, because most teams never revisit defaults once something works.

When we put agents into a client environment, the egress policy and tool permissions are written into the definition of done before any prompt work starts. If the agent can only reach the three hosts it needs, a bad tool call becomes a failed request instead of an incident. It also makes the review conversation with a security team much shorter.

I would be curious whether the YAML config lets you pin the sandbox setting per agent so it cannot be overridden locally. That would make it much easier to enforce across a team.

Collapse
 
bloqarl profile image
Carlos (Bloqarl) •

"Defaults are policy" is the whole post for me.

Someone made the same point on my coding agents post yesterday: a sandboxed, read-only agent can still leak things through one "helpful" curl. Default-deny egress kills that path no matter what the model decided, so it really should be the default.

The MCP part worries me more though. Tool descriptions are text the model reads as instructions, and most reviews of the YAML stop at the toolset name. Did you see anything in the docs about pinning or diffing the tool definitions themselves, not just the image digest?

Collapse
 
slabb profile image
Sam LABBE •

Straight answer: no — the docs pin the image, and the tool definitions ride outside the pin. A pinned image pins the server code, which constrains what it can serve, but tool definitions are produced at runtime: config-driven, env-dependent, and in the remote case not pinned at all — an SSE/HTTP MCP reference is a URL, and whatever it serves this morning is what your model reads. So the layer you're pointing at is the real one: the toolset is the lockfile nobody writes.

The shape I'd trust: capture the listTools response at registration, hash it, seal the hash as the reviewed baseline — then on every session start, diff the served definitions against it and surface a changed description as a finding, not silently. One caveat carried over from the image-pin argument: the baseline has to be sealed by whoever reviewed it, not by the process that connects — a baseline the connector can rewrite drifts with the thing it measures. Your curl example is the same philosophy one layer up, by the way: default-deny egress pins the network path, toolset diffing pins the context path — the two halves of the supply chain the model actually travels.

Collapse
 
glenallen profile image
Glen Allen •

The default-off issue highlights a broader deployment problem: a security control can be well designed and still provide no protection if the deployed configuration never activates it. At IT Path Solutions, we see the value in treating critical agent safeguards as deployment requirements rather than optional runtime preferences. A CI check could reject configurations that enable shell or filesystem access without the required sandbox, network restrictions, and reviewed tool definitions. That shifts the burden from remembering a security flag to proving that the deployed agent meets a minimum security baseline. The important distinction is between having a safeguard available and making sure every production path actually uses it.

Collapse
 
slabb profile image
Sam LABBE •

The CI-as-bouncer framing is right — the check doesn't have to be clever, it has to be mandatory. One gap worth designing for: CI proves the config at review time, drift happens at run time. The YAML that shipped and the runtime that boots should be the same artifact, hashed — otherwise the baseline is a document, not a control.

Collapse
 
analista_83 profile image
Sammi De Blas •

Off-by-default is exactly right for a feature like this: an agent runtime that lands on millions of machines via auto-update should stay opt-in until its permission model has been proven in production. The toolsets-as-YAML part is what makes it auditable - when the surface an agent can touch is a diffable file, drift between what it can read and what it may do becomes something CI can catch instead of something an incident reveals.

Collapse
 
slabb profile image
Sam LABBE •

Agreed — and your CI point is the strongest case for the toolsets-as-YAML model: a diffable surface is something a pipeline can actually judge.

The drift I'd add: CI can catch a surface that got wider, but it can't catch a wall that never turned on. The YAML can declare a perfect sandbox while the invocation runs without --sandbox, and the diff stays green. Baking runtime: sandbox: true into the agent closes most of that; the remaining gap is the local override — a policy problem wearing a config costume.

And "proven in production" has a chicken-and-egg edge: the feedback loop that hardens the permission model only runs for teams that switch it on. The opt-out excludes exactly the fleet that would prove it.

Collapse
 
analista_83 profile image
Sammi De Blas •

Two sharp edges on dependency-set invalidation worth closing: the set should be captured at read time (the concrete fields and their hashed values) rather than declared in YAML, because a check that derives one field from two others misses writes to either parent if the receipt only keys on the derived name; and register the shadow-mode flip threshold before the first run, since once divergence events are visible the definition of held quietly drifts toward whatever number keeps the default off.

Collapse
 
slabb profile image
Sam LABBE •

Both edges close the design, and the first is the difference between a claim and a receipt: a declared set is YAML asserting what the check reads; a read-time capture binds the concrete fields and their hashed values as they were actually touched — so a predicate deriving one field from two parents inherits their writes automatically, because the parents are in the captured set whether or not anyone declared them. And the hash is what makes it a state binding rather than a watchlist: divergence isn't "field A changed," it's "field A's value at read time was this digest, and it isn't anymore." Names are claims; reads with hashes are facts.

The second edge is the one that would have bitten quietly. Pre-registration only bites if it happens before the first shadow event exists to tune against — once divergence numbers are visible, "held" drifts toward whatever threshold keeps the default off, which is the custody lesson in decision clothing: the only criterion that's wrong by drift is the one chosen after the data. Seal the flip threshold at provision the way the governance pin gets sealed — published, hashed, dated before the fleet lands. After that, the number can only be argued with, not moved.

Collapse
 
intelliflame profile image
Intelliflame •

Default-deny egress inside a disposable VM is the primitive agent tooling has been missing, and 'opt in' is sadly the part that will decide whether it protects anyone. From running untrusted scans in our own stack, the pattern that actually held up was treating egress as an incident surface: per-engagement allowlists, no ambient credentials, and logging every outbound request so unexpected traffic gets investigated rather than ignored. Good to see Docker shipping the wall — now the interesting question is how many teams flip the default.

Collapse
 
slabb profile image
Sam LABBE •

The flip won't come from virtue — defaults move when obligations do. Seatbelts and airbags shipped optional until an incident or a regulator made the opt-out expensive, and the Article-12 class of rules is that mechanism arriving for agent sandboxes; insurers will move before regulators do, because "the wall existed but was off" reads the same to a claims adjuster as "there was no wall." Your incident-surface framing is the third independent arrival of the same instrument today — outbound requests to their own sink with their own alert — which suggests the floor is forming faster than the default. So my read on your closing question: the teams that flip early won't be the careful ones, they'll be the ones whose next contract, audit, or premium demands it — and the postmortems that force the flip are already being written, 72 days at a time.

Collapse
 
dev_in_the_fog profile image
Jason Y. (dev_in_the_fog) •

Container lifecycle management and resource headroom are such common production pain points. Great job highlighting this with reproducible examples.

Collapse
 
slabb profile image
Sam LABBE •

Thanks — with one honest caveat: everything reproducible in the post comes straight from the public repo and docs; I just walked it in an evening.

Your lifecycle angle is the part I under-weighted. The sandbox isn't a flag, it's a VM per run — which quietly turns "is the wall on?" into a capacity question: bootstrap time, memory headroom, a proxy in the hot path. That cost is probably why it ships off by default, and it's the number I'd want published before anyone mandates the sandbox fleet-wide.

Collapse
 
baumgaerben profile image
baumgaerben •

VM sandbox với default-deny egress là bước đi đúng hướng cho supply-chain security — cuối cùng cũng có isolation level thực sự cho agent workload thay vì chỉ頼 vào network policy ở host level. Declarative YAML agents giúp version-control được behavior, nhưng lo ngại là schema có đủ expressive cho các pattern phức tạp (multi-step reasoning, dynamic tool discovery) hay sẽ buộc dev phải workaround qua custom entrypoint scripts.

MCP toolsets integration thú vị, nhưng thấy thiếu clarity về versioning/pinning strategy — khi upstream MCP server update breaking changes, agent YAML có mechanism lock xuống specific capability set không? Hay sẽ gặp vấn đề "works on my machine" khi CI/CD pull latest?

Default-off cho egress deny là pragmatic choice cho backward compat, nhưng production workloads nên bật mặc định và dùng allowlist explicit. Curious về performance overhead của VM sandbox so với gVisor/Kata — có benchmark nào trong source read không? PS: the tool I meant is on labagent .tech