Files
vnctalk-prosody/PATCHES_AND_MODULES.md
T
Stefan-Sanger 2046867241 docs: add AGENTS.md, patch analysis, and pytest integration suite
- Add AGENTS.md with repo-specific conventions, build steps, and quirks
- Add PATCHES_AND_MODULES.md documenting every upstream deviation
- Add tests/ with pytest/slixmpp integration suite for core, MUC,
  vnctalk extensions, and infrastructure verification
- Include pytest.ini and .gitignore

Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
2026-07-15 17:58:02 +02:00

14 KiB

VNCtalk Prosody Patches & Custom Modules Analysis

This document describes how the VNCtalk Prosody distribution deviates from upstream Prosody 0.11.6. The deviations are delivered as:

  • patches/ — direct replacements for upstream core files
  • vnctalk/ — additional custom modules

Note: patches.list is stale and unused. The Dockerfile hard-copies each patch in a RUN cp … block.


1. Core Patches (patches/)

1.1 mod_mam.luamodules/mod_mam/mod_mam.lua

Purpose: Message Archive Management (XEP-0313) for 1:1 chats.

Key deviations from upstream:

  • vnc-rest-message hook support: Added vnc_message_handler(event) hooked on vnc-rest-message (priority 0). This allows the REST injection module (mod_http_rest) to archive messages that are injected via HTTP.
  • Store-user logic for REST: When vnc_rest is true, store_user is derived from orig_from instead of orig_to, so injected stanzas are archived under the sender's archive.
  • shall_store always returns true: The function shall_store(user, who) unconditionally returns true, bypassing any roster-based archive filtering.
  • Archives normal messages with body: The condition orig_type == "chat" or (orig_type == "normal" and stanza:get_child("body")) is kept but the implementation now also processes vnc-rest-message events.

Behavioural contract:

  • Any message injected via /rest (see mod_http_rest) must be archived under the sender's MAM archive.
  • MAM queries must return stanza-id elements and support RSM pagination.

1.2 mod_muc.luamodules/muc/mod_muc.lua

Purpose: MUC component loader and room lifecycle management.

Key deviations from upstream:

  • Custom unregister IQ hook: Added iq-set/bare/xmpp:vnctalk:unregister:queryhandle_unregister_iq. This exposes a VNCtalk-specific room-unregistration endpoint.
  • Debug helper: Added dumpTable(t, depth) utility function.
  • Room defaults: muc_room_default_public defaults to false (rooms are hidden by default).

1.3 mod_muc_mam.luamodules/mod_muc_mam.lua

Purpose: MUC Message Archive Management.

Key deviations from upstream:

  • with filter hardcoded to message<groupchat: The archive query and history loader both set with = "message<groupchat", meaning only groupchat messages are stored/retrieved; presence stanzas are ignored unless muc_log_presences is enabled.
  • Presence archiving option: If muc_log_presences is true, join/leave presences are archived with synthetic with values (presence / presence<unavailable).
  • MUC history from archive: The muc-get-history hook loads history from the archive backend when the in-memory _history buffer is insufficient, respecting max_history_messages.
  • Stanza-id stripping before broadcast: A muc-broadcast-message hook (priority 1) strips any stanza-id tags that claim to be from the room JID before the stanza is broadcast.
  • Occupant affiliation injection: When whois == "anyone", archived messages include an <x xmlns="http://jabber.org/protocol/muc#user"> item with the sender's affiliation and role.

1.4 mod_muc_unique.luamodules/mod_muc_unique.lua

Purpose: XEP-0307 Unique Room Names.

Key deviations from upstream:

  • Bare-JID request returns error: Added handle_iq_tobare that replies with item-not-found for IQ-get requests sent to a bare JID, rather than generating a unique name. Unique names are only served for host-targeted requests.

1.5 moduleapi.luacore/moduleapi.lua

Purpose: Module API base.

Key deviations from upstream:

  • Added open_host_store: New method api:open_host_store(host, name, store_type) allows a module to open a data store on behalf of a different host. This is heavily used by MUC FCM modules to read user private data / vCards from the main virtual host while running inside the MUC component.

1.6 muc.lib.luamodules/muc/muc.lib.lua

Purpose: MUC room implementation (the largest patch).

Key deviations from upstream:

  • Offline-affiliate broadcast (broadcast method): After broadcasting to all online occupants, the method iterates over _affiliations and sends the stanza to any affiliated JIDs that are not currently in the room, provided their domain is not hosted locally. This enables federation/remote-user delivery.
  • publicise_occupant_status skip for unavailable: When an occupant's role becomes nil (they left), the code logs "skipping unavailable presence" and does not route an unavailable presence to the leaving user themselves. This changes the standard XEP-0045 self-presence delivery.
  • Debug helpers: Added dumpTable and table_clone.

1.7 hidden.lib.luamodules/muc/hidden.lib.lua

Purpose: Room visibility (public/hidden).

Key deviations from upstream:

  • Restricted public rooms: If muc_room_allow_public is false (default in VNCtalk config), the public-room config option is hidden from non-admins, and only admins may create public rooms.

1.8 register.lib.luamodules/muc/register.lib.lua

Purpose: MUC room nickname registration.

Key deviations from upstream:

  • VNCtalk unregister handler (handle_unregister_iq): Fires vnc-muc-kick event and unconditionally removes the user's affiliation from _affiliations, bypassing normal affiliation-change logic. This is used for VNCtalk-specific account deletion/room cleanup.

1.9 mod_carbons.luamodules/mod_carbons.lua

Purpose: XEP-0280 Message Carbons.

Key deviations from upstream:

  • vnc-rest-message hook: Added module:hook("vnc-rest-message", c2s_message_handler, -0.5). Messages injected via the REST endpoint are carbon-copied to the user's other resources just like locally-sent messages.

1.10 mod_admin_telnet.luamodules/mod_admin_telnet.lua

Purpose: Admin telnet console.

Key deviations from upstream:

  • Listens on all interfaces: Changed interface from "127.0.0.1" to "*". The healthcheck script (healthcheck.sh) relies on connecting to 127.0.0.1:5582, but the console is now bound to 0.0.0.0.

1.11 portmanager.luacore/portmanager.lua

Purpose: Network port management.

Key deviations from upstream:

  • Respects network_default_read_size for socket mode: local default_mode = config.get("*", "network_default_read_size") or 4096; instead of a hardcoded 4096. The VNCtalk config sets this to 8192 to support larger stanzas (Jitsi/file transfers).

2. Custom Modules (vnctalk/)

2.1 Authentication

mod_auth_http_async

  • HTTP-based authentication. Replaces Prosody's internal password database with an async HTTP call to http_auth_url.
  • Sends a Basic auth header with base64(username@host:password).
  • If util.async is unavailable, falls back to synchronous socket.http / ssl.https.
  • user_exists always returns true; set_password / create_user / delete_user are no-ops.

2.2 Push Notifications (FCM)

mod_vnc_fcm

  • FCM push for 1:1 messages. Hooks on message/bare, pre-message/bare/full, and vnc-rest-message.
  • Reads FCM tokens from private storage (documents:stanza:io:jsonfcm element) and from a map store (fcmtoken).
  • Supports iOS and Android tokens; iOS gets a notification payload with sound, Android gets data-only.
  • Handles VNCtalk-specific extensions: vncTalkConference, whiteboard, attachment, read signals, Jitsi URL/room.
  • Removes invalid FCM tokens (NotRegistered, InvalidRegistration, etc.) from user private data automatically.
  • Inactive-device tracking: Uses csi-client-active/inactive hooks to avoid pushing to devices that are online but inactive.

mod_vnc_fcm_hin

  • Variant of mod_vnc_fcm with HIN (German health network) specific modifications.

mod_vnc_muc_fcm / mod_vnc_muc_fcm_hin

  • FCM push for MUC messages. Loaded on the MUC component.
  • Opens stores against storage_host (the main virtual host) using module:open_host_store.
  • On each groupchat message, iterates over room affiliations and pushes to offline/non-occupant members.

2.3 MUC Extensions

mod_vnc_muc_automember

  • Automatically grants member affiliation to anyone who receives a MUC invite, if they were previously unaffiliated.

mod_vnc_muc_hook

  • Sends a XMPP message notification (with http://vnc.biz/xmpp/muc#hook namespace) to online users who are affiliated with a room but not currently joined, when an "important" message (has non-empty body) is broadcast.
  • Optionally sends a mediated invite instead of a plain notification (muc_notification_invite).

mod_vnc_muc_data

  • Adds a muc#roomconfig_vdata config field and muc#roominfo_vdata disco info field.
  • On config change, broadcasts a groupchat message with <x xmlns='xmpp:vnctalk:update'/> containing JSON-encoded affiliations and room data.

mod_vnc_e2ehints

  • Adds a muc#roomconfig_e2e boolean field and muc#roominfo_e2e disco info field for end-to-end encryption hints.

mod_vnc_remotemucstore

  • Archives groupchat messages from remote MUCs (federation) into a local archive (muc_remote) so users can query history even for rooms not hosted locally.
  • Stores MUC presence metadata (real JID mapping, subject, nick) in a map store.
  • Prevents further processing of messages addressed to bare JIDs from remote MUCs (so they don't create offline message spam).

mod_vnc_remotemucinvite

  • Archives mediated MUC invitations sent to remote users in a store (muc_remote_inv).

mod_vnc_track_kicks

  • Tracks kick/ban events in MUC rooms (lightweight event logger).

2.4 vCard & Avatar

mod_vnc_vcard_avatar

  • On vCard update (IQ-set to self), extracts the PHOTO binval, decodes it, and uploads it via HTTP PUT to avatar_upload_url.
  • Uses Basic auth if avatar_upload_user / avatar_upload_pass are configured.
  • Stores an avatarupdate timestamp in a map store.

mod_vnc_vcard_fallback

  • Intercepts vCard IQ-get requests.
  • If the user has no vCard on file, auto-generates one from the username (dot-separated usernames become FIRSTNAME LASTNAME).
  • Adds ORGNAME if default_vcard_orgname is configured.

2.5 Messaging Extensions

mod_vnc_timestamp

  • Adds a custom <stamp xmlns='xmpp:vnctalk:stamp'/> tag to incoming chat/groupchat messages that have a body.
  • Sends an IQ-result back to the sender containing the timestamp and original stanza ID.

mod_vnc_receipts

  • Archives XEP-0184 delivery receipts (<received xmlns='urn:xmpp:receipts'/>) in an archive store (receipts).

mod_vnc_lastactivity

  • Extends XEP-0012 last activity with avatar hash caching.
  • Caches SHA1 of the user's vCard PHOTO in memory (avt_hash_table).
  • Stores/retrieves remote user activity and avatar updates in map stores.

mod_vnc_broadcast

  • Implements a vnc_broadcast component.
  • Accepts messages with <vncTalkBroadcast xmlns='xmpp:vnctalk'> and fans them out to:
    • Explicit to JIDs
    • Roster groups (via roster_manager.load_roster)
    • MUC room affiliations (if a to JID is a MUC)

2.6 HTTP / REST

mod_http_rest

  • Exposes POST /rest on the HTTP server.
  • Accepts Content-Type: text/xml bodies, parses them as XMPP stanzas, and injects them into the server via module:fire_event("vnc-rest-message", …).
  • Returns 201 on success, 415 for wrong content type, 422 for unparseable body.

mod_http_upload_external

  • Delegates HTTP file upload to an external PHP endpoint (share.php).
  • Generates signed URLs using http_upload_external_secret.

2.7 Utility Modules

Module Purpose
mod_alias Allows users to have alias JIDs
mod_roster_command Ad-hoc commands for roster management
mod_s2s_keepalive Keepalive pings for S2S connections (Jitsi compatibility)
mod_smacks / mod_smacks_offline Stream Management (XEP-0198) with offline queue support
mod_carbons_copies Helper for carbons routing
mod_log_slow_events Logs events exceeding log_slow_events_threshold
mod_discoitems Manual override of disco items for a host
mod_filter_chatstates Drops chat-state notifications under certain conditions
mod_idlecompat Compatibility shim for idle detection
mod_http_altconnect Alternative connection methods discovery
mod_http_index Static HTTP index page
mod_webpresence Publish presence as web images/icons
mod_auto_accept_subscriptions Auto-accept presence subscriptions
mod_vcard_muc vCard support for MUC rooms
mod_vnc_delfile File deletion command via XMPP

3. Configuration-Driven Behaviour

Several behaviours are not hard-coded in Lua but emerge from config/prosody.cfg.lua.template:

Config Option Effect
authentication = "http_async" All client auth goes to hybridaAuthUrl
default_archive_policy = "roster" MAM archives only roster contacts by default
muc_log_by_default = true / muc_log_all_rooms = true All MUC messages archived
c2s_require_encryption = true Plaintext C2S rejected
keepalive_servers = { "${DEFAULT_JITSI_CONFERENCE}" } S2S keepalive pings sent to Jitsi
component_secret = "…" Fixed MUC component secret (not randomly generated)

4. Healthcheck & Runtime Quirks

  • healthcheck.sh exits 2 (not 1) when the TLS cert in /etc/tls-update/tls.crt differs from the loaded one. Orchestrators must treat exit code 2 as a restart signal.
  • startup-sidecar.sh runs a Node http-server on port 8080 for static files; this is not the XMPP server.
  • startup.sh generates fallback certs from base64 defaults if prosodySSLkey/cert env vars are missing.