cli
source same‑repo
Latest
dist-town/cli 0.7.0
The command is now just disttown publish. disttown.jsonc A repo can commit a manifest describing what it publishes: the project, the artifact globs, the storage choice, and the account expected to do the publishing. It's JSON with comments (the .jsonc extension is the honest…
dist-town/cli 0.7.0
The command is now just disttown publish.
disttown.jsonc
A repo can commit a manifest describing what it publishes: the project,
the artifact globs, the storage choice, and the account expected to do
the publishing. It's JSON with comments (the .jsonc extension is the
honest label — editors just work), it resolves by walking up from
wherever you are (monorepo sub-projects get their own), and a published
JSON Schema at dist.town/disttown.schema.json gives you completion
and validation as you write it.
disttown init writes a commented scaffold, proposing the project name
and detecting dist/.
Inference, shown
With a manifest committed and an annotated tag at HEAD, publish infers
everything: the tag proposes the version (a leading v before a digit
is stripped), the manifest supplies the files, the storage choice
routes the bytes. Nothing inferred is ever silent — interactive publish
renders the whole plan first (every value with its source, every file
with its digest and destination, every pointer move) and asks. --yes
skips the question; non-interactive runs behave exactly as before, so
no CI pipeline changes.
Explicit arguments override the manifest everywhere. No manifest means the CLI you already know, unchanged.
Storage profiles
The machine defines buckets, repos pick them by name: disttown storage setup --profile cdn records the definition, a manifest says
"storage": "cdn", and per-entry overrides route individual globs
elsewhere. "blobs" is reserved for bytes-on-your-own-PDS. Upload
commands and credentials never live in the repo, so a cloned manifest
carries no secrets. DISTTOWN_STORAGE_BASE_URL / _UPLOAD_CMD serve
CI without any files.
The publisher guard
The manifest records which DID it expects to publish. Logged in as
someone else, publish stops before a byte moves and says so — the
multi-account save. The other resolution is adoption: the manifest is
rewritten to expect you, and the original project reference moves into
derivedFrom — fork lineage, captured at the only moment the CLI can
know it, preserved comment-for-comment. The claim reaches the project
record and renders on the fork's page as exactly what it is: a claim.
Also
- Every command that takes a project now infers it from the manifest
when you don't say —
release listbare, inside a governed repo, means that repo's project, and the output says so. - Manifest globs are containment-checked: never absolute, never escaping the repo, symlinks resolved — a hostile manifest can't publish your dotfiles.
storage doctor --profile <name>checks a named profile.- Nix builds of the CLI now stamp their version (
0.7.0+nix) and update source instead of reportingdev.
dist-town/cli 0.5.0
One new command, and it's the point of the whole thing.
disttown update
The CLI now updates itself — and it does it without touching a single
dist.town service. It resolves its publisher's identity, follows the
latest pointer in their repo, fetches the release record, downloads
the artifact for your platform, and verifies it against the digest in
the signed record. If dist.town went dark tomorrow, this command would
not notice.
Before replacing anything, it tells you where the update comes from —
publisher handle and DID, version, publication time, digest — and asks.
--yes consents for automation; --check reports without acting
(exit 2 when an update is available, for scripts); --channel beta
follows a channel instead of latest.
Some behavior worth knowing:
- A yanked release is never installed. If
latestpoints at one, the updater reports no update and stays put. - If the publisher rolls
latestback to an earlier release, the updater says so plainly — a rollback is followed with your consent, never silently. - Package-managed installs (nix store, system paths) are left alone, with a pointer to the right tool.
Also
publishand the lifecycle verbs no longer write a pointer update when the pointer already targets the release — a no-op record update turned out to be something firehose consumers can choke on.
dist-town/cli 0.4.0
This release gives the CLI read capabilities, and reorganizes some commands in the name of consistency. There are breaking changes as a result.
The read surface
disttown release list [project]— what you've published, newest first, with channel and lifecycle state.disttown release show <project> <version-or-pointer>— one release in full: digests, download locations with verification state, pointers, source claim, changelog.release show mytool latestworks.disttown project listanddisttown pointer list <project>— your projects (aliases included) and where every pointer aims.disttown whoami— who this machine publishes as, and whether your cached session's scopes still cover everything. The stale-session surprise now surfaces here instead of mid-publish.disttown logout— clears the local session, but does not delete the session server-side.
Reads consult dist.town's index by default — the same public surface
the pages use. --pds reads straight from your own repo instead, and
--compare reports exactly where the two disagree (useful right after
publishing, before the index catches up). Every read says which source
answered, and everything takes --json.
Breaking: lifecycle verbs moved, and the vocabulary is complete
disttown yank is now disttown release yank <project> <version> —
and it brought company:
release deprecate/release undeprecate— "still works, stop building on it." Deprecated releases keep resolving, with a warning.release unyank— every verb has a named inverse now;--undois gone.project archive/project unarchive— wind a project down without breaking anything it ever shipped.
All of them take --kind and --reason — the inverses too, because
"why it came back" is as much history as "why it left."
Breaking: --channel changes what latest means
disttown publish 2.0.0-rc1 --channel beta now moves the beta
pointer and leaves latest alone. Previously it moved latest — which
meant a release candidate could silently become what every updater and
install script sees. The rule is one sentence: the default pointer
follows the channel. Want a channel release on latest? Say so:
--pointer latest --pointer beta. Naming any pointer replaces the
default entirely. Channelless publishes are unchanged.
Fixed: publish could hang forever under OAuth
A logging deadlock could freeze the CLI — silently, permanently — on the first library log line emitted after login, which in practice meant the first blob upload of a publish. It could also invalidate your cached session on the way down, forcing a re-login.
Publish is harder to hurt yourself with
- Scope problems now fail before anything uploads.
publish --dry-runprints the whole plan — digests, records, which pointers move — touching nothing. CI can gate on its exit status.- If a publish fails partway, the CLI names every file already uploaded (content-addressed, so retrying reclaims them; nothing is deleted).
disttown doctor [project]checks the whole pipeline in one go: session, scopes, PDS, storage, and whether dist.town's index can see you — with "publishable but not yet visible" clearly told apart from anything actually wrong.
0.3.0 — releases that say where they came from
Releases can now say where they came from — and this one does.
Source linking
A release can pin the git commit and annotated tag it was cut from, right in the immutable record. publish --tag v0.3.0 reads both hashes from your local checkout; since git is content-addressed, the tag object hash transitively pins the entire source tree. Names are labels, hashes are truth: force-moving a tag never moves a published claim.
Set your repository once with the new disttown project set --source, and every release inherits it. When the source is a record in your own atproto repo — a Tangled repository — the association is signed by the same key as the release itself, and release pages badge it same-repo. This is honest verification: it proves association, not build provenance.
Tangled artifacts are first-class
If your CI uploads artifacts to Tangled, dist.town now treats them as locations rather than rivals:
- At publish time, tag-matched Tangled artifacts are offered for attachment (
--attach-tangledfor CI). The CLI downloads and hashes every blob locally — each digest in your release is your own measurement, never the forge's claim. - Afterward, any Tangled upload whose bytes match a declared digest becomes an additional verified download location, including for releases published before the upload existed.
- Uploads that match nothing appear under an explicit "not in the record" caveat — visible, and honestly unverified.
Windows builds
This is the first release with Windows artifacts (amd64 and arm64, as zip archives), alongside Linux and macOS. The CLI is pure Go, so the whole matrix cross-compiles from one machine — tools/dist.sh in the repo is the two-line recipe. disttown --version now exists, stamped at build time.
Also
- Release pages show the source claim in the record rail: repo, tag, and commit, with copy-to-clipboard hashes.
project setupdates a project's display name, description, source, and website — only the flags you pass change.
dist-town/cli 0.2.0
The release that renamed itself: disttown now lives at dist-town/cli, published under the nested project name this version introduces. The old name remains a permanent alias — every existing URL keeps resolving.
Nested projects
- Project names are now paths:
dist-town/clion the command line and in URLs,dist-town:cliin the record key. Flat names remain valid — nesting is never required. - Hierarchy lives in the name itself: no new lexicon, no parent field, and parentage derives by prefix. An umbrella page lists its children automatically.
disttown project createwrites a bare project record (umbrellas), anddisttown project renameperforms the alias dance atomically: successor created, old record markedrenamedTo, history staying exactly where it happened.
Changelogs
disttown changelog set <version>attaches release notes to any published release — including ones published before this command existed. Notes are plainsite.standard.documentrecords carryingat.markpub.markdowncontent with a derived plaintext fallback, so they render in any standard.site reader.- Re-running the command edits the existing document in place rather than shadowing it.
disttown publish --notes <file>lands the notes in the same atomic commit as the release. These very notes shipped that way.
Housekeeping
- OAuth sessions gain the
site.standard.documentscope; cached sessions need onedisttown loginre-run before authoring changelogs. --projectaccepts both the URL form (dist-town/cli) and the record-key form (dist-town:cli) everywhere.