# Publishing & updates

> Sharing a card on GitHub, the addresses users can install from, how installs are pinned, how updates reach users and when they ask again, and versioning the card and the protocol.

Source: https://docs.neurosquad.ai/en/card-sdk/publishing

There is no store: **a card is published when it is on GitHub**, and installed by its GitHub
address. Review is optional — if you want your card in the app's [verified catalog](https://docs.neurosquad.ai/en/card-sdk/verified-cards)
(searchable from the add menu, with a Verified badge), send a pull request to
`glmn-ai/neurosquad-cards` as described in its [SUBMITTING.md](https://github.com/glmn-ai/neurosquad-cards/blob/HEAD/docs/SUBMITTING.md). Each reviewed version is pinned
to one commit.

## Publish

1. Run `npx @neurosquad/card-sdk validate` and `pack --dry-run`. Fix every error; read the install
dialog preview as a stranger would.
2. Commit everything the card needs — including build output (`dist/`), since the app never
builds anything. `pack` lists exactly what will be installed.
3. Push to a public GitHub repository (private ones work too, for users who add a GitHub token).
4. Optionally tag a release: `git tag v1.0.0 && git push --tags`, and create a GitHub release from
the tag.
5. Tell people the address.

**Several cards in one repository** is fine: each lives in its own folder with its own manifest,
and is installed as `owner/repo/path/to/folder`.

## What users can install from

| Address | Installs | Updates follow |
| --- | --- | --- |
| `owner/repo` | the default branch's newest commit | the default branch |
| `owner/repo@main` | that branch | that branch |
| `owner/repo@v1.2.0` | that tag | nothing — a tag is fixed |
| `owner/repo@<40-hex commit>` | that commit | nothing |
| `https://github.com/owner/repo/releases/tag/v1.2.0` | that release | the **latest release** |
| `owner/repo/cards/pomodoro`, `https://github.com/owner/repo/tree/main/cards/pomodoro` | the card in that folder | as above |

`github.com/owner/repo`, `https://github.com/owner/repo.git` work too. A ref containing `/` must use
the `@ref` form.

**Pinned to a commit.** Whatever the address, the app resolves it to one commit and installs that
commit's files. A commit given by hash must be reachable from the repository's default branch — a
commit that exists only in a fork is refused — and a repository that was renamed or transferred must
be installed under its current name. The app records the commit and a tree hash of every file. The address the user typed is
not the card's identity — `github:owner/repo[/folder]` is — so an update keeps the card's
permissions, storage and every copy on every canvas.

## How updates reach users

- The app checks a minute after it starts, then every 24 hours, and when the user presses **Check
for updates**.
- A newer commit is downloaded and validated, then offered: **Update** in Settings and a dot on the
card. **Nothing is applied automatically.**
- The user sees the change: the new version, the old → new commit with a link to GitHub's
comparison, and the permission diff —
  - **new permissions** and **new network hosts** → the update waits for the user's consent;
  - **permissions no longer needed** → revoked on update;
  - new **optional** permissions → listed as "may ask later", never granted automatically;
  - new or reworded **tools**, new or retyped **ports**, new **secret settings**, a changed
**displayName, author or homepage** → the update waits for the user's consent too.
- On update, every open copy of the card reloads with `launch === 'updated'`. Storage and settings
carry over — migrate them in code if you changed their shape.
- If a manifest no longer validates in a newer app, the card is shown as broken, with the reason; it
is never run anyway.

> Force-pushing a tag or rewriting history does not sneak code past users: the same commit
> re-downloaded with different contents is refused, and every update is shown before it applies.
> But users do trust you with each update they accept — protect your GitHub account (2FA), and
> review pull requests to your card like code that runs on other people's machines.

## Versioning

**Your card's `version`** is informational — installs are pinned to commits — but users see it in
the install dialog, the update dialog and **Settings → Custom cards**. Use SemVer and bump it with
every release. Put breaking changes to your ports or tools in the description: other people's cards
and agents' habits may depend on them.

**`minAppVersion`** stops the card from installing into an app that is too old for it.

**The protocol** is versioned separately: `"protocol": 1` in your manifest, `CARD_PROTOCOL_VERSION`
in the SDK.

- New methods, events and optional fields arrive **within** protocol 1. Feature-detect them with
`card.host.supports('method.name')` and keep working without them.
- A breaking change would be protocol 2. An app that only speaks 1 refuses a card built for 2 with
"needs a newer NeuroSquad"; the app would keep serving protocol-1 cards alongside for at least a
year.
- Upgrading `@neurosquad/card-sdk` within the same major is safe; rebuild and republish.

## Changing permissions and what the card offers

Adding a required permission or a network host — or a tool, a port, a secret setting, or changing
your card's name, author or homepage — means every existing user must accept the update
before they get **anything** in it — bug fixes included. Consider:

- making the new permission **optional** and asking for it when the user reaches for the feature;
- shipping the permission change in its own release, explained in the description.

## Deprecating a card

Keep the repository available: installed copies do not need it to run, but updates and reinstalls
do. Say so in the description, and point to the replacement.
