The map
The one rule that shapes all of it
The console process holds the database connection, the JWT signing secret, and object-storage credentials. Running a plugin there would hand over the whole platform to whoever published it. So code plugins only ever execute somewhere that is already yours — your training container, or your own laptop. To change control-plane behaviour, cross a process boundary instead: a webhook for a notification channel, an external MCP server for an agent tool. That is also why there is noruns_in: console plugin kind, and why one will not be added.
Declarative packs cannot contain code
A pack the platform only reads (recipe, benchmark, playbook, prompt) is refused at publish
time if it contains an executable file at its root — .py, .sh, .so, .js and about twenty
other suffixes.
template/ is the exception, and the reason is worth knowing: it is scaffolding that sf new
copies into your repository, which you then run in your container. The platform never touches
it.
This turns “the platform never executes declarative packs” from a promise into something a script
checks. The error, if you trip it:
Three gates on executable plugins
Every plugin whose code runs passes the same three digest checks. They exist so that what ran on the cluster is exactly what you published.1
At publish
The server unpacks your directory and recomputes the content digest, then compares it to what the
client claimed. A mismatch is refused.
2
At submission
The
(id, version, digest) triple in the JobSpec is checked against the library record, and
against whether an administrator has disabled the plugin.3
At launch
The launcher recomputes the digest of the directory that was actually injected into the job. If
it differs from the one the JobSpec locked, the job refuses to start rather than producing a
result nobody can reproduce.
__pycache__, .git, .venv, *.pyc and .DS_Store are excluded from the digest. They are
environment by-products, not plugin content.Two more checks on executable packages
Published-time structural validation, so a mistake fails on your laptop instead of forty minutes into a cluster allocation:The entrypoint module must actually exist
The entrypoint module must actually exist
entrypoint: patch:install requires patch.py or patch/__init__.py in the package. A typo is
rejected at publish:Top-level names must not shadow real dependencies
Top-level names must not shadow real dependencies
Your plugin root joins
sys.path, so a file called torch.py at the root would make every
subsequent import torch in the training process load your file. The symptom would appear
nowhere near the cause, so these names are reserved and refused:Versions are immutable
Publishing1.2.0 twice is a 409. Fix a bug by publishing 1.2.1.
This is the premise digest pinning rests on: if a version could be overwritten, a lockfile would
name something that no longer exists. It also covers README.md — the readme is inside the digest,
so changing the long description requires a new version. “The docs say one thing, the code does
another” is the most common failure of plugin marketplaces, and this is the cheapest way to prevent it.
Naming and namespaces
Governance
Disabling blocks new submissions only. Jobs already running or queued are unaffected.
Where to go next
Plugin packages
The manifest, the directory layout, publishing and installing.
Algorithm plugins
The entrypoint signature, and when your function is called.
Recipe packs
Add a training method without a platform release.
Benchmark packs
Declare what to run and which numbers to read.
Environments
Task suites for agent RL, and the four protocols.
Verifiers
The three ways to decide whether a task was completed.