Conversation
…t peer and connection discorvery
Signed-off-by: Tonic <joseph.pollack@emle.eu>
|
@greptileai , comprehensively review this pr : |
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Signed-off-by: Tonic <joseph@legml.ai>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Signed-off-by: Tonic <joseph@legml.ai>
|
@greptileai , comprehensively update your review based on the changes above : |
|
@greptileai comprehensively update your review based on the above : |
|
@greptileai , comprehensively update your review based on the latest changes and the entire PR accordingly : |
|
@greptileai : comprehensively review your assessment of this PR . this PR introduces a lot of changes , some may seem out of scope but they are included in this PR , review them as if they are in scope. review every touched file . review every intent and method and corresponding datamodel. review out of diff and within diff changes. assess the quality of this PR . comprehensively update your assessment of this PR , produce a complete assessment of this PR : |
|
@greptileai : comprehensively review your assessment of this PR . this PR introduces a lot of changes , some may seem out of scope but they are included in this PR , review them as if they are in scope. review every touched file . review every intent and method and corresponding datamodel. review out of diff and within diff changes. assess the quality of this PR . comprehensively update your assessment of this PR , produce a complete assessment of this PR : |
|
@greptileai : comprehensively review your assessment of this PR . this PR introduces a lot of changes , some may seem out of scope but they are included in this PR , review them as if they are in scope. review every touched file . review every intent and method and corresponding datamodel. review out of diff and within diff changes. assess the quality of this PR . comprehensively update your assessment of this PR , produce a complete assessment of this PR , double check your work to assure you are not missing false positives , review this PR comprehensively : |
|
@greptileai : comprehensively review your assessment of this PR . this PR introduces a lot of changes , some may seem out of scope but they are included in this PR , review them as if they are in scope. review every touched file . review every intent and method and corresponding datamodel. review out of diff and within diff changes. assess the quality of this PR . comprehensively update your assessment of this PR , produce a complete assessment of this PR , double check your work to assure you are not missing false positives , review this PR comprehensively : |
1 similar comment
|
@greptileai : comprehensively review your assessment of this PR . this PR introduces a lot of changes , some may seem out of scope but they are included in this PR , review them as if they are in scope. review every touched file . review every intent and method and corresponding datamodel. review out of diff and within diff changes. assess the quality of this PR . comprehensively update your assessment of this PR , produce a complete assessment of this PR , double check your work to assure you are not missing false positives , review this PR comprehensively : |
| self.udp_tracker_client: Optional[Any] = None | ||
| # Queue manager for priority-based torrent scheduling | ||
| self.queue_manager: Optional[Any] = None | ||
| self.key_manager: Optional[Any] = None |
There was a problem hiding this comment.
key_manager parameter silently overwritten with None
self.key_manager = key_manager is assigned from the constructor parameter at line 3490, but then unconditionally overwritten with None at this line — discarding whatever was passed in.
Because key_manager is used in _build_xet_node_id, _ensure_xet_discovery_graph (XET allowlist creation), and XetAllowlist instantiation, any caller that passes a live key manager (e.g., the daemon startup) will silently get no signing/identity capability.
| self.key_manager: Optional[Any] = None | |
| self.key_manager = self.key_manager or None # preserved from __init__ param |
Or simply remove this line — the assignment at line 3490 is sufficient.
| msg = "Cryptography library not available for signing" | ||
| raise RuntimeError(msg) | ||
|
|
||
| # Build message to sign: salt + seq + v (data) | ||
| # BEP 44: sig = sign(salt + seq + v) | ||
| message = salt + seq.to_bytes(8, "big") + data | ||
| # Build message to sign per BEP 44: bencoded-style buffer (salt + seq + v) |
There was a problem hiding this comment.
BEP 44 signature format change breaks all previously stored mutable data
The old signing message was salt + seq.to_bytes(8, "big") + data — a non-standard raw concatenation. The new format is the correct BEP 44 bencoded buffer (4:salt<len>:<salt>3:seqi<seq>e1:v<len>:<v>).
While the new format is spec-compliant, this is a backward-incompatible change: any mutable DHT record written by a previous build will fail verify_mutable_data_signature after this update because the signing message they were signed with no longer matches. Nodes that have not yet upgraded will also reject signatures from upgraded nodes and vice-versa.
There is no migration path in this PR. Consider either:
- Bumping the mutable store version/salt so old records are naturally discarded, or
- Adding a fallback verification attempt with the legacy format during a transition window.
| if peer_manager.is_peer_xet_authorized(peer_id, None): | ||
| return True | ||
| return False | ||
|
|
||
| async def start(self) -> None: | ||
| """Start the sync manager.""" | ||
| if self._running: | ||
| return | ||
|
|
||
| if self.sync_mode == SyncMode.CONSENSUS: | ||
| self.logger.warning( | ||
| "Consensus mode is not transport-backed yet; downgrading to best_effort" | ||
| ) | ||
| self.sync_mode = SyncMode.BEST_EFFORT | ||
| self.set_last_error( | ||
| "Consensus mode is disabled until transport-backed RPCs exist" | ||
| ) | ||
| elif self.sync_mode == SyncMode.BROADCAST: | ||
| if not self._has_healthy_propagation_backend(): | ||
| self.logger.warning( | ||
| "Broadcast mode has no healthy propagation backend; downgrading to best_effort" | ||
| ) | ||
| self.sync_mode = SyncMode.BEST_EFFORT | ||
| self.set_last_error( | ||
| "Broadcast mode requires at least one healthy propagation backend" |
There was a problem hiding this comment.
DESIGNATED mode always downgrades to BEST_EFFORT on first start
_has_verified_designated_source() checks peer_manager.is_peer_xet_authorized() across all active torrent sessions. When a workspace is first opened — which happens before any peer connections are established — self.session_manager.torrents will be empty (or contain sessions with uninitialized peer managers). The check therefore always returns False, and every DESIGNATED-mode workspace silently downgrades to BEST_EFFORT at startup, regardless of configuration.
The last_error is set to a string that implies a legitimacy problem with the peers, which would be confusing to operators.
Consider deferring this authorization check to the first sync cycle (inside _sync_loop) rather than enforcing it as a hard prerequisite in start(). The downgrade should only happen if the peers remain unverifiable after a configurable warm-up period.
Greptile Summary
This PR wires up the XET ("scientific") workspace transport layer: it introduces per-workspace
XetFolderRuntimeownership, a_xet_transport_registryon the session manager, a shared XET discovery graph (CAS client, bloom filter, catalog, LPD, multicast, gossip, flooding),MediaStreamManagerintegration, and canonical internal status models (CanonicalTorrentStatus/CanonicalGlobalStats). It also ships several correctness fixes: BEP 44 mutable-signing now uses the spec-compliant bencoded buffer, DHT index decoding is fixed,XetDeduplication.close()is made idempotent,_cache_infono longer races the dedup connection with a rawsqlite3.connect(),tonic?:URI parsing is corrected, and inline debug log blocks are removed fromsession.py.key_managersilently discarded:AsyncSessionManager.__init__assignsself.key_manager = key_managerfrom the parameter at line 3490, then unconditionally overwrites it withself.key_manager: Optional[Any] = Noneat line 3565. Any caller that passes a live key manager (e.g., the daemon startup path) will have it silently replaced withNone, breaking XET identity, allowlist loading, and node-ID generation.salt + seq.to_bytes(8, "big") + data) to the correct BEP 44 bencoded buffer. This is spec-correct, but all mutable data stored by previous builds will fail signature verification after upgrade. There is no migration or fallback path.DESIGNATEDmode always downgrades at start:XetSyncManager.start()calls_has_verified_designated_source()before any peer connections exist. The check iterates active torrent peer managers, which are empty at workspace-open time, and always returnsFalse— causing everyDESIGNATED-mode workspace to silently downgrade toBEST_EFFORTon startup.Confidence Score: 2/5
ccbt/session/session.py(key_manager double assignment),ccbt/discovery/dht_storage.py(BEP 44 migration),ccbt/session/xet_sync_manager.py(DESIGNATED mode startup check)Important Files Changed
key_managerparameter is overwritten withNonelater in__init__, silently breaking XET identity and signing.Sequence Diagram
sequenceDiagram participant Daemon participant SessionManager as AsyncSessionManager participant XetFolder participant XetSyncManager participant CASClient as P2PCASClient participant XetRealtimeSync Daemon->>SessionManager: __init__(output_dir, key_manager) Note over SessionManager: key_manager param stored at line 3490<br/>then overwritten with None at line 3565 Daemon->>SessionManager: open_xet_workspace(tonic_source, DESIGNATED) SessionManager->>SessionManager: _ensure_xet_discovery_graph() SessionManager->>XetFolder: construct XetFolder(session_manager) SessionManager->>XetFolder: start() XetFolder->>XetSyncManager: start() Note over XetSyncManager: _has_verified_designated_source() runs<br/>before any peer connections exist<br/>Always downgrades to BEST_EFFORT XetFolder->>XetRealtimeSync: start() loop sync cycle every 30s XetRealtimeSync->>XetFolder: _build_file_metadata(file_path) XetRealtimeSync->>XetSyncManager: get_pending_updates_snapshot() XetRealtimeSync->>CASClient: find_chunks_peers_batch(hashes) CASClient-->>XetRealtimeSync: peer list per chunk XetRealtimeSync->>XetSyncManager: register_discovered_peer(peer) endComments Outside Diff (1)
ccbt/session/session.py, line 154-155 (link)Silent DHT behavior change: now initializes for every
.torrentfile whenenable_dht=TruePreviously DHT only initialized when the caller passed
--enable-dhtexplicitly or the torrent was a magnet link. The guarddht_explicitly_requested or is_magnet_linkhas been removed, so now every.torrentfile download starts a DHT client whenconfig.discovery.enable_dht=True(the default).For users who have
enable_dht=Truein config (the default) and are downloading many.torrentfiles, this will:The commit message and inline comment describe this as an intentional change ("allow DHT as fallback for all torrents"), but the default config value
enable_dht=Truemeans all users get this behavior change transparently. If the intent is to allow opt-in, consider introducing a separateenable_dht_for_regular_torrentsconfig key, or document the intentional change in the PR description so it can be deliberately reviewed.Last reviewed commit: 4290693