PluginsHost a marketplace

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.

HostUsers add it withGood for
A GitHub repositoryacme/acme-plugins (add @ref or #ref for a branch or tag)Most teams. Versioned, reviewable, easy to fork.
Any git serverhttps://git.example.com/team/plugins.git, git@host:team/plugins.git, gitlab:acme/pluginsSelf-hosted git, GitLab, other hosts.
A shared or local folder/srv/plugins or ~/src/pluginsA network share, or developing the catalog itself.
A URL that serves the JSONhttps://plugins.example.com/marketplace.jsonA static web server or object store.
An npm packagenpm:@acme/plugins or @acme/pluginsTeams that already publish to a registry.
A program that prints the JSONOnly 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

WhatLimit
A plugin .zip download100 MB
What a .zip unpacks to300 MB, at most 20,000 entries
What an npm tarball unpacks to200 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=ssh

Credentials 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 every syntic 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 press r on the plugin in /plugin to fetch the new files. The plugin keeps its on/off state.

Turn auto-update off for a catalog

syntic plugin marketplace auto-update acme off

The 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:

ValueEffect
unsetDaily, catalogs with auto-update on only.
0, off, false, noNever refresh in the background.
1, on, true, yes, forceRefresh 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

  1. Change the plugin and bump version in its manifest.
  2. Run syntic plugin tag in the plugin folder to create the tag <name>--v<version>, then push it. See Release a plugin.
  3. If an entry pins a ref, update it in marketplace.json and 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 name in marketplace.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>, then syntic plugin reconcile --prune to 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 --config at install.
  • It cannot enforce which catalogs people add. That is what managed policy is for.