Open Registry Protocol
Version: 1.0 The Qomicex plugin store’s API protocol has been abstracted into an open standard, allowing third parties to implement compatible registries (mirrors / private repositories / intranet custom sources). The launcher talks to any compatible implementation through multi-source discovery.1. Core concepts
A registry is an HTTP API endpoint that provides plugin metadata queries and distribution. A registry has two roles:
The launcher configures its source list in
settings.registryUrl and tries them in priority order.
2. Endpoint subset
2.1 Core endpoints (mirrors must implement)
Plugin object (core fields):
{id, slug, name, description, category?, tags[], iconUrl?, latestVersion?, downloadsCount, ratingAverage?, ratingCount, createdAt}.
2.2 Optional endpoints (mirrors may skip)
The launcher degrades gracefully for optional endpoints: when they are unavailable, the corresponding UI features are hidden or disabled.
2.3 Common conventions
- Base URL: the registry API root, e.g.
https://plugins.qomicex.top/api/v1 - Request/response: JSON, fields named camelCase; times are ISO 8601
- Error format: always
{ "error": { "code": "...", "message": "..." } }, with semantic HTTP status codes - Pagination:
page(starts at 1),pageSize(default 20, max 50) - Caching: 15s recommended for the plugin list, 300s for details, 60s for registry metadata
3. Registry metadata GET /registry
The registry’s identity card. Once the launcher discovers registryUrl, the first request it sends is to this endpoint, to confirm compatibility:
The mirror domains inmirrorsare used for racing downloads, not for API fallback. API fallback is controlled by the launcher’sregistryUrlmulti-source list.
4. Client discovery
4.1 Configuration
The launcher settings support plugin-style multi-source discovery:- Primary source (
primary: true): the registry the launcher queries by default - Backup source (
primary: false): used automatically when the primary is unreachable or times out (backup sources are read-only; write operations are only attempted on the primary) - Priority (
priority): lower numbers come first
4.2 Startup flow
- The launcher sends
GET /registryto all sources withenabled: truein parallel - A source is marked available when
apiVersion >= 1andcapabilitiesincludes at leastlist,detail, anddownload - Sources are sorted by
priority, and the first available one is chosen as the primary - It caches
pluginCountfor every source and notes the plugin source in the UI - After N consecutive failures on the primary, it switches automatically to a remaining available source (if none is available, polling pauses)
4.3 Fallback strategy
5. Dual-channel downloads and SHA-256 verification
5.1 Dual-channel semantics
GET /plugins/:slug/versions/:version/download returns:
sha256.
5.2 Mandatory verification rules
- After the download completes, compute the SHA-256 and compare it with
sha256in the response (all-lowercase hex) - A mismatch → refuse to install, and do not retain the tampered package
- The
sha256returned by a mirror must match the primary source exactly — mirrors may not modify package contents
5.3 Package requirements for mirrors
When a mirror re-serves a package it must:- Keep the file structure inside the package completely unchanged (no repacking, no manifest edits, no added or removed files)
- Preserve
signature.jsonandsignature.cert.jsonverbatim (the signature chain must be passed through intact; the receiver’s verification relies on the certificate inside the package) - Respond with status 200 and
Content-Type: application/octet-streamorapplication/zip
6. Signature chain requirements (ADR-050)
The Open Registry Protocol’s signing requirements concern package integrity only; mirrors are not required to provide their own signature verification service:6.1 In-package signature chain structure
- Mirrors must not repack, and must not modify
signature.json/signature.cert.jsonor any other file in the package - Mirrors may add an extra checksum digest in response headers (such as
X-Checksum-SHA256), but it cannot replace the in-package signature - The launcher verifies the Ed25519 signature before installing; mirror services do not need to care about verification details
7. Self-hosting mirror guide
7.1 Option A: Cloudflare Worker replication (shared load)
Best when the goal is to offload download traffic from the official store:- Fork / deploy the Worker code from the
qomicex-plugin-storerepository to your own Cloudflare account - Configure your own
CDN_BASE_URL(R2 or external storage) andMIRROR_BASE_URL - Periodically sync plugin data from the main store into your own D1 (
wrangler d1 execute --file=sync.sql) - Add
registryUrl: "https://your-domain/api/v1"to the launcher settings as a backup source - Point download traffic at your own R2 / CDN
7.2 Option B: pure static hosting (read-only mirror)
If you only need a read-only cache, no Worker compute is required:- Periodically fetch the main store’s
GET /pluginslist plus each detail page, and generate a staticregistry.json - Upload the downloaded
.qpluginpackages to your own CDN / object storage - Host
registry.jsonand the download files under the same domain - Provide a static
GET /registrymetadata endpoint (returning the contents ofregistry.json) - A static mirror can only provide core read endpoints and does not support
check-updates(which requires compute); declare this honestly incapabilities
7.3 Option C: full self-hosted registry (independent source)
Run your own review/organization/authentication flows:- Fully implement the core endpoints +
GET /registry - Optionally implement endpoints such as
authandreviews(declare them honestly incapabilities) - Configure your own signing root public key (
PLUGIN_ROOT_PUBLIC_KEY); developers upload a public key, receive a certificate, then sign and upload - Once clients configure
registryUrl, it works as a complete independent source
7.4 Verify mirror compatibility
8. Launcher integration plan (TODO)
The launcher backendservices/plugin_store.rs currently uses a fixed QOMICEX_STORE_API_BASE environment variable as a single-source proxy. The changes needed for multi-source integration:
Suggested phased plan:
- Add a
GET /store/registrypass-through in the backend (a small standalone endpoint that does not break existing semantics) - Parameterize the backend’s
store_api_base()to select the base URL by source - Add a
registryUrlinput (multiple entries allowed) to the frontend settings page - Install/update pull from the specified source; when unspecified, default to the official source
🚧 The integration above is not implemented yet; the launcher is still a fixed single-source proxy. This specification serves as the foundation for the open ecosystem and will be implemented in detail by follow-up PHASE tasks.