| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
| |
The LGPL closure check excluded only names declared at the top level of the
LGPL files, so keyring.js's `const state = () => ...`, local to
createKeyring, read as a call to photo-sync.js's top-level `state` once that
file was added. A name the file declares at any depth now shadows an AGPL
global for that file. A real call into an AGPL module is still caught
(checked by adding one to keyring.js).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A test-signed install of the MSIX build, in the WindowsApps folder a Store
install uses, showed that every script-made piece of the NSIS model breaks
there, because each names the install folder and every update deletes it:
the firewall rules went stale, the PATH entries piled up pointing at deleted
folders, and the Startup-folder .vbs was refused ("Permission denied") right
after sign-in. The network capabilities the manifest declared covered
nothing: they make rules for sandboxed apps only, and a listener in the
package still got the Windows firewall prompt. The package's own startup
task was on by default, started the node whatever mode the Node page said,
and ran the console executable, whose window stopped the node when closed.
The package now declares what Windows then creates at install, carries
across updates and removes with the app, all without an administrator
prompt (each measured on the real install, through an update and a reboot):
- firewall rules for the node, in a custom manifest template, since only a
package-level element can hold them;
- the startup task, off by default, running meshbay-nodew.exe, a new build
of the daemon without a console;
- an execution alias for meshbay-node.exe, so the app adds no PATH entry.
The node's CLI switches the startup task (platform.startup_task, ctypes over
the WinRT ABI): Windows gives the package's identity to the executables in
it, not to a powershell.exe the app starts, which got "Element not found".
`meshbay-node autostart install | remove | status` therefore works in the
Store package from the app and a terminal alike; the app caches the answer,
since the Node page polls. Starting at boot stays the .exe installer's: the
Store package offers no service mode, and the CLI refuses `service install`
there. Process listings count both image names.
The Node page's status poll cleared the message of a refused action within
five seconds; the two errors are kept apart now (all Windows builds).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Nothing on the upload path knew about ENOSPC: a write that found no room
raised out of the handler, the catch-all answered "Request failed", and the
.part stayed behind holding the space that had run out. The node now refuses
with `disk_full` at chunk 0 when the announced size would leave less than
1 GiB free, and at any write that fails with ENOSPC/EDQUOT, dropping the
partial. The client carries the code on the error and the transfers panel
says "The node's disk is full" in every catalogue.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Signed chat_purge from the Chat settings deletes every stored message;
epoch keys and attachments stay. The ack is broadcast so open chat
panels empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
hash_version 3: size + first 4 MB + last 4 MB + 1 MB at the middle,
5.5x faster cold on a USB disk than the 45 MB sample. The cache now
serves a hit under whatever version it holds, so no existing id moves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
Two drives with a folder of the same name made the second add fail,
and no screen could supply another name. add_root now names it
"Name (H)" or "Name (parent)"; a name the operator typed is still
refused on a clash.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
| |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Switching from "at sign-in" to "only while MeshBay is open" left the node the
sign-in launcher had started running after Quit: only a node this process had
started was stopped. In that mode the app owns the node, so Quit stops the
one that is there.
The start with the app and the sign-in's own start (ensureNode) also both ran
`autostart start` at launch -- three meshbay-node.exe were seen racing for the
port. The sign-in's start and node:start now wait for the launch's.
The end-to-end test covers the mode: Quit leaves no node, opening the app
starts one. It launches the app with the environment it was imported with:
the suite's conftest points HOME, USERPROFILE, LOCALAPPDATA and APPDATA at a
throwaway directory per test, and the app started under that crashed at once
(0x80000003), which first looked like a crash of the app itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
The boot task or the sign-in launcher, as chosen, and the node in session 0
for the service's S4U logon or in the signed-in session otherwise. Passed in
service and sign-in modes on the installed 0.18.0 build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Found by the first Windows beta tester, then reproduced on a clean install.
After a service-mode install nothing set the node up for the account that
signed in: the boot task started a node that quit ("hub.username not set"),
and the sidebar showed Node / Create group only once the hub held a node key.
The only way to the wizard that provisions was the home page's welcome card,
which an account already in a group never sees. The way out was
`meshbay-node init` and the key pasted on the profile page -- which is also
what PACKAGING-GUIDE.md told people to do.
- main.js `node:ensure`, called by app.js at sign-in: provisions, starts and
links the node this build ships (Windows, bundled node only). A node set up
for another account, or an account linked to another node, is left alone.
node:start waits for it, so the two never race.
- The sidebar shows the Node section when a node exists on this machine.
- The Node page's status is the node's: its control API and the process
list, not the service task's state (a node started from a terminal ran
while the page said Stopped). Stop says Stopped only once no
meshbay-node.exe is left, and stays offered for a process that answers
nothing.
- CLI stop kills the pid that answered when a graceful stop does not finish,
and fails with the reason when a node process is still there.
- The daemon ends its process 3s after _shutdown(): Python's exit waited for a
busy indexer thread, with the control API already closed. Armed by main()
only, never by a daemon run inside a test.
- node.toml is read as utf-8-sig (PowerShell 5.1 writes a BOM), and a config
that cannot be read is logged instead of dying silently in service mode.
- "Pair this browser" queues the code for the next group of this node to
open instead of saying "Paired successfully"; no banner before a group.
- test_e2e_windows_app.py (opt-in, MESHBAY_WIN_E2E=1) drives the installed
app against a throwaway hub: fresh account to linked node, Stop, Start,
Restart, checked against the real processes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
On Windows watchdog cannot tell what a deleted path was -- it is gone -- and
reports a directory moved out of the root as a file deleted, so the fix in
3d5a168 never ran there and test_the_watcher_reports_a_directory_moved_out
failed. A deleted path the index still holds entries below was a directory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
| |
On Windows a bare `read_text()` or `subprocess.run(text=True)` decodes with
the locale's code page (cp1252), not UTF-8. `test_licensing.py` then failed on
a byte of the vendored LICENSES.txt, and `test_keyring_vectors.py` decoded the
generator's output, which carries CJK test strings, into something that no
longer matched keyring.json. Both files are UTF-8; say so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
| |
A member who joined by link, by a new device or into an open group was
pinned in the roster with no name, so the audit log showed only the
first characters of their id. The hub's MNP token now carries the
account's username, and after the handshake the node writes it into the
roster for an account whose name is empty. An invitation's name is never
overwritten; the name is a label, authority stays on `sub`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Every full index walked all roots on the loop, and a node with several
large roots stopped answering for seconds. Walk directories only, on the
roots' disk thread; index_sync is spawned and still answers on failure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
The Yes/No before it defaulted to No, so they stayed behind. Read both
unelevated and raise the UAC prompt only when one is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Watchdog reports such a move as one "directory deleted" event and nothing
for the files, which the indexer ignored until the next reconcile. The
freeze rules still apply: root live, directory gone, parent present.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
shutil.which never looks beside meshbay-node.exe, and node-runtime is
appended to the user PATH, so any earlier ffmpeg.exe on PATH ran instead
of the pinned copy. Log the resolved media tools at startup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A node started with the desktop session runs before the session has mounted
its USB drives. The safety net then auto-ejected every removable root and
persisted it exactly like an operator's eject, so after each reboot those
roots stayed ejected until someone plugged them by hand (seen on a node whose
/media drives were mounted a minute after it started).
An auto-eject is now stored as such ("auto" in roster.db). At startup and at
every reconcile, an auto-ejected root whose path is readable again is checked
against a few files the hash cache knows under it, at the same path with the
same size and mtime; one found and the root is plugged back and rescanned.
An empty mount point or another drive in its place is not recognised and
stays ejected. An operator's eject is never undone automatically.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
docs/MESHBAY_HTTP_API.md lists every route of the hub (by domain, with the
authentication each requires) and of the node's loopback control API. It is
written by docs/generate_http_api.py from the routes and their docstrings;
test_http_api_doc.py fails when the file drifts from the code or when a
route has no docstring, so a new route must say what it does.
79 routes had no docstring and get a one-line description; a few whose first
line did not describe the route get a summary line.
The login page's developer docs gain an API link next to Design and
Protocol, in every language. README, MESHBAY_DESIGN.md (§0.1, §6.7, §7) and
CLAUDE.md point to the listing; README also points to examples/.
The examples scripts with a shebang become executable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
The daemon line repeated the state the daemon reports ("running — running").
The state is now appended only when it says something more than "running",
such as waiting_for_hub. The QUICKSTART example is updated to match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The protocol layer is LGPL-3.0-or-later in every language it exists in, so
any client may use it whatever its own licence: meshbay-common, and the files
marked with an SPDX line — keyderive.js, crypto.js, playlist-crypto.js,
transport*.js; keyring.js, transcripts.js and argon2-wasm.js on the desktop;
Kdf.kt, Keyring.kt and Transcripts.kt on Android. Everything else is
AGPL-3.0-or-later, which the RPM specs and package.json already declared
without a licence file to back them.
Two AGPL section 7 permissions:
- group applications may be under any licence when they use the interface
only through a named surface (static/licenses/APPLICATION-EXCEPTION.txt);
the reference application is 0BSD so that copying it brings no AGPL code;
- the Android application may be conveyed linked with Google Play services.
Third-party code is accounted for: THIRD-PARTY-NOTICES.txt is generated from
what a build ships (packaging/third_party_notices.py) for the deb/rpm venv and
the frozen Windows node — PyAV's wheel grafts in libx264 and libx265, which its
BSD licence does not mention — and the vendored browser libraries get their
licence texts and htm-preact.js its provenance. Wheels carry SPDX metadata,
RPMs %license, debs a DEP-5 copyright file, every Windows target LICENSE.txt.
test_licensing.py holds the line: the LGPL layer imports nothing under the
AGPL, the reference application nothing outside the application interface,
and every SPDX line is one of the known ones.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
| |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |\ |
|
| | |
| |
| |
| |
| |
| |
| |
| |
| | |
node_status, node_settings_set, roster_read, denylist_read, denylist_clear,
node_reload and the signed gek_rotate, member_unpin, transfer_limits,
group_detach leave MNP 6.0; the Node page and the CLI do this work over
loopback. Their ops keep their tests, moved to the ops level.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| | |
| |
| |
| |
| |
| |
| | |
Removes confirmFolder (addRoot, attachGroup) and the writable confirmation
added in e4f6177, with their two catalogue keys.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
root_add, root_update and group_attach leave MNP: adding a directory and
switching writable/removable go through the loopback API (native dialog in
the desktop app) or the CLI. The operator's Settings tab still lists the
roots from any browser, read-only. The desktop app refuses to sign those
ops; a loopback flag change now reaches open pages (publish_roots).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |/
|
|
|
|
|
|
|
|
|
| |
folder is skipped
The root fixtures wrote `path = C:\Users\...`, which is not valid TOML:
node_toml now reads values with tomllib, so the four tests failed on Windows.
The node and the app always write paths with `/`, as the other fixtures do.
A folder named with a quote and a newline cannot exist on Windows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
sender_name and thread_id travel in clear beside the sealed envelope and were
stored and relayed whatever their type and size. A name longer than a username
or a thread id that is not a short id is now dropped (F-27).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Stream, transcode and subtitle failures sent the exception's text — operator
paths, versions — to the member. Fixed messages now, the cause in the log
(F-24).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Any *.part older than a day in a writable root was deleted — a browser's
download in progress in a shared folder included. Only names carrying the
node's tag (name.<8 hex>.part) are reaped now (F-28).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
A user revocation closed nothing: the denylist stopped the next connection and
left the live ones streaming and chatting. Revocations now go through one
method that closes the account's or the group's sessions (F-21).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
| |
The desktop app writes the Mark-of-the-Web on each file it saves on Windows, as
a browser does. Bidirectional controls are reserved characters in a saved name
(portable-name.js and paths.sanitize_for_download, and again in the main
process), so a name cannot display one extension and carry another. The node
refuses uploads of files Windows Explorer acts on by itself: desktop.ini,
.lnk, .url, .scf, .library-ms, .searchConnector-ms (F-19).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
128 peer sessions on the node, at most 64 per account (the hub names the
account with each offer; the node's own account is not counted). One account
plays at most half the stream slots, rounded up, and runs two subtitle
extractions at once. Frames after the handshake are 8 MiB (was 64), decoded
with per-container bounds, and a frame refused for either ends the session
instead of jamming its buffer (F-16).
Sized for the heaviest real member: twenty groups on one node, three devices
and a tab, up to 52 sessions. Measured: ~0.15 MiB and one fd per idle session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
| |
A name an upload in flight will take is reserved; each upload writes its own
`name.<tag>.part`; the finished file is published by a hard link, which
refuses an existing target, and takes the next free name if one appeared
meanwhile — the last ack names it. Two members sending one name at once wrote
one part and published it twice; a file copied in during an upload was
replaced (F-09).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
| |
probe_video waits 30 s at most and kills ffprobe on a timeout or when its
caller gives up — a cancelled wait left the process running. The seek probe
kills what it timed out on. Stream, subtitle and enrichment requests no longer
hang on a file that keeps ffprobe busy (F-18, timeouts; the protocol
whitelist was dropped: ffmpeg already confines nested protocols of a local
input, measured on 8.0 against HLS and concat inputs).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
| |
The name is resolved off the event loop and every answer checked; the socket
is then opened to that IP literal through a pinned httpcore backend, TLS still
verifying the certificate for the name, and no proxy from the environment.
A name that answers clean and then with a LAN address no longer gets a
request sent there, and a slow name no longer stalls the node (F-12).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
| |
attach_group no longer copies join_policy and visibility from the hub's
answer: they come with the operator's request (the desktop creation form,
`group add --open`) and default to invite/private; the CLI says when the hub
lists the group otherwise. Every string written into node.toml is escaped
(toml_string) and read back through tomllib, so a group or folder name cannot
write lines of its own (F-17).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
| |
The desktop main process builds every transcript itself from fields
(transcripts.js) and signs no raw bytes; the page's identity has the same
contract (crypto.js transcriptFor). The keyring seals no bundle while browser
access is off. On the node, device_add must redeem a pending request filed by
the same keys, and device_revoke is signed under its own prefix
(meshbay:device_revoke:v1), so a retirement signature admits nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
| |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
| |
widens the node
node:call is replaced by named operations with checked arguments; hosting a
group, sharing an unpicked folder, key rotation, denylist clearing and a change
of node account are confirmed by a native dialog. Every channel checks its
sender, secrets:get/set/clear are gone, node:start writes the app's own hub.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The handshake opened a session for anyone holding the group key with a hub
token naming the group; the roster was consulted only when wrapping the key
in a join. A member revoked or unpinned on the node but still a member on
the hub kept a full session with the key they already held — and was handed
the chat epoch their removal had just opened, since chat keys go to any
session. An honest client never met this (it asks for the key through
join_request every time); one that kept the key did not have to.
- After the proof, the node asks the roster and refuses with
`not_authorized_for_group` unless the account is an active member of the
group or the node's operator.
- A removal from any door — MNP, the node page, the CLI — now opens a new
chat epoch in each group the person could read, broadcasts it, and closes
every connection they hold (`ops.members._after_removal`). The CLI and the
node page did neither.
- Design §5.2, protocol §6.1, §6.3, §14.2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
unasked
- hub: a node may host a group only if its account owns it or the owner
approved that node (new `group_hosts`). Membership was the ceiling, and
every member holds the group key, so any member's node could register as a
host and be the one clients kept. A node claiming a group it may not host
is recorded as a request; the owner is notified once and approves or
refuses it (GET/POST/DELETE /v1/groups/{id}/hosts[/{node_id}]), which
takes effect on a connected node at once.
- hub: an owner adding a username creates an invitation (new
`group_invitations`), accepted or declined by the invitee
(/v1/groups/invitations, /{id}/invitation/accept|decline). Until then the
group is not listed, not dialled, not searched and not in any token.
Invitation links, open joins and group creation still make members
directly: they are the account's own act.
- hub: the MNP token names only the group it is minted for (group_id is now
required), so a node operator no longer learns a member's other groups.
- SPA: invitations on the home page; invited people and host requests in
the group's settings; the transport sends group_id. Ten catalogues.
- Browser probes for both screens, run in Chrome and Firefox.
- Design §5.2, §7.2, §7.3, AV32, AV33; protocol §6.3; user guide.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
cleanup
- node: only a wrong code counts towards the join lock, now per account
(5) as well as node-wide (20), and it is consulted only when a code is
tried. Every member reconnecting gets the group key through join_request,
so a lock checked before recognition let one member refuse it to everyone.
- node: link previews read the body as a stream and stop at the cap,
counted on decoded bytes; a declared oversized image is not read; 15 s
total deadline; image decoding off the loop. `client.get` had buffered
the whole (decompressed) response before the caps looked at it.
- hub: the daily purge of never-verified accounts detaches their IP-log
rows (keeping the name) and clears every other reference first, and each
cleanup step runs on its own. On PostgreSQL the bare DELETE violated the
ip_logs foreign key and stopped every purge behind it for good.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
The group-setting routes for app directories, chat directory, link
previews, Search listing and scan settings had no caller and no test;
those settings are signed MNP operations only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
The interface reads a member's cap from transfer_state and never read the
copy on the ack. The transfer probe reads it from transfer_state too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
A node hosting a public group syncs the hub's blocklist on every
connection (paged, node token only) and applies pushed changes. A
blocked file leaves the index and is refused (content_blocked); private
groups are untouched. The unused per-hash check route is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Nodes registered the hashes of their public groups on the hub and nothing
ever read them back. Routes, model and node registration removed; a
migration drops swarm_sources. No node sends the hub a content hash now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
| |
Any member could make a node hold unbounded challenge requests; a
connection now keeps at most 8, 64 KiB each. root_add, group_attach,
invite_create and tmdb_config signed less than they did; their subjects
are now canonical JSON of every value (the TMDB token by SHA-256).
MNP 5.0, floor kept at 4.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
Unused modules, functions, constants and client helpers with no caller,
the unreachable hub:probe IPC handler, and the CSAM hash matching.
Behaviour unchanged; the dispatch golden loses only the two removed
message types.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
serialize/deserialize had no production caller, and took with them the
per-chunk signature, the ChaCha20 cipher variant and the zstandard
dependency. Key derivations are unchanged. Docs corrected, including
design §4.3's claim that chunks are compressed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|