Building + Shipping Code
Do the build on your Mac / dev machine — the device only runs the artifacts.
The Go binary cross-compiles to linux/arm64; the HAL is plain Python, no
build step needed.
The bootstrap service polls the Intern team's OTA channel and, if it finds
a newer official build, will reinstall os-server / HAL / web on top of
whatever you just pushed — silently wiping your custom binary at the next
tick. Before you push a single custom build to a device, turn bootstrap off
so your changes stick:
sudo systemctl disable --now bootstrap
See Controlling the OTA Bootstrap for the full enable / disable / re-enable flow. Only turn bootstrap back on when you want to go back to tracking the official fleet builds — at that point the next tick will overwrite your custom changes with the current OTA version.
Go — os-server + bootstrap
# From the repo root
make os-build # → os/services/os-server
make os-build-bootstrap # → os/services/bootstrap
Push the binary and restart:
sshpass -p 'orangepi' scp os/services/os-server orangepi@<ip>:/tmp/os-server-new
sshpass -p 'orangepi' ssh orangepi@<ip> "echo orangepi | sudo -S bash -c '
cp /usr/local/bin/os-server /usr/local/bin/os-server.bak.\$(date +%s)
mv /tmp/os-server-new /usr/local/bin/os-server
chmod 755 /usr/local/bin/os-server
systemctl restart os-server
'"
Python — HAL
Sync individual files, or the whole directory:
# One file
sshpass -p 'orangepi' scp os/hal/drivers/mic_button.py orangepi@<ip>:/tmp/
sshpass -p 'orangepi' ssh orangepi@<ip> "echo orangepi | sudo -S bash -c '
mv /tmp/mic_button.py /opt/hal/drivers/mic_button.py
chown root:root /opt/hal/drivers/mic_button.py
systemctl restart hal
'"
# Whole HAL tree (destructive — replaces /opt/hal contents)
sshpass -p 'orangepi' rsync -avz --delete os/hal/ orangepi@<ip>:/tmp/hal/
sshpass -p 'orangepi' ssh orangepi@<ip> "echo orangepi | sudo -S bash -c '
rsync -avz --delete /tmp/hal/ /opt/hal/
systemctl restart hal
'"
Web (Setup + Admin UI)
The React SPA lives in os/services/web/. Nginx serves the built assets from
/usr/share/nginx/html/setup/.
# Build
cd os/services/web && npm install && npm run build # → dist/
# Push (from repo root)
cd os/services/web/dist && zip -qr /tmp/setup-web.zip .
sshpass -p 'orangepi' scp /tmp/setup-web.zip orangepi@<ip>:/tmp/
sshpass -p 'orangepi' ssh orangepi@<ip> "echo orangepi | sudo -S bash -c '
find /usr/share/nginx/html/setup -mindepth 1 -not -name VERSION -exec rm -rf {} + 2>/dev/null || true
unzip -o -q /tmp/setup-web.zip -d /usr/share/nginx/html/setup
chown -R root:root /usr/share/nginx/html/setup
'"
Nginx serves the SPA — no restart needed after replacing files.