How to install Prosody with Tiki as its identity provider
Prosody is a lightweight and modular XMPP server.
This guide describes how to configure Prosody so that Tiki acts as its identity provider for:
- Registered Tiki users
- Persistent anonymous website visitors
- Password and temporary-token authentication
- Community group chats
- Personalized direct support
- Tiki group-to-XMPP room mapping
- MUC room creation and affiliation synchronization
- Browser chat through Converse.js
- Native XMPP clients such as Gajim and Monal
- Mobile push notifications through XEP-0357
The reference deployment described here uses:
- Prosody 0.12
- Lua 5.4
- Apache with PHP-FPM
- Tiki with persistent guest XMPP identities
- Converse.js
- Let's Encrypt certificates
- firewalld
- Monal or another XEP-0357-compatible mobile client
Never publish:
- The Tiki/Prosody shared secret
- Administrator passwords
- Guest passwords
- Temporary XMPP tokens
- Database credentials
- Private TLS keys
- Push notification device identifiers
- Authentication URLs containing credentials
Replace every example hostname, path, username and credential before deployment.
The values in this guide are examples only.
This guide requires a Tiki version containing all of the following components:
-
tiki-xmpp-auth.php -
tiki-xmpp-guest-auth.php - The
tiki_xmpp_guest_credentialstable - Persistent browser identities stored under
tiki-xmpp-guest-identity -
TIKI_SKIP_HTTP_BASIC_AUTHhandling for Prosody endpoints - The corresponding Converse.js integration
- XMPP room and group synchronization support
- Anonymous destination selection between community and private support
- Support-presence integration when online or last-seen information is required
A server-only Prosody configuration cannot provide features that are missing from Tiki.
1. Functional overview
The intended user scenarios are:
- Alice, a website visitor: Tiki creates a persistent guest XMPP identity in her browser. She can return later with the same identity and conversation history.
- Bob, a registered user: Tiki authenticates him to Prosody and may associate him with the Registered users room.
- Charlie, a translator: membership in the Translators Tiki group gives him access to the corresponding XMPP room.
- Dan, an external XMPP user: he may use an external JID when federation, transport and room permissions allow it.
- Eve, a support agent: she can answer visitors through Converse.js, Gajim, Monal or another XMPP client.
- An anonymous visitor choosing community: the visitor joins the configured community room.
- An anonymous visitor choosing private support: the visitor opens a direct conversation with the configured support JID.
Presence is aggregated across XMPP resources. A support agent connected through Gajim, Monal or Converse.js may therefore appear online even when one of the other clients is closed.
2. Deployment status
| Feature | Status | ||
| Prosody HTTP authentication | Production baseline | ||
| Registered-user existence lookup | Production baseline | ||
| Temporary Tiki XMPP tokens | Production baseline | ||
| Persistent guest authentication | Production baseline | ||
| Guest password hashing and verification | Production baseline | ||
| TLS for native XMPP clients | Production baseline | ||
| HTTPS BOSH | Production baseline | ||
| Secure WebSocket | Production baseline | ||
| Let's Encrypt import and renewal | Production baseline | ||
| Community MUC rooms | Production baseline | ||
| Personalized direct support | Production baseline | ||
| Anonymous destination selection | Production baseline | ||
| Multi-resource support presence | Production baseline with custom presence modules | ||
| Mobile push notifications | Production baseline with mod_cloud_notify
| ||
| Group-to-room synchronization | Requires administrator credentials and site-specific testing | ||
| External XMPP identities | Requires site-specific testing | ||
| Public XMPP federation | Optional; requires DNS, firewall and remote testing | ||
3. Production architecture
The recommended architecture uses four logical roles:
| Role | Example hostname | ||
| Tiki website | tiki.example.org
| ||
| Registered XMPP users | xmpp.example.org
| ||
| Persistent website guests | guest.example.org
| ||
| Multi-user chat component | conference.example.org | ||
The Tiki website and registered-user XMPP VirtualHost may share the same hostname in a compact deployment. The guest VirtualHost and MUC component must still use distinct XMPP domains.
Prosody exposes:
| Purpose | Port | Exposure | |
| Native XMPP clients | 5222/tcp | Public when native clients are supported | |
| XMPP server-to-server communication | 5269/tcp | Required for federation and some external push gateways | |
| Unencrypted Prosody HTTP | 5280/tcp | Private only | |
| HTTPS BOSH and WebSocket | 5281/tcp | Public for direct browser access | |
Port 5280 must not be publicly reachable.
Where possible, bind unencrypted HTTP to loopback or protect it with the firewall.
4. Preparation
Confirm that:
- Tiki is installed at
https://tiki.example.org. - Tiki manages its users, groups, permissions and passwords.
- The required Tiki XMPP implementation is present.
- Apache and PHP-FPM work correctly.
- Root or sudo access is available.
- DNS records can be changed.
- A real Tiki administrator or support account exists.
- Ports
5222,5269when required and5281can be opened. - The system clock and timezone are correct.
- Outbound DNS and XMPP S2S connections are allowed.
LDAP, Shibboleth, Active Directory and other external authentication systems require additional testing.
5. DNS configuration
Create A and/or AAAA records for:
-
tiki.example.org -
xmpp.example.org -
guest.example.org -
conference.example.org
For native clients:
_xmpp-client._tcp.xmpp.example.org. 3600 IN SRV 0 5 5222 xmpp.example.org.
For federation or external push-gateway communication:
_xmpp-server._tcp.xmpp.example.org. 3600 IN SRV 0 5 5269 xmpp.example.org.
Verify:
dig A tiki.example.org dig A xmpp.example.org dig A guest.example.org dig A conference.example.org dig SRV _xmpp-client._tcp.xmpp.example.org dig SRV _xmpp-server._tcp.xmpp.example.org
All enabled XMPP hosts must resolve correctly.
6. Install Prosody
On Debian or Ubuntu:
sudo apt update sudo apt install -y \ prosody \ prosody-modules \ lua-sec \ certbot \ curl
Verify:
sudo prosodyctl about sudo prosodyctl check config sudo systemctl enable prosody
This guide targets Prosody 0.12 with Lua 5.4.
Prosody 13 includes mod_cloud_notify natively. Before upgrading, test:
-
mod_auth_http - Tiki authentication endpoints
- Persistent guests
- Custom presence modules
- MAM
- Push notifications
- Converse.js
- Native clients
Use a currently supported Prosody maintenance release whenever possible.
7. Install required Prosody modules
The deployment requires:
-
mod_auth_http -
mod_guest_presence_autoapprove -
mod_support_presence
The Debian prosody-modules package supplies mod_cloud_notify for Prosody 0.12.
First locate the modules:
find /usr/lib/prosody /usr/local/lib/prosody \ \( -name "mod_auth_http.lua" \ -o -name "mod_cloud_notify.lua" \ -o -name "mod_guest_presence_autoapprove.lua" \ -o -name "mod_support_presence.lua" \) \ -type f 2>/dev/null
Site-specific modules may be installed in:
/usr/local/lib/prosody/modules
Configure:
plugin_paths = { "/usr/local/lib/prosody/modules"; }
Production deployments must:
- Pin reviewed module revisions.
- Record checksums in deployment automation.
- Avoid downloading an unreviewed moving
tiprevision automatically. - Test module compatibility after every Prosody update.
- Install modules with root ownership and mode
0644.
Some versions of mod_auth_http send check_password parameters using an HTTP GET query string.
This can expose passwords in web-server access logs even when HTTPS protects the network connection.
A production integration should prefer POST for password-bearing requests. If compatibility requires GET, explicitly redact the pass parameter from Apache, proxy and application logs.
8. Obtain TLS certificates
The certificate must cover:
-
xmpp.example.org -
guest.example.org -
conference.example.org
Example:
sudo certbot certonly \ --webroot \ -w /var/www/tiki \ --cert-name tiki-xmpp-prosody \ -d xmpp.example.org \ -d guest.example.org \ -d conference.example.org
The Apache VirtualHost serving the ACME challenge must accept all names.
Example:
ServerName tiki.example.org ServerAlias xmpp.example.org ServerAlias guest.example.org ServerAlias conference.example.org
Validate:
sudo apache2ctl configtest sudo systemctl reload apache2 sudo openssl x509 \ -in /etc/letsencrypt/live/tiki-xmpp-prosody/fullchain.pem \ -noout \ -dates \ -ext subjectAltName
Every Prosody host must appear in the certificate SAN list.
9. Import certificates into Prosody
Import the certificate:
sudo prosodyctl --root cert import \ /etc/letsencrypt/live/tiki-xmpp-prosody
Verify:
sudo ls -l /etc/prosody/certs sudo prosodyctl check certs
A single SAN certificate may be stored under the primary XMPP hostname while covering the guest and conference domains.
Private keys must remain readable only by root and Prosody.
10. Automatic certificate renewal
Create:
/etc/letsencrypt/renewal-hooks/deploy/reload-prosody.sh
Content:
#!/bin/sh set -eu /usr/bin/prosodyctl --root cert import /etc/letsencrypt/live/tiki-xmpp-prosody /usr/bin/systemctl reload prosody || /usr/bin/systemctl restart prosody
Set permissions:
sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-prosody.sh
Test renewal:
sudo certbot renew --cert-name tiki-xmpp-prosody --dry-run
Adapt the certificate name when necessary.
Some Certbot versions support , but not all versions do. A plain run-deploy-hooksdry-run does not verify that deployment hooks are executed.
After renewal, verify the certificate actually served by Prosody on port 5222.
11. Shared HTTP authentication secret
Generate a secret:
sudo sh -c \ 'openssl rand -base64 48 | tr -d "/=+\n" | cut -c1-40 > /etc/prosody/xmpp_http_secret' sudo chown root:prosody \ /etc/prosody/xmpp_http_secret sudo chmod 640 \ /etc/prosody/xmpp_http_secret
The same value must be configured in:
- The registered-user Prosody VirtualHost
- The persistent-guest Prosody VirtualHost
- The Tiki
xmpp_shared_secretpreference
Example:
http_auth_credentials = "prosody:REPLACE_WITH_THE_SHARED_SECRET"
Do not add a separate http_auth_user when http_auth_credentials already contains the API username and secret.
Protect /etc/prosody/prosody.cfg.lua if it contains the secret directly. A protected, deployment-specific included file may also be used for secrets.
Never place the secret in version control or public documentation.
12. Persistent guest table
The persistent guest endpoint requires:
CREATE TABLE IF NOT EXISTS `tiki_xmpp_guest_credentials` ( `username` varchar(64) NOT NULL, `password_hash` varchar(255) NOT NULL, `created` int(11) NOT NULL, `last_seen` int(11) NOT NULL, PRIMARY KEY (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
Apply schema changes through the normal Tiki installer or upgrade mechanism.
Verify:
SHOW CREATE TABLE tiki_xmpp_guest_credentials;
Guest passwords must be stored with password_hash() and verified with password_verify(). Never store them in plaintext.
Define a retention policy for obsolete guest credentials according to the site's privacy requirements.
13. Tiki authentication endpoints
Tiki requires:
| Endpoint | Purpose | ||
tiki-xmpp-auth.php | Registered users and temporary XMPP tokens | ||
tiki-xmpp-guest-auth.php | Persistent website guest identities | ||
Prosody uses:
-
/user_exists -
/check_password
The endpoints must:
- Require HTTP Basic Authentication.
- Accept the API username
prosody. - Verify
xmpp_shared_secret. - Reject an empty shared secret.
- Return plain-text
trueorfalse. - Return HTTP
403for invalid API authentication. - Never expose supplied passwords in application logs.
- Use constant-time secret comparison where available.
- Accept password-bearing requests through POST in the recommended implementation.
- Retain GET compatibility only when required by the deployed
mod_auth_httpversion.
The registered endpoint checks:
- A temporary Tiki XMPP token.
- The current Tiki password when password fallback is intentionally enabled.
The guest endpoint accepts usernames matching:
guest-[20 lowercase hexadecimal characters]
Guest passwords contain:
48 lowercase hexadecimal characters
The first valid guest /check_password request creates the hashed guest record. Later requests use password_verify().
The guest /user_exists method may return true for a syntactically valid new identity before its first password check.
14. PHP-FPM Basic Auth handling
Apache and PHP-FPM may expose the Prosody API credentials as normal website credentials.
Without endpoint-specific handling, Tiki may redirect the request to:
tiki-login.php?challenge=1
Each Prosody endpoint must capture the Authorization header before loading Tiki:
$prosodyAuthorization = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['Authorization'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? ''; define('TIKI_SKIP_HTTP_BASIC_AUTH', true); unset( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'], $_SERVER['AUTH_TYPE'], $_SERVER['HTTP_AUTHORIZATION'], $_SERVER['Authorization'], $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ); require_once __DIR__ . '/tiki-setup.php';
The endpoint must validate the saved $prosodyAuthorization value separately.
The Tiki bootstrap must respect TIKI_SKIP_HTTP_BASIC_AUTH when:
- Copying
REDIRECT_HTTP_AUTHORIZATION - Reading
apache_request_headers() - Performing normal website HTTP Basic Authentication
This behavior must be restricted to the Prosody endpoints.
After changing authentication code:
cd /var/www/tiki php console.php cache:clear sudo systemctl reload php8.2-fpm
Use the correct PHP-FPM service name for the deployment.
15. Apache Authorization forwarding
First test whether the Authorization header reaches PHP. Do not add forwarding directives unnecessarily.
When required:
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 CGIPassAuth On <Directory /var/www/tiki> Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch Require all granted SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 CGIPassAuth On </Directory>
Validate:
sudo apache2ctl configtest sudo systemctl reload apache2
Configure Apache logging so that password-bearing query parameters are not recorded.
16. Complete Prosody configuration
Edit:
/etc/prosody/prosody.cfg.lua
Reference configuration:
plugin_paths = { "/usr/local/lib/prosody/modules"; } admins = { "admin@xmpp.example.org"; } modules_enabled = { "disco"; "roster"; "saslauth"; "tls"; "blocklist"; "bookmarks"; "carbons"; "dialback"; "limits"; "pep"; "private"; "smacks"; "vcard4"; "vcard_legacy"; "csi_simple"; "ping"; "time"; "uptime"; "version"; "mam"; "lastactivity"; "admin_adhoc"; "admin_shell"; "bosh"; "websocket"; "support_presence"; "cloud_notify"; } pidfile = "/run/prosody/prosody.pid"; s2s_secure_auth = true s2s_require_encryption = true archive_expires_after = "never" limits = { c2s = { rate = "10kb/s"; }; s2sin = { rate = "30kb/s"; }; } log = { info = "/var/log/prosody/prosody.log"; error = "/var/log/prosody/prosody.err"; { levels = { "error" }; to = "syslog"; }; } interfaces = { "0.0.0.0"; } http_interfaces = { "0.0.0.0"; } https_interfaces = { "0.0.0.0"; } c2s_interfaces = { "0.0.0.0"; } s2s_interfaces = { "0.0.0.0"; } http_ports = { 5280; } https_ports = { 5281; } consider_websocket_secure = true https_ssl = { key = "/etc/prosody/certs/xmpp.example.org.key"; certificate = "/etc/prosody/certs/xmpp.example.org.crt"; } VirtualHost "xmpp.example.org" ssl = { key = "/etc/prosody/certs/xmpp.example.org.key"; certificate = "/etc/prosody/certs/xmpp.example.org.crt"; } authentication = "http" http_auth_url = "https://tiki.example.org/tiki-xmpp-auth.php" http_auth_credentials = "prosody:REPLACE_WITH_THE_SHARED_SECRET" c2s_require_encryption = true allow_unencrypted_plain_auth = false sasl_mech_list = { "PLAIN"; } disco_items = { { "conference.example.org"; }; } support_jid = "admin@xmpp.example.org" VirtualHost "guest.example.org" ssl = { key = "/etc/prosody/certs/xmpp.example.org.key"; certificate = "/etc/prosody/certs/xmpp.example.org.crt"; } authentication = "http" http_auth_url = "https://tiki.example.org/tiki-xmpp-guest-auth.php" http_auth_credentials = "prosody:REPLACE_WITH_THE_SHARED_SECRET" c2s_require_encryption = true allow_unencrypted_plain_auth = false sasl_mech_list = { "PLAIN"; } allow_registration = false disco_items = { { "conference.example.org"; }; } modules_enabled = { "guest_presence_autoapprove"; } presence_requester_jid = "admin@xmpp.example.org" Component "conference.example.org" "muc" ssl = { key = "/etc/prosody/certs/xmpp.example.org.key"; certificate = "/etc/prosody/certs/xmpp.example.org.crt"; } restrict_room_creation = false modules_enabled = { "muc_mam"; } muc_room_default_persistent = true muc_room_default_public = true muc_room_default_members_only = false muc_room_default_allow_anonymous = true muc_room_default_history_length = 20 muc_max_history_messages = 100 muc_tombstones = true Include "conf.d/*.cfg.lua"
The history settings have different purposes:
-
archive_expires_after = "never"keeps MAM archives indefinitely. -
muc_room_default_history_length = 20limits the initial history delivered when joining a room. -
muc_max_history_messages = 100limits the room's immediate in-memory history behavior.
An unlimited MAM archive has privacy and storage implications. Production sites should define a retention policy suitable for their users.
Do not leave an unused default localhost VirtualHost enabled.
Validate:
sudo prosodyctl check config sudo prosodyctl check certs sudo systemctl restart prosody sudo systemctl status prosody --no-pager
When adding a module for the first time, restart Prosody or explicitly load the module. A configuration reload alone may not load a newly enabled module.
17. Support presence modules
mod_support_presence and mod_guest_presence_autoapprove support the private help-desk workflow.
The registered-user VirtualHost defines:
support_jid = "admin@xmpp.example.org"
The guest VirtualHost enables:
modules_enabled = { "guest_presence_autoapprove"; } presence_requester_jid = "admin@xmpp.example.org"
Expected behavior:
- Guests can obtain the configured support account's presence when permitted.
- The support status may reflect any connected resource.
- Gajim, Monal and Converse.js resources are treated as sessions of the same bare JID.
- One unavailable resource must not hide another resource that remains connected.
- Subscription acknowledgements must not be interpreted as online presence.
- Fresh direct-message activity may temporarily confirm that the support account is active.
- Archived messages must not incorrectly mark support as online.
The displayed Last seen value may use XEP-0012 and locally observed activity.
Document this behavior in the site's privacy policy if user activity information is exposed publicly.
18. Mobile push notifications
Mobile push uses XEP-0357 through mod_cloud_notify.
For Prosody 0.12, install the Debian prosody-modules package or a reviewed compatible community module.
Verify:
find /usr/lib/prosody /usr/local/lib/prosody \ -path "*mod_cloud_notify*" \ -type f 2>/dev/null
Required modules:
-
cloud_notify -
smacks -
mam -
carbons
Enable:
modules_enabled = { "smacks"; "mam"; "carbons"; "cloud_notify"; }
By default, do not expose message bodies or sender identities to push gateways:
push_notification_with_body = false push_notification_with_sender = false push_max_devices = 5 push_max_hibernation_timeout = 259200
Validate:
sudo prosodyctl check config sudo systemctl restart prosody
Reconnect the mobile client completely.
A successful registration appears as:
Push notifications enabled for user@xmpp.example.org/ResourceName
The mobile application supplies its own push gateway. Prosody must be able to reach it through encrypted XMPP server-to-server communication.
For Monal, the gateway hostname is operated by the Monal project and must not be hard-coded into the Prosody configuration.
A successful registration proves that XEP-0357 was discovered and accepted. Complete validation also requires:
- Close or suspend the mobile application.
- Send a message from another account.
- Confirm that the device receives a notification.
- Reopen the client and confirm that MAM retrieves the message.
Idle S2S connections may close normally and reconnect when needed.
19. Configure firewalld
Check:
systemctl is-active firewalld firewall-cmd --state firewall-cmd --list-ports
Open native client and browser transport ports:
sudo firewall-cmd --permanent --add-port=5222/tcp sudo firewall-cmd --permanent --add-port=5281/tcp
Open S2S when federation or external push gateways require it:
sudo firewall-cmd --permanent --add-port=5269/tcp
Reload:
sudo firewall-cmd --reload
Do not expose:
5280/tcp
Verify:
ss -lnt | grep -E \ ':(5222|5269|5280|5281)[[:space:]]'
A listening socket does not prove public exposure. Confirm the firewall and test from an external network.
20. Configure Tiki
Open:
Administration → XMPP
Configure values equivalent to:
| Preference | Example | ||
| XMPP client | ConverseJS | ||
| XMPP server domain | xmpp.example.org
| ||
| XMPP client port | 5222
| ||
| Authentication method | Prosody HTTP Auth | ||
| Registered-user domain | xmpp.example.org
| ||
| Anonymous visitor domain | guest.example.org
| ||
| MUC domain | conference.example.org
| ||
| BOSH URL | https://xmpp.example.org:5281/http-bind
| ||
| WebSocket URL | wss://xmpp.example.org:5281/xmpp-websocket
| ||
| Anonymous community room | community@conference.example.org
| ||
| Registered room | registered@conference.example.org
| ||
| Anonymous private target | admin@xmpp.example.org
| ||
| Anonymous destination behavior | Community or private support choice | ||
| Support label | Support team
| ||
| Group-room mapping | Site-specific JSON mapping | ||
| Shared secret | Same secret as Prosody | ||
| CORS allowed origins | https://tiki.example.org
| ||
| XMPP administrator JID | admin@xmpp.example.org
| ||
| XMPP administrator password | Current Tiki password or temporary token | ||
| Always Load ConverseJS | Enable for site-wide chat | ||
| ConverseJS Debug Mode | Disable in production | ||
The support account must:
- Be a real Tiki user.
- Authenticate through Tiki HTTP Auth.
- Be included in the Prosody
adminslist when administrative synchronization is required. - Use a secure password.
- Be reachable through the configured support JID.
Do not use prosodyctl register to create a second password for an HTTP-authenticated VirtualHost.
Clear caches after configuration changes:
cd /var/www/tiki php console.php cache:clear sudo systemctl reload php8.2-fpm
21. Configure Converse.js
Recommended extra settings:
{ "discover_connection_methods": false, "keepalive": true, "persistent_store": "localStorage" }
The dedicated WebSocket preference should contain:
wss://xmpp.example.org:5281/xmpp-websocket
It may also be supplied explicitly:
{ "websocket_url": "wss://xmpp.example.org:5281/xmpp-websocket", "discover_connection_methods": false, "keepalive": true, "persistent_store": "localStorage" }
Use localStorage because persistent guest identities must survive navigation and browser restarts.
Debugging is temporary only:
{ "debug": true }
Disable debug mode in production to avoid excessive console output and performance overhead.
For large private conversations:
- Load only a reasonable initial page of recent messages.
- Do not request all MAM pages automatically.
- Keep older messages available through incremental history loading.
- Avoid rendering off-screen messages unnecessarily.
These optimizations must not delete archived messages.
22. Persistent guests
Visitors do not use Prosody SASL ANONYMOUS.
Tiki generates:
Username: guest-<20 lowercase hexadecimal characters> Password: <48 lowercase hexadecimal characters>
Example:
guest-0123456789abcdef0123@guest.example.org
Converse.js connects using:
-
authentication: login -
auto_login: true - The generated guest JID
- The generated guest password
The browser stores the identity under:
tiki-xmpp-guest-identity
On subsequent page loads:
- A valid stored identity is reused.
- A missing or malformed identity is replaced.
- Normal Converse.js cleanup must not remove the Tiki guest identity.
- Prosody validates credentials through
tiki-xmpp-guest-auth.php.
Clearing browser storage intentionally creates a new anonymous identity.
23. Community and private support behavior
When both a community room and private support JID are configured, an anonymous visitor is asked:
How would you like to chat?
Description:
Join the community conversation, or chat privately with a member of our support team.
Choices:
Join the community group Chat privately with a team member
The private conversation header uses:
Support team
Community choice:
- Connects with the persistent guest identity.
- Joins the configured community room.
- Allows conversation with room members and other visitors.
Private choice:
- Connects with the persistent guest identity.
- Opens a direct chat with the configured support JID.
- Does not force the visitor to join a support room.
- Preserves access to the community option through the normal launcher.
A deployment may configure only one destination. In that case, the available destination may open directly.
24. Group-to-room mapping
Example:
{ "Registered": "registered@conference.example.org", "Translators": "translators@conference.example.org" }
With a by-groups strategy:
- Registered users receive the registered room.
- Each mapped Tiki group adds its corresponding room.
- Administrators may receive community or support rooms.
- Missing rooms may be created during synchronization.
- The effective JID receives an appropriate affiliation.
- Obsolete affiliations may be removed.
Administrative synchronization requires a valid administrator JID and credential.
Test:
- Group addition
- Group removal
- JID changes
- Room creation
- Affiliation addition
- Affiliation removal
- Authentication failures
- Existing room permissions
25. External XMPP identities
An external JID may be used only when:
- It is syntactically valid.
- The user explicitly selects it.
- Compatible BOSH or WebSocket transport is available.
- Federation is configured.
- The external domain can communicate with the MUC component.
- Room permissions allow the external identity.
Otherwise Tiki uses:
username@xmpp.example.org
External identity support requires site-specific testing.
26. Add XMPP chat to a Tiki page
Enable the XMPP wiki plugin.
Embedded community chat:
{xmpp room="Community" visibility="anonymous" view_mode="embedded" width="100%" height="600px"}
Floating chat:
{xmpp room="Community" visibility="anonymous" view_mode="overlayed"}
Fullscreen presentation:
{xmpp room="Community" visibility="anonymous" view_mode="fullscreen"}
Other examples:
{xmpp room="Support"} {xmpp room="Registered"}
Group-restricted example:
{GROUP(groups="Translators")} {xmpp room="Translators"} {GROUP}
Complete room JID:
{xmpp room="community@conference.example.org"}
Embedded mode is meaningful only on a page containing the XMPP plugin. Site-wide overlay and fullscreen behavior depends on Converse.js being loaded globally.
27. Test registered-user authentication
Without API authentication:
curl -sS \ -o /dev/null \ -w "%{http_code}\n" \ "https://tiki.example.org/tiki-xmpp-auth.php/user_exists?user=admin"
Expected:
403
With API authentication:
SECRET="$(sudo cat /etc/prosody/xmpp_http_secret)" curl --fail-with-body \ -u "prosody:${SECRET}" \ "https://tiki.example.org/tiki-xmpp-auth.php/user_exists?user=admin" unset SECRET
Expected for an existing user:
true
Do not put a real Tiki password directly in a shell command or query-string URL.
28. Test persistent guest authentication
Use a disposable guest identity.
For a POST-capable endpoint:
GUEST_USER="guest-0123456789abcdef0123" GUEST_PASS="0123456789abcdef0123456789abcdef0123456789abcdef" SECRET="$(sudo cat /etc/prosody/xmpp_http_secret)" curl --fail-with-body \ -u "prosody:${SECRET}" \ --data-urlencode "user=${GUEST_USER}" \ --data-urlencode "pass=${GUEST_PASS}" \ "https://tiki.example.org/tiki-xmpp-guest-auth.php/check_password" unset SECRET GUEST_PASS GUEST_USER
Expected:
true
Repeat with the same password. It must still return true.
A wrong password must return false.
Delete the disposable test row afterward.
If the deployed mod_auth_http requires GET, perform compatibility testing in a controlled environment and ensure the pass parameter is redacted from all logs.
29. Test BOSH
curl -vk \ https://xmpp.example.org:5281/http-bind
Expected:
- HTTP
200 - The Prosody BOSH endpoint response
30. Test WebSocket correctly
A normal HTTP request is not a complete WebSocket test.
curl \ --http1.1 \ --max-time 2 \ -vk \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Protocol: xmpp" \ -H "Sec-WebSocket-Version: 13" \ -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \ https://xmpp.example.org:5281/xmpp-websocket
Expected:
HTTP/1.1 101 Switching Protocols Sec-WebSocket-Protocol: xmpp
A 501 response may mean that the test omitted the XMPP WebSocket subprotocol.
31. Configure native clients
Example Gajim or Monal account:
| Setting | Value | ||
| JID | admin@xmpp.example.org
| ||
| Password | Current Tiki password | ||
| Server | xmpp.example.org
| ||
| Port | 5222
| ||
| Encryption | STARTTLS required | ||
| Authentication | SASL PLAIN over TLS | ||
| Certificate verification | Enabled | ||
With correct SRV records, a manual server override may not be necessary.
To join a room:
Room: community Server: conference.example.org
Room JID:
community@conference.example.org
For mobile clients, allow notifications at the operating-system level and reconnect after enabling cloud_notify.
32. Logs and diagnostics
Prosody:
sudo systemctl status prosody --no-pager sudo journalctl -u prosody -f sudo tail -f /var/log/prosody/prosody.log sudo tail -f /var/log/prosody/prosody.err sudo prosodyctl check config sudo prosodyctl check certs
Apache:
sudo tail -f /var/log/apache2/tiki.example.org_access_log sudo tail -f /var/log/apache2/tiki.example.org_error_log
PHP-FPM:
sudo systemctl status php8.2-fpm --no-pager sudo journalctl -u php8.2-fpm -f
Listeners:
ss -lnt | grep -E \ ':(5222|5269|5280|5281)[[:space:]]'
Do not enable verbose Converse.js or Prosody debugging permanently in production.
33. Troubleshooting
33.1 Redirect to tiki-login.php?challenge=1
Cause:
Tiki consumed Prosody's Basic Auth credentials as website-login credentials.
Fix:
- Capture Authorization before Tiki initialization.
- Define
TIKI_SKIP_HTTP_BASIC_AUTH. - Prevent Tiki from consuming or reconstructing the header.
- Clear caches.
- Reload PHP-FPM.
33.2 Prosody cannot read its private key
Import certificates:
sudo prosodyctl --root cert import \ /etc/letsencrypt/live/tiki-xmpp-prosody
Reference files under /etc/prosody/certs.
33.3 Prosody serves an expired certificate
Compare the source and served certificates:
sudo openssl x509 -in /etc/letsencrypt/live/tiki-xmpp-prosody/fullchain.pem -noout -dates echo | openssl s_client -connect xmpp.example.org:5222 -starttls xmpp -servername xmpp.example.org 2>/dev/null | openssl x509 -noout -dates -subject -issuer -ext subjectAltName
If automatic renewal did not occur, verify that the Certbot timer is not masked or disabled:
sudo systemctl unmask certbot.timer sudo systemctl enable --now certbot.timer sudo systemctl status certbot.timer
Reimport and restart Prosody when necessary.
Verify the certificate actually presented by Prosody, including its expiration date, hostname and certificate chain.
33.4 WebSocket returns 501
Include:
Sec-WebSocket-Protocol: xmpp
33.5 Guest identity changes on every page
Confirm that:
-
persistent_storeislocalStorage. -
tiki-xmpp-guest-identityexists. - Cleanup code does not remove it.
- Username and password formats remain valid.
- Browser privacy settings are not clearing site storage.
33.6 Administrator synchronization fails
Confirm that:
- The administrator JID identifies a real Tiki user.
- The JID is in the Prosody
adminslist. - The password or token is current.
- Authentication uses
tiki-xmpp-auth.php. - The MUC domain is correct.
33.7 Private support does not join a support room
This is expected.
The private-support flow opens a direct chat with the configured support JID. It does not automatically join a support room.
33.8 Client reports missing XEP-0357 support
Confirm that:
-
cloud_notifyis installed and loaded. -
smacks,mamandcarbonsare enabled. - Prosody was restarted after enabling the module.
- The mobile client was fully closed and reconnected.
- Prosody logs contain
Push notifications enabled for .... - Port
5269and outbound S2S connectivity are available when required.
A warning cached before the module was loaded may disappear only after reconnecting the account.
33.9 Support status is incorrect
Confirm that:
- The configured support JID is the correct bare JID.
-
support_presenceis loaded. -
guest_presence_autoapproveis loaded on the guest VirtualHost. - The visitor is authorized to receive support presence.
- Every connected resource is considered.
- Unavailable resources are removed independently.
- Archived messages are not treated as current activity.
33.10 Large private conversations open slowly
Confirm that:
- The initial local history page is bounded.
- MAM is not fetching every page automatically.
- Older messages load incrementally.
- Converse.js debug mode is disabled.
- The browser cache contains the current JavaScript build.
- DOM observers are scoped and disconnected when no longer required.
34. Final production checklist
Before declaring the deployment ready, confirm:
- The required Tiki XMPP implementation is present.
- Prosody configuration validation passes.
- Certificate validation passes for every host.
- Certificates contain all required SAN names.
- Prosody, Apache, PHP-FPM and firewalld are active.
- Port
5280is not publicly exposed. - Port
5222is reachable when native clients are supported. - Port
5269is reachable when federation or push gateways require S2S. - Port
5281is reachable for direct BOSH and WebSocket. - The registered endpoint returns
403without API authentication. - The registered endpoint recognizes an existing user.
- The guest endpoint returns
403without API authentication. - First guest authentication creates a hashed record.
- Repeated guest authentication succeeds.
- A wrong guest password is rejected.
- Password parameters are absent from or redacted in access logs.
- BOSH responds correctly.
- WebSocket returns HTTP
101. - Native SASL authentication succeeds through TLS.
- Converse.js uses
localStorage. - Community choice joins the configured room.
- Private choice opens the configured support conversation.
- The support status recognizes Gajim, Monal and Converse.js resources.
- One disconnected resource does not hide another connected resource.
- Group-to-room synchronization has been tested.
-
cloud_notifyis loaded. - A mobile client registers its push gateway.
- A closed mobile client receives a real test notification.
- Converse.js debug mode is disabled in production.
- MAM retention matches the site's privacy policy.
- Shared secrets and private keys are not stored in public documentation or version control.
- The Certbot renewal timer or equivalent scheduler is enabled and active.
- Automatic certificate renewal has been tested successfully.
- The Certbot deployment hook imports renewed certificates.
- The renewed certificate actually served by Prosody has been verified.
- The served certificate matches the expected hostname and certificate chain.
At this point:
- Registered Tiki users authenticate to Prosody through HTTP Auth.
- Website visitors use persistent HTTP-authenticated guest identities.
- Visitors can choose between community discussion and private support.
- Converse.js connects through secure WebSocket or HTTPS BOSH.
- Native clients connect through STARTTLS.
- Support presence can reflect multiple XMPP clients.
- Tiki groups can map to MUC rooms.
- Mobile clients can receive XEP-0357 push notifications.
- Certificates are imported and refreshed automatically.
- Only required production ports are publicly exposed.