To implement MCP functionality and chat functionality, libvault is used.#6
To implement MCP functionality and chat functionality, libvault is used.#6wujian0327 wants to merge 41 commits intogitmono-dev:mainfrom
Conversation
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces MCP (stdio + SSE) endpoints and an encrypted P2P chat feature on top of the existing MegaEngine node/gossip/QUIC stack, while also migrating certificate generation to libvault and expanding repository metadata persisted in SQLite.
Changes:
- Add MCP server support (stdio JSON-RPC and SSE/HTTP) with repo tools (list/details/clone).
- Add encrypted P2P chat messages over gossip + local persistence (
chat_messages) and CLI commands (chat send/list). - Update storage schema/models for richer repo metadata and refs; switch certificate generation from
rcgentolibvault/OpenSSL.
Reviewed changes
Copilot reviewed 36 out of 38 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/gossip_three_nodes.rs | Updates test cert file names and adds cert cleanup. |
| tests/git_pack.rs | Minor Command::args idiom updates. |
| tests/bundle_two_nodes.rs | Small Rust idiom cleanups and cert cleanup at end of test. |
| src/transport/quic.rs | Improves close logging and serializes tests to avoid cert/test collisions; adds cleanup helper and new test. |
| src/transport/config.rs | Uses rustls crypto provider schemes and adds QUIC transport keepalive/idle timeout config for clients. |
| src/transport/cert.rs | Replaces rcgen with libvault + OpenSSL for CA/server cert generation; adds unit tests. |
| src/storage/repo_model.rs | Switches DB init to pooled getter and expands repo model fields (language/size/latest_commit_at); adjusts tests. |
| src/storage/ref_model.rs | Changes refs table to composite primary key and adds created_at; updates CRUD queries accordingly. |
| src/storage/node_model.rs | Switches DB access from init_db to get_db_conn. |
| src/storage/mod.rs | Replaces init_db with get_db_conn, adds chat_messages table creation, updates schema DDL. |
| src/storage/chat_message.rs | New SeaORM entity + helpers for persisted chat message queue/status updates. |
| src/repo/repo_manager.rs | Updates tests for new repo description fields. |
| src/repo/repo.rs | Updates P2PDescription to include language/size/latest_commit_at and updates tests. |
| src/node/node_manager.rs | Removes NodeManager module and its tests. |
| src/node/node_id.rs | Minor test cleanup (borrow elision). |
| src/node/node.rs | Minor assertion idiom cleanup in tests. |
| src/node/mod.rs | Removes node_manager module export. |
| src/mcp/sse_server.rs | Adds Axum-based SSE server + /messages JSON-RPC bridge for MCP tools. |
| src/mcp/mod.rs | New MCP module exports (stdio + SSE server starters). |
| src/mcp/mcp_server.rs | Adds stdio JSON-RPC MCP server and repo tool implementations. |
| src/main.rs | Adds chat and mcp CLI commands and routes tracing output to stderr. |
| src/lib.rs | Exposes new chat and mcp modules from the library crate. |
| src/identity/keypair.rs | Adds ECIES-like encryption/decryption for chat using Ed25519→X25519 conversion and ChaCha20-Poly1305. |
| src/gossip/service.rs | Integrates chat + chat ack processing; moves Envelope type to message module; adds verbose ref update logging. |
| src/gossip/message.rs | Adds chat message/ack types and a shared Envelope; changes hashing canonicalization. |
| src/git/pack.rs | Adds post-clone checkout/reset steps to better materialize working tree from bundles. |
| src/git/git_repo.rs | Reads both local and remote refs; adds helper to read latest commit time. |
| src/cli/repo.rs | Adds language/size/latest_commit_at computation, improves listing output, and improves error reporting. |
| src/cli/node.rs | Adds node flags to start MCP stdio server and optional MCP SSE server; starts chat sender background task. |
| src/cli/mod.rs | Wires new chat command handler. |
| src/cli/chat.rs | New CLI for queuing chat messages and listing chat history from DB. |
| src/chat/service.rs | New chat sender task (DB-backed queue) + incoming chat/ack handlers over gossip. |
| src/chat/mod.rs | New chat module export. |
| src/bundle/transfer.rs | Improves chunked transfer to support out-of-order writes via seek and truncation on start. |
| README.md | Documents chat usage. |
| Cargo.toml | Adds dependencies: libvault/openssl/axum/tower-http/uuid/futures/tokio-stream/chacha20poly1305/curve25519-dalek; removes rcgen. |
| Cargo.lock | Updates lockfile for new dependencies. |
| .gitignore | Fixes tmp ignore path and adds dist/. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pub async fn update_repo_bundle(repo_id: &str, bundle_path: &str) -> Result<()> { | ||
| let db = init_db().await?; | ||
| let now = chrono::Local::now().timestamp(); | ||
| let db = get_db_conn().await?; | ||
|
|
||
| // 查询是否存在 | ||
| if let Some(model) = Entity::find_by_id(repo_id).one(&db).await? { | ||
| let active_model = ActiveModel { | ||
| id: Unchanged(model.id), | ||
| bundle: Set(bundle_path.to_string()), | ||
| updated_at: Set(now), | ||
| updated_at: Unchanged(model.updated_at), | ||
| // Keep other fields unchanged |
| ttl: DEFAULT_TTL, | ||
| }; | ||
| tracing::debug!("Broadcasting NodeAnnouncement: {:?}", env); | ||
| tracing::debug!("Broadcasting NodeAnnouncement: {:?}", env); |
| } | ||
|
|
||
| tokio::time::sleep(Duration::from_secs(30)).await; | ||
| tokio::time::sleep(Duration::from_secs(30)).await; |
| tracing::info!("MCP server enabled, starting alongside node"); | ||
| println!("MCP server is enabled"); | ||
| std::thread::spawn(|| { | ||
| let rt = tokio::runtime::Runtime::new().unwrap(); | ||
| if let Err(e) = rt.block_on(start_mcp_server()) { | ||
| tracing::error!("MCP server error: {}", e); | ||
| } | ||
| }); |
| ttl: DEFAULT_TTL, | ||
| }; | ||
| tracing::debug!("Broadcasting RepoAnnouncement: {:?}", env); | ||
| tracing::debug!("Broadcasting RepoAnnouncement: {:?}", env); |
| let app = Router::new() | ||
| .route("/sse", get(sse_handler)) | ||
| .route("/messages", post(message_handler)) | ||
| .layer(CorsLayer::permissive()) | ||
| .with_state(state); |
| // Store the sender | ||
| state | ||
| .sessions | ||
| .write() | ||
| .await | ||
| .insert(session_id.clone(), tx.clone()); | ||
|
|
| if let Some(port) = mcp_sse_port { | ||
| tracing::info!("MCP SSE server enabled on port {}", port); | ||
| println!("MCP SSE server enabled on port {}", port); | ||
| tokio::spawn(async move { | ||
| let addr = std::net::SocketAddr::from(([0, 0, 0, 0], port)); | ||
| if let Err(e) = start_sse_server(addr).await { | ||
| tracing::error!("MCP SSE server error: {}", e); | ||
| } | ||
| }); |
| let path = db_path(); | ||
|
|
||
| { | ||
| let map = pool.lock().await; | ||
| if let Some(db) = map.get(&path) { | ||
| return Ok(db.clone()); | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e95d8f6d30
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| "CREATE TABLE IF NOT EXISTS repos ( | ||
| id TEXT PRIMARY KEY, | ||
| name TEXT NOT NULL, | ||
| creator TEXT NOT NULL, | ||
| description TEXT NOT NULL, |
There was a problem hiding this comment.
Add migration for existing repos schema
This change introduces new repos columns (language, size, latest_commit_at) but only executes CREATE TABLE IF NOT EXISTS repos (...). For users upgrading from the previous schema, SQLite keeps the old table definition, and later SeaORM reads/writes against the new model will fail with missing-column errors, breaking repo load/list flows after upgrade. Add an explicit migration/ALTER TABLE path before using the new fields.
Useful? React with 👍 / 👎.
| let envelope = Envelope { | ||
| payload: signed_msg, | ||
| ttl: TTL, | ||
| }; |
There was a problem hiding this comment.
Stop resetting TTL when relaying chat
When a chat message is not for the current node, this code re-wraps and rebroadcasts it with ttl: TTL (16). GossipService::handle_incoming already forwards handled messages with decremented TTL, so this extra relay path both duplicates each hop and reintroduces fresh TTL, which can cause persistent message amplification/loops in multi-hop networks.
Useful? React with 👍 / 👎.
src/chat/service.rs
Outdated
| let _ = mgr.send_gossip_message(receiver_node_id.clone(), data.clone()).await; | ||
| } else { | ||
| for peer in peers { | ||
| let _ = mgr.send_gossip_message(peer.clone(), data.clone()).await; |
There was a problem hiding this comment.
Return error when gossip send fails
Both send paths ignore send_gossip_message failures (let _ = ...), but callers treat try_send_pending_msg success as delivery progress and move DB status from Sending to Sent. If connections are stale or writes fail, the message is incorrectly marked sent and is no longer retried, causing silent message loss.
Useful? React with 👍 / 👎.
| tokio::time::sleep(Duration::from_secs(30)).await; | ||
| tokio::time::sleep(Duration::from_secs(30)).await; |
There was a problem hiding this comment.
Remove duplicate sleep in gossip loop
The broadcaster loop now sleeps twice for 30 seconds each cycle, effectively doubling the announcement period from 30s to about 60s. That slows node/repository gossip convergence and delays propagation compared with the intended interval.
Useful? React with 👍 / 👎.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
@copilot open a new pull request to apply changes based on the comments in this thread |
There was a problem hiding this comment.
Pull request overview
This PR adds MCP support (stdio + SSE), introduces an encrypted P2P chat feature, and switches certificate generation to libvault/OpenSSL while extending the local SQLite schema and repo metadata.
Changes:
- Add MCP servers: stdio JSON-RPC (
mcp) and an Axum-based SSE/HTTP endpoint (--mcp-sse-port). - Add P2P chat: CLI commands + DB-backed outgoing queue + gossip message types + encryption helpers.
- Update transport/storage: libvault-based cert generation, QUIC client transport tweaks, repo/ref schema changes, and improved bundle chunk writing.
Reviewed changes
Copilot reviewed 36 out of 38 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/gossip_three_nodes.rs | Update test cert filenames and add cleanup. |
| tests/git_pack.rs | Minor Rust idiom cleanup for Command::args. |
| tests/bundle_two_nodes.rs | Minor iterator/strings cleanup; add cert cleanup. |
| src/transport/quic.rs | Improve connection close logging; serialize some transport tests; add cert cleanup + new test. |
| src/transport/config.rs | Make verifier schemes provider-driven; add QUIC client keepalive/idle timeout config. |
| src/transport/cert.rs | Replace rcgen with libvault/OpenSSL cert generation + add tests. |
| src/storage/repo_model.rs | Extend repo DB model (language/size/latest_commit_at) + use get_db_conn. |
| src/storage/ref_model.rs | Change refs primary key strategy and add created_at; update queries. |
| src/storage/node_model.rs | Switch DB access to get_db_conn. |
| src/storage/mod.rs | Replace init_db with pooled get_db_conn; update/create tables (incl. chat_messages). |
| src/storage/chat_message.rs | New SeaORM entity + helpers for chat message persistence. |
| src/repo/repo_manager.rs | Update tests for new repo description fields. |
| src/repo/repo.rs | Update repo description struct + tests for new fields. |
| src/node/node_manager.rs | Remove NodeManager module and tests. |
| src/node/node_id.rs | Minor API usage cleanup in tests. |
| src/node/node.rs | Minor assertion style cleanup in tests. |
| src/node/mod.rs | Stop exporting removed node_manager module. |
| src/mcp/sse_server.rs | New Axum SSE MCP endpoint + message handler. |
| src/mcp/mod.rs | New MCP module exports. |
| src/mcp/mcp_server.rs | New stdio MCP JSON-RPC server and repo tools. |
| src/main.rs | Add CLI commands: chat and mcp; route tracing output to stderr. |
| src/lib.rs | Export new chat and mcp modules. |
| src/identity/keypair.rs | Add chat encryption/decryption helpers (Curve25519 + ChaCha20Poly1305). |
| src/gossip/service.rs | Add chat message handling; move Envelope type; add extra logging (with some duplicates). |
| src/gossip/message.rs | Add chat/ack message types; add shared Envelope; adjust hashing canonicalization. |
| src/git/pack.rs | Add post-clone checkout/reset steps when restoring from bundles. |
| src/git/git_repo.rs | Include remote branch refs; add latest commit time helper. |
| src/cli/repo.rs | Add language/size/latest_commit_at; improve repo list UX and status output. |
| src/cli/node.rs | Start chat sender task; add --mcp and --mcp-sse-port node options. |
| src/cli/mod.rs | Export chat CLI handler. |
| src/cli/chat.rs | New chat send and chat list commands (DB-backed queue/history). |
| src/chat/service.rs | New chat send/receive/ack logic integrated with gossip + DB queue processing. |
| src/chat/mod.rs | New chat module. |
| src/bundle/transfer.rs | Write chunks by offset (seek) and ensure file truncation at start. |
| README.md | Document new chat command usage. |
| Cargo.toml | Add dependencies: libvault/openssl/axum/tower-http/uuid/crypto crates. |
| Cargo.lock | Dependency lock updates for new crates. |
| .gitignore | Ignore dist/ and adjust tmp ignore pattern. |
Comments suppressed due to low confidence (1)
src/transport/cert.rs:146
- If the CA cert/key are missing or regenerated (e.g. CA key deleted),
generate_ca_cert()will create a new CA, butensure_certificates()will NOT regenerate an existing server cert/key pair (cert_exists && key_existsstays true). That can leave a server cert signed by an old CA while clients trust the new CA, causing TLS failures. Consider detecting CA regeneration (or missing CA key) and forcing server cert/key regeneration in that case.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| tokio::time::sleep(Duration::from_secs(30)).await; | ||
| tokio::time::sleep(Duration::from_secs(30)).await; |
| if msg.receiver_id != *my_node.node_id() { | ||
| tracing::info!("Message not for me, forwarding to {}", msg.receiver_id); | ||
|
|
||
| // Construct the payload to forward | ||
| let gossip_msg = GossipMessage::Chat(msg.clone()); | ||
|
|
||
| let mut signed_msg = SignedMessage { | ||
| node_id: my_node.node_id().clone(), | ||
| message: gossip_msg, | ||
| timestamp: timestamp_now(), | ||
| signature: "".to_string(), | ||
| }; | ||
| let self_hash = signed_msg.self_hash(); | ||
| let sign = my_node.sign_message(self_hash.as_slice())?; | ||
| signed_msg.signature = hex::encode(sign); | ||
|
|
||
| let envelope = Envelope { | ||
| payload: signed_msg, | ||
| ttl: TTL, | ||
| }; | ||
| let data = serde_json::to_vec(&envelope)?; |
| // Store the sender | ||
| state | ||
| .sessions | ||
| .write() | ||
| .await | ||
| .insert(session_id.clone(), tx.clone()); | ||
|
|
| } | ||
| } | ||
| // Handle other JSON-RPC methods or notifications if needed | ||
| _ => None, |
| let active_model = ActiveModel { | ||
| id: Unchanged(model.id), | ||
| bundle: Set(bundle_path.to_string()), | ||
| updated_at: Set(now), | ||
| updated_at: Unchanged(model.updated_at), | ||
| // Keep other fields unchanged |
| // Format result as MCP CallToolResult with text content | ||
| let content_text = result_value.to_string(); | ||
| Some(json!({ | ||
| "jsonrpc": "2.0", | ||
| "id": request.get("id"), | ||
| "result": { | ||
| "content": [{ | ||
| "type": "text", | ||
| "text": content_text | ||
| }], | ||
| "isError": false | ||
| } |
| tracing::info!("ACK not for me (for {}), forwarding", ack.target_id); | ||
|
|
||
| // Construct the payload to forward | ||
| let gossip_msg = GossipMessage::ChatAck(ack.clone()); | ||
|
|
||
| let mut signed_msg = SignedMessage { | ||
| node_id: my_node.node_id().clone(), | ||
| message: gossip_msg, | ||
| timestamp: timestamp_now(), | ||
| signature: "".to_string(), | ||
| }; | ||
| let self_hash = signed_msg.self_hash(); | ||
| let sign = my_node.sign_message(self_hash.as_slice())?; | ||
| signed_msg.signature = hex::encode(sign); | ||
|
|
||
| let envelope = Envelope { | ||
| payload: signed_msg, | ||
| ttl: TTL, | ||
| }; | ||
| let data = serde_json::to_vec(&envelope)?; | ||
|
|
||
| let mgr = manager.lock().await; | ||
| let peers = mgr.list_peers().await; | ||
|
|
||
| if peers.contains(&ack.target_id) { | ||
| tracing::info!("Found target {} in neighbors, sending ACK directly", ack.target_id); | ||
| let _ = mgr.send_gossip_message(ack.target_id.clone(), data).await; | ||
| } else { | ||
| tracing::info!("Target {} not in neighbors, broadcasting ACK", ack.target_id); | ||
| for peer in peers { | ||
| if peer != ack.sender_id { | ||
| let _ = mgr.send_gossip_message(peer.clone(), data.clone()).await; | ||
| } | ||
| } | ||
| } |
| let app = Router::new() | ||
| .route("/sse", get(sse_handler)) | ||
| .route("/messages", post(message_handler)) | ||
| .layer(CorsLayer::permissive()) | ||
| .with_state(state); |
| { | ||
| let map = pool.lock().await; | ||
| if let Some(db) = map.get(&path) { | ||
| return Ok(db.clone()); | ||
| } | ||
| } |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 00cd2745c0
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| GossipMessage::Chat(c) => { | ||
| if let Err(e) = crate::chat::service::process_incoming_chat( | ||
| c.clone(), | ||
| self.manager.clone(), |
There was a problem hiding this comment.
Validate chat sender against signed envelope
handle_incoming verifies the outer signature with signed.node_id, but this chat branch forwards c without checking that c.sender_id matches that authenticated signer. A malicious peer can sign with its own key while setting sender_id to someone else, and downstream handling will treat the spoofed sender as genuine. Add an explicit signer/sender consistency check before accepting or forwarding chat messages.
Useful? React with 👍 / 👎.
| crate::storage::chat_message::update_message_status( | ||
| &ack.msg_id, | ||
| MessageStatus::Delivered, | ||
| ) |
There was a problem hiding this comment.
Reject unauthenticated ACKs before marking delivered
process_ack marks a message as Delivered using only ack.msg_id and ack.target_id, but it never verifies that the ACK actually came from the original recipient (or that ack.signature is valid). Since msg_id is visible in relayed chat envelopes, an intermediate peer can forge an ACK and force false delivery state. Validate ACK provenance against the authenticated sender and expected recipient before updating status.
Useful? React with 👍 / 👎.
| tracing::info!("Received Chat from {}: {}", msg.sender_id.0, content); | ||
|
|
||
| // 3. Store | ||
| let db = crate::storage::get_db_conn().await.unwrap(); |
There was a problem hiding this comment.
Remove panic on DB init failure in chat receive path
This unwrap() turns any transient database error into a panic during chat receive handling. Because it runs inside the gossip processing task, a DB open failure can crash that task and stop processing subsequent gossip/chat messages. Propagate the error (or handle it explicitly) instead of panicking here.
Useful? React with 👍 / 👎.
Start mcp server
To start a MegaEngine node with MCP enabled, launch the node as usual and add the --mcp flag, for example:
if you also want an MCP SSE endpoint, add --mcp-sse-port 3001 (or another port), such as:
Send a message to node1:
Use libvault
Generate certificates using libvault