Commit Graph
960 Commits
Author SHA1 Message Date
Outis 2f6604c5db Merge pull request #325 from outis1one/claude/extensions-qr-code-popup-6jlkcp
asterisk vendor: always append TURN port even when TURN_SERVER is hos…
2026-08-12 23:12:39 -04:00
Claude c2e02f78ae asterisk vendor: always append TURN port even when TURN_SERVER is host-only
entrypoint.sh's turn_server fallback only appended :TURN_PORT when TURN_SERVER
was completely unset — a TURN_SERVER carried over from an older install (or
set to a bare host by hand) passed straight through with no port, so the
Sipnetic QR export's "st=turn:user:pass@host" field ended up missing the
port entirely. Append it whenever the configured value has no colon at all,
not just when it's empty.
2026-08-13 02:50:20 +00:00
Outis 080412d07a Merge pull request #324 from outis1one/claude/extensions-qr-code-popup-6jlkcp
Retire the shared coturn service — every WebRTC/SIP service now runs …
2026-08-12 22:21:13 -04:00
Claude ca239a3886 Retire the shared coturn service — every WebRTC/SIP service now runs its own
Shared coturn (services/coturn.sh, ensure_coturn_user in lib/common.sh) is no
longer an installable or usable option anywhere in this repo. It's moved to
attic/coturn.sh (with tools/coturn-test-check.sh alongside it), which is
outside setup.sh's services/*.sh glob, so it never registers, never appears
in the menu, and `sudo ./setup.sh coturn` now fails with "unknown service".

Asterisk and Mattermost each already had an opt-out to run their own
dedicated coturn instead of the shared one; that opt-out is now the only
behavior — the shared-coturn preference, the opt-out prompt, and every
ensure_coturn_user() call site are gone. find_free_coturn_range()
(lib/common.sh) is what makes unconditional dedicated coturn safe: it scans
every coturn-owning service's own .env on the box for already-claimed relay
ranges and picks one that can't collide, so Asterisk + any number of
Mattermost instances can each run their own coturn on one box without the
relay-port collisions this repo's coturn history warns about.

Existing installs still pointed at a shared coturn container are left
running as-is on `update` (no silent migration attempt against a service
that no longer exists to heal against) — a full/fresh reinstall is the
migration path, which generates a new dedicated coturn with fresh
credentials and says so.

Also updates CLAUDE.md's coturn guidance for future service authors,
attic/README.md with the retirement rationale, and stale
services/coturn.sh path references in services/asterisk.sh,
tools/pstn-test-check.sh, README.md, and docs/vps-sizing-recommendations.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crt4ymNEHEbWqscB1qvZgC
2026-08-13 02:12:32 +00:00
Outis 7db680d5e4 Merge pull request #323 from outis1one/claude/extensions-qr-code-popup-6jlkcp
Claude/extensions qr code popup 6jlkcp
2026-08-12 21:36:04 -04:00
Claude d835e0d734 mattermost, asterisk: dynamic, collision-safe dedicated coturn ranges
Mattermost's own embedded-coturn fallback hardcoded the same relay range
(49153-49352) for every instance, with no per-instance offset -- running
two Mattermost instances without the shared coturn service (or one
alongside Asterisk's own dedicated coturn, now possible via the prior
commit) would silently reproduce the exact pre-merge collision bug this
repo's coturn history warns about, just among Mattermost instances
instead of Asterisk/Mattermost.

Adds find_free_coturn_range() (lib/common.sh, standalone-mode-stubbed in
both services matching the existing port_in_use/find_free_port
convention): a coturn relay range can't be collision-checked with live
socket scanning the way a single port can -- coturn only opens ports
inside its configured range on demand, so an idle range looks the same
as an unclaimed one to ss/netstat. The only reliable check is reading
what every other coturn-owning service's .env on the box actually
claims (COTURN_MAX_PORT for the shared instance, TURN_MAX_PORT for each
dedicated one) and picking a range starting safely past the highest.

Also adds Mattermost's own opt-out prompt for the shared coturn
preference, matching the one just added to Asterisk (fresh-install-only,
never re-asked on update, same as every other coturn-shape decision in
that file). An update now explicitly preserves its existing dedicated
range from .env rather than silently recomputing a new one.

Verified end-to-end: a shared instance + Asterisk's dedicated coturn +
two independent Mattermost instances, each discovering and avoiding
every range already claimed by the others, land on entirely
non-overlapping port blocks.
2026-08-13 00:55:31 +00:00
Claude 85fc172377 asterisk: avoid relay-port collision between dedicated and shared coturn
Answers "can Asterisk run its own coturn while Mattermost keeps using
the shared one" -- yes, but not safely until now: Asterisk's dedicated
coturn hardcoded relay ports 49152-49252, entirely inside the shared
instance's own default range (49152-49452). Running both on the same
box (now possible via the previous commit's opt-out prompt) recreated
the exact pre-merge collision this repo's coturn history warns about.

When a shared instance is present, read its actual configured
COTURN_MAX_PORT from ~/docker/coturn/.env and pick a dedicated range
starting safely past it, so the two can never overlap regardless of
what the shared instance was configured with. No shared instance on
the box means no collision risk, so the historical 49152-49252 default
is left untouched in that case.

Threaded the computed range through every place it was previously
hardcoded: the coturn container's own --min-port/--max-port, the UFW
rule, the DigitalOcean Cloud Firewall rule list, the non-DO firewall
reminder, and the generated README's port table. Verified the
range-shift arithmetic directly: a shared instance configured up to
49452 shifts the dedicated range to 49502-49602 (clear); no shared
instance leaves it at the original default.
2026-08-13 00:27:33 +00:00
Claude 3abc46d8a9 asterisk: add opt-out for shared coturn on fresh reinstall
ensure_coturn_user always preferred the shared coturn service with no
override once it was reachable -- there was no way to deliberately run
Asterisk's own dedicated coturn again short of stopping the shared
service outright (which would also break every other consumer, e.g.
Mattermost Calls). Useful for reproducing an older install's exact
shape when troubleshooting anything that might be specific to the
coturn-sharing path. Only offered when a shared instance actually
exists, and only reachable via an explicit fresh reinstall, matching
this repo's existing rule that coturn shape never changes silently on
an update.
2026-08-13 00:13:09 +00:00
Outis ca1b004798 Merge pull request #322 from outis1one/claude/extensions-qr-code-popup-6jlkcp
asterisk: mount Caddy's cert store regardless of coturn mode
2026-08-12 18:53:25 -04:00
Claude 65b7964411 asterisk: mount Caddy's cert store regardless of coturn mode
The read-only bind mount that lets Asterisk's entrypoint auto-sync a
real Let's Encrypt cert from Caddy (instead of falling back to
self-signed) was gated on USE_EMBEDDED_COTURN == true. That condition
conflated two unrelated things: Asterisk's own SIP transport-tls cert
(what this mount is actually for) and coturn's separate TURNS
capability (which the shared coturn service genuinely doesn't support,
but is irrelevant here).

Confirmed live: on a shared-coturn install with a real Caddy-issued
cert already sitting on disk for DOMAIN_NAME, Asterisk kept generating
a self-signed cert on every restart anyway, because /caddy-data was
never mounted into the container -- sync_caddy_cert() had no cert store
to find. Most SIP/TLS clients refuse a self-signed cert outright with
no clear error, which was the actual cause of a "port's open, cert
domain matches, registration still silently fails" case where every
other layer (firewall, coturn reachability, DNS, cert CN/SAN) had
already checked out clean.
2026-08-12 22:11:36 +00:00
Outis 32b2e1ced6 Merge pull request #321 from outis1one/claude/extensions-qr-code-popup-6jlkcp
Claude/extensions qr code popup 6jlkcp
2026-08-12 17:35:27 -04:00
Claude b76230b341 asterisk: regenerate self-signed TLS cert when DOMAIN_NAME changes
Vendor's entrypoint.sh only regenerates the self-signed cert if the file
is missing or lacks a SAN extension -- it never checks whether the SAN
actually matches the currently configured DOMAIN_NAME. Since
/etc/asterisk/certs is a bind-mounted host directory, neither an update
nor a full reinstall ever wipes it, so a domain entered once (even a
placeholder, or one later changed) sticks in the cert indefinitely.

Confirmed live: a box kept presenting a cert for a stale, originally-
entered domain long after DOMAIN_NAME had changed and a full reinstall
had run in between. Most SIP/TLS clients refuse a mismatched cert
outright with no clear error, which was the actual cause of a
"port's open but registration still fails" case -- firewall, coturn, and
DNS had all already checked out clean.

Patches the vendored entrypoint.sh (same guarded-sed pattern as the
existing logger.conf patch) to also regenerate when the existing cert's
SAN doesn't include the current DOMAIN_NAME. Verified against a scratch
copy: missing cert regenerates, a cert already matching the domain is
left alone, a mismatched domain now correctly regenerates and then
stabilizes.
2026-08-12 21:33:24 +00:00
Claude 893ab19759 asterisk: warn non-DO public installs about provider-side firewalls
FQDN mode was already available outside DigitalOcean detection (the
home/LAN path's "Networking mode" menu offers it), but only DO installs
got any reminder about a network-edge firewall sitting in front of the
box -- non-DO public VPS installs got no equivalent, and UFW being wide
open gives no signal that a separate provider-managed firewall exists at
all.

Confirmed live on an IONOS VPS: UFW allowed every SIP/TURN/RTP port,
Asterisk's own PJSIP logger showed zero incoming packets, and nothing in
the installer's own output pointed at the cause -- IONOS's own network
firewall (Cloud Panel -> Networking -> Firewall Policies) only allowed
22/80/443/8443/8447 and silently dropped the rest before it ever reached
the box.

Adds _asterisk_remind_non_do_firewall(), fired whenever a fresh install
sets a public FQDN without being in DO/droplet mode: same port list as
what UFW just opened, plus a pointer at the IONOS console location as a
concrete example other providers can generalize from.
2026-08-12 21:09:25 +00:00
Claude 2b926ebf0b security-dashboard: widen main container for the Extensions table
The previous "box too narrow" fix targeted the QR popup, but the actual
complaint (confirmed by screenshot) was the Extensions table itself --
ten columns (Ext/Name/Mobile/Status/Transport/PSTN/Whitelist/Messaging/
Voicemail/actions) forced .table-wrap's horizontal scrollbar even on a
normal desktop viewport because main was capped at 1180px. Bumped to
1600px; verified via headless render at 1280-1920px that the table no
longer overflows.
2026-08-12 20:32:33 +00:00
Outis d8a375f5db Merge pull request #320 from outis1one/claude/extensions-qr-code-popup-6jlkcp
security-dashboard: put TURN creds in the Sipnetic QR, widen the popup
2026-08-12 16:14:44 -04:00
Claude 5d0b6355b1 security-dashboard: put TURN creds in the Sipnetic QR, widen the popup
- ea_device_sipnetic_string() now sets Sipnetic's documented st= field to
  an explicit turn:user:pass@host:port URI built from the same
  TURN_SERVER/TURN_USERNAME/TURN_PASSWORD Asterisk itself reads from its
  .env (the shared VPS coturn on a droplet, or whichever coturn Asterisk
  is actually configured against). Previously the QR carried no TURN
  info at all, silently falling back to Sipnetic's own default STUN
  server instead -- registration/media then depends on whatever got
  typed in by hand instead of what Asterisk is actually using.
- Popup widened (192px content -> 320px card) and the QR rendered at 3x
  its displayed resolution (physical size unchanged): the longer
  TURN-inclusive account string needs a denser code, and verified via a
  headless render + OpenCV/pyzbar decode that the extra module density
  needs the resolution bump to stay reliably scannable.
- Restored (and expanded) the plain-text-credentials warning that was
  dropped when the box became a modal, now covering TURN creds too.
2026-08-12 20:10:10 +00:00
Outis a8757cddba Merge pull request #319 from outis1one/claude/extensions-qr-code-popup-6jlkcp
security-dashboard: add white quiet zone around Sipnetic QR code
2026-08-12 15:51:46 -04:00
Claude 779afcba62 security-dashboard: add white quiet zone around Sipnetic QR code
The QR popup's code was unreadable by real scanners: qrcodejs draws
modules edge-to-edge with no margin of its own, so the code sat directly
against the modal's dark background with no quiet zone. Verified with a
headless render + pyzbar/OpenCV decode that the raw generated image had
the code running to its edge and failed OpenCV's detector outright, while
wrapping it in a 20px white padded frame (still ~2in overall) fixed it.
2026-08-12 19:50:53 +00:00
Outis 73e6fd052f Merge pull request #318 from outis1one/claude/extensions-qr-code-popup-6jlkcp
security-dashboard: show extension Sipnetic QR code in a popup modal
2026-08-12 15:38:15 -04:00
Claude f0d34a0028 security-dashboard: show extension Sipnetic QR code in a popup modal
Converts the existing inline QR toggle on the Extensions tab's detail
panel into a small (2in square) modal popup with an X close button,
click-outside, and Escape-to-close, instead of an expanding inline box.
2026-08-12 19:33:55 +00:00
Outis 68e56803f1 Merge pull request #317 from outis1one/claude/ionos-script-integration-x32ofw
Claude/ionos script integration x32ofw
2026-08-12 09:11:45 -04:00
Claude aa65b5ef5b Make "Full reinstall" a real teardown for asterisk, mattermost, and coturn
Extends the security-dashboard prototype to the shared-coturn trio, since
these three are exactly the case that pattern was built for — a fresh
reinstall of any of them today just overwrote files in place without
stopping old containers first, and coturn's own fresh path never made an
informed choice about the consumer credentials/database it happens to
leave alone (safe today, but by omission rather than design).

- asterisk.sh / mattermost.sh: "Full reinstall" now stops the existing
  containers (`docker compose down`) before falling through to the normal
  install flow, and asks a single explicit question — delete stored data
  (PBX config/spool/voicemail for Asterisk; Postgres db/uploads/config/
  plugins for Mattermost) — defaulting to preserve. Their shared-coturn
  TURN credential is deliberately left alone either way (reused from
  cache via ensure_coturn_user(), same as update) — it's not this
  service's own data, and coturn already handles that continuity.
  Mattermost's existing "_db_has_data" check already reads the
  filesystem to decide whether to reuse or regenerate DB_PASS, so the
  wipe/preserve choice composes with that for free — no separate flag
  needed. Asterisk's warns to re-run pstn-trunk afterward if data is
  wiped, since that's what actually goes stale (its dialplan patch),
  not the fabricated "AMI secret" framing an earlier draft of this
  warning used before I checked the actual code.

- coturn.sh: "Full reinstall" now lists which consumers are currently
  registered (from users/*.env) and asks explicitly whether to also
  wipe TURN credentials and the user database, instead of silently
  preserving them as an unexamined side effect of never deleting the
  directory. Defaults to preserve. If the operator does choose to wipe,
  the running container is restarted afterward — it holds the old,
  now-deleted turndb file open, so new turnadmin writes to the fresh
  file would otherwise go unseen until a restart anyway. Every affected
  consumer already self-heals a missing credential on its own next
  Update run via ensure_coturn_user()'s existing cache-miss path — no
  changes needed there, just confirmed it covers this case.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-12 13:07:38 +00:00
Claude 7a1f09a0f7 Rename reinstall-mode prompt; make security-dashboard's "Full reinstall" a real teardown
Two-part change discussed and scoped in this session before touching
anything:

1. Rename "Reinstall in place" (r) -> "Update" (u) and "Full install" (f)
   -> "Full reinstall" everywhere the prompt appears: lib/common.sh's
   shared prompt_reinstall_mode(), plus the three services that carry
   their own duplicated standalone-stub copy of it for standalone
   execution (asterisk.sh, coturn.sh, wordpress.sh — per this repo's
   documented standalone-bootstrap pattern). Internal state values
   (update/fresh/cancel) are unchanged, so no other service's case
   statement needed touching. docs/anveo-direct-setup-guide.md's `r`
   reference updated to `u` to match. attic/asterisk-digital-ocean.sh
   deliberately left alone — this repo's own policy is to not backport
   fixes into attic/.

2. security-dashboard.sh's "Full reinstall" now does a real teardown
   before reinstalling — stops and removes the systemd unit, sudoers
   grant, Caddy site block, and secdash system user, then proceeds
   through the normal fresh-install flow — instead of just overwriting
   files in place while leaving the old service running underneath.
   Prototype for a pattern discussed for other services later: split the
   destructive question out explicitly ("also delete
   dashboard-admins.conf — per-admin extension scoping?", default n) so
   full reinstall doesn't silently discard state a plain "start over"
   request wouldn't expect to lose. Verified the backup/restore mechanics
   (mktemp, copy out before teardown, copy back after) against a mock
   under `set -u` for both the preserve and wipe paths before shipping.

Update mode was already the strongest existing example of surfacing
newer optional prompts (its "Reconfigure Caddy protection?" /
"Reconfigure per-admin scoping?" sub-prompts already cover every setting
fresh-install offers) — no changes needed there for this service.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-12 12:47:31 +00:00
Claude d55a7cc81a Reach the coturn self-heal check from asterisk.sh's update path too
Direct follow-up to the previous commit's ensure_coturn_user() fix: that
fix is useless for Asterisk specifically unless something actually calls
ensure_coturn_user("asterisk") again, and the update ("Reinstall in
place") branch returns 0 well before the fresh-install path's call to it
— only "Full install" reached it, which re-prompts everything (droplet
detection, domain, etc.) just to fix a credential re-registration.

Added the same call to the update path, gated on NOT having an embedded
coturn (checked via the existing _HAD_EMBEDDED_COTURN detection) — calling
it unconditionally would silently chain-install the shared coturn service
for a box deliberately running Asterisk's own dedicated coturn, exactly
the kind of silent update-time migration CLAUDE.md's coturn guidance
warns against. .env stays untouched either way (self-heal re-registers
with the same cached password, never generates a new one).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-12 11:44:02 +00:00
Claude f94f43786f Self-heal orphaned coturn credentials in ensure_coturn_user()
Answers a direct question from this session: no, reinstalling
asterisk/mattermost did NOT fix a coturn user missing from the live
database, because ensure_coturn_user() only ever calls turnadmin -a in
the else branch — reached only when the cache file (users/<consumer>.env)
is MISSING. A stale-but-present cache file (exactly what a coturn
container/volume recreation without preserving ./db leaves behind, per
this session's real diagnosis) looked identical to a healthy one and was
trusted blindly, so every consumer's installer kept silently reusing
credentials that no longer existed in coturn's database.

Now checks the cached username against coturn's actual live user list on
every call, and re-registers it with the same cached password if it's
missing — the same self-heal pattern this repo already applies elsewhere
(Beszel's compose patch, Vaultwarden's SMTP half-state, FMD's chown).
Re-uses the turnadmin -l log-noise filter from tools/coturn-test-check.sh
(a real "user[realm]" line never contains a space; at least one coturn
build writes its own startup log lines to stdout, not stderr, so a bare
2>/dev/null doesn't catch them).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-12 11:13:46 +00:00
Claude cd1d0e3b40 Filter turnadmin -l log noise before parsing usernames
Live run surfaced it: this coturn build writes its own startup log lines
("INFO SQLite connection was closed.", "INFO log file opened: ...") to
turnadmin -l's STDOUT, not stderr — 2>/dev/null never caught them, so
they got parsed as if they were usernames, producing nonsensical
"Database has user '2026-...INFO SQLite connection was closed.'" warnings
on a real run. A genuine "user[realm]" line never contains a space; every
log line does, so filtering on that is a simple, build-independent fix.

Also diagnosed the actual underlying failure this surfaced: coturn's live
user database was genuinely empty (both 'asterisk' and 'mattermost' had
cached credential files but neither was registered in the DB) — exactly
the container/volume-recreated-without-db drift this script's consumer
cross-check exists to catch, confirmed against a real run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-12 03:29:23 +00:00
Outis 6789b1152e Merge pull request #316 from outis1one/claude/ionos-script-integration-x32ofw
Distinguish timeout from real TURN test failure, bump timeout to 20s
2026-08-11 23:21:16 -04:00
Claude 8ee018ec10 Distinguish timeout from real TURN test failure, bump timeout to 20s
Latest live run showed the test getting killed by its own `timeout 10`
before turnutils_uclient printed any result — just two startup INFO
lines, no error. That's the coturn/coturn Docker image's turnutils_uclient
(apparently a newer build with structured "LEVEL component: message"
logging, different from the older packaged version available for local
testing) taking longer than 10s to complete, not a real failure.

Bumped both scripts' timeout to 20s, and now check for timeout(1)'s own
exit code (124) separately from a real reported error — reported as WARN
with a suggested manual command to re-run with more time and see the full
result, instead of lumping "still running" in with "actually failed."

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-12 02:32:33 +00:00
Outis d7c88de918 Merge pull request #315 from outis1one/claude/ionos-script-integration-x32ofw
Switch TURN test from -e <peer> to -y — real verification this time
2026-08-11 22:29:51 -04:00
Claude 1624b76a82 Switch TURN test from -e <peer> to -y — real verification this time
Not another guess: installed coturn locally (apt-get install coturn) and
ran the actual server + turnutils_uclient against it to verify this
before shipping, since the last two rounds shipped based on reading the
usage text alone and both turned out incomplete.

-e 127.0.0.1 satisfies turnutils_uclient's "-e or -y required" check, but
then fails allocation with "channel bind: error 403 (Forbidden IP)" —
services/coturn.sh never sets --allow-loopback-peers, so loopback as a
peer address is correctly rejected by a real coturn instance, and the
previous fix's own comment about "loopback is always reachable" missed
that reachable and permitted aren't the same thing.

-y ("client-to-client") sidesteps this: it negotiates both ends of a real
relay through the server itself, needs no separate peer address, and
works fine over loopback. Verified directly against a real local
instance: exits 0 with real packet-loss/RTT stats on valid credentials,
and correctly fails ("Cannot complete Allocation", exit 255) on a wrong
password — so it's still a meaningful pass/fail, not just "didn't crash."

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-12 02:28:22 +00:00
Outis c12d0d6cb1 Merge pull request #314 from outis1one/claude/ionos-script-integration-x32ofw
Fix "Either -e peer_address or -y must be specified" in TURN allocati…
2026-08-11 22:18:48 -04:00
Claude 9f4f779220 Fix "Either -e peer_address or -y must be specified" in TURN allocation test
Another real failure from a live run: turnutils_uclient refuses to run at
all without either -e <peer> or -y — a bare auth-only invocation isn't
enough for it to actually attempt anything. Add -e 127.0.0.1 to both
tools/pstn-test-check.sh's and tools/coturn-test-check.sh's invocations;
loopback is always reachable since the test already runs via `docker exec`
inside the coturn container itself, and it lets the test actually prove
data relays through the allocation, not just that auth succeeded.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-12 02:16:24 +00:00
Outis 5fff3f7c48 Merge pull request #313 from outis1one/codex/fix-drum-rhythm-game-audio-issues
drum-rhythm-game: inject runtime fixes for WebAudio, pad fallback, and song metadata
2026-08-11 21:59:36 -04:00
Claude 2ac00c946b Merge remote-tracking branch 'origin/main' into codex/fix-drum-rhythm-game-audio-issues
# Conflicts:
#	services/drum-rhythm-game.sh
2026-08-12 01:55:11 +00:00
Outis c16224f6d0 Add sourced nursery rhyme tune fixes 2026-08-11 20:59:55 -04:00
Outis 9d2a4291ee Add sourced classical tune fixes 2026-08-11 18:20:32 -04:00
Outis 8617b071a1 Merge pull request #311 from outis1one/codex/fix-drum-rhythm-game-audio-issues
drum-rhythm-game: inject post-install JS to fix WebAudio, pad audio fallback, and song metadata
2026-08-11 18:16:30 -04:00
Claude 6b199f1b22 Merge remote-tracking branch 'origin/main' into codex/fix-drum-rhythm-game-audio-issues
# Conflicts:
#	services/drum-rhythm-game.sh
2026-08-11 22:15:53 +00:00
Outis 93683e6bec Merge pull request #312 from outis1one/claude/ionos-script-integration-x32ofw
Fix false TURN allocation failure, add attention recap, one-at-a-time…
2026-08-11 18:10:27 -04:00
Outis d32014c3ea Add sourced sing-along tune fixes 2026-08-11 18:09:48 -04:00
Claude 5dbfcbd120 Fix false TURN allocation failure, add attention recap, one-at-a-time reprint
Real bug caught from a live run: the coturn allocation test passed -t -T
(TCP/TLS) to turnutils_uclient, but services/coturn.sh always starts
coturn with --no-tls --no-dtls — requesting an encrypted/TCP transport
against a server that never offered one fails the allocation outright
("Cannot complete Allocation"), misreporting a config problem that didn't
exist. Dropped both flags in both tools/pstn-test-check.sh and
tools/coturn-test-check.sh so the test matches what the server actually
supports (plain UDP).

Also, from user feedback on the same run:
- warn()/fail() now collect their messages into arrays; the Summary
  section prints a "Needs attention" recap of every FAIL/WARN together
  at the end, instead of leaving the user to scroll back through a long
  run to find what needs fixing.
- The softphone-setup block now offers to reprint itself one extension
  at a time (paced with a keypress between each) after the main run, so
  a long device list isn't lost in the scrollback either. Factored the
  per-extension print into print_ext_info() so the full run and this
  reprint can't drift apart. Guarded with `[ -t 0 ]` so it's skipped
  automatically when the script isn't run interactively.

Verified via a fuller mock harness (fake docker/curl/systemctl/getent,
non-TTY stdin) that: the corrected turnutils_uclient invocation reports
success, the recap correctly lists FAIL before WARN, and the interactive
reprint prompt is skipped without hanging when stdin isn't a terminal.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-11 20:53:23 +00:00
Outis a66b82beaa Merge pull request #310 from outis1one/claude/ionos-script-integration-x32ofw
Add provider-portal checklist with real values to the PSTN health check
2026-08-11 16:45:24 -04:00
Claude bb7aece023 Test Asterisk's own coturn config and print softphone setup info
Two follow-ups on the PSTN health check:

- New "coturn (TURN relay for Asterisk)" section reads Asterisk's own
  TURN_* values from its .env (not re-derived) and runs a live TURN
  allocation against whichever coturn Asterisk is actually configured to
  use — the shared instance, or its own embedded per-Asterisk coturn if
  that's what this box has (detected via the same "grep -q '^  coturn:'
  docker-compose.yml" check CLAUDE.md's migration guidance describes).
  Proves what Asterisk itself would use at call time, complementing
  tools/coturn-test-check.sh's broader multi-consumer check.
- New "Softphone setup" section parses pjsip.conf directly and prints
  per-extension SIP server/username/password/port/transport, plus TURN
  credentials for any extension with ice_support=yes — the same values
  Sipnetic's "Add Account" screen needs, computed here so a client isn't
  installed just to read them out of the Security Dashboard.

Also fixed a bug caught while building a mock test harness to verify both
additions: the extension-registration parser grabbed state via a fixed
field position ($3), silently truncating multi-word states like "Not in
use" down to "Not". Replaced with a regex that captures everything
between the extension and the trailing "N of inf" — verified against both
single- and multi-word states.

And a real syntax bug caught by bash -n before this ever shipped: an
apostrophe inside a ${VAR:-default} expansion ("this box's IP") opens an
unterminated single-quote context even inside double quotes — reworded
to avoid the apostrophe entirely rather than fight bash's parser.

Full mock run (fake docker/curl/systemctl/getent, real pjsip.conf/.env
fixtures matching the actual generated format) confirmed both new
sections and the registration fix all produce correct output end to end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-11 20:44:09 +00:00
Claude 5c18cfa364 Add SMS test reminder, "which box handled it" note, and a coturn health check
Three follow-ups from live testing on this session's actual VPS:

- tools/pstn-test-check.sh's SMS section printed the Forward-to-URL value
  to configure but never said what to do next — add the "text this DID,
  then watch journalctl -u sms-inbound -f" step right after it.
- docs/pstn-sms-test-checklist.md: the "which box actually handled this"
  question has a simple answer (a DID's inbound routing targets exactly
  one IP:port, so there's no ambiguity to resolve, only a portal setting
  to confirm) — written up so it doesn't need re-deriving. Also fixed the
  --list example to cd into the repo first; ./setup.sh is a relative path
  and silently fails with "command not found" from any other directory,
  confirmed live in this session.
- New tools/coturn-test-check.sh: health-checks the shared coturn instance
  (services/coturn.sh) and every consumer registered against it (Asterisk,
  any number of Mattermost instances) — container/identity, each cached
  consumer credential cross-checked against coturn's own live user
  database (catches the container/volume-recreated-without-db drift case),
  UFW rules for both the TURN port and the relay range, a capacity
  explanation reasoned from the actual port-range math instead of a guess,
  and a real TURN allocation test per consumer via turnutils_uclient —
  the only way to prove credentials + port range + firewall all actually
  work together, not just that each looks right in isolation. Deliberately
  does not attempt a concurrent load test, since that would consume real
  relay ports other services may be actively using.

Verified the turnadmin -l output parsing, UFW rule matching, and the
empty-array-under-set–u loop pattern against mock data before shipping.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-11 20:32:02 +00:00
Outis cb1bb9b11b Merge pull request #308 from outis1one/codex/fix-drum-rhythm-game-audio-issues
drum-rhythm-game: inject runtime fixes for Christmas songs, pad audio, and WebAudio resume
2026-08-11 16:29:59 -04:00
Outis 5a0eb84909 Merge pull request #309 from outis1one/claude/ionos-script-integration-x32ofw
Add provider-portal checklist with real values to the PSTN health check
2026-08-11 16:29:34 -04:00
Outis a28e9a5a1c Fix drum rhythm Christmas audio 2026-08-11 16:29:08 -04:00
Claude efb86fd92d Add provider-portal checklist with real values to the PSTN health check
Server-side config was fully verifiable already; what wasn't is the
provider-account side (Anveo's authorized-IP list, DID routing, SMS
forward-URL) since that lives entirely outside this box. Rather than
leave "go check the portal" as a vague pointer, compute and print the
exact values each portal field needs to match: this box's public IP,
the trunk DID (from .pstn-trunk.env), and the SMS forward URL read
straight from /opt/sms-inbound/settings.env (SMS_FORWARD_URL) instead of
making the user reconstruct or hunt for a value the installer already
generated and stored. Anveo-specific field-by-field checklist when
PROVIDER_NAME matches; generic fallback otherwise.

Verified the .pstn-trunk.env / settings.env sourcing against mock files
matching the real generated format, including the literal $[from]$-style
Anveo placeholders in SMS_FORWARD_URL, which must survive `source` under
`set -u` without triggering bash's legacy $[...] arithmetic expansion —
same guard pattern services/pstn-trunk.sh's own update path already uses.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-11 20:24:48 +00:00
Outis ab076f076e Merge pull request #307 from outis1one/claude/ionos-script-integration-x32ofw
Add automated PSTN/trunk/SMS health-check script
2026-08-11 16:08:43 -04:00
Claude 52fd75f679 Add automated PSTN/trunk/SMS health-check script
docs/pstn-sms-test-checklist.md's manual steps (registration, trunk
reachability, dialplan contexts, kill-switch state, usage-alert timer
health, recent call/message activity) are all things a script can check
directly instead of re-typed by hand each time — and re-typing them is
exactly what led to the container-name mistake in the prior commit.

tools/pstn-test-check.sh auto-detects the container/directory the same
way the checklist doc now does, runs every automatable check, and prints
PASS/WARN/FAIL per item plus a summary. What it can't cover — actually
placing a call or sending a text — still needs the checklist doc.

Caught during testing against real command output pasted in this
session: the endpoint-parsing loop matched pjsip's own column-header
line ("<Endpoint/CID...>  <State...>") as if it were a real endpoint row,
producing a bogus result. Fixed by skipping any row whose parsed
extension starts with "<".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
2026-08-11 20:07:19 +00:00