diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-19 14:24:13 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-19 14:24:13 +0200 |
| commit | 86188385cbdae1ee90c1dca7a7b9db2edef1ecd4 (patch) | |
| tree | cc01153f05e84ad6cd34556caecd3f4bc335d0bd /packages/meshbay-node/src | |
| parent | d2495a2c4b89fbbfc18cefec83ae96cabdd745e2 (diff) | |
| download | meshbay-86188385cbdae1ee90c1dca7a7b9db2edef1ecd4.tar.gz | |
style: ruff's own fixes, mechanically applied
`ruff check .` had gone unrun long enough to report 568 errors, which is the
same as having no linter: the next real finding would have been invisible in the
noise. This is the 521 it fixes by itself, in 173 files, and nothing else — the
98 it cannot fix are the next commit.
What actually changed: import sorting (225), imports nobody used (87, none of
them a re-export — no `__init__.py` is touched, which was the one way this could
have broken an import elsewhere), `datetime.timezone.utc` to `datetime.UTC` (69)
and `asyncio.TimeoutError` to `TimeoutError` (18), both plain aliases on the 3.12
this project requires, `Optional[X]` to `X | None` (24), and f-strings with
nothing to interpolate (19).
Checked rather than assumed: every module in the three packages still imports,
and the suite is 2893 passed — the same count, test for test, as the merge
before it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/src')
22 files changed, 135 insertions, 138 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/config.py b/packages/meshbay-node/src/meshbay_node/config.py index 2ae3c7c..437f629 100644 --- a/packages/meshbay-node/src/meshbay_node/config.py +++ b/packages/meshbay-node/src/meshbay_node/config.py @@ -14,9 +14,9 @@ from pathlib import Path from meshbay_node.platform import config_dir, data_dir try: - import tomllib # Python 3.11+ + import tomllib # Python 3.11+ except ImportError: - import tomli as tomllib # type: ignore[no-redef] + import tomli as tomllib # type: ignore[no-redef] log = logging.getLogger(__name__) diff --git a/packages/meshbay-node/src/meshbay_node/daemon.py b/packages/meshbay-node/src/meshbay_node/daemon.py index 3492d03..a0ce211 100644 --- a/packages/meshbay-node/src/meshbay_node/daemon.py +++ b/packages/meshbay-node/src/meshbay_node/daemon.py @@ -25,43 +25,43 @@ Usage: """ import asyncio -from dataclasses import asdict, replace import base64 -import json import logging import os import signal import sys import time +from dataclasses import asdict, replace from pathlib import Path import uvicorn - +from meshbay_common import MNP_VERSION from meshbay_common.background import spawn from meshbay_common.paths import fold -from meshbay_common import MNP_VERSION from meshbay_common.protocol import MNP -from meshbay_node.audit import RETENTION_DAYS as AUDIT_RETENTION_DAYS, AuditStore + +from meshbay_node import uploads as uploads_mod +from meshbay_node.audit import RETENTION_DAYS as AUDIT_RETENTION_DAYS +from meshbay_node.audit import AuditStore from meshbay_node.bundle_store import BundleStore from meshbay_node.chat.store import ChatStore -from meshbay_node.config import Config, DEFAULT_CONFIG_PATH, load_config, write_example_config -from meshbay_node.roots import RootSet, RootError, entry_abs_path, off_disk +from meshbay_node.config import DEFAULT_CONFIG_PATH, Config, load_config from meshbay_node.hub_client import HubClient, HubConfig -from meshbay_node.indexer import DirectoryIndexer, IndexCache, GroupIndex +from meshbay_node.indexer import DirectoryIndexer, GroupIndex, IndexCache from meshbay_node.indexer.enrich import Enricher from meshbay_node.indexer.enrich_audio import AudioEnricher from meshbay_node.indexer.enrich_photo import PhotoEnricher +from meshbay_node.keystore import create_keystore, load_keystore, load_or_create_keystore from meshbay_node.media_cache import MediaCache -from meshbay_node.tmdb import TmdbClient -from meshbay_node import uploads as uploads_mod from meshbay_node.musicbrainz import MusicBrainzClient -from meshbay_node.keystore import create_keystore, load_keystore, load_or_create_keystore from meshbay_node.platform import chmod_private, config_dir, data_dir, state_dir +from meshbay_node.roots import RootError, RootSet, entry_abs_path, off_disk from meshbay_node.roster import Roster +from meshbay_node.tmdb import TmdbClient from meshbay_node.transport import ( - Denylist, QUIC_AVAILABLE, WEBRTC_AVAILABLE, + Denylist, ) from meshbay_node.transport.wire import index_delta_message, index_sync_message @@ -109,8 +109,8 @@ def _owning_directory(path: str, directories: list[str]) -> str | None: def calibrate_argon2(target_ms: int = 500) -> None: """Benchmark Argon2id and suggest parameters targeting ~target_ms.""" - import time import os + import time print(f"Calibrating Argon2id (target: {target_ms}ms) ...") salt = os.urandom(16) @@ -1938,8 +1938,7 @@ def _systemctl_user(verb: str, unit: str, *, not_running_hint: str, def main() -> None: import argparse - from meshbay_node.platform import (configure_event_loop, force_utf8_stdio, - load_node_env) + from meshbay_node.platform import configure_event_loop, force_utf8_stdio, load_node_env force_utf8_stdio() configure_event_loop() # Before anything reads the environment. On Linux systemd has usually loaded @@ -2164,10 +2163,10 @@ def main() -> None: print("Aborted.") return - import subprocess as _sp import json as _json - import urllib.request + import subprocess as _sp import urllib.error + import urllib.request token_file = data_dir_ / "ui-token" if token_file.exists(): @@ -2475,7 +2474,11 @@ def main() -> None: if args.command == "restart-daemon": if sys.platform == "win32": from meshbay_node.platform import ( - autostart_end, autostart_run, service_end, service_run, service_status, + autostart_end, + autostart_run, + service_end, + service_run, + service_status, ) if service_status()["installed"]: service_end() diff --git a/packages/meshbay-node/src/meshbay_node/hub_client.py b/packages/meshbay-node/src/meshbay_node/hub_client.py index ef12bb4..58bf657 100644 --- a/packages/meshbay-node/src/meshbay_node/hub_client.py +++ b/packages/meshbay-node/src/meshbay_node/hub_client.py @@ -20,7 +20,7 @@ import logging import time from dataclasses import dataclass, field from pathlib import Path -from typing import Any, Callable +from typing import Any import httpx import jwt diff --git a/packages/meshbay-node/src/meshbay_node/indexer/__init__.py b/packages/meshbay-node/src/meshbay_node/indexer/__init__.py index c92c730..a40e9f2 100644 --- a/packages/meshbay-node/src/meshbay_node/indexer/__init__.py +++ b/packages/meshbay-node/src/meshbay_node/indexer/__init__.py @@ -1,6 +1,6 @@ """Directory indexer and Mesh Group Index.""" -from .indexer import DirectoryIndexer -from .group_index import GroupIndex from .cache import IndexCache +from .group_index import GroupIndex +from .indexer import DirectoryIndexer __all__ = ["DirectoryIndexer", "GroupIndex", "IndexCache"] diff --git a/packages/meshbay-node/src/meshbay_node/indexer/enrich.py b/packages/meshbay-node/src/meshbay_node/indexer/enrich.py index b44a246..9881298 100644 --- a/packages/meshbay-node/src/meshbay_node/indexer/enrich.py +++ b/packages/meshbay-node/src/meshbay_node/indexer/enrich.py @@ -13,12 +13,12 @@ the rest asynchronously and hands the result back via a callback. import asyncio import logging +from collections.abc import Awaitable, Callable from pathlib import Path -from typing import Awaitable, Callable import blake3 - from meshbay_common.protocol import IndexEntry + from meshbay_node.indexer import title_parse from meshbay_node.indexer.indexer import MEDIA_EXTENSIONS from meshbay_node.media_cache import MediaCache @@ -173,7 +173,7 @@ async def _make_thumbnail(file_path: Path, duration: float | None) -> bytes | No ) try: stdout, _ = await asyncio.wait_for(proc.communicate(), THUMB_TIMEOUT_SECS) - except asyncio.TimeoutError: + except TimeoutError: proc.kill() await proc.wait() return None diff --git a/packages/meshbay-node/src/meshbay_node/indexer/enrich_photo.py b/packages/meshbay-node/src/meshbay_node/indexer/enrich_photo.py index 6613f85..5cacef9 100644 --- a/packages/meshbay-node/src/meshbay_node/indexer/enrich_photo.py +++ b/packages/meshbay-node/src/meshbay_node/indexer/enrich_photo.py @@ -22,13 +22,13 @@ import asyncio import datetime import io import logging +from collections.abc import Awaitable, Callable from pathlib import Path -from typing import Awaitable, Callable import blake3 +from meshbay_common.protocol import IndexEntry from PIL import ExifTags, Image, ImageOps -from meshbay_common.protocol import IndexEntry from meshbay_node.media_cache import MediaCache log = logging.getLogger(__name__) diff --git a/packages/meshbay-node/src/meshbay_node/indexer/group_index.py b/packages/meshbay-node/src/meshbay_node/indexer/group_index.py index 2340c78..9e4e780 100644 --- a/packages/meshbay-node/src/meshbay_node/indexer/group_index.py +++ b/packages/meshbay-node/src/meshbay_node/indexer/group_index.py @@ -13,29 +13,27 @@ Delta format: import base64 import logging -import os -import time -from dataclasses import dataclass, field, asdict -from pathlib import Path -from typing import Iterator +from dataclasses import asdict, dataclass, field import blake3 import msgpack import zstandard as zstd from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey - from meshbay_common.crypto import ( + pk_to_b64, sign_chunk, verify_chunk_signature, - pk_to_b64, - generate_gek, ) +from meshbay_common.protocol import IndexDelta, IndexEntry from meshbay_common.webcrypto import ( chunk_key_aes as derive_chunk_key, - encrypt_chunk_aes as encrypt_chunk, +) +from meshbay_common.webcrypto import ( decrypt_chunk_aes as decrypt_chunk, ) -from meshbay_common.protocol import IndexEntry, IndexDelta +from meshbay_common.webcrypto import ( + encrypt_chunk_aes as encrypt_chunk, +) log = logging.getLogger(__name__) @@ -171,7 +169,6 @@ class GroupIndex: ) -> "GroupIndex": """Deserialize, verify signature, and decrypt (if private).""" from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey - from meshbay_common.crypto import verify_chunk_signature envelope = msgpack.unpackb(data, raw=False) diff --git a/packages/meshbay-node/src/meshbay_node/indexer/indexer.py b/packages/meshbay-node/src/meshbay_node/indexer/indexer.py index 3fe4f3e..4b619d7 100644 --- a/packages/meshbay-node/src/meshbay_node/indexer/indexer.py +++ b/packages/meshbay-node/src/meshbay_node/indexer/indexer.py @@ -25,19 +25,20 @@ it is the only thing that recovers a missed event. import asyncio import logging import time +from collections.abc import Awaitable, Callable from concurrent.futures import ThreadPoolExecutor -from dataclasses import dataclass, field as dataclass_field +from dataclasses import dataclass +from dataclasses import field as dataclass_field from pathlib import Path -from typing import Callable, Awaitable import blake3 from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey +from meshbay_common.background import spawn +from meshbay_common.paths import find_fold_collisions, fold, long_path +from meshbay_common.protocol import IndexEntry from watchdog.events import FileSystemEvent, FileSystemEventHandler from watchdog.observers import Observer -from meshbay_common.background import spawn -from meshbay_common.paths import fold, find_fold_collisions, long_path -from meshbay_common.protocol import IndexEntry from meshbay_node.indexer.cache import IndexCache from meshbay_node.indexer.group_index import GroupIndex from meshbay_node.roots import Root, RootSet, off_disk diff --git a/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py b/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py index 14728ff..5df70c4 100644 --- a/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py +++ b/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py @@ -19,7 +19,7 @@ directory listing; this module only parses strings it's handed. from __future__ import annotations import re -from dataclasses import dataclass, field +from dataclasses import dataclass from guessit import guessit diff --git a/packages/meshbay-node/src/meshbay_node/keystore.py b/packages/meshbay-node/src/meshbay_node/keystore.py index 00e504e..78fe122 100644 --- a/packages/meshbay-node/src/meshbay_node/keystore.py +++ b/packages/meshbay-node/src/meshbay_node/keystore.py @@ -38,8 +38,6 @@ from pathlib import Path import msgpack from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey - -from meshbay_node.platform import chmod_private, config_dir from meshbay_common.crypto import ( ARGON2_ITERATIONS, ARGON2_LANES, @@ -50,12 +48,12 @@ from meshbay_common.crypto import ( decrypt_keystore, derive_keystore_key, encrypt_keystore, - generate_gek, pk_to_b64, sk_to_b64, - sk_to_raw, ) +from meshbay_node.platform import chmod_private, config_dir + log = logging.getLogger(__name__) KEYSTORE_VERSION = 1 diff --git a/packages/meshbay-node/src/meshbay_node/ops.py b/packages/meshbay-node/src/meshbay_node/ops.py index ca1b87d..baea365 100644 --- a/packages/meshbay-node/src/meshbay_node/ops.py +++ b/packages/meshbay-node/src/meshbay_node/ops.py @@ -24,8 +24,8 @@ from __future__ import annotations import asyncio import logging -import time as _time import re +import time as _time from dataclasses import asdict from pathlib import Path from typing import Any @@ -37,8 +37,9 @@ from meshbay_common.crypto import ( unwrap_gek_aes, wrap_gek_aes, ) -from meshbay_node.config import DEFAULT_CONFIG_PATH from meshbay_common.join import ROLE_MEMBER, ROLE_OPERATOR + +from meshbay_node.config import DEFAULT_CONFIG_PATH from meshbay_node.roots import RootError, RootSet, off_disk from meshbay_node.roster import Roster @@ -951,6 +952,7 @@ async def remove_root(state: dict, group_id: str, root_name: str) -> dict: raise OpError("Group not configured on this node", status=404) from meshbay_common.paths import fold + from meshbay_node.roots import derive_name target = fold(root_name) match_idx = None @@ -1001,6 +1003,7 @@ async def update_root(state: dict, group_id: str, root_name: str, *, raise OpError("Group not configured on this node", status=404) from meshbay_common.paths import fold + from meshbay_node.roots import RootSet target = fold(root_name) match = None @@ -1462,8 +1465,7 @@ async def list_transfers(state: dict) -> dict: ctx = getattr(webrtc, "_ctx", {}) if webrtc else {} slots = ctx.get("_transfer_slots") if slots is None: - from meshbay_node.transfers import ( - DEFAULT_MAX_CONCURRENT, DEFAULT_MAX_PER_MEMBER, KINDS) + from meshbay_node.transfers import DEFAULT_MAX_CONCURRENT, DEFAULT_MAX_PER_MEMBER, KINDS # No pool built means nothing has transferred since the daemon started, # which is a real answer and not an error. # diff --git a/packages/meshbay-node/src/meshbay_node/replication.py b/packages/meshbay-node/src/meshbay_node/replication.py index c2f5cd3..297ed0b 100644 --- a/packages/meshbay-node/src/meshbay_node/replication.py +++ b/packages/meshbay-node/src/meshbay_node/replication.py @@ -16,8 +16,6 @@ Usage: await replicator.replicate_file(file_id, file_name, file_size) """ -import asyncio -import hashlib import logging from pathlib import Path diff --git a/packages/meshbay-node/src/meshbay_node/revocation.py b/packages/meshbay-node/src/meshbay_node/revocation.py index d3ee18f..1e8caee 100644 --- a/packages/meshbay-node/src/meshbay_node/revocation.py +++ b/packages/meshbay-node/src/meshbay_node/revocation.py @@ -17,7 +17,6 @@ Usage in daemon: import asyncio import json import logging -import time from typing import Literal import httpx @@ -151,5 +150,5 @@ class RevocationSubscriber: else: log.debug("WS message: %s", msg.get("type")) - except asyncio.TimeoutError: + except TimeoutError: continue diff --git a/packages/meshbay-node/src/meshbay_node/roots.py b/packages/meshbay-node/src/meshbay_node/roots.py index 67778ca..89b0441 100644 --- a/packages/meshbay-node/src/meshbay_node/roots.py +++ b/packages/meshbay-node/src/meshbay_node/roots.py @@ -71,7 +71,7 @@ def _free_name(directory: Path, filename: str) -> str: raise FileExistsError(filename) -def safe_subdir(roots: "RootSet", rel: str) -> Path | None: +def safe_subdir(roots: RootSet, rel: str) -> Path | None: """ Resolve a client-supplied directory inside one of the group's roots, or refuse. @@ -219,7 +219,7 @@ class RootSet: # ── Construction ───────────────────────────────────────────────────────── @classmethod - def build(cls, specs: list[dict]) -> "RootSet": + def build(cls, specs: list[dict]) -> RootSet: """ Build from configuration, refusing anything ambiguous. diff --git a/packages/meshbay-node/src/meshbay_node/roster.py b/packages/meshbay-node/src/meshbay_node/roster.py index 8c9b3ef..9478a32 100644 --- a/packages/meshbay-node/src/meshbay_node/roster.py +++ b/packages/meshbay-node/src/meshbay_node/roster.py @@ -24,13 +24,11 @@ from __future__ import annotations import hashlib import json import logging -import os import secrets -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from pathlib import Path import aiosqlite - from meshbay_common.paths import fold log = logging.getLogger(__name__) @@ -194,11 +192,11 @@ def hash_code(code: str) -> str: def _now() -> str: - return datetime.now(timezone.utc).isoformat(timespec="seconds") + return datetime.now(UTC).isoformat(timespec="seconds") def _iso_in(seconds: int) -> str: - return (datetime.now(timezone.utc) + return (datetime.now(UTC) + timedelta(seconds=seconds)).isoformat(timespec="seconds") @@ -1073,7 +1071,7 @@ class Roster: (group_id, user_id), ) code = generate_code() - expires = datetime.now(timezone.utc) + timedelta(seconds=ttl) + expires = datetime.now(UTC) + timedelta(seconds=ttl) await self._db.execute( "INSERT INTO invites (code_hash, group_id, user_id, username, role, " "created_by, created_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", @@ -1106,7 +1104,7 @@ class Roster: # redeemed by whoever finds it first. if invite["user_id"] != user_id: return None - if datetime.fromisoformat(invite["expires_at"]) < datetime.now(timezone.utc): + if datetime.fromisoformat(invite["expires_at"]) < datetime.now(UTC): return None cur = await self._db.execute( diff --git a/packages/meshbay-node/src/meshbay_node/transport/__init__.py b/packages/meshbay-node/src/meshbay_node/transport/__init__.py index e423e35..86dbf23 100644 --- a/packages/meshbay-node/src/meshbay_node/transport/__init__.py +++ b/packages/meshbay-node/src/meshbay_node/transport/__init__.py @@ -15,8 +15,8 @@ Transport decision (2026-08-13, second security review): # QUIC transport (MNP v2) — requires aioquic>=1.0 try: - from .quic_server import QuicChunkServer, Denylist from .quic_client import QuicChunkClient + from .quic_server import Denylist, QuicChunkServer QUIC_AVAILABLE = True except ImportError: QuicChunkServer = None # type: ignore[assignment,misc] @@ -26,7 +26,7 @@ except ImportError: # WebRTC transport (browsers + native clients) — requires aiortc>=1.9 try: - from .webrtc_server import WebRTCTransport, WebRTCPeerSession + from .webrtc_server import WebRTCPeerSession, WebRTCTransport WEBRTC_AVAILABLE = True except ImportError: WebRTCTransport = None # type: ignore[assignment,misc] diff --git a/packages/meshbay-node/src/meshbay_node/transport/quic_client.py b/packages/meshbay-node/src/meshbay_node/transport/quic_client.py index af87b70..273f225 100644 --- a/packages/meshbay-node/src/meshbay_node/transport/quic_client.py +++ b/packages/meshbay-node/src/meshbay_node/transport/quic_client.py @@ -13,18 +13,14 @@ import base64 import logging import os import struct -from pathlib import Path -import jwt import msgpack -from aioquic.asyncio import connect, QuicConnectionProtocol +from aioquic.asyncio import QuicConnectionProtocol, connect from aioquic.quic.configuration import QuicConfiguration from aioquic.quic.events import QuicEvent, StreamDataReceived from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey - from meshbay_common import MNP_VERSION from meshbay_common.groupbox import PURPOSE_ACK, PURPOSE_INDEX, unseal -from meshbay_common.protocol import MNP, file_chunk_plaintext from meshbay_common.handshake import ( MNP_MIN_SUPPORTED, NONCE_LEN, @@ -36,6 +32,7 @@ from meshbay_common.handshake import ( quic_binding, verify_proof, ) +from meshbay_common.protocol import MNP, file_chunk_plaintext def _peer_cert_der(proto) -> bytes | None: diff --git a/packages/meshbay-node/src/meshbay_node/transport/quic_server.py b/packages/meshbay-node/src/meshbay_node/transport/quic_server.py index 2a2b07a..036574b 100644 --- a/packages/meshbay-node/src/meshbay_node/transport/quic_server.py +++ b/packages/meshbay-node/src/meshbay_node/transport/quic_server.py @@ -24,17 +24,16 @@ import os import struct import uuid from pathlib import Path -from typing import Any, Callable +from typing import Any -import jwt import msgpack from aioquic.asyncio import QuicConnectionProtocol, serve from aioquic.quic.configuration import QuicConfiguration from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey - from meshbay_common import MNP_VERSION -from meshbay_node.roots import ROOT_NOT_SERVED, RootSet, entry_abs_path +from meshbay_common.crypto import pk_to_b64 +from meshbay_common.groupbox import PURPOSE_ACK, seal from meshbay_common.handshake import ( MNP_MIN_SUPPORTED, NONCE_LEN, @@ -48,10 +47,10 @@ from meshbay_common.handshake import ( quic_binding, verify_proof, ) -from meshbay_common.crypto import pk_to_b64 -from meshbay_common.groupbox import PURPOSE_ACK, seal from meshbay_common.protocol import MNP, file_chunk_wire + from meshbay_node.indexer import GroupIndex +from meshbay_node.roots import ROOT_NOT_SERVED, RootSet, entry_abs_path from meshbay_node.transport.wire import index_sync_message log = logging.getLogger(__name__) diff --git a/packages/meshbay-node/src/meshbay_node/transport/tls_cert.py b/packages/meshbay-node/src/meshbay_node/transport/tls_cert.py index 8d680ea..cfc93c7 100644 --- a/packages/meshbay-node/src/meshbay_node/transport/tls_cert.py +++ b/packages/meshbay-node/src/meshbay_node/transport/tls_cert.py @@ -10,11 +10,10 @@ the QUIC handshake proof (11.5.6), since QUIC has no DTLS fingerprint to bind to Certificate is generated once and cached at ~/.config/meshbay/node_tls.crt/.key. """ -import logging -import os -from pathlib import Path import datetime import ipaddress +import logging +from pathlib import Path from cryptography import x509 from cryptography.hazmat.primitives import hashes, serialization @@ -47,8 +46,8 @@ def generate_self_signed_cert( .issuer_name(issuer) .public_key(rsa_key.public_key()) .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.now(datetime.timezone.utc)) - .not_valid_after(datetime.datetime.now(datetime.timezone.utc) + .not_valid_before(datetime.datetime.now(datetime.UTC)) + .not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=3650)) .add_extension( x509.SubjectAlternativeName([ diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py index 8a5bbff..2bd1419 100644 --- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py +++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py @@ -25,8 +25,6 @@ Signaling flow (handled externally by the hub): import asyncio import base64 import contextvars -import hashlib -import hmac import logging import os import re @@ -38,63 +36,53 @@ from pathlib import Path from typing import Any import blake3 -import jwt import msgpack -from aiortc import RTCPeerConnection, RTCSessionDescription, RTCDataChannel +from aiortc import RTCDataChannel, RTCPeerConnection, RTCSessionDescription from cryptography.hazmat.primitives.asymmetric.ed25519 import ( Ed25519PrivateKey, Ed25519PublicKey, ) - from meshbay_common import MNP_VERSION -from meshbay_common.handshake import ( - MNP_MIN_SUPPORTED, - NONCE_LEN, - ROLE_CLIENT, - ROLE_NODE, - HandshakeError, - authorize_token, - check_version, - handshake_transcript, - make_proof, - verify_proof, - webrtc_binding, -) from meshbay_common.adminop import ( ADMIN_CHALLENGE_TTL, + OP_APP_DIRECTORIES, + OP_APPS_ENABLED, + OP_CHAT_DIRECTORY, + OP_CHAT_EPOCH, + OP_CHAT_LINK_PREVIEW, OP_DIR_DELETE, OP_FILE_DELETE, + OP_GEK_ROTATE, + OP_GROUP_ATTACH, + OP_GROUP_DETACH, OP_INVITE_CREATE, OP_MEMBER_REVOKE, - OP_GEK_ROTATE, OP_MEMBER_UNPIN, - OP_APPS_ENABLED, + OP_MUSICBRAINZ_ENABLED, + OP_ROOT_ADD, + OP_ROOT_EJECT, + OP_ROOT_PLUG, + OP_ROOT_REMOVE, + OP_ROOT_UPDATE, + OP_SEARCH_LISTED, OP_SET_SCAN_SETTINGS, - OP_TRANSFER_LIMITS, OP_TMDB_CONFIG, OP_TMDB_ENABLED, OP_TMDB_OVERRIDE, OP_TMDB_REMATCH, - OP_MUSICBRAINZ_ENABLED, - OP_APP_DIRECTORIES, - OP_CHAT_DIRECTORY, - OP_CHAT_EPOCH, - OP_CHAT_LINK_PREVIEW, - OP_SEARCH_LISTED, - OP_ROOT_ADD, - OP_ROOT_REMOVE, - OP_ROOT_UPDATE, - OP_ROOT_EJECT, - OP_ROOT_PLUG, - OP_GROUP_ATTACH, - OP_GROUP_DETACH, + OP_TRANSFER_LIMITS, admin_transcript, ) +from meshbay_common.chatbox import ( + NONCE_LEN as CHAT_NONCE_LEN, +) +from meshbay_common.chatbox import ( + SIG_LEN as CHAT_SIG_LEN, +) from meshbay_common.crypto import pk_to_b64, wrap_gek_aes from meshbay_common.device import ( DEVICE_TTL, device_add_transcript, - device_code_hash, device_hello_transcript, device_request_transcript, ) @@ -104,44 +92,62 @@ from meshbay_common.groupbox import ( PURPOSE_ROSTER, seal, ) +from meshbay_common.handshake import ( + MNP_MIN_SUPPORTED, + NONCE_LEN, + ROLE_CLIENT, + ROLE_NODE, + HandshakeError, + authorize_token, + check_version, + handshake_transcript, + make_proof, + verify_proof, + webrtc_binding, +) from meshbay_common.join import ( JOIN_TTL, ROLE_MEMBER, ROLE_OPERATOR, join_transcript, ) -from meshbay_common.chatbox import ( - NONCE_LEN as CHAT_NONCE_LEN, - SIG_LEN as CHAT_SIG_LEN, -) from meshbay_common.protocol import ( MNP, + UPLOAD_PROBE_INDEX, chunk_ciphertext, file_chunk_wire, - UPLOAD_PROBE_INDEX, file_upload_ack_wire, file_upload_payload, ) -from meshbay_node.chat import FORMAT_SEALED_V1, ReplayedMessage -from meshbay_node.transport.wire import index_sync_message -from meshbay_node.indexer import GroupIndex -from meshbay_node.indexer.indexer import DirectoryIndexer + from meshbay_node import hwaccel, linkpreview, ops, platform from meshbay_node import transfers as transfers_mod from meshbay_node import uploads as uploads_mod -from meshbay_node.transfers import TransferSlots +from meshbay_node.chat import FORMAT_SEALED_V1, ReplayedMessage +from meshbay_node.indexer import GroupIndex +from meshbay_node.indexer.indexer import DirectoryIndexer + # Re-imported under its original name: every call site and existing test in # this module still refers to it as `_probe_video`. The implementation lives # in media_probe.py so the indexer package (imported just above) can call it # too, for index-time enrichment, without a circular import. from meshbay_node.media_probe import ( BROWSER_INCOMPATIBLE_VIDEO_CODECS, +) +from meshbay_node.media_probe import ( probe_video as _probe_video, ) from meshbay_node.roots import ( - ROOT_NOT_SERVED, RootSet, entry_abs_path, off_disk, SAFE_UPLOAD_NAME, safe_subdir, + ROOT_NOT_SERVED, + SAFE_UPLOAD_NAME, + RootSet, _free_name, + entry_abs_path, + off_disk, + safe_subdir, ) +from meshbay_node.transfers import TransferSlots +from meshbay_node.transport.wire import index_sync_message log = logging.getLogger(__name__) @@ -6160,7 +6166,7 @@ class WebRTCPeerSession: # total budget is unchanged. await asyncio.wait_for(self._stream_credit_evt.wait(), timeout=STREAM_CREDIT_POLL) - except asyncio.TimeoutError: + except TimeoutError: silent = time.monotonic() - self._stream_heard_at if silent >= STREAM_CREDIT_TIMEOUT: log.info("Stream stalled: nothing from peer=%s for %.0fs", @@ -6208,7 +6214,7 @@ class WebRTCPeerSession: await asyncio.wait_for(asyncio.shield(prev), timeout=15) log.info("stream: previous stream ended in %.1fs", time.monotonic() - t0) - except asyncio.TimeoutError: + except TimeoutError: log.warning("stream: previous stream STILL RUNNING after 15s") except Exception: pass # it failed on its own; the slot is free either way @@ -6851,7 +6857,7 @@ async def _transcode_audio_to_aac(file_path: Path) -> bytes: try: _, stderr = await asyncio.wait_for( proc.communicate(), timeout=AUDIO_TRANSCODE_TIMEOUT_SECS) - except asyncio.TimeoutError: + except TimeoutError: proc.kill() await proc.wait() raise RuntimeError(f"ffmpeg timed out after {AUDIO_TRANSCODE_TIMEOUT_SECS}s") @@ -6912,7 +6918,7 @@ async def _seek_lands_at(file_path: Path, t: float, map_args: list[str]) -> floa ) stdout, _ = await asyncio.wait_for( probe.communicate(), timeout=SEEK_PROBE_TIMEOUT_SECS) - except (asyncio.TimeoutError, OSError) as e: + except (TimeoutError, OSError) as e: log.warning("stream: seek probe failed at %.1fs: %r", t, e) return None finally: @@ -6967,7 +6973,7 @@ async def _extract_subtitle_to_webvtt(file_path: Path, ordinal: int, try: _, stderr = await asyncio.wait_for( proc.communicate(), timeout=timeout) - except asyncio.TimeoutError: + except TimeoutError: proc.kill() await proc.wait() raise RuntimeError(f"ffmpeg timed out after {timeout:.0f}s") @@ -7152,7 +7158,7 @@ class WebRTCTransport: Returns (answer_sdp, ice_candidates) to relay back via hub signaling. ICE candidates are embedded in the SDP (aiortc gathers before returning). """ - from aiortc import RTCIceServer, RTCConfiguration + from aiortc import RTCConfiguration, RTCIceServer # aiortc keeps only the first STUN entry it sees here; the actual # multi-server fan-out is done by transport/stun_multi, which patches diff --git a/packages/meshbay-node/src/meshbay_node/ui/app.py b/packages/meshbay-node/src/meshbay_node/ui/app.py index 77491a0..888487c 100644 --- a/packages/meshbay-node/src/meshbay_node/ui/app.py +++ b/packages/meshbay-node/src/meshbay_node/ui/app.py @@ -16,8 +16,8 @@ import logging from fastapi import FastAPI, HTTPException, Query from fastapi.responses import JSONResponse - from meshbay_common.background import spawn + from meshbay_node import __version__, ops from meshbay_node.indexer.indexer import DirectoryIndexer diff --git a/packages/meshbay-node/src/meshbay_node/uploads.py b/packages/meshbay-node/src/meshbay_node/uploads.py index f8ae7f9..dd31e1e 100644 --- a/packages/meshbay-node/src/meshbay_node/uploads.py +++ b/packages/meshbay-node/src/meshbay_node/uploads.py @@ -25,9 +25,9 @@ build first. from __future__ import annotations import time +from collections.abc import Iterable from dataclasses import dataclass, field from pathlib import Path -from typing import Iterable # What an unfinished upload is called on disk while it is being written. The # node has always used this; it is named here because the reaper below has to |