Commit Graph
6700 Commits
Author SHA1 Message Date
shum 175d200787 store: move the HTTP helpers into Web.Http 2026-10-21 22:08:32 +00:00
shum bb08fcfc99 store: read the names and chain settings 2026-10-21 22:08:32 +00:00
shum 4a8be1a7a7 store: keep any ini out of the Docker context 2026-10-21 15:53:41 +00:00
shum a454ba63ea store: move badge service app to store service 2026-10-21 12:27:02 +00:00
shum 4aaeadd591 store: rename badge service to StoreService 2026-10-21 12:19:49 +00:00
shum 8990562850 store: pin service JSON tags 2026-10-21 12:00:54 +00:00
Evgeny Poberezkin f06910698d Merge branch 'names' into ab/names-registration-primitives-sponsored 2026-10-14 15:53:36 +01:00
Evgeny Poberezkin a1e000d2be Merge branch 'master' into names 2026-10-14 15:50:56 +01:00
sh f65c2da587 nodejs: lazy per-backend libsimplex download (#7543)
* ci: drop dlltool from nodejs libs release

* nodejs: download libsimplex into the user cache

* nodejs: load libsimplex at runtime per backend

* support bot: select db backend with --db

* python: match backend switch error text

* nodejs: download prebuilt add-on at runtime

* ci: build nodejs add-on for libs release

* calculator bot: load libs and add-on in image

* support bot: document add-on override

* nodejs: keep worker test output free of colors

* nodejs: verify sha-256 of downloaded assets

* python: pin core libs release with sha-256

* ci: add packages release script

* ci: release nodejs add-on and open release PR

* ci: publish packages after release PR

* nodejs: keep npm from building the add-on

* calculator bot: build the add-on in the image

* ci: merge package release workflows

* ci: open release PR when publishing fails

* ci: build the add-on with node-addon-api only

* ci: skip publishing in a called packages workflow

* ci: update package release actions to Node 24

* nodejs: fix receive test timeout

* ci: test packages on pull requests

* nodejs: reuse the database in the close stress test

* ci: keep failed package tests from hanging

* ci: skip preset server tests

* ci: plan package builds from the changes

* ci: test packages with a given libsimplex

* ci: test packages with libsimplex of core changes
2026-10-14 09:53:53 +01:00
Alain Brenzikofer 9c524dd28a round 11 converged 2026-10-14 09:38:53 +02:00
Alain Brenzikofer f85d53e918 intent signature includes duration of reservation. verify field before submitting the tx 2026-10-13 19:37:42 +02:00
1b329f41fb core: wallet for names (#7684)
* core: wallet key for name ownership (CLI only)

A name has to be owned by an address the client can still derive after a
restart or on a new device. This adds that key and nothing else.

/wallet create makes one BIP-39 key per device and one BIP-44 account per
chat profile under it, /wallet shows the address that would own the next
name that profile buys, /wallet import and /wallet export move the key with
its recovery phrase.

A name key sits at m/44'/60'/<profile>'/0/<name>, which is ordinary BIP-44,
so the phrase reaches the same addresses in other wallets. No signing, so
nothing can be bought or edited yet.

Split out of #7390 / #7425.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: pin simplexmq with eth crypto merged into master

ab/eth-crypto forked before Crypto.Entitlement, which Chat.Badges imports,
so the branch had to be merged with simplexmq master before it could be
pinned here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: wallet review fixes, and show the derived addresses

Concurrency: the account index is incremented in SQL and read back in the
same transaction, so two profiles cannot be handed the same key. Importing
a phrase is one transaction and single_seed is UNIQUE, so a phrase cannot
be discarded in favour of a key created meanwhile, and a device cannot end
up with two keys.

Wallet commands are no longer forwarded to a remote host: the recovery
phrase must not leave the device, and the raw command is logged there.

/wallet delete removes the key, confirmed by the last word of the phrase,
so creating a key before importing your own is no longer a dead end.

/wallet now shows every profile on the key with the first two name
addresses each, to check derivation against other wallets. Hidden profiles
are left out, as they are by /users. A bad phrase no longer says which word
was wrong. /wallet export uses the profile's own key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: make the wallet an API, not a user-facing feature

Names commands will use the wallet; users do not. The commands move to
/_wallet, and the help section, its topic and the changelog entry are
removed. The tests drive the API end to end.

Comments cut to what the code does not say, dead exports removed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: wallet export and delete act on the device key

The key is one per device, so both commands read it directly instead of
going through the active profile's account. A profile without an account
of its own can now export, and the delete error says that the key goes for
every profile on the device. The confirmation word is lowercased, as the
phrase is when imported.

Two known limitations are written down: account indexes restart at 0 after
an import, so profiles are bound in the order they ask for a key, and the
gap left by a hidden profile in the printed indexes shows that it exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: /_wallet delete takes no confirmation word

The wallet is an API, and no other API command takes a typed confirmation:
/_delete user takes the profile password. The word was also no barrier to a
caller, which can read it from /_wallet export. Confirming belongs in the
command that will wrap this one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: name the unique index on wallet_seeds

The one key per device rule is meant to be lifted later. SQLite cannot drop
a column with an inline UNIQUE, or its automatic index, so lifting it would
have meant rebuilding the table. As a named index it is a DROP INDEX and a
DROP COLUMN.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: export the secret of a single name key

/_wallet export gives the seed mnemonic, /_wallet export <account> <name>
gives the secret of one derived key, so a single name can be handed over
without the seed. Both are what a wallet takes on import: the mnemonic as a
recovery phrase, the secret as hex.

The commands are named for what they return, APIWalletExportSeedMnemonic
and APIWalletExportDerivedSecret, as the difference is which secret leaves
the device.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: reject a key index BIP-32 cannot harden

A.decimal wraps into Word32 and hardened leaves anything at or above 2^31
alone, so /_wallet export 4294967296 0 returned account 0's secret under the
path m/44'/60'/0'/0/0. Handing out a different account's key for a mistyped
index is the wrong failure for this command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: /_wallet bind, and name the other profiles instead of numbering them

Restoring a chat database older than the key loses which account each
profile had, and the keys are still there, so /_wallet bind <account> sets
it by hand. The counter moves past an account bound this way, and an
account another profile holds is refused.

/_wallet now shows the paths of the active profile only, and names the
other profiles on the key. Their account indexes were the gap that showed
a hidden profile exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: say same seed, not same key, for the other profiles

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: create makes the seed, bind claims a path

The two were one command: on a device that already had a seed, /_wallet
create only bound a profile. Now create makes the seed and refuses when one
exists, and binds every profile that exists, as a new seed has no account
that already owns a name.

/_wallet import binds nothing. Which account a profile had is what an
import is recovering, and the seed does not say, so the profile says it
with /_wallet bind <account>. /_wallet bind with no account takes the next
free one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: the known issue is now about bind, not import

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: create binds no profile either

Create and import differed only in whether they bound a profile, which made
the convenient one inconsistent. Neither binds now, so the store keeps one
function for both, and a profile reaches an account only through bind.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: test the restore cases the key has to survive

A database backed up after the key carries the key and the binding, so the
restore is all of it and an import is refused. A database backed up before
the key comes back with the profiles and no binding, so the phrase is
imported and each profile says which account was its. A key imported before
the database is restored is replaced by what the backup held.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: drop the types nothing reads, and say account where it means account

AccountRef and WalletAccount were built and never read: deriveNameKey now
returns the key, and the address comes from addressFromPrivateKey, which
already exists. SeedId is a plain Int64 like every other row id here.

The master key is derived once per command rather than once per name key,
which is where PBKDF2 runs.

A device has a key and a profile has an account, so /_wallet says "no
account for this profile" rather than repeating "no wallet key", which is
what it says when the device has none.

Also: no "wallet: " in front of "bad chat command: ", strEncode for the
address, the repeated seed lookup in one place, comments cut to what the
code does not say, and the export test pins its addresses and secrets,
including one whose first byte is zero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: store the seed as bytes, and bind one account per profile

On Postgres a bare ByteString binds as a text literal, so entropy with a
backslash or a high bit was rejected and entropy with a zero byte was
truncated: the BIP-39 test vector stored as an empty seed. Blob columns go
through DB.Binary here, as every other one does.

/_wallet bind with no account moved a profile that already had one to a
fresh account, abandoning the old one without saying so. It refuses now;
moving is asked for by number.

The counter could walk past 2^31, where BIP-32 hardening folds every index
back onto a low one, so a profile handed 2147483648 derived account 0's
keys and printed account 0's path. Binding refuses once the counter is
there, and a very long index is now rejected on its digit count rather than
after reading it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* docs: rfc for the keys that own names

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: pin simplexmq for the BIP-44 path, and use it

ethereumPath now takes the address index, so the path a name key sits at is
built once in the library rather than again here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: refuse an account index BIP-32 cannot harden, whichever way it arrives

The counter path checked it, the explicit one did not, and the parser is not
the only caller: processChatCommand takes APIWalletBind from library callers
too. hardened leaves an index at or above 2^31 alone, so account i and
i + 2^31 are the same key while the duplicate check compares the stored
integers, and two profiles could hold one account's names.

The rfc now says what hiding a profile from /_wallet does and does not
protect: the seed is one per device, so whoever unlocks any profile can
derive every account.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: pin simplexmq with the wordlist embedded from the upstream file

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* docs: cut the rfc to what stands on its own

Drops the references to other documents and to what is or is not in a pull
request, so the design reads without them, and the list of tests, which
restated their names and would drift. 164 lines to 134, against a median of
117 for the rfcs here that define schema.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* docs: what the stealth meta address is for

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* docs: the wallet is not only for names

The seed, the accounts and the derivation are a wallet. Owning a name is the
first thing it is used for, not what it is, and the title and the problem it
solves now say so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* docs: the reason for a key per name is that owners are public

One address owning several names links them: whoever knows one name can read
its owner and find the others. That is the reason, not the narrower export.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* docs: give the reason for the counter before what it costs

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: names are the device's, so drop the per-profile account

A name's profile is the record it resolves to, not the key that owns it, so
the account level was carrying a mapping nothing needs. It was also the only
source of the profile to account ambiguity after a restore, of /_wallet
bind, and of the index gap and the refusal that told a visible profile an
account was held by one it cannot see. All of it goes.

Names sit in account 0 from index 1. Index 0 is left unused so neither the
names nor the profile accounts, which start at 1, claim the origin of both
dimensions. users gains no columns at all now, so the migration is one
CREATE TABLE.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* docs: rfc for names counted per device

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: one command for the seed, whichever way it arrives

create and import differed only in where the entropy came from and already
shared one store function, so they are one command with the source named:
/_wallet create new, or /_wallet create seed=<phrase>. Naming it means no
seed is generated by typing a prefix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: the argument is a mnemonic, so name it one

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: the column holds entropy, so name it entropy

The mnemonic comes back from the entropy exactly, which is what export
returns, and the seed PBKDF2 derives from the mnemonic is computed when a
key is needed and never stored. The column was the one thing called seed
that is not the seed, which is why it needed a comment saying so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* docs: 24 words is a choice, not a default

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: deleting the seed reports ok, not the empty wallet

Delete re-ran /_wallet, so a successful delete printed "no wallet key",
which is the same words the error uses and reads as a failure. There is
nothing to show after a delete.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* docs: the export argument is an index, not a name

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* core: export says which kind of secret, so other kinds can follow

/_wallet export name <k> rather than an index straight after export. The
bare form stays the seed mnemonic, and a profile's key can be added beside
the name one without the argument having to be guessed from its shape.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX

* disencourage hidden profiles using names

* let remote use wallet, but filter logs to avoid leaking secrets

* change RFC wallet architecture to BIP-85 per profile and hardened paths for names

* introduce keyring as the thing a profile binds to

* restructure RFC

* shorten

* drop BIP85 and flattenhierarche. no keyrings

* /goal iteration 4 - unconfirmed

* /goal iteration 5 - unconfirmed

* repin

* rename to adapt

* cut comments

* next review round

* minimize diff and align style even more

* missing type mappings

* fix review comments

* self-review iteration 2

* self-review iteration 3

* self-review iteration 5

* iteration 6

* self-review iteration 9

* self-review iteration 10

* repin

* rng preserve

* refactor WalletSeed type, see mq wip

* repin

* infallibel wallet and account derivation

* review iterations

* repin to mq, derivation fallible again

* repin

* use deriveAddress, update simplexmq

* simplexmq

* add down migration for db and drop redundant masterkey field in db

* repin to simplexmq PR

* fable review fixes

* iterations on /goal

* treat hidden profiles same as normal ones. they can hold accounts now

* reduce explicit types

* repin mq

* doc cosmetics

* simplify

* query plans

* query plan

* simplify

* query plans

* simplify more

* fixes for comment ^^

---------

Co-authored-by: Alain Brenzikofer <alain@encointer.org>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-13 18:22:55 +01:00
7306f3aeef core: alerts with information about registering names (#7653)
* minimal api change

* embed status. reduce

* drop fake owner change

* no-valid-link fix

* add UX flow for name lookups

* diagram edits

* corrections

* update name lookup API

* manual renaming

* implement core + cli

* implement UI

* fix

* build iOS app blindly

* shrink diff

* fix review comments

* review iterations

* opuse 5.5 /goal iterations

* fixes EP review

* fixes

* fixes

* short goal iterations

* refactor core-UI API to NameWarning with logic in core

* diff cosmetics

* simplify and normalize diagram

* add excalidraw normalizer script and test git hook

* cosmetics

* commit wireframe collab doc to repo as README

* review round on UX coverage

* iterate on /goal

* iterate on /goal

* add tests

* iterate on /goal

* fix freshness bug with local chats: When core marks a chat verified for a name, it now first un-verifies the user's other chats of the same kind:
  - a contact name covers contacts and business chats;
  - a channel name covers channels.

* align app divergences

* docs fixes

* iterations on /goal

* align excalidraw

* align and simplify core/UI/UXcanvas

* 27 rounds of /goal

* review

* core: simplify connect plan with name lookup

* simplify

* cache names in a separate table

* cache names resolution

* wip

* all

* fix

* remove cache

* update plans

* simplify

* bot types

* change types

* simplify

* update bot types

* remove review

* update view, tests

* simplify "unverify"

* update query plan

* changes

* simplify

* test helpers

* simpler

* update query plans

* fix

* query plans

* query plans

* fixes

* query plans

---------

Co-authored-by: Alain Brenzikofer <alain@encointer.org>
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-13 17:05:15 +01:00
Alain Brenzikofer 965046fe10 adapt contract updates 2026-10-13 16:04:42 +02:00
EvgenyandEvgeny @ SimpleX Chat 2f4a443dda ui: markdown viewer (#7702)
* ui: markdown viewer

* simplify

* fixes

* plan

* ios sheet, center modal on desktop

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-13 14:38:37 +01:00
EvgenyandEvgeny @ SimpleX Chat 7d870c6ff5 ui: open file after tap when it is received (#7700)
* ui: open file after tap when it is received

* fix ios

* simplify

* simplify

* better

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-13 11:24:14 +01:00
EvgenyandEvgeny @ SimpleX Chat 1bd7c95006 ui: additional check for plain domains (#7699)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-13 08:53:04 +01:00
Evgeny d673a6b021 ui: show unicode characters in address as unicode markers (#7698)
* ui: show unicode characters in address as unicode markers

* defence in depth for links in messages (they are non-clickable from parser)

---------

Current code does not have this problem, as markdown parser rejects such links, and they are not clickable - it is needed for new markdown file viewer - this way unicode links in markdown show with unicode sequences instead of look-alike letters.
2026-10-13 08:42:59 +01:00
Evgeny Poberezkin 4b6ac88c6e ios: update core library 2026-10-13 08:40:32 +01:00
Alain Brenzikofer b4c8355693 7 rounds of /goal 2026-10-13 07:57:39 +02:00
EvgenyandEvgeny @ SimpleX Chat eadebccb4f nodejs: send error events to bot (#7691)
* nodejs: send error events to bot

* version

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-12 22:59:42 +01:00
Alain Brenzikofer 3d7ec93b80 write a plan doc 2026-10-12 18:16:15 +02:00
Alain Brenzikofer 2356e00e33 2 rounds of goal iter 2026-10-12 18:02:54 +02:00
Alain Brenzikofer 1cee07dd9f name registration service and client functions and mocks 2026-10-12 17:02:26 +02:00
EvgenyandEvgeny @ SimpleX Chat eda94a505d nodejs: send error events to bot (#7691)
* nodejs: send error events to bot

* version

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-12 13:26:48 +01:00
EvgenyandEvgeny @ SimpleX Chat 599076d7a2 packages: update nodejs libraries (#7690)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-12 09:14:37 +01:00
Evgeny Poberezkin 9711dcfc9c core: 7.1.0.11 (simplexmq 7.1.0.11) 2026-10-11 23:01:35 +01:00
EvgenyandEvgeny @ SimpleX Chat 64aa2825d5 core: release core library to simplex-chat-libs every time cabal version changes (#7687)
* core: release core library to simplex-chat-libs every time cabal version changes

* implement

* test build

* update libs release

* test library release

* create release commit, so the date is correct

* rework to create release in advance in maybe-release job

* remove test build

* move postgresql job back

* test build

* remove test build

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-11 22:01:22 +01:00
nahuhh 8ec8c451db core: fix slow group membership lookup (#7688) 2026-10-11 13:06:06 +01:00
EvgenyandEvgeny @ SimpleX Chat 07c363849b core, ui: invite member of one group to another group (#7682)
* rfc: invite member of one group to another group

* update

* update

* implement

* version

* update

* do not show error if contact was removed when joining group

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-11 09:50:42 +01:00
EvgenyandEvgeny @ SimpleX Chat 1053e9fb5c core: use simpler agent api (#7685)
* core: use simpler agent api

* simplexmq

* simplexmq, plan

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-10 23:30:31 +01:00
Narasimha-scandEvgeny Poberezkin d457bc18d2 ios: delete temp data from storage view (#7626)
* ios: delete temp data from storage view

The temp folder is the XFTP agent work directory and holds scratch
files for received-file encryption and roster transfers, so deleting it
under a running chat breaks those writes mid-flight. The button is only
enabled while chat is stopped, the same precondition core deleteStorage
relies on, and the row says how to stop chat.

* ios: show delete temp data alert over settings sheet

AlertManager presents from ContentView, which is behind the settings
sheet, so the confirmation never appeared. The alert now also warns
that interrupted transfers never complete and that videos in unsent
drafts, which live in the temp folder, are lost, and the caption names
the screen where chat is stopped.

* ios: say unsent draft videos are sent without the file

A draft video whose converted file was deleted from the temp folder is
still sent, as a preview with no file, so "lost" understated it.

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-10-10 22:38:14 +01:00
Narasimha-sc 7d8a174388 android: recognise passphrase fields as passwords for password managers (#7612)
* android: recognise passphrase fields as passwords for password managers

Passphrase fields used a hand-rolled VisualTransformation lambda, so Compose never
marked them as password fields: the autofill structure carried no hints and no
password inputType, and password managers could not identify them. The raw
passphrase was also exposed through the CopyText/ACTION_COPY accessibility action.

PassphraseField now declares an autofill content type and uses
PasswordVisualTransformation, which sets the password semantics Compose derives
that flag from.

Fields that create a passphrase declare NewPassword. Where no field would carry
plain password (first-time setup, hidden profile password) they declare both, as
managers do not recognise a form whose every field is newPassword. On the change
form the current-passphrase field supplies password, so the new fields declare
only newPassword - adding password there leaves the manager unable to tell which
field holds the new value and suppresses the save prompt.

* android: do not offer to save hidden profile passwords

Hidden profile password fields no longer declare an autofill content type.
Compose commits the autofill session, which shows the password manager's
save prompt, only after the last field with a content type leaves the
screen, so these fields never trigger it. A saved entry would reveal that
the hidden profile exists.

Rewrite the plan to match the code.
2026-10-10 21:57:12 +01:00
Narasimha-scandEvgeny Poberezkin afcaf22100 android, desktop: storage breakdown in developer options (#7603)
* android, desktop: storage breakdown in developer options

Port of the iOS Storage screen (#5529): lists each top-level entry of the app's data, config and temp folders with its recursive size, so it is possible to see what takes space.

* android, desktop: simplify storage view

Use DefaultProgressView, drop the bold total and redundant absoluteFile, and do not follow top-level symlinks (matches iOS; avoids counting the Android lib link).

* android, desktop: storage view: do not crash on names the JVM cannot convert to Path

Under a non-UTF-8 locale on Linux, File.toPath() throws InvalidPathException for a non-ASCII top-level name (e.g. a received file decrypted into tmpDir); log it and show 0 bytes. Also correct plan wording.

* android, desktop: storage view: fold path conversion into treeSize, use produceState

Also correct plan wording (du per-directory overhead, unlistable roots, Windows junctions).

* android, desktop: storage view: open as card screen like other settings screens

* android, desktop: storage view: list top-level entries as Path to keep their exact names

File.listFiles + toPath loses names that are not valid in the JVM file-name encoding (non-ASCII under a C locale, invalid UTF-8), so those entries showed 0 bytes. Files.newDirectoryStream keeps the raw bytes.

* android, desktop: storage view: normalize root paths before dropping nested roots

A root spelled with .. (e.g. XDG_DATA_HOME=/x/data/../tmp) compared as nested in another root and its size was shown nowhere.

* android, desktop: document storage view in spec and product docs

* android, desktop: storage view: single catch when listing a root

* android, desktop: do not log file paths in storage view

The walk's IOException messages contain the full path, which in tmpDir can be a received file's original name.

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-10-10 21:55:08 +01:00
Narasimha-scandEvgeny Poberezkin d72f68e6dc android, desktop: free strings returned by the core (#7595)
The core allocates every chat_* response with malloc and passes ownership to
the caller, as iOS does in API.swift. Both JNI bindings copied the bytes into a
Java string and dropped the pointer, so every command, event and file read
leaked its whole response into the C allocator - invisible to both the JVM and
the Haskell heap profiler.

Desktop frees in decode_to_utf8_string, the funnel every entry point already
goes through. Android has no such funnel because NewStringUTF is called inline,
so decode_and_free is added and the call sites routed through it. chatReadFile
frees directly on both.

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-10-10 19:08:35 +01:00
EvgenyandEvgeny @ SimpleX Chat 2e56d32b52 core: badge presentations in profiles (#7573)
* badge presentation in groups

* fix tests

* badge presentations in profiles

* update simplexmq

* change types

* badge profile presentations

* improvements

* remove proof from cards, send owner public key for channel to relay

* add test

* merge migrations

* update

* prevent fallback to PHTest

* fixes

* many fixes

* fixes

* fixes

* cleanup

* improve

* query plans

* fix test

* diff

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-10 17:48:45 +01:00
Narasimha-sc 74c6b9d930 android: show attachment options above the keyboard (#7570) 2026-10-10 17:02:40 +01:00
spaced4ndy 7c2c7503f2 ui: do not show "No relays" while leaving a channel (#7673) 2026-10-09 10:56:00 +00:00
EvgenyandEvgeny @ SimpleX Chat 10166454fd website: add sitemap, robots, llms.txt and markdown pages (#7670)
* website: markdown pages for llms

* website: add sitemap, robots, llms.txt and markdown pages

* faster site generation

* more faster

* faster

* update

* optimize build

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-09 07:53:40 +01:00
EvgenyandEvgeny @ SimpleX Chat f924d233e4 core: fix encoding for name proof (#7672)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-09 07:48:04 +01:00
EvgenyandEvgeny @ SimpleX Chat f2b3fe385c core: remove legacy field to support contact connections in groups (#7671)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-09 07:00:27 +01:00
EvgenyandEvgeny @ SimpleX Chat 9c9c86f9c0 core, ui: fix profiles, add error alerts (#7663)
* core, ui: fix profiles, add error alerts

* simplify

* query plans

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-08 22:53:59 +01:00
EvgenyandEvgeny @ SimpleX Chat a24a3b1911 website: redirect page (#7667)
* website: redirect page

* script

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-08 18:10:37 +01:00
EvgenyandEvgeny @ SimpleX Chat 64d68ff710 core: fix test (#7666)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-08 17:11:46 +01:00
EvgenyandEvgeny @ SimpleX Chat 96970373b0 core: add status when switching queue (#7664)
* core: add status when switching queue

* fix

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-08 16:31:29 +01:00
EvgenyandEvgeny @ SimpleX Chat 0c1ffa65bc core: group fixes/improvements (#7660)
* core: group fixes/improvements

* Large messages dropped by relays and hosts (the same 15602 limit was applied, without accounting for forwarding header)
* Members below v18 did not receive forwarded messages or history in p2p groups.
* Join confirmations near the size limit could fail.
* SMP servers are limited to 2 host names each.

* simplify

* remove legacy forwarding and history support

* only restrict p2p groups

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-08 15:06:18 +01:00
sh dd7768e543 android, desktop: upgrade moko-resources to 0.27.1 (#7661) 2026-10-08 12:32:57 +00:00
sh 40478c1c45 android: target API 36, AGP 8.10.1 (#7656)
* android: migrate back handling to callbacks

* android: target API 36, AGP 8.10.1

* android: opt out of predictive back app-wide
2026-10-07 10:30:26 +01:00
Evgeny Poberezkin 84f73ce8b8 website: update crowdfunding page 2026-10-07 09:53:45 +01:00
EvgenyandEvgeny @ SimpleX Chat df95de8a2c tests, fix: execute in parallel, send connected events after creating chat items, fix race when merging multiple members to one contact (#7500)
* tests: execute in parallel

* remove race in terminal

* fix some tests

* stop chat controller on exit

* fix more tests

* fix tests

* query plans

* update simplexmq

* update direct-sqlcipher

* fix tests

* query plans

* ios: update core

* fix tests

* update simplexmq and direct-sqlcipher, fixes

* simplexmq

* update simplexmq

* parallel channel tests

* update tests

* fix tests

* fix tests

* query plans

* fix test

* more tests in CI

* use ram drive in CI

* fix tests, fix directory deleting voice file before it is uploaded

* fix query

* fix query to use index

* link contact and channel owner profile when connecting via a signed link

* update simplexmq, fix ghc 8.10.7

* fix tests

* avoid double stop

* query plans

* fix more test races

* more test fixes

* fix queries

* fixes

* more fixes

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-07 08:12:02 +01:00