Submit a plugin
How to publish your own forensic plugin to the MacCrab Rave catalog
The MacCrab Rave catalog lists vetted forensic plugins that MacCrab installs and runs under Apple’s App Sandbox. If you’ve built a collector or analyzer that fits the platform, this is the path to get it listed.
Status
| Channel | What lives here | Open for submissions? |
|---|---|---|
official | First-party plugins built and signed by the MacCrab team. (MacCrab also ships with built-in forensic collectors compiled into the app — those are part of the app, not catalog entries.) | No |
contrib | Third-party plugins from independent authors. Same App Sandbox, same Ed25519 signing requirements, listed in the catalog after vetting. | Open — owner-reviewed |
The contrib channel is open: submit a plugin and it opens an owner-reviewed vetting pull request. Nothing publishes until it passes vetting and the catalog is re-signed. The flow below is what to expect.
The submission flow
1 · Build to the platform spec
Your plugin must conform to the sandboxed plugin format:
- A signed binary (Ed25519, your publisher key)
- A
manifest.jsondeclaring every capability the binary uses - A reproducible build from public source
- Runs to completion in a few seconds; emits structured records (NDJSON over stdout)
The fastest start is to copy the reference plugin (com.example.maccrab.login-items-lite) — a minimal, reproducible, least-privilege collector that already passes every vetting gate; swap in your own logic. The official com.maccrab.forensics.* plugins in the catalog are also good models.
Build your release with the catalog’s pinned toolchain. Stage 3 rebuilds your source on a hosted macOS runner with a fixed Xcode and compares the canonical hash to your published artifact. A binary built with a different Swift/Xcode won’t byte-match — the reproducibility gate fails even when your source is identical. Build (and cut your release) with the same Xcode the vetter pins, or your submission can’t be verified.
2 · Self-vet locally
Run the same checks the catalog runs:
# 1. Schema-validate your manifest (fetch the published schema first)
curl -o manifest-schema.json https://rave.maccrab.com/schemas/manifest.json
python3 -c "import json, jsonschema; \
jsonschema.validate(json.load(open('manifest.json')), \
json.load(open('manifest-schema.json')))"
# 2. Confirm your local install + verify succeeds
maccrabctl plugin install ./dist/<your-plugin-id>
# 3. Confirm reproducible build
./scripts/build.sh # first build
./scripts/build.sh # second build — same canonical hash
If any of these fail locally, the catalog vetter will catch the same problem — fix before submitting.
3 · Submit through the in-site form
Submission opens with the contrib channel. When it lands, you submit through an authenticated form on this site: sign in with GitHub, and it opens a vetting pull request against the catalog on your behalf — no hand-filed issues. The form collects:
| Field | Why we ask |
|---|---|
Plugin id (reverse-DNS, e.g. com.example.myorg.disk-walker) | Must not collide with an existing entry |
| Source repository URL | Public GitHub repo. We clone it to verify reproducibility. |
| Release tag + canonical hash | Pins the exact bytes you’re submitting |
| Capability declarations | What TCC permissions, file paths, network endpoints your plugin needs |
| What it does | One paragraph for the catalog detail page |
| What it reads | Human-readable list of files/databases |
| What it emits | Content type IDs and one-line per-type descriptions |
| Author identity | Becomes signer_identity in the catalog entry (e.g. github:your-handle) |
| Security contact | Where to route disclosures |
| Maintenance commitment | Bus-factor + response-time expectations |
4 · Vetting
Every submission goes through three gates:
Automated checks
- Manifest validates against schema v1
- Ed25519 signature verifies against your declared publisher key
- Publisher key not on the revocation list
- Reproducible rebuild from declared source matches the canonical tree hash
- Capability declarations match observed binary behaviour
Human review
- Code-read for hidden capability bypasses (paths constructed at runtime, dynamic execve, dylib injection)
- Sanity-check on author identity + signer_identity
- Project-fit: is this a collector or analyzer, or does it fit some other tier we don’t have yet?
Catalog landing
- A PR adds your plugin’s catalog entry at
rave/catalog/<plugin-id>.jsonwith pinned digests, vetting timestamp, and the policy commit it was vetted against - The PR is signed by the maccrab-rave project key
- Lands in the next deploy of the catalog
A typical end-to-end window for a clean submission is 3–7 days.
What can be a plugin
MacCrab forensic plugins fall into two roles:
Collectors
Snapshot a part of the system into structured records. For example, a collector might capture:
- every privacy-permission (TCC) grant
- every launch agent + daemon
- Safari history, downloads, and extensions
- iMessage handles + threads (metadata only, no bodies)
A new collector should pick a specific data source — a file, a database, a system service — and emit one record per item found. The first-party com.maccrab.forensics.posture-pro is a worked example of a collector.
Analyzers
Cross-reference records that other plugins already collected, and surface patterns. For example, an analyzer might:
- flag unsigned persistence + unfamiliar-team automation by correlating multiple collectors’ output
Analyzers don’t read the filesystem directly. They read other plugins’ committed artifacts in the same case.
Out of scope
Plugins that try to:
- Modify system state (write to anywhere outside MacCrab’s case dir)
- Make outbound network connections
- Spawn subprocesses
- Hold privileges the binary doesn’t declare in its manifest
…will be rejected. The App Sandbox refuses them at runtime anyway, but the catalog enforces declarative consistency at vetting time.
Capability policy
The vetting policy is canonical, but as a rough guide:
| Capability | Default reception |
|---|---|
Read /etc, /Library/LaunchAgents, similar system-config paths | Accepted |
Read user-content paths (~/Documents, ~/Downloads) | Needs explicit justification — what files specifically and why |
| Write anywhere | High bar — plugins shouldn’t normally write outside MacCrab’s case dir |
| Outbound network (allowlisted) | Accepted with an explicit allowlist; refused for wildcard |
| Process exec | High bar — must be a specific path with specific args |
| Process fork | Refused outside the platform’s own sandbox spawn primitives |
| Mach service connect | High bar — must be a specific service with stated purpose |
A plugin that asks for the minimum needed gets vetted faster.
Channel promotion: contrib → official
contrib plugins are perfectly installable and run under the same sandbox as official plugins. The badge difference just affects sort order in the catalog browser.
To get promoted to official:
- 90 days in
contribwith no reported issues - An update cycle through the catalog (you’ve shipped a v0.2.x at least)
- A second human reviewer agreeing on capability-fit
- Adoption signal (community usage)
Namespace review
com.maccrab.* is reserved for first-party plugins and a contrib submission claiming it is hard-rejected — that rule does not change. Separately, if your plugin id’s vendor label is confusingly close to the MacCrab brand (a misspelling like com.maccreb.*, or a brand-plus-suffix like com.maccrab-tools.*), the submission is accepted and carries a soft needs_namespace_review triage flag.
This flag is not a rejection. It only force-routes the submission to manual owner review — a namespace-flagged submission is never auto-merged, regardless of diff size. From there:
- A flagged author proceeds via owner review and appeal — if your vendor is legitimately distinct, contact the owner with the context.
- If you are a genuinely distinct vendor, pick a clearly distinct vendor label so your id reads as your own brand rather than a lookalike of MacCrab’s.
If a submission gets rejected
You’ll get the reason in your submission’s review thread. Common ones:
- Reproducibility fails — declared source doesn’t produce the released bytes. Fix the build to be deterministic.
- Capabilities don’t match source — manifest declares one thing, binary tries another. Reconcile.
- Too broad — plugin does five things; split it.
- Capability ask too high — see the table above.
Re-submission after a fix is fine. No quota.
Help
- Sandboxed plugin format: /docs/verification/
- HTTP catalog API: /docs/catalog-api/
- Install URL scheme: /docs/url-scheme/
- Terms of Service (third-party-plugin liability, capability disclosure, takedown): /docs/terms/
- Contact: maccrab@peterhanily.com