Once your sandboxed plugin works, publish it so other sites can install it. Publishing is sandboxed-only — native plugins distribute via npm.
When you publish, the CLI records the release to your own Atmosphere account. You host the tarball yourself — a GitHub release asset, R2, S3, or any public URL — and the registry stores a link to it.
Prerequisites
- A valid
emdash-plugin.jsoncwithslug,publisher,license, an author (authororauthors), and a security contact (securityorsecurityContacts). Runemdash-plugin validateto confirm. - A
version(inpackage.json, or the manifest for registry-only plugins). - An Atmosphere account to publish under.
Your Atmosphere account
You publish under an Atmosphere account: a portable, user-owned identity used across Bluesky and other apps in the AT Protocol network. One account is your single login across the network, with the same @handle everywhere, and your identity and data are not tied to any one app. EmDash uses this account as your publisher identity: every release you publish is a record in your own account, signed in as you.
EmDash uses the same Atmosphere accounts as its Atmosphere login for sites.
Use an existing account
If you already have a Bluesky account or any other Atmosphere account, sign in with its handle:
emdash-plugin login alice.bsky.social
This opens your account provider’s sign-in page in the browser. EmDash never sees your password. emdash-plugin whoami lists your stored sessions; emdash-plugin switch <did> changes the active one.
Sign up for an account
If you do not have an Atmosphere account yet, create one through any provider, then run emdash-plugin login <your-handle>. Your options:
- An app, such as Bluesky. Signing up for Bluesky creates an Atmosphere account hosted by Bluesky. This is the quickest route.
- An independent provider. Community-run or privacy-focused account hosts. Browse options at atmosphereaccount.com.
- Self-hosted. Run your own provider for full control over your identity and data.
Whichever you choose, the @handle from that account is what you pass to emdash-plugin login, and the account’s DID is what you pin as the publisher in your manifest.
Three steps
The following commands log in, build a tarball, and publish a release that points at the hosted tarball:
emdash-plugin login # if not already logged in
emdash-plugin bundle # produces dist/<slug>-<version>.tar.gz
# upload that tarball to a public URL, then:
emdash-plugin publish --url https://your-host/<slug>-<version>.tar.gz
bundle prints the next two steps when it finishes, including the --url invocation, so you don’t have to remember the shape.
Bundle
bundle runs build, validates, collects assets, and creates a tarball. Inside the tarball, plugin.mjs is packed as backend.js (the filename the registry expects).
The command accepts the following flags:
emdash-plugin bundle [--dir <path>] [--out-dir|-o <path>] [--validate-only]
| Flag | Default | Description |
|---|---|---|
--dir | Current directory | Plugin source directory. |
--out-dir, -o | dist | Output directory for the tarball. |
--validate-only | false | Skip the tarball, but still produce dist/ artifacts. |
Tarball contents
| File | Required | Description |
|---|---|---|
manifest.json | Yes | Generated manifest: id, version, capabilities, hosts, and the hooks and routes read from your source. You do not maintain this by hand. |
backend.js | Yes | The built, self-contained runtime file (dist/plugin.mjs). |
README.md | No | Plugin documentation. |
icon.png | No | 256×256 PNG. |
screenshots/ | No | Up to 5, max 1920×1080. |
Validation
bundle (and --validate-only) check:
- Size caps (RFC 0001, decompressed): total ≤ 256 KB, per-file ≤ 128 KB, ≤ 20 files. The gzipped tarball is a fraction of that.
- No Node built-ins in
backend.js— sandbox code can’t importfs,path,child_process, etc. Use Web APIs, or move that logic to a native plugin. - Capability sanity — names must be in the recognised set.
- Trust-contract coherence — the
network:request/allowedHostscross-rules from the manifest. - Asset limits — icon 256×256, ≤ 5 screenshots at ≤ 1920×1080.
To inspect the tarball before publishing, list its contents:
emdash-plugin bundle
tar tzf dist/my-plugin-1.1.0.tar.gz
Publish
Publishing writes the release record. The tarball must already be hosted at a public URL:
emdash-plugin publish --url <hosted-tarball-url>
--url is required: it is where the plugin’s bytes live, and the registry record points at it. To verify the hosted URL serves the exact bytes you built before writing the record, pass --local:
emdash-plugin publish --url https://your-host/foo-1.0.0.tar.gz --local dist/foo-1.0.0.tar.gz
What publish does:
- Fetches the tarball at
--url(with URL and size guards) and extracts the manifest from those bytes. - Resumes your Atmosphere account session and checks publisher pinning — the active session must match the manifest’s pinned
publisher, or it refuses withMANIFEST_PUBLISHER_MISMATCH. - Creates the package profile from the manifest on first publish (
license, author, security contact). On later publishes, the existing profile wins. - Publishes the profile and release records to your account.
On first publish you can supply profile fields by flag (--license, --security-email, …) instead of the manifest; explicit flags override manifest values, which is handy in CI. --no-manifest opts out of the manifest entirely.
Versions are immutable
A published version cannot be overwritten or republished. Bump version before publishing again. The build reads version from package.json (see the manifest reference). Bump major for a broadened trust contract, minor for new hooks or routes, and patch for fixes.
Publisher mismatch
If publish fails with MANIFEST_PUBLISHER_MISMATCH, the active session is a different Atmosphere account than the manifest’s pinned publisher. Switch to the pinned account with emdash-plugin switch <did>, or update publisher in the manifest if you are genuinely transferring the plugin to a new account. See Use an existing account for managing sessions.
What to read next
- The
emdash-pluginCLI — every command - The manifest — fields, trust contract, publisher pinning
- Capabilities and security