dc3d760d2b
Closes the gap where all of this session's new server-side capability (live replication, coordinated promotion, identical signing keys) had no UI at all -- an operator using the Master Site modal had no way to know any of it existed or was working. - Master Site modal: new "Live Replication" row (spoke) shows whether this join actually registered for live updates or is stuck on a one-time snapshot; new "Registered Spokes" row (master) shows how many spokes are receiving live pushes. - Join form: new "this site's own reachable URL" field, prefilled from window.location.origin, wired to the selfUrl the join API already supported but the UI never sent -- a UI-driven join previously NEVER registered for live replication, only the setup.sh bootstrap path did. The success toast now reports whether live replication actually activated, not just "joined". - Promote button: success toast now surfaces the handoff result (old master demoted / unreachable / no previous master), so the operator sees immediately whether the coordinated demotion actually happened. - GET /api/site/config no longer returns masterJoinKey or replicationPushToken in the response -- found while wiring this up: live credentials were being sent straight to the browser for every admin session. Replaced with boolean derivatives (hasMasterJoinKey, liveReplication). - GET /api/directory-admin/site-status gained liveReplication (spoke) and registeredSpokesCount (master) so the modal has something to render. Verified by actually driving it in a real browser against a live container (not just code review): logged in, opened the modal, saw the new rows, minted a real join key end-to-end, no console errors. docs/site-join.md rewritten to cover live replication, signing-key sync, coordinated promotion/demote, and the new endpoints -- it previously only described the v2.2.0-v2.3.0 one-time-snapshot behavior.