Catalog JSON API
Signed catalog resources, verification, and current compatibility constraints
Rave publishes a static catalog index, per-plugin entries, and a revocation list. Each of those JSON documents has a detached Ed25519 signature. Release archives have their own publisher signature inside the bundle. Public keys, schemas, and website pages are not themselves covered by those document signatures.
Base URL
https://rave.maccrab.com/
The CLI’s explicit --catalog-base option changes the metadata transport base. It does not change the pinned key or rewrite artifact URLs inside signed entries. The current entry schema requires release URLs at https://rave.maccrab.com/; a complete enterprise mirror is planned.
Resource map
| Path | Purpose | Format |
|---|---|---|
/catalog.json | Plugin index | Catalog schema |
/catalog.json.sig | Index signature | Raw 64-byte Ed25519 signature |
/catalog/<id>.json | Per-plugin versions, vetting, and digests | Entry schema |
/catalog/<id>.json.sig | Entry signature | Raw 64-byte Ed25519 signature |
/revocations.json | Plugin version revocations and serial | Revocation schema |
/revocations.json.sig | Revocation-list signature | Raw 64-byte Ed25519 signature |
/keys/catalog.pub | Published catalog public key | Raw 32-byte key |
/keys/catalog.fingerprint | Public-key SHA-256 | Hex string |
/releases/<tag>/<id>.maccrabplugin.zip | Published plugin archive | Flat plugin directory in ZIP |
/schemas/<name>.json | Schema definitions | JSON Schema 2020-12 |
/kits/<id>.json, .json.sig | Planned kit definitions/signatures | Kit schema; none published |
See current status for the available version and its limitations. Resource definitions for kits do not imply available kit installation.
Signature verification
Catalog document signatures cover their exact file bytes, with no JSON normalization or envelope. Load a trusted catalog key before fetching metadata:
from pathlib import Path
from urllib.request import urlopen
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
# catalog.pub must already come from an authenticated distribution or
# have a fingerprint confirmed through a separately authenticated source.
key = Ed25519PublicKey.from_public_bytes(Path("catalog.pub").read_bytes())
base = "https://rave.maccrab.com/"
with urlopen(base + "catalog.json", timeout=30) as response:
catalog = response.read()
with urlopen(base + "catalog.json.sig", timeout=30) as response:
signature = response.read()
key.verify(signature, catalog)
This illustrates the signature primitive, not a complete network client. Production clients also need bounded responses, URL/redirect policy, schema validation, rollback state, compatibility checks, and revocation handling. Fetching a key and its fingerprint from the metadata origin alone does not independently establish that origin’s identity.
The same primitive verifies each signed entry and revocation list. The archive’s publisher signature uses a different signed payload and key. See verification for a worked archive example and the distinction between content integrity, publisher trust, and runtime admission.
Index and entry facts
The current index uses schema_version: "1", a catalog_serial, a generation timestamp, and a plugins object keyed by plugin ID. Each summary carries display and compatibility metadata, current version, channel, trust tier, runtime token, and publisher-key fingerprint. Detail consumers fetch the corresponding signed entry.
The wire value runtime: "tierB" identifies the external plugin format. It does not establish App Sandbox or no-network enforcement. Published MacCrab v1.21.5 executes first-party plugins unsandboxed after Developer ID and host-team validation, with the host’s privileges including granted Full Disk Access. UIs must describe the actual execution path, separately from requested/declared behavior.
| Field | Meaning and limits |
|---|---|
id | Plugin identity; must agree with the entry path and manifest. |
schema_version | Required on the index. Optional in the current entry schema and absent from the current signed Posture Pro entry. Missing historical facts should remain “not recorded.” |
signer_identity | Human-readable publisher label; not sufficient for key trust. |
signer_public_key_sha256 | SHA-256 of the publisher’s raw 32-byte Ed25519 key, endorsed by verified catalog metadata. |
current_version | Default version selected from the entry’s version records. |
versions.<v>.tag | Recorded release tag used in URL construction. |
versions.<v>.source_commit | Recorded source revision; an accessible source and exact rebuild are still required for independent reproducibility. |
versions.<v>.artifact_sha256 | Raw archive SHA-256. |
versions.<v>.manifest_sha256 | Exact manifest-byte SHA-256. |
versions.<v>.signature_sha256 | Raw publisher-signature file SHA-256. |
versions.<v>.canonical_tree_sha256 | Normalized archive digest. A match alone does not prove a source rebuild. |
versions.<v>.reproducibility | verified or not_applicable; distinguish absent historical values. Posture Pro 0.3.1 records not_applicable. |
versions.<v>.vetting | Recorded check labels, policy commit, and version-specific vetted_at timestamp. |
release_url_template | URL template with {tag} and {file}; current schema pins the official origin. |
metadata.min_maccrab_version | Entry-wide minimum app version; enforced on installation. |
metadata.max_maccrab_version, metadata.min_macos | Declared compatibility fields; current clients do not enforce every field, including the OS floor. Do not rely on an ignored field to exclude unsupported hosts. |
status | active, pre-release, unreachable_temporary, unreachable_extended, archived, or revoked. |
The current contract has no per-version compatibility fields or architecture restriction. MacCrab supports Intel, but Posture Pro 0.3.1’s published archive is arm64-only. Supporting a future archive requires both compatible client admission and qualification of the actual host/artifact combination.
Install resolution
A complete installation must:
- Fetch and verify the index and per-plugin entry against the trusted catalog key, enforcing rollback and publication state.
- Resolve the requested ID/version, check applicable compatibility and publisher trust, and fetch/verify current revocations.
- Reject a matching exact-version, all-versions, or inclusive version-range revocation.
- Expand
release_url_templateusing the selected record’stagand<plugin-id>.maccrabplugin.zip. - Apply the client’s download-origin/redirect policy and verify the raw archive digest.
- Validate the extracted bundle, manifest identity, pinned publisher key, and signature before committing the install.
The app’s install link carries only a plugin ID and still requires confirmation; see the URL contract. Current app capability descriptions are not yet derived from the exact version’s manifest, so the listing’s access disclosures remain important.
Revocation and freshness
The index’s catalog_serial and revocation list’s serial are signed monotonic counters. A client with recorded serial N rejects a subsequently fetched valid document whose serial is lower than N. Publication must advance the relevant counter when signed state changes.
Serial checks protect against rollback relative to recorded history. They do not prove the first document received is current. Likewise, a successful fetch today does not establish a recent signed issue date. The current app’s revocation freshness display tracks fetch time; do not describe it as signed expiry enforcement.
Development rehearsals and schema changes
Local catalog-key overrides, loopback transport, and release-URL rewrites are development seams. They are not supported production trust-configuration instructions and must not be presented as an enterprise deployment procedure.
Current schemas use additionalProperties: false; new fields are not automatically compatible with all validators or readers. Schema, generator, verifier, and client changes must be introduced together with tested compatibility behavior. New required constraints need supporting readers and a published enforcing host before dependent plugins are activated. A future format path or expiry policy is a design decision, not an already implemented guarantee.