CommonCompute
Get startedDownload the Mac app
← All posts
May 14, 2026·8 min·Tyler Gee

How we sign and audit every task

The trust model behind running customer code on someone else's Mac.

The trickiest part of running a compute marketplace isn't dispatch — it's trust. When a customer submits a task and a provider runs it, three different parties need to believe the same thing about what happened:

  • The customer needs to know the task ran the way they asked, on hardware they didn't pick, with outputs they didn't see produced in real time.
  • The provider needs to know what they ran isn't going to compromise their Mac, exhaust their disk, or burn their network.
  • We need to know neither party is lying to the other before we settle billing.

This post walks through the trust model and the specific bits of code that enforce it.

The threat model

We don't trust ourselves. The coordinator is one bug or one breach away from sending a malicious task to a provider, so the provider's runtime treats every assignment as untrusted until proven signed. We don't trust the customer. The task payload — model name, parameters, input URL — could be crafted to escalate, exfiltrate, or denial-of-service the runner. We don't trust the provider. A misbehaving runner can lie about completion, claim partial work it didn't do, or sit on a task forever.

Each of those is a separate enforcement layer. The rest of this post is what each one looks like in the codebase.

Signed task assignments

The router (apps/router, written in Rust on Cloudflare Workers) signs every task assignment with an Ed25519 key it holds in Workers Secrets. The Mac app pins the corresponding public key in `Security/RouterPubkey.swift`.

When a provider's Mac receives a task_assignment frame over the WebSocket, the first thing it does is verify the signature against that pinned key, with two extra guards:

  • 5-minute clock-skew window. A signed assignment that's older than five minutes is rejected. Stops replay attacks even if the WebSocket is somehow MITM-able (it isn't — TLS 1.2+ and ATS enforce that — but defense-in-depth).
  • Replay protection via task ID. Each assignment carries a UUID; the runner refuses to dispatch a task ID it has already seen.

The signature scheme is implemented in `Security/Envelope.swift`. If the verification fails, the assignment is dropped silently and a structured log line is emitted to the ai.commoncompute.app subsystem (visible via Console.app or the log show CLI).

Hash-pinned model downloads

A naïve compute marketplace ships customer-supplied model bundles to provider machines. That's a great way to ship malware to a thousand Macs at once. We solve it by publishing a signed manifest of allowed models — apps/web/public/downloads/models-manifest.json — that maps model_id → SHA-256 + download URL.

The Mac app's ModelManager only downloads from URLs in the manifest, only fetches over HTTPS, and SHA-256s every byte before allowing a runner to load the bundle. A compromised mirror cannot serve a backdoored model — the manifest signature would fail first, then the per-file hash would fail second.

This sounds paranoid until you see the surface area: ANE inference needs mlpackage bundles, Whisper needs Core ML conversions of OpenAI's checkpoints, image upscale needs Real-ESRGAN weights, and each of those is hundreds of megabytes of trusted binary that runs in-process on the provider's Mac.

Trusted URL gating

The other vector is task inputs. A customer submits "transcribe this audio file" with a URL. The naïve implementation fetches that URL on the provider's Mac and pipes the bytes to Whisper. That URL is attacker-controlled.

We added a single chokepoint in `Networking/TrustedDownload.swift` for every job input. It refuses anything except HTTPS to public hosts. Loopback (127.0.0.0/8, ::1), RFC1918 ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), AWS metadata (169.254.169.254), and link-local addresses are all rejected — including via HTTP redirect, which is the part naive implementations miss.

Magic-byte checks confirm the downloaded content matches what the runner asked for. A Whisper runner that requested an audio file will refuse to operate on a payload whose first eight bytes don't match a known audio container. That blocks the variant of the attack where the URL serves audio to your validator and a Python script to your runner.

Hardened runtime and entitlements

The Mac app ships with Apple's hardened runtime enabled. It declares only two entitlements: com.apple.security.network.client (so it can make outbound HTTPS calls) and com.apple.security.keychain.access-groups (so it can store the user's session token in the Keychain). The full list lives in `CommonCompute.entitlements`.

What we deliberately don't declare:

  • com.apple.security.network.server — no inbound listeners. The app cannot be reached on a port by anything on your network.
  • com.apple.security.device.camera / microphone — no AV access. Even if a runner crashed in a creative way, it can't grab your webcam.
  • com.apple.security.files.* — no file system access outside the app's container. Job inputs land in a sandboxed directory the runner cannot escape.

The one entitlement that does raise an eyebrow on a security audit is com.apple.security.cs.disable-library-validation — but it's there for Sparkle. Sparkle's update flow requires it because Sparkle's helper binaries are signed by Sparkle, not by our team. The compensating control is EdDSA signature verification on every update: even if library validation is off, an unsigned update binary cannot replace the running app.

Receipts: who signed what

Once a task completes, the provider's runner emits a result envelope that's signed with the device's own Ed25519 keypair (generated at first run, stored in Keychain). That result lands at the coordinator, which:

1. Verifies the device signature against the public key we recorded at provider enrollment. 2. Cross-checks the result against the original assignment (right task ID, right output schema, no extra fields). 3. Co-signs the resulting envelope with the coordinator's own key.

The double-signed envelope is what we hand back to the customer as the receipt. It is exportable as JSONL, and it's what we use to settle billing. If a customer disputes a charge, the receipt is the artifact that proves a specific provider ran a specific task at a specific time and produced a specific output hash.

Crash and diagnostics: opt-out, no third-party SDK

The Mac app subscribes to Apple's MetricKit for crash and performance diagnostics. MetricKit delivers payloads ~24 hours after the event, which we forward to /v1/diag/crash after running them through a PII redactor that strips email addresses, API keys, AWS credentials, and Bearer tokens before persisting.

The whole diagnostics pipeline is gated on a Settings → Advanced toggle. It defaults to on, but a click turns it off and the MetricKit subscription is removed live without an app restart. The implementation is in `DiagnosticsReporter.swift`. We don't embed any third-party crash SDK — no Sentry, no Bugsnag, no Crashlytics — both because we want to keep the binary minimal and because the supply-chain risk of a third-party crash reporter on machines running customer workloads isn't a tradeoff we're willing to make.

What we haven't done yet

In the spirit of being honest about this, the things you'd reasonably expect a serious shop to have that we don't yet:

  • External penetration test. Scoped for after v1.7. Results will be posted in summary form on /security once remediation lands.
  • SOC 2 Type II. Targeting Q1 2027. We won't sell a certification we don't have; we'll roadmap it transparently.
  • Bug bounty program. No paid bounty yet. The disclosure process is documented in SECURITY.md and we publish a researcher hall of fame for valid reports.

The trust posture is built brick by brick. If you find a brick out of place, email [email protected] — we acknowledge within 7 days.

Common Compute is a marketplace for AI workloads on idle Apple Silicon. We pay providers every Friday and quote customers a locked per-task price. $5 in credits on signup — no card required.
More from the blog