install.sh is the curl -fsSL <url> | sudo bash -s -- --token … bootstrap for a clean Raspberry Pi OS:
Preflight — root / arch / apt / SDR dongle
Install Docker + compose plugin + git + jq
Clone node-26 at a pinned ref (default v1, --track-main opt-in) into /opt/drb/node-26
Write .env non-interactively from flags/env, with a /dev/tty interactive fallback (necessary — stdin is the pipe under curl | bash)
Enroll: POST /nodes/enroll → poll GET /nodes/{id}/credentials → write configs/credentials.json. Verified against drb-c2-core/app/routers/enrollment.py — headers (X-Enrollment-Token, X-Pickup-Secret), request/response fields, and the 401/403/429 semantics all match.
docker compose pull && up -d — prebuilt by default; --build opts into the ~1h op25 build
Print the admin-approval step
Idempotent: re-run preserves .env, skips enrollment if a key is on disk, picks up the api_key after approval.
Also:
setup.sh deleted — two scripts writing .env drift.
Makefilesetup: no longer calls the removed script.
README.md Setup section rewritten around the one-liner; make setup / make up kept as the local-dev path.
After merge: re-cut v1 at the merge commit so the tag actually contains install.sh (git tag -f v1 <merge-sha> && git push -f origin v1). The images (:latest/:stable) are unaffected — this touches no drb-edge-node/** or op25-container/** path.
Not addressed (follow-ups):
Client-side enrollment belongs in the edge-node app (mqtt_manager.py:73-85); install.sh doing it is interim.
Standing hazard D3 (script header): docker-compose.yml bind-mounts app source over the image, so a pinned ref and the pulled image tags must not diverge.
install.sh is bash -n clean but has not been run on a real clean Pi OS image — the RTL-SDR kernel-module path is unverified.
Addresses node-26#4.
`install.sh` is the `curl -fsSL <url> | sudo bash -s -- --token …` bootstrap for a clean Raspberry Pi OS:
1. Preflight — root / arch / apt / SDR dongle
2. Install Docker + compose plugin + git + jq
3. Clone `node-26` at a **pinned ref** (default `v1`, `--track-main` opt-in) into `/opt/drb/node-26`
4. Write `.env` non-interactively from flags/env, with a `/dev/tty` interactive fallback (necessary — stdin is the pipe under `curl | bash`)
5. **Enroll**: `POST /nodes/enroll` → poll `GET /nodes/{id}/credentials` → write `configs/credentials.json`. Verified against `drb-c2-core/app/routers/enrollment.py` — headers (`X-Enrollment-Token`, `X-Pickup-Secret`), request/response fields, and the 401/403/429 semantics all match.
6. `docker compose pull && up -d` — prebuilt by default; `--build` opts into the ~1h op25 build
7. Print the admin-approval step
Idempotent: re-run preserves `.env`, skips enrollment if a key is on disk, picks up the `api_key` after approval.
**Also:**
- `setup.sh` deleted — two scripts writing `.env` drift.
- `Makefile` `setup:` no longer calls the removed script.
- `README.md` Setup section rewritten around the one-liner; `make setup` / `make up` kept as the local-dev path.
**After merge:** re-cut `v1` at the merge commit so the tag actually contains `install.sh` (`git tag -f v1 <merge-sha> && git push -f origin v1`). The images (`:latest`/`:stable`) are unaffected — this touches no `drb-edge-node/**` or `op25-container/**` path.
**Not addressed (follow-ups):**
- Client-side enrollment belongs in the edge-node app (`mqtt_manager.py:73-85`); `install.sh` doing it is interim.
- Standing hazard D3 (script header): `docker-compose.yml` bind-mounts app source over the image, so a pinned ref and the pulled image tags must not diverge.
- `install.sh` is `bash -n` clean but has not been run on a real clean Pi OS image — the RTL-SDR kernel-module path is unverified.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
install.sh is the `curl -fsSL <url> | sudo bash -s -- --token ...` bootstrap:
preflight (root/arch/apt/SDR) → install docker + compose + git + jq → clone
node-26 at a pinned ref (default `v1`, `--track-main` opt-in) → write .env
non-interactively from flags/env with a /dev/tty interactive fallback →
enroll with C2 (POST /nodes/enroll, poll GET /nodes/{id}/credentials, matches
drb-c2-core/app/routers/enrollment.py exactly) and write configs/credentials.json
→ docker compose pull && up -d (prebuilt; --build opts into the ~1h op25 build)
→ print the admin-approval step. Idempotent: re-run picks up the api_key after
approval; existing .env is preserved.
- setup.sh deleted — two scripts writing .env drift. install.sh owns it now.
- Makefile `setup:` no longer calls the removed script (cp .env.example fallback).
- README Setup section rewritten around the one-liner; `make setup`/`make up`
kept as the local-dev path.
Notes carried in the script header: git.vpn.cusano.net is public (D1, settled);
`v1` must be re-cut at this change's merge commit so the tag actually contains
install.sh. Standing hazard D3: docker-compose.yml bind-mounts the app source
over the image, so a pinned ref and the pulled image tags must not diverge.
Client-side enrollment still belongs in the edge-node app (mqtt_manager.py:73-85);
install.sh doing it is the interim. Tracked for follow-up.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Addresses node-26#4.
install.shis thecurl -fsSL <url> | sudo bash -s -- --token …bootstrap for a clean Raspberry Pi OS:node-26at a pinned ref (defaultv1,--track-mainopt-in) into/opt/drb/node-26.envnon-interactively from flags/env, with a/dev/ttyinteractive fallback (necessary — stdin is the pipe undercurl | bash)POST /nodes/enroll→ pollGET /nodes/{id}/credentials→ writeconfigs/credentials.json. Verified againstdrb-c2-core/app/routers/enrollment.py— headers (X-Enrollment-Token,X-Pickup-Secret), request/response fields, and the 401/403/429 semantics all match.docker compose pull && up -d— prebuilt by default;--buildopts into the ~1h op25 buildIdempotent: re-run preserves
.env, skips enrollment if a key is on disk, picks up theapi_keyafter approval.Also:
setup.shdeleted — two scripts writing.envdrift.Makefilesetup:no longer calls the removed script.README.mdSetup section rewritten around the one-liner;make setup/make upkept as the local-dev path.After merge: re-cut
v1at the merge commit so the tag actually containsinstall.sh(git tag -f v1 <merge-sha> && git push -f origin v1). The images (:latest/:stable) are unaffected — this touches nodrb-edge-node/**orop25-container/**path.Not addressed (follow-ups):
mqtt_manager.py:73-85);install.shdoing it is interim.docker-compose.ymlbind-mounts app source over the image, so a pinned ref and the pulled image tags must not diverge.install.shisbash -nclean but has not been run on a real clean Pi OS image — the RTL-SDR kernel-module path is unverified.🤖 Generated with Claude Code
install.sh is the `curl -fsSL <url> | sudo bash -s -- --token ...` bootstrap: preflight (root/arch/apt/SDR) → install docker + compose + git + jq → clone node-26 at a pinned ref (default `v1`, `--track-main` opt-in) → write .env non-interactively from flags/env with a /dev/tty interactive fallback → enroll with C2 (POST /nodes/enroll, poll GET /nodes/{id}/credentials, matches drb-c2-core/app/routers/enrollment.py exactly) and write configs/credentials.json → docker compose pull && up -d (prebuilt; --build opts into the ~1h op25 build) → print the admin-approval step. Idempotent: re-run picks up the api_key after approval; existing .env is preserved. - setup.sh deleted — two scripts writing .env drift. install.sh owns it now. - Makefile `setup:` no longer calls the removed script (cp .env.example fallback). - README Setup section rewritten around the one-liner; `make setup`/`make up` kept as the local-dev path. Notes carried in the script header: git.vpn.cusano.net is public (D1, settled); `v1` must be re-cut at this change's merge commit so the tag actually contains install.sh. Standing hazard D3: docker-compose.yml bind-mounts the app source over the image, so a pinned ref and the pulled image tags must not diverge. Client-side enrollment still belongs in the edge-node app (mqtt_manager.py:73-85); install.sh doing it is the interim. Tracked for follow-up. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>