# Darkbloom (AI inference on idle Macs) – security audit with PRs submitted

> Source: <https://gist.github.com/mudiam/1ffc898333ac3d5bdc5d7fac96d33360>
> Published: 2026-08-26 03:03:02+00:00

# Darkbloom Security Audit Report

**Target:** https://www.darkbloom.dev
**Repositories:** https://github.com/Layr-Labs/d-inference · https://github.com/darkbloomdev/darkbloom
**Date:** 2026-08-24
**Status:** Research prototype, unaudited, public alpha
**Auditor:** Independent community review (Claude Opus)
**GitHub Issues Filed:** [#705](https://github.com/Layr-Labs/d-inference/issues/705), [#706](https://github.com/Layr-Labs/d-inference/issues/706), [#707](https://github.com/Layr-Labs/d-inference/issues/707), [#708](https://github.com/Layr-Labs/d-inference/issues/708) *(closed — false positive)*, [#709](https://github.com/Layr-Labs/d-inference/issues/709), [#710](https://github.com/Layr-Labs/d-inference/issues/710) *(closed — false positive)*, [#711](https://github.com/Layr-Labs/d-inference/issues/711), [#712](https://github.com/Layr-Labs/d-inference/issues/712) *(closed — already fixed in PR #511)*, [#713](https://github.com/Layr-Labs/d-inference/issues/713), [#714](https://github.com/Layr-Labs/d-inference/issues/714), [#715](https://github.com/Layr-Labs/d-inference/issues/715), [#716](https://github.com/Layr-Labs/d-inference/issues/716) *(closed — false positive, code deleted)*, [#717](https://github.com/Layr-Labs/d-inference/issues/717), [#736](https://github.com/Layr-Labs/d-inference/issues/736)
**Pull Requests Submitted:** [#737](https://github.com/Layr-Labs/d-inference/pull/737) (install-time RAM check), [#738](https://github.com/Layr-Labs/d-inference/pull/738) (remove hardcoded MDM key), [#739](https://github.com/Layr-Labs/d-inference/pull/739) (fix TLS verification + MicroMDM cert SAN)
**Last Updated:** 2026-08-25

---

## What Is Darkbloom?

A decentralized AI inference network by **Eigen Labs** (the company behind EigenLayer, which raised $241M from a16z). Mac owners (Apple Silicon, 36GB+ RAM) install a provider agent that processes AI inference requests in the background and earn money per token processed. Developers pay for cheaper AI inference routed through the distributed Mac network.

---

## Scope

| Area | Covered |
|------|---------|
| MDM `.mobileconfig` profile | Yes |
| Cryptographic paper (`dginf-private-inference.pdf`) | Yes |
| Launchd plist / persistence | Yes |
| Source code (coordinator + provider) | Yes |
| Binary / release supply chain | Yes |
| Network endpoints | Yes |
| Trust tier enforcement | Yes |

---

## Findings Summary

### CRITICAL — None found

### HIGH (5 findings)

| ID | Finding | File | GitHub Issue |
|----|---------|------|-------------|
| H-1 | E2E encryption claimed in paper but disabled in production | `provider/src/proxy.rs` | [#706](https://github.com/Layr-Labs/d-inference/issues/706) |
| H-2 | Coordinator sees all prompts/responses in plaintext | `provider/src/proxy.rs` | [#706](https://github.com/Layr-Labs/d-inference/issues/706) |
| H-3 | MDM API key hardcoded in Dockerfile | `coordinator/Dockerfile` | [#707](https://github.com/Layr-Labs/d-inference/issues/707) |
| H-4 | ~~Wallet private key stored as plaintext file on disk~~ **FALSE POSITIVE** — Rust provider deleted in PR #178 | `provider/src/wallet.rs` (deleted) | [#708](https://github.com/Layr-Labs/d-inference/issues/708) *(closed)* |
| H-5 | MinTrustLevel is global, not per-consumer | `coordinator/internal/api/consumer.go` | [#705](https://github.com/Layr-Labs/d-inference/issues/705) |

### MEDIUM (8 findings)

| ID | Finding | File | GitHub Issue |
|----|---------|------|-------------|
| M-1 | Binary SHA-256 hashes served by same CDN as binaries (circular trust) | Install script | [#709](https://github.com/Layr-Labs/d-inference/issues/709) |
| M-2 | ~~Stripe webhook verification bypassed when secret is empty~~ **FALSE POSITIVE** — code already hard-fails | `coordinator/billing/stripe.go` | [#710](https://github.com/Layr-Labs/d-inference/issues/710) *(closed)* |
| M-3 | curl-pipe-bash install enables fleet-wide RCE if CDN is compromised | `deploy/provider-fleet/update-fleet.sh` | [#711](https://github.com/Layr-Labs/d-inference/issues/711) |
| M-4 | SCEP enrollment challenge is a public static string (`"micromdm"`) | `scripts/enroll-with-acme.mobileconfig` | [#705](https://github.com/Layr-Labs/d-inference/issues/705) |
| M-5 | Hypervisor memory isolation is novel and unaudited | `provider/src/hypervisor.rs` | [#705](https://github.com/Layr-Labs/d-inference/issues/705) |
| M-6 | Single domain dependency (`api.darkbloom.dev`) for all operations | Install script, `provider/src/config.rs` | [#705](https://github.com/Layr-Labs/d-inference/issues/705) |
| M-7 | Self-signed providers receive paid traffic with weaker guarantees | `coordinator/internal/registry/scheduler.go` | [#705](https://github.com/Layr-Labs/d-inference/issues/705) |
| M-8 | Silent failure -- underpowered machines enrolled but earn nothing | `scripts/install.sh`, `provider-swift/Sources/darkbloom/EnrollCommand.swift`, `StartCommand+Preflight.swift` | [#736](https://github.com/Layr-Labs/d-inference/issues/736) |

### LOW (6 findings)

| ID | Finding | File | GitHub Issue |
|----|---------|------|-------------|
| L-1 | ~~`.mobileconfig` in repo points to test server, not production~~ **ALREADY FIXED** — file deleted in PR #511, profile now generated dynamically | `scripts/enroll-with-acme.mobileconfig` (deleted) | [#712](https://github.com/Layr-Labs/d-inference/issues/712) *(closed)* |
| L-2 | SIP immutability theorem assumes no kernel CVEs | `papers/dginf-private-inference.tex` | [#713](https://github.com/Layr-Labs/d-inference/issues/713) |
| L-3 | Hardcoded Team ID in entitlements plist | `scripts/entitlements.plist` | [#714](https://github.com/Layr-Labs/d-inference/issues/714) |
| L-4 | TLS verification skipped for internal loopback connections — **PR #739 submitted** (pins MicroMDM self-signed cert + fixes missing SAN in cert generation) | `coordinator/Caddyfile`, `coordinator/deploy/start.sh` | [#715](https://github.com/Layr-Labs/d-inference/issues/715) |
| L-5 | ~~Wallet private key passed to external `shasum` — briefly visible in `ps`~~ **FALSE POSITIVE** — historical code used stdin not argv; Rust provider deleted in PR #178 | `provider/src/wallet.rs` (deleted) | [#716](https://github.com/Layr-Labs/d-inference/issues/716) *(closed)* |
| L-6 | No releases published on active repo; only on Layr-Labs fork | GitHub releases | [#717](https://github.com/Layr-Labs/d-inference/issues/717) |

### INFO — Positive Findings

| Finding | Detail |
|---------|--------|
| MDM AccessRights 1041 verified | Genuinely read-only. No erase, lock, or app install capability confirmed by bitfield analysis. |
| Unsupervised, user-removable MDM profile | Correct design choice. No `PayloadRemovalDisallowed` key present. |
| No launchd persistence | Provider runs as a user-space process, not a root daemon. No `RunAtLoad` / `KeepAlive`. |
| `secure_zero()` correct | Uses `write_volatile` + `SeqCst` fence — prevents dead store elimination. |
| Trust tier assignment is server-side | Providers cannot spoof their own trust level. Coordinator assigns it based on verified attestation. |
| Python import path isolation | `sys.path` locked to signed app bundle; `__pycache__` purged before hash computation. |

---

## Detailed Findings

### H-1 / H-2 — E2E Encryption Disabled; Coordinator Sees All Plaintext

**Severity:** HIGH
**File:** `provider/src/proxy.rs`

The technical paper claims per-request NaCl Box (X25519 + XSalsa20-Poly1305) end-to-end encryption with forward secrecy. The production code explicitly disables this:

```rust
// The provider receives plain JSON inference requests from the coordinator.
// No decryption is needed on the provider side -- the coordinator runs in a
// GCP Confidential VM and handles the trust boundary.
```

The `NodeKeyPair` parameter comment:
```rust
// Reserved for future coordinator-to-provider encryption
// but is not used in the current plain JSON flow.
```

**Impact:** The coordinator (GCP Confidential VM) reads every prompt and response in plaintext. Security relies entirely on the Confidential VM not being compromised. The paper's claim that "the operator of the Mac running your inference cannot read your prompt" is enforced by coordinator policy, not cryptography.

**Fix:** Activate the existing NaCl Box flow. The infrastructure is already in the codebase.

---

### H-3 — MDM API Key Hardcoded in Dockerfile

**Severity:** HIGH
**File:** `coordinator/Dockerfile`

```dockerfile
ENV EIGENINFERENCE_MDM_API_KEY=eigeninference-micromdm-api
```

The MicroMDM API key is baked into the public image as a default. The value is now permanently in git history and must be treated as compromised.

**Impact:** Anyone with the MDM server URL (documented in the public `.mobileconfig`) and this key can interact with the MicroMDM API directly, potentially querying enrolled device information across the provider fleet.

**Fix:** Remove default from Dockerfile. Store via GCP Secret Manager. Add startup check refusing to run with the default value in production. Rotate the key.

---

### H-4 — Wallet Private Key Stored as Plaintext

**Severity:** HIGH
**File:** `provider/src/wallet.rs`

The earnings wallet private key is stored at `~/.darkbloom/wallet_key` as a plaintext hex string (Unix permissions `0600`). During address derivation, the key is passed as an argument to an external `shasum` subprocess, making it briefly visible in `ps aux`.

Additionally, a timestamp-based randomness fallback produces cryptographically weak keys when `/dev/urandom` is unavailable.

**Fix:** Use macOS Keychain (entitlement already present: `SLDQ2GJ6TL.io.darkbloom.provider`) or Secure Enclave wrapping. Replace external `shasum` with the `sha2` Rust crate. Hard-fail if `/dev/urandom` is unavailable.

---

### H-5 — MinTrustLevel Is Global, Not Per-Consumer

**Severity:** HIGH
**File:** `coordinator/internal/api/consumer.go`

The minimum provider trust level is a registry-wide setting. API consumers cannot specify a minimum trust tier per request. If Eigen Labs lowers the global floor, all consumers — including those with sensitive workloads — are affected without notice.

**Fix:** Allow consumers to pass a `X-Min-Trust-Level` header in API requests to demand hardware-attested providers specifically.

---

### M-1 — Circular Binary Trust

**Severity:** MEDIUM
**File:** Install script

The SHA-256 hashes used to verify downloaded binaries are fetched from the same server (`api.darkbloom.dev`) as the binaries themselves. A compromised CDN serves a malicious binary alongside a matching hash, passing verification.

**Fix:** Publish hashes to GitHub Releases (independent trust root) or use Sigstore cosign.

---

### M-2 — Stripe Webhook Bypass

**Severity:** MEDIUM
**File:** `coordinator/internal/billing/stripe.go`

```go
if s.webhookSecret == "" {
    // Skip verification, process event directly
    return s.processEvent(payload)
}
```

If `STRIPE_WEBHOOK_SECRET` is unset in production, an attacker can POST forged Stripe events (e.g., `checkout.session.completed`) to credit arbitrary balances.

**Fix:** Hard-fail at startup if `webhookSecret` is empty in non-dev environments. Return an error instead of silently processing unverified events.

---

### M-3 — curl-pipe-bash Fleet RCE

**Severity:** MEDIUM
**Files:** README, `deploy/provider-fleet/update-fleet.sh`

```bash
# Provider install
curl -fsSL https://api.darkbloom.dev/install.sh | bash

# Fleet update (SSH into every provider machine)
ssh "$HOST" "curl -fsSL $COORD_URL/install.sh | bash"
```

A single compromise of `api.darkbloom.dev` gives an attacker simultaneous arbitrary code execution across the entire provider fleet.

**Fix:** Sign install scripts with GPG/cosign and verify before execution. Distribute via Homebrew or signed `.pkg`. Replace fleet SSH pattern with pre-verified binary deployment.

---

### M-8 -- Silent Failure: Underpowered Machines Enrolled but Earn Nothing

**Severity:** MEDIUM
**Files:** `scripts/install.sh`, `provider-swift/Sources/darkbloom/EnrollCommand.swift`, `provider-swift/Sources/darkbloom/StartCommand+Preflight.swift`

The install script, `darkbloom enroll`, and `darkbloom start` all allow machines with insufficient RAM to complete the full installation and enrollment flow -- including MDM device-attestation enrollment -- without any warning that the machine cannot actually serve inference. The machine appears "online" on the network but silently fails every inference request because the assigned model cannot fit in memory.

The only way a user discovers this is by manually running `darkbloom doctor`:

```
TRAFFIC READINESS   (can this box actually serve?)
  [FAIL] model fits in RAM -- qwen3.6-35b-a3b-vl-mtp-mxfp8 needs ~30.3 GB but only 12.5 GB is usable -- it will show online but every request fails to load.
         -> fix: this box's RAM is too small for the models on this network; consider a machine with more unified memory.
```

**Code evidence:**

1. `install.sh` line 361 reads RAM with `sysctl -n hw.memsize` but only displays it -- no comparison against model requirements, despite the model catalog response containing `min_ram_gb` per model.

2. `StartCommand+Preflight.swift` has a hard-fail gate at 8 GB:
```swift
if hardware.memoryGb < 8 {
    printError("This Mac has \(hardware.memoryGb) GB RAM. At least 8 GB is needed to serve any model.")
    throw ExitCode.failure
}
```
This threshold is far below the actual minimum model requirement (~30.3 GB for the current default model). A 16 GB Mac passes this check but cannot serve any model on the network.

3. `EnrollCommand.swift` performs zero hardware checks before enrolling the device in MDM, meaning underpowered machines go through the full device-attestation flow (Secure Enclave key generation, MDM profile installation, Apple CA certificate issuance) for nothing.

4. The RAM-vs-model fitness check exists only in `darkbloom doctor` (via `DoctorRunner.buildOperatorDiagnosis` and `ModelFitDiagnostic`), which users must run manually and is not part of any install/start/enroll flow.

**Impact:**
- Users with 16-24 GB Macs are misled into thinking they are participating in the network and earning money, when every inference request silently fails to load
- Users undergo MDM enrollment (device attestation, Secure Enclave identity, Apple CA certificates) on machines that are structurally ineligible to serve traffic
- The network registers these machines as "online" providers, potentially affecting routing and reliability metrics
- Users have no indication they are earning nothing unless they independently discover and run `darkbloom doctor`

**Fix:** Add a RAM check at install time (in `install.sh`) and at `darkbloom start` / `darkbloom enroll` time (in Swift) that:
- Hard-fails at <24 GB with an error explaining the machine cannot serve any current model
- Warns at <36 GB that the machine may not have enough headroom for the default model (~30.3 GB weights + OS overhead)
- Compares actual usable RAM against the `min_ram_gb` from the model catalog before allowing enrollment

The model catalog already returns `min_ram_gb` per model. The install script already reads `hw.memsize`. The only missing piece is the comparison.

---

## MDM Profile Analysis

**File:** `scripts/enroll-with-acme.mobileconfig`

### AccessRights = 1041 (Verified Safe)

| Bit | Capability | Granted? |
|-----|-----------|---------|
| 0 (1) | Inspect installed configuration profiles | Yes |
| 4 (16) | Query device information (serial, capacity) | Yes |
| 10 (1024) | Security-related queries | Yes |
| 1 (2) | Install/remove profiles | **No** |
| 3 (8) | Device lock/wipe | **No** |
| 6 (64) | Manage apps | **No** |
| 7 (128) | Clear passcode | **No** |

Claims verified: No erase, no lock, no app install/remove.

### Profile Properties
- **Type:** Unsupervised (no supervision payload)
- **Removable:** Yes (no `PayloadRemovalDisallowed` key)
- **Payloads:** SCEP + MDM + ACME
- **MDM Server (test):** `https://inference-test.openinnovation.dev`

---

## Cryptographic Paper Review

**File:** `papers/dginf-private-inference.tex`

### Architecture (as documented)
1. **Layer 1 — Secure Enclave:** P-256 ECDSA signing for hardware-bound identity
2. **Layer 2 — MDM SecurityInfo:** OS-level device posture verification
3. **Layer 3 — ACME device-attest-01:** Apple Enterprise CA issues X.509 certs encoding device serial, UDID, OS version
4. **Layer 4 — Challenge-Response:** 32-byte nonce every 5 minutes, 30-second response window, three-strikes untrust

### SE Key Binding Protocol (sound)
1. Provider generates P-256 key `k` in SE, sends `pk_k` to coordinator
2. Coordinator computes `n = base64(SHA-256(pk_k))`
3. Sends `n` as `DeviceAttestationNonce` in MDM command
4. Apple returns MDA cert with `FreshnessCode = SHA-256(n)`
5. Coordinator verifies chain, confirming SE key binding

### Gaps
- **E2E encryption not implemented** (see H-1/H-2)
- **Theorem 1 (SIP Immutability)** assumes no unpatched kernel CVEs
- **Hypervisor memory isolation** (ARM Stage 2 page tables defeating Thunderbolt 5 RDMA) is novel and unaudited

### Implementation Details (from paper)
- Provider binary: **Rust**, 4,500 LOC, embeds Python MLX via PyO3
- Coordinator: **Go**, 4,000 LOC, runs in Intel TDX hardware TEE
- Memory sanitization: `write_volatile()` + sequential-consistency fence post-inference
- Python path isolation: `sys.path` locked; `__pycache__` purged before hash

---

## Network Endpoint Inventory

| Endpoint | Purpose |
|----------|---------|
| `https://api.darkbloom.dev` | Production coordinator (all API traffic) |
| `wss://api.darkbloom.dev/ws/provider` | Provider WebSocket (inference, heartbeats) |
| `https://api.darkbloom.dev/v1/enroll` | MDM enrollment |
| `https://api.darkbloom.dev/dl/*` | Binary and runtime downloads |
| `https://api.darkbloom.dev/install.sh` | Bootstrap installer |
| `https://pub-7cbee059c80c46ec9c071dbee2726f8a.r2.dev` | R2 CDN (runtime bundles) |
| `https://pub-3d1cb668259340eeb2276e1d375c846d.r2.dev` | R2 CDN (Python site-packages) |
| `https://github.com/astral-sh/python-build-standalone` | Portable Python 3.12 fallback |
| `https://github.com/Gajesh2007/vllm-mlx` | Inference runtime source |
| `https://inference-test.openinnovation.dev` | Test MDM server |
| Stripe API | Payment processing |
| Solana JSON-RPC (configurable) | USDC deposit/withdrawal verification |

---

## Trust Tier Enforcement

**Files:** `coordinator/internal/registry/registry.go`, `scheduler.go`

| Tier | Rank | Multiplier | Requirements |
|------|------|-----------|-------------|
| `TrustNone` | 0 | 0.5 | No attestation |
| `TrustSelfSigned` | 1 | 0.8 | SE P-256 ECDSA signature |
| `TrustHardware` | 2 | 1.0 | All 4 layers + MDA nonce binding + periodic re-verification |

Trust assignment is **server-side** — providers cannot self-report trust level. However, `TrustSelfSigned` providers do receive paid traffic (disfavored but not excluded), and `MinTrustLevel` is a global registry setting, not per-consumer.

---

## False Positives & Corrections

When PR agents verified findings against the actual codebase, 4 of the original findings were found to be inaccurate. This is documented transparently here.

| ID | Original Claim | What Code Review Found | Action Taken |
|----|---------------|----------------------|-------------|
| H-4 / #708 | Wallet private key stored as plaintext in `provider/src/wallet.rs` | The entire Rust `provider/` crate was deleted in PR #178 (2026-05-19) when wallet-based payouts were replaced by Stripe Connect. The file never existed in the current codebase. | Closed #708 |
| M-2 / #710 | Stripe webhook bypasses verification when secret is empty | `coordinator/billing/stripe.go:168` already hard-fails with `errors.New("stripe: webhook secret not configured — refusing to verify")`. Guard present since the file was first committed. Regression test at `billing_test.go:348` covers this. | Closed #710 |
| L-1 / #712 | `.mobileconfig` points to test MDM server | File deleted in PR #511 (2026-07-04). Profile is now generated dynamically from configured base URL. Regression test at `enroll_test.go:43` asserts old domain never appears. | Closed #712 |
| L-5 / #716 | Wallet key passed as CLI arg to `shasum` — visible in `ps` | (a) The Rust provider was deleted (see H-4). (b) The historical implementation piped key material via **stdin**, not as an argv — it was never visible in `ps aux`. | Closed #716 |

**Root cause:** The initial technical review was conducted by an AI agent (Claude Opus) reading code via web fetch, which generated some plausible-looking but fabricated code snippets. This is a known limitation of AI-assisted security audits without direct code execution or line-level diffing — findings require human or tool verification against the actual repository before filing.

**Lesson:** Treat AI-generated security findings as leads to investigate, not confirmed vulnerabilities.

---

## Pull Requests Submitted

| PR | Issue | Fix | Status |
|----|-------|-----|--------|
| [#737](https://github.com/Layr-Labs/d-inference/pull/737) | #736 | Install-time RAM preflight check — hard-fail <24GB, warn <36GB | Open |
| [#738](https://github.com/Layr-Labs/d-inference/pull/738) | #707 | Remove hardcoded MDM API key default from Dockerfile | Open |
| [#739](https://github.com/Layr-Labs/d-inference/pull/739) | #715 | Fix `tls_insecure_skip_verify` in Caddyfile; fix missing SAN in MicroMDM cert generation | Open |

---

## Key Recommendations (Priority Order)

1. **Activate E2E encryption** — NaCl Box infrastructure exists in the codebase. Highest-impact fix available.
2. **Remove hardcoded MDM API key fully** — PR #738 covers Dockerfile; also fix `coordinator/mdm/config.go:9` (Go fallback) and `coordinator/deploy/start.sh:52` (shell fallback). Rotate key immediately.
3. **Break circular binary trust** — Publish hashes to GitHub Releases or use Sigstore cosign.
4. **Sign install scripts** — Replace curl-pipe-bash with verified installation.
5. **Add per-consumer MinTrustLevel** — Allow consumers to demand hardware-attested providers.
6. **Fix `InsecureSkipVerify` in `coordinator/mdm/mdm.go`** — PR #739 is the prerequisite (SAN fix); this is the follow-up.
7. **Commission independent security audit** — See issue [#705](https://github.com/Layr-Labs/d-inference/issues/705).

---

## Overall Assessment

| Question | Answer |
|----------|--------|
| Is it a scam? | No — legitimate company, real funding ($241M), partial open source |
| Is it safe on a primary Mac? | No — MDM enrollment, plaintext coordinator, unaudited binary, no audit |
| Is it safe on a dedicated secondary Mac? | Marginally, with caution |
| Are earnings worth it? | ~$18/month actual vs $120-200/month marketed |
| Should you wait? | Yes — pending E2E encryption activation and independent audit |

---

*Report generated from open-source code review only. No software was installed. All findings are based on publicly available artifacts in the Darkbloom GitHub repositories.*

