From 0d7543704bf6744ef54241f084cc790ae17f852e Mon Sep 17 00:00:00 2001 From: reachableceo Date: Tue, 1 Sep 2026 19:55:23 -0500 Subject: [PATCH] feat: add ChirpStack Cloudron package (Infrastructure) [#668] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ChirpStack 4.19.1 (LoRaWAN network server) as the 14th package: official-image wrapper, digest-pinned, with only bash added to the upstream alpine runtime. Native OIDC login wired to the Cloudron platform provider (openid_connect backend, config regenerated every start); postgresql + redis addons; diesel migrations auto-run. Operator config (NetID, US915 region, gateway MQTT backend) lives in seeded files under /app/data/config. Build green; config generation validated through chirpstack's own TOML parser. Docs gardened to 14 packages. Ticket: https://projects.knownelement.com/issues/668 ๐Ÿ’˜ Generated with Crush Assisted-by: Crush:glm-5.2 --- JOURNAL.md | 99 ++++++- .../Infrastructure/chirpstack/.dockerignore | 1 + .../Infrastructure/chirpstack/.env.example | 13 + .../Infrastructure/chirpstack/CHANGELOG.md | 17 ++ .../chirpstack/CloudronManifest.json | 25 ++ .../Infrastructure/chirpstack/Dockerfile | 51 ++++ .../Infrastructure/chirpstack/README.md | 90 ++++++ .../Infrastructure/chirpstack/logo.png | Bin 0 -> 12143 bytes .../Infrastructure/chirpstack/start.sh | 261 ++++++++++++++++++ README.md | 7 +- STATUS.md | 17 +- 11 files changed, 570 insertions(+), 11 deletions(-) create mode 100644 Package-Workspace/Infrastructure/chirpstack/.dockerignore create mode 100644 Package-Workspace/Infrastructure/chirpstack/.env.example create mode 100644 Package-Workspace/Infrastructure/chirpstack/CHANGELOG.md create mode 100644 Package-Workspace/Infrastructure/chirpstack/CloudronManifest.json create mode 100644 Package-Workspace/Infrastructure/chirpstack/Dockerfile create mode 100644 Package-Workspace/Infrastructure/chirpstack/README.md create mode 100644 Package-Workspace/Infrastructure/chirpstack/logo.png create mode 100755 Package-Workspace/Infrastructure/chirpstack/start.sh diff --git a/JOURNAL.md b/JOURNAL.md index 99641f6..2d60deb 100644 --- a/JOURNAL.md +++ b/JOURNAL.md @@ -4,7 +4,7 @@ **Project**: TSYSDevStack-SupportStack-Cloudron **Goal**: Package ~57 applications for Cloudron PaaS platform **Start Date**: 2025-01-24 -**Current Status**: 13/~57 packages completed (~23%) +**Current Status**: 14/~57 packages completed (~25%) ## Completed Packages @@ -1038,6 +1038,103 @@ nothing) --- +### 14. ChirpStack (Infrastructure) โœ… + +**Application**: ChirpStack โ€” open-source LoRaWAN network-server (web UI + +gRPC/REST on one port, PostgreSQL storage, Redis sessions/dedup, external +MQTT broker for gateways and integrations). Upstream: +https://github.com/chirpstack/chirpstack (MIT), v4.19.1. + +**Ticket**: [#668](https://projects.knownelement.com/issues/668) + +**Pattern**: official-image wrapper. A from-source build would drag the +whole Rust workspace + pnpm UI through a multi-GB compile; upstream ships +a supported image (`chirpstack/chirpstack`) whose final stage is alpine + +one static musl binary + ca-certificates, run as nobody:nogroup. Wrapping +it costs one `apk add bash` and an ENTRYPOINT override โ€” 83.4MB final +image, the smallest package in the workspace so far. + +**Auth gate verdict**: โœ… OIDC preferred. ChirpStack 4 has a native +OpenID Connect backend โ€” `[user_authentication]` `enabled="openid_connect"` +plus `[user_authentication.openid_connect]` (provider_url, client_id, +client_secret, redirect_url, scopes; PKCE + nonce state stored in Redis). +`start.sh` regenerates this block on every start from +`CLOUDRON_OIDC_ISSUER` / `CLOUDRON_OIDC_CLIENT_ID` / +`CLOUDRON_OIDC_CLIENT_SECRET` with +`redirect_url = ${CLOUDRON_APP_ORIGIN}/auth/oidc/callback`. + +**Key findings / decisions**: + +- **Config model**: `chirpstack --config ` concatenates EVERY `*.toml` + in the dir (read_dir order is unsorted, so tables must be disjoint across + files โ€” duplicates are a parse error) and substitutes `${ENV}` vars after + concatenation. Split into `10-cloudron.toml` (generated every boot: + logging, postgresql, redis, api, user_authentication โ€” addon credentials + stay current across Cloudron password rotations) and operator-owned + `50-network.toml` + `region_us915_0.toml` seeded once, editable via the + file manager. +- **Migrations**: embedded diesel migrations run automatically in + `storage::setup()` at startup and seed an internal `admin` user + (email `admin`, password `admin`, is_admin). No manual migrate step. +- **Admin bootstrap gap**: users auto-registered via OIDC are non-admin, + and the internal login form is disabled in openid_connect mode. Document + path: `CHIRPSTACK_AUTH_MODE=internal` โ†’ login admin/admin โ†’ set a real + password + your SSO email โ†’ back to openid_connect. ChirpStack links an + OIDC identity to an existing user BY EMAIL, which transfers the admin + role to the SSO login. +- **API JWT secret**: `api.secret` signs login tokens; persisted at + `/app/data/.api_jwt_secret` so restarts don't invalidate sessions. +- **Ports**: single listener `api.bind 0.0.0.0:8080` (UI + gRPC + REST + + `/auth/oidc/*`). Gateways do NOT dial the app: ChirpStack 4 consumes an + external MQTT broker configured per region + (`[regions.gateway.backend.mqtt]`); US915 region file seeded as default + (Texas), operator points it at their broker. +- **Redis addon first use** in this repo: `CLOUDRON_REDIS_URL` feeds + `redis.servers` directly (auth embedded in the URL). + +**Challenges & solutions**: + +- **Hub API digest mismatch**: the Docker Hub tags API reported an index + digest for `4.19.1` that BuildKit refused (`not found` when used as + `tag@digest`). `docker manifest inspect --verbose` gave the real + registry digest (amd64 manifest `sha256:c74901โ€ฆ`); pinned + tag+that-digest and the build resolved. Lesson: trust the registry, not + the Hub API, when pinning. +- **Addon wait without clients**: the wrapper image has no psql/redis-cli, + and alpine package names drift between versions. Used bash `/dev/tcp` + probes instead โ€” no extra packages, no version pinning headaches. +- **Secret escaping into TOML**: generated DSN/OIDC values pass through a + `toml_escape` (backslash + double-quote) helper; verified with an + adversarial password containing both characters โ€” chirpstack's own TOML + parser accepted the generated files (run reached DB connect, i.e. past + config load, by design of the test). + +**Verification**: + +- `docker build --cgroup-parent ukrrs-batch.slice` green; image 83.4MB; + `chirpstack --version` โ†’ 4.19.1 inside the image. +- start.sh executed against a scratch `/app/data`: config + seeds written, + chirpstack parsed all TOML and proceeded to storage setup (failed only + at the intentionally absent DB โ€” the expected boundary of a + no-addons smoke test). + +**Files Created**: + +- Dockerfile (official-image wrapper, digest-pinned, bash added) +- CloudronManifest.json (manifestVersion 2, port 8080, localstorage + + postgresql 16 + redis addons) +- start.sh (addon waits, JWT secret persistence, config generation, + seeding, exec) โ€” committed executable +- README.md (auth story, admin bootstrap, config layout, MQTT note) +- CHANGELOG.md +- .env.example (CHIRPSTACK_AUTH_MODE / OIDC_REGISTRATION / LOG_LEVEL) +- .dockerignore (excludes the cloned repo/ from the build context) +- logo.png (from upstream ui/public/logo.png) + +**Commit**: `feat: add ChirpStack Cloudron package (Infrastructure) [#668]` + +--- + ## Packaging Pattern: Download Pre-Compiled Binaries ### When to Use diff --git a/Package-Workspace/Infrastructure/chirpstack/.dockerignore b/Package-Workspace/Infrastructure/chirpstack/.dockerignore new file mode 100644 index 0000000..7d37f1a --- /dev/null +++ b/Package-Workspace/Infrastructure/chirpstack/.dockerignore @@ -0,0 +1 @@ +repo/ diff --git a/Package-Workspace/Infrastructure/chirpstack/.env.example b/Package-Workspace/Infrastructure/chirpstack/.env.example new file mode 100644 index 0000000..4c2e754 --- /dev/null +++ b/Package-Workspace/Infrastructure/chirpstack/.env.example @@ -0,0 +1,13 @@ +# ChirpStack Cloudron package - optional env knobs +# (set via `cloudron env set`, restart to apply) +# +# Authentication backend. Default: openid_connect (Cloudron SSO). +# Use "internal" ONLY for the one-time admin bootstrap described in +# README.md, then switch back. +CHIRPSTACK_AUTH_MODE=openid_connect + +# Auto-register users on first SSO login (true/false). +CHIRPSTACK_OIDC_REGISTRATION=true + +# Log level: trace | debug | info | warn | error. +CHIRPSTACK_LOG_LEVEL=info diff --git a/Package-Workspace/Infrastructure/chirpstack/CHANGELOG.md b/Package-Workspace/Infrastructure/chirpstack/CHANGELOG.md new file mode 100644 index 0000000..d4cb2cf --- /dev/null +++ b/Package-Workspace/Infrastructure/chirpstack/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +## 1.0.0 โ€” 2026-09-01 + +Initial Cloudron package (ChirpStack 4.19.1, ticket +[#668](https://projects.knownelement.com/issues/668)). + +- Official-image wrapper around `chirpstack/chirpstack:4.19.1` (digest + pinned); only addition to the runtime image is `bash` for `start.sh`. +- Native OIDC login against the Cloudron platform identity provider; + admin bootstrap path documented (temporary `internal` auth mode). +- PostgreSQL + Redis addons; diesel migrations auto-run at startup; + persistent API JWT secret under `/app/data`. +- Config split into a per-boot generated fragment and operator-owned + `50-network.toml` + `region_us915_0.toml` under `/app/data/config/`. +- Env knobs: `CHIRPSTACK_AUTH_MODE`, `CHIRPSTACK_OIDC_REGISTRATION`, + `CHIRPSTACK_LOG_LEVEL`. diff --git a/Package-Workspace/Infrastructure/chirpstack/CloudronManifest.json b/Package-Workspace/Infrastructure/chirpstack/CloudronManifest.json new file mode 100644 index 0000000..b002f0c --- /dev/null +++ b/Package-Workspace/Infrastructure/chirpstack/CloudronManifest.json @@ -0,0 +1,25 @@ +{ + "manifestVersion": 2, + "type": "app", + "id": "io.cloudron.chirpstack", + "title": "ChirpStack", + "description": "Open-source LoRaWAN network server: manage gateways, devices, tenants and integrations from a web interface. Users log in via OIDC (Cloudron single sign-on); PostgreSQL stores all state, Redis handles sessions and de-duplication. LoRaWAN gateways connect through an external MQTT broker configured per region.", + "author": "Orne Brocaar", + "website": "https://www.chirpstack.io/", + "contactEmail": "cloudron@tsys.dev", + "tagline": "LoRaWAN network server with web UI", + "version": "4.19.1", + "healthCheckPath": "/", + "httpPort": 8080, + "memoryLimit": 512, + "addons": { + "localstorage": true, + "postgresql": { + "version": "16" + }, + "redis": {} + }, + "mediaLinks": [], + "changelog": "Initial Cloudron package for ChirpStack 4.19.1 (official-image wrapper). Native OIDC login wired to the Cloudron platform identity provider (user_authentication.openid_connect from CLOUDRON_OIDC_* env); PostgreSQL addon for storage with auto-run diesel migrations; Redis addon for sessions and de-duplication. Config is split into a generated platform fragment (rewritten each start) and operator-owned files under /app/data/config (network NetID, regions, gateway MQTT backend) editable with the Cloudron file manager. US915 region seeded by default.", + "icon": "file://logo.png" +} diff --git a/Package-Workspace/Infrastructure/chirpstack/Dockerfile b/Package-Workspace/Infrastructure/chirpstack/Dockerfile new file mode 100644 index 0000000..6a9ec3e --- /dev/null +++ b/Package-Workspace/Infrastructure/chirpstack/Dockerfile @@ -0,0 +1,51 @@ +# ChirpStack Cloudron Package +# +# ChirpStack is an open-source LoRaWAN network-server: web UI + gRPC/REST +# API on a single port, PostgreSQL for storage, Redis for sessions / +# deduplication / OIDC state, and an external MQTT broker for gateway +# connectivity and integrations (configured per region, not embedded). +# +# Upstream: https://github.com/chirpstack/chirpstack +# - Official Docker image chirpstack/chirpstack:4.19.1 (alpine, single +# static musl binary /usr/bin/chirpstack, upstream runs it as +# nobody:nogroup with ENTRYPOINT /usr/bin/chirpstack) +# - Takes a config DIRECTORY via `chirpstack --config `; every *.toml +# in it is concatenated (tables must not collide across files) and +# ${ENV_VAR} placeholders are substituted +# - DB schema migrations (diesel, embedded) run automatically at startup +# and seed an internal `admin` user +# +# Authentication: NATIVE OIDC (preferred). start.sh regenerates +# /app/data/config/10-cloudron.toml on every start, wiring the Cloudron +# platform OIDC provider (CLOUDRON_OIDC_ISSUER / CLIENT_ID / CLIENT_SECRET) +# into [user_authentication.openid_connect]. CHIRPSTACK_AUTH_MODE=internal +# is kept as an operator escape hatch for admin bootstrap only (see README). +# +# Pattern: official-image wrapper. Building the Rust workspace + pnpm UI +# from source is a multi-GB compile; the upstream image is the supported +# distribution channel. Image pinned by tag AND digest (amd64 manifest +# digest of the 4.19.1 tag, verified via docker manifest inspect). +FROM chirpstack/chirpstack:4.19.1@sha256:c749015e640b8cf33338c08b12922896b17636feb06e421abdd3cc80f1cdc6b9 + +# bash is the only addition: start.sh uses it for the addon wait loops +# (bash /dev/tcp) and TOML generation. Kept as root only for apk; the +# runtime user stays the upstream nobody:nogroup. +USER root +RUN apk add --no-cache bash + +# start.sh waits for the postgresql + redis addons, seeds the persistent +# config fragments under /app/data/config/ and execs chirpstack. +# Made executable on the host, not at build time (Cloudron builds hit +# permission errors on RUN chmod). +COPY start.sh /app/start.sh + +WORKDIR /app/data + +# Cloudron exposes the web UI / REST / gRPC on this port (api.bind in the +# generated config). No other TCP listener is enabled by default: gateway +# connectivity is outbound MQTT to an external broker. +EXPOSE 8080 + +USER nobody:nogroup + +ENTRYPOINT ["/bin/bash", "/app/start.sh"] diff --git a/Package-Workspace/Infrastructure/chirpstack/README.md b/Package-Workspace/Infrastructure/chirpstack/README.md new file mode 100644 index 0000000..b7ec85e --- /dev/null +++ b/Package-Workspace/Infrastructure/chirpstack/README.md @@ -0,0 +1,90 @@ +# ChirpStack โ€” Cloudron Package + +[ChirpStack](https://www.chirpstack.io/) is an open-source LoRaWAN +network-server: it manages gateways, devices, tenants, device-profiles and +integrations, and exposes a web UI plus gRPC / REST APIs from a single +port. Packaged as an **official-image wrapper** around the upstream +`chirpstack/chirpstack:4.19.1` image (pinned by digest). + +- **Upstream:** https://github.com/chirpstack/chirpstack (MIT) +- **Ticket:** [#668](https://projects.knownelement.com/issues/668) +- **Category:** Infrastructure ยท **Pattern:** official-image wrapper + + `start.sh` config generation + +## Authentication (auth gate verdict: โœ… OIDC preferred) + +ChirpStack 4 ships a **native OpenID Connect backend** +(`[user_authentication.openid_connect]`). `start.sh` wires the Cloudron +platform OIDC provider (`CLOUDRON_OIDC_ISSUER` / `CLOUDRON_OIDC_CLIENT_ID` +/ `CLOUDRON_OIDC_CLIENT_SECRET`) with +`redirect_url = ${CLOUDRON_APP_ORIGIN}/auth/oidc/callback`, so logins go +through Cloudron SSO. Registration is enabled: the first SSO login +auto-creates the user. + +### Admin bootstrap (one-time) + +The DB migration seeds an internal `admin` user (email `admin`, +password `admin`, `is_admin = true`). Users created via OIDC registration +are regular (non-admin) users. To become admin over SSO: + +1. Set the env `CHIRPSTACK_AUTH_MODE=internal` and restart the app. +2. Log in as `admin` / `admin`, immediately set a strong password, and + change the account email to your Cloudron login email. +3. Set `CHIRPSTACK_AUTH_MODE` back to `openid_connect` (or remove it) and + restart. +4. Log in via SSO: ChirpStack links the OIDC identity to the existing + user **by email**, granting the admin role. + +Until step 2 is done the seeded `admin` account keeps its default +password โ€” do the bootstrap right after installing. + +## Addons & ports + +| Concern | Cloudron wiring | +|---------|-----------------| +| Storage | `postgresql` addon (diesel migrations auto-run at startup) | +| Sessions / dedup / OIDC state | `redis` addon (`CLOUDRON_REDIS_URL`) | +| Files | `localstorage` (`/app/data`) | +| Web UI + gRPC + REST | single HTTP port `8080` (`api.bind`) | + +LoRaWAN gateways do **not** connect to this app directly: ChirpStack 4 +consumes an **external MQTT broker** per region (see +`/app/data/config/region_*.toml`). Point `regions.gateway.backend.mqtt` +at your broker (e.g. a Mosquitto container/app) and configure integrations +the same way. + +## Configuration layout + +`chirpstack --config ` concatenates every `*.toml` in the directory; +tables must not repeat across files. `/app/data/config/` is split: + +| File | Written | Owns | +|------|---------|------| +| `10-cloudron.toml` | every start | `[logging]` `[postgresql]` `[redis]` `[api]` `[user_authentication]` โ€” regenerated, never hand-edit | +| `50-network.toml` | first start | `[network]` (NetID, enabled regions) | +| `region_us915_0.toml` | first start | US915 `[[regions]]` block incl. gateway MQTT backend | + +Operator knobs live in `50-network.toml` and the region files โ€” edit them +with the Cloudron file manager; changes apply on restart. **Change +`net_id`** from the seeded `000001` to a unique value for your network, +and point the region MQTT backend at a real broker. The API JWT secret is +persisted at `/app/data/.api_jwt_secret`. + +## Environment knobs (.env.example) + +| Variable | Default | Purpose | +|----------|---------|---------| +| `CHIRPSTACK_AUTH_MODE` | `openid_connect` | `internal` only for admin bootstrap (see above) | +| `CHIRPSTACK_OIDC_REGISTRATION` | `true` | auto-register unknown SSO users | +| `CHIRPSTACK_LOG_LEVEL` | `info` | trace / debug / info / warn / error | + +## Build & install + +```bash +docker build --cgroup-parent ukrrs-batch.slice -t chirpstack-cloudron:test \ + Package-Workspace/Infrastructure/chirpstack/ +``` + +`cloudron build && cloudron install` on the Cloudron VPS for real +deployment. First start waits for PostgreSQL + Redis, seeds config and +runs migrations automatically. diff --git a/Package-Workspace/Infrastructure/chirpstack/logo.png b/Package-Workspace/Infrastructure/chirpstack/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..24264fe80b6901b1755f6f38b2f45c692dcb3da9 GIT binary patch literal 12143 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 Mtf^G@(jxNz0CFL+!2kdN literal 0 HcmV?d00001 diff --git a/Package-Workspace/Infrastructure/chirpstack/start.sh b/Package-Workspace/Infrastructure/chirpstack/start.sh new file mode 100755 index 0000000..84d9812 --- /dev/null +++ b/Package-Workspace/Infrastructure/chirpstack/start.sh @@ -0,0 +1,261 @@ +#!/bin/bash +set -euo pipefail + +# ChirpStack runtime setup: +# 1. wait for the Cloudron postgresql + redis addons +# 2. persist the API JWT secret (rotating it would invalidate tokens) +# 3. regenerate /app/data/config/10-cloudron.toml on EVERY start so addon +# credentials and OIDC secrets are always current (Cloudron rotates +# addon passwords on restore / migration) +# 4. seed the operator-owned config fragments ONCE (network + region); +# these are meant to be edited with the Cloudron file manager and +# survive restarts +# 5. exec chirpstack (diesel migrations run automatically at startup) +# +# Config layout (chirpstack concatenates every *.toml in --config ; +# tables must not repeat across files): +# 10-cloudron.toml generated: [logging] [postgresql] [redis] [api] +# [user_authentication] - DO NOT hand-edit +# 50-network.toml seeded once: [network] (net_id, enabled_regions) +# region_*.toml seeded once: [[regions]] blocks (gateway MQTT +# backend, channel plan, region network overrides) + +CONFIG_DIR="/app/data/config" +GENERATED_CONF="${CONFIG_DIR}/10-cloudron.toml" +NETWORK_CONF="${CONFIG_DIR}/50-network.toml" +SECRET_FILE="/app/data/.api_jwt_secret" + +mkdir -p "${CONFIG_DIR}" + +# --- 1. wait for the addons ------------------------------------------------- +wait_tcp() { + local host="$1" port="$2" name="$3" + echo "Waiting for ${name} at ${host}:${port} ..." + until (exec 3<>"/dev/tcp/${host}/${port}") 2>/dev/null; do + echo "${name} is unavailable - sleeping" + sleep 2 + done + echo "${name} is up" +} + +wait_tcp "${CLOUDRON_POSTGRESQL_HOST:-127.0.0.1}" "${CLOUDRON_POSTGRESQL_PORT:-5432}" "PostgreSQL" +wait_tcp "${CLOUDRON_REDIS_HOST:-127.0.0.1}" "${CLOUDRON_REDIS_PORT:-6379}" "Redis" + +# --- 2. persistent API JWT secret -------------------------------------------- +if [[ ! -s "${SECRET_FILE}" ]]; then + ( umask 077; head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' > "${SECRET_FILE}" ) + echo "Generated new API JWT secret" +fi +API_SECRET="$(cat "${SECRET_FILE}")" + +# --- 3. generated platform config (rewritten on every start) ----------------- +toml_escape() { + local s="$1" + s="${s//\\/\\\\}" + s="${s//\"/\\\"}" + printf '%s' "${s}" +} + +DB_HOST="${CLOUDRON_POSTGRESQL_HOST:-127.0.0.1}" +DB_PORT="${CLOUDRON_POSTGRESQL_PORT:-5432}" +DB_NAME="${CLOUDRON_POSTGRESQL_DATABASE:-chirpstack}" +DB_USER="$(toml_escape "${CLOUDRON_POSTGRESQL_USERNAME:-chirpstack}")" +DB_PASSWORD="$(toml_escape "${CLOUDRON_POSTGRESQL_PASSWORD:-chirpstack}")" +PG_DSN="postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable" + +# Cloudron redis requires auth; prefer the platform-provided URL. +if [[ -n "${CLOUDRON_REDIS_URL:-}" ]]; then + REDIS_SERVERS="$(toml_escape "${CLOUDRON_REDIS_URL}")" +else + REDIS_PASSWORD="$(toml_escape "${CLOUDRON_REDIS_PASSWORD:-}")" + REDIS_SERVERS="redis://:${REDIS_PASSWORD}@${CLOUDRON_REDIS_HOST:-127.0.0.1}:${CLOUDRON_REDIS_PORT:-6379}" +fi + +AUTH_MODE="${CHIRPSTACK_AUTH_MODE:-openid_connect}" +OIDC_REGISTRATION="${CHIRPSTACK_OIDC_REGISTRATION:-true}" +LOG_LEVEL="${CHIRPSTACK_LOG_LEVEL:-info}" +OIDC_ISSUER="$(toml_escape "${CLOUDRON_OIDC_ISSUER:-}")" +OIDC_CLIENT_ID="$(toml_escape "${CLOUDRON_OIDC_CLIENT_ID:-}")" +OIDC_CLIENT_SECRET="$(toml_escape "${CLOUDRON_OIDC_CLIENT_SECRET:-}")" +APP_ORIGIN="$(toml_escape "${CLOUDRON_APP_ORIGIN:-}")" + +cat > "${GENERATED_CONF}" < "${NETWORK_CONF}" <<'EOF' +# Operator configuration - seeded on first start, safe to edit with the +# Cloudron file manager (changes apply on restart). +# +# Do NOT add [postgresql], [redis], [api], [user_authentication] or +# [logging] here: those tables are owned by the generated 10-cloudron.toml +# and chirpstack fails to parse duplicate tables. + +[network] +# NetID (3 bytes, hex) - MUST be changed to a unique value for this +# network. 000000-0000FF is reserved for private / experimental networks +# (see LoRa Alliance NetID assignments). +net_id = "000001" + +# Enabled regions; each entry must match the id of a [[regions]] block in +# one of the region_*.toml files in this directory. More region files can +# be copied from the upstream repo (chirpstack/configuration/). +enabled_regions = ["us915_0"] +EOF + echo "Seeded network config at ${NETWORK_CONF}" +fi + +if [[ ! -f "${CONFIG_DIR}/region_us915_0.toml" ]]; then + cat > "${CONFIG_DIR}/region_us915_0.toml" <<'EOF' +# US915 region (channels 0-7 + 64) - the standard US915 sub-band plan. +# Verbatim from upstream chirpstack/configuration/region_us915_0.toml; +# edit the gateway MQTT backend below to point at your broker. + +[[regions]] +id = "us915_0" +description = "US915 (channels 0-7 + 64)" +common_name = "US915" +user_info = "" + +[regions.gateway] +force_gws_private = false + +[regions.gateway.backend] +enabled = "mqtt" + +[regions.gateway.backend.mqtt] +topic_prefix = "us915_0" +share_name = "chirpstack" +server = "tcp://localhost:1883" +username = "" +password = "" +qos = 0 +clean_session = false +client_id = "" +keep_alive_interval = "30s" +ca_cert = "" +tls_cert = "" +tls_key = "" + +[[regions.gateway.channels]] +frequency = 902300000 +bandwidth = 125000 +modulation = "LORA" +spreading_factors = [7, 8, 9, 10] + +[[regions.gateway.channels]] +frequency = 902500000 +bandwidth = 125000 +modulation = "LORA" +spreading_factors = [7, 8, 9, 10] + +[[regions.gateway.channels]] +frequency = 902700000 +bandwidth = 125000 +modulation = "LORA" +spreading_factors = [7, 8, 9, 10] + +[[regions.gateway.channels]] +frequency = 902900000 +bandwidth = 125000 +modulation = "LORA" +spreading_factors = [7, 8, 9, 10] + +[[regions.gateway.channels]] +frequency = 903100000 +bandwidth = 125000 +modulation = "LORA" +spreading_factors = [7, 8, 9, 10] + +[[regions.gateway.channels]] +frequency = 903300000 +bandwidth = 125000 +modulation = "LORA" +spreading_factors = [7, 8, 9, 10] + +[[regions.gateway.channels]] +frequency = 903500000 +bandwidth = 125000 +modulation = "LORA" +spreading_factors = [7, 8, 9, 10] + +[[regions.gateway.channels]] +frequency = 903700000 +bandwidth = 125000 +modulation = "LORA" +spreading_factors = [7, 8, 9, 10] + +[[regions.gateway.channels]] +frequency = 903000000 +bandwidth = 500000 +modulation = "LORA" +spreading_factors = [8] + +[regions.network] +installation_margin = 10 +rx_window = 0 +rx1_delay = 1 +rx1_dr_offset = 0 +rx2_dr = 8 +rx2_frequency = 923300000 +rx2_prefer_on_rx1_dr_lt = 0 +rx2_prefer_on_link_budget = false +downlink_tx_power = -1 +adr_disabled = false +min_dr = 0 +max_dr = 3 +enabled_uplink_channels = [0, 1, 2, 3, 4, 5, 6, 7, 64] + +[regions.network.rejoin_request] +enabled = false +max_count_n = 0 +max_time_n = 0 + +[regions.network.class_b] +ping_slot_dr = 8 +ping_slot_frequency = 0 +EOF + echo "Seeded region config at ${CONFIG_DIR}/region_us915_0.toml" +fi + +# --- 5. run ------------------------------------------------------------------- +# chirpstack applies the embedded diesel migrations on startup, then serves +# the web UI + gRPC/REST API on 0.0.0.0:8080. +echo "Starting ChirpStack ..." +exec /usr/bin/chirpstack --config "${CONFIG_DIR}" diff --git a/README.md b/README.md index 2ce1dec..9a41d2f 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ The Cloudron component focuses on packaging upstream free/libre/open application ### ๐Ÿ“Š Current Progress - **Total Applications**: ~57 (see [GitUrlList.txt](GitUrlList.txt)) -- **Completed Packages**: 13/~57 (~23%) +- **Completed Packages**: 14/~57 (~25%) - **Packaging Templates**: Created โœ… -- **Packages Committed & Pushed**: 13 โœ… +- **Packages Committed & Pushed**: 14 โœ… - **Build Tickets**: 46 filed (#633-#678, umbrella [#632](https://projects.knownelement.com/issues/632), Redmine project 55); grist-core excluded (packaged upstream) @@ -35,6 +35,7 @@ The Cloudron component focuses on packaging upstream free/libre/open application | 11 | Rathole | Infrastructure | 3.51GB | 8000, 2333, 5200-5299 | localstorage (auth proxy) | โœ… Committed | | 12 | Database Gateway | Infrastructure | 93.7MB | 8080 | localstorage, postgresql | โœ… Committed | | 13 | FX | DevOps-Tools | 3.55GB | 8000 | localstorage (auth proxy) | โœ… Committed | +| 14 | ChirpStack | Infrastructure | 83.4MB | 8080 | localstorage, postgresql, redis | โœ… Committed | ### ๐Ÿ“ฆ Packages in Development @@ -150,7 +151,7 @@ Applications are organized by function rather than programming language: | [Docker DrawIO](https://github.com/jgraph/docker-drawio) | [GitHub](https://github.com/jgraph/docker-drawio) | Dockerized version of Draw.io diagramming tool | Documentation-Tools | โœ… Packaged | | [SigNoz](https://github.com/SigNoz/signoz) | [GitHub](https://github.com/SigNoz/signoz) | Open-source observability platform | Monitoring | | [Sentry](https://github.com/getsentry/sentry) | [GitHub](https://github.com/getsentry/sentry) | Error tracking and performance monitoring | Monitoring | -| [ChirpStack](https://github.com/chirpstack/chirpstack) | [GitHub](https://github.com/chirpstack/chirpstack) | Open-source LoRaWAN network server | Infrastructure | +| [ChirpStack](https://github.com/chirpstack/chirpstack) | [GitHub](https://github.com/chirpstack/chirpstack) | Open-source LoRaWAN network server | Infrastructure | โœ… Packaged | | [eLabFTW](https://github.com/elabftw/elabftw) | [GitHub](https://github.com/elabftw/elabftw) | Electronic lab notebook for research teams | Business-Apps | | [PLMore](https://github.com/PLMore/PLMore) | [GitHub](https://github.com/PLMore/PLMore) | Business process management platform | Business-Apps | | [Jamovi](https://github.com/jamovi/jamovi) | [GitHub](https://github.com/jamovi/jamovi) | Statistical spreadsheet software | Scientific-Computing | diff --git a/STATUS.md b/STATUS.md index ef90f69..d7a1d37 100644 --- a/STATUS.md +++ b/STATUS.md @@ -3,20 +3,21 @@ > **Human read-only. Agents maintain this file automatically after each work > session.** Do not edit by hand โ€” the next agent run will overwrite it. > -> **Last updated:** 2026-09-01 by Crush (GLM-5.2) โ€” FX packaged (#640, -> DevOps-Tools, 13th package); auth gate verdict: no user concept โ€” -> httpAuth proxy gates the landing page (CLI-workstation pattern). +> **Last updated:** 2026-09-01 by Crush (GLM-5.2) โ€” ChirpStack packaged +> (#668, Infrastructure, 14th package); auth gate verdict: native OIDC +> (`user_authentication.openid_connect`) wired to the platform provider; +> postgresql + redis addons, official-image wrapper of chirpstack 4.19.1. ## Current State: STABLE (packaging phase, ongoing) -Cloudron packaging pipeline is operational. 13 of ~57 upstream applications are +Cloudron packaging pipeline is operational. 14 of ~57 upstream applications are packaged, committed, and pushed. Packaging templates exist for the core patterns. The gardening protocol (this file + AGENTS.md) keeps docs in sync. All remaining apps now carry build tickets (#633-#678) under umbrella [#632](https://projects.knownelement.com/issues/632) in Redmine project 55 โ€” ready for the sequential grind-driver pattern. -## Completed Packages (13) +## Completed Packages (14) | # | Application | Category | Pattern | Port(s) | Addons | |---|-------------|----------|---------|---------|--------| @@ -33,6 +34,7 @@ ready for the sequential grind-driver pattern. | 11 | Rathole | Infrastructure | Pre-compiled binaries + auth proxy | 8000, 2333, 5200-5299 | localstorage | | 12 | Database Gateway | Infrastructure | Multi-stage (Go, CGO) | 8080 | localstorage, postgresql | | 13 | FX | DevOps-Tools | Pre-compiled binaries + auth proxy | 8000 | localstorage | +| 14 | ChirpStack | Infrastructure | Official-image wrapper | 8080 | localstorage, postgresql, redis | Each package lives in `Package-Workspace///` and contains a `Dockerfile`, `CloudronManifest.json`, `README.md`, `CHANGELOG.md`, `logo.png`, @@ -121,7 +123,7 @@ Full write-ups of each pattern + challenges are in [`JOURNAL.md`](JOURNAL.md). | DevOps-Tools | 1 | 1/1 (100%) โœ… | fx done | | Financial-Payments | 1 | 0/1 | | | Financial-Trading | 1 | 0/1 | | -| Infrastructure | 6 | 3/6 | easy-gate, rathole, database-gateway done | +| Infrastructure | 6 | 4/6 | easy-gate, rathole, database-gateway, chirpstack done | | Legal | 1 | 0/1 | | | Project-Management | 1 | 0/1 | | | Scientific-Computing | 2 | 0/2 | | @@ -135,7 +137,7 @@ Auth capability is a hard gate before packaging (see LDAP acceptable (risk flag), ๐Ÿ”„ = auth-proxy (no users), โŒ = local-only (unacceptable / blocked-on-auth). -### Completed packages (13) +### Completed packages (14) | App | OIDC | LDAP | Verdict | Note | |-----|------|------|---------|------| @@ -152,6 +154,7 @@ LDAP acceptable (risk flag), ๐Ÿ”„ = auth-proxy (no users), โŒ = local-only | Rathole | n/a | n/a | ๐Ÿ”„ proxy | **Packaged** with `httpAuth.type=proxy` on the status page; tunnels secured by mandatory per-service tokens (Noise/TLS optional) | | Database Gateway | yes | no | โœ… preferred | **Packaged**; native OIDC-only app โ€” platform provider env (`CLOUDRON_OIDC_*`) seeded into config.json; roles from the `groups` claim | | FX | n/a | n/a | ๐Ÿ”„ proxy | **Packaged**; CLI-only FaaS tool with no user concept โ€” pinned binary + workspace driven from the Cloudron terminal; landing page gated by `httpAuth.type=proxy` | +| ChirpStack | yes | no | โœ… preferred | **Packaged**; native `[user_authentication.openid_connect]` wired to `CLOUDRON_OIDC_*`; OIDC-registered users are non-admin โ€” one-time `CHIRPSTACK_AUTH_MODE=internal` bootstrap links the seeded `admin` to your SSO email (README) | ### Candidates researched