# Publish your plug-in

From a plug-in that passes check to one in the Plug-in Store, through kanboombo://publish: by you in the portal, or by your agent with the tool.

> [!IMPORTANT]
> The publishing portal is open to invited organizations first. Any
> organization can verify its domain, upload and run the checks; submitting
> waits for an [invitation](#invitation).

The portal is `kanboombo://publish`, in the app and on the web. Walk these steps
yourself, or let [your agent](#agent) walk most of them; a reviewer may ask for
changes, and you upload again.


## Ask to be invited {#invitation}

Once you have named your domain, the portal offers **Ask to be invited**: say in
a line what you will publish. Your agent can ask, with no password or token:

```sh norun="a request to Kanboombo's live API"
curl -X POST https://api.kanboombo.com/api/developers/invitations \
  -H 'content-type: application/json' \
  -d '{"email":"you@example.org","organization":"Example Org","domain":"example.org","note":"Tide tables and council meetings"}'
```

The answer is the same whether or not the domain is known; nothing is mailed
until you are invited; one request per domain is open at a time, five a day per
address. The invitation belongs to the domain.

## 1. Your organization and domain {#organization}

You publish as an organization, which readers see as the publisher, through
an owner or admin with a verified email. Verify its domain with the portal's
token, as a DNS record or a file:

```text
_kanboombo-verify.example.org   TXT   "kanboombo-verify=<token>"
https://example.org/.well-known/kanboombo-verify.txt
```

An IP address, a name with no dot, a shared host (`github.io`, `vercel.app`)
and a domain another organization verified are refused. It is checked every 30
days; after a failed check you have 14 days, told by email, before it lapses,
and published plug-ins keep running without the verified mark. Unverified, you
can upload and check, not submit. Accept the
[Developer Agreement](/developers/plugins/publish/agreement/) once, and again
before your next submission after a new version is posted.

## 2. Upload the ZIP {#upload}

Upload what [`pack`](/developers/plugins/quickstart/#6-pack-it) makes: your
folder under its id, with `.kanboombo-pack.json` (contract, id, version, digest,
each file's path, size and SHA-256, the tool's version), without
`node_modules/`, `.git/`, `.DS_Store` or earlier packs.

| Rule | Limit |
|---|---|
| Size | The upload 8 MiB; uncompressed 32 MiB; the code the digest covers 1 MiB; compression at most 100 to 1 |
| Entries | At most 500 plain files, no links; stored or deflated; no ZIP64, no encryption, nothing before the archive, a central directory that agrees with its entries |
| Paths | No `..`, absolute path, `\` or control character; at most 8 folders deep; no two differing only in case |
| Code | `.mjs`, `.json`, `.md`, `.txt` and `LICENSE` only; no `node_modules/` or declared dependencies. Fixtures and listing may be any kind |
| Quotas | 20 uploads a day per organization; 3 versions being checked and 5 in review at once |

No path in the ZIP becomes a path on Kanboombo's disk. Over a quota nothing is
stored; the same bytes twice are one version. A new version must be greater
than every one you published, the same version with other code is refused, and
the same code with a new listing is a listing-only version, its code approval
standing.

## 3. Read the checks {#checks}

At upload, in seconds, nothing runs: the archive; the manifest (contract 1, an
id not reserved or taken, at most 24 runs a day, a terms link, no IP address as
a source); what an [update may not change](/developers/plugins/publish/updates/#refused);
a code scan (outside or undeclared imports, dynamic imports, network APIs,
`eval`, `new Function`, minified code); fixtures and listing. Then the locked
room runs your fixtures twice (same output), each broken source (it must fail),
your example days, your real sources once, and each listing link. Every problem has a code in
[Submission errors](/developers/plugins/publish/submission-errors/); **Copy
issues** copies them in `check`'s words.

## 4. Submit, and a person reviews it {#submit}

Add your [review information](/developers/plugins/publish/review-requirements/).
Submit is refused, with each reason, until it is complete with every
attestation, the current agreement is accepted, and your domain is verified and
invited.

A person at host.kanboombo.com reads the checks, the manifest, every changed
file, your example days as the locked room drew them, your listing and review
information, then **approves** exactly that (a change since refuses it),
**requests changes** or **rejects**, with a reason and a note. You and your
owners and admins hear by email and in the portal. A reviewer from your
organization does not approve yours when someone else can.

## 5. Publish {#publish}

An approved version waits for you, or publishes itself if you said so before
submitting. It is live within 15 minutes, and readers move to it at its next run
([Updates](/developers/plugins/publish/updates/)). Any organization may upload
and check within the quotas; a verified one may reserve an id; only a verified,
invited one may submit and publish.

## With your agent {#agent}

Any agent that runs commands can upload, read the checks, fill in review
information and submit, without your password
([Use your agent](/developers/plugins/#agent)). You approve it once, by a code,
for one organization, and choose whether it may also publish.

> [!IMPORTANT]
> Five things stay yours, and the agent's token is refused for each: verifying
> the domain, accepting the agreement, approving the agent, retiring a plug-in
> and, unless you allow it, pressing Publish.

```sh norun="these reach Kanboombo's live API with the agent's token"
node kanboombo-plugin.mjs login
node kanboombo-plugin.mjs upload bcrd-weekly
node kanboombo-plugin.mjs status
node kanboombo-plugin.mjs review --file bcrd-weekly/review.json --attest
node kanboombo-plugin.mjs submit
node kanboombo-plugin.mjs publish
```

`login` shows a code, the agent's name (`--name` changes it) and an address,
`kanboombo://publish/agents?code=…`. Open it as an owner or admin, choose the
organization, leave **It may also publish** off or turn it on, and **Approve**.
The code lasts 10 minutes, and `login` run again within them waits for it;
`--server` names another Kanboombo API (https, or this machine). Approve only a
code your own agent just showed you: one someone sends you asks for your
organization's name on their plug-in, so **Deny** it.

`status` says when the locked room is done and what is next, in the portal's
words. With no version named, these act on the last upload from the folder you
are in, or else from this machine. `AGENTS.md` tells the agent to ask you
before `--attest`. `publish` stops with `AGENT_MAY_NOT_PUBLISH` unless you
allowed it; if you did, `unpublish <id>` and `relist <id>` work too. `retire`
answers `AGENT_FORBIDDEN`: retiring is for good, so you do it in the portal.

The token reaches only the portal's routes, for that organization, while you
are its owner or admin: the steps above, withdrawing and re-running checks, and
reading reports, never who sent them. It can never name a domain, ask to be
invited or revoke agents, and every upload records the agent's name. Shown
once, kept by Kanboombo only as a hash, it lives in `~/.config/kanboombo/plugin-token.json`, readable only by
you, and stops at `logout`, at **Revoke** under Agents in the portal, at a new
login on that machine, or when its approver stops being owner or admin.
