supply chain
Running agents without inheriting everyone's supply chain
Agents can install dependencies and invoke tools while working with your files and credentials. That makes the dependency choices part of the agent's security boundary. This page covers controls to review, then explains Caletta's choice of Go and direct Apple API bindings.
The new surface
What agents changed
An agent can choose a dependency, install it, and run it while trying to finish a task. A workflow that relied on a person reviewing each of those steps needs explicit controls when the steps are delegated.
- Dependency resolution is now an agent action. A human choosing a package deliberates; an agent adds an import to make the build pass. The decision that used to be a review gate is now an inner-loop step, executed dozens of times a day.
- Hallucinated package names are an attack primitive. Models suggest plausible-sounding packages that don't exist. Attackers register those names and wait. The agent installs the package without blinking. This failure mode has no pre-AI analogue.
- Tool plugins are the sharpest edge. MCP servers and agent plugins are third-party executables, commonly installed by
npx/uvxone-liners that fetch whatever is latest at launch, and they run holding the operator's credentials. That is remote code execution by configuration, normalized as setup instructions. - Weights and prompts are dependencies too. Pickle-format checkpoints execute code on load; that is the reason the safetensors format exists. Most teams still treat model files as data.
- The blast radius changed. A compromised build tool used to get the attacker your build. A compromised agent dependency gets them a live environment with real credentials, real files, and network access.
- Silent version drift. A hosted model changes underneath you with no pin and no changelog. Your system's behavior moves without a diff anywhere you can read.
The controls
Controls to apply
- No install-time code execution. Prefer toolchains where fetching a dependency runs nobody's code. Go has no
setup.pyand no postinstall hook; against agent-driven installs, that single property removes the largest category of attack. - Run tool servers as pinned, reviewed binaries. An MCP server should be a self-contained binary you reviewed and pinned, not an
npx <name>@latestthat resolves at launch. A static binary is a reviewable, hashable artifact; a launch-time fetch can change the executable without review. - Use the transparency log for what it proves. Go's checksum database proves two things: the bytes you fetched are the bytes everyone else fetched, and a published module version cannot be quietly replaced after the fact. It does not prove the module is safe. Those are different properties. The log eliminates targeted substitution and silent revision; it does nothing about a malicious module published in the open. Reviewing the module itself remains necessary.
- Vendor, and build offline. A vendored tree with
-mod=vendorgives you a build that needs no network and can be audited as a snapshot. This is the same property as exit: a system you can build offline is a system you can keep. - Pin the toolchain, scan for reachability, check capabilities.
GOTOOLCHAINpins the compiler itself.govulncheckreports vulnerabilities your code paths actually reach, not everything in the tree.capslockreports what capabilities (network, exec, filesystem) a dependency can exercise. Together they make "read the sensitive path" a bounded task instead of an aspiration. - Load weights as data, never as code. safetensors or equivalent data-only formats, hashes recorded, versions pinned. And for hosted models, treat the version as a dependency: pin what can be pinned, and eval what can't: behavioral tests are the only changelog a hosted model gives you.
- Scope what agents hold. Credentials and policy belong in a layer around the agent, not in its environment; work belongs in environments you can reset and inspect. Any implementation of those two controls is better than none. Caletta ships one of each (
skifffor policy, credentials, and audit around agents, andcovefor resettable, inspectable Mac environments), but the recommendation stands whether or not you use ours.
Audit the auditor
Why this stack is Go, bound directly to Apple's APIs
Caletta aims to keep the sensitive code small and open enough for a security team to read. That requirement informed two engineering choices.
Go reduces the dependency surface we need to manage. Fetching modules does not run install hooks, the public checksum database supports checking module contents, and builds can be vendored and inspected. The runtime module, mlx-go, has a fourteen-line go.sum in the snapshot described here. A small dependency tree makes review more practical; it is not evidence that each dependency is safe.
Direct Apple API bindings keep the stack close to the platform it already depends on. On a Mac, Apple supplies the silicon, operating system, and code-signing infrastructure. Binding to MLX and Metal uses that existing platform, while avoiding the additional Python distribution, wheels, and package tree of a conventional local ML setup. This reduces the additional components we need to review. It also creates substantial dependence on Apple's hardware and APIs.
The concessions
What this doesn't buy
- Go modules can be malicious too. The advantage is fewer execution points and a smaller surface to read. Malicious code can still enter the tree. A hostile module in a fourteen-line tree is still hostile; it's just findable.
- Other ecosystems are catching up. npm and PyPI have real provenance work now: sigstore attestations, signed publishing. The durable difference is defaults: what the median install does with no flags set.
- The ML research ecosystem is Python and will stay Python. The scope of this page is the runtime, proxy, agent, and tooling layer: the code that holds credentials and touches your files. Nobody should pretend the training-research world is migrating.
- Platform dependence is real. Apple silicon is the measured platform today. Broader support is an ambition; deep MLX integration currently ties the runtime to one vendor's hardware.
- Weights stay unauditable by inspection. No layer of this page fixes that. You cannot read a checkpoint the way you read a dependency; the only real controls are behavioral: hashes, pins, evals, and the standing test from the rest of this site. Unplug, and measure what still works.
These controls reduce the amount of code and behavior a team needs to trust. They do not make a compromised dependency harmless.
The system these choices serve is on the north star; the current state of each component is on the odometer.