| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
| |
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>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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>
|
| |
|
|
|
|
|
|
| |
Every node the 4.0 floor admits signs its challenge, and one without a
channel binding could not complete the proof anyway, so a missing
signature is refused like a wrong one (browser and QUIC client).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
_pid_alive only ran tasklist, so request_graceful_stop raised
FileNotFoundError off Windows and two real-daemon tests failed on Linux.
Read /proc there, counting a zombie as gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Found by installing the builds and driving every startup mode live:
- Stop through the node's own control API first (POST /api/shutdown, loopback
and per-run token): the one channel that reaches a daemon in any session
without elevation -- a service node runs in session 0 -- and the one that
runs its shutdown. Then Task Scheduler, then a forced stop. Nine stops in a
row used to log no shutdown at all: each was a TerminateProcess.
- The forced stop spares the command running it. The frozen meshbay-node.exe
is the daemon and every CLI verb, so `taskkill /IM meshbay-node.exe` killed
`autostart stop` and `restart-daemon` themselves: exit 1, no output, and no
node after a restart. It excludes its own pid and its parent's, and /T takes
a venv launcher's python child and a daemon's ffmpeg children with it.
- Start and restart report the version that answered, never "started" about a
node nobody asked; `service start` says so when no node answered, and where
the log is.
- A second instance fails before it touches anything. The daemon wrote
ui-token, then failed to bind inside uvicorn's task and exited with the
reason on a hidden console; the node still running then refused every stop
and status, its token file naming a dead process. The control port is now
bound first (exclusively on Windows, where SO_REUSEADDR would share it), and
a refusal is logged and exits 2. Linux had the same order.
- The daemon logs to %LOCALAPPDATA%\meshbay\state\node.log: Task Scheduler
discards its stderr. Only the daemon run opens it, never a CLI verb.
- Hub sign-in waits are interruptible, a stop requested before the node is up
is honoured, and a hub that answers 429 or restarts leaves the node in
waiting_for_hub rather than looking dead.
- operator_paired is null until the roster is read, instead of a false that
showed "No operator paired" about a node whose pairing was intact.
The node test conftest also points HOME, USERPROFILE, LOCALAPPDATA and APPDATA
at a throwaway directory for every test, and keeps log_file() away from the
developer's own node: redirecting HOME alone isolates nothing on Windows, and
the CLI tests had been writing invite and pairing codes into the real profile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
TMDB fiches are fetched lazily, on browse, and the fetch used to run whatever
the moment it was first triggered — routinely before the operator had opened
settings and picked a language, so it queried in TMDB's English default. Then
the fiche was cached by tmdb_id alone, with no note of language and a 30-day
TTL, so switching to the intended language afterwards changed nothing: the
English fiche was served until it expired. The operator's only recourse was to
find and wipe the cache by hand (found live 2026-09-26: a whole library indexed
in English although "Français" had been chosen).
Two rules now, both there to make the first fetch the right language rather
than English-then-corrected, and to stop the doubled requests that eventually
get a node rate-limited:
- No language configured, no query. media_meta_req/season_meta_req answer
confidence 0 and make no TMDB call while tmdb_language is unset; the fetch
waits for the operator's choice, so the first (and only) query is in it.
English is now a first-class choice (en-US), not the default of skipping the
setting.
- Changing the language wipes the metadata cache (ops.set_tmdb_config), so the
new language takes effect on an already-browsed library. The file->tmdb
matches are language-independent and kept. The client refetches on the
tmdb_config_ack that carries the new language, so the grid updates without a
page reload.
docs/MESHBAY_DESIGN.md §9.7 states both rules; tests cover the gate and the
cache wipe, and two existing handler harnesses now declare a language.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
A node onboarded by the desktop client never ran `init`, so default.env was
never copied to node.env; and default.env was 0600 root, unreadable to a
per-user node anyway. The daemon now loads it beneath node.env, 0644.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The audience split stopped a member's node credential from opening the hub API.
It did not stop the credential being *replayed to another node*: the MNP token
carried the member's whole group set and named no node, so a token handed to
node A's operator could be presented to node B the member also belongs to. That
does not read content on B — the handshake still requires proving node B's group
key, which the operator lacks — but it reaches B's pre-proof window and fetches
the member's *encrypted* keypair bundle for B (offline-attackable, bounded,
audited): a disclosure §2.4 says should not follow from hosting a member on A.
The token now names the node it is minted for (a `node` claim = that node's
Ed25519 key), and authorize_token refuses one that names a different key. The
client already knows the target node's key (from /v1/groups/{id}/nodes) and asks
for a token bound to it: POST /v1/nodes/mnp-token takes node_pk, and
transport.connect threads it (group-page, the connection pool and rewrap pass
n.pk_node; reconnect preserves it). A token that names no node is still
accepted, because the hub only mints one for the authenticated requester, so an
unbound token grants nothing across accounts — which also keeps non-binding
callers working with no churn.
Done before deploy, so it folds into the MNP 4.0 flag day rather than needing
its own. Docs: §5.2, register E10, MESHBAY_NODE_PROTOCOL.md §6.3.
test_handshake.py and test_mnp_token.py hold the binding (a token for node A is
refused by node B, accepted by node A; an unbound token still works); red
before, green after. common/node/hub suites green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
failed node-key link
Two defensive gaps turned a routine reset-and-reonboard into "impossible de
démarrer le node":
1. daemon._login_with_retry retried a 401 (node key not linked yet) but `raise`d
on every other status, so a 429 — the daemon's own 5s retries hitting the
sign-in rate limit — or a 502/503 while the hub restarts during a deploy
killed the process, and systemd crash-looped it. Those statuses (429, 5xx)
are now retried with a back-off that respects Retry-After, so a freshly
reset node stays alive (the operator needs it up to read its key) instead of
dying. A genuine 4xx (400/422) still raises.
2. create-group's linkNodeKey swallowed every error as "already linked or same
key" — but PUT /me/node_key is idempotent and returns 200 on a re-link, so
there was no benign error to hide: the catch only ever hid a real failure
(a rejected session, a bad key), letting the wizard proceed against a node
that looked linked but was not, which then could not authenticate. The link
failure now surfaces (detectNode shows it).
test_login_retry_is_resilient.py holds the retry behaviour (429/5xx retried,
Retry-After honoured, 401 stays alive, 400 still raises); red before, green
after. common/node/hub suites green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
session token
A member authenticated to a node in the MNP handshake with its hub *session*
token — scope=user, valid at the hub API for hours. A node operator is in the
threat model, so this handed them a live hub credential for the member: enough
to enumerate the member's other groups, act as them, and (before the previous
commit closed it) take the account over. The node genuinely needs a hub-signed
membership assertion, so the fix is to make that a separate credential that
opens nothing at the hub API.
Two audiences signed by the one hub key (meshbay_common/tokens.py):
- HUB_API_AUD — session tokens (login, device-auth, node-auth, refresh), used
for hub calls and signaling. decode_access_token now binds this audience, so
an MNP token cannot be replayed against the hub API.
- MNP_AUD — a short-lived token a member presents to a node and nothing else,
from POST /v1/nodes/mnp-token. authorize_token now binds this audience, so a
session token presented to a node is refused. This closes the disclosure.
The node's own self-decode (hub_client.py) reads its node token with
audience=HUB_API_AUD. The client fetches the MNP token inside transport.connect()
(and on every reconnect) using the session token, so callers are unchanged and
signaling keeps using the session token.
No regression to a long session: the MNP token is checked once, at the
handshake, before any proof — a film already playing is not re-authenticated, so
a 15-minute token does not interrupt a 4-hour film; reconnects refetch a fresh
one. Denylist and membership checks are unchanged (the MNP token carries
sub/jti/groups).
Tests: authorize_token refuses a session/no-audience token and accepts an MNP
token; the hub API refuses an MNP token; POST /v1/nodes/mnp-token is minted only
for a member's own session. Verified red-before/green-after; common, node and
hub suites green (the pre-existing test_cli_golden failure is an argparse/pytest
prog artifact unrelated to this change).
Still to do before deploy (B2): bump the MNP version and client.minimum so a
stale desktop client is told to update rather than getting a handshake refusal,
update docs/MESHBAY_DESIGN.md and MESHBAY_NODE_PROTOCOL.md, and validate against
a real node locally, then deploy hub+node+SPA atomically.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
A link is redeemable by whoever opens it first, so it can be sent by any
messaging app. The address is optional (mail + label only); a link lives
7 days, fixed. Adds a Share button; see MESHBAY_DESIGN.md §3.4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
| |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
Each section of ops.py becomes a module of meshbay_node/ops/ (core,
node_toml, members, chat, groups, roots, files, settings, apps), cut as
text; ops/__init__.py keeps the docstring and re-exports every name, so
`ops.<name>` is unchanged for every caller. Logger name unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
The _enrich_*/_reenrich_* methods and _on_enriched become
EnrichmentMixin in meshbay_node/enrichment.py, with the two directory
helpers only they use. APP_DIR_KEYS stays on NodeDaemon.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
| |
Each `if args.command == ...` branch of main() becomes a function in
cli/ (one module per family of verbs); the parser, the process setup
and a VERBS table go to cli/parser.py and cli/dispatch.py. daemon.main
runs the verb or starts the daemon. Tests patch the CLI through
conftest.patch_cli; the CLI golden is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
/api/peers imported _get_remote_ip, inside the function, from
webrtc_server, which no longer has it. A test now resolves every
meshbay import in the node's source, function bodies included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
_do_admin_response looked each operation up in a 28-branch elif, every
branch the same call. The admin cases of the dispatch golden are
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
| |
The pre-authentication guards stay explicit code, in the same order and
text. After the handshake, a table maps each type to its handler and to
whether it runs as a task, the choice each branch made; the three inline
blocks become StreamingMixin methods, unchanged. The dispatch golden is
identical, including types that are not strings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
DispatchMixin in transport/webrtc/dispatch.py, with the pre-proof fetch
bound it enforces. The elif chain moves as it is; turning it into a table
is the next commit, on its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
SessionCore in transport/webrtc/core.py, last among the bases: state,
the data channel, task ownership, the peer registry, sending and teardown.
The facade keeps _dispatch_message and WebRTCTransport.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
webrtc_server
AdminMixin in transport/webrtc/admin.py: who the operator is, the signed
challenge, signature checks, and _do_admin_response moved unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
GroupOpsMixin in transport/webrtc/group_ops.py: member revocation and
unpinning, the group key rotation, apps and their directories with the
allow-list, and Search listing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
NodeOpsMixin in transport/webrtc/node_ops.py: status and settings, roster
and denylist, roots, hosted groups, reload, scan pacing and transfer
limits, with their class-level bounds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Three headers stayed in webrtc_server when their sections moved; they now
sit above the same methods in blobs.py and admission.py. Two that were only
a title over what is now a whole module are dropped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
HandshakeMixin in transport/webrtc/handshake.py: challenge, proof, channel
binding, and the sealed configuration a peer receives once admitted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
The peer-session ceiling no longer quotes the hub's old per-account number,
and the note on a single uploads/ directory goes: its constant left with
f3fb449, when an upload started landing in the folder it was sent to.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
AdmissionMixin in transport/webrtc/admission.py, with the invite handle
pattern and the join attempt bounds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
UploadMixin in transport/webrtc/upload_handlers.py, with the upload cap;
_upload_chunk moved unchanged, _append_chunk joins webrtc/disk.py.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
TransferMixin in transport/webrtc/transfer_handlers.py, with the sweep
interval.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
FilesMixin in transport/webrtc/files.py; the blocking disk helpers join
_locate in webrtc/disk.py, and the lease states go to webrtc/limits.py.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|