Automated plugin releases

On this page

Automated releases build and publish a sandboxed plugin when you push a version tag or start a GitHub Actions workflow manually. Your Atmosphere account owns the package profile and release records. GitHub identifies the approved workflow, and the release service verifies and publishes the result without storing an account credential in the repository.

Use emdash-plugin publish for a release started from your computer. Use this guide when GitHub Actions should build and publish releases.

Prerequisites

Prepare the following before starting:

  • A public GitHub repository containing a sandboxed EmDash plugin.
  • A valid emdash-plugin.jsonc with slug, publisher, license, an author, and a security contact. Set repo to the canonical GitHub URL, or enter it during interactive setup.
  • A version in package.json, or in emdash-plugin.jsonc for a registry-only plugin.
  • The Atmosphere account named by publisher.
  • Permission to add a GitHub Actions secret to the repository.
  • A browser that supports passkeys. Release approval requires user verification.

Run the manifest check before configuring the workflow:

pnpm exec emdash-plugin validate

Set up automated releases

  1. Sign in to the plugin CLI with the Atmosphere account that owns the package.

    pnpm exec emdash-plugin login alice.example.com

    The CLI stores this local publishing session outside the project. GitHub Actions never receives it.

  2. Prepare the package profile and generate the workflow.

    pnpm exec emdash-plugin release setup

    The command reads the package metadata from emdash-plugin.jsonc. If the package profile is missing, it offers to create it. If the profile exists without delegated-release settings, it offers to add them while preserving the existing package metadata.

    Setup asks when a release needs approval:

    • When plugin permissions increase is the default. A release waits for approval when its declared access expands relative to the latest release.
    • For every release requires approval for every version.

    The signed-in Atmosphere account becomes the initial approver. The profile also binds the package to the canonical GitHub repository URL and requires verifiable provenance.

    Run only the profile step when a workflow file already exists:

    pnpm exec emdash-plugin profile setup

    In a non-interactive terminal, pass --yes to accept the default approval policy. Pass --repository <https-url> when the manifest does not contain repo, and --confirmation always to require approval for every release.

  3. Review and commit the generated workflow.

    The command creates .github/workflows/emdash-release.yml. It does not push the file and does not replace an existing workflow unless you pass --force.

    The generated workflow runs for version tags matching v* and through workflow_dispatch. It grants contents: read, id-token: write, and attestations: write; pins third-party Actions to full commit identifiers; builds one plugin bundle; creates GitHub build provenance for those exact bytes; and passes both files to the EmDash release Action.

  4. Open the release-service dashboard and sign in with the same Atmosphere account.

    Select Authorize publishing. Your account provider shows the exact delegated permission. The retained grant can create package release records and upload package or listing-image blobs. It cannot create or edit package profiles, update or delete releases, or write another collection.

  5. Create a workflow invitation.

    Enter the plugin ID from emdash-plugin.jsonc, then select Create invitation. Add the one-time value to the GitHub repository as an Actions secret named EMDASH_CONNECTION_INVITATION.

    The invitation is valid for 30 minutes and can connect only the named plugin. Create a new invitation if it expires before the workflow consumes it.

  6. Start the release workflow.

    Update the package version before creating the version tag. The following commands start a 1.2.3 release:

    git tag v1.2.3
    git push origin v1.2.3

    You can also select Run workflow on the repository’s GitHub Actions page.

  7. Approve the workflow connection on its first run.

    The Action writes a link to the GitHub job summary and waits. Open the link and confirm the plugin, repository, workflow file, branch or tag, and environment.

    For a tag-triggered run, choose All version tags or Only this tag. A branch-triggered request covers only that branch. The service stores the GitHub repository and owner IDs as well as the selected ref and environment scope. Later runs must match this policy.

  8. Approve the release when required.

    A release that expands plugin permissions, or a profile configured for every-release confirmation, enters Awaiting approval. Open the approval URL from the Action output or release dashboard. Enrol a passkey if the approving account does not already have one, review the permission change, and approve or reject the release.

    The default Action setting returns successfully when the release reaches Awaiting approval. The service workflow continues waiting for the browser decision and publishes after approval.

What the release service verifies

The service completes these checks before it writes a release:

  1. The GitHub OpenID Connect (OIDC) token names an authorised repository, owner, workflow, ref, environment, commit, run, and GitHub-hosted runner.
  2. The package profile exists, is signed by the publisher, contains delegated-release settings, and names the same canonical GitHub repository.
  3. The requested package and version match the built plugin bundle.
  4. The package checksum matches the uploaded bytes.
  5. GitHub provenance covers the same bundle, repository, workflow, commit, and run.
  6. The release record’s declared access matches the bundle manifest.
  7. The version record does not already exist.
  8. Any required passkey approval covers the exact verification result and current profile revision.

The Action requests a fresh GitHub OIDC token for each service call. Bundle and provenance files enter private transient storage only after the workflow is authorised. The service uploads verified package and image bytes to the publisher’s personal data server (PDS), creates the release record there, and exposes the verified provenance through an immutable checksum-addressed URL.

Authority boundaries

Each credential has one job:

CredentialUsed byAuthority
Local CLI OAuth sessionemdash-plugin profile setupCreate or update the publisher-owned package profile after local confirmation.
GitHub OIDC tokenRelease ActionIdentify one GitHub workflow run to the service. It grants no AT Protocol write access.
Release-service delegationRelease serviceCreate package release records and upload the required blobs.
Publisher application sessionRelease dashboardAuthorise workflow connections and revoke delegated publishing.
Approver session and passkeyApproval pageApprove or reject one checksum-bound release verification.
Cloudflare Access identityService operator consoleOperate the hosted service. It does not represent a publisher or approver.

The service stores publisher and approver state separately. Signing in to view your releases does not grant operator access, and an operator identity cannot approve a release as a publisher.

Action behaviour

The generated workflow uses the Action from apps/release-action. The Action accepts either a built bundle plus raw Sigstore provenance, or a compatibility release-file containing checksum-bound HTTPS artifact sources. Do not combine release-file with bundle or provenance inputs.

The standard generated workflow supplies these inputs:

InputValue
service-urlRelease-service HTTPS origin.
publisher-didDID that owns the package profile and releases.
connection-invitationEMDASH_CONNECTION_INVITATION on the first connection.
bundle-fileThe single tarball produced by emdash-plugin bundle.
provenance-fileRaw bundle-path output from actions/attest-build-provenance.

The Action returns these outputs:

OutputMeaning
connection-urlBrowser URL for first-run workflow approval.
intent-idRelease intent identifier.
statePublished, terminal, or awaiting_approval state.
approval-urlBrowser URL when passkey approval is required.
release-uriPublished release AT URI.
release-cidPublished release record CID.
reason-codeStable reason for a terminal intent.

See the Action reference for optional inputs, custom URL-source workflows, polling controls, and exact output behavior.

Troubleshooting

PACKAGE_PROFILE_REQUIRED

The package profile is missing, lacks delegated-release settings, uses a non-canonical repository URL, or names a different repository from the GitHub workflow.

Run profile setup locally with the publisher account, then start the workflow again:

pnpm exec emdash-plugin profile setup

This check runs before the service accepts bundle or provenance uploads.

Public repository required

GitHub uses a private Sigstore trust root for private and internal repositories. The release verifier currently trusts only public GitHub provenance. Move the release workflow to a public repository or publish locally with emdash-plugin publish.

Invitation expired or invalid

Create another invitation in the release dashboard and replace EMDASH_CONNECTION_INVITATION. Start the workflow within 30 minutes. An invitation is single-use and scoped to one plugin ID.

WORKLOAD_NOT_ALLOWED

The GitHub repository, owner, workflow file, ref, or environment does not match the approved workflow policy. Open the release dashboard and approve a new workflow connection with the intended scope.

PROFILE_FETCH_FAILED

The service could not verify the profile from the publisher’s PDS. Retry after the account provider is available. Run emdash-plugin profile setup if the profile was removed or changed.

POLL_TIMEOUT

The Action reached timeout-minutes before workflow approval, release approval, or publication completed. Check the release dashboard for the intent state before rerunning. A rerun of the same GitHub Actions run reuses its idempotency key.

Revoke automated publishing

Select Turn off automated publishing in the release dashboard. Revocation clears the retained release delegation. Existing package profiles, releases, moderation labels, installed plugins, and the dashboard login do not change.

Reconnect publishing and approve the workflow again before the next automated release.