--- title: "1.29.4 — Chat delivery follows live access; atomic member saves" description: "Open realtime sockets stop receiving chat as soon as a user loses access. Group member saves now commit together with their signal cleanup. Serializing a file's renditions reuses prefetched rows." date: "2026-09-24" tags: ["release"] canonical: "https://django-mojo.sitesmojo.com/changelog/1-29-4/" --- Open realtime sockets stop receiving chat as soon as a user loses access. Group member saves now commit together with their signal cleanup. Serializing a file's renditions reuses prefetched rows. ### Changed - Group member saves run in one database transaction together with their synchronous `pre_save`/`post_save` receivers. If a receiver fails, that save is rolled back and the request fails. A request that changes several permissions can do several saves, and saves that succeeded before the failure stay committed. Refresh the member before you retry. ### Fixed - `File.get_rendition_by_role` uses prefetched renditions when they are present, so a graph that lists many files no longer runs one query per file. ### Security - Chat topics (`chat:`) are re-authorized on every delivery to an open socket, not only when the socket subscribes. Each frame reloads the account from the primary database. Removing a membership, banning a user, removing a chat permission or deactivating the account stops delivery without a reconnect. If authorization raises an error, the frame is dropped. - If a chat frame is denied, the socket is unsubscribed from that topic without notifying the client. The protected group-topic recheck works the same way. ### Upgrade notes - Code that raises from a `GroupMember` save receiver now rolls back that member save. Before, the row stayed committed.