Host a marketplace
Once your marketplace.json works on your own machine, you need a place others can fetch it from, a way to keep private catalogs private, and a plan for shipping new versions. This page covers each of those. To write the catalog in the first place, start with Create a marketplace.
Where to host it
Syntic Code can read a marketplace from six kinds of place. Pick the one that matches how your team already shares code.
| Host | Users add it with | Good for |
|---|---|---|
| A GitHub repository | acme/acme-plugins (add @ref or #ref for a branch or tag) | Most teams. Versioned, reviewable, easy to fork. |
| Any git server | https://git.example.com/team/plugins.git, git@host:team/plugins.git, gitlab:acme/plugins | Self-hosted git, GitLab, other hosts. |
| A shared or local folder | /srv/plugins or ~/src/plugins | A network share, or developing the catalog itself. |
| A URL that serves the JSON | https://plugins.example.com/marketplace.json | A static web server or object store. |
| An npm package | npm:@acme/plugins or @acme/plugins | Teams that already publish to a registry. |
| A program that prints the JSON | Only through settings (see below) | Catalogs generated from an internal service. |
For git and folder hosts, marketplace.json must sit at the root of the repository or folder. For a URL, the response body is the catalog. For an npm package, the catalog is read from marketplace.json at the package root.
Git sources need git on each user’s machine. When it is missing, adding or refreshing the catalog stops with a clear message instead of a clone error.
Entries in a URL-hosted or npm catalog
A catalog served from a URL or an npm package has no folder of its own on disk, so its entries cannot use relative ./ sources, and an entry with no source cannot be installed. Give each entry its own source: a git repository, a //folder of one, a .zip URL, or an npm package.
The same applies to git-hosted catalogs in one respect: write owner/repo//plugins/name rather than ./plugins/name, so each plugin is cloned from the right place. Relative paths are resolved only for catalogs added from a local folder.
Size limits
| What | Limit |
|---|---|
A plugin .zip download | 100 MB |
What a .zip unpacks to | 300 MB, at most 20,000 entries |
| What an npm tarball unpacks to | 200 MB |
Zip archives are unpacked as plain files and folders only; links inside an archive are not created.
Share it with a repository
Name the marketplace in the project’s .syntic/settings.json so everyone who opens the repository can use it without typing the source:
{
"extraKnownMarketplaces": {
"acme": { "source": { "source": "github", "repo": "acme/acme-plugins" } }
}
}A teammate can then run syntic plugin install formatter@acme, and the catalog is added on the way. Opening /plugin also adds any catalogs that settings name. additionalMarketplaces is accepted as another name for the same key.
An entry can be written as a plain source string or as an object:
{
"extraKnownMarketplaces": {
"short": "acme/acme-plugins",
"github": { "source": { "source": "github", "repo": "acme/acme-plugins" } },
"git": { "source": { "source": "git", "url": "https://git.example.com/team/plugins.git" } },
"folder": { "source": { "source": "directory", "path": "/srv/plugins" } },
"pkg": { "source": { "source": "npm", "package": "@acme/plugins" } }
}
}Project settings count only in a folder you have trusted. A folder added with --add-dir may name marketplaces in its own .syntic/settings.json; those come last and never replace an entry with the same name from your own settings.
The key in extraKnownMarketplaces must match the name inside the catalog. If they differ, the install stops and says so.
Private catalogs
Private git repositories
Git sources use your normal git credentials: an SSH key, a credential helper, or a token your git setup already provides. To clone GitHub shorthand sources over SSH, set:
export SYNTIC_PLUGIN_GIT_PROTOCOL=sshCredentials embedded in a URL (https://user:token@host/...) work but are masked whenever Syntic Code shows or logs the source. Prefer a credential helper so tokens never land in settings files.
A token for a URL-hosted catalog
A catalog served over https can ask a local program for request headers. Add a headersHelper to its settings entry:
{
"extraKnownMarketplaces": {
"internal": {
"source": {
"source": "url",
"url": "https://plugins.corp.example/marketplace.json",
"headersHelper": "corp-token --print-header"
}
}
}
}The helper prints either a JSON object of header names to values, or Name: value lines:
Authorization: Bearer eyJhbGciOi...Rules the helper must follow:
- It runs through the system shell with no input and a 60-second time limit.
- Syntic Code’s own sign-in secrets are removed from its environment.
- Output over 16 KB is rejected. Header names must be plain tokens, and values may not contain line breaks.
A catalog generated by a program
When the catalog comes from an internal service, a command source runs a program and reads the catalog from what it prints:
{
"extraKnownMarketplaces": {
"generated": { "source": { "source": "command", "command": "corp-catalog --json" } }
}
}A command source and a headersHelper run programs on the user’s machine. They are honored only from the user’s own settings, managed policy, or the environment. The same entry in a repository’s .syntic/settings.json, or in a folder added with --add-dir, is refused, because cloning a repository must never run its programs. A repository also cannot replace a trusted entry of the same name.
Roll out to a whole company
To make catalogs available everywhere, or to limit which ones people may use, put the rules in managed policy. Plugins for your organization covers allow and block lists, force-disabling plugins, and the install note your users see.
Keep users up to date
How updates reach users
- Marketplace refresh.
syntic plugin marketplace refresh [name]re-reads catalogs. Before everysyntic plugin install, the catalog in question is refreshed so the newest listing is used; if that refresh fails, the saved listing is used and a note says so. - Background refresh. Once a day, an interactive session refreshes every catalog that has auto-update on, in the background. It never delays startup. New catalogs have auto-update on.
- Plugin update. A refreshed catalog does not change installed plugins by itself. Users run
syntic plugin update <plugin>@<marketplace>or pressron the plugin in/pluginto fetch the new files. The plugin keeps its on/off state.
Turn auto-update off for a catalog
syntic plugin marketplace auto-update acme offThe saved listing then stays as it is until someone runs syntic plugin marketplace refresh acme by name. SYNTIC_PLUGIN_AUTO_UPDATE changes the background behavior for one machine:
| Value | Effect |
|---|---|
| unset | Daily, catalogs with auto-update on only. |
0, off, false, no | Never refresh in the background. |
1, on, true, yes, force | Refresh every catalog, including pinned ones, at every start. |
The daily refresh also stops under SYNTIC_DISABLE_NONESSENTIAL_TRAFFIC and the other privacy switches. An explicit force still runs unless offline mode is on (SYNTIC_OFFLINE, or SYNTIC_PRIVACY=disabled). Sessions started with --bare never refresh in the background.
Release a new version
- Change the plugin and bump
versionin its manifest. - Run
syntic plugin tagin the plugin folder to create the tag<name>--v<version>, then push it. See Release a plugin. - If an entry pins a ref, update it in
marketplace.jsonand push the catalog.
Hold users on one version
Pin the entry’s source to a tag or an exact commit:
{ "name": "formatter", "source": "acme/formatter-plugin@0123456789abcdef0123456789abcdef01234567" }A pinned commit installs exactly that commit no matter what else is pushed. For a zip, use a versioned file name with a #sha256= pin.
Run release channels
Keep two catalogs under different names, each pinned to its own git ref of one plugin set, for example acme (tags) and acme-beta (a branch). People opt in by adding the second catalog. A plugin installed from each is tracked separately (formatter@acme, formatter@acme-beta).
Installed plugin files are kept in a folder named after the plugin. Installing the same plugin name from two catalogs on one machine replaces the first copy’s files, so ask people to use one channel at a time.
Rename or remove things
- Rename the catalog. Change
nameinmarketplace.json. The next refresh notices the new name, saves the catalog under it, and moves the installed plugins with it, keeping their version, scope and on/off state. - Remove a plugin. Delete its entry. At the next refresh, users who have it installed see a line flagging it as no longer published. It stays installed until they remove it.
- Retire a catalog. Users run
syntic plugin marketplace remove <name>, thensyntic plugin reconcile --pruneto remove the plugins that came from it.
Depend on other plugins
A plugin can list others it needs under dependencies. They are installed from the same catalog as the plugin that asks for them; a dependency on another catalog is refused, so one catalog cannot pull in code from another. Keep a plugin and everything it depends on in one marketplace. See Dependencies.
What a marketplace cannot do
- It cannot rename an individual plugin for users already on the old name. Publish the new name and ask users to switch.
- It cannot uninstall a plugin from users’ machines. Delisting only flags it.
- It cannot set plugin options for users. Users run
syntic plugin configure, or pass--configat install. - It cannot enforce which catalogs people add. That is what managed policy is for.