Skip to content

User verification with kick for open groups #44

Description

@daopunk

Summary

Optional group mode: new joiners must complete a DM handshake with Bread Bot before they may stay in the group. Unverified joiners are kicked. Verification is stored on the live CVM (encrypted prefs volume), so it survives in-place upgrades and applies across groups that enable the mode.

This is the only Signal-shaped slice of the broader gated-entry request in #40. It is a bouncer, not a restricted role and not proof-of-humanity.

Why this shape

Signal groups have two roles: member and admin. There is no mute, no pending-in-group role, and no hold channel. Send permission is group-wide (everyone, or announcement-only). So we cannot “let them in but block posting” until they pass a check.

What Signal can do, if the bot is a group admin:

  • Kick (remove_members — already used for Language Thread !leave)
  • DM a challenge and accept a text reply
  • Persist a verified-identity set on group-prefs-translation

Native group link + require admin approval is still the stronger gate (they never see the chat). This issue is for groups that want to keep the invite link open and bounce strangers automatically.

Proposed behavior

Happy path (preferred):

  1. User DMs the bot and completes the handshake before joining.
  2. Group description / kick copy points at that: “DM Bread Bot, then join.”
  3. They join an !only-registered group; the bot sees a known UUID and does nothing.

Cold join:

  1. A group admin enables the mode (command name TBD; must not collide with TEE !verify).
  2. Current members are grandfathered. Only identities that appear after the flag flips are gated.
  3. Bot detects a new member (poll GET /v1/groups and diff members; signal-cli has no reliable join event).
  4. If unverified: DM them while they are still in the group (shared group helps the message request), then kick.
  5. Post one short group line, rate-limited per identity (e.g. once per hour): <name> is unregistered — DM the bot, then rejoin.
  6. Do not paste phone numbers into the group. Prefer profile name; fall back to a generic “A new member”.
  7. On a correct DM reply, persist the Signal UUID (phone as a secondary alias only).
  8. They rejoin via the same open link, or the bot add_members them back after unban if we used ban.

Sidecars: do not run the bouncer on Language Thread groups. The bot adds those people from main.

Design decisions (defaults)

Decision Default Why
Who can enable/disable Signal group admins only Anyone with the command could lock or empty the group
Existing members when enabled Grandfather Mass-kick would empty the chat
Identity key UUID, plus phone alias via existing identities_match Many envelopes have no E.164
Storage Global verified set in existing encrypted prefs (group_preferences_store) Same CVM volume we must not wipe; not a new disk
Join detection Poll + member diff (invite acceptor already polls every 5s) signal-cli does not emit a clean “member joined” event
DM vs kick order DM first, then kick After kick there is no shared group; the DM looks like spam
Challenge Rotating text prompt in DM Signal has no buttons / CAPTCHA UI
Fail / ignore Stay kicked; they can retry the DM No per-member mute, no timeout-mute
Kick/rejoin spam Rate-limit the group announcement; consider ban-until-pass then unban + re-add Open link means they can rejoin immediately
Command names Not !verify !verify is TEE attestation
Product claim “Completed a DM handshake with Bread Bot” A scripted signal-cli client can pass a text question

Open (decide in plan, not here):

  • Exact command names (!register / !only-registered vs something else).
  • Ban-until-pass vs kick-only (ban/unban is not wrapped in the client today).
  • Whether a passed handshake auto-re-adds them, or they must tap the invite link again.
  • Whether the prompt is global or configurable per group.

Caveats (do not paper over)

  • Race: poll/diff means a few seconds of full access. They can post before the kick.
  • Bot must be a group admin. On the main Breadchain group it may only be a member; the command should refuse unless Group.admins includes the bot. Sidecars the bot created are fine.
  • Message requests: if they have never chatted with the bot, Signal shows a message request. Links are not tappable until they accept. Enough “report as spam” hits can rate-limit or burn the bot phone.
  • Not anti-bot. Text (or even an image-captcha attachment) only proves they can complete a 1:1. Do not design a pluggable GateCheck framework for CAPTCHA buttons, wallet signatures, or per-invite-link rules — Signal cannot host those.
  • Scope: this is moderation, not translation/transcription. Treat it as an explicit product expansion.
  • CVM: in-place deploy only. Do not wipe group-prefs-translation / signal-config-translation.

Out of scope

  • Restricted-in-group state, mute, hold channel / “limited thread”
  • Telegram-style CAPTCHA buttons, inline keyboards, / command picker
  • Per-invite-link rules (Signal has one group link)
  • Wallet / signature verification
  • Pluggable onboarding-gate framework
  • Running the bouncer on Language Thread sidecars
  • Reusing or overloading !verify

Origin

Split from #40 (Josh / @BreadrichEngels). #40 remains the place for the broader “gated entry like Telegram” request and the platform limits.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions