This guide covers everything needed to set up the syncbench development environment on macOS (Apple Silicon).
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"Add to ~/.zshrc:
eval "$(/opt/homebrew/bin/brew shellenv)"brew install gnupg pinentry-macGenerate a key:
gpg --full-generate-key
# Choose: RSA 4096, no expiry, your personal name + emailConfigure pinentry:
echo "pinentry-program $(brew --prefix)/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agentUpload the public key to GitHub → Settings → GPG Keys:
gpg --armor --export <KEY_ID> | pbcopyssh-keygen -t ed25519 -C "your@email.com" -f ~/.ssh/id_ed25519_personalConfigure ~/.ssh/config:
Host github.com-personal
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_personal
IdentitiesOnly yes
Upload the public key to GitHub → Settings → SSH Keys:
cat ~/.ssh/id_ed25519_personal.pub | pbcopyVerify:
ssh -T git@github.com-personal
# Expected: Hi <username>! You've successfully authenticated...brew install uv
uv python install 3.11brew install --cask dockerOpen Docker.app and wait until the menu bar whale icon shows "Docker Desktop is running".
brew install mosquittoThese provide mosquitto_pub and mosquitto_sub for manual MQTT testing.
Note: this installs the CLI tools only — the broker itself runs inside Docker.
git clone git@github.com-personal:marsyanggo/syncbench.git
cd syncbench
uv syncgit config user.name "Your Name"
git config user.email "your@personal-email.com"
git config user.signingkey <GPG_KEY_ID>
git config commit.gpgsign true
git config gpg.program /opt/homebrew/bin/gpg# Python imports
uv run python -c "import paho.mqtt.client; import fastapi; import pydantic; import influxdb_client; print('OK')"
# Docker
docker compose versiondocker compose up -dVerify core services (Mosquitto + InfluxDB):
# InfluxDB
curl localhost:8086/health
# Expected: {"status":"pass", ...}
# Mosquitto pub/sub roundtrip
mosquitto_sub -h localhost -p 1883 -t "atf/test" -C 1 &
mosquitto_pub -h localhost -p 1883 -t "atf/test" -m "hello-atf"
# Expected output: hello-atfGrafana is optional. The Inspector (
http://localhost:8080) has a built-in live chart and run results. Grafana is useful for historical run comparison and advanced queries.To start Grafana:
docker compose --profile monitoring up -d grafana open http://localhost:3000 # admin / atf-grafana-2026Datasource and dashboard are auto-provisioned via
deploy/grafana/.
Mac mini ──Ethernet── AX4200 LAN (192.168.1.x)
RPi × N ──5GHz Wi-Fi (atf_test_5g)── AX4200
sudo scutil --set LocalHostName atf-brokerVerify: ping atf-broker.local should resolve (127.0.0.1 on Mac mini itself; actual LAN IP from RPi).
ssh root@192.168.1.1
uci set wireless.radio1.disabled='0'
uci set wireless.default_radio1.ssid='atf_test_5g'
uci set wireless.default_radio1.encryption='psk2+ccmp'
uci set wireless.default_radio1.key='12345678'
uci commit wireless
wifi upradio1 = 5GHz (MT7986A). radio0 = 2.4GHz (keep disabled for testing).
ssh-copy-id -i ~/.ssh/id_ed25519_personal.pub <user>@raspberrypi.local# 1. Rsync repo to RPi
rsync -av --exclude='.git' --exclude='.venv' --exclude='__pycache__' \
-e "ssh -i ~/.ssh/id_ed25519_personal" \
/path/to/syncbench/ <user>@raspberrypi.local:~/syncbench/
# 2. Run setup script on RPi (sets hostname, installs packages, configures systemd)
ssh -i ~/.ssh/id_ed25519_personal <user>@raspberrypi.local \
"bash ~/syncbench/scripts/setup-linux.sh --broker atf-broker.local --agent-id rpi-sta-01"setup-linux.sh will:
- Set hostname to
rpi-sta-01→ reachable asrpi-sta-01.localafter reboot - Install
iperf3,iw,chronyvia apt - Install
uv+ Python 3.11 - Run
uv sync - Install + enable
atf-agentsystemd service (broker:atf-broker.local)
Each RPi gets a unique
--agent-id(rpi-sta-01, rpi-sta-02, …). The ID is baked into the systemd service — RPi always knows who it is after reboot.
# On RPi — via systemd (auto-restarts, survives reboot)
sudo systemctl start atf-agent
journalctl -u atf-agent -f
# Or manually
uv run atf-agent --broker atf-broker.local --agent-id rpi-sta-01Open Inspector: uv run atf-inspector
Browser: http://localhost:8080 → should show ● online rpi-sta-01 IDLE
uv run atf-run scenarios/00_smoke_test.yaml # 1 STA, 30s
uv run atf-run scenarios/01_two_sta_equal.yaml # 2 STA, 60satf-run automatically:
- Assigns a unique iperf3 port to each STA (5201, 5202, …)
- Starts
iperf3 -slocally for each port - Synchronises all agents to start at the same timestamp (
sleep_untilwith busy-wait) - Streams per-second throughput to InfluxDB in real-time
- Kills iperf3 servers and writes run summary when done
No need to manually run
iperf3 -s— the orchestrator handles it.
uv run atf-inspector # then open http://localhost:8080Select devices → set duration → Start Run. Live throughput curves, phase progress, and Jain's FI appear in the browser as the test runs.
docker compose --profile monitoring up -d grafana
open http://localhost:3000 # admin / atf-grafana-2026- Dashboards → syncbench
- Set time range to Last 5 minutes, auto-refresh 5s
- Run
atf-run— lines appear in real-time
After changing agent code on Mac, push to all RPis:
for ip in 192.168.1.221 192.168.1.233; do
rsync -av --exclude='.git' --exclude='.venv' --exclude='__pycache__' \
-e "ssh -i ~/.ssh/id_ed25519_personal" \
/path/to/syncbench/ mars@$ip:~/syncbench/
ssh -i ~/.ssh/id_ed25519_personal mars@$ip "sudo systemctl restart atf-agent"
done- No commits between 09:00–18:00 on workdays (legal compliance)
- Every commit is GPG-signed automatically
- Use personal email only — never company email
See design_spec/syncbench-phase1-spec.md §15 for full legal compliance rules.