simplexmq 92b3d049 reshaped NameRecord text fields from Maybe Text to
Text (empty string sentinel). Adapt firstNameLink to take Text directly
and treat T.null as "absent". dispatchResolvedRecord destructure
unchanged; passes the text values straight through. apiVerifySimplexName
switches from Just/Nothing pattern to a T.null guard with the same UX.
Test fixtures updated.
Addresses the TOFU vulnerability where peer-claimed simplex_name was
accepted unverified. Adds:
- contacts.simplex_name_verified_at + groups.simplex_name_verified_at
(M20260606_simplex_name_verified)
- APIVerifySimplexName ChatRef command: RSLV-resolves the claimed name
and compares the resolved link to the peer's stored connection link;
on match writes verified_at and emits CEvtSimplexNameVerified;
on mismatch emits CEvtSimplexNameVerifyFailed
- CEvtSimplexNameUnverified passive warning emitted on incoming XInfo /
XGrpInfo when a name claim arrives without a current verification
- updateContactProfileWithConflict / updateGroupProfileWithConflict
clear simplex_name_verified_at whenever the peer's claim transitions
(any value change including Nothing<->Just): the prior verification
was bound to the prior claim.
UI can surface the unverified indicator next to a contact / group's
name, and prompt the user to invoke the verify command. This shifts
the security model from "TOFU + last-writer-wins" to "TOFU + on-demand
RSLV verification".
Six call sites duplicated the same forM_ ((,) <$> claim <*> displaced)
shape emitting CEvtSimplexNameConflict. Extract to a single helper so
future call sites don't drift on whether to emit, and so the conflict
event shape change (post-Task-3 SimplexNameConflictEntity split into
SNCEContact / SNCEGroup) propagates through one site.
Audit flagged the column as "INSERTed but never UPDATEd". This is by
design per the prior plan's connect-via-plan flow: the column is a
transient carrier between connection-creation and contact-creation.
After the Contact row is created via XInfo handling, contacts.simplex_name
is the source of truth and the connections value is a historical snapshot.
Documents the intent so future readers don't reflag it.
Privacy: every miss previously broadcast the candidate name to every
enabled SMP server. Now only NETWORK / TIMEOUT failures fall through
to the next server; definite resolver answers (NAME / AUTH / CMD
PROHIBITED / other ERR) stop iteration.
simplexmq 5ee014dd reshaped NameRecord to align with the Python resolver
JSON: nrChannelLinks/nrContactLinks (lists of NameLink) became
nrSimplexChannel/nrSimplexContact (Maybe Text); nrDisplayName became
nrName; nrResolver was added; the NameLink wrapper type and nrIsTest/
nrExpiry/nrAdminAddress/nrAdminEmail fields were dropped.
Update dispatchResolvedRecord destructure and firstNameLink signature
to the new Maybe Text shape, and refresh the ResolveNameTests fixtures
and assertions accordingly.
dispatchResolvedRecord now picks the first nrContactLinks (NTContact) or
nrChannelLinks (NTPublicGroup) entry from the resolved record, decodes it
as AConnShortLink, fetches the short-link data, and eagerly calls
createPreparedContact / createPreparedGroup with the simplex_name set.
Returning CPContactAddress (CAPKnown ct) / CPGroupLink (GLPKnown g ...)
mirrors the local-store-hit branch of connectPlanName: hit and miss
converge on the same plan shape, so the connectWithPlan caller cannot
distinguish where the prepared row came from. Threading uses the
existing Maybe SimplexNameInfo parameter added in c6f26150 for the
local-prepare path -- no new write path or transient carrier.
Pure helper firstNameLink is extracted and exported so the link-picker
contract is testable without a DB / agent. ResolveNameTests gains five
cases covering the per-type selection, the first-link policy, and the
empty-list to CESimplexNameNotFound collapse.
- updateUserProfile no longer writes contact_profiles.simplex_name on
the user's own row (the column is reserved for peer claims; the user's
broadcastable name lives on contacts.simplex_name via uct.simplex_name).
- updateMemberContactProfile_'/Reset_' now write simplex_name; new
updateMemberProfileWithConflict / updateContactMemberProfileWithConflict
variants run conflict-clear and return the displaced name, with
processMemberProfileUpdate emitting CEvtSimplexNameConflict.
- createContact_ runs conflict-clear before INSERT to avoid UNIQUE
constraint violations on first-write peer collisions, returning the
displaced name; createPreparedContact / createDirectContact thread it
through to APIPrepareContact and saveConnInfo XInfo for event emission.
- groups conflict-clear takes ProfileId directly (avoids the NOT IN (NULL)
silent-noop edge case when groups.group_profile_id is ON DELETE SET NULL).
- Moves clearConflictingContactProfileSimplexName_ to Shared.hs so
createContact_ can call it without inducing a circular import.
Write paths:
- updateContactProfile_' / updateGroupProfile_ now set the new
contact_profiles.simplex_name / group_profiles.simplex_name columns
from Profile.simplexName / GroupProfile.simplexName respectively.
- createContact_ INSERT writes Profile.simplexName to the new
contact_profiles column (separate from the existing simplexName arg,
which still writes contacts.simplex_name — the user's locally-known
label).
Read paths (closing Task 2's deferred sites):
- toContact splits simplex_name reads: Contact.simplexName from
contacts.simplex_name (existing); LocalProfile.simplexName from
contact_profiles.simplex_name (new column).
- toGroupInfo similarly splits: GroupInfo.simplexName from
groups.simplex_name; groupProfile.simplexName from
group_profiles.simplex_name.
- ProfileRow / rowToLocalProfile, toContactRequest, getUserContactProfiles,
toGroupProfile, getProfileById, groupMemberQuery, getGroupAndMember_,
saveRcvChatItem-related quotes — all extended to read p.simplex_name
and decode it into LocalProfile.simplexName / GroupProfile.simplexName.
Conflict handling (Decision B):
- clearConflictingContactProfileSimplexName_ / *Group* helpers do an
atomic UPDATE-with-RETURNING that NULLs simplex_name on any other
row in the same user that would collide on the partial UNIQUE index,
returning the displaced row's display_name.
- updateContactProfileWithConflict / updateGroupProfileWithConflict
bundle clear+update in one transaction.
- processContactProfileUpdate / xGrpInfo invoke the *WithConflict
variants and emit CEvtSimplexNameConflict when a displacement
happened (with the claiming and displaced display names).
Adds ChatEvent CEvtSimplexNameConflict and SimplexNameConflictEntity
(SNCEContact / SNCEGroup) with JSON instances and View.hs rendering.
Adds a Maybe SimplexNameInfo field to the wire-level Profile and
GroupProfile (and their DB sibling LocalProfile). JSON instances are
TH-derived with omitNothingFields = True, so the new optional field is
auto-handled and old peers / old JSON without the key decode as Nothing.
Existing record-construction sites are set to simplexName = Nothing as
a placeholder. Outgoing dissemination (userProfileDirect /
userProfileInGroup) and incoming persistence wire-up land in follow-up
commits. redactedMemberProfile passes the field through, matching how
peerType is preserved.
Comment claimed SEDBException is re-thrown as CRITICAL but only
SEDBBusyError is (via the `critical` helper at Subscriber.hs:136
and the showCritical branch at :1695). Updated to describe the actual
behaviour.
connectPlanName previously threw CEInvalidConnReq when a name lookup
hit a contact / group row whose preparedContact / preparedGroup was
NULL. The error message ("Connection link is invalid, possibly it was
created in a previous version") was wrong: the name resolved fine,
the device just has no link material to reconnect via (typical for a
contact created via the XInfo handler rather than the prepare path).
Introduce CESimplexNameUnprepared SimplexNameInfo for this case.
Also mirror the link-based path's gPlan (Commands.hs:4133) for groups
whose membership state is GSMemRemoved — return CESimplexNameNotFound
rather than GLPKnown for a removed-member group, since GLPKnown for
removed members would be inconsistent with how /_connect plan over a
short link handles the same situation.
Previously the function always probed contacts.simplex_name first and
fell through to groups for NTPublicGroup misses. But the discriminator
(`@`/`#`) is embedded in the stored bytes via strEncode, so an
`#group.simplex` lookup can never match a contact row. Reorder to
case on nameType up front, saving one DB query and one withFastStore
transaction acquire on the group path.
connectPlanName now distinguishes "name not found" from "connection
link is invalid". CEInvalidConnReq's message ("Connection link is
invalid, possibly it was created in a previous version") was
misleading when a user typed @alice.simplex against a database that
simply has no contact by that name.
The two "missing prepared link" cases stay on CEInvalidConnReq —
the lookup found a row but the stored link is unusable, which is
closer to the existing semantics. The two truly-missing cases
(no contact found / no group found) move to CESimplexNameNotFound,
which also surfaces the name back to the client for a precise UX.
APIConnectPlan/Connect flip from Maybe AConnectionLink to Maybe ConnectTarget.
connectPlan dispatches CTLink -> connectPlanLink (the prior body, renamed)
and CTName -> connectPlanName (new) which looks the name up against
contacts.simplex_name and groups.simplex_name via the new
getContactBySimplexName / getGroupInfoBySimplexName store helpers.
The hit path returns the contact's / group's stored conn link from
preparedContact / preparedGroup; missing prepared state or unknown
names return CEInvalidConnReq. RSLV on-chain resolution is out of
scope for this branch -- known-name lookup is enough for conversation
display, search, and external-link share.
connLinkP_ parser is unchanged: APIConnect's preparedLink_ stays
ACreatedConnLink-shaped, and the Connect / APIConnectPlan parsers
already use inline strP for ConnectTarget without going through the
helper.
Directory.Service call sites updated to wrap their AConnectionLink in
CTLink when invoking APIConnectPlan.
createPreparedContact/createPreparedGroup gain a Maybe SimplexNameInfo
parameter that they write to contacts.simplex_name / groups.simplex_name
directly. createConnection_ writes to connections.simplex_name as a
transient carrier for the connect-via-plan path. The XInfo handler
in Library/Subscriber.hs reads the connection's simplexName and passes
it to createDirectContact so the final contact row captures the name.
All current callers pass Nothing; the actual flow lights up when
APIConnectPlan accepts ConnectTarget and connectPlanName threads the
name through (later commits in this branch).
Uses the upstream ToField SimplexNameInfo (simplexmq 0b334b66) for
writes; reads continue to go via the soft-degradation helper.
* plan: web previews for channels
* types for recipient side to support channel web previews and domain names
* fix
* migrations
* update schema and api types
* update schema
* rename migrations
* core: check member role
---------
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
* core: fix /start remote host parser when iface name contains a space
The iface= field used jsonP (which calls takeByteString and strict-decodes
the entire remaining input as JSON). When port= followed iface=, the strict
decode failed on the trailing data and the text1P fallback stopped at the
first space inside the JSON-quoted interface name (e.g. "Ethernet 2"),
leaving unparseable junk and producing "Failed reading: empty".
Replace jsonP with a bounded quotedP that consumes only up to the closing
quote, leaving port=… for the next parser.
* plan: document fix for /start remote host iface-with-space parser bug
* core: update simplexmq
* update agent api
* update simplexmq
* core: add flag to User to use client services
* update simplexmq
* cli command to toggle service for a user
* test, fix
* query plans, core/bot api types
* remove local package reference
* increase server queue size in tests
* show client service status in users list
* update query plans
* cli: fix redraw slowness (#6735)
* cli: add pland to fix redraw slowness
* updtae doc
* cli: decouple key reading from processing via TQueue
* schema and bot types
---------
Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>
* Revert "core: forward compatible support for owners/admins/moderations deleting channel and public group messages without limitations (#6962)"
This reverts commit 08108ebabb.
* core, ui: allow indefinite deletion from history for public channel/group owners/moderators
* style
Co-authored-by: Evgeny <evgeny@poberezkin.com>
* refactor
* show error on deletion
* better alerts
* test
* plan
* simplify test
* bot api docs
* refactor
* test that removed from history is not delivered to the new subscribers
* fix, refactor
* fix
* rename
* rename predicate in UI
* rename
* do not forward channel deletions from history
* remove redundant check
---------
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
* plan: delete channel messages
* core: allow deletion of own messages in public groups in channels to moderators-owners
---------
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
* core: test support chats in channels, CLI defaults to sending as member in support chat
* ui: enable support chats in channels
* use correct scope when sending from UI
* more readable
* remove test output
* show member support chat in channels
* preference for support chats
* ios: types for support preference
* mp: support preference types
* show support preference in UI
* fix ios
* make support preference optional in JSON parser
* update string
* change strings, pass parameters to prefs
* refactor kotlin
* take support preference into account
* refactor core
* do not show broadcast placeholder in support scope
* move role check, add pref check on update
* support preference test (failing)
* fix version
* fix tests
* warning alert when enabling chats with admins
* revert on dismiss
* update text and icons
* query plans
---------
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
* core, ui: item about no e2ee in public channels
* fix, refactor
* all tests
* update bot api types
---------
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
* directory: support public channels and relay-based groups (plan)
* types
* amend types
* directory types, resolve known link
* implementation, test fails
* fix test
* fix test
* more test
* minimal test
* more test
* debug test
* clean up
* remove debug logs
* refactor
* use group/channel terms correctly
* remove unsupported commands
* manage profile update
* owner left the channel
* more tests, correct response to sent link
* re-registration
* /help and /link commands
* correct listing for channels
* fix test
* fix bot api
* refactor
* do not include link data in GLPKnown
* refactor
* diff
* undo refactor
* simplify
* remove harness test
* remove flip
* add v6.5 app requirement for channels
* add website support
* update bot api types
* correct member count, fix test
* members -> subscribers
* add link to channel description
* fix css
* move version note
---------
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
* core: share links to channels and verify shared links when connecting
* refactor
* improve
* refactor case
* simplify
* exctract encodeChatBinding
* share api
* corrections
Co-authored-by: Evgeny <evgeny@poberezkin.com>
* tests
* verify signature in the tests
* drop signature if context does not match on reception
* try to test "fake" forward
* fix
* fix direct chat sharing test
* channel test
* sign link
* rename api
* refactor view
* chal link item CLI view, tests
* clean up
* share channel in channel as channel
* query plan
* fix test
* refactor
* whitespace
* simpler
* refactor
* dont use partial field update
---------
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>