Enterprise mirror
Run MacCrab Rave in an air-gapped or restricted-network environment
🚧 Planned — not yet available. The reference mirror container isn’t published yet; this page documents the intended design. For early access or to register interest, email maccrab@peterhanily.com.
For enterprises whose MacCrab fleet can’t reach maccrab.com directly (air-gapped networks, restricted internet egress, compliance requirements).
The mirror is a Docker container that runs on a periodically-online proxy machine, pulls the rave catalog + author plugin binaries from upstream, and serves them on the enterprise’s internal network. Clients configure their maccrabctl to use the mirror’s URL instead of rave.maccrab.com/; the rest of the rave architecture — signatures, reproducibility, capability summaries — works identically.
Quick start
The reference container is not yet published; for enterprise-mirror access, contact maccrab@peterhanily.com.
# Illustrative only — the published image path + final flags are announced at
# release (see the "Planned" note above). On the proxy machine:
docker pull ghcr.io/<org>/rave-mirror:latest
docker run -d --name maccrab-rave-mirror -p 8443:8443 \
-v maccrab-rave-mirror-data:/var/maccrab-mirror \
ghcr.io/<org>/rave-mirror:latest
# Periodic refresh — example: every 6 hours via cron
echo "0 */6 * * * docker exec maccrab-rave-mirror maccrab-rave-mirror-sync" | crontab -
# On the air-gapped MacCrab clients
maccrabctl channel add https://internal-mirror.example.com:8443/
maccrabctl plugin search # now hits the mirror, not maccrab.com
Architecture
rave.maccrab.com/ ← upstream, the canonical source
│
▼ pull (sync.sh)
maccrab-rave-mirror container on proxy
/var/maccrab-mirror/
├── rave/catalog.json catalog index
├── rave/catalog/<id>.json per-plugin entries
├── rave/revocations.json revocation list
├── rave/kits/<id>.json kit definitions
├── rave/schemas/*.json JSON schemas
├── rave/.synced-at freshness marker
└── releases/<id>-v<ver>.zip author plugin binaries (flat-directory plugin format)
│
▼ HTTPS serve (Caddy on :8443)
Air-gapped MacCrab clients
maccrabctl plugin install <id>
│
▼ verifies the Ed25519 signature against the pinned signer key
│ (signatures travel with artifacts; mirror is transport-only)
▼
plugin installed
The mirror preserves upstream signatures. Each plugin zip carries its own signature file and signing.key.pub; clients verify the raw Ed25519 signature against the publisher’s pinned signer_public_key_sha256 (and catalog/kit/revocation files against the catalog key), exactly as they would against rave.maccrab.com/. The mirror doesn’t re-sign anything in pass-through mode. This is what makes air-gapped verification work: the mirror is just a transport optimization, not a new trust root.
Freshness + staleness
The freshness indicator that matters:
| Indicator | Source | Warn at | Critical at |
|---|---|---|---|
| Revocation list age | timestamp in /var/maccrab-mirror/revocations.json | 30 days | 90 days |
There’s no trust-root age to track: Ed25519 verification has no TUF trust root and no expiry to refresh. catalog.pub is a static key — it only matters if it rotates (see below), not on a clock.
The MacCrab dashboard surfaces revocation-list age as a banner when the operator’s channel is the mirror. Stale revocations mean a plugin compromised after the last sync may not get auto-uninstalled — operators with security responsibilities should refresh more aggressively than the warn threshold.
Catalog key distribution
A fully-air-gapped environment (no internet ever) needs only one thing fetched out-of-band, once: the catalog public key catalog.pub. Everything else — catalog index, per-plugin entries, kit definitions, revocations, and the plugin zips with their own signature + signing.key.pub — travels in the mirrored static tree and verifies offline against that key. There is no trust root to fetch, no Rekor inclusion proof to chase, and no network call at verification time.
# On a host with internet, once:
curl -O https://rave.maccrab.com/keys/catalog.pub
shasum -a 256 catalog.pub # record this fingerprint and carry it separately
# Transfer catalog.pub to the air-gapped network via your usual mechanism
# (USB, signed media, secure file transfer), and confirm the recorded
# fingerprint on arrival before trusting it.
# On the air-gapped proxy / mirror, pin the key the mirror serves:
cp catalog.pub /var/maccrab-mirror/keys/catalog.pub
catalog.pub is a static Ed25519 key with no expiry to monitor. It only needs re-distributing if the rave project rotates its catalog signing key; a rotation is announced out-of-band and shows up as a signature-verification failure against the old key (re-fetch and re-confirm the fingerprint, then re-pin).
Mirroring the static tree itself is a plain recursive fetch — the whole / directory is signed, static files:
# On the periodically-online proxy, pull the signed tree as-is:
wget -r -np -nH --cut-dirs=0 https://rave.maccrab.com/ -P /var/maccrab-mirror/
Pass-through vs curated mirror
Pass-through (default): mirror everything upstream serves, unmodified. Clients see the full upstream catalog. Recommended for most enterprises — operationally simplest, fewest moving parts, no enterprise signing infrastructure required.
Curated: enterprise removes plugins per policy.
- Filter mode: mount a
filter.jsonlisting approved plugin IDs; the mirror’s sync.sh script skips entries not in the filter. Clients still verify against the upstream signer key; they just see a smaller catalog. Recommended over re-sign for most “we don’t want plugin X” cases. - Re-sign mode: enterprise re-signs the curated subset with their own Ed25519 catalog key. Requires the enterprise to manage that keypair. Clients configure
maccrabctlto pin the enterprise’scatalog.pubfor that channel. Recommended only when filter-mode isn’t enough (e.g., the enterprise needs to MODIFY plugin contents, not just exclude).
Reference container ships pass-through. Filter and re-sign mode require operator customization documented in the container README.
What the mirror does NOT replace
- Plugin authors’ GitHub Releases — those are still upstream of the mirror. The container pulls release artifacts from
github.com/<author>/<repo>/releases/download/...during sync. If you want a fully-detached deployment, fetch + commit the binaries to your enterprise’s internal release server and modify the mirror’srelease_url_templatesubstitution to point there. - maccrab.com landing page — the mirror serves
/only. The MacCrab core landing page is its own concern; for enterprise deployments, MacCrab typically gets installed via internal MDM and doesn’t need the landing page. - Cloudflare — the mirror doesn’t depend on Cloudflare; it’s running on your network. Upstream availability of
rave.maccrab.com/is required for the periodic sync, but not for the air-gapped clients’ install flow.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
maccrabctl plugin verify fails on every signature | wrong or missing catalog.pub pinned on the client/mirror | restore catalog.pub from the out-of-band copy and re-confirm its fingerprint |
| Sync fetches catalog metadata but not binaries | FETCH_RELEASES=0 set, or GitHub Releases unreachable from proxy | check proxy’s internet egress; toggle env var |
Client maccrabctl plugin install fails with “signer key mismatch” | mirror re-signed but client still pins the upstream catalog.pub | re-pin the client’s channel key per re-sign mode docs |
/var/maccrab-mirror/.synced-at shows old timestamp | refresh cron not running, or sync failures | check container logs: docker logs maccrab-rave-mirror |
| Freshness banner showing critical in MacCrab dashboard | mirror hasn’t refreshed in >90 days | manually run docker exec maccrab-rave-mirror maccrab-rave-mirror-sync |