/_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
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
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
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
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
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
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
* ios, android, desktop: do not add loaded items of another chat to the opened chat
apiLoadMessages could apply a loaded page to a chat it was not loaded for,
mixing messages of two chats in one list:
- the chat id was not checked at all for .last pagination, and the loading
coroutine is not cancelled when the chat is closed (PreloadItems wraps it
into NonCancellable, apiFindMessages runs in a scope of its own), so a .last
page loaded for the previously opened chat was appended to the items of the
chat opened while it was loading;
- for the other paginations the chat id was checked before the items were
loaded, but they are applied on the main thread after a dispatch, so the chat
could be switched in between.
Check the chat id (and the remote host id in kotlin) right before the items are
applied, in every pagination except .initial and opening around item that set
chat id themselves. Empty chatItems remain allowed for .last that is used for
searching.
* android, desktop: test that loaded items of another chat are not added to the opened chat
Reproduces the bug deterministically, without depending on the timing of the
race: processLoadedChat is called with a page loaded for another chat while the
opened chat has its own items.
Without the fix the items of the two chats end up in one list:
lastPageLoadedForAnotherChatIsNotAddedToOpenedChat
expected:<[101, 102]> but was:<[101, 102, 201, 202]>
beforePageLoadedForAnotherChatIsNotAddedToOpenedChat
expected:<[101, 102]> but was:<[201, 202, 101, 102]>
aroundPageLoadedForAnotherChatIsNotAddedToOpenedChat
expected:<[101, 102]> but was:<[101, 102, 201, 202]>
* Translated using Weblate (French)
Currently translated at 100.0% (379 of 379 strings)
Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/fr/
* Translated using Weblate (Italian)
Currently translated at 100.0% (379 of 379 strings)
Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/it/
* Translated using Weblate (Spanish)
Currently translated at 100.0% (379 of 379 strings)
Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/es/
* Translated using Weblate (French)
Currently translated at 100.0% (379 of 379 strings)
Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/fr/
* Translated using Weblate (Italian)
Currently translated at 100.0% (379 of 379 strings)
Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/it/
* Translated using Weblate (Spanish)
Currently translated at 100.0% (379 of 379 strings)
Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/es/
* Added translation using Weblate (Azerbaijani)
* Translated using Weblate (Hungarian)
Currently translated at 100.0% (379 of 379 strings)
Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/hu/
* website: fix translation review findings (fr, es, it, hu)
Adversarial review of the translations on this branch.
Blocking corrections:
- fr tap-the-connect-button-in-the-app named a button that does not exist.
The French app label is "Se connecter" (MR/fr/strings.xml connect_button);
the branch had dropped "se".
- it index-token-p2-cf rendered "equity crowdfunding" as "raccolta fondi",
the Italian term for a charitable fundraiser, on a string describing a
securities offering. It also contradicted its own neighbour
index-token-cta-cf ("Investi in SimpleX Chat").
- es index-hero-invest used an infinitive where the hero is otherwise
tu-imperative, and index-hero-invest-cta turned "learn more on Wefunder"
into "learn more about Wefunder".
- hu more-info moved to "tudnivalok" while its show/hide partner hide-info
still said "informacio"; both labels belong to one button, so hide-info
is realigned.
French quality:
- "ratchet steps" had become "chaine de cles" (keychain). Every other locale
keeps "ratchet", as does the sibling string in the same table.
- Five Wikipedia links had been de-localised to en.wikipedia.org while
simplex-network-overlay-card-1-p-1 still pointed at fr.wikipedia.org.
Restored the French targets (es and it localise these too) along with the
French release title of The Mauritanian.
- "each node is discoverable" had become "identifiable", contradicting the
same sentence's "ne permet pas de decouvrir les serveurs".
- "relais de messagerie" reads as an SMTP mail relay; docs-dropdown-16 now
says "relais de chat" and matches the nominal pattern of docs-dropdown-4/5.
- Realigned terminology against untouched strings: base de donnees des chats,
chatbot, files d'attente, code QR, annuaire, and the infinitive used in the
comparison table.
- Removed a dangling feminine pronoun in the-instructions--source-code.
- Unified apostrophes, em dashes and non-breaking spaces across the 92 strings
this branch touches.
No keys added or removed; all four files stay complete against en.json.
Not addressed here, pre-existing and not introduced by this branch: fr
hero-overlay-card-3-p-3 has lost its "Read more" anchor, hu
hero-overlay-card-3-p-3 describes the July 2024 Trail of Bits review as a
repeat audit of cryptographic and networking components rather than a review
of cryptographic design, and es docs-dropdown-4/5 read as noun phrases rather
than "Host an SMP/XFTP server".
---------
Co-authored-by: Ranakan <ranakan.dev@protonmail.ch>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: No name <usir.alerts@onionmail.org>
Co-authored-by: Evgeny Poberezkin <e.poberezkin@me.com>
Co-authored-by: summoner001 <summoner@disroot.org>
Co-authored-by: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com>
* desktop: fix rotated video squashed on playback and preview rotated twice
vlc applies the display matrix before a frame reaches the vmem callback, so the
buffer has to be requested with the sides swapped for the transposed orientations,
and the snapshot must not be rotated again by hand. Read the snapshot on the event
thread, where the render callback writes it, and draw the inline playback surface
with FillWidth so a video narrower than the item fills it like its preview does.
Bound the requested buffer: the size comes from a received file, so it is capped by
area, cannot be zero, and a frame that does not fill the bitmap is dropped.
* desktop: harden the video frame path against crafted files
Only transpose the buffer for the track's own sides - the size libvlc passes is
already rotated, so swapping it would recreate the squash for a file declaring a
rotation with a zero-sized track. Copy the frame inside the render callback, on
vlc's thread, where the native buffer is guaranteed to exist, and hand only the
copy to the event thread. Drop a frame rendered with a format the bitmap was not
sized by, or arriving before any buffer was allocated. Divide the pixel budget by
a side pinned at 1 instead of scaling both sides, so a 2000000000x1 declaration
cannot take 45 times the budget. Publish the bitmap only when skia took the
pixels, size the copy after a rewind, and log a failed snapshot conversion
instead of throwing it into callers that have no handler for it.
* ui: show role (if exists) and remove "new" in open channel/group alerts
* restore removed strings
* info color
* docs
---------
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
* desktop: add bounded animated image decoder
Skia's Codec is already on the desktop classpath through skiko and decodes
both GIF and animated WebP. The frames come from a file somebody else
composed, so the decoder is bounded before it allocates: the raster is
measured in bytes with the sides multiplied as Long, each side is capped
separately so an extreme aspect ratio cannot slip under the byte budget, and
the encoded size is checked before the bytes are copied into native memory.
Anything outside the bounds, or any failure, keeps the still image the chat
already renders.
Nothing calls this yet.
* desktop: animate GIFs in chat items and full screen
Both views drew the first frame only. The full screen view also decoded its
still on every recomposition, which an animation recomposes once per frame,
so that decode is remembered against the data it comes from.
The chat list preview stays a still image: it is a 36dp box that the desktop
layout keeps on screen the whole time, so animating it would hold a raster and
spend a frame of work per listed chat, without pause.
Removes the two markers left for this work.
* desktop: don't decode animation frames that cannot be seen
With media blur on, a blurred image is only revealed while the mouse is over
it, so every frame was decoded, uploaded and then blurred away again for
nobody - and the blur is a render effect re-run per frame. Frames now decode
only while the image can be seen, which also stops motion showing through a
blur that is there to hide it.
Passing the blur state to the view is why the shared signature changes; coil
drives its own animation on Android, so there is nothing to pause there.
* docs: move animated images plan to plans/
* docs: drop file path references from animated images plan
* docs: correct animated images plan against the code
* desktop: correct animated image comments
* desktop: reduce animated image comments
* desktop: correct and bound animated image decoding
* docs: correct animated images plan against measurements
* desktop: fuse the animation prior frame decision
* docs: cover desktop animated images in spec and product
* desktop: drop the unused animated image component
* desktop: return the animation frame instead of its state
* docs: correct the animated images documentation
* desktop: don't decode animations under the full screen viewer
* desktop: bound the frames an animation rebuilds
* desktop: pause animations under any full screen modal
* desktop: stop animations that alternate expensive frames
* desktop: read what playing a frame needs only once
* desktop: close the codec of an animation outside the bounds
* desktop: wait out what an animation frame cost to decode
* docs: correct animated images claims against the code
* desktop: bound the frame count where the others are bounded
* desktop: don't wait out a stall an animation frame did not spend
* desktop: say what the slow frame constants stand for
* desktop: don't decode animations behind a minimised window
* desktop: make the animation frame wait testable
* desktop: bound the file size where the others are bounded
* desktop: pin the frame wait clamp in its test
* desktop: keep the frame wait clamp private
* desktop: reduce animated image comments
---------
Co-authored-by: sh <github.shum@liber.li>