--- id: plugin-ecosystem/no-install-from-git-url title: "No install-from-git-URL" theme: plugin-ecosystem status: done created: 2026-08-18 date_source: section order: 3 --- **No install-from-git-URL.** The catalogue is the only route for a remote plugin, so the "build your own, give it to a friend" path has no affordance. Small, and needed before the unofficial channel is real. **Done 2026-08-28.** `POST /api/plugins/install-from-url`, owner-only, taking `{ url }` and optionally `{ appName }`. Smaller than this note assumed: `fetchPlugin` already cloned, validated the URL, refused to overwrite an existing directory, checked for a `manifest.ts` and cleaned up a failed clone. One line tied it to the catalogue — where the URL came from. So the change is a source parameter threaded through, plus a route. The name comes from the repository, because `appName` IS the directory name and is never declared in a manifest. That is checked against `^[a-z0-9]+(-[a-z0-9]+)*$` BEFORE cloning: it becomes the route segment, the table prefix and the pm2 process name, and a half-clone under an unusable name reads as a broken plugin rather than an absent one. The first real user is `fofos/fofos-admin` — one website's admin panel, which should never be in a registry. Verified end to end against it and then purged, since this is not that website's machine.