Opening a shop is an API call; the broker learns of it in the same request
The last step of onboarding that needed a shell: provision site printed a broker password and a person typed it into Mosquitto's passwd file on the host - mounted read-only in the container, so the first attempt failed silently and the password was re-rolled. No tenant could open a second branch without us. The server now drives Mosquitto's dynamic-security plugin over its own broker login: POST /api/sites (owner) writes the row and the sealed password, registers the login and a per-site role with literal topics (the 2.0 plugin does not substitute %u - measured), and removes the row again if the broker refuses, so a shop cannot exist in the database and not on the broker. provision site goes through the same path. The head-office Shops screen gets 'Open a new shop'. broker-init converts the existing passwd file into the plugin's store with every hash intact - PBKDF2-SHA512 both sides - so the cutover re-claims no shop PC. Rehearsed locally: old logins keep working, isolation holds, the health probe works, and a PC claiming a shop opened through the API connects as that shop. run-local.sh now brings the broker up the same way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj
This commit is contained in:
49
run-local.sh
49
run-local.sh
@@ -72,15 +72,34 @@ step "3b. Schema"
|
||||
"./$STATE/bv-server" migrate
|
||||
|
||||
step "4. Mosquitto"
|
||||
if [ ! -f "$STATE/mosquitto/mosquitto.conf" ]; then
|
||||
# Dynamic security, not a passwd file - the same shape as production. The
|
||||
# server registers each site's broker login itself over the control topic, so
|
||||
# there is no per-site password to type here and nothing to restart. The store
|
||||
# is seeded once with the server's own login as the plugin admin; after that
|
||||
# the plugin owns the file.
|
||||
mkdir -p "$STATE/mosquitto/data"
|
||||
# Rewritten when it is the pre-plugin shape, so a checkout that ran the old
|
||||
# script comes up in the new one rather than half of each.
|
||||
if ! grep -q mosquitto_dynamic_security "$STATE/mosquitto/mosquitto.conf" 2>/dev/null; then
|
||||
rm -f "$STATE/mosquitto/passwd" "$STATE/mosquitto/acl"
|
||||
docker rm -f bv-mqtt >/dev/null 2>&1 || true
|
||||
cat > "$STATE/mosquitto/mosquitto.conf" <<EOF
|
||||
per_listener_settings false
|
||||
listener 1883
|
||||
allow_anonymous false
|
||||
password_file /mosquitto/config/passwd
|
||||
acl_file /mosquitto/config/acl
|
||||
plugin /usr/lib/mosquitto_dynamic_security.so
|
||||
plugin_opt_config_file /mosquitto/data/dynamic-security.json
|
||||
EOF
|
||||
printf 'user behavision-server\ntopic read bv/#\n' > "$STATE/mosquitto/acl"
|
||||
: > "$STATE/mosquitto/passwd"
|
||||
fi
|
||||
if [ ! -f "$STATE/mosquitto/data/dynamic-security.json" ]; then
|
||||
: > "$STATE/mosquitto/passwd.seed"
|
||||
docker run --rm -v "$PWD/$STATE/mosquitto:/m" eclipse-mosquitto:2 \
|
||||
mosquitto_passwd -b /m/passwd.seed behavision-server "$MQTT_PASSWORD" 2>/dev/null
|
||||
"./$STATE/bv-server" broker-init -passwd "$STATE/mosquitto/passwd.seed" \
|
||||
-out "$STATE/mosquitto/data/dynamic-security.json" -backend-user behavision-server >/dev/null
|
||||
rm -f "$STATE/mosquitto/passwd.seed"
|
||||
# The plugin rewrites this file, so the broker's user (1883) must own it.
|
||||
chmod 666 "$STATE/mosquitto/data/dynamic-security.json"
|
||||
fi
|
||||
# A container is reused only if its config mount still points HERE. The bind
|
||||
# source is baked in when the container is created, so one made while the
|
||||
@@ -98,6 +117,7 @@ if docker inspect bv-mqtt >/dev/null 2>&1; then
|
||||
fi
|
||||
docker inspect bv-mqtt >/dev/null 2>&1 || docker run -d --name bv-mqtt \
|
||||
-p "${MQTT_PORT}:1883" -v "$MQTT_CONF:/mosquitto/config" \
|
||||
-v "$MQTT_CONF/data:/mosquitto/data" \
|
||||
eclipse-mosquitto:2 >/dev/null
|
||||
docker start bv-mqtt >/dev/null 2>&1 || true
|
||||
|
||||
@@ -114,13 +134,7 @@ if ! docker exec bv-mqtt sh -c 'exit 0' >/dev/null 2>&1; then
|
||||
docker logs --tail 5 bv-mqtt >&2
|
||||
exit 1
|
||||
fi
|
||||
# stderr is NOT discarded here. A failure means the server cannot authenticate
|
||||
# to its own broker, and the whole point of this script is that you find that
|
||||
# out now rather than from an empty arrivals feed.
|
||||
docker exec bv-mqtt mosquitto_passwd -b /mosquitto/config/passwd \
|
||||
behavision-server "$MQTT_PASSWORD" >/dev/null
|
||||
docker restart bv-mqtt >/dev/null
|
||||
echo " broker on ${MQTT_PORT}"
|
||||
echo " broker on ${MQTT_PORT} (dynamic security)"
|
||||
|
||||
step "5. First accounts"
|
||||
# Idempotent throughout: every provision subcommand upserts, so re-running this
|
||||
@@ -140,14 +154,9 @@ step "5. First accounts"
|
||||
# never readable again - so it is pushed into Mosquitto here in the same breath.
|
||||
# A shop PC enrolled on an earlier run therefore has to be claimed again, which
|
||||
# is the right trade locally and is why this is not how production works.
|
||||
SITE_OUT=$("./$STATE/bv-server" provision site -client tenext-retail -slug chennai \
|
||||
-name "TeNext Chennai" -tz Asia/Kolkata)
|
||||
BUSER=$(printf '%s' "$SITE_OUT" | sed -n "s/.*passwd \([^ ]*\) .*/\1/p")
|
||||
BPASS=$(printf '%s' "$SITE_OUT" | sed -n "s/.*passwd [^ ]* '\(.*\)'.*/\1/p")
|
||||
docker exec bv-mqtt mosquitto_passwd -b /mosquitto/config/passwd "$BUSER" "$BPASS" >/dev/null
|
||||
grep -q "^user $BUSER$" "$STATE/mosquitto/acl" || \
|
||||
printf '\nuser %s\ntopic write bv/%s/#\n' "$BUSER" "$BUSER" >> "$STATE/mosquitto/acl"
|
||||
docker restart bv-mqtt >/dev/null
|
||||
MQTT_URL="tcp://127.0.0.1:${MQTT_PORT}" MQTT_USERNAME=behavision-server MQTT_PASSWORD="$MQTT_PASSWORD" \
|
||||
"./$STATE/bv-server" provision site -client tenext-retail -slug chennai \
|
||||
-name "TeNext Chennai" -tz Asia/Kolkata | sed 's/^/ /'
|
||||
|
||||
printf ' platform admin admin@loyaly.ai / loyaly-platform-2026 (Companies only)\n'
|
||||
printf ' TeNext owner suriya@tenext.in / tenext-2026 (Shops, Live, Cameras, Customers, Reports)\n'
|
||||
|
||||
Reference in New Issue
Block a user