Skip to content

Commit a0e0243

Browse files
committed
fix repo
1 parent 522cc57 commit a0e0243

10 files changed

Lines changed: 91 additions & 354 deletions

File tree

.github/ISSUE_TEMPLATE/3-bug.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ body:
88
attributes:
99
label: Where is the bug?
1010
options:
11-
- Tooling (validate / pack / catalog / publish)
11+
- Tooling (validate / pack / publish)
1212
- CI workflow
1313
- A specific plugin
14-
- Catalog / index.json
1514
- Other
1615
validations:
1716
required: true

.github/workflows/publish.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,14 @@ jobs:
102102

103103
- name: Install minisign
104104
if: steps.changed.outputs.names != ''
105-
run: sudo apt-get update && sudo apt-get install -y minisign
105+
env:
106+
MINISIGN_VERSION: "0.12"
107+
run: |
108+
set -euo pipefail
109+
curl -fsSL "https://github.com/jedisct1/minisign/releases/download/${MINISIGN_VERSION}/minisign-${MINISIGN_VERSION}-linux.tar.gz" -o /tmp/minisign.tar.gz
110+
tar -xzf /tmp/minisign.tar.gz -C /tmp
111+
sudo install -m 0755 "/tmp/minisign-linux/$(uname -m)/minisign" /usr/local/bin/minisign
112+
minisign -v
106113
107114
- name: Pack, sign, and upload
108115
if: steps.changed.outputs.names != ''

DESIGN.md

Lines changed: 66 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
**fork → add a plugin → open a PR** to get it listed.
1313
- Every merged plugin is **validated, zipped, hashed, and signed** as a
1414
tamper-evident artifact.
15-
- The repo publishes a **catalog** (`index.json` + per-plugin metadata + signed
16-
zips) via **GitHub Releases**. There is no separate backend server — *GitHub is
17-
the marketplace API*.
15+
- On merge, CI **uploads** each changed plugin (signed zip + signed metadata +
16+
listing assets) to the **VitoDeploy marketplace API** at vitodeploy.com. The
17+
backend stores the artifacts and serves the marketplace listing.
1818
- The Vito app shows a **catalog** of plugins (name, description, icon,
1919
categories) with **a link to each plugin's home page** — a discovery surface,
2020
exactly like muxy's marketplace listing.
@@ -37,16 +37,18 @@ Vito **already** has a plugin system and a rudimentary "marketplace":
3737

3838
This design **adds the registry/marketplace layer** on top: a single curated
3939
monorepo with composer.json manifests, deterministic signed artifacts, and a
40-
published catalog. The 3 official plugins move *into* this repo.
40+
backend that ingests them and serves the marketplace. The 3 official plugins
41+
move *into* this repo.
4142

4243
### What is in v1 vs deferred
4344

4445
- **v1 (this work):** the monorepo + manifest schema + validate/pack/sign/publish
45-
scripts + CI + the 3 migrated plugins + a published catalog, **and** wiring
46-
Vito's marketplace UI to read that catalog (display + homepage link).
46+
scripts + CI + the 3 migrated plugins + uploading signed artifacts to the
47+
vitodeploy.com marketplace API, **and** wiring Vito's marketplace UI to read
48+
that listing (display + homepage link).
4749
- **Deferred (not v1):** rewiring Vito's *installer* to consume signed monorepo
4850
artifacts and derive the namespace from `composer.json`. Vito's existing
49-
GitHub-URL/release install flow stays as-is. The catalog is discovery only
51+
GitHub-URL/release install flow stays as-is. The marketplace is discovery only
5052
for now; "Install" continues to use the existing path (or links out).
5153

5254
## 3. End-to-end flow
@@ -75,20 +77,19 @@ Author forks repo
7577
└─ for EACH changed plugin, independently:
7678
├─ pack deterministically (fixed order, epoch mtimes → stable sha256)
7779
├─ sha256(zip)
78-
├─ sign the zip AND a metadata doc (name,version,sha256,perms,asset hashes)
80+
├─ sign the zip AND a metadata doc (name,version,sha256,asset hashes)
7981
│ with the Vito release key (minisign -W) → two .minisig
80-
├─ upload zip + sigs + assets as a GitHub Release asset set
81-
│ (tag: <name>-v<version>)
82-
└─ regenerate and commit/publish catalog/index.json
82+
└─ POST zip + sigs + metadata + assets to the marketplace API
83+
(vitodeploy.com/api/plugins/upload), one multipart request
8384
8485
85-
GitHub (Releases + raw index.json) ← the marketplace "API"
86-
├─ serves catalog/index.json (the list) + per-plugin metadata
87-
└─ serves the signed zip + signatures as Release assets (CDN-backed)
86+
VitoDeploy backend (vitodeploy.com) ← the marketplace API
87+
├─ verifies the request against the metadata + headers, stores the artifact
88+
└─ serves the marketplace listing + signed zip/signatures to the app
8889
8990
9091
Vito App
91-
├─ Marketplace UI fetches catalog/index.json → browse/search
92+
├─ Marketplace UI fetches the listing → browse/search
9293
├─ shows name, description, icon, categories, "Home page" link
9394
└─ (deferred) install: download signed zip → verify minisign → extract
9495
```
@@ -127,16 +128,12 @@ vito-plugins/ # github.com/vitodeploy/plugins
127128
│ │ └── images.mjs # icon/screenshot dimension + size checks (no deps)
128129
│ ├── validate.mjs # validate one/all plugins (CI + local)
129130
│ ├── pack.mjs # deterministic zip + sha256 for one plugin
130-
│ ├── publish.mjs # pack+sign+upload changed plugins, build index.json
131-
│ └── catalog.mjs # (re)generate catalog/index.json from plugins/
132-
133-
├── catalog/
134-
│ └── index.json # published catalog the app reads (generated)
131+
│ └── publish.mjs # pack + sign + upload changed plugins to the API
135132
136133
├── .github/
137134
│ ├── workflows/
138135
│ │ ├── validate.yml # on PR: validate + dry-run pack + meta gate
139-
│ │ └── publish.yml # on push to main: pack + sign + release + index
136+
│ │ └── publish.yml # on push to main: pack + sign + upload
140137
│ ├── ISSUE_TEMPLATE/
141138
│ │ ├── 1-new-plugin.yml
142139
│ │ ├── 2-report-plugin.yml # security/abuse report
@@ -197,60 +194,53 @@ muxy's "package.json + `muxy` key" pattern.
197194
the plugin root, so the PSR-4 prefix maps to `""` (the plugin dir).
198195
- **`extra.vito`** carries everything the marketplace listing needs; Vito's
199196
plugin *loader* ignores it (it only cares about `Plugin.php`).
200-
- **`min_vito_version`** lets the catalog/app hide plugins incompatible with the
201-
running Vito version (advisory in v1).
197+
- **`min_vito_version`** lets the marketplace/app hide plugins incompatible with
198+
the running Vito version (advisory in v1).
202199

203200
The published schema (`schema/manifest.schema.json`) is the single source for CI
204201
and editor autocomplete.
205202

206-
## 6. The catalog (`catalog/index.json`) — the marketplace "API"
203+
## 6. The upload contract — the marketplace API
207204

208-
There is **no backend**. The repo publishes a catalog that the app fetches over
209-
HTTPS from GitHub (raw file on `main`, and/or a `catalog` GitHub Release for a
210-
stable URL). Shape:
205+
There is **no committed catalog**. CI is the single trusted publisher: on every
206+
push to `main`, `scripts/publish.mjs` packs each changed plugin and POSTs it to
207+
the marketplace API at `vitodeploy.com/api/plugins/upload` as one
208+
`multipart/form-data` request. The backend stores the artifact and owns the
209+
marketplace listing the app renders.
210+
211+
The wire format mirrors what the API's `UploadPluginRequest` validates:
211212

212-
```jsonc
213-
{
214-
"generated_at": "2026-06-18T00:00:00Z",
215-
"schema_version": 1,
216-
"plugins": [
217-
{
218-
"name": "vitodeploy/laravel-reverb",
219-
"slug": "laravel-reverb",
220-
"display_name": "Laravel Reverb",
221-
"description": "Laravel Reverb plugin for VitoDeploy",
222-
"version": "2.0.0",
223-
"official": true,
224-
"categories": ["laravel", "websockets"],
225-
"homepage": "https://vitodeploy.com/docs/plugins/laravel-reverb",
226-
"repository": "https://github.com/vitodeploy/plugins",
227-
"author": { "name": "VitoDeploy", "github": "vitodeploy" },
228-
"min_vito_version": "3.0.0",
229-
"icon_url": "https://github.com/vitodeploy/plugins/releases/download/laravel-reverb-v2.0.0/icon.svg",
230-
"screenshots": ["https://.../screenshot-1.png"],
231-
"namespace": "App\\Vito\\Plugins\\Vitodeploy\\LaravelReverb\\Plugin",
232-
"artifact": {
233-
"url": "https://github.com/vitodeploy/plugins/releases/download/laravel-reverb-v2.0.0/laravel-reverb-2.0.0.zip",
234-
"sha256": "",
235-
"size": 12345,
236-
"signature_url": "https://.../laravel-reverb-2.0.0.zip.minisig",
237-
"metadata_url": "https://.../metadata.json",
238-
"metadata_signature_url": "https://.../metadata.json.minisig"
239-
}
240-
}
241-
]
242-
}
243213
```
214+
POST https://vitodeploy.com/api/plugins/upload
215+
Authorization: Bearer <VITO_UPLOAD_TOKEN>
216+
X-Plugin-Name / X-Plugin-Version / X-Plugin-Sha256 ← cross-checked against metadata + bytes
217+
218+
multipart/form-data:
219+
artifact the packed zip (file)
220+
signature minisign signature over the zip (string)
221+
metadata the signed metadata document (file, application/json)
222+
metadataSignature minisign signature over metadata.json (string)
223+
icon listing icon (file, hash declared in metadata)
224+
screenshot-N listing screenshots (files, hashes declared in metadata)
225+
```
226+
227+
The signed `metadata.json` is the authoritative facts the API consents to —
228+
`name`, `slug`, `version`, zip `sha256`/`size`, `description`, `namespace`,
229+
`categories`, `min_vito_version`, and a `field`+`filename`+`sha256` entry for
230+
the icon and each screenshot. The API cross-checks the `X-Plugin-*` headers and
231+
the received bytes against this signed document, so every trusted fact is
232+
signature-covered (see §7).
244233

245-
The app's marketplace UI reads `plugins[]` to render the catalog and the home
246-
page link. The `artifact` block is what a future signed-install path consumes.
234+
Auth is a static bearer token (the single trusted publisher): the API compares
235+
`Authorization: Bearer <token>` against its `PLUGINS_UPLOAD_TOKEN`. The token is
236+
a GitHub Actions secret (`VITO_UPLOAD_TOKEN`) scoped to the publish workflow.
247237

248238
## 7. Integrity model (signed metadata + signed zip)
249239

250240
Identical to muxy: two minisign (Ed25519) signatures per publish — one over the
251-
zip, one over a metadata document binding `name`, `version`, zip sha256,
252-
declared permissions/capabilities, and each asset's sha256. The matching public
253-
key is committed as `minisign.pub` and **pinned in the Vito app**.
241+
zip, one over a metadata document binding `name`, `version`, zip sha256, and
242+
each asset's sha256. The matching public key is committed as `minisign.pub` and
243+
**pinned in the Vito app**.
254244

255245
When the signed-install path lands (deferred), Vito enforces, in order:
256246
pinned key → verify signed metadata → verify zip sig + sha256 matches metadata →
@@ -275,12 +265,11 @@ no fork access. See SECURITY.md.
275265
1. Skip unless `minisign.pub` is real and `MINISIGN_SECRET_KEY` is set.
276266
2. Diff the merge → changed plugin dirs (or `workflow_dispatch` explicit list).
277267
3. For each: validate → `scripts/pack.mjs``minisign -S -W` (zip + metadata) →
278-
create/update a GitHub Release `<name>-v<version>` with zip + sigs + assets →
279-
`scripts/catalog.mjs` regenerates `catalog/index.json` → commit it back to
280-
`main` (and/or attach to a `catalog` release for a stable URL).
268+
`scripts/publish.mjs` POSTs the signed zip + signatures + metadata + assets to
269+
`vitodeploy.com/api/plugins/upload`.
281270

282271
Determinism: re-running publish on an unchanged plugin yields the identical zip
283-
and hash, so redundant publishes dedupe by `name@version + sha256`.
272+
and hash, so redundant uploads dedupe by `name@version + sha256` on the backend.
284273

285274
## 9. Packaging rules (PHP-specific divergence from muxy)
286275

@@ -307,10 +296,10 @@ path escapes, invalid `composer.json`.
307296
## 11. Vito app-side changes (v1)
308297

309298
In `~/Projects/vito`:
310-
- A **catalog client** that fetches `vitodeploy/plugins` `catalog/index.json`
299+
- A **marketplace listing** rendered from the plugins uploaded to the backend
311300
(replacing or augmenting the GitHub-search queries in `official.tsx` /
312301
`community.tsx`).
313-
- Render the catalog: name, description, icon, categories, a **"Home page"**
302+
- Render the listing: name, description, icon, categories, a **"Home page"**
314303
link (`extra.vito.homepage`), and a star/repository link.
315304
- Keep the existing GitHub-URL install dialog working unchanged.
316305
- Commit the pinned `minisign.pub` into the app for the future verify path
@@ -323,20 +312,18 @@ Resolved:
323312
root `plugins/`. ✓
324313
- **Manifest = `composer.json` with `extra.vito`**, PSR-4 namespace
325314
authoritative. ✓
326-
- **Hosting = GitHub** (Releases for signed zips/assets, `catalog/index.json`
327-
for the listing). No separate vitodeploy.com backend. ✓
315+
- **Publishing = upload to the vitodeploy.com marketplace API** (signed zip +
316+
signed metadata + assets); the backend owns the listing. ✓
328317
- **Signing = minisign/Ed25519**, pinned `minisign.pub`, two sigs (zip +
329318
metadata), key only in CI. ✓
330319
- **Publish granularity = incremental** (only changed plugins). ✓
331-
- **v1 app-side = catalog display + homepage link**; installer rewiring
320+
- **v1 app-side = marketplace display + homepage link**; installer rewiring
332321
deferred. ✓
333322

334323
Open (non-blocking; sensible defaults applied):
335-
1. **Stable catalog URL** — raw `main` file vs a dedicated `catalog` Release
336-
asset. Default: publish both; app prefers the Release asset, falls back to
337-
raw. (Release asset survives history rewrites and is CDN-backed.)
338-
2. **Per-plugin `min_vito_version` enforcement** — advisory in v1 (catalog
324+
1. **Per-plugin `min_vito_version` enforcement** — advisory in v1 (metadata
339325
carries it; app may grey-out incompatible plugins later).
340-
3. **Community tier** — keep GitHub-topic search for non-monorepo community
341-
plugins alongside the curated catalog, or require all via PR. Default: keep
342-
topic-search community tab for now; official tab reads the curated catalog.
326+
2. **Community tier** — keep GitHub-topic search for non-monorepo community
327+
plugins alongside the curated marketplace, or require all via PR. Default:
328+
keep topic-search community tab for now; official tab reads the marketplace.
329+
```

README.md

Lines changed: 12 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,31 @@
11
# VitoDeploy Plugins
22

3-
The official marketplace monorepo for [VitoDeploy](https://vitodeploy.com)
4-
plugins. Every official plugin lives here, and anyone can **fork → add a plugin
5-
→ open a PR** to get it listed.
3+
The official marketplace for [VitoDeploy](https://vitodeploy.com) plugins.
64

7-
Each merged plugin is validated, deterministically zipped, hashed, and signed,
8-
then published as a [GitHub Release](https://github.com/vitodeploy/plugins/releases).
9-
The repo also publishes a **catalog** (`catalog/index.json`) that the VitoDeploy
10-
app reads to render the marketplace. There is no separate backend — **GitHub is
11-
the marketplace API**.
5+
Every official plugin lives here, and anyone can **fork → add a plugin → open a
6+
PR** to get it listed. On merge, CI validates each changed plugin, packs it into
7+
a deterministic zip, signs it with minisign, and uploads it to the VitoDeploy
8+
marketplace.
129

13-
## Official plugins
10+
## Docs
1411

15-
| Plugin | Package | Categories | Home page |
16-
| ------ | ------- | ---------- | --------- |
17-
| **Laravel Reverb** | `vitodeploy/laravel-reverb-plugin` | laravel, websockets | [docs](https://vitodeploy.com/docs/plugins/laravel-reverb) |
18-
| **Laravel Octane** | `vitodeploy/laravel-octane-plugin` | laravel, performance | [docs](https://vitodeploy.com/docs/plugins/laravel-octane) |
19-
| **Tiny File Manager** | `vitodeploy/tiny-file-manager-plugin` | files, utilities | [docs](https://vitodeploy.com/docs/plugins/tiny-file-manager) |
20-
21-
## Browse plugins
22-
23-
The VitoDeploy app's marketplace UI fetches the catalog and shows each plugin's
24-
name, description, icon, categories, and a **Home page** link. To browse here:
25-
26-
- Look under [`plugins/`](plugins/) — one directory per plugin.
27-
- Read [`catalog/index.json`](catalog/index.json) for the generated listing.
28-
- Download a signed zip and its assets from the
29-
[Releases](https://github.com/vitodeploy/plugins/releases) page (tag
30-
`<slug>-v<version>`).
31-
32-
## How the catalog works
33-
34-
On every push to `main`, CI packs each **changed** plugin into a deterministic
35-
zip (stable bytes → stable SHA-256), signs the zip and a metadata document with
36-
the VitoDeploy release key (minisign / Ed25519), and creates a GitHub Release
37-
tagged `<slug>-v<version>` carrying the zip, signatures, and listing assets.
38-
It then regenerates `catalog/index.json` and publishes it (committed to `main`
39-
and attached to a `catalog` release for a stable, CDN-backed URL).
40-
41-
Each catalog entry carries the plugin's identity (name, version, namespace),
42-
listing metadata (display name, description, icon, screenshots, categories,
43-
home page), and an `artifact` block (zip URL + SHA-256, signature URLs, and the
44-
signed metadata URL). See [`DESIGN.md`](DESIGN.md) for the full shape and the
45-
integrity model.
46-
47-
> The published catalog is **discovery/display** in v1. Enforced signed-install
48-
> in the app is a fast-follow. See [`SECURITY.md`](SECURITY.md).
12+
https://vitodeploy.com/docs/plugins
4913

5014
## Contributing
5115

52-
Want to publish a plugin? Read [`CONTRIBUTING.md`](CONTRIBUTING.md) for the full
53-
fork → scaffold → validate → PR flow. The copyable starter is in
54-
[`examples/hello-world`](examples/hello-world).
55-
56-
> **Don't full-clone.** This repo holds every published plugin and grows large
57-
> over time. Use a partial + sparse checkout so you only download your own
58-
> plugin and the tooling:
59-
>
60-
> ```bash
61-
> git clone --filter=blob:none --sparse https://github.com/vitodeploy/plugins
62-
> cd plugins
63-
> git sparse-checkout set plugins/my-plugin scripts schema examples
64-
> ```
65-
66-
## Repository layout
67-
68-
```
69-
plugins/ # one directory per plugin (composer.json + PHP source + assets)
70-
examples/ # hello-world starter plugin to copy
71-
schema/ # manifest.schema.json — the composer.json + extra.vito schema
72-
scripts/ # validate / pack / catalog / publish tooling (Node ≥ 20)
73-
catalog/ # index.json — the generated catalog the app reads
74-
.github/ # CI workflows, issue/PR templates, CODEOWNERS
75-
DESIGN.md # the authoritative design for the marketplace
76-
minisign.pub # signing public key, pinned in the VitoDeploy app
77-
```
16+
Read [`CONTRIBUTING.md`](CONTRIBUTING.md) for the fork → scaffold → validate → PR
17+
flow. Copy the starter in [`examples/hello-world`](examples/hello-world) to begin.
7818

7919
## Local tooling
8020

81-
The tooling needs **Node ≥ 20**:
21+
Needs **Node ≥ 20**:
8222

8323
```bash
8424
npm install
85-
node scripts/validate.mjs [slug] # validate one plugin (or all)
86-
node scripts/pack.mjs --dry-run [slug] # prove the zip is deterministic + see its sha256
25+
npm run validate # validate plugins
26+
npm run pack -- --dry-run # prove the zip is deterministic + see its sha256
8727
```
8828

89-
Authoring a plugin needs **PHP 8.4** and familiarity with Vito's
90-
`App\Plugins\AbstractPlugin` and the `Register*` builders. See
91-
<https://vitodeploy.com/docs/plugins> and the existing plugins in
92-
[`plugins/`](plugins/).
93-
9429
## License
9530

9631
[AGPL-3.0](LICENSE), matching the VitoDeploy app.

SECURITY.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ If a **listed plugin** behaves maliciously (data exfiltration, unexpected shell
1616
or network use, etc.) or violates policy, file a
1717
[report](.github/ISSUE_TEMPLATE/2-report-plugin.yml). Maintainers triage these
1818
with priority and can unlist the plugin by removing its directory, which drops
19-
it from the next published catalog.
19+
it from the VitoDeploy marketplace on the next publish.
2020

2121
## How integrity is guaranteed
2222

@@ -51,8 +51,8 @@ covered by a signature from a key that never leaves CI, a compromised host or
5151
transport cannot substitute bytes, misrepresent the listing, or roll a user back
5252
to an older signed version.
5353

54-
> **v1 is discovery/display.** The catalog is published for browsing today;
55-
> **enforced signed-install in the app is a fast-follow.** The trust anchor
54+
> **v1 is discovery/display.** Plugins are published to the marketplace for
55+
> browsing today; **enforced signed-install in the app is a fast-follow.** The trust anchor
5656
> (`minisign.pub`) ships ahead of the installer work so the pinned key is already
5757
> in users' hands when the verify path lands.
5858

0 commit comments

Comments
 (0)