Skip to content

Support the GroupsV2 "end group" (terminate) feature - #2098

Merged
AsamK merged 2 commits into
AsamK:masterfrom
FailSpy:feature/groupv2-terminate
Aug 8, 2026
Merged

Support the GroupsV2 "end group" (terminate) feature#2098
AsamK merged 2 commits into
AsamK:masterfrom
FailSpy:feature/groupv2-terminate

Conversation

@FailSpy

@FailSpy FailSpy commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Signal recently added the ability for a group admin to permanently terminate a group ("End group" in the app UI): once it's terminated the group is read-only for everyone -- nobody, admins included, can send messages or start calls -- and members keep the existing history. Other clients silently drop any message sent to a terminated group. The pinned signal-network already carries the protobuf for it (DecryptedGroup.terminated, plus the TerminateGroupAction change), so signal-cli just wasn't reading or offering it yet. This wires it up both directions.

Seeing that a group is terminated

Today signal-cli can't tell a terminated group from a live one, and it'll report a sent message as SUCCESS even though every other client drops it. This reads DecryptedGroup.terminated through GroupInfo.isTerminated() and:

  • surfaces it in listGroups (JSON, and both the default and --detailed plaintext lines) and as the DBus IsTerminated property
  • refuses to send to a terminated group locally -- messages, reactions, typing and group stories -- rather than send one other clients ignore
  • drops incoming messages addressed to a terminated group (in isNotAllowedToSendToGroup, alongside the announcement-group handling)

The send refusal mirrors the existing isAnnouncementGroup wiring, with one difference: it's unconditional. Announcement-only exempts admins; a terminated group blocks everyone.

On the listGroups output: I put Terminated on the default (non-detailed) line, right after Blocked. Blocked is almost always false yet already lives there, so that line is a status-at-a-glance row, not an only-show-non-defaults one. Terminated is a peer of Blocked -- both mean "this isn't a usable conversation" -- and since a terminated group otherwise looks completely normal, the glanceable line is where it's most worth surfacing. Happy to move it behind --detailed if you'd rather leave the default line untouched.

Terminating a group

Adds a terminateGroup command (admin-only, v2 groups) that sends the TerminateGroupAction via the library's own createTerminateGroup() and notifies the members. It follows the quitGroup shape end to end -- GroupHelper.terminateGroup -> Manager/ManagerImpl -> the terminateGroup command (command line + JSON-RPC) and the DBus Group.terminateGroup method -- with the same conflict-retry as updateGroup.

Tests / scope

Added a unit test for the terminated model reads and the Group record wiring, in the existing JUnit style (no new deps). The send-path guards aren't unit-covered -- there's no existing test path for the outgoing/send layer to build on -- but the isTerminated() signal they rely on is.

@FailSpy
FailSpy force-pushed the feature/groupv2-terminate branch from 01b1c34 to db847f1 Compare August 7, 2026 18:29

@AsamK AsamK left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, looks good.
Just the naming is inconsistent, please use terminate instead of end everywhere

Comment thread lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java Outdated
Comment thread lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java Outdated
FailSpy added 2 commits August 8, 2026 11:00
A group admin can permanently terminate a group. Afterwards the group is
read-only for everyone, not even an admin can send messages or start calls;
members keep access to the existing history. Other clients silently drop
messages sent to a terminated group.

Read the DecryptedGroup.terminated flag through GroupInfo.isTerminated() and:

- surface it in listGroups (json and plain text) and as the DBus IsTerminated
  group property
- refuse to send to a terminated group locally (messages, reactions, typing and
  group stories) rather than send one that other clients ignore
- drop incoming messages addressed to a terminated group
Let a group admin permanently terminate a GroupV2 group via the
TerminateGroupAction (Groups.proto field 28, change epoch 7) the pinned library
already exposes as GroupsV2Operations.createTerminateGroup().

- GroupV2Helper.terminateGroup builds and commits the change
- GroupHelper.terminateGroup resolves/refreshes the group (v2 only), sends the
  update to members and syncs storage, with the same conflict-retry as
  updateGroup
- exposed through Manager.terminateGroup, the terminateGroup CLI/JSON-RPC command
  and the DBus Group.terminateGroup method
@FailSpy
FailSpy force-pushed the feature/groupv2-terminate branch from db847f1 to 1ba6595 Compare August 8, 2026 15:03
@AsamK
AsamK merged commit 13819c8 into AsamK:master Aug 8, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants