prosody.events.fire_event is a plain function (event_name, event_data), not
a method. The colon form prosody.events:fire_event(name, data) passed the
events table as event_name, so the lookup found no handlers and the
vnc-fcm-invalidate-notify-cache / vcard-cache signals were silently lost.
mod_vnc_muc_fcm hooks these via module:hook_global (which registers under
the string event name), so its notify_cache was never invalidated cross-host.
A stale empty cache entry for an MUC affiliate (left by an earlier test
before the user had a token) then suppressed FCM pushes to that affiliate,
breaking test_muc_fcm_push_to_offline_member in the full suite.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/11>
fcm_notify splits userName internally via jid_split, so callers were
double-splitting: passing jid_split(to) fed the bare node as userName
and shifted every subsequent argument by one (host→title, resource→body,
…), corrupting all notification payloads. Pass the full JID instead.
In mod_vnc_muc_fcm, replace event.stanza:clone() with st.clone() and
add the util.stanza import for robustness.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/11>
PostgreSQL rejects INSERT ... ON CONFLICT ... DO UPDATE on any table
that has a conditional (WHERE) DO/DO ALSO rule or a non-NOTHING DO
INSTEAD rule, erroring with 'INSERT with ON CONFLICT clause cannot be
used with table that has INSERT or UPDATE rules'. Prosody 13's
mod_storage_sql uses ON CONFLICT upserts against the prosody kv table
whenever prosody_unique_index exists (created by these scripts), so the
five inherited conditional DO rules on prosody (cache_group_avatarids,
update_profile_queue_from_insert/_update, update_muc_remote_name,
update_room_nick_jid_map_remote) broke every kv upsert (vcard, vcard_muc,
muc_remote, config, fcmtoken, ...).
Replace those rules with AFTER INSERT / AFTER INSERT OR UPDATE
row-level triggers, which do not block ON CONFLICT. The two profile-queue
rules merge into one AFTER INSERT OR UPDATE trigger so the UPDATE branch
of an ON CONFLICT upsert is also covered (it fires AFTER UPDATE triggers,
not AFTER INSERT, when the conflict is taken).
Also drop the legacy 0.11.6 update_group_owners rule. It is logically
dead under 13.0.6 (fires on key='_affiliations', which is never written)
but PostgreSQL checks rule existence at plan time, so even a dead
conditional rule blocks ON CONFLICT. Dropping it is mandatory, not
optional as the README previously claimed.
Conversion is added to both prosody-13-new-deployment.sql (fresh
deployments, and the run_new_deployment branch of migrate.sh) and
prosody-13-rules-triggers.sql (the run_rules_triggers branch for
0.11.6->13.0 upgrades), so every helm pre/post-upgrade hook path
reaches the fix. Derived-table INSTEAD upsert rules are unchanged.
Verified against postgres:16: both scripts apply cleanly, pg_rewrite
for prosody returns 0 rows, ON CONFLICT upserts succeed, and triggers
fire on both INSERT and conflict-UPDATE branches; reproduced the
production error by re-adding the legacy rules, then confirmed the
incremental script resolves it. Idempotent re-run safe.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/10>
mod_auth_http_async blocks the c2s async runner on an HTTP call inside
SASL plain_test. If the client disconnects during that call,
sessionmanager.retire_session nils every session field (incl.
base_type) and marks it destroyed. When the runner resumed,
mod_saslauth crashed at sasl_process_cdata line 94 on
'sasl/'..session.base_type..'/'. Bail out when the session is gone
instead of firing the event and sending a reply to a dead connection.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/9>
The pre-upgrade hook failed with "could not create unique index
prosody_unique_index" because the prosody table contained duplicate
rows (same host, user, store, key). Without the unique index,
mod_storage_sql falls back to SELECT-then-INSERT instead of ON
CONFLICT upsert, which races under concurrent writes and inserts
duplicates — most commonly in the fcmtoken map store.
Add a DELETE that removes duplicate rows (keeping the last-written
row per group via ctid ordering) immediately before the CREATE UNIQUE
INDEX in both prosody-13-new-deployment.sql and
prosody-13-migration-once.sql. The DELETE is a no-op when no
duplicates exist, preserving idempotency.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/8>
The mod_websocket continuation-frame patch accumulated fragments in an
unbounded dataBuffer with no size limit, fragment count limit, or
timeout. A buggy or malicious client sending endless continuation
frames without FIN could exhaust memory and degrade the entire server.
Add a configurable websocket_max_message_size option (default 2 MB)
that closes the connection with code 1009 ("Message too big") and
resets the buffer when exceeded. Also add wss-perf-analysis.md
documenting the WebSocket performance and drop investigation.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/8>
mod_vnc_broadcast and mod_vnc_fcm accessed the deprecated
prosody.hosts[host].modules.muc.rooms[to] table, removed in Prosody 13,
triggering 'Attempt to read a non-existent global rooms' warnings and
silently breaking MUC affiliation fan-out for broadcasts and push.
Replace with a version-safe get_room_from_jid() helper (rawget fallback
to .rooms for older Prosody), matching the pattern already in
mod_vcard_muc. Guard _affiliations access behind a nil room check.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/7>
Add a module-scoped util.cache LRU (lastactivity_cache_size default 5000,
lastactivity_cache_ttl default 300s) in front of the activity_store /
remote_activity_store fallback. Write-through on all set sites
(authentication-success, local last-session unavailable, remote
unavailable) and read-through in both IQ handlers (batch and standard
jabber:iq:last), so repeated last-activity queries for the same contacts
no longer hit the synchronous SQL driver, reducing event-loop blocking.
Also removes dead code (avatar-hash helpers, dumpTable, unused imports,
commented-out debug logging) and the unused remote avatar-update tracking.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/5>
Prosody 13's mod_storage_sql upgrade path only warns when the index is
missing (it is created only on fresh tables). On DBs upgraded from 0.11.6
the table pre-existed, so the index was never created and has_upsert_index
stayed false, disabling ON CONFLICT upserts and degrading write performance.
Add an idempotent CREATE UNIQUE INDEX IF NOT EXISTS to both the migration
and new-deployment scripts.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/4>
The pre-upgrade hook failed with 'permission denied for table
pg_ts_config_map' because the migration runs as prosodyDBuser (a
non-superuser), and pg_ts_config_map is a PostgreSQL system catalog that
only superusers can DELETE from.
Replace the direct 'delete from pg_ts_config_map where ...' with the
equivalent non-superuser DDL 'ALTER TEXT SEARCH CONFIGURATION ... DROP
MAPPING IF EXISTS FOR <token types>', enumerating every token type the
block re-adds. Verified against postgres:15 as a non-superuser role: the
DELETE reproduces the exact error, DROP MAPPING IF EXISTS succeeds, and
the drop+re-add round-trip is idempotent across repeated runs.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
The http (5280), telnet (5582), and web2 (8080) Services were hardcoded
as NodePort, exposing them on every node's IP. Only s2s (5222/5269)
needs external exposure and remains LoadBalancer. Switch the three
internal services to ClusterIP so they are reachable only via ingress or
within the cluster. Also update the unused service.type value in
values.yaml to ClusterIP so NOTES.txt lands in the port-forward branch
instead of the stale NodePort branch.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Switch hook-delete-policy from hook-succeeded to before-hook-creation on
both the pre-upgrade and post-upgrade migration Jobs. hook-succeeded
deleted the Job immediately on success, making post-deploy log
inspection impossible. before-hook-creation keeps the most recent run's
Job and logs around (inspectable via kubectl logs job/<name>) and only
removes the previous Job when the next deployment fires the hook,
avoiding name conflicts and accumulation.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
1. REST 404 "Unknown host: prosody": the tester reaches prosody by
service name (http://prosody:5280), so aiohttp sends Host: prosody,
which Prosody rejects as an unknown vhost. The auto host-header
heuristic only overrides for IP/localhost. Set REST_HOST_HEADER=
example.com explicitly in the tester env so HTTP routing lands on the
example.com VirtualHost that serves mod_http_rest /rest.
2. healthcheck.sh not found: the static test_04_infra checks resolve
../config/healthcheck.sh (= /config/healthcheck.sh) but the tester
image only ships /tests. Mount ./config:/config:ro so the path
resolves inside the container.
3. telnet non-loopback banner: read only 256 bytes, capturing just the
ASCII-art top and never the literal "Prosody" text. Bump to 1024 to
match test_04_infra.test_telnet_banner, which passes with the larger
read.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
The tester container uses WORKDIR /tests, so 'tests/pytest.ini' resolves
to /tests/tests/pytest.ini which does not exist, causing FileNotFoundError
in CI. Use paths relative to /tests (pytest . -c pytest.ini) for all
in-container invocations: CI job, Makefile target, tester image CMD, and
docs. Host-run scripts are unchanged (they run from repo root).
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Dockerfile: add postgresql-client and bake db-customization/ SQL scripts
into the image at /vnc/db-customization/.
config/migrate.sh: detection + migration script with two modes:
- pre-upgrade: runs idempotent prosody-13-new-deployment.sql on 13.0.x
databases; skips 0.11.6 (unsafe pre-upgrade) and new deployments.
- post-upgrade: waits for Prosody table, then runs the appropriate
scripts — full 0.11.6->13.0.6 migration (rules-triggers + migration-once
+ new-deployment) or idempotent drift correction for 13.0.x.
Helm chart: two Job templates (db-migration-pre-upgrade.yaml,
db-migration-post-upgrade.yaml) gated by dbMigration.enabled (default
true). Both reuse the Prosody image and DB credentials from existing
values. backoffLimit: 0, hook-delete-policy: hook-succeeded.
Also tracks the db-customization SQL files (previously untracked, now
referenced by the Dockerfile ADD).
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Adds a detection query set and decision tree to determine from the
database itself whether it is a new deployment, an unmigrated 0.11.6
database, an upgraded 0.11.6 database, or a clean 13.0 state — and which
script to run in each case.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Documents which SQL script to run for new deployments vs existing 0.11.6
database upgrades, ordering prerequisites, and what changed in the MUC
storage layout between 0.11.6 and 13.0.6.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
The store :set() calls go through mod_storage_sql (LuaDBI), a blocking C
client. These hooks fire on the presence/auth hot path, so inline writes
stall the event loop under presence storms. Buffer writes in memory and
flush every 5 seconds (configurable via vnc_lastactivity_flush_interval).
The IQ handlers already read from in-memory map/remote_act_cache first,
so read consistency is preserved. Final flush on server-stopping and
host-deactivating prevents data loss on graceful shutdown.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Prosody 13.0.6 removed continuation-frame (fragmented-message) support
from mod_websocket — validate_frame hard-rejects any frame with FIN=false
(close code 1003). VNCtalk clients fragment large WebSocket messages
(e.g. vCard sets with avatars >~64 KB), so the connection is silently
reset. The rejection fires on the partial-frame validation path before
handle_frame is reached, and websocket_close() does not log, so nothing
appears in prosody debug logs.
This patch removes the blanket FIN=false rejection from validate_frame
and restores the dataBuffer fragment-accumulation logic from 0.11.6 in
handle_frame: text frames (0x1) with FIN=false start a buffer,
continuation frames (0x0) append, and concatenated data is returned
only when FIN=true arrives.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Neither _hin variant is enabled in the config (the active push modules
are mod_vnc_fcm for 1:1 and mod_vnc_muc_fcm for MUC). Drop the dead
files and update AGENTS.md to reference the active modules.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Always sending Host: <xmpp domain> to /rest broke when REST was fronted
by an ingress (TLS hostname mismatch -> 431). Add a --rest-host-header
option (REST_HOST_HEADER env, default 'auto') that sends the XMPP domain
Host only for IP/localhost URLs where prosody needs it for vhost routing;
'none' disables, any other value is sent verbatim. Documents the option
in tests/README.md.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Pin the version and expected SHA256 as build ARGs and verify the
download with sha256sum -c so a tampered or corrupt source download
fails the build loudly. Also drops the noisy tar -v and removes the
tarball after extraction.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Bumps the base image to docker:24.0 (needed for Usage: docker compose [OPTIONS] COMMAND
Define and run multi-container applications with Docker
Options:
--all-resources Include all resources, even those not
used by services
--ansi string Control when to print ANSI control
characters ("never"|"always"|"auto")
(default "auto")
--compatibility Run compose in backward compatibility mode
--dry-run Execute command in dry run mode
--env-file stringArray Specify an alternate environment file
-f, --file stringArray Compose configuration files
--parallel int Control max parallelism, -1 for
unlimited (default -1)
--profile stringArray Specify a profile to enable
--progress string Set type of progress output (auto,
tty, plain, json, quiet)
--project-directory string Specify an alternate working directory
(default: the path of the, first
specified, Compose file)
-p, --project-name string Project name
Management Commands:
bridge Convert compose files into another model
Commands:
attach Attach local standard input, output, and error streams to a service's running container
build Build or rebuild services
commit Create a new image from a service container's changes
config Parse, resolve and render compose file in canonical format
cp Copy files/folders between a service container and the local filesystem
create Creates containers for a service
down Stop and remove containers, networks
events Receive real time events from containers
exec Execute a command in a running container
export Export a service container's filesystem as a tar archive
images List images used by the created containers
kill Force stop service containers
logs View output from containers
ls List running compose projects
pause Pause services
port Print the public port for a port binding
ps List containers
publish Publish compose application
pull Pull service images
push Push service images
restart Restart service containers
rm Removes stopped service containers
run Run a one-off command on a service
scale Scale services
start Start services
stats Display a live stream of container(s) resource usage statistics
stop Stop services
top Display the running processes
unpause Unpause services
up Create and start containers
version Show the Docker Compose version information
volumes List volumes
wait Block until containers of all (or specified) services stop.
watch Watch build context for service and rebuild/refresh containers when files are updated
Run 'docker compose COMMAND --help' for more information on a command. v2) and
adds a test:compose job that builds the stack, waits for prosody health,
runs the slixmpp suite in the tester container, and emits a junit report.
Runs on merge_request_event and non-main branch pipelines.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
- chart: add secretEnv (existingSecret + keys) rendered as secretKeyRef in
both containers; add resourcesProsody/resourcesSidecar defaults with
dedicated requests and limits via a containerResources helper
- argo: move prosodyDBpass/fcmApiKey/fileShareSecret/avatarUploadPass out of
env into secretEnv referencing a prosody-secrets Secret; set dedicated
resource requests+limits per container
- argo: add prosody-secrets.example.yaml ArgoCD Application (with a warning
to use Sealed Secrets / External Secrets / SOPS instead of plaintext)
- chart fixes: bump stale appVersion 0.11.6 -> 13.0.6; fix malformed service
block in values.yaml
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Set PROSODY_DEBUG=true to switch the prosody.log level from info to
debug. This makes stanza-too-large rejections and XML parse errors
visible — both are logged at debug level and otherwise invisible.
Usage in k8s:
env:
- name: PROSODY_DEBUG
value: "true"
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Add c2s_stanza_size_limit and s2s_stanza_size_limit to the config
template, backed by C2S_STANZA_SIZE_LIMIT and S2S_STANZA_SIZE_LIMIT
env vars. Both default to 5242880 (5MB) when unset, set in startup.sh.
The compose harness sets them explicitly.
Prosody 13.0.6 defaults are 256KB (c2s) and 512KB (s2s), which are too
small for large file-transfer invitations and Jitsi meet sessions.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Combined results from compose harness (80 passed, 6 skipped) and external
deployment (50 passed, 36 skipped). 81 unique tests passed across both
runs, 0 failures. 5 remaining skips are config/infrastructure prerequisites,
not regressions. All 81 passed tests described with what they verify.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
The test was skipping because it depended on the second_client fixture,
which uses XMPP_JID2 (a distinct account). Carbons require both
resources to share the same bare JID.
Rewrite to create a second VNCXmppClient inline with the same bare JID
as xmpp_client but a different resource (/carbon-<random>), so the test
runs regardless of whether XMPP_JID2 is configured or distinct.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Check if the SSH tunnel to PostgreSQL (port 14322) and the kubectl
port-forward for telnet (port 5582) are active before running the
testsuite. Start them automatically if missing. Clean up the
kubectl port-forward on exit if we started it.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Bump Dockerfile to prosody-13.0.6; all 8 patches re-ported against 13.0.6.
Re-ported patches (3 changed, 5 applied with offset):
- hidden.lib.patch: 13.0 uses module:may() instead of um_is_admin; changed
to 'if restrict_public then' (same intent: hide option for everyone)
- mod_muc.patch: 13.0 added restrict_pm between register and
presence_broadcast; updated hunk 1 context
- mod_muc_unique.patch: 13.0 uses 'require "prosody.util.stanza"'
(namespaced); updated context
- muc.lib, mod_carbons, mod_mam, mod_muc_mam, register.lib: applied
with line offsets, no re-port needed
Config changes:
- Remove mod_posix from modules_enabled (13.0 absorbed signal handling,
pidfile, and run_as_root check into core util/startup.lua)
- Move pubsub from modules_enabled to Component (13.0 requires pubsub
to be loaded as a component, not a module)
Test fix:
- test_muc_fcm_push_to_offline_member: wait for count=2 captures instead
of 1 — mod_vnc_muc_fcm pushes to ALL affiliated members (including
sender, because it can't see main-host sessions from the MUC
component); the test was racing on which push arrived first
Verified: prosodyctl check config passes; compose-harness testsuite
green (80 passed, 6 skipped, 0 failed).
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Update M2 status to reflect both compose-harness (80 passed, 6 skipped,
0 failed) and external testsuite results (48 passed, 30 skipped — all
skips are infrastructure prerequisites). test_vcard_fallback now skipped
pending mod_vnc_vcard_fallback enablement.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
The module exists in vnctalk/ but is not listed in modules_enabled in
the config template. The test previously passed on external deployments
by coincidence — the test users already had vCards with FN from real
usage. On the compose harness with a fresh DB, the test fails because
nothing generates a vCard.
Add @pytest.mark.skip with a reason pointing to the missing module.
Simplify run-tests.sh to a single pytest invocation (was 12 separate
calls). Update m1-manual-tasks.md §3.5 to reflect the skip.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Brings up the docker-compose stack (postgres + mocks + prosody), waits
for prosody to become healthy, and runs the full pytest suite with all
env vars set so tests that need MOCK_URL, PG, BOSH/WS, telnet, etc. are
not skipped.
Auto-detects if port 5582 is already in use (e.g. kubectl port-forward)
and remaps the telnet host port to 5583 via the TELNET_HOST_PORT env var
in docker-compose.yml.
Usage:
./run-compose-tests.sh # up + test + down
./run-compose-tests.sh --no-down # leave stack running
./run-compose-tests.sh --down # just tear down
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Swap all lua5.2-* Alpine packages to lua5.4-* in both Dockerfile stages
and add --lua-version=5.4 to ./configure.
Dropped packages:
- lua5.2-bitop: Prosody's util.bitcompat uses util.bit53 (native Lua
5.3/5.4 bitwise operators) when bit32 is unavailable
- lua5.2-lpeg_patterns, lua5.2-rapidjson, lua5.2-redis: not required by
any require in Prosody 0.12.6 or any vnctalk module
- luarocks5.2: installed but never used
- Duplicate lua5.2-socket entry: removed
Code audit for 5.2→5.4 breakage: clean.
- No bit32/bitop usage in vnctalk modules
- Every Prosody file using unpack has 'local unpack = table.unpack or
unpack' (resolves to table.unpack on 5.4)
- No string.format('%d', float) patterns in vnctalk modules
Verified: image builds, prosodyctl check config passes, compose-harness
testsuite green (80 passed, 5 skipped — identical to M1).
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Three changes to complete the repo-level work for Milestone 1 (0.11.6 →
0.12.6) plus a howto for the remaining manual/external tasks:
1. config/prosody.cfg.lua.template: re-add run_as_root = true. Production
and the compose harness run as root (startup.sh writes into root-owned
/etc/prosody/). Without it, mod_posix calls prosody.shutdown() during
startup, which deactivates c2s (port 5222) before the shutdown itself
errors out (prosody.main_thread is nil during module init), leaving
Prosody running without c2s.
2. tests/test_08_image_patches.py: replace 3 stale patch-marker entries
that checked for patches M1 intentionally dropped (moduleapi,
mod_admin_telnet, muc.lib dumpTable) with markers that verify their
config-based replacements (console_interfaces, http_interfaces) and
the storagemanager.open() rewrite in mod_vnc_muc_fcm.lua.
3. upgrade-plan.md: update M1 status to reflect the post-M1 fixes
(run_as_root, default_storage, stale tests), mark manual steps (DB
migration, telnet console regression, external testsuite) with
cross-references to m1-manual-tasks.md, and correct 0.12.5 → 0.12.6
throughout.
4. m1-manual-tasks.md: new file documenting the three manual tasks that
require external infrastructure — DB schema migration (one-way, with
rehearse-on-copy procedure), telnet console regression test (0.12
reimplemented the console on mod_admin_shell), and external testsuite
run against a dev deployment.
Verified: compose-harness testsuite — 80 passed, 5 skipped, 1 pre-existing
failure (test_vcard_fallback: mod_vnc_vcard_fallback not enabled in config,
unrelated to M1).
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
The M1 upgrade (0ea4d0a) removed default_storage = "sql" as seemingly
redundant with storage = "sql". However, the MUC component sets
storage = { muc_log = "sql" } (a table), and storagemanager.get_driver
falls back to default_storage (or "internal") for stores not listed in
the table. Without default_storage = "sql", the kick store on the MUC
component falls back to internal storage, whose archive driver requires
stanza objects — mod_vnc_track_kicks passes a plain string, causing
"unsupported-datatype" errors and kick data not being persisted.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Extend tests/README.md with:
- Running with docker-compose: make test one-shot, host-run flow, mock
capture/log inspection, teardown.
- Importing an existing database into the compose postgres (psql/pg_restore,
fresh-volume flow, auth-backend caveat).
- Running against a standalone local prosody container (build + docker run,
port mapping, optional separate mock for side-effect tests).
- Updated env table (MOCK_URL, SMACKS_HIBERNATION_TIME), test coverage table
(test_08-12), prerequisites (tests/requirements.txt), and notes reflecting
the new healthcheck execution, REST Host header, and XMPP_HOST/PORT fix.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
- mod_vnc_lastactivity: the jabber:iq:last handler returns <forbidden> when
the query has no 'to'; send it to another existing user's bare JID and
assert the result carries a 'seconds' attribute
- set body via msg["body"] and do not await message send() (returns None)
- use unique uuid room names instead of a fixed duplicated localpart
- unlock the freshly-created (locked) room with configure_muc before a
second user joins / is affiliated
- the unregister IQ is fire-and-forget (no reply); tolerate IqTimeout rather
than failing the kick-tracking smoke test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Prosody keeps every store in just two tables — prosody (keyval/map) and
prosodyarchive (archive) — distinguished by a `store` column. The previous
tests looked for separate tables named like %muc_log%, %kick%, %activity%,
which never exist, so they failed.
- check stores via the `store` column in prosody/prosodyarchive, not by
table name: muc_log + kick are archive stores, activity + vcard are
keyval stores (matching the modules' open_store calls)
- assert the two real tables (prosody, prosodyarchive) exist
- verify MUC archives are stored under the conference component host
- store_user inversion: 'key' is the generated archive UID, not the message
id, so match the REST-injected message on value LIKE and then assert the
owning 'user' is the sender and 'with' is the recipient
Verified against the live database (schema and sample rows inspected).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
A half-open PostgreSQL tunnel accepts TCP but never completes the PG
handshake; asyncpg.connect() with no timeout then hangs the entire
test_06 suite indefinitely. Wrap connect in wait_for(timeout=10) and skip
with a clear message when the DB is unreachable, and only close the
connection in the finally if it was actually established (the previous
unconditional close raised NameError when connect failed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
- set body via msg["body"] / read via msg["body"]; do not await message
send() (returns None in slixmpp)
- str(msg["from"]) / str(pres["from"]) before .startswith (JID not str)
- mod_muc_unique: query a bare JID on the MUC component (a room JID), not a
user on the main host. The item-not-found handler is registered on the
component; a query to the user host is merely service-unavailable.
- offline-affiliate test: unlock the freshly-created room with configure_muc
before setting affiliations
Verified the portmanager network_default_read_size patch end-to-end: chat
bodies up to 12000 bytes are delivered intact.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
conftest:
- send initial presence after connect so the server routes directed
messages to the resource (chats to a bare JID otherwise go to offline
storage, breaking the timestamp/receipt tests)
- configure_muc: include FORM_TYPE on non-empty config submits, else
Prosody rejects them ("Form is not of type room configuration")
test_03_vnctalk:
- set body via msg["body"] and do not await presence/message send()
- read body via msg["body"], not msg.body
- replace nonexistent slixmpp.jid.nodeprep / slixmpp.util.id.short with uuid
- disco_info form lookup uses .// (form nested under <query>)
- test_broadcast_component rewritten to exercise real fanout: the
component is message-only (no disco), so post a <vncTalkBroadcast> with a
<to> recipient and assert that recipient receives the cast copy
- config-change broadcast test: unlock the room first, trigger via a real
config submit (not a subject change), and catch the bodyless
<x xmlns='xmpp:vnctalk:update'> with a low-level handler (slixmpp's
message event needs a <body>)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
conftest:
- presence.send()/message.send() return None in slixmpp; do not await them
- JID objects are not str; use str(pres["from"]) before .startswith
- disco_info/disco_items take a timeout so a non-responding server fails
fast instead of hanging the suite
- query_mam_and_collect takes a `to` arg to query a MUC archive (MUC MAM
lives at the room, not the user)
- add configure_muc() to submit the owner config form; a freshly-created
Prosody room is locked until configured and other users cannot join
test_02_muc:
- set body via msg["body"], not msg.body (the latter sets a Python attr
and produces a bodyless stanza that is neither reflected nor archived)
- MUC MAM test queries the room and matches the inner forwarded message by
local name (Prosody may omit xmlns='jabber:client')
- unlock rooms with configure_muc before a second user joins / is affiliated
- broadcast test rewritten as test_muc_broadcast_strips_spoofed_stanza_id:
the real contract is anti-spoofing (a client-injected by-room stanza-id is
stripped) while the room still adds its own legitimate XEP-0359 stanza-id
- unregister IQ handler is fire-and-forget (sends no reply); tolerate the
IqTimeout and assert the side effect (affiliation removed)
- hidden-override test skips when the server does not restrict public rooms
- disco_info form lookups use .// (form is nested under <query>)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
mod_vnc_e2ehints put the raw boolean returned by get_e2e() (false when
unset) into the disco#info roominfo formdata. muc#roominfo_e2e is a
text-single field, and util.dataforms/util.stanza cannot serialize a
boolean field value, so room:get_disco_info() raised an error and the
server silently dropped the disco#info reply for every existing room
(confirmed at the wire level: non-existent rooms returned item-not-found,
existing rooms returned nothing). Coerce the value to a string, matching
the working mod_vnc_muc_data pattern. get_e2e() itself is left returning a
boolean since the muc-config-form boolean field needs it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
slixmpp's high-level "message" event only fires for stanzas with a
top-level <body> (matcher '{jabber:client}message/{jabber:client}body').
MAM result wrappers (<message><result xmlns='urn:xmpp:mam:2'>...) have no
<body> of their own, so the event never fired and the MAM tests saw zero
results even though the server correctly returned them (<fin complete>).
Capture results with a low-level Callback + MatchXPath on the result
element instead, the same mechanism slixmpp's own xep_0313 plugin uses.
Also filter MAM queries by a start timestamp so a large archive does not
push fresh messages off the first page, and match the inner forwarded
<message> by local name to tolerate Prosody omitting xmlns='jabber:client'.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Archiving triggered by REST injection is asynchronous: the HTTP endpoint
returns 201 before archive:append necessarily completes. A single 1s sleep
was often too short, causing flaky failures even though the message was
present in the database moments later.
- Add _poll_mam_for_id helper that queries MAM up to 10 times with 1s
intervals until the target message id appears
- Replace fixed sleeps in both REST MAM roundtrip tests with polling
- Improves reliability on slower or more loaded Prosody instances
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
For REST-injected messages, mod_mam.lua's patched message_handler sets
both store_user and the 'with' field to the sender's JID (because c2s is
false for REST and the vnc_rest branch only overrides store_user, not
'with'). Filtering by recipient to_jid therefore returns no results.
- Remove with_jid=to_jid from both REST MAM roundtrip tests
- Query the full sender archive and scan for the message id instead
- This is more robust regardless of the 'with' field semantics
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
XEP-0313 requires the MAM query data form to include a hidden FORM_TYPE
field with value 'urn:xmpp:mam:2'. Without it, Prosody's dataform
validation may reject or ignore the 'with' filter, causing the query to
return empty results even when messages exist in the archive.
- Introduce _add_mam_query_form helper that always injects FORM_TYPE
- Update query_mam and query_mam_and_collect to use the helper
- Also support start/end filters for completeness
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Prosody's mod_mam sends <result> elements as individual messages but the
<fin> element inside the IQ response stanza. The old code waited for a
<message> containing <fin>, which never arrives, causing TimeoutError.
- Remove on_mam_fin message handler
- Use iq.send() timeout instead of waiting for a message <fin>
- Add 0.5s grace period after IQ response for trailing <result> messages
- Clean up only the message handler in finally
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
mod_mam.lua advertises urn:xmpp:mam:2 and urn:xmpp:sid:0 via the
account-disco-info hook (user bare JID), not on the server domain.
The previous test incorrectly expected them from the server domain.
- test_disco_info_server: only assert basic disco#info / disco#items
- test_disco_info_account: new test querying bare JID for MAM + sid
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
- slixmpp 1.16 (asyncio) has no process() method; connect() already
schedules background processing. Remove the call that caused
AttributeError: 'VNCXmppClient' object has no attribute 'process'
- REST endpoint returns 422 (not 415) for invalid XML; accept both
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
slixmpp 1.16.0 connect() is not a coroutine; it returns an asyncio.Future
and takes (host, port) kwargs, not (address, use_ssl). The previous code
awaited connect() with wrong args causing TypeError inside the async fixture,
which pytest-asyncio surfaced as ERROR at setup.
- Pass host/port/ssl_context to super().__init__() so XMLStream stores them
- Remove manual connect_address and use_ssl tracking
- async_connect: simply await self.connect() with no args (host/port already
configured in __init__)
- Keep ssl_context creation with verify_mode=CERT_NONE for self-signed certs
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
pytest-asyncio 1.4 with asyncio_mode=auto requires async fixtures to be
decorated with @pytest_asyncio.fixture instead of @pytest.fixture.
Without this, async generator fixtures raise AssertionError during setup
at plugin.py:558, causing ERROR before any test code runs.
- Add import pytest_asyncio
- Replace @pytest.fixture with @pytest_asyncio.fixture for xmpp_client,
second_client, and pg_connection async fixtures
- Leave sync fixtures (xmpp_config, rest_injector) unchanged
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Prosody containers use self-generated self-signed certs. slixmpp with
use_ssl=True verifies certificates by default, causing
SSLCertVerificationError and pytest ERROR during fixture setup.
- VNCXmppClient: add verify_ssl parameter (default False) that creates
an ssl.SSLContext with verify_mode=CERT_NONE when disabled
- conftest.py: add --verify-ssl CLI option / VERIFY_SSL env var;
pass verify_ssl through xmpp_config to both client fixtures
- docs: document --verify-ssl in README.md and AGENTS.md
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
The /rest endpoint is typically protected by a reverse proxy. The
RESTInjector helper now accepts optional auth_user/auth_password and
sends an Authorization: Basic header when configured.
- conftest.py: add --rest-user/--rest-password CLI options and env vars;
update RESTInjector to include Basic auth headers
- test_01_core.py: refactor TestRestInjection to use the authenticated
rest_injector fixture instead of raw aiohttp calls
- tests/README.md + AGENTS.md: document REST_USER/REST_PASSWORD
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
- test_02_muc.py: add test_muc_mam_presence_not_archived (negative assertion
that presence stanzas are excluded from MUC MAM) and
test_hidden_lib_rejects_public_override (config form absence + raw override
submission with xfail for admin accounts)
- test_06_postgres.py: add TestPostgresStoreUserInversion which injects via
REST and queries the archive table directly to assert the 'user' column is
the sender; add TestPostgresKickStore and TestPostgresActivityStore for
mod_vnc_track_kicks and mod_vnc_lastactivity store tables
- test_07_module_load.py: new file with smoke tests for the five previously
zero-coverage modules (lastactivity IQ, delfile message hook, remotemucstore
message hook, remotemucinvite event, track_kicks event)
- MANUAL_TESTS.md: expand mod_auth_http_async section with mock-HTTP-server
procedure; add container build verification steps
- AUDIT.md: revise verdict to 'adequate for upgrade safety'; update all
coverage tables to reflect 55 tests across 7 files
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
- 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>
Re-port all source patches onto 0.12.6; three are gone entirely:
moduleapi (the two vnc_muc_fcm modules call core.storagemanager
directly now), mod_admin_telnet and portmanager (replaced by
console_interfaces/http_interfaces config). The muc.lib fork keeps its
four functional changes including the externally consumed
muc-config-sub-mitted event; the operator-precedence hunk was fixed
upstream. mod_muc_mam shrinks to keep-archive-on-room-destroy since
muc_log_expires_after="never" disables cleanup upstream in 0.12.
Delete the bundled mod_smacks fork and the no-op mod_smacks_offline;
core 0.12 smacks supersedes them (options audited, dead smacks_max_old
corrected to smacks_max_old_sessions).
Config/startup: drop legacyauth, run_as_root, daemonize; bosh_ports ->
http_ports; cross_domain_* -> http_cors_override; randomize
component_secret at startup (env-overridable); export
log_slow_events_threshold fallback (latent render bug).
Found while smoke-testing: pin --idn-library=idn (0.12's ICU default
segfaults without ICU data in the image); http became a private
service in 0.12 so 5280 needs http_interfaces to stay public; the
telnet console now depends on mod_admin_socket, whose socket moves to
/var/log/prosody.
Verified: prosodyctl check config clean; boots against Postgres with
empty error log and same port bindings as 0.11; healthcheck green;
telnet console and SQL storage round-trip; 0.11->0.12 schema upgrade
rehearsed on a 0.11-created database with data intact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Phase 0 of upgrade-plan.md. The 11 forked copies of 0.11.6 files in
patches/ are replaced by unified .patch files applied in the Dockerfile
builder stage with patch -p1 --fuzz=0 before make install, so upstream
drift fails the build instead of silently shipping stale forks. The
cp-over-installed-files block in the final stage is gone and /vnc/patches
no longer ships in the image. Stale patches.list replaced by a README
documenting each patch's intent.
Verified: installed prosody tree (269 files) is byte-identical to the
image built from the previous mechanism.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>