{
  "slug": "dense-on-bandwidth",
  "lab_number": 22,
  "title": "Memory bandwidth sets how fast a 31B dense model works, not how well.",
  "type": "editorial",
  "status": "published",
  "sponsor": null,
  "author": "Keith Townsend",
  "date": "October 19, 2026",
  "date_iso": "2026-10-19",
  "layers": [
    "layer0",
    "layer2c"
  ],
  "axes": [
    {
      "instrument": "4plus1",
      "layers": [
        "layer0",
        "layer2c"
      ],
      "has_dapm_table": true
    }
  ],
  "supersedes": [],
  "superseded_by": null,
  "corrects": [
    {
      "lab": "studio-or-spark",
      "what": "Lab 020 reports that the dense 31B model’s first pool attempt on the same Studio stopped in a tool-call loop and was still being diagnosed. The loop was oMLX’s handling of this model; on llama.cpp the same model completed the full pool, 15 of 22. Lab 020 also cites this model’s two-session decode on oMLX, 44.9 to 43.1 tok/s per stream, as a sign the concurrency loss may not apply to every model. On llama.cpp, the runtime that served the pool, it fell from 42.8 to 29.3 tok/s per stream at two sessions, so the two-session behavior depends on the runtime."
    }
  ],
  "corrected_by": [],
  "vendors": [
    {
      "key": "apple",
      "role": "hw"
    },
    {
      "key": "nvidia",
      "role": "hw"
    },
    {
      "key": "google",
      "role": "model"
    },
    {
      "key": "anthropic",
      "role": "harness"
    }
  ],
  "themes": [
    "serving-economics",
    "operating-cost-of-ownership"
  ],
  "finding": "The same dense Gemma 4 31B solved about the same share of agentic repair tasks on one DGX Spark and on one M5 Ultra Mac Studio, 5 and 6 of the same 11, but took a median 61 minutes per task on the Spark against 12.8 on the Studio, whose memory bandwidth is 4.4 times higher. The parity depends on a two-hour task ceiling: at 60 minutes the Spark would have kept 3 of its 5 solves.",
  "video": null,
  "lab_detail": "https://labs.layer2c.com/downloads/dense-on-bandwidth-lab-detail.md",
  "question": "Can a DGX Spark run a 31B model well enough for an AI coding agent? It holds the model easily, but every word the model writes means reading all 31 billion of its weights from memory, and the Spark’s memory is slow. This lab ran the same model through the same coding agent on a Spark and on a Mac Studio, whose memory is more than four times faster. The Studio was about five times faster per task, and both solved about the same tasks. On the Spark that is too slow to be practical, and with a time limit per task it stops finishing them.",
  "load": "The certified 22-task Python repair pool from Labs 011 to 014, driven by a headless Claude Code harness at two concurrent sessions with a two-hour ceiling per task, against Gemma 4 31B. Compared on the 11 tasks a single DGX Spark ran, plus the full pool on the Mac Studio. Plus decode, prefill and long-context gates on the Studio.",
  "verdict": {
    "scope": "One dense model (Gemma 4 31B, 4-bit), one Mac Studio (M5 Ultra, 80-core GPU, 256 GB) and one DGX Spark, both at two concurrent sessions with a two-hour ceiling per task. The Studio ran llama.cpp with a Q4_K_M file; the Spark ran vLLM with NVIDIA’s NVFP4 file, measured in August. Runtime, file format, proxy route and date differ along with the hardware, so the speed ratio is not bandwidth alone. The matched comparison is the 11 tasks the single Spark ran, the half of the pool that Lab 013’s rented L40S venue had failed. A one-task solve difference is noise on this pool. Bandwidth figures are the vendors’ published numbers, not measured here.",
    "independence": "Self-funded editorial. The Mac Studio was bought by the practice, and the DGX Spark was bought at retail for an earlier lab. Disclosure: Google Cloud is a client of this practice, and the model under test, Gemma 4 31B, is a Google model. Google did not commission, fund, preview or have any say in this lab. Apple and NVIDIA hardware are both under test, and Anthropic’s Claude Code is the agent harness on every arm. Neither Apple, NVIDIA nor Anthropic is a client. No vendor paid for this answer.",
    "calls": [
      {
        "kind": "do",
        "text": "Size hardware for a model with many active parameters by memory bandwidth, not memory capacity. What counts is the bytes read per token: a 31B dense model reads all 31 billion weights, a 26B mixture-of-experts model with 4B active reads about 4 billion. The Spark held the 31B dense model easily and decoded it at 7.0 tokens per second; the Studio decoded it at 42.8. Both solved about the same tasks, and the Studio’s median task took about one fifth of the Spark’s time, 12.8 minutes against 61.0."
      },
      {
        "kind": "dont",
        "text": "Do not plan agentic work around a 31B dense model on a DGX Spark. It is too slow to be practical, as Lab 013 found. On the 11 tasks a single Spark ran, its five solves took 43 to 66 minutes each. With a 60-minute limit per task it would have kept three, with a 40-minute limit none. The Studio’s six solves all finished within 13 minutes."
      },
      {
        "kind": "dont",
        "text": "Do not trust a serving stack until it has run one real agentic task. On the Studio, oMLX passed every short test with this model and then called the same file read more than 600 times per session in the harness. llama.cpp on the same box ran clean. Read the session log, not just the speed and the error log."
      }
    ]
  },
  "objection": {
    "q": "Isn’t this NVIDIA’s software, not memory bandwidth?",
    "a": [
      "Partly, and the arithmetic says by how much. The published bandwidth ratio is 4.4x: 1.2 TB/s on the M5 Ultra against 273 GB/s on the GB10. The measured decode ratio is about 6x: 42.8 to 46.2 tok/s on the Studio against 7.0 on the Spark. The rest is the Spark’s 4-bit kernel.",
      "NVIDIA’s 4-bit file reads about 15.5 GB per token, so 273 GB/s caps the Spark at about 17.6 tok/s. Lab 013 measured 7.0, about 40 percent of that cap, and measured an 8-bit file at the same 7 tok/s even though it reads twice the bytes. The GB10 has no native instruction for converting 4-bit floating point, which is where the 4-bit path loses its speed.",
      "Give the Spark a perfect 4-bit kernel and it would run at 17.6 tok/s, still 2.4x under the Studio’s measured 42.8. That floor is conservative: it grants the Spark 100 percent of its bandwidth and the Studio only what it actually achieved, about two thirds. Better Spark software would narrow the gap and cannot close it. This lab did not run the same runtime on both boxes. Peer testing of the same pairing reports consistent results, which is why that test was skipped."
    ]
  },
  "numbers": [
    {
      "label": "Median minutes per task, same 11 tasks",
      "value": "61.0 against 12.8",
      "note": "One DGX Spark (vLLM, NVFP4, August 2026) against the Mac Studio (llama.cpp, Q4_K_M), two sessions each, same harness, two-hour ceiling. The Spark’s one timeout counts at 120 minutes; excluding it, the Spark’s median is 59.1. The 11 are the tasks the single Spark ran, the harder half of the pool."
    },
    {
      "label": "Solved, same 11 tasks",
      "value": "5 against 6",
      "note": "Spark against Studio. Three tasks flipped between them: one toward the Spark, two toward the Studio."
    },
    {
      "label": "Minutes per solved task",
      "value": "43.4 to 66.2 against 2.7 to 12.9",
      "note": "Spark against Studio. Under a 60-minute ceiling the Spark keeps 3 of 5 solves; under 40 minutes, none."
    },
    {
      "label": "Summed task time, same 11 tasks",
      "value": "10.86 against 3.79 hours",
      "note": "Spark against Studio."
    },
    {
      "label": "Seconds per turn, same 11 tasks",
      "value": "310 against 78",
      "note": "Spark against Studio. The Studio sessions took more turns, 176 against 126."
    },
    {
      "label": "Single-stream decode, 256-token probe",
      "value": "7.0 against 42.8 tok/s",
      "note": "Spark (vLLM, NVFP4) against Studio (llama.cpp, Q4_K_M). oMLX on the Studio: 46.2. 8-bit on the Spark: 6.8 to 6.9."
    },
    {
      "label": "Memory bandwidth, published",
      "value": "273 GB/s against 1.2 TB/s",
      "note": "GB10 against M5 Ultra, 4.4x. Vendor figures, not measured here."
    },
    {
      "label": "Spark 4-bit decode, share of its bandwidth limit",
      "value": "about 40%",
      "note": "Computed here: Lab 013’s measured 7.0 tok/s against 17.6 tok/s, which is 273 GB/s over about 15.5 GB read per token."
    },
    {
      "label": "Full pool, Mac Studio",
      "value": "15 of 22",
      "note": "Run sf_gemma31b_studio_r2, 2026-10-18. 2 h 30 min elapsed, no timeouts, no session errors, median 7.4 min per task."
    },
    {
      "label": "Two sessions on the Studio, llama.cpp",
      "value": "29.3 tok/s per stream",
      "note": "58.0 aggregate, short prompts. oMLX ran 43.1 per stream, 85.0 aggregate."
    },
    {
      "label": "Studio decode at harness-sized context",
      "value": "25.8 and 21.9 tok/s",
      "note": "Measured on oMLX only, after 35.8K and 48.1K cold prompt tokens. Not measured on llama.cpp. Prefill on llama.cpp: 909 tok/s at 2.4K, 728 at 38.4K."
    },
    {
      "label": "oMLX run, file reads per session",
      "value": "603 and 628",
      "note": "Run 1, first two sessions, stopped at 71 minutes with 0 of 22 done. On llama.cpp the diagnostic run of one of those tasks took 15 turns."
    },
    {
      "label": "Metered spend",
      "value": "$0",
      "note": "Owned hardware throughout."
    }
  ],
  "opened": [
    {
      "q": "How much bandwidth does a model with 31 billion active parameters need for agentic work?",
      "a": "Two points are measured: 273 GB/s is too slow, about an hour per task, and 1.2 TB/s is fast, about 13 minutes. The useful threshold sits between, it moves with the time budget an agent gets per task, and it falls as active size falls, though not in proportion for models with few active parameters. An M5 Max, which this practice owns, and a discrete GPU would place it. Lab 013’s rented L40S scored 11 of 22 with this model in the same harness, but its serving configuration, timing and decode rate were not recorded, so it is not a point on the curve."
    },
    {
      "q": "What in oMLX breaks this model under an agent?",
      "a": "Both oMLX model files passed short tests and failed in the harness, and both showed a token-level defect in short answers. Replaying one captured request from a looping session against oMLX and against stock mlx-lm would locate it. Until then, run one real harness task on any new runtime before trusting it."
    }
  ],
  "not_proved": [
    "The speed ratio is not bandwidth alone. The Spark arm ran vLLM with NVIDIA’s NVFP4 file in August; the Studio ran llama.cpp with a Q4_K_M file in October. The same runtime was not run on both boxes. The 2.4x hardware floor uses the Spark’s computed 4-bit limit of 17.6 tok/s, not a measurement.",
    "The matched comparison is 11 tasks, the half of the pool that Lab 013’s L40S venue had failed, run once on each box. It is harder than the full pool. Three of the 11 flipped between the two boxes, and one task flipped between two llama.cpp runs on the same Studio. A one-task solve difference is noise on this pool; the 5 against 6 is no difference found, not proof of equal capability.",
    "Timeouts are counted as misses here, because ceiling truncation is part of what this lab measures. Labs 012 and 013 adopted a later convention that scores a ceiling-interrupted session as no verdict; under it the single Spark reads 5 of 10 against the Studio’s 6 of 11.",
    "The solve parity is conditional on the two-hour ceiling. Under a shorter ceiling the Spark loses solves the Studio keeps, as computed from the recorded task times. That is a recomputation, not a run at a shorter ceiling.",
    "The Spark’s exact serve command for that arm was not preserved beyond its memory setting, context length and model file. The pod definition used for this model on the Spark sets no reasoning options, so the model’s thinking mode was off by default, matching the Studio. That is an inference from the configuration, not a record of the run.",
    "The two arms reached their servers by different proxy routes: the Spark’s through chat completions, the Studio’s through the Responses API. Lab 020 found reasoning dropped on one route in another arm, and fewer turns on the Responses route is an open apparatus question across the Studio arms. Thinking was off on both here, and the Studio took more turns than the Spark, so the route is not expected to flatter the Studio. It was not tested.",
    "The Studio did not run the agent harness itself. The harness ran on a DGX Spark and reached the Studio over a relay, because bench code is not run on a machine on the personal network. A buyer needs a separate host or VM for agent code.",
    "Decode at the 35K to 48K token contexts the harness carries was measured on oMLX only, at 25.8 and 21.9 tok/s, about half the short-prompt rate. It was not measured on llama.cpp, the runtime that ran the pool. At two sessions llama.cpp dropped to 29.3 tok/s per stream on short prompts.",
    "Memory bandwidth figures are the vendors’ published numbers. Neither box’s bandwidth was measured.",
    "The oMLX failure was not isolated. It reproduced with the prefix cache off and changed shape with a different model file. Whether it is the server, the chat template handling, or the MLX kernels for this architecture is open. The llama.cpp loader also warned that three control-looking tokens in the GGUF file were not marked as control tokens and overrode them.",
    "One model, with 31 billion active parameters. The result is about bytes read per token, not about dense models as a class. A small dense model or a mixture-of-experts model with few active parameters reads far fewer bytes per token and runs much faster on the Spark: Lab 016 measured a 12B dense model at 22.0 tok/s and a 26B mixture-of-experts model with about 4B active at 30.0. Below the 31B, speed does not scale in proportion to bytes read. Where the active size crosses from fast enough to too slow on each box was not measured."
  ],
  "executive_summary": [
    "Gemma 4 31B is a dense model: every one of its 31 billion weights is read for every token it generates. Decode speed follows the bytes read per token, so what makes this model slow on low-bandwidth hardware is its 31 billion active parameters, not that it is dense. A small dense model, or a mixture-of-experts model with a few billion active parameters, reads far fewer bytes and runs far faster. Lab 016 measured a 12B dense model at 4 bits at 22.0 tok/s on the same Spark and Gemma 4 26B-A4B, with about 4B active in NVIDIA’s 4-bit format, at 30.0, against the 31B’s 7.0. Bandwidth over bytes read is a ceiling: a large dense model tracks it closely, and a model with few active parameters runs well below it, because routing and kernel overhead take a larger share. At 4 bits the 31B weights are about 15.5 to 18 GB depending on the file, which fits easily on a DGX Spark (128 GB) and on a Mac Studio with an M5 Ultra (256 GB). Capacity is not the constraint on either box. Memory bandwidth is the difference: 273 GB/s on the Spark’s GB10, 1.2 TB/s listed for the M5 Ultra.",
    "Single-stream decode measured 7.0 tok/s on the Spark (vLLM, NVIDIA’s NVFP4 file) and 42.8 tok/s on the Studio (llama.cpp, a Q4_K_M file), on the same 256-token probe. A second Studio runtime, oMLX, measured 46.2. Part of the 6x gap is the Spark’s 4-bit kernel, which reaches about 40 percent of its bandwidth limit. At least 2.4x of it is hardware.",
    "Lab 013 published that the 31B can’t practically run this harness on a Spark. Its rerun of 11 pool tasks on a single Spark in August, kept in the working notes until now, shows why. It ran two sessions at a time, through the same Claude Code harness with the same two-hour ceiling. It solved 5, hit the ceiling once, and took a median 61 minutes per task, counting the timeout at 120. On those same 11 tasks the Studio solved 6, never hit the ceiling, and took a median 12.8 minutes. Summed task time was 10.86 hours against 3.79. Three tasks flipped between the two boxes, one toward the Spark and two toward the Studio, which is this pool’s known variance at its borderline.",
    "The faster box did not take shortcuts. On the matched tasks the Studio sessions took more turns than the Spark sessions, 176 against 126, at 78 seconds per turn against 310.",
    "The solve parity depends on the ceiling. The Spark’s five solves took 43.4 to 66.2 minutes each. Under a 60-minute ceiling it would have kept three; under 40 minutes, none. The Studio’s six solves took 2.7 to 12.9 minutes, so any ceiling above 13 minutes leaves its count unchanged.",
    "On the full pool the Studio solved 15 of 22 in 2 hours 30 minutes elapsed, with no timeouts and no session errors, at a median 7.4 minutes per task. The single Spark never ran the full pool.",
    "The Studio’s first serving stack failed in a way short tests did not show. Under oMLX the model passed a tool-call round trip, decode, and recall of a planted fact at 38,000 tokens. In the harness it then called the same file read more than 600 times per session. A second model file on oMLX did not loop but wrote 32,000 tokens of nonsense instead. Both oMLX files also put hyphens in place of spaces in short answers. llama.cpp on the same Studio, with the same settings, ran clean."
  ],
  "dapm": [
    {
      "instrument": "4plus1",
      "reading": "portability",
      "layer": "layer0",
      "placement": "Retained",
      "cells": {
        "studio": "Retained",
        "spark": "Retained"
      },
      "note": "Retained on both. Each box ran its own 4-bit conversion of the same published open weights, NVIDIA’s NVFP4 file on the Spark and a GGUF file on the Studio, and either conversion can be redone from those weights, so nothing at this layer tied the workload to one vendor’s silicon. Commodity NVIDIA hardware is Retained by the instrument’s rule. On the Studio both runtimes tried are open source. Bandwidth decides how fast the model runs at this layer, not who controls it."
    },
    {
      "instrument": "4plus1",
      "reading": "portability",
      "layer": "layer2c",
      "placement": "Retained",
      "cells": {
        "studio": "Retained",
        "spark": "Retained"
      },
      "note": "Retained on both, because open weights ran on self-operated serving with the configuration written down. The Studio result shows what that ownership costs: the first runtime chosen produced a broken agent and the second did not, and only the operator was in a position to catch it."
    }
  ],
  "seam_map": null,
  "writeup": [
    "A dense model’s speed should be easy to predict. To write one token it reads every weight once, so tokens per second is roughly memory bandwidth divided by the bytes read per token. Gemma 4 31B at 4 bits reads about 15.5 GB per token from NVIDIA’s NVFP4 file and about 18 GB from the Q4_K_M file the Studio ran. On a DGX Spark, whose GB10 moves 273 GB/s, that predicts at most 17.6 tok/s. On a Mac Studio with an M5 Ultra, listed at 1.2 TB/s, it predicts about 66. Both boxes hold the model with room to spare, so the property that differs is bandwidth. That arithmetic is a ceiling, not a prediction for every model. A large dense model runs close to it. A model with few active parameters runs well below it: Lab 016 measured a 12B dense model at 4 bits at 22.0 tok/s on the same Spark and Gemma 4 26B-A4B, with about 4B active in NVIDIA’s 4-bit format, at 30.0, against the 31B’s 7.0, where the bytes alone would predict about three times the 12B’s speed rather than 1.4.",
    "Measured decode was 7.0 tok/s on the Spark and 42.8 to 46.2 on the Studio. Lab 013 measured the Spark at 7 tok/s with both a 4-bit and an 8-bit file, even though the 8-bit file reads twice the bytes, so the 4-bit path runs at about 40 percent of its limit. The GB10 has no native 4-bit floating-point conversion. The Studio ran at about two thirds of its listed bandwidth by the same arithmetic. The two boxes also ran different runtimes and different 4-bit formats, so the 6x decode gap is hardware plus software. At least 2.4x of it is hardware, because that is the gap even with a perfect Spark kernel.",
    "The harness measures what decode speed does to real work. Lab 013 ruled the 31B harness arm out on the Spark as too slow to be practical. Its working notes hold one partial arm that shows the cost: a single Spark ran 11 of the 22 pool tasks in August, the 11 that Lab 013’s rented L40S venue had failed, at two sessions and a two-hour ceiling. The Studio ran the full 22 on October 18 at the same concurrency and ceiling. On the shared 11, the Spark solved 5 and the Studio 6. The Spark’s median task took 61 minutes and the Studio’s took 12.8. Summed over the 11 tasks, the Spark spent 10.86 hours and the Studio 3.79.",
    "The solves stayed level while the time fell by about five times at the median. Bandwidth bought speed, not capability: the same model with the same harness reached about the same answers. Three tasks flipped between the boxes, one toward the Spark and two toward the Studio, and this pool is known to flip borderline tasks between identical runs. The Studio also took more turns than the Spark on the matched set, 176 against 126, so its speed did not come from shorter sessions.",
    "That parity holds only because the ceiling was generous. Every one of the Spark’s five solves took between 43.4 and 66.2 minutes. Had the ceiling been 60 minutes, the Spark would have kept three of them; at 40 minutes, none. The Studio’s six solves all finished within 12.9 minutes. An agent with a time budget per task turns the speed gap into a capability gap: the slow box runs out of time before it reaches the answer it would otherwise reach.",
    "On the full pool the Studio finished in 2 hours 30 minutes elapsed: 15 of 22 solved, no timeouts, no session errors, a median 7.4 minutes per task. The single Spark never ran the full pool. At its matched-set pace, a full pool at two sessions would take longer than a working day. An August arm on Lab 014’s two clustered Sparks, not published until now, ran the full pool with this model and solved 4 of 22. Lab 017 published the same pair’s speed with this model, about 10 tok/s. That arm ran four sessions on a different image and spent many of its turns repeating tool calls, so it is not a comparison of bandwidth and is left out of this ruling.",
    "Getting the Studio result took a second runtime. The Studio first served the model through oMLX, the community server Lab 020 used. The model passed every short check: a tool call and its result, decode speed, and recall of a planted fact at 38,000 tokens. In the harness, the first two sessions called the same file read 603 and 628 times, each answered by the harness with a note that the file had not changed. Turning off the server’s prefix cache did not stop it. A second model file, in NVIDIA’s 4-bit format for MLX, stopped looping and wrote 32,000 tokens of repeated nonsense instead. Both files also put hyphens in place of spaces in short answers, at every prompt length. llama.cpp, with a standard 4-bit file and the same sampling settings, worked the same task in 15 turns of reading, editing and rerunning tests. That diagnostic run did not solve it; the full-pool run hours later did, another borderline flip. The fault is somewhere in oMLX’s handling of this model. Which part was not isolated.",
    "That failure is the practical lesson for anyone serving open models on their own hardware. Every gate the lab normally runs before a pool passed. The defect showed only when the model ran a real agentic task, and only in the session log, not in any error. A team that stopped at the first runtime would have concluded that the model or the hardware was broken."
  ],
  "abstraction": [],
  "method": [
    "Self-funded. The Mac Studio was bought by the practice. The DGX Spark was bought at retail. No vendor was briefed, reviewed this, or was given advance sight. Google Cloud is a client of this practice, and the model under test is a Google model; Google had no part in this lab.",
    "The repair pool, its certification and the deterministic gate are the instrument used in Labs 011 to 014, unchanged, run through the same v1 Claude Code harness and litellm proxy at two concurrent sessions with a 7,200-second ceiling per task. The launcher and serving configuration are recorded with each result.",
    "Studio, run 2: llama.cpp b11399 (commit 2ca15f5), built from source with Metal, unsloth/gemma-4-31B-it-GGUF Q4_K_M (revision c1ac76e, SHA-256 verified), two slots of 131,072 tokens, flash attention on, reasoning off, temperature 1.0, top_p 0.95, top_k 64. The harness ran on a DGX Spark and reached the Studio through an SSH relay, because bench code is not run on a machine on the author’s personal network.",
    "Studio, run 1 and diagnostics: oMLX v0.7.1rc1 (commit 7a5a3d7) on MLX 0.32.4, with mlx-community/gemma-4-31b-it-4bit (revision 696d436c) and mlx-community/gemma-4-31b-it-nvfp4 (revision a0a935b). Run 1 was stopped at 71 minutes and is void. The diagnostics ran one task each.",
    "Spark comparator: arm sf_gemma31b_nvfp4_gb10, 2026-08-03 and 04, NVIDIA’s vLLM image, nvidia/Gemma-4-31B-IT-NVFP4, memory utilization 0.70, 131,072-token context, two sessions.",
    "The venue preflight was changed for this lab to read llama.cpp’s context field, which reports under a different name than vLLM and oMLX. Its behavior on the other servers is unchanged.",
    "Reviewed before publication by a model from a different family (Gemini, via the Antigravity CLI) instructed to attack the record. Its findings and their disposition are kept with the lab’s working notes.",
    "The harness, the gate and the certification tooling are public. The assessment methodology, the vendor grades and the judgment that turns a score into a placement are not.",
    "The essay on this page was drafted by Claude Opus 5.5, an Anthropic model, from this record under the author’s voice specification, passed the deterministic post gate, and was validated by the author before publication. Anthropic’s Claude Code is the agent harness on every arm of this lab, so the drafting model’s vendor also appears on the bench."
  ],
  "assessments": [],
  "publisher": "The Advisor Bench LLC",
  "program": "Layer2C Labs",
  "url": "https://labs.layer2c.com/labs/dense-on-bandwidth",
  "markdown_url": "https://labs.layer2c.com/labs/dense-on-bandwidth.md",
  "json_url": "https://labs.layer2c.com/labs/dense-on-bandwidth.json"
}
