Releases: veilnetwork/xVeil
Release list
xVeil v0.13.41
[0.13.41] — 2026-09-04
Fixed
- Refusing the bundled seeds left the node nowhere to look. Two questions
shared one answer:builtin_seed_policysays whether the node may dial the
compiled-in seed list, and the meeting-point checkboxes say where it may look
for a first peer. The composition took the seed answer for both, so a refusal
wrotemeeting_points = "off"— no DHT, no Nostr, no LAN. That cost little
while a seed list existed; it ships empty on every network now, so the refusal
removed the only way left to find anyone while removing nothing that existed.
Measured on a user's machine: the app's node up with zero peers, zero sessions
and zero outbound connections, while a peerless node beside it met all three
seeds through Nostr in thirty seconds. The opt-out stays and still forbids the
compiled-in list; only the coupling goes. - The switch said the shared seeds were on while the node had refused them.
An open space that will not answer resolves tofalsefor the node — fail
closed, so an unreadable setting cannot undo a refusal — while the control's
read returned "no answer" and left the switch on its boot default. The person
had a control saying the seeds were in use, a node told the opposite, and no
warning, because the re-offer card keys off that same value. Both reads answer
the same way now. - Quitting crashed the app, and Error Reporting held it for 150 seconds.
windowManager.destroy()isPostQuitMessage(0)on Windows, so the window
outlives the message loop,OnDestroynever runs, and the controller dies in
~FlutterWindow— whoseDestroyWindowdispatches messages back into a
half-gone controller.unique_ptrdoes not clear its pointer before running
the deleter, so the guard inMessageHandlerpassed. The access violation
escaped a kernel callback, which Windows turns into
STATUS_FATAL_USER_CALLBACK_EXCEPTION, and Error Reporting then kept the
process alive for two and a half minutes with the window and tray icon
already gone. Now 2 seconds, no dump, no event. - Close-to-tray answered with its default on the launch that mattered.
Riverpod builds a notifier on its first read andbuild()is synchronous, and
the only read that decides anything — the window-close handler — is on a cold
start also the first. A user who turned close-to-tray off had it honoured
until they restarted. - A dialog opened under one identity applied its answer to another. In
all-online mode a switch unmounts nothing, somountedstays true while
ref.readalready returns the new identity's services. Clearing a chat wrote
its tombstones into the wrong identity and queued a frame for the old peer in
the new outbox; the file policy was replaced wholesale; one Nostr toggle could
store a meeting-point set neither identity asked for. Guarded as a rule over
the file, not a list of known helpers. - An SSH secret and a picked folder crossed to the wrong identity. The
managed-node editor showed one identity's SSH password and private key under
another and saved them there; a folder chosen from one identity's picker
became a sync pair of another, whose scheduler then uploaded those files. - A notification for one identity was posted and attributed to another.
Re-subscribing is what an identity switch does in the notification binder, and
it left the generation alone. An alert posted after the switch is now taken
back down by its exact id. - A captured device or cloud service kept working for the identity it left.
prepareDeviceAdoptionhad no dispose fence at all and its admission lasts
seven days across restarts;CloudCapabilityService.closeknew nothing about
the transient endpoints a listing or download holds, so a stale download wrote
verified pieces and a manifest into a storage the app had stopped showing. - Auto-signing followed the user from one identity to the next. The policy
provider never watched the active identity, so anautochosen by one made
the next produce non-repudiable proof of authorship its owner never agreed to. - A delete during the directory restore left the share serving. The public
directory service set_startedbefore its first await, so a delete arriving
in that window saw an empty status, skipped the withdraw and dropped the
folder — and the late restore republished the pointer. The bearer share lives
seven days. - A captured nullable was passed where a non-nullable was wanted. Accepted
by the pinned Flutter, rejected by current stable. - A Windows or iOS stand could never have a control plane.
builder.pydid
not pass the debug-hook define on either, so those builds had no hook and no
way to gain one — which reads from outside as a node that never bootstrapped.
Changed
- veil 0.11.19.
xVeil v0.13.40
[0.13.40] — 2026-09-03
Fixed
- A native build on an ARM64 Windows machine now works without being told a
secret. BoringSSL has no assembly route for Windows on ARM64, and the crate
that knows the answer cannot reach it on a native build; the release
workflow has handed over the toolchain file that says so since v0.13.11, but
nothing handed it to a person running the builder by hand. The build died
deep inside cmake with a message naming no cause. The builder sets it itself
on an ARM64 Windows host, and refuses loudly if the file is missing.
Changed
- veil 0.11.18: two Windows CI test steps were running zero tests, and audio
playout now reports whether it actually started.
xVeil v0.13.39
[0.13.39] — 2026-09-03
Fixed
- Revoking a shared folder now stays revoked. The withdrawal was recorded by
removing the share and saving the list afterwards, so a save that failed —
a full container is enough — left the folder on disk with nothing saying it
had been withdrawn, and the next launch served the link again for the rest
of its seven days. The runtime state was already gone by then, so there was
nothing left to retry. The withdrawal is now written down first, and a
launch reads that record before it hosts anything. File shares have worked
this way all along; folders did not.
xVeil v0.13.38
[0.13.38] — 2026-09-03
Fixed
-
One bad mailbox relay can no longer destroy a message's honest copies. A
message is filed under an id that names the message, not its contents, so a
relay could answer first with a genuine id and a substituted body. Every
layer then treated the id as the message: the drain kept one body per id, a
body that would not open condemned the id, and the app told every relay to
delete it — including the ones holding the real thing, and including the
real thing itself if it arrived later. Bodies are now told apart by their
contents as well as their id, every one filed under an id is tried, and an
id is given up only when nothing filed under it opened. Replies are also
accepted only from a relay this drain asked and only when the daemon
verified who sent it; one relay's malformed answer no longer discards what
the others sent. -
The rollback anchor now follows the container instead of naming the moment
it was opened. Written once at boot, it protected only the boot: the device
ran past that commit, and restoring any later snapshot still read as a clean
continuation while the send positions that snapshot did not carry were
derived a second time. It advances with each durable reservation, which is
the point where new ciphertext becomes publishable.Three companions to that. The record now says WHICH container it is about,
so a fresh container after a wipe is a different container rather than one
that went backwards — it used to burn keys and raise an alarm at every
launch. A container whose history is not ours is no longer quietly adopted
at the next launch, which destroyed the only evidence it was ever a
different one. And an anchor write that does not reach the disk says so
instead of reporting success.
Changed
- hidden-volume 2.2.2: the Argon2 working matrix — the password's expansion,
64 MiB and up — is wiped when the derivation returns. It was freed as it
stood while the security notes said otherwise.
xVeil v0.13.37
[0.13.37] — 2026-09-03
Changed
- veil 0.11.17: repairs a test that stopped compiling in 0.11.12 and was only
caught by the project's own automated checks, not by anything run before the
tag. No behaviour change for the app.
xVeil v0.13.36
[0.13.36] — 2026-09-03
Changed
- veil 0.11.16: a node now limits how much route-discovery traffic it will
relay in total, not just how much each peer may ask it to. Forwarding one
request costs a message to every peer the node is connected to, and the old
limit was per sender — so enough senders, each behaving, still added up to
the node flooding its own neighbours.
xVeil v0.13.35
[0.13.35] — 2026-09-03
Changed
- veil 0.11.15: a node with value persistence enabled no longer pauses
everything else it is doing while it writes its periodic snapshot. The
snapshot was built on the same thread that runs the rest of the node, and on
a full store that is a large copy taken every two minutes. Affects operators
who run a seed with that setting; the app never enables it.
xVeil v0.13.34
[0.13.34] — 2026-09-03
Fixed
- Downloading a file from a shared folder no longer holds all of it in memory.
Each piece is written out as its hash checks, so what the app holds at once
is one piece rather than the whole file — which, on that path, is whatever
size the sharer says it is. Tapping Download on a large or hostile entry
could take the app out on a phone before a single byte was persisted. The
two sibling download paths had been streaming for a while; this was the one
left assembling.
Changed
- veil 0.11.14: a gossiped contact can no longer move where this node dials a
peer it was told about another way. The peer table is searched by node id to
answer "do we know this one", and that search returned whichever row held
the id — so a peer on the exchange path could rewrite the address of a peer
from the operator's own configuration, and the rewrite survived a restart. A
rumour may now refresh only the rows the exchange itself created.
xVeil v0.13.33
[0.13.33] — 2026-09-03
Changed
- veil 0.11.13: two ways a stranger could aim this node's routing are closed.
A proof-of-work challenge and its answer are relayed frames, and every fault
they could carry was charged against whoever delivered them — five of those
ban a peer, so a stranger could choose which of your relays your own node
would cut off. And an announcement claiming to be one hop from a node was
trusted over a route this device had confirmed itself, which let any peer
with a session pull that node's traffic through itself. The payload stays
sealed either way; what was at stake is who can see whom you talk to, and
who can quietly drop it.
xVeil v0.13.32
[0.13.32] — 2026-09-03
Fixed
-
A container put back to an older copy no longer re-uses a key that has
already been on the wire. Every conversation's send-position reservation
lives inside the container, beside the ratchet state it guards, so restoring
an older copy restored both: the next send re-derived a key and nonce this
device had already published, and two plaintexts under one key is not a lost
message. Nothing outside the container remembered where it had got to.The app now records the container's commit counter outside it, on the
acknowledged space only, and judges the container against that record at
every unlock. A container that is behind burns every send position the
missing commits could have reserved before a single message goes out; one
whose history is not the one this device wrote is reported as such. The
bound is deliberately generous — burning too many positions costs a peer
some skipped keys, burning too few cannot be taken back.What this stops, and what it does not. It stops a container that was
restored from a backup, synced backwards, or copied back by hand, which is
how this actually happens. It does not stop somebody who puts the whole disk
back at once, because that returns the record too. Going further needs a
counter that cannot be rewritten, which this device does not have.Decoy identities are never anchored. An anchor for a hidden space announces
that the space exists, so only the acknowledged space keeps one, and the
identities opened through the multi-space handle cannot answer the question
at all rather than being trusted not to.