mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-08-12 10:03:13 +00:00
Add readme
This commit is contained in:
parent
4dbd004298
commit
a0e1226f21
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -3706,6 +3706,21 @@ version = "2.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "meshshark"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossterm 0.29.0",
|
||||||
|
"libc",
|
||||||
|
"logos-delivery",
|
||||||
|
"ratatui",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.9"
|
version = "0.8.9"
|
||||||
|
|||||||
68
bin/meshshark/README.md
Normal file
68
bin/meshshark/README.md
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# meshshark
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
A sniffer for logos-delivery networks. meshshark starts an embedded
|
||||||
|
logos-delivery node, subscribes to the content topics you ask for, and shows
|
||||||
|
arriving-message metadata live in a terminal UI — one pane per subscription, or
|
||||||
|
a single merged stream.
|
||||||
|
|
||||||
|
It reports metadata only: arrival time (as a delta from start), a color-coded
|
||||||
|
content topic, and the payload size. Payload bytes are never inspected, only
|
||||||
|
measured, and an FNV-1a hash of each payload is used as a message id so you can
|
||||||
|
spot duplicate deliveries.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
meshshark --sub <ADDRESS|TOPIC> [--sub …] [--all] [--preset …] [--port …]
|
||||||
|
```
|
||||||
|
|
||||||
|
A value passed to `--sub` that starts with `/` is used verbatim as a content
|
||||||
|
topic; anything else is treated as a delivery address and wrapped as
|
||||||
|
`/logos-chat/1/<address>/proto`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Watch two delivery addresses
|
||||||
|
meshshark --sub saro --sub raya
|
||||||
|
|
||||||
|
# Watch a full content topic, plus a firehose of everything the node receives
|
||||||
|
meshshark --sub /logos-chat/1/saro/proto --all
|
||||||
|
```
|
||||||
|
|
||||||
|
You need at least one `--sub` to join a shard and receive anything, even when
|
||||||
|
using `--all`.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Flag | Default | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `-s, --sub <ADDRESS\|TOPIC>` | — | Topic to watch (repeatable). One pane each. |
|
||||||
|
| `--all` | off | Add a firehose pane showing every message on any content topic. |
|
||||||
|
| `--preset <NAME>` | `logos.dev` | logos-delivery network preset. |
|
||||||
|
| `--log-level <LEVEL>` | `ERROR` | Node log level. Kept quiet so it doesn't corrupt the TUI. |
|
||||||
|
| `--port <PORT>` | OS-assigned | TCP/UDP port for the node. A free port is chosen by default so multiple instances can run side by side. |
|
||||||
|
|
||||||
|
## Keys
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
| --- | --- |
|
||||||
|
| `v` / `Tab` | Toggle between grid and unified views. |
|
||||||
|
| `a` | Add a subscription at runtime (type an address or topic, `Enter` to confirm, `Esc` to cancel). |
|
||||||
|
| `q` / `Esc` | Quit. |
|
||||||
|
| `Ctrl-C` | Quit from any mode. |
|
||||||
|
|
||||||
|
## Views
|
||||||
|
|
||||||
|
- **Grid** — one pane per subscription, each keeping the last 500 messages.
|
||||||
|
- **Unified** — a single newest-first merged stream across all subscriptions,
|
||||||
|
keeping the last 1000 messages.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
meshshark links the native logos-delivery node through the `logos-delivery`
|
||||||
|
crate. Because the embedded Nim/libwaku runtime installs `atexit` handlers that
|
||||||
|
block on node teardown, meshshark exits via `_exit` rather than a normal
|
||||||
|
shutdown — the OS reclaims the node at process exit.
|
||||||
BIN
bin/meshshark/docs/meshshark_screen.png
Normal file
BIN
bin/meshshark/docs/meshshark_screen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 621 KiB |
@ -40,7 +40,10 @@ fn fnv1a_64(bytes: &[u8]) -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(name = "meshshark", about = "See what is happening on a logos-delivery network")]
|
#[command(
|
||||||
|
name = "meshshark",
|
||||||
|
about = "See what is happening on a logos-delivery network"
|
||||||
|
)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
/// Topic to watch (repeatable). One pane each. A value starting with `/`
|
/// Topic to watch (repeatable). One pane each. A value starting with `/`
|
||||||
/// is used as a full content topic; otherwise it is treated as a delivery
|
/// is used as a full content topic; otherwise it is treated as a delivery
|
||||||
@ -92,7 +95,7 @@ fn main() {
|
|||||||
// never needs the bytes themselves.
|
// never needs the bytes themselves.
|
||||||
let start_cfg = P2pConfig {
|
let start_cfg = P2pConfig {
|
||||||
preset: cli.preset.clone(),
|
preset: cli.preset.clone(),
|
||||||
tcp_port: port,
|
port,
|
||||||
log_level: cli.log_level.clone(),
|
log_level: cli.log_level.clone(),
|
||||||
};
|
};
|
||||||
let mut delivery = match ThreadedDeliveryWrapper::start(start_cfg, |event: WakuEvent| {
|
let mut delivery = match ThreadedDeliveryWrapper::start(start_cfg, |event: WakuEvent| {
|
||||||
|
|||||||
@ -125,7 +125,11 @@ impl App {
|
|||||||
/// unified stream. A message on a named topic lands in both its pane and any
|
/// unified stream. A message on a named topic lands in both its pane and any
|
||||||
/// firehose (`ALL`) pane.
|
/// firehose (`ALL`) pane.
|
||||||
pub fn record(&mut self, msg: ObservedMessage) {
|
pub fn record(&mut self, msg: ObservedMessage) {
|
||||||
for pane in self.panes.iter_mut().filter(|p| p.matches(&msg.content_topic)) {
|
for pane in self
|
||||||
|
.panes
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|p| p.matches(&msg.content_topic))
|
||||||
|
{
|
||||||
pane.record(msg.clone());
|
pane.record(msg.clone());
|
||||||
}
|
}
|
||||||
if self.unified.len() == UNIFIED_HISTORY {
|
if self.unified.len() == UNIFIED_HISTORY {
|
||||||
@ -205,7 +209,10 @@ fn draw_header(frame: &mut Frame, app: &App, area: Rect) {
|
|||||||
.fg(Color::Cyan)
|
.fg(Color::Cyan)
|
||||||
.add_modifier(Modifier::BOLD),
|
.add_modifier(Modifier::BOLD),
|
||||||
),
|
),
|
||||||
Span::styled(format!(" · port {}", app.port), Style::default().fg(Color::DarkGray)),
|
Span::styled(
|
||||||
|
format!(" · port {}", app.port),
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
frame.render_widget(Paragraph::new(Line::from(spans)), area);
|
frame.render_widget(Paragraph::new(Line::from(spans)), area);
|
||||||
}
|
}
|
||||||
@ -272,7 +279,12 @@ fn draw_pane(frame: &mut Frame, pane: &Pane, area: Rect, start: Instant) {
|
|||||||
} else {
|
} else {
|
||||||
pane.content_topic.as_str()
|
pane.content_topic.as_str()
|
||||||
};
|
};
|
||||||
let title = format!(" {} ({} msgs, {}) ", label, pane.count, human_bytes(pane.total_bytes),);
|
let title = format!(
|
||||||
|
" {} ({} msgs, {}) ",
|
||||||
|
label,
|
||||||
|
pane.count,
|
||||||
|
human_bytes(pane.total_bytes),
|
||||||
|
);
|
||||||
// Color the border by topic so a pane's color matches its lines in the
|
// Color the border by topic so a pane's color matches its lines in the
|
||||||
// unified view. A firehose pane mixes topics, so leave it neutral.
|
// unified view. A firehose pane mixes topics, so leave it neutral.
|
||||||
let border_color = if pane.match_all {
|
let border_color = if pane.match_all {
|
||||||
@ -357,7 +369,12 @@ fn draw_status(frame: &mut Frame, app: &App, area: Rect) {
|
|||||||
ViewMode::Unified => "unified",
|
ViewMode::Unified => "unified",
|
||||||
};
|
};
|
||||||
let text = if app.status.is_empty() {
|
let text = if app.status.is_empty() {
|
||||||
format!(" {} subs · {} msgs · view {} ", app.panes.len(), total, view)
|
format!(
|
||||||
|
" {} subs · {} msgs · view {} ",
|
||||||
|
app.panes.len(),
|
||||||
|
total,
|
||||||
|
view
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(" {} ", app.status)
|
format!(" {} ", app.status)
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,21 +29,12 @@ pub const DEFAULT_NETWORK_PRESET: &str = "logos.dev";
|
|||||||
/// Default TCP port for the embedded logos-delivery node.
|
/// Default TCP port for the embedded logos-delivery node.
|
||||||
pub const DEFAULT_TCP_PORT: u16 = 60000;
|
pub const DEFAULT_TCP_PORT: u16 = 60000;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
/// The content-topic prefix carrying logos-chat traffic.
|
/// The content-topic prefix carrying logos-chat traffic.
|
||||||
const CHAT_TOPIC_PREFIX: &str = "/logos-chat/1/";
|
const CHAT_TOPIC_PREFIX: &str = "/logos-chat/1/";
|
||||||
|
|
||||||
pub fn content_topic_for(delivery_address: &str) -> String {
|
pub fn content_topic_for(delivery_address: &str) -> String {
|
||||||
format!("{CHAT_TOPIC_PREFIX}{delivery_address}/proto")
|
format!("{CHAT_TOPIC_PREFIX}{delivery_address}/proto")
|
||||||
}
|
}
|
||||||
=======
|
|
||||||
pub fn content_topic_for(delivery_address: &str) -> String {
|
|
||||||
format!("/logos-chat/1/{delivery_address}/proto")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The content-topic prefix carrying logos-chat traffic.
|
|
||||||
const CHAT_TOPIC_PREFIX: &str = "/logos-chat/1/";
|
|
||||||
>>>>>>> 08e9b4f (Isolate logos-delivery)
|
|
||||||
|
|
||||||
// ── EmbeddedLogosDelivery ──────────────────────────────────────────────────
|
// ── EmbeddedLogosDelivery ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@ -49,20 +49,12 @@ type SubscriberList<T> = Arc<Mutex<Vec<Sender<T>>>>;
|
|||||||
pub const DEFAULT_NETWORK_PRESET: &str = "logos.dev";
|
pub const DEFAULT_NETWORK_PRESET: &str = "logos.dev";
|
||||||
|
|
||||||
/// Default TCP port for the embedded logos-delivery node.
|
/// Default TCP port for the embedded logos-delivery node.
|
||||||
<<<<<<< HEAD
|
|
||||||
pub const DEFAULT_PORT: u16 = 60000;
|
pub const DEFAULT_PORT: u16 = 60000;
|
||||||
=======
|
|
||||||
pub const DEFAULT_TCP_PORT: u16 = 60000;
|
|
||||||
>>>>>>> 08e9b4f (Isolate logos-delivery)
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct P2pConfig {
|
pub struct P2pConfig {
|
||||||
pub preset: String,
|
pub preset: String,
|
||||||
<<<<<<< HEAD
|
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
=======
|
|
||||||
pub tcp_port: u16,
|
|
||||||
>>>>>>> 08e9b4f (Isolate logos-delivery)
|
|
||||||
pub log_level: String,
|
pub log_level: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,11 +62,7 @@ impl Default for P2pConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
preset: DEFAULT_NETWORK_PRESET.into(),
|
preset: DEFAULT_NETWORK_PRESET.into(),
|
||||||
<<<<<<< HEAD
|
|
||||||
port: DEFAULT_PORT,
|
port: DEFAULT_PORT,
|
||||||
=======
|
|
||||||
tcp_port: DEFAULT_TCP_PORT,
|
|
||||||
>>>>>>> 08e9b4f (Isolate logos-delivery)
|
|
||||||
log_level: "ERROR".into(),
|
log_level: "ERROR".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,13 +281,8 @@ impl<T> ThreadedDeliveryWrapper<T> {
|
|||||||
"logLevel": cfg.log_level,
|
"logLevel": cfg.log_level,
|
||||||
"mode": "Core",
|
"mode": "Core",
|
||||||
"preset": cfg.preset,
|
"preset": cfg.preset,
|
||||||
<<<<<<< HEAD
|
|
||||||
"tcpPort": cfg.port,
|
"tcpPort": cfg.port,
|
||||||
"discv5UdpPort": cfg.port,
|
"discv5UdpPort": cfg.port,
|
||||||
=======
|
|
||||||
"tcpPort": cfg.tcp_port,
|
|
||||||
"discv5UdpPort": cfg.tcp_port,
|
|
||||||
>>>>>>> 08e9b4f (Isolate logos-delivery)
|
|
||||||
})
|
})
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user