From 23f2ecb9505ec35ef7a02bf7465fa6beb5ea2b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sun, 16 Aug 2026 11:13:00 +0000 Subject: [PATCH] todo: the marketplace's deferred work, ranked Split out of docs/marketplace.md so the design document stays about how it works. Ranked by what blocks something rather than by size: submissions first (until a stranger's manifest can be read without executing it, the catalogue is ours only and everything below is cosmetic), then plugin upgrades not migrating, then staleness, then scale. Co-Authored-By: Claude Opus 5 --- docs/marketplace-todo.md | 70 ++++++++++++++++++++++++++++++++++++++++ docs/marketplace.md | 2 ++ 2 files changed, 72 insertions(+) create mode 100644 docs/marketplace-todo.md diff --git a/docs/marketplace-todo.md b/docs/marketplace-todo.md new file mode 100644 index 00000000..ad54cc86 --- /dev/null +++ b/docs/marketplace-todo.md @@ -0,0 +1,70 @@ +# Marketplace — deferred work + +What `marketplace.officer.dev` and the plugin install path do not do yet, as of 2026-08-16. Design and +rationale live in `docs/marketplace.md`; this is the list. + +Ranked by what actually blocks something. + +## Blocks other people using it + +- [ ] **Third-party submissions cannot use the sync.** `scripts/marketplace-sync.ts` reads a manifest by + IMPORTING it, which is code execution. Fine for our own repositories, not a mechanism a stranger's + plugin can go through. Needs either a sandbox or a published `manifest.json` alongside the + TypeScript one. Until this exists the catalogue is ours only, and everything below is cosmetic. + +- [ ] **No write API and no submission flow.** Registering a plugin means running the sync by hand against + the database. Deliberate while nobody can submit; the first outside contributor makes it the gap. + +## Blocks a plugin evolving + +- [ ] **Plugin installs CREATE, they do not MIGRATE.** `pushPluginSchema` generates the SQL that builds a + schema from nothing and applies it, skipping objects that already exist. A new version that adds a + column will not get it — the CREATE is skipped as "already exists" and the column is silently + absent. Plugin upgrades need real migrations. The old global `db:push` appeared to handle this, at + the price of being able to drop half the database, so this is a known trade rather than a + regression. + +- [ ] **No versions table.** `plugin_metadata` is one row per plugin — a snapshot, not a history. A store + that can offer "install 1.2 instead of 1.3", or show a changelog, needs a different shape. + +## Staleness + +- [ ] **Nothing refreshes the catalogue automatically.** Publishing a plugin update changes nothing until + somebody runs `bun --env-file=.env scripts/marketplace-sync.ts`. A Gitea webhook or a cron is the + obvious next step. + +- [ ] **The sync reads local clones, not the remotes.** The machine running it must have every plugin + checked out, so the catalogue describes that machine's copies. The version that clones each repo + into a temp directory is a small change. + +- [ ] **`rawUrl()` assumes Gitea and a `main` branch.** A GitHub-hosted plugin, or one whose default branch + is `master`, gets a 404 for its icon. The forge belongs in the registry rather than inferred from a + URL shape. + +## Scale + +- [ ] **`fetchCatalogue()` fetches everything, every time.** ~737 bytes per entry, so 100 plugins ≈ 72KB + and 1,000 ≈ 720KB. Correct at three; the fix at that size is search rather than pagination, and + search is a server-side query that changes the shape anyway — so this is deliberately not built + early. The seam is `fetchCatalogue()` itself, which already returns a promise with three fallback + paths behind it. + +- [ ] **The platform fetches the whole catalogue to answer "what can I install", then discards everything + already on disk.** Fine at three, wasteful at 300. Moving that filter server-side means the platform + telling the store what it has, which would be the first data sent TO the marketplace — worth doing + deliberately rather than by accident. + +- [ ] **No search, no categories, no install counts.** Three plugins do not need them. + +## Smaller + +- [ ] **A plugin's own icon is invisible before install.** `assets/` is published to + `public/plugins//` on mount, which only happens once it is installed; the catalogue's `iconUrl` + works around it by pointing at the repository, and a plugin author has to know to add one. + +- [ ] **`git`, `pm2` and `sudo` are still PATH lookups** in the install path. They work on both machines we + have, and `bunx` did not — see `dc420ba5`. Anything new should follow `pushPluginSchema`'s pattern + of resolving a path rather than trusting `$PATH`. + +- [ ] **Uninstall leaves the ecosystem file's PM2 entry removed but the process's logs behind.** Harmless, + unexamined. diff --git a/docs/marketplace.md b/docs/marketplace.md index a49ec792..f3ec8d14 100644 --- a/docs/marketplace.md +++ b/docs/marketplace.md @@ -106,6 +106,8 @@ refused outright, because that string reaches `git clone`. ## Open items +The full list, ranked, is `docs/marketplace-todo.md`. The ones that shape the design: + - **`[blocker]` Third-party submissions cannot use this sync.** It reads a manifest by importing it, which is code execution. Fine for our own repositories; not a mechanism a stranger's plugin can go through. Submissions need either a sandbox or a published `manifest.json` alongside the TypeScript one.