Skip to content
NewTwo Macs, one Claude Code setup: skills as plugins, not symlinks
Logbook
May 29, 2026·8 MIN READ
C2PA in Ableton: Making AI Music Provenance Visible Inside Your DAW

C2PA in Ableton: Making AI Music Provenance Visible Inside Your DAW

In May 2026 I shipped an MCP for reading C2PA manifests in music. This post is the follow-up: the same reader, now inside Ableton Live as an open-source Max for Live device.

Key takeaways

  • Google Lyria signs every generated MP3 with a C2PA manifest, but Ableton has no way to display that information today.
  • A Max for Live device + local FastAPI server makes the manifest visible the moment you click a clip.
  • All in one repo — Python HTTP server + Max for Live device. One clone, one install, no cloud round-trip.
  • Read-side only — the harder problem (signing the DAW project, attributing the training corpus) is next.

In May 2026 I shipped a Claude MCP for reading C2PA manifests in music files. This post is the follow-up: the same reader, now inside Ableton Live as an open-source Max for Live device.

This is the fourth article in my Max for Live series. It builds directly on the M4L → FastAPI pattern I wrote about in January 2026, with one change: the API runs on your laptop, not in the cloud.

The problem

Google Lyria signs every MP3 it generates with a C2PA manifest. The manifest records who made the file, what model produced it, whether it is AI-generated, what watermarks were applied (Lyria adds SynthID), and who signed the claim. The data is there. Producers cannot see it.

You drop a Lyria stem onto an audio track. Ableton shows you the waveform. It does not show you that the file is AI-generated, who signed it, or what the manifest says about the source. To find out, you have to leave the DAW, run c2patool on the file, and read raw JSON.

Andrew Melchior — Massive Attack's CTO, advising the UK DCMS on AI and the Copyright Act — framed the bigger gap in a reply on LinkedIn to my MCP announcement:

C2PA now tells you a machine generated this track. It doesn't tell you whose work trained the machine.

Training-corpus provenance is the hard problem. This article is about the easier half — making the output manifest visible at the point a producer is actually working.

The fix

A Max for Live device. Click a clip → see the manifest summary. That is the whole product.

Under the hood, the device borrows a pattern I already shipped: a Max for Live js object reads the Live Object Model, then routes the work to a Node for Max HTTP client. I wrote about this exact shape in January 2026. The only change here is where the HTTP server lives.

Architecture in one diagram

flowchart TB
    subgraph Ableton["Ableton Live"]
        device["MTL_C2PA_Ableton_PoC.amxd"]
        livepi["LiveAPI observer<br/>(detail_clip)"]
        js["c2pa_reader.js"]
        node["c2pa_node.js<br/>(Node for Max)"]
        ui["UI textedit<br/>summary display"]
        device --> livepi --> js --> node --> ui
    end

    subgraph LocalServer["mtl-c2pa-http (127.0.0.1:8765)"]
        fastapi["FastAPI app"]
        summary["/summary"]
        verify["/verify"]
        scan["/scan"]
        info["/info"]
        fastapi --> summary
        fastapi --> verify
        fastapi --> scan
        fastapi --> info
    end

    subgraph PythonPkg["mtl_c2pa_server (in this repo)"]
        c2pamod["c2pa.py (parser)"]
        reader["c2pa-python Reader<br/>(Rust binding)"]
        c2pamod --> reader
    end

    node -->|POST /summary| fastapi
    fastapi -->|import| c2pamod

    launchd["launchd plist<br/>auto-start on login"]
    launchd -.->|spawn| fastapi

Three sentences:

  1. A LiveAPI observer in the js object watches live_set view detail_clip. When the selection changes, it pulls the clip's file_path.
  2. The path flows into a Node for Max script, which POSTs it to http://127.0.0.1:8765/summary.
  3. The local FastAPI server is shipped in this same repo as the device — a small Python package (mtl_c2pa_server) that wraps the official c2pa-python Rust binding. One clone, one poetry install.

Selection-to-display, end to end

sequenceDiagram
    actor User
    participant Live as Ableton Live
    participant Device as M4L Device
    participant Reader as c2pa_reader.js
    participant Node as c2pa_node.js
    participant HTTP as mtl-c2pa-http :8765
    participant Lib as c2pa-python

    User->>Live: click audio clip
    Live->>Device: detail_clip changed
    Device->>Reader: observer fires
    Reader->>Live: get detail_clip.file_path
    Live-->>Reader: /path/to/lyria.mp3
    Reader->>Node: outlet "fetch" path
    Node->>HTTP: POST /summary {path}
    HTTP->>Lib: Reader(mime, stream).json()
    Lib-->>HTTP: manifest store
    HTTP-->>Node: summary JSON
    Node->>Device: outlet "result" json
    Device-->>User: display summary

The manual "Refresh" button short-circuits the observer and triggers the same POST /summary call. Same pipeline, different trigger source.

Why local, not cloud, not CLI

I considered three options. Local won.

A CLI shell-out per click

Simplest, but Python startup costs ~300 ms. On every clip selection. You feel it.

I use c2pa-python, which wraps the Rust binding. The Python interpreter cold-start is the bottleneck, not the C2PA read itself.
Cloud Run

Right for generation, wrong for reading. You'd upload audio just to inspect a local file.

Cloud is what my reference M4L → API article uses — and for storing generation events with an audit log, it is the right answer. For reading a manifest already in your file system, it isn't.
Local FastAPI server

Keeps c2pa-python warm. Loopback only. Reuses the MCP parser without changes.

The HTTP layer is ~80 lines wrapping c2pa-python directly. Self-contained in this repo — one clone, one install, no separate dependency on the sibling MCP server.

A persistent local FastAPI server keeps c2pa-python warm in memory, runs on loopback only (no external attack surface), and reuses the existing MCP parser without changes.

What you see

For a Lyria-signed MP3, the device shows the same shape the MCP produces:

c2pa_summary output in Ableton
{
  "file": "/Users/you/Music/Sovereign_Ascent.mp3",
  "generator": {"name": "Google C2PA Core Generator Library"},
  "is_ai_generated": true,
  "actions": [
    {"action": "c2pa.created", "description": "Created by Google Generative AI."},
    {"action": "c2pa.edited", "description": "Applied imperceptible SynthID watermark."}
  ],
  "watermarks": [
    {"description": "Applied imperceptible SynthID watermark."}
  ],
  "signature_issuer": "Google LLC",
  "validation": "valid"
}

For an unsigned audio clip you get {"error": "No C2PA manifest found"}. For a MIDI clip, {"info": "MIDI clip — no C2PA manifest applicable"}. The Refresh button re-runs the lookup manually.

What this doesn't solve

This is read-side only. The device tells you the C2PA truth that is already in the file. It does not sign anything. It does not tell you what corpus trained the model. Andrew's point still stands.

The C2PA community is working on the harder problem. There is an active conversation in the C2PA group about capturing provenance during DAW work — signing the project at bounce time, attributing the samples and MIDI sources that went in. That is the generation side. I would like to help build it next.

Install and try it

You need Ableton Live with Max for Live (Live Suite, or Standard plus the M4L add-on), and macOS for the auto-start script.

setup.sh
# 1. Clone and install (one-time)
git clone https://github.com/musictechlab/mtl-c2pa-ableton.git
cd mtl-c2pa-ableton && poetry install

# 2. Auto-start on login (macOS)
bash install/install.sh

# 3. Verify
curl http://127.0.0.1:8765/health
# {"status":"ok"}

# 4. Drop the device on a track
# Drag device/MTL_C2PA_Ableton_PoC.amxd onto any audio track in Live.

That's it. Click a Lyria clip — see the manifest. Full setup detail in the repo README.

Roadmap

Generation-side device next. The plan is a Max for Live effect that signs the project at bounce time and emits a C2PA manifest describing the session's ingredients — samples, MIDI sources, plugin chain. I would like input from the C2PA community before settling on the assertion shape.

If you are interested in the broader open-source MCP family I have shipped:

Try it, break it, send feedback

The device is MIT-licensed. Repo: musictechlab/mtl-c2pa-ableton. Issues and PRs welcome. If you build something on top of the local HTTP server (a Logic plugin, a REAPER script, a standalone viewer), tell me — same pattern works for any DAW that can shell out to localhost.


Need help integrating C2PA into your music workflow?

Adding provenance to your distribution pipeline, AI music platform, DAW plugin, or rights workflow? I have been there.

drop me a line — no sales pitch, just honest engineering advice.

FAQ

Why a local HTTP server instead of a CLI shell-out or a cloud API?

Python startup takes about 300 ms, which you feel on every clip click. A persistent local server keeps c2pa-python warm in memory. Cloud would mean uploading audio to a server just to inspect a file the user already has on disk — wrong shape for reading, right shape for generation.

Does the device work without the Max for Live add-on?

No. You need Ableton Live Suite (which includes Max for Live), or Ableton Live Standard with the Max for Live add-on. The device is a .amxd file — it cannot run as a stock VST or AU plugin.

Does the device upload my audio anywhere?

No. The FastAPI server binds to 127.0.0.1 only — loopback. No external network access. The Node for Max HTTP client only ever talks to your own machine. You can verify with lsof -i :8765 while the server runs.

What happens with MIDI clips or recorded audio that has no source file?

The device returns a structured info message: MIDI clip — no C2PA manifest applicable, or audio clip has no file path (recorded in session?). It never crashes the device, never blocks Live.

Will this device tell me whose work trained the model that generated my Lyria stem?

No, and that is the next problem the C2PA community is working on. This device surfaces the output manifest — what Google Lyria declared about the file. Training-corpus attribution requires upstream provenance on the training data itself, which almost no major dataset is C2PA-signed today.