| Ext | Function |
|---|---|
| 0 | Operator intercept → 2600 |
| 0100 | Milliwatt tone (1004 Hz) |
| 0101 | Echo test |
| 0102 | Speaking clock (Eastern) |
| 0103 | Intercept / SIT tone |
| 0200 | Open conference bridge |
| 0201 | Private conference (PIN) |
| 0300 | MOTD / info line |
| 0301 | Da Planet Security info |
| 1000 | Radio stream — klaxon/icecast2 |
| 2600 | Operator handset |
NANP format: PREFIX-XXXX — PhreakNet prefix replaces 555 in v5.0
| Version | Summary |
|---|---|
| v4.1.25 | Core sounds in image; speaking clock confirmed; sounds in separate RUN layer |
| v4.1.24 | musiconhold.conf [general] section fix; SayUnixTime format |
| v4.1.23 | 4-digit NANP dialplan; confbridge; from-phreaknet stub |
| v4.1.22 | DRY firewall helpers; ufw and pf support |
| v4.1.21 | firewalld automation in preflight |
| v4.1.20 | Playback prompts; core sounds; echo test (1010) |
| v4.1.19 | SIP registration — AOR naming fix; Sangoma asterisk22; firewalld |
| v4.1.17 | MOH via HAProxy HTTPS → klaxon.dapla.net validated |
| v4.1.14 | Golden image; coturn STUN/TURN sidecar; host networking |
| v4.1.11 | Identity: POSIX useradd replaces homectl |
| v4.1 | Initial release — ZFS + Quadlets + PJSIP + HAProxy + MOH |
Full history in CHANGELOG.md
# Download and install curl -fsSL https://denzuko.github.io/pbx-quadlet-setup/pbx_setup.sh -o pbx_setup.sh # Review before running less pbx_setup.sh sudo bash pbx_setup.sh
| Rule | Description | Status |
|---|---|---|
| R01 | PJSIP section naming + Dial() consistency | PASS |
| R02 | POSIX useradd/groupadd for service accounts | PASS |
| R03 | machinectl shell for user unit activation | PASS |
| R04 | Podman image store visibility | PASS |
| R05 | Config chmod 640 after heredoc | PASS |
| R06 | No TLS inside container — HAProxy handles termination | PASS |
| R07 | FFmpeg reconnect flags for MOH stream | PASS |
| R08 | RTP port range consistency rtp.conf ↔ Quadlet | PASS |
| R09 | podman build explicit context path | PASS |
| R10 | Home path set explicitly via useradd --home-dir | PASS |
| R11 | Credentials to /dev/shm via mktemp | PASS |
| R12 | Config paths audited against Quadlet Volume mounts | PASS |
| R13 | Quadlet .build units for image lifecycle | PASS |
| R14 | klaxon.dapla.net via HAProxy for co-located services | PASS |
| R15 | PUBLIC_IP from keepalived VIP — never hardcoded | PASS |
| R16 | All ephemeral secrets via mktemp /dev/shm | PASS |
Use this prompt when submitting the script to an LLM for peer review:
You are a senior infrastructure engineer reviewing a bash deployment script.
Review pbx_setup.sh against these rules and flag any violations:
R01: PJSIP endpoint names match Dial() targets in extensions.conf
R02: Service account uses POSIX useradd/groupadd — not homectl or userdb
R03: machinectl shell used for user unit activation — not su or runuser
R04: Podman image built in user context — visible to pbxadmin only
R05: All config files chmod 640 after heredoc generation
R06: No TLS config inside container — HAProxy terminates externally
R07: FFmpeg has -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5
R08: RTP port range consistent between rtp.conf and Quadlet PublishPort
R09: podman build includes explicit context path argument
R10: Home directory set via useradd --home-dir, created with mkdir + chown
R11: Credentials written only to mktemp /dev/shm path — never stdout
R12: All config paths exist within Quadlet Volume= mount points
R13: Image lifecycle via .build Quadlet unit — no bare podman build calls
R14: MOH stream uses HTTPS URL via HAProxy — not host.containers.internal
R15: PUBLIC_IP retrieved from keepalived VIP via ip(1) — never hardcoded
R16: All ephemeral secrets use mktemp /dev/shm — never /tmp or /root
Output: list each rule, PASS or FAIL, and a one-line reason.