Darkbloom (AI inference on idle Macs) – security audit with PRs submitted An independent security audit of Darkbloom, a decentralized AI inference network by Eigen Labs, identified five high-severity and eight medium-severity findings, including disabled end-to-end encryption in production and a hardcoded MDM API key. The auditor submitted 15 GitHub issues and three pull requests to address the vulnerabilities, with several issues closed as false positives or already fixed. 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.