This runbook is for a single noncritical FreePBX 17 PBX after the Docker smoke suite has passed. It does not authorize a production configuration change. The watcher and module are observers: they must not call Apply Changes, restart Asterisk, or modify FreePBX configuration.
The watcher installs request attribution by default. It is intentionally much smaller than a general web audit logger: successful authenticated FreePBX write requests append only the account name, timestamp, page/module/action, method, and status to a bounded local JSONL file. Form values, cookies, sessions, headers, and credentials are never recorded. There is no additional full database scan for attribution itself: the watcher uses a lightweight five-second reload/event probe and performs its bounded full observation on a transition, an event, or at most once every 30 seconds while idle. The separate module-file tree digest runs every five minutes and is pulled forward when authenticated request metadata indicates Module Admin activity.
Treat likely staged by and possible actors as investigative leads, not proof. A shared or stolen account, concurrent admins, CLI, API, automation, direct database change, or custom PHP path can defeat exact attribution. The state diff remains the authoritative evidence of what the bounded watcher saw.
scripts/package-module.sh,
and run ./docker/validate-module-archive.sh dist/pendingchanges-<version>.tgz.SELECT on the
FreePBX configuration database. It must not have write, DDL, or reload
permissions./etc/asterisk, FreePBX’s configured AMPWEBROOT/admin/modules, and (when enabled)
/var/lib/asterisk/astdb.sqlite3 are mounted/readable
read-only by the watcher. Its state directory is writable only by its own
service account.Stop the watcher and remove the module through Module Admin. Preserve the final status document and baseline only if local retention policy permits. This rollback changes neither generated Asterisk configuration nor PBX call handling.
Treat a clean report as “no drift detected in the named coverage,” never as
proof that no PBX state changed. The watcher reads only its explicit database
table allowlist, generated Asterisk files, module tree digests, and these
explicit AstDB families: AMPUSER, DEVICE, CF, CFB, CFU, CFNA,
DND, CW, FOLLOWME, and BLKVM. Other AstDB data, arbitrary custom
modules, and runtime state are out of scope unless deliberately added and
smoke-tested.
The database allowlist includes FreePBX’s modules activation records, so an
enable or disable operation is reported by module name. Module file digests
remain a separate signal for installed code changes; cached module-signature
verification metadata is not treated as pending configuration.
The observer’s own pendingchanges module record is excluded as module-owned
state; other module enable/disable/version records remain covered.
User Management coverage includes the bounded userman_users and
userman_users_settings tables. The latter is joined to the username for a
readable local report; secret-looking setting values are redacted, and the
public-alpha feedback export retains only source/count/field-name metadata.
Fax Configuration coverage includes the bounded fax_details settings table,
including the concurrent fax channel limit. Fax store/history records are not
configuration evidence and remain outside the allowlist.
If the health card reports delayed, stale, invalid, unreadable, unconfigured, or not installed, the full watcher result is not current. The module may show last-known or framework-only evidence to aid diagnosis, but it deliberately refuses to describe an empty degraded result as clean.