ABOUT THE EXTENSION PUBLISHING LIFECYCLE
Publishing an extension to the swamp registry is not a single upload. It is a sequence of gates, each of which must pass before the next is attempted, and the final push is refused unless every earlier gate has already passed. This page is about why the pipeline is shaped that way, what each gate is actually protecting, and how the work of writing an extension connects to the work of releasing it.
Two state machines, one path
There are really two lifecycles involved, and they meet in the middle.
The first is authoring: searching the registry to confirm nothing already covers the need, writing the model, confirming swamp loads it, running it, testing it. This lifecycle is exploratory. You move back and forth — write a method, run it, find a bug, fix it, run it again. Nothing here is gated, because nothing here is irreversible. The cost of a mistake is a re-run.
The second is publishing: taking a finished extension and releasing it under a collective so other people can install it. This lifecycle is linear and gated, because publishing is irreversible in a way authoring is not. A version pushed to the registry can be deprecated or yanked, but the fact that it existed, and anything anyone pulled from it, cannot be recalled. The gates exist to make the irreversible step boring — by the time you reach the push, every question that could have made it go wrong has already been answered.
The handoff between the two is the manifest. Authoring produces source files; the manifest is the first artifact that belongs to publishing, because it declares what a release is: its name, its version, the files it ships. Once the manifest exists, the publishing gates have something to check.
What each gate protects
The gates are ordered so that the cheapest, most fundamental checks come first and the expensive, registry-touching checks come last. Each one rules out a specific class of failure.
The repository gate confirms you are inside an initialized swamp repository at all. It comes first because every later command needs the repository marker to resolve paths and configuration. Without it, nothing else can run.
The authentication and collective gates confirm who you are and that you are
allowed to publish under the name in the manifest. A scoped name like
@acme/widget is a claim of ownership, and the registry will reject a push
whose collective does not match the authenticated account. Checking this
locally, early, means you discover a naming mistake before building an archive
rather than after uploading one. Reserved collectives such as @swamp and @si
are refused outright.
The version gate asks the registry what already exists and computes the next
CalVer version. Versions are immutable
once published — you cannot overwrite 2026.05.31.1 with new code — so the
pipeline computes the next free version rather than trusting a number you typed.
This is what prevents two different builds from claiming the same identity.
The formatting gate runs deno fmt and deno lint over the extension's
TypeScript. This is not about taste. Published source is read by the people who
install it, and the scorer parses it with deno doc; consistent, lint-clean
source is a precondition for both. The push enforces formatting automatically,
so an unformatted extension cannot reach the registry by accident.
The safety gate scans every file for patterns that have no place in a
published extension: dynamic code execution, symlinks, hidden files, disallowed
file types, and archives that are too large or contain too many files. Some
findings are hard errors that block the push; others — spawning a subprocess,
very long lines, embedded base64 — are warnings that ask for confirmation. The
distinction reflects intent: eval() is almost never legitimate in this
context, but shelling out to a command sometimes is, so one is refused and the
other is merely flagged.
The dry run is the whole pipeline rehearsed without the upload. It builds the archive, runs every safety and quality check the real push runs, and stops short of touching the registry. It exists so that "will this push succeed?" has a definite answer that costs nothing to obtain.
Only after all of this does the push happen, and by then it is almost anticlimactic: the archive is built, the checks are green, the version is known to be free. The upload itself is a three-phase exchange — initiate, upload, confirm — but the interesting work already happened in the gates.
Where the quality score fits
The quality score is not a gate. An extension that scores 4 out of 14 publishes exactly as readily as one that scores 14 out of 14. The score is a signal for the people deciding whether to install your extension, and a checklist for you while you still have the source in front of you — does it have a README, is the type surface documented, is there a license, does the repository link resolve.
It sits between formatting and the dry run in the pipeline for a practical reason: by that point the archive has been packaged and cached, so scoring it is nearly free, and seeing the score before you push is more useful than seeing it afterward. But skipping it never blocks anything. The scorecard explanation covers what the score measures and why verification is a separate badge; the rubric reference lists the exact factors.
Supply-chain trust is the exception
One part of the quality system does behave like a gate, and it is worth
calling out because it is easy to conflate with the score. The
dependency-trust factor audits every npm: package the extension imports
against OSV.dev for known vulnerabilities and against the npm registry for trust
signals — deprecation, license, maintainer count, download volume, recency. Most
of these only affect the score: a package with few downloads or an unrecognized
license costs points but does not stop a publish. But a deprecated package, or
one with a HIGH, CRITICAL, or unknown-severity vulnerability, is a hard error
that blocks the push the same way a safety violation does. You cannot publish an
extension that pulls in a known-vulnerable dependency, regardless of how every
other factor scores.
Two details surprise people. The first is that jsr: dependencies are not
audited at all — the jsr registry already enforces SPDX licensing, provenance,
and a prohibition on install scripts, so the npm-style trust gates would be
redundant. The second is that zod, despite being an npm: import, is not
counted as a dependency: it is shared with swamp's own runtime rather than
bundled into the extension, so there is nothing to audit. An extension whose
only import is npm:zod@4 reports no dependencies to audit and earns the factor
automatically.
This audit is about the code your extension ships. It is unrelated to the
collective trust managed by swamp extension trust, which governs whether swamp
will auto-resolve types from a collective when someone installs an extension.
One is about the safety of what you publish; the other is about who a consumer
is willing to pull from. They share the word "trust" and nothing else.
After publication
Publishing is not the end of an extension's life. A version that should no longer be used can be deprecated — a soft signal that leaves it pullable but flags it across search, info, and pull — or yanked, which removes it from normal resolution. Both are reversible. Deprecation is the gentler tool, meant for "there is a better option now"; yanking is for "do not use this version." Neither deletes anything, because the registry's promise is that a version, once published, keeps meaning what it meant.
Related
- Publish Your First Extension — walk the whole pipeline end to end.
- Create and Publish — the task-oriented version for when you already know the shape.
- About the Extension Scorecard — what the quality score means.
- Deprecate an Extension — retiring a published extension.