Get started free
Documentation

Publish & the marketplace

Your extension stays in your repo. A registry is a git repo of pointers to other repos, so listing costs a pull request and nobody has to trust a build service.

The shape of it

intentic does not host extension code, build it, sign it, or take a cut of it. A registry entry is a pointer to your repository at a commit, and installing follows that pointer directly from the owner's sandbox to your git host. Three consequences worth stating plainly:

  • Publishing is a pull request. No account, no upload, no packaging step, no review queue for your code.
  • You keep the code. Your repo is the artifact. Delisting doesn't delete anything; it only removes a pointer.
  • Registries are plural. The official one is a default, not a gate. A company can run a private registry of internal extensions and never touch ours.

Install without a registry at all

A registry is a convenience for discovery, never a requirement. Anyone with your repo URL and a commit sha can install your extension today from Capabilities → Add → Extension. That's the whole distribution mechanism; the marketplace just saves people from copy-pasting shas.

It also means a private extension needs no publication path: point a sandbox at an internal repo with a token and you're done.

A registry is a git repo

Add a .claude-plugin/marketplace.json to any repository and it is a registry. Mark an entry "kind": "extension" and it installs as a sha-pinned intentic extension.

.claude-plugin/marketplace.json
{
    "name": "acme-extensions",
    "plugins": [
        {
            "name": "acme.incidents",
            "kind": "extension",
            "trust": "listed",
            "description": "Incident triage in the rail: open alerts, acknowledge, jump to the failing run.",
            "version": "1.0.0",
            "icon": "exclamation-triangle",
            "source": { "source": "github", "repo": "acme/incidents", "sha": "9f2c1ab…" }
        },
        {
            "name": "acme.linear",
            "kind": "extension",
            "trust": "listed",
            "description": "Linear connector: the agent reads and files issues from its shell.",
            "version": "0.3.0",
            "logo": "linear",
            "source": { "source": "github", "repo": "acme/linear-connector", "sha": "4d81e07…" }
        }
    ]
}

The file format is Claude Code's plugin-marketplace format, deliberately. kind and trust are intentic's own markers and Claude Code ignores fields it doesn't know, so one registry repo can serve both, and a team that already publishes agent plugins lists its intentic extensions in the same file rather than standing up a second thing.

An entry's name is publisher.name read from your manifest, not a label the registry picks. That is the same identity the app installs under, so two publishers can both ship an incidents extension, and a repo that copies somebody else's manifest collides with their listing instead of shadowing it.

A second file sits beside it. registry.generated.json is written by the registry's nightly job and holds only facts read back off the source host: stars, and the last push date. It is separate so a nightly refresh never conflicts with an open pull request, and so a review diff shows the decision being made rather than a churn of star counts. It deliberately carries no "latest upstream commit": the approved sha is the one that runs, and advertising a newer one would invite a click that skips the review.

Source shapes

source says where the code is. A bare string is a path inside the registry repo itself; the object forms point outward.

What source can be
// A repo of its own
{ "source": "github", "repo": "acme/incidents", "sha": "9f2c1ab…" }

// Any git host
{ "source": "url", "url": "https://gitlab.com/acme/incidents.git", "sha": "9f2c1ab…" }

// A subdirectory: one repo, several extensions
{ "source": "git-subdir", "url": "https://github.com/acme/tools.git", "path": "ext/incidents", "sha": "9f2c1ab…" }

Give a sha. A branch name is accepted for plugins, but an extension install requires a full 40-character commit sha, so a registry entry without one can't be installed in a click; it makes the user go and find the sha themselves.

Getting listed

Two ways in, and they end at the same place: a pull request against the official registry. There is nowhere to sign up.

  1. Add the topic. Put intentic-extension on your GitHub repository, with an intentic-extension.json at its root. A nightly job finds it, checks the manifest parses, resolves your latest commit, and opens the pull request for you. That is the whole submission process. Your logo or icon is copied across with it, so the gallery card and the in-app browse row wear the mark you declared — a listing that declares neither shows its initials.
  2. Or open it yourself. Required if your extension lives in a subdirectory or isn't on GitHub, because there is nowhere for the scan to look.
  3. Review checks the pointer, not the code. The manifest parses, the entry bundle exists at that sha, the description matches what the manifest actually contributes, the publisher is yours. We do not audit your source, and we don't pretend to.
  4. Ship an update by opening another pull request with the new sha. The version in your manifest is what users see; the sha is what runs.

Publisher slugs are first-come and tied to proven ownership of the source repo. Because the listing key is the manifest-derived publisher.name, a scan that finds a repo claiming a slug somebody else already holds refuses it rather than opening a pull request that looks legitimate.

What the person installing sees

The manifest is rendered as plain English, not as JSON. An install dialog says this extension adds a tile to your rail, reads your workspace logs, runs a background process, puts a tool on your agent's PATH: one line per declared contribution, with the route allowlist spelled out underneath. The user approves that specific list at that specific commit.

Nothing about the install is ambient or implied:

  • An extension that declared no permissions.sandbox cannot call the daemon at all.
  • An extension that declared no badge cannot interrupt you from another screen.
  • An extension that registers a view its manifest never declared is refused at runtime, not warned about.

Updates

Because the installed identity is a commit sha and a registry entry carries one, "update available" is a comparison the app can make for you. The rule that matters is what happens next:

  • Same contributions, new sha: one click. The daemon re-clones into staging, validates the manifest and the entry bundle, and swaps only if it's sound. A broken release cannot replace a working install.
  • The manifest grew, with a new route, a new process, a new PATH entry or a badge it didn't have: the update re-asks, showing the diff. Approval never transfers to powers you didn't approve.

Your settings survive all of it. They're stored on the daemon under publisher.name, not in the checkout, so an update, a downgrade, or a remove and re-add keeps them.

Nothing auto-updates. On a machine the owner owns, silently changing the code that runs against their repos and credentials is not a convenience.

Trust, stated honestly

Three layers protect the person installing your extension, and it's worth being precise about which does what:

LayerWhat it guaranteesWhat it doesn't
Sha pinning The approved code is the running code. Force-pushing your branch changes nothing on an installed sandbox. That the code is good.
The manifest gate Blast radius. Undeclared registrations are refused; undeclared routes throw; secrets never reach the bundle. That declared powers are used well.
Registry review The pointer resolves, the publisher is real, the description is accurate. A code audit. It is not one.

Each listing states which of these it has had, in a trust field: listed means the pointer resolves and nobody read the code, which is the honest default; verified means somebody read the source at that commit; blocked means known-malicious or known-broken, with the reason alongside. A blocked entry stays in the file rather than being deleted. Removing the row would hide it from people browsing and tell the people who already installed it nothing, which is backwards.

The honest summary: installing an extension is trusting its author, the same way installing an editor plugin is. What intentic contributes is that the trust is bounded and legible: you can read what it may touch before you say yes, and it can't grow past that without asking you again.

Money

There is no revenue share, no payments layer, and no paid tier for listing. Extensions are free to publish and free to install, and the registry takes no position on how you license or monetize your own repository. If that changes it will be an addition with its own opt-in, not a retroactive term.

Available today, and next

Everything in Build an extension works now: authoring, sha-pinned install from any git repo public or private, the manifest gate, per-extension settings, and the on/off switch. So does the registry: the gallery, the topic scan that opens your pull request, and browsing plus one-click install straight from Capabilities → Add → Extension, against the official registry or any repo you point the field at.

Still to come: update badges driven by registry shas, a published blocklist an installed sandbox checks (delisting protects people browsing, not the people who already installed), and a typed npm artifact for @intentic/extension-ui so out-of-repo authors get the shell's components with full types.

Building something and want it listed? The build guide is the whole prerequisite. Add the topic when your first sha is pushed and the pull request writes itself.