Staff Chatfor WHMCS · Documentation
v2.0.0

How It Works

AJAX polling, encrypted storage and safe previews.


Delivery

The widget polls the module's own admin controller through WHMCS. No WebSocket server, daemon, extra port or reverse-proxy rule is involved.

admin/addonmodules.php?module=StaffChat&p=chat&a=history
admin/addonmodules.php?module=StaffChat&p=chat&a=poll
admin/addonmodules.php?module=StaffChat&p=chat&a=send
admin/addonmodules.php?module=StaffChat&p=chat&a=read
admin/addonmodules.php?module=StaffChat&p=chat&a=active

All chat actions are POST-only, CSRF-protected and require X-Requested-With: XMLHttpRequest. A plain navigation request returns 404 {"ok":false,"error":"Not found"}.

Polling and presence

  • The client polls for messages newer than its cursor, so each request only returns new data.
  • Presence is stored with a TTL and refreshed on every poll; expired admins drop off the online list.
  • Unread counters are persisted server-side per admin and room, so they survive a refresh.
  • Typing state is short-lived and pruned automatically.
  • The polling interval is configurable from 1 to 60 seconds.

Storage files

  • data/history.json — encrypted rooms, messages, edits and reactions.
  • data/attachments.json — encrypted attachment metadata.
  • data/uploads/*.enc — encrypted attachment files.
  • data/unread.json — per-admin unread counters.
  • data/presence.json — online and presence state.
  • data/typing.json — typing state.
  • data/previews.json — cached link previews.
  • data/analytics-cache.json — encrypted dashboard aggregates, invalidated by the history and attachment signatures.
  • data/rate-limits.json — encrypted per-admin send, preview and upload counters, shared across sessions.

The whole data/ directory is denied over HTTP by the bundled .htaccess. The settings and activity log use the hsc_staffchat_settings and hsc_staffchat_log tables.

Encryption

History, attachment metadata, previews, presence and typing data are stored with the WHMCS encrypt()/decrypt() helpers. Attachment bytes are encrypted at rest and decrypted only inside the authenticated download endpoint; no plaintext copy is written to disk.

Attachments

  • One attachment per message, uploaded over an authenticated POST with a CSRF token.
  • MIME type is checked against the allowlist; images are additionally validated with getimagesize().
  • Files are stored under random names with an .enc extension; the original name is kept only for display.
  • Downloads require an authenticated request and are streamed with the correct headers.
  • Orphaned files are pruned by the daily cron.

Link previews

  • Built server-side and rendered as text-only title and description.
  • Only HTTP and HTTPS URLs, and never URLs containing credentials.
  • Private, loopback, link-local and reserved IP ranges are rejected.
  • Redirects are not followed; the resolved IP is pinned for the request.
  • A 3-second timeout, a 512 KB response limit and a one-day cache apply.

Notifications

Desktop notifications use a service worker when the browser supports it and fall back to new Notification() otherwise. The worker is registered under the module asset scope and only handles notification clicks; it does not cache pages or intercept chat traffic.

Cron

  • Activity log retention and row-cap enforcement.
  • Chat history retention and per-room trimming.
  • Live-only history wipe.
  • Attachment encryption migration and orphan pruning.

Performance

  • Polling returns only messages newer than the client cursor.
  • History files are read and written under a lock to avoid partial writes.
  • Link previews are cached so repeated URLs do not trigger new outbound requests.
  • For large teams, raise the polling interval and lower the per-room message cap.