For contributors

Add a device

Run tutti against your LAN, review the capture, submit it. Two submission paths: open a merge request directly, or file an issue if that's easier.

1. Get tutti

Pre-built binaries for macOS, Linux, and Windows live on thereleases page. Each platform has a one-time setup; pick the block that matches your machine.

macOS (Apple Silicon)
curl -LO https://gitlab.com/dunn.dev/tutti/-/releases/permalink/latest/downloads/tutti-darwin-arm64
chmod +x tutti-darwin-arm64
xattr -d com.apple.quarantine ./tutti-darwin-arm64
mv tutti-darwin-arm64 /usr/local/bin/tutti

Until tutti ships a notarized macOS binary, Gatekeeper quarantines the download. The xattr step is the one-time unquarantine. Alternatively: right-click the file in Finder, choose Open, then click "Open" in the dialog.

Linux (amd64 or arm64)
# Pick the arch that matches your machine.
curl -LO https://gitlab.com/dunn.dev/tutti/-/releases/permalink/latest/downloads/tutti-linux-amd64
# or:
curl -LO https://gitlab.com/dunn.dev/tutti/-/releases/permalink/latest/downloads/tutti-linux-arm64

chmod +x tutti-linux-*
sudo mv tutti-linux-* /usr/local/bin/tutti

Static binary, no runtime dependencies. Runs on any modern Linux that can bind UDP/1900 + UDP/5353. Multicast must actually be allowed on the interface (Wi-Fi AP isolation and IGMP snooping are common silent killers).

Windows (amd64)
# In PowerShell.
Invoke-WebRequest `
  -Uri https://gitlab.com/dunn.dev/tutti/-/releases/permalink/latest/downloads/tutti-windows-amd64.exe `
  -OutFile tutti.exe

SmartScreen warns on first run because the binary isn't code-signed yet. Click "More info" → "Run anyway." Windows Firewall will also prompt the first time tutti binds for SSDP/mDNS multicast — allow on private networks. Don't run inside WSL: WSL networking is NAT'd and can't see the host's LAN devices.

From source (any platform)
git clone https://gitlab.com/dunn.dev/tutti.git
cd tutti
make build
# binary lands at ./tutti

Requires Go 1.25 or newer. Useful for development; for submitting captures the released binaries are fine.

2. Capture the LAN

./tutti capture --drive --contributor github:your-handle

Output lands in ./capture-<timestamp>-<host>/. Open the directory and review what tutti wrote: SSDP/mDNS dumps, device descriptors, GetProtocolInfo sink lists, and per-tone drive transcripts. Edit notes.md if there's context worth adding ("the device is slow to wake," "I had to disable IPv6," etc.).

--drive opt-in: tutti callsGetTransportInfo first and refuses to interrupt a device that's currently PLAYING. Pass --forceif you really do want to interrupt.

--contributor takes a forge-prefixed handle (github: or gitlab:); the manifest stores it and the site links your profile from any capture you submit. Anonymous captures are accepted (omit the flag).

3. Validate locally

./tutti validate ./capture-<timestamp>-<host>

The validator catches schema-shape issues, vacuity (e.g. zero SSDP responses + zero mDNS records), and missing redaction records. CI runs the same validator on submission. If it fails locally, the failure message names the next action.

4. Submit

Two paths. Pick whichever is friction-free for you; the result is the same — your capture lands in the corpus and the site rebuilds.

faster

Open a merge request

mkdir -p evidence/<vendor>-<model>/captures/
mv ./capture-<ts>-<host> \
   evidence/<vendor>-<model>/captures/<ts>-<contributor>

Fork the repo, commit the capture directory underevidence/, push, open the MR. The MR template covers the pre-submit checklist; CI validates the bundle and your branch merges on green.

Open an MR →

simpler

File an issue

tar czf my-capture.tar.gz ./capture-<ts>-<host>/
# attach the tarball to the issue

If MRs aren't your workflow, file an issue with the capture attached. A maintainer will fold it into the corpus on your behalf. The issue template walks you through what's useful.

File an issue →

What's preserved, what's redacted

tutti scrubs RFC1918 LAN addresses, Subsonic auth params (user, token, salt), and Authorization headers from every transcript by default. The manifest records every redaction it applied, so a reader of the bundle always knows what was stripped.

The device's UDN is preserved on purpose: it's central to bug evidence, advertised on every multicast, and not sensitive in isolation.