Przeglądaj źródła

doc: Update documentation for DarkIRC

x 3 tygodni temu
rodzic
commit
abad64cfdd
37 zmienionych plików z 874 dodań i 1705 usunięć
  1. 0 18
      Makefile
  2. 4 4
      bin/darkirc/config/darkirc-clearnet.toml
  3. 4 4
      bin/darkirc/config/darkirc-i2p.toml
  4. 11 11
      bin/darkirc/config/darkirc-mixed.toml
  5. 5 5
      bin/darkirc/config/darkirc-tor.toml
  6. 10 3
      bin/darkirc/config/inbound.toml
  7. 10 3
      bin/darkirc/config/outbound.toml
  8. 10 3
      bin/darkirc/config/seed.toml
  9. 13 11
      bin/darkirc/darkirc_config.toml
  10. 43 18
      bin/darkirc/src/main.rs
  11. 1 1
      doc/build_jsonrpc.py
  12. 2 2
      doc/generate_seminar_ics.py
  13. 3 1
      doc/src/SUMMARY.md
  14. 12 104
      doc/src/arch/arch.md
  15. 3 3
      doc/src/arch/net/p2p-network.md
  16. 5 5
      doc/src/dev/dev.md
  17. 8 7
      doc/src/learn/dchat/network-tools/using-dnet.md
  18. 80 0
      doc/src/misc/darkirc/archive-node.md
  19. 151 356
      doc/src/misc/darkirc/darkirc.md
  20. 81 0
      doc/src/misc/darkirc/normal-node.md
  21. 48 69
      doc/src/misc/darkirc/private_message.md
  22. 51 98
      doc/src/misc/darkirc/specification.md
  23. 6 35
      doc/src/misc/dnetview.md
  24. 1 1
      doc/src/misc/event_graph/recovery.md
  25. 5 4
      doc/src/misc/faq.md
  26. 114 250
      doc/src/misc/network-troubleshooting.md
  27. 25 101
      doc/src/misc/nodes/i2p-guide.md
  28. 6 4
      doc/src/misc/nodes/node-configurations.md
  29. 22 120
      doc/src/misc/nodes/nym-guide.md
  30. 57 305
      doc/src/misc/nodes/public-guide.md
  31. 55 135
      doc/src/misc/nodes/tor-guide.md
  32. 13 11
      doc/src/start-here.md
  33. 1 1
      src/event_graph/deg.rs
  34. 4 4
      src/event_graph/mod.rs
  35. 4 4
      src/event_graph/proto.rs
  36. 3 3
      src/event_graph/rln.rs
  37. 3 1
      src/event_graph/util.rs

+ 0 - 18
Makefile

@@ -26,8 +26,6 @@ BINS = \
 	darkfid \
 	drk \
 	darkirc \
-	genev \
-	genevd \
 	lilith \
 	taud \
 	vanityaddr \
@@ -73,20 +71,6 @@ darkirc: $(PROOFS_BIN) zkas
 		RUST_TARGET="$(RUST_TARGET)" \
 		RUSTFLAGS="$(RUSTFLAGS)"
 
-genev: $(PROOFS_BIN)
-	$(MAKE) -C bin/genev/genev-cli \
-		PREFIX="$(PREFIX)" \
-		CARGO="$(CARGO)" \
-		RUST_TARGET="$(RUST_TARGET)" \
-		RUSTFLAGS="$(RUSTFLAGS)"
-
-genevd: $(PROOFS_BIN)
-	$(MAKE) -C bin/genev/genevd \
-		PREFIX="$(PREFIX)" \
-		CARGO="$(CARGO)" \
-		RUST_TARGET="$(RUST_TARGET)" \
-		RUSTFLAGS="$(RUSTFLAGS)"
-
 lilith:
 	$(MAKE) -C bin/$@ \
 		PREFIX="$(PREFIX)" \
@@ -179,8 +163,6 @@ clean:
 	$(MAKE) -C bin/darkfid clean
 	$(MAKE) -C bin/drk clean
 	$(MAKE) -C bin/darkirc clean
-	$(MAKE) -C bin/genev/genev-cli clean
-	$(MAKE) -C bin/genev/genevd clean
 	$(MAKE) -C bin/lilith clean
 	$(MAKE) -C bin/tau/taud clean
 	$(MAKE) -C bin/vanityaddr clean

+ 4 - 4
bin/darkirc/config/darkirc-clearnet.toml

@@ -15,7 +15,7 @@ autojoin = [
 magic_bytes = [251, 229, 199, 181]
 
 ## Path to the P2P datastore
-datastore = "~/.local/share/darkfi/darkirc"
+p2p_datastore = "~/.local/share/darkfi/darkirc/p2p"
 
 ## Path to a configured hostlist for saving known peers
 hostlist = "~/.local/share/darkfi/darkirc/p2p_hostlist.tsv"
@@ -34,10 +34,10 @@ mixed_profiles = []
 
 [net.profiles."tcp+tls"]
 ## Seed nodes to connect to
-seeds = ["tcp+tls://lilith1.dark.fi:25551"]
+seeds = ["tcp+tls://lilith0.dark.fi:9600", "tcp+tls://lilith1.dark.fi:9600"]
 
 ## Addresses we want to advertise to peers
-external_addrs = ["tcp+tls://MY_IP_V4:26661", "tcp+tls://MY_IP_V6:26661", "tcp+tls://my.resolveable.address:26661"]
+external_addrs = ["tcp+tls://MY_IP_V4:9600", "tcp+tls://MY_IP_V6:9600", "tcp+tls://my.resolvable.address:9600"]
 
 ## P2P accept addresses
-inbound = ["tcp+tls://0.0.0.0:26661", "tcp+tls://[::]:26661"]
+inbound = ["tcp+tls://0.0.0.0:9600", "tcp+tls://[::]:9600"]

+ 4 - 4
bin/darkirc/config/darkirc-i2p.toml

@@ -15,7 +15,7 @@ autojoin = [
 magic_bytes = [251, 229, 199, 181]
 
 ## Path to the P2P datastore
-datastore = "~/.local/share/darkfi/darkirc"
+p2p_datastore = "~/.local/share/darkfi/darkirc/p2p"
 
 ## Path to a configured hostlist for saving known peers
 hostlist = "~/.local/share/darkfi/darkirc/p2p_hostlist.tsv"
@@ -42,11 +42,11 @@ i2p_socks5_proxy = "socks5://127.0.0.1:4447"
 ## Seed nodes to connect to
 seeds = [
     ##TODO: replace with an official seed address
-    "i2p://6l2rdfriixo2nh5pr5bt555lyz56qox2ikzia4kuzm4okje7gtmq.b32.i2p:5262"
+    "i2p://6l2rdfriixo2nh5pr5bt555lyz56qox2ikzia4kuzm4okje7gtmq.b32.i2p:9600"
 ]
 
 ## Addresses we want to advertise to peers
-external_addrs = ["i2p://youraddress.b32.i2p:25551"]
+external_addrs = ["i2p://youraddress.b32.i2p:9600"]
 
 ## P2P accept addresses
-inbound = ["tcp://127.0.0.1:25551"]
+inbound = ["tcp://127.0.0.1:9601"]

+ 11 - 11
bin/darkirc/config/darkirc-mixed.toml

@@ -15,7 +15,7 @@ autojoin = [
 magic_bytes = [251, 229, 199, 181]
 
 ## Path to the P2P datastore
-datastore = "~/.local/share/darkfi/darkirc"
+p2p_datastore = "~/.local/share/darkfi/darkirc/p2p"
 
 ## Path to a configured hostlist for saving known peers
 hostlist = "~/.local/share/darkfi/darkirc/p2p_hostlist.tsv"
@@ -37,39 +37,39 @@ mixed_profiles = []
 
 [net.profiles."tcp+tls"]
 ## Seed nodes to connect to
-seeds = ["tcp+tls://lilith0.dark.fi:25551", "tcp+tls://lilith1.dark.fi:25551"]
+seeds = ["tcp+tls://lilith0.dark.fi:9600", "tcp+tls://lilith1.dark.fi:9600"]
 
 ## P2P accept addresses
-inbound = ["tcp+tls://0.0.0.0:26661", "tcp+tls://[::]:26661"]
+inbound = ["tcp+tls://0.0.0.0:9600", "tcp+tls://[::]:9600"]
 
 ## Addresses we want to advertise to peers
-external_addrs = ["tcp+tls://MY_IP_V4:26661", "tcp+tls://MY_IP_V6:26661", "tcp+tls://my.resolveable.address:26661"]
+external_addrs = ["tcp+tls://MY_IP_V4:9600", "tcp+tls://MY_IP_V6:9600", "tcp+tls://my.resolvable.address:9600"]
 
 [net.profiles."tor"]
 ## Seed nodes to connect to
 seeds = [
-    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:25552",
-    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:25552",
+    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:9600",
+    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:9600",
 ]
 
 ## P2P accept addresses
-inbound = ["tcp://127.0.0.1:26662"]
+inbound = ["tcp://127.0.0.1:9601"]
 
 ## Addresses we want to advertise to peers
-external_addrs = ["tor://youraddress.onion:26662"]
+external_addrs = ["tor://youraddress.onion:9600"]
 
 [net.profiles."i2p"]
 ## Seed nodes to connect to
 seeds = [
     ##TODO: replace with an official seed address
-    "i2p://6l2rdfriixo2nh5pr5bt555lyz56qox2ikzia4kuzm4okje7gtmq.b32.i2p:5262"
+    "i2p://6l2rdfriixo2nh5pr5bt555lyz56qox2ikzia4kuzm4okje7gtmq.b32.i2p:9600"
 ]
 
 ## Manual peers to connect to
 #peers = []
 
 ## P2P accept addresses
-inbound = ["tcp://127.0.0.1:26663"]
+inbound = ["tcp://127.0.0.1:9602"]
 
 ## Addresses we want to advertise to peers
-external_addrs = ["i2p://youraddress.b32.i2p:26663"]
+external_addrs = ["i2p://youraddress.b32.i2p:9600"]

+ 5 - 5
bin/darkirc/config/darkirc-tor.toml

@@ -15,7 +15,7 @@ autojoin = [
 magic_bytes = [251, 229, 199, 181]
 
 ## Path to the P2P datastore
-datastore = "~/.local/share/darkfi/darkirc"
+p2p_datastore = "~/.local/share/darkfi/darkirc/p2p"
 
 ## Path to a configured hostlist for saving known peers
 hostlist = "~/.local/share/darkfi/darkirc/p2p_hostlist.tsv"
@@ -38,12 +38,12 @@ mixed_profiles = []
 [net.profiles."tor"]
 ## Seed nodes to connect to
 seeds = [
-    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:25552",
-    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:25552",
+    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:9600",
+    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:9600",
 ]
 
 ## Addresses we want to advertise to peers
-external_addrs = ["tor://youraddress.onion:26661"]
+external_addrs = ["tor://youraddress.onion:9600"]
 
 ## P2P accept addresses
-inbound = ["tcp://127.0.0.1:26661"]
+inbound = ["tcp://127.0.0.1:9601"]

+ 10 - 3
bin/darkirc/config/inbound.toml

@@ -2,7 +2,7 @@
 irc_listen = "tcp://127.0.0.1:11067"
 
 ## Sets Datastore Path
-datastore = "~/.config/darkfi/darkirc-inbound"
+datastore = "~/.local/share/darkfi/darkirc/localnet/inbound/darkirc_db"
 
 ## JSON-RPC settings
 [rpc]
@@ -17,6 +17,13 @@ rpc_disabled_methods = ["p2p.get_info"]
 # Magic Bytes to distinguish the p2p network
 magic_bytes = [251, 229, 199, 181]
 
+## Permit loopback P2P addresses for this development network
+localnet = true
+
+## Dedicated P2P state
+p2p_datastore = "~/.local/share/darkfi/darkirc/localnet/inbound/p2p"
+hostlist = "~/.local/share/darkfi/darkirc/localnet/inbound/p2p_hostlist.tsv"
+
 ## Connection slots
 #outbound_connections=5
 
@@ -31,7 +38,7 @@ seeds = ["tcp://127.0.0.1:11001"]
 inbound = ["tcp://127.0.0.1:11002"]
 
 ## P2P external addresses
-external_addr = ["tcp://127.0.0.1:11004"]
+external_addrs = ["tcp://127.0.0.1:11002"]
 
 ## Peers to connect to
-#peers=["tcp://127.0.0.1:11003"]
+#peers=["tcp://127.0.0.1:11003"]

+ 10 - 3
bin/darkirc/config/outbound.toml

@@ -2,7 +2,7 @@
 irc_listen = "tcp://127.0.0.1:11066"
 
 ## Sets Datastore Path
-datastore = "~/.config/darkfi/darkirc-outbound"
+datastore = "~/.local/share/darkfi/darkirc/localnet/outbound/darkirc_db"
 
 ## JSON-RPC settings
 [rpc]
@@ -17,6 +17,13 @@ rpc_disabled_methods = ["p2p.get_info"]
 # Magic Bytes to distinguish the p2p network
 magic_bytes = [251, 229, 199, 181]
 
+## Permit loopback P2P addresses for this development network
+localnet = true
+
+## Dedicated P2P state
+p2p_datastore = "~/.local/share/darkfi/darkirc/localnet/outbound/p2p"
+hostlist = "~/.local/share/darkfi/darkirc/localnet/outbound/p2p_hostlist.tsv"
+
 ## Connection slots
 outbound_connections = 5
 
@@ -31,7 +38,7 @@ seeds = ["tcp://127.0.0.1:11001"]
 # inbound=["tcp://127.0.0.1:11002"]
 
 ## P2P external addresses
-#external_addr=["tcp://127.0.0.1:11002"]
+#external_addrs = ["tcp://127.0.0.1:11002"]
 
 ## Peers to connect to
-#peers=["tcp://127.0.0.1:11003"]
+peers = ["tcp://127.0.0.1:11002"]

+ 10 - 3
bin/darkirc/config/seed.toml

@@ -2,7 +2,7 @@
 irc_listen = "tcp://127.0.0.1:11065"
 
 ## Sets Datastore Path
-datastore = "~/.config/darkfi/darkirc-seed"
+datastore = "~/.local/share/darkfi/darkirc/localnet/seed/darkirc_db"
 
 ## JSON-RPC settings
 [rpc]
@@ -17,6 +17,13 @@ rpc_disabled_methods = ["p2p.get_info"]
 # Magic Bytes to distinguish the p2p network
 magic_bytes = [251, 229, 199, 181]
 
+## Permit loopback P2P addresses for this development network
+localnet = true
+
+## Dedicated P2P state
+p2p_datastore = "~/.local/share/darkfi/darkirc/localnet/seed/p2p"
+hostlist = "~/.local/share/darkfi/darkirc/localnet/seed/p2p_hostlist.tsv"
+
 ## Connection slots
 # outbound_connections=5
 
@@ -31,7 +38,7 @@ inbound = ["tcp://127.0.0.1:11001"]
 # seeds=["tcp://127.0.0.1:11002"]
 
 ## P2P external addresses
-# external_addr=["tcp://127.0.0.1:11001"]
+external_addrs = ["tcp://127.0.0.1:11001"]
 
 ## Peers to connect to
-# peers=["tcp://127.0.0.1:11001"]
+# peers=["tcp://127.0.0.1:11001"]

+ 13 - 11
bin/darkirc/darkirc_config.toml

@@ -13,11 +13,14 @@
 ## How many recent DAGs to sync at startup. Each DAG is 1 hour of
 ## message history. This can be larger than 24 if history_retention_dags
 ## is also larger.
-#dags_count = 8
+#dags_count = 24
 
-## How many rotating DAGs to retain locally. This controls how much
-## history the node can serve or lazily sync without old DAGs being
-## pruned from sled.
+## Archive mode never prunes rotating DAGs. Storage use grows for as long as
+## the node runs. history_retention_dags is ignored when this is enabled.
+#archive_mode = false
+
+## How many rotating DAGs a normal node retains locally. This controls how
+## much history the node can serve or lazily sync before old DAGs are pruned.
 #history_retention_dags = 24
 
 ## Sets Datastore Path
@@ -31,7 +34,7 @@
 #rln_enabled = false
 
 ## Datastore path for RLN proving and verifying keys
-#zk_key_datastore = "~/.local/share/darkfi/darkirc_zk_keys"
+#zk_key_datastore = "~/.local/share/darkfi/darkirc/zk_keys"
 
 ## Sled cache capacity for the RLN key datastore, in MiB
 #zk_key_sled_cache_mb = 16
@@ -67,7 +70,7 @@ autojoin = [
 
 ## Running darkirc in header-only mode
 ## history won't be fetched but DAG sync fast
-#fast_mod = false
+#fast_mode = false
 
 ## JSON-RPC settings
 [rpc]
@@ -83,7 +86,7 @@ rpc_disabled_methods = ["p2p.get_info"]
 magic_bytes = [251, 229, 199, 181]
 
 # Path to the P2P datastore
-p2p_datastore = "~/.local/share/darkfi/darkirc"
+p2p_datastore = "~/.local/share/darkfi/darkirc/p2p"
 
 # Path to a configured hostlist for saving known peers
 hostlist = "~/.local/share/darkfi/darkirc/p2p_hostlist.tsv"
@@ -215,7 +218,7 @@ seeds = [
 ## IRC channel settings
 ## ====================
 ##
-## You can create a shared secret with `darkirc --gen-secret`.
+## You can create a shared secret with `darkirc --gen-channel-secret`.
 ## Never share this secret over unencrypted channels or with someone
 ## who you do not want to be able to read all the channel messages.
 ## Use it like this example:
@@ -252,9 +255,8 @@ topic = "LunarDAO talk"
 ## ================
 ##
 ## In this section we configure our contacts and people we want to
-## have encrypted DMs with. Whenever something in the configuration
-## is changed, you can send a SIGHUP signal to the running darkirc
-## instance to reload these.
+## have encrypted DMs with. After changing contacts, send /rehash from
+## an IRC client (or restart darkirc) to reload them.
 ##
 ## The format is:
 ## [contact."nickname"]

+ 43 - 18
bin/darkirc/src/main.rs

@@ -102,11 +102,19 @@ fn sled_cache_capacity_bytes(name: &str, cache_mb: u64) -> Result<u64> {
         .ok_or_else(|| Error::Custom(format!("{name} overflows bytes")))
 }
 
-fn validate_history_window(dags_count: usize, history_retention_dags: usize) -> Result<()> {
+fn history_retention_limit(
+    dags_count: usize,
+    history_retention_dags: usize,
+    archive_mode: bool,
+) -> Result<Option<usize>> {
     if dags_count == 0 {
         return Err(Error::Custom("dags_count must be greater than 0".to_string()))
     }
 
+    if archive_mode {
+        return Ok(None)
+    }
+
     if history_retention_dags == 0 {
         return Err(Error::Custom("history_retention_dags must be greater than 0".to_string()))
     }
@@ -118,7 +126,7 @@ fn validate_history_window(dags_count: usize, history_retention_dags: usize) ->
         )))
     }
 
-    Ok(())
+    Ok(Some(history_retention_dags))
 }
 
 fn panic_hook(panic_info: &std::panic::PanicHookInfo) {
@@ -158,14 +166,18 @@ struct Args {
     irc_tls_secret: Option<String>,
 
     /// How many recent DAGs to sync at startup.
-    #[structopt(long, default_value = "8")]
+    #[structopt(long, default_value = "24")]
     dags_count: usize,
 
+    #[structopt(long)]
+    /// Retain every rotating DAG instead of pruning old history
+    archive_mode: bool,
+
     #[structopt(long, default_value = "24")]
-    /// How many rotating DAGs to retain locally
+    /// How many rotating DAGs to retain locally in normal mode
     history_retention_dags: usize,
 
-    #[structopt(long, default_value = "~/.local/share/darkfi/darkirc_db")]
+    #[structopt(long, default_value = "~/.local/share/darkfi/darkirc/darkirc_db")]
     /// Datastore (DB) path
     datastore: String,
 
@@ -173,7 +185,7 @@ struct Args {
     /// Sled cache capacity for the datastore, in MiB
     sled_cache_mb: u64,
 
-    #[structopt(long, default_value = "~/.local/share/darkfi/darkirc_zk_keys")]
+    #[structopt(long, default_value = "~/.local/share/darkfi/darkirc/zk_keys")]
     /// Datastore path for RLN proving and verifying keys
     zk_key_datastore: String,
 
@@ -185,7 +197,7 @@ struct Args {
     /// Enable RLN proof generation and verification
     rln_enabled: Option<bool>,
 
-    #[structopt(short, long, default_value = "~/.local/share/darkfi/replayed_darkirc_db")]
+    #[structopt(short, long, default_value = "~/.local/share/darkfi/darkirc/replayed_darkirc_db")]
     /// Replay logs (DB) path
     replay_datastore: String,
 
@@ -489,11 +501,19 @@ pub const DARKIRC_GENESIS_COMMITMENTS_REPR: &[[u8; 32]] = &[
     };
     let replay_mode = args.replay_mode;
 
-    validate_history_window(args.dags_count, args.history_retention_dags)?;
-    info!(
-        "Retaining {} DAG(s) of local history; syncing {} DAG(s) at startup",
-        args.history_retention_dags, args.dags_count,
-    );
+    let max_dags =
+        history_retention_limit(args.dags_count, args.history_retention_dags, args.archive_mode)?;
+    if let Some(retention) = max_dags {
+        info!(
+            "Retaining {retention} DAG(s) of local history; syncing {} DAG(s) at startup",
+            args.dags_count,
+        );
+    } else {
+        info!(
+            "Archive mode enabled; retaining all local DAGs and syncing {} recent DAG(s) at startup",
+            args.dags_count,
+        );
+    }
 
     let sled_cache_capacity = sled_cache_capacity_bytes("sled_cache_mb", args.sled_cache_mb)?;
     info!("Instantiating event DAG with {} MiB sled cache", args.sled_cache_mb);
@@ -551,7 +571,7 @@ pub const DARKIRC_GENESIS_COMMITMENTS_REPR: &[[u8; 32]] = &[
         } else {
             Vec::new()
         },
-        max_dags: Some(args.history_retention_dags),
+        max_dags,
     };
     let event_graph = match if let Some(zk_key_db) = zk_key_db.clone() {
         EventGraph::new_with_zk_key_db(
@@ -979,7 +999,7 @@ mod tests {
     use darkfi::event_graph::rln::MAX_MSG_LIMIT;
     use rand::rngs::OsRng;
 
-    use super::{sled_cache_capacity_bytes, validate_history_window, RlnIdentity, BYTES_PER_MIB};
+    use super::{history_retention_limit, sled_cache_capacity_bytes, RlnIdentity, BYTES_PER_MIB};
 
     #[test]
     fn generated_rln_identity_limit_matches_genesis_budget() {
@@ -1002,21 +1022,26 @@ mod tests {
 
     #[test]
     fn history_window_rejects_zero_startup_sync() {
-        assert!(validate_history_window(0, 24).is_err());
+        assert!(history_retention_limit(0, 24, false).is_err());
     }
 
     #[test]
     fn history_window_rejects_zero_retention() {
-        assert!(validate_history_window(1, 0).is_err());
+        assert!(history_retention_limit(1, 0, false).is_err());
     }
 
     #[test]
     fn history_window_rejects_sync_beyond_retention() {
-        assert!(validate_history_window(25, 24).is_err());
+        assert!(history_retention_limit(25, 24, false).is_err());
     }
 
     #[test]
     fn history_window_allows_sync_inside_retention() {
-        assert!(validate_history_window(48, 168).is_ok());
+        assert_eq!(history_retention_limit(48, 168, false).unwrap(), Some(168));
+    }
+
+    #[test]
+    fn archive_mode_disables_retention_limit() {
+        assert_eq!(history_retention_limit(24, 0, true).unwrap(), None);
     }
 }

+ 1 - 1
doc/build_jsonrpc.py

@@ -110,7 +110,7 @@ def main(path):
     for m in methods:
         anchor = m["method"].replace(".", "").replace("/", "").lower()
         ghlink = f"https://codeberg.org/darkrenaissance/darkfi/src/branch/master/{path.replace('../', '')}#L{m['line']}"
-        print(f"### `{m['method']}` <sup><a href=\"{ghlink}\">[source]</a></sup> {{#{anchor}}}\n")
+        print(f"### `{m['method']}` [source]({ghlink}) {{#{anchor}}}\n")
 
         if m["comment"]:
             print(f"{m['comment']}\n")

+ 2 - 2
doc/generate_seminar_ics.py

@@ -4,7 +4,7 @@ import hashlib
 from datetime import datetime
 from sys import argv
 
-from prettytable import PrettyTable, HEADER
+from prettytable import HRuleStyle, PrettyTable
 
 EVENTS = [
     {
@@ -62,7 +62,7 @@ def print_table():
     x = PrettyTable()
     x.field_names = ["Date", "Track", "Topic", "#", "Title", "Rec"]
     x.align = "l"
-    x.hrules = HEADER
+    x.hrules = HRuleStyle.HEADER
     x.junction_char = "|"
 
     for event in EVENTS:

+ 3 - 1
doc/src/SUMMARY.md

@@ -20,6 +20,8 @@
 - [Contracts](testnet/contract.md)
 - [Merge Mining](testnet/merge-mining.md)
 - [DarkIRC](misc/darkirc/darkirc.md)
+  - [Run a Normal Node](misc/darkirc/normal-node.md)
+  - [Run an Archive Node](misc/darkirc/archive-node.md)
   - [Private Message](misc/darkirc/private_message.md)
 - [Node Configurations](misc/nodes/node-configurations.md)
   - [Public Node Configurations](misc/nodes/public-guide.md)
@@ -144,7 +146,7 @@
   - [Network Protocol](misc/event_graph/network_protocol.md)
   - [Security Invariants](misc/event_graph/security_invariants.md)
   - [Recovery](misc/event_graph/recovery.md)
-- [dnetview](misc/dnetview.md)
+- [dnet](misc/dnetview.md)
 - [Zero2darkfi](zero2darkfi/zero2darkfi.md)
   - [darkmap](zero2darkfi/darkmap.md)
 - [FAQ](misc/faq.md)

+ 12 - 104
doc/src/arch/arch.md

@@ -36,104 +36,14 @@ gantt
     Dcon5            :      d5, after d4,   7d
 ```
 
-<table>
-    <tr>
-        <th>Phase</th>
-        <th>Description</th>
-        <th>Duration</th>
-        <th>Details</th>
-        <th>Version</th>
-    </tr>
-    <tr>
-        <td>Dcon0</td>
-        <td>Research</td>
-        <td></td>
-        <td>
-Research new techniques, draft up architecture design documents and
-modify the specs.
-
-During this phase the team looks into new experimental techniques and
-begins to envision how the product will evolve during the next phase of
-the cycle.
-        </td>
-        <td>pre-alpha</td>
-    </tr>
-    <tr>
-        <td>Dcon1</td>
-        <td>New features and changes</td>
-        <td></td>
-        <td>
-Add big features and merge branches. Risky changes that are likely to
-cause bugs or additional work must be done before the end of this phase.
-
-The first 10 weeks overlap with the Dcon3  & Dcon4 phases of the
-previous release, and many developers will focus on bug fixing in those
-first weeks.
-
-Developers dedicate a steady 1-2 days/week to the bug tracker, focusing
-on triaging and newly introduced bugs.
-        </td>
-        <td>alpha</td>
-    </tr>
-    <tr>
-        <td>Dcon2</td>
-        <td>Improve and stabilize</td>
-        <td></td>
-        <td>
-Work to improve, optimize and fix bugs in new and existing features. 
-Only smaller and less risky changes, including small features, should
-be made in this phase.
-
-If a new feature is too unstable or incomplete, it will be reverted
-before the end of this phase. Developers spend 2-3 days/week in the
-bug tracker, triaging, fixing recently introduced or prioritized module
-bugs.
-        </td>
-        <td>alpha</td>
-    </tr>
-    <tr>
-        <td>Dcon3</td>
-        <td>Bug fixing only</td>
-        <td>2 months</td>
-        <td>
-Focus on bug fixing and getting the release ready.
-
-Development moves to the stable stabilizing branch. In master Dcon1
-for the next release starts. stable is regularly merged into master.
-
-High priority bugs dictate how much time developers will spend in the 
-tracker as oppose to work on the next release Dcon1 features.
-        </td>
-        <td>beta</td>
-    </tr>
-    <tr>
-        <td>Dcon4</td>
-        <td>Prepare release</td>
-        <td>2 weeks</td>
-        <td>
-Stable branch is frozen to prepare for the release. Only critical and
-carefully reviewed bug fixes allowed.
-
-Release candidate and release builds are made.
-Developers spend a short time 5 days/week with an eye in the tracker
-for any unexpected high priority regression.
-        </td>
-        <td>release candidate</td>
-    </tr>
-    <tr>
-        <td>Dcon5</td>
-        <td>Release</td>
-        <td>1 week</td>
-        <td>
-Stage where the final builds are packaged for all platforms, last
-tweaks to the logs, memes, social media, video announcements.
-
-The final switch is flicked on [dark.fi](https://dark.fi/) for the new
-release to show up on the Download page.
-        </td>
-        <td>release</td>
-    </tr>
-</table>
+| Phase | Description | Duration | Details | Version |
+| --- | --- | --- | --- | --- |
+| Dcon0 | Research | — | Research new techniques, draft architecture documents, and modify the specs. The team investigates experimental techniques and plans how the product will evolve during the next phase. | pre-alpha |
+| Dcon1 | New features and changes | — | Add major features and merge branches. Risky changes must land before this phase ends. The first ten weeks overlap with Dcon3 and Dcon4 of the previous release, so developers also triage and fix newly introduced bugs. | alpha |
+| Dcon2 | Improve and stabilize | — | Improve, optimize, and fix new and existing features. Only smaller, lower-risk changes should land. Unstable or incomplete features are reverted before the phase ends, while developers prioritize module bugs. | alpha |
+| Dcon3 | Bug fixing only | 2 months | Focus on making the release ready. Development moves to the stable branch while Dcon1 for the next release starts on master. Stable is regularly merged into master, and high-priority bugs take precedence over new features. | beta |
+| Dcon4 | Prepare release | 2 weeks | Freeze the stable branch except for carefully reviewed critical fixes. Produce release-candidate and release builds and watch for high-priority regressions. | release candidate |
+| Dcon5 | Release | 1 week | Package final builds for all platforms, finish release communications, and publish the new release on [dark.fi](https://dark.fi/). | release |
 
 ## Mainnet Roadmap
 
@@ -219,12 +129,10 @@ to deal with the entire wallet itself.
 
 ### `darkirc`
 
-Write documentation about usage. We need proper tutorials about
-running on mobile. Both weechat-android setups, and local ones.
-
-A simple Android app can be made drawing inspiration from Orbot
-where there is a big On/Off button that can start/stop the node on
-the phone and then the user can use any IRC client they prefer.
+DarkIRC is a P2P chat daemon backed by Event Graph synchronization. It exposes a
+local IRC interface, rotates message DAGs hourly, and supports bounded normal
+nodes and unpruned archive nodes. See the [DarkIRC user guide](../misc/darkirc/darkirc.md)
+and [protocol reference](../misc/darkirc/specification.md).
 
 ### `tau`
 

+ 3 - 3
doc/src/arch/net/p2p-network.md

@@ -210,9 +210,9 @@ Swarming means more efficient downloading of data specific to a certain
 subset. A new p2p instance is spawned with a clean hosts table. This
 subnetwork is self contained.
 
-An application is for example DarkIRC where everyday a new event graph
-is spawned. With swarming, you would connect to nodes maintaining this
-particular day's event graph.
+An application is for example DarkIRC, where the current implementation rotates
+to a new event DAG every hour. With swarming, a node could connect to peers
+maintaining a particular history slot.
 
 The feature allows overlaying multiple different features in a single
 network such as tau, darkirc and so on. New networks require nodes to

+ 5 - 5
doc/src/dev/dev.md

@@ -67,14 +67,14 @@ This allows us to coordinate more effectively.
 | gm      | good morning       | Reporting in                                                                                          |
 | gn      | good night         | Logging off for the day                                                                               |
 | +++     | thumbs up          | Understood, makes sense                                                                               |
-| afk*    | away from keyboard | Shutting down the computer so you will lose messages sent to you                                      |
-| b*      | back               | Returning back after leaving                                                                          |
+| afk     | away from keyboard | Shutting down the computer or stepping away.                                                           |
+| b       | back               | Returning after leaving.                                                                               |
 | brb     | be right back      | If you are in a meeting and need to leave for a few mins. For example, maybe you need to grab a book. |
 | one sec | one second         | You need to search something on the web, or you are just doing the task (example: opening the file).  |
 
-\* once we have proper syncing implemented in darkirc, these will become less relevant and not needed.
-
-Another option is to run your darkirc inside a persistent tmux session, and never miss messages.
+DarkIRC now syncs a rolling 24-hour history by default. A continuously running
+[archive node](../misc/darkirc/archive-node.md) retains future history without
+the normal pruning limit.
 
 ## Code coverage
 

+ 8 - 7
doc/src/learn/dchat/network-tools/using-dnet.md

@@ -17,8 +17,7 @@ engine that makes everything run.
 
 ### Using a venv
 
-Dnet requires Python 3.12.0. Make sure Python is installed and on the
-latest version.
+Dnet currently requires Python 3.14. Run these commands from `bin/dnet`.
 
 Depending on your setup you may need to install a virtual environment
 for Python. Do so as follows:
@@ -37,7 +36,7 @@ Then install the requirements:
 Run dnet:
 
 ```shell
-% python main.py
+% python dnet
 ```
 
 You will need to reactivate the venv in your current terminal session
@@ -53,19 +52,21 @@ If you don't require a venv, install the requirements and run dnet as follows:
 
 ```shell
 % pip install -r requirements.txt
-% python main.py
+% python dnet
 ```
 
 ## Config
 
-On first run, `dnet` will create a config file in the config directory
-specific to your operating system.
+On first run, `dnet` creates `dnet_config.toml` in DarkFi's platform-specific
+configuration directory (`~/.config/darkfi/` on Linux).
 
 To use `dnet` you will need to open the config file and modify it. Enter
 the RPC ports of the nodes you want to connect to and title them as you
 see fit. The default config file uses localhost, but you can replace
 this with hostnames or external IP addresses. You must also specify
-whether it is a `NORMAL` or a `LILITH` node.
+whether it is a `NORMAL` or a `LILITH` node. DarkIRC's default RPC port is
+9605. Remove `"p2p.get_info"` from DarkIRC's `rpc_disabled_methods` and restart
+the daemon before connecting dnet.
 
 ## Usage
 

+ 80 - 0
doc/src/misc/darkirc/archive-node.md

@@ -0,0 +1,80 @@
+# Run an Archive DarkIRC Node
+
+An archive node keeps every rotating DAG in its datastore instead of deleting
+old history. Run it continuously if you want to preserve and serve DarkIRC
+message history. Disk use and startup work grow over time, so use persistent
+storage, monitor its free space, and back it up.
+
+## Build and create a dedicated configuration
+
+From the repository root, build and run DarkIRC once:
+
+```shell
+% make darkirc
+% ./darkirc
+```
+
+The first run creates `~/.config/darkfi/darkirc_config.toml` and continues
+starting the node. Stop it with `Ctrl-C` before editing the file.
+
+An archive should use a dedicated DarkIRC datastore. Do not point two running
+processes at the same datastore:
+
+```toml
+# Keep message bodies; header-only mode is not suitable for a complete archive.
+fast_mode = false
+
+# Sync the latest full day when starting or reconnecting.
+dags_count = 24
+
+# Never prune rotating DAGs. history_retention_dags is ignored in this mode.
+archive_mode = true
+
+datastore = "~/.local/share/darkfi/darkirc/archive/darkirc_db"
+
+[net]
+p2p_datastore = "~/.local/share/darkfi/darkirc/archive/p2p"
+hostlist = "~/.local/share/darkfi/darkirc/archive/p2p_hostlist.tsv"
+```
+
+Keep the remaining `[net]` and `[net.profiles.*]` tables from the generated
+configuration. Configure at least one working outbound profile so the node can
+sync. A reachable inbound address and matching `external_addrs` entry let more
+peers discover the archive and request its history; follow the
+[public node guide](../nodes/public-guide.md) for that transport setup.
+
+## Understand archive bootstrap
+
+`archive_mode` prevents future pruning and reloads all DAG trees already in the
+datastore. It does not recreate history that was pruned before archive mode was
+enabled. A fresh archive initially syncs the `dags_count` recent DAGs—24 hours
+with the configuration above—and then retains every new hourly DAG.
+
+To operate an archive containing history from before it was started, bootstrap
+it from a known-good archive datastore while both nodes are stopped. Copy the
+entire DarkIRC datastore, not individual sled trees. Be aware that this
+datastore can also contain local NickServ account secrets; a purpose-built
+archive should not be used for personal IRC accounts. See the
+[operations and recovery notes](darkirc.md#operations-and-recovery) before copying or
+restoring it.
+
+## Start and verify
+
+Start the archive with its configuration:
+
+```shell
+% ./darkirc --config ~/.config/darkfi/darkirc_config.toml
+```
+
+At startup, the logs should state that archive mode is enabled and then report
+the recent full sync:
+
+```text
+Archive mode enabled; retaining all local DAGs and syncing 24 recent DAG(s) at startup
+Event DAG synced successfully (full mode, 24 dag(s))
+```
+
+Keep the process running across hourly rotations. Stop it with `Ctrl-C` for a
+clean datastore flush before backups, upgrades, or datastore transfers.
+`archive_mode`, sync, datastore, and network changes require a restart; they
+cannot be applied with IRC `/rehash`.

+ 151 - 356
doc/src/misc/darkirc/darkirc.md

@@ -1,432 +1,227 @@
-# DarkIRC: Strong Anonymity P2P Chat
+# DarkIRC
 
-In DarkFi, we organize our communication using resilient and
-censorship-resistant infrastructure. For chatting, `darkirc` is a
-peer-to-peer implementation of an IRC server in which any user can
-participate anonymously using any IRC frontend and by running the
-IRC daemon. `darkirc` uses the DarkFi P2P engine to synchronize chats
-between hosts.
+DarkIRC is DarkFi's peer-to-peer chat daemon. It exposes a local IRC server so
+standard IRC clients can use it, while DarkFi's P2P network and Event Graph
+synchronize messages between DarkIRC nodes.
 
-## Benefits
+DarkIRC implements a practical subset of IRC rather than the complete IRC
+protocol. Public channel messages are public. A configured channel secret or
+contact keypair encrypts the corresponding message fields with
+`crypto_box::ChaChaBox`, but DarkIRC does not implement the Signal protocol.
 
-* Encrypted using same algorithms as Signal.
-* There are no identities. You cannot see who is in the chat.
-* Completely anonymous. You can rename yourself easily by using the
-  command `/nick foo`. This means all messages are unlinkable.
-* God-fearing based CLI without soy gui shit.
-* p2p decentralized.
-* Optionally run it over Tor or Nym (soon) for network level anonymity.
+Nicknames are included in message events and may be changed at any time. They
+are not proof of a real-world identity. Optional NickServ/RLN identities are a
+separate feature and RLN is disabled by default.
 
-Therefore this is the world's most strongly anonymous chat in existence.
-Nothing else exists like it.
+## Build and install
 
-<u><b>Note</b></u>: `darkirc` follows IRC's [RFC2812](https://www.rfc-editor.org/rfc/rfc2812)
-
-## Building
-
-Follow the instructions in the [README](../../index.html#build) to ensure
-you have all the necessary dependencies. After that, in repo root folder:
+Follow the repository [build prerequisites](../../README.md#build), then build
+DarkIRC from the repository root:
 
 ```shell
-% git checkout a05956d412a091e8b54c1cd4f4264c33b941203d
 % make darkirc
 ```
 
-## Installation (Optional)
-
-It is adviced to use `darkirc` directly from the repo root folder.
-Install system wide only if you can make sure there would be no
-multiple darkirc versions installed:
-
-```shell
-% sudo make install darkirc
-```
-
-You have to reinstall `darkirc` on new versions manually.
-
-## Building for Android
-
-This is for Android 64 bit (which is most phones).
-You will compile darkirc on your computer then copy it to your phone
-and run it in Termux (a command-line terminal for Android).
-
-We will use podman which is a secure replacement for docker. However if you
-prefer to use docker just be aware of
-[the security risks](https://docs.docker.com/engine/security/#docker-daemon-attack-surface).
-Podman is a drop in replacement.
-
-1. Setup podman on your computer which may look like:
-    1. Install podman package
-    2. Run the podman daemon service under your local user
-        1. Use the command `podman system service`.
-        2. For Docker it's more complicated, see [rootless mode](https://docs.docker.com/engine/security/rootless/).
-2. Run `cd bin/darkirc/ && make podman-android`. The resulting file 
-    will be called `darkirc.aarch64-android` (it might be needed to 
-    make the file executable `chmod +x darkirc.aarch64-android`). 
-    Copy this to your phone.
-3. Install Termux and RevolutionIRC on F-Droid.
-4. Run `termux-setup-storage` and allow access to external storage.
-   Now you can access the phone storage from `/sdcard/` and copy the file
-   into the Termux home.
-5. Run `termux-wake-lock`. This stops Android suspending the daemon.
-6. Run the daemon. You can open new Termux sessions by swiping from
-   the left to bring up the sidebar.
-7. Connect the RevolutionIRC frontend by adding a new server:
-    1. Write a name for the server (i.g `darkirc`).
-    2. Set the server address and port (if using default config these 
-        should be 127.0.0.1:6667).
-    3. Untick `Use SSL/TLS` option.
-    4. Save and connect.
-
-## Logs
-
-The public channels have [logs available](https://agorism.dev/log/), and
-additionally there is a mirror on telegram @darkfi_darkirc channel.
-You can also message @darkirc_bot with "sub" to avoid doxxing your username.
-Use "unsub" to unsubscribe.
-
-## Network-level privacy
-
-Nodes have knowledge of their peers, including the IP addresses of
-connected hosts. We suggest configuring your instance to use a different
-transport so it is not connected via clearnet.
-
-DarkFi supports the use of pluggable transports, including [Tor](../nodes/tor-guide.md#configure-network-settings)
-and Nym, to provide network-level privacy. As long as there are live seed
-nodes configured to support a Tor or Nym connection, users can connect to
-`darkirc` and benefit from the protections offered by these protocols.
-
-Other approaches include connecting via a cloud server or VPN. Research
-the risks involved in these methods before connecting.
-
-## Usage (DarkFi Network)
-
-Upon compiling `darkirc` as described above, the preconfigured defaults
-will allow you to connect to the network and start chatting with the
-rest of the DarkFi community.
-
-First, try to start `darkirc` from your command-line so it can spawn its
-configuration file in place. The preconfigured defaults will autojoin
-you to several default channels one of which is `#dev` where we have 
-weekly meetings, and where the community is most active and talks 
-about DarkFi development.
+The binary is written to `./darkirc`. To install it under
+`$HOME/.cargo/bin`, use the DarkIRC makefile:
 
 ```shell
-% ./darkirc
+% make -C bin/darkirc install
 ```
 
-`darkirc` will create a configuration file `darkirc_config.toml` by 
-default in `~/.config/darkfi/` you can review and potentially edit. It 
-might be useful if you want to add other channels you want to autojoin 
-(like `#philosophy` and `#memes`), or if you want to set a shared 
-secret for some channel in order for it to be encrypted between its 
-participants. We strongly suggest to make sure you are using the
-desired network transport before proceeding.
+Run that command again after building a newer version.
 
-When done, you can run `darkirc` for the second time in order for it to
-connect to the network and start participating in the P2P protocol:
+### Android
 
-```shell
-% ./darkirc
-```
+The Android container target builds a 64-bit ARM binary suitable for running
+inside Termux:
 
-The daemon will start conncting to peers and sync its database, you'll 
-know it's finished syncing when you see this log message:
 ```shell
-% [EVENTGRAPH] DAG synced successfully!
+% make -C bin/darkirc podman-android
 ```
 
-Now connect your favorite IRC client and it should replay missed 
-messages that have been sent by people.
-
+The result is `bin/darkirc/darkirc.aarch64-android`. Copy it to the phone,
+make it executable, and run it in Termux. Keep the process awake if Android
+would otherwise suspend it, then connect an Android IRC client to
+`127.0.0.1:6667` without IRC TLS. This local IRC connection is distinct from
+the encrypted P2P transports used between DarkIRC nodes.
 
-## Clients
+## Network privacy and message privacy
 
-### Weechat
+These are separate concerns:
 
-In this section, we'll briefly cover how to use the [Weechat IRC
-client](https://github.com/weechat/weechat) to connect and chat with
-`darkirc`.
+- Direct P2P peers can observe the network address used for a connection.
+- The generated configuration enables the built-in `tor` profile by default.
+  See the [Tor guide](../nodes/tor-guide.md) before changing transports.
+- Nym can be used as an outbound SOCKS5 transport; see the
+  [Nym guide](../nodes/nym-guide.md).
+- Public channels store their channel name, nickname, and message as
+  plaintext Event Graph content.
+- A channel is encrypted only when every participant configures the same
+  channel `secret`. Contact DMs require the keypairs described in the
+  [private-message guide](private_message.md).
 
-Normally, you should be able to install weechat using your
-distribution's package manager. If not, have a look at the weechat
-[git repository](https://github.com/weechat/weechat) for instructions
-on how to install it on your computer.
+Transport anonymity depends on the selected network and its threat model. It
+does not make messages unlinkable by itself, and encrypted content can still
+expose metadata through timing, participation, or the IRC client.
 
-Once installed, we can configure a new server which will represent our
-`darkirc` instance. First, start weechat, and in its window - run the
-following commands (there is an assumption that `irc_listen` in the
-`darkirc` config file is set to `127.0.0.1:6667`):
+## Choose a node mode
 
-```
-/server add darkfi localhost/6667 -notls -autoconnect
-/save
-/quit
-```
+DarkIRC rotates its Event Graph once per hour. The default normal node syncs
+and retains 24 DAGs, a rolling full-day history window.
 
-This will set up the server, save the settings, and exit weechat.
-You are now ready to begin using the chat. Simply start weechat
-and everything should work.
+- [Run a normal node](normal-node.md) for chatting and ordinary P2P
+  participation. Old rotating DAGs are pruned.
+- [Run an archive node](archive-node.md) to retain every rotating DAG received
+  after the archive starts and serve that history to peers.
 
-When you join, you should see users nicknames on the right panel.
-those nicknames are users who previously sent messages and you got 
-those messages as history when you synced.
-Normally nicks would not be shown since there is no concept of 
-nicknames or registration on this p2p anonymous chat.
+`dags_count` controls startup and reconnect sync depth. `archive_mode` controls
+retention. When archive mode is selected, `history_retention_dags` is ignored,
+but `dags_count` still determines how many recent DAGs are requested during a
+sync. The node-mode guides explain the bootstrap implications.
 
-You can change your nickname using `/nick foo`, and navigate channels
-using F5/F6 or ALT+X where X is the channel number displayed.
-You can also use ALT+up/down.
+## First run
 
-Whenever you edit `darkirc_config.toml` file and if you have your 
-`darkirc` daemon running you don't need to restart it to reload the 
-config, you just need to send a `rehash` command from IRC client for 
-the changes to reflect, like so:
+Start DarkIRC from the repository root:
 
-```
-/rehash
+```shell
+% ./darkirc
 ```
 
-## Usage (Local Deployment)
+On its first run it creates
+`~/.config/darkfi/darkirc_config.toml` and continues starting. On macOS the
+configuration directory is `~/Library/Application Support/darkfi/`.
 
-These steps below are only for developers who wish to make a testing
-deployment. The previous sections are sufficient to join the chat.
+The default runtime state is namespaced under
+`~/.local/share/darkfi/darkirc/`:
 
-### Seed Node
+| Setting | Default |
+| --- | --- |
+| `datastore` | `~/.local/share/darkfi/darkirc/darkirc_db` |
+| `zk_key_datastore` | `~/.local/share/darkfi/darkirc/zk_keys` |
+| `replay_datastore` | `~/.local/share/darkfi/darkirc/replayed_darkirc_db` |
+| `net.p2p_datastore` | `~/.local/share/darkfi/darkirc/p2p` |
+| `net.hostlist` | `~/.local/share/darkfi/darkirc/p2p_hostlist.tsv` |
 
-First you must run a seed node. The seed node is a static host which
-nodes can connect to when they first connect to the network. The
-`seed_session` simply connects to a seed node and runs `protocol_seed`,
-which requests a list of addresses from the seed node and disconnects
-straight after receiving them.
+Review the generated `[net]` settings before connecting. Stop the daemon with
+`Ctrl-C`, edit the file, and restart it when changing node mode, storage, RPC,
+or P2P settings. With the defaults, startup sync completes with:
 
-The first time you run the program, a config file will be created in
-`~/.config/darkfi` if you are using Linux or in 
-`~/Library/Application Support/darkfi/` on MacOS. 
-You must specify an inbound accept address in your config file to configure a seed node:
-
-```toml
-## P2P accept addresses
-inbound=["127.0.0.1:9600"]
+```text
+Event DAG synced successfully (full mode, 24 dag(s))
 ```
 
-Note that the above config doesn't specify an external address since
-the seed node shouldn't be advertised in the list of connectable
-nodes. The seed node does not participate as a normal node in the
-p2p network. It simply allows new nodes to discover other nodes in
-the network during the bootstrapping phase.
-
-### Inbound Node
-
-This is a node accepting inbound connections on the network but which
-is not making any outbound connections.
-
-The external addresses are important and must be correct.
+## Connect an IRC client
 
-To run an inbound node, your config file must contain the following
-info:
-		
-```toml
-## P2P accept addresses
-inbound=["127.0.0.1:9600"]
-
-## P2P external addresses
-external_addr=["127.0.0.1:9600"]
+DarkIRC listens on `tcp://127.0.0.1:6667` by default. For WeeChat, add the
+local server after starting the client:
 
-## Seed nodes to connect to 
-seeds=["127.0.0.1:9601"]
+```text
+/server add darkfi localhost/6667 -notls -autoconnect
+/save
+/connect darkfi
 ```
-### Outbound Node
-
-This is a node which has 8 outbound connection slots and no inbound
-connections.  This means the node has 8 slots which will actively
-search for unique nodes to connect to in the p2p network.
 
-In your config file:
+The displayed nick list is reconstructed from messages the local node has
+seen; it is not a live global presence list. Change nickname with `/nick foo`.
 
-```toml
-## Connection slots
-outbound_connections=8
+After editing only `autojoin`, `[channel.*]`, or `[contact.*]`, reload those
+settings without restarting:
 
-## Seed nodes to connect to 
-seeds=["127.0.0.1:9601"]
+```text
+/rehash
 ```
 
-### Attaching the IRC Frontend
-
-Assuming you have run the above 3 commands to create a small model
-testnet, and both inbound and outbound nodes above are connected,
-you can test them out using weechat.
-
-To create separate weechat instances, use the `--dir` command:
-
-    weechat --dir /tmp/a/
-    weechat --dir /tmp/b/
-
-Then in both clients, you must set the option to connect to temporary
-servers:
-
-    /set irc.look.temporary_servers on
-
-Finally you can attach to the local darkirc instances:
-
-    /connect localhost/6667
-    /connect localhost/6668
-
-And send messages to yourself.
+All other configuration changes require a daemon restart.
 
-### Running a Fullnode
+## Encrypted channels
 
-See the script `script/run_node.sh` for an example of how to deploy
-a full node which does seed session synchronization, and accepts both
-inbound and outbound connections.
+Generate a shared channel secret:
 
-## Global Buffer
-
-Copy [this script](https://github.com/narodnik/weechat-global-buffer/blob/main/buffclone.py) 
-to `~/.local/share/weechat/python/autoload/`, and you will create a single buffer 
-which aggregates messages from all channels. It's useful to monitor 
-activity from all channels without needing to flick through them.
-
-You may need to install `weechat-python` to enable Python scripting support
-in your weechat.
-
-## Emojis
-
-Install the `noto` fonts to have the full unicode set. Popular Linux distros
-should have packages for them.
-
-Once installed you can view all the emojis in your terminal. Note, you may need
-to regenerate your font cache (or just restart) after installing them.
-
-## Further Customization
-
-Group channels under respective networks:
-
-```
-/set irc.look.server_buffer independent
-/set irc.look.new_channel_position near_server
+```shell
+% ./darkirc --gen-channel-secret
 ```
 
-Filter all join-part-quit messages (only relevant for other networks):
+Configure the exact same secret on every participant's node:
 
+```toml
+[channel."#project"]
+secret = "BASE58_CHANNEL_SECRET"
+topic = "Private project channel"
 ```
-/set irc.look.smart_filter on
-/filter add joinquit * irc_join,irc_part,irc_quit,irc_nick,irc_account,irc_chghost *
-```
-
-For customizing the colors, see
-[this article](https://blog.swwomm.com/2020/07/weechat-light-theme.html).
-
-### Settings Editor
-
-Make sure you run `/save`, `/quit` to reload your config after these changes.
 
-To see the Weechat settings editor, simply type `/set` in the main buffer.
-You can then type prefixes like "autojoin" and press enter to find all settings
-related to that. To change it type ALT+enter. Everything in Weechat is
-customizable!
+Exchange this secret over an authenticated, confidential channel. Anyone with
+the secret can read all encrypted messages for that channel that they obtain;
+there is no per-member key rotation or forward secrecy.
 
-The help is your friend. Every command has help.
+## Local two-node deployment
 
-```
-/help key
-/help server
-```
+For development, use two independent configurations and datastores. The first
+node listens on `127.0.0.1:9700`:
 
-For example to set the shortcut ALT-w to close a buffer,
-use `/key bind meta-w /close`.
+```toml
+irc_listen = "tcp://127.0.0.1:6667"
+datastore = "~/.local/share/darkfi/darkirc/localnet/a/darkirc_db"
 
-### Other IRC Networks
+[rpc]
+rpc_listen = "tcp://127.0.0.1:9705"
 
-For more fun, you can join Libera IRC. Note this may potentially dox your node,
-especially if you have autoconnect enabled since Libera is not anon.
+[net]
+localnet = true
+active_profiles = ["tcp"]
+p2p_datastore = "~/.local/share/darkfi/darkirc/localnet/a/p2p"
+hostlist = "~/.local/share/darkfi/darkirc/localnet/a/p2p_hostlist.tsv"
 
+[net.profiles."tcp"]
+inbound = ["tcp://127.0.0.1:9700"]
 ```
-/server add libera irc.libera.chat/6697 -ssl -autoconnect
-/save
-/connect libera
-/join #rust
-/join #linux
-/join #math
-```
-
-You can find more channels with `/list`. Then add your favorite channels to the
-libera autojoin list.
-
-Note that your nick is temporary. If you want to claim a nick, you will need to
-[register with the NickServer](https://libera.chat/guides/registration).
-
-## Troubleshooting
-
-If you encounter connectivity issues refer to
-[Network troubleshooting](../network-troubleshooting.md)
-for further troubleshooting resources.
-
-## Operator Security Notes
-
-DarkIRC's datastore contains both public event graph state and local account
-secrets. Back up the whole `--datastore` directory before recovery work, and
-export important local accounts with `NickServ INFO <account_name>` before
-discarding a store. A clean event graph resync can recover public DAG state from
-peers, but it cannot recover nullifiers and trapdoors that were only stored
-locally.
-
-Do not edit RLN counters or static DAG state by hand. DarkIRC persists the next
-RLN `message_id` before proof creation so a crash cannot roll the counter back
-and self-slash the account on restart. The static DAG is also the source of
-truth for network registrations and slashes. For the full set of invariants, see
-[Event Graph Security Invariants](../event_graph/security_invariants.md).
-
-## Hosting Instances
-
-The main thing is that all participating nodes must have a separate clean config
-to the DarkIRC instance that doesn't include seeds or nodes from other networks.
-Otherwise the P2P networks will bleed into each other. You want them to remain
-distinct.
 
-You have two options:
+The second node connects to it manually:
 
-1. The simplest is just setting up a persistent node, and adding a manual
-   connection. This is good when your network is small and you might not have
-   enough nodes for the P2P network to sustain itself.
-2. The proper way is to setup seed nodes that are first queried on startup,
-   and then used to bootstrap the P2P network.
+```toml
+irc_listen = "tcp://127.0.0.1:6668"
+datastore = "~/.local/share/darkfi/darkirc/localnet/b/darkirc_db"
 
-Additionally if you want to separate multiple transports like Tor, you will need
-nodes that bridge between the networks. Or you can just run everything
-completely over Tor.
+[rpc]
+rpc_listen = "tcp://127.0.0.1:9706"
 
-First make sure the storages are separate in `mynet_config.toml`:
+[net]
+localnet = true
+active_profiles = ["tcp"]
+p2p_datastore = "~/.local/share/darkfi/darkirc/localnet/b/p2p"
+hostlist = "~/.local/share/darkfi/darkirc/localnet/b/p2p_hostlist.tsv"
 
+[net.profiles."tcp"]
+peers = ["tcp://127.0.0.1:9700"]
 ```
-datastore = "~/.local/darkfi/mynet_db"
 
-[net]
-hostlist = "~/.local/darkfi/mynet/hostlist.tsv"
-```
+Start both with `./darkirc --config PATH`. Connect separate IRC clients to
+ports 6667 and 6668. `localnet = true` is required because loopback P2P
+addresses are rejected in ordinary network mode.
 
-To setup a manual connection:
+## Custom networks
 
-```
-peers = ["tcp+tls://mynet-manual.peer:16754"]
-```
+Keep a custom DarkIRC network isolated from the public network:
 
-Also make sure, the seeds are set to either be blank, or use your custom seed
-node:
+- give every instance distinct DarkIRC, P2P, hostlist, and RPC paths;
+- use only custom `peers` and `seeds` under the appropriate
+  `[net.profiles."..."]` table;
+- use the same non-public `net.magic_bytes` on every custom node; and
+- do not reuse a datastore created with different Event Graph consensus
+  parameters.
 
-```
-seeds = ["tcp+tls://mynet-seed.peer:5645"]
-```
+Changing `magic_bytes` separates P2P handshakes; it does not change DarkIRC's
+compiled Event Graph genesis or RLN commitment set.
 
-For hosting the seed node, you can either use the generic seed node called
-'lilith' which is generic, or you can simply just run a normal DarkIRC node
-which has the inbound correctly set.
+## Operations and recovery
 
-To make your network distinct, an extra measure is to modify the magic bytes
-used in messages. This means any nodes that do drift into your custom instance
-will be unable to connect anyway.
+The DarkIRC datastore contains public Event Graph state and can also contain
+local NickServ account secrets. Stop the process and back up the complete
+`datastore` directory before recovery or migration. A clean resync can recover
+public DAG state from suitable peers, but it cannot recover local nullifiers
+and trapdoors that were never backed up or exported.
 
-```
-magic_bytes=[127, 64, 12, 201]
-```
+Do not hand-edit RLN counters, static DAG state, or individual sled trees. See
+[Event Graph recovery](../event_graph/recovery.md) and
+[security invariants](../event_graph/security_invariants.md). For connection
+problems, see [network troubleshooting](../network-troubleshooting.md).

+ 81 - 0
doc/src/misc/darkirc/normal-node.md

@@ -0,0 +1,81 @@
+# Run a Normal DarkIRC Node
+
+A normal node is the recommended setup for chatting. It performs a full sync
+of the latest 24 hourly DAGs and keeps a rolling 24-DAG window. When a new DAG
+is created, the oldest DAG and its message data are deleted from the local
+datastore.
+
+## Build and create the configuration
+
+From the repository root, build DarkIRC:
+
+```shell
+% make darkirc
+```
+
+Run it once to create `~/.config/darkfi/darkirc_config.toml`:
+
+```shell
+% ./darkirc
+```
+
+The node continues running after creating the file. Stop it with `Ctrl-C` if
+you want to review the configuration before connecting. On macOS, the default
+configuration directory is `~/Library/Application Support/darkfi/`.
+
+## Configure the history window
+
+The generated configuration already uses these defaults. Uncomment the
+settings if you want them recorded explicitly:
+
+```toml
+# Fetch complete message data rather than headers only.
+fast_mode = false
+
+# Fetch the latest 24 hourly DAGs at startup.
+dags_count = 24
+
+# Prune old DAGs, keeping a rolling 24-hour window.
+archive_mode = false
+history_retention_dags = 24
+```
+
+`dags_count` is the number of recent DAGs fetched during startup and after a
+network reconnection. `history_retention_dags` is the number kept locally. In
+normal mode, `dags_count` cannot be greater than
+`history_retention_dags`. Each DAG represents one hour with DarkIRC's current
+rotation settings.
+
+Review the `[net]` section before starting, especially `active_profiles`, seed
+addresses, proxy settings, and any inbound address you intend to expose. See
+[network and message privacy](darkirc.md#network-privacy-and-message-privacy) and the
+[node configuration guides](../nodes/node-configurations.md) for transport and
+public-listener setups.
+
+## Start and connect
+
+Start the node from the repository root:
+
+```shell
+% ./darkirc
+```
+
+To use a configuration in another location, pass it explicitly:
+
+```shell
+% ./darkirc --config /path/to/darkirc_config.toml
+```
+
+Wait for the full startup sync to finish:
+
+```text
+Event DAG synced successfully (full mode, 24 dag(s))
+```
+
+Then connect an IRC client to `127.0.0.1:6667`, unless you changed
+`irc_listen`. The [IRC client instructions](darkirc.md#connect-an-irc-client)
+provide a WeeChat example.
+
+Stop the node with `Ctrl-C` so the datastores are flushed cleanly. Changes to
+history, datastore, and network settings take effect only after a restart;
+`/rehash` is only for reloadable IRC settings such as channels and contacts.

+ 48 - 69
doc/src/misc/darkirc/private_message.md

@@ -1,97 +1,76 @@
+# Configure encrypted direct messages
 
-# Configuring a Private chat between users
+DarkIRC disables plaintext direct messages. Two users can configure a
+`crypto_box::ChaChaBox` using each other's public key and their own secret key.
+The resulting Event Graph message fields are encrypted and base58 encoded.
 
-Any two users on the `darkirc` server can establish a fully encrypted 
-communication medium between each other using a basic keypair setup.
+This is static public-key encryption, not the Signal protocol: it has no
+ratchet, automatic key verification, or forward secrecy. Protect the secret
+keys and authenticate public keys through a trusted channel.
 
-## Configuring darkirc_config.toml
+## Generate and exchange keys
 
-`darkirc_config.toml` should be created by default in `~/.config/darkfi/`
-when you first run `darkirc`.
-
-Generate a keypair using the following command: 
+Each participant generates a keypair locally:
 
 ```shell
 % darkirc --gen-chacha-keypair
 ```
-This will generate a Public Key and a Private Key.
 
-Save the Private key safely & add it to the `darkirc_config.toml` 
-file under your contact(s). You may reuse this keypair for multiple
-contacts, or generate a new one each time.
-```toml
-[contact.“satoshi”]
-dm_chacha_public = “the_contact_public_key_goes_here”
-my_dm_chacha_secret = “your_private_key_for_this_contact_goes_here”
-```
+Share only the generated public key. A public DarkIRC channel is visible to
+everyone and retained by nodes, so use an authenticated out-of-band channel
+when key substitution is a concern. Never share the secret key.
 
-To share your Public Key with a user over `darkirc` you can use one of the 
-public channels or via an external app like Signal, as plaintext DMs 
-are disabled in `darkirc`.
+If a secret key remains available but its public key was lost, derive it with:
 
-<u><b>Note</b></u>: When sharing/receiving public keys 
-(i.e modifying `darkirc_config.toml`), we don't have to restart the 
-daemon for the new changes to take effect, we simply send `/rehash`
-command from IRC client (or `/quote rehash`)
-
-<u><b>Note</b></u>: If you use the `darkirc`'s public channel, your 
-message will be publically visible on the IRC chat.
+```shell
+% darkirc --get-chacha-pubkey BASE58_SECRET_KEY
+```
 
-See the [example darkirc_config.toml](https://codeberg.org/darkrenaissance/darkfi/src/branch/master/bin/darkirc/darkirc_config.toml) for more details
+## Configure both participants
 
-## Example
-Lets start by configuring our contacts list in the generated 
-`darkirc_config.toml` file (you can also refer to the examples written 
-in the comments of the toml file), let's assume Alice and Bob want to
-privately chat after they have each other's public keys:
+Suppose Alice labels Bob as `Bob`. Alice places Bob's public key and Alice's
+secret key in her configuration:
 
-Alice would add bob to her contact list in her own config file:
 ```toml
-[contact.”Bob”]
-dm_chacha_public = “D6UzKA6qCG5Mep16i6pJYkUCQcnp46E1jPBsUhyJiXhb”
-my_dm_chacha_secret = “A3mLrq4aW9UkFVY4zCfR2aLdEEWVUdH4u8v4o2dgi4kC”
+[contact."Bob"]
+dm_chacha_public = "BOBS_BASE58_PUBLIC_KEY"
+my_dm_chacha_secret = "ALICES_BASE58_SECRET_KEY"
 ```
 
-And Bob would do the same:
-```toml
-[contact.”Alice”]
-dm_chacha_public = “9sfMEVLphJ4dTX3SEvm6NBhTbWDqfsxu7R2bo88CtV8g”
-my_dm_chacha_secret = “E229CzXev335cxhHiJyuzSapz7HMfNzf6ipbginFTvtr”
-```
+Bob configures the inverse relationship:
 
-Lets see an Example where 'Alice' sends “Hi” message to 'Bob' using 
-the /msg command
-```
-/msg Bob Hi
+```toml
+[contact."Alice"]
+dm_chacha_public = "ALICES_BASE58_PUBLIC_KEY"
+my_dm_chacha_secret = "BOBS_BASE58_SECRET_KEY"
 ```
 
-<u>Note for Weechat Client Users:</u>\
-When you private message someone as shown above, the buffer will not 
-pop in weechat client until you receive a reply from that person.
+The contact label is local, case-sensitive, and does not have to equal the
+other user's current IRC nickname. Use the exact configured label when
+sending a message.
 
-For example here 'Alice' will not see any new buffer on her irc interface for 
-the recent message which she just send to 'Bob' until 'Bob' replies,
-but 'Bob' will get a buffer shown on his irc client with the message 'Hi'.
+After editing `[contact.*]`, reload contacts from the IRC client:
 
-Reply from 'Bob' to 'Alice'
-```
-/msg alice welcome!
+```text
+/rehash
 ```
 
-Or instead of `/msg` command, you can use:
-```
-/query Bob hello
-```
-This works exactly the same as `/msg` except it will open a new buffer 
-with Bob in your client regardless of sending a msg or not.
+You can also restart DarkIRC. Invalid base58 or a key that is not 32 bytes is
+rejected while loading the configuration.
 
-<u><b>Note</b></u>: The contact name is not the irc nickname, it can 
-be anything you want, and you should use it when DMing.
+## Send messages
 
-<u><b>Note</b></u>: It's always a good idea to save your keys somewhere safe, but in 
-case you lost your Public Key and you still have your Private key in 
-`darkirc_config.toml` file, you recover the Public Key like so:
-```shell
-% darkirc --get_chacha_pubkey <chacha-secret>
+Alice can send to her configured `Bob` contact with either command:
+
+```text
+/msg Bob Hi
+/query Bob Hi
 ```
 
+`/query` commonly opens the contact buffer immediately; `/msg` buffer behavior
+depends on the IRC client. A direct message to a name absent from the local
+`[contact.*]` table is refused rather than sent as plaintext.
+
+Reusing one local keypair for several contacts is supported, but separate
+keypairs reduce the impact and linkability of one compromised key. Back up the
+configuration securely if the keys must survive a datastore or device loss.

+ 51 - 98
doc/src/misc/darkirc/specification.md

@@ -1,121 +1,74 @@
-# darkirc Specification
+# DarkIRC protocol and data reference
 
-## PrivMsgEvent
+This page describes the current DarkIRC-specific data placed in the Event
+Graph and the IRC surface exposed to local clients. The underlying peer
+protocol is documented in the [Event Graph network protocol](../event_graph/network_protocol.md).
 
-This is the main message type inside `darkirc`. The `PrivMsgEvent` is an
-[event action](../event_graph/network_protocol.md#event).
+## Event content
 
+Each chat event serializes the following `Privmsg` structure:
 
-| Description   | Data Type  | Comments                                                   | 
-|---------------|------------|------------------------------------------------------------|
-| `nickname`    | `String`   | The nickname for the sender (must be less than 32 chars)   |
-| `target`      | `String`   | The target for the message (recipient)                     |
-| `message`     | `String`   | The actual content of the message                          |
+| Field | Type | Current meaning |
+| --- | --- | --- |
+| `version` | `u8` | Format version; newly emitted messages currently use `0`. |
+| `msg_type` | `u8` | Message subtype; newly emitted messages currently use `0`. |
+| `channel` | `String` | Channel name, or an encrypted dummy value for a DM. |
+| `nick` | `String` | Sender nickname, or an encrypted dummy value for a DM. |
+| `msg` | `String` | Message body or encoded ciphertext. |
 
-## ChannelInfo
+Nicknames and channel names are limited to 24 bytes. Message bodies and topics
+are limited to 512 bytes. The IRC input buffer is limited to 1024 bytes.
 
-Preconfigured channel in the configuration file.
+### Public channels
 
-In the TOML configuration file, the channel is set as such:
+Without a configured channel secret, `channel`, `nick`, and `msg` are stored as
+plaintext. Public channel history should be treated as public and persistent.
 
-```toml
-[channel."#dev"]
-secret = "GvH4kno3kUu6dqPrZ8zjMhqxTUDZ2ev16EdprZiZJgj1"
-topic = "DarkFi Development Channel"
-```
-
-| Description  | Data Type     | Comments                                                      |
-|--------------|---------------| --------------------------------------------------------------|
-| `topic`      | `String`      | Optional topic for the channel                                |
-| `secret`     | `String`      | Optional NaCl box for the channel, used for {en,de}cryption.  |
-| `joined`     | `bool`        | Indicate whether the user has joined the channel              |
-| `names`      | `Vec<String>` | All nicknames which are visible on the channel                |
-
-
-## ContactInfo
+### Encrypted channels
 
-Preconfigured contact in the configuration file.
-
-In the TOML configuration file, the contact is set as such:
+For a `[channel."#name"]` with a `secret`, DarkIRC constructs a
+`crypto_box::ChaChaBox` from that 32-byte base58 secret. The channel name,
+padded nickname, and message are encrypted independently and encoded with
+base58. Every participant must configure the identical secret.
 
 ```toml
-[contact."nick"]
-contact_pubkey = "7CkVuFgwTUpJn5Sv67Q3fyEDpa28yrSeL5Hg2GqQ4jfM"
+[channel."#project"]
+secret = "BASE58_32_BYTE_SECRET"
+topic = "Private project channel"
 ```
 
-| Description   | Data Type     | Comments                                             |
-|---------------|---------------| -----------------------------------------------------|
-| `pubkey`      | `String`      | A Public key for the contact to encrypt the message  |
-
-## IrcConfig
-
-The base Irc configuration for each new `IrcClient`.
-
-| Description     | Data Type                       | Comments                                                                        |
-|-----------------|-------------------------------- | --------------------------------------------------------------------------------|
-| `is_nick_init`  | `bool`                          | Confirmation of receiving /nick command                                         |
-| `is_user_init`  | `bool`                          | Confirmation of receiving /user command                                         |
-| `is_cap_end`    | `bool`                          | Indicate whether the irc client finished the Client Capability Negotiation      |
-| `is_pass_init`  | `bool`                          | Confirmation of checking the password in the configuration file                 |
-| `is_registered` | `bool`                          | Indicate the `IrcClient` is initialized and ready to sending/receiving messages |
-| `nickname`      | `String`                        | The irc client nickname                                                         |
-| `password`      | `String`                        | The password for the irc client. (it could be empty)                            |
-| `private_key`   | `Option<String>`                | A private key to decrypt direct messages from contacts                          |
-| `capabilities`  | `HashMap<String, bool>`         | A list of capabilities for the irc clients and the server to negotiate          |
-| `auto_channels` | `Vec<String>`                   | Auto join channels for the irc clients                                          |
-| `channels`      | `HashMap<String, ChannelInfo>`  | A list of preconfigured channels in the configuration file                      |
-| `contacts`      | `HashMap<String, ContactInfo>`  | A list of preconfigured contacts in the configuration file for direct message   |
-
-## IrcServer
+Generate a secret with `darkirc --gen-channel-secret`.
 
-The server start listening to an address specifed in the configuration file. 
+### Direct messages
 
-For each irc client get connected, an `IrcClient` instance created.
+For a configured contact, DarkIRC encrypts dummy channel and nickname fields
+and encrypts the message with the contact ChaCha box. Contact configuration
+requires the contact's public key and the local secret key:
 
-| Description               | Data Type                     | Comments                                              |
-|---------------------------|-------------------------------| ------------------------------------------------------|
-| `settings`                | `Settings`                    | The base settings parsed from the configuration file  |
-| `clients_subscriptions`   | `SubscriberPtr<ClientSubMsg>` | Channels to notify the `IrcClient`s about new data    |
-
-##  IrcClient
-
-The `IrcClient` handle all irc opeartions and commands from the irc client.
-
-| Description       | Data Type                     | Comments                                                                  |
-|-------------------|-------------------------------|---------------------------------------------------------------------------|
-| `write_stream`    | `WriteHalf<Stream>`           | A writer for sending data to the connection stream                        |
-| `read_stream`     | `ReadHalf<Stream>`            | Read data from the connection stream                                      |
-| `address`         | `SocketAddr`                  | The actual address for the irc client connection                          |
-| `irc_config`      | `IrcConfig`                   | Base configuration for irc                                                |
-| `server_notifier` | `Channel<(NotifierMsg, u64)>` | A Channel to notify the server about a new data from the irc client       |
-| `subscription`    | `Subscription<ClientSubMsg>`  | A channel to receive notification from the server                         |
-
-
-## Communications between the server and the clients
+```toml
+[contact."Bob"]
+dm_chacha_public = "BOBS_BASE58_PUBLIC_KEY"
+my_dm_chacha_secret = "ALICES_BASE58_SECRET_KEY"
+```
 
-Two Communication channels get initialized by the server for every new `IrcClient`. 
+See [encrypted direct messages](private_message.md) for the two-party setup and
+security limitations.
 
-The channel `Channel<(NotifierMsg, u64)>` used  by the `IrcClient` to
-notify the server about new messages/queries received from the irc client.
+## Local IRC interface
 
-The channel `Subscription<ClientSubMsg>` used by the server to notify
-`IrcClient`s about new messages/queries fetched from the `View`. 
+DarkIRC currently handles these IRC commands:
 
-### ClientSubMsg
+`ADMIN`, `CAP`, `INFO`, `JOIN`, `LIST`, `MODE`, `MOTD`, `NAMES`, `NICK`,
+`PART`, `PASS`, `PING`, `PRIVMSG`, `REHASH`, `TOPIC`, `USER`, and `VERSION`.
 
-```rust
-	enum ClientSubMsg {
-		Privmsg(`PrivMsgEvent`),
-		Config(`IrcConfig`),	
-	}
-```
+It provides the custom client capabilities `no-history` and `no-autojoin`.
+NickServ commands are sent with `PRIVMSG NickServ ...` when RLN is enabled.
+DarkIRC does not claim complete RFC 2812 compatibility; commands that depend
+on conventional centralized IRC server state may be absent or have P2P-specific
+semantics.
 
-### NotifierMsg 
-
-```rust
-	enum NotifierMsg {
-		Privmsg(`PrivMsgEvent`),
-		UpdateConfig,
-	}
-```
+## Reloadable configuration
 
+`REHASH` reloads `autojoin`, `[channel.*]`, and `[contact.*]` from the active
+configuration file. P2P, datastore, history, archive, RPC, listener, password,
+and RLN settings require a daemon restart.

+ 6 - 35
doc/src/misc/dnetview.md

@@ -1,37 +1,8 @@
-# Dnetview
+# dnet
 
-A simple tui to explore darkfi darkirc network topology.
-
-dnetview displays: 
-
-1. all active nodes
-2. outgoing, incoming and manual sessions
-3. each associated connection and recent messages.
-
-## Install 
-
-```shell
-% git clone https://codeberg.org/darkrenaissance/darkfi
-% cd darkfi
-% make BINS=dnetview
-```
-
-## Usage
-
-Run dnetview as follows:
-
-```shell
-dnetview -v
-```
-
-On first run, dnetview will create a config file in .config/darkfi. You
-must manually enter the RPC ports of the nodes you want to connect to
-and title them as you see fit.
-
-Dnetview creates a logging file in /tmp/dnetview.log. To see json data
-and other debug info, tail the file like so:
-
-```shell
-tail -f /tmp/dnetview.log
-```
+`dnet` is the Python TUI for inspecting DarkFi P2P sessions and messages. The
+old `dnetview` binary and `make BINS=dnetview` instructions no longer apply.
 
+See the maintained [dnet guide](../learn/dchat/network-tools/using-dnet.md).
+For DarkIRC, configure dnet to connect to RPC port 9605 and remove
+`"p2p.get_info"` from `rpc_disabled_methods` before restarting DarkIRC.

+ 1 - 1
doc/src/misc/event_graph/recovery.md

@@ -10,7 +10,7 @@ needed.
 
 `EventGraph` stores several sled trees in the application's datastore.
 For DarkIRC this is the `--datastore` path, defaulting to
-`~/.local/share/darkfi/darkirc_db`. This is not the same as the P2P
+`~/.local/share/darkfi/darkirc/darkirc_db`. This is not the same as the P2P
 `p2p_datastore` path.
 
 Important trees are:

+ 5 - 4
doc/src/misc/faq.md

@@ -3,7 +3,8 @@
 ## What is DarkFi?
 DarkFi is an ecosystem of anonymous applications. It consists of a layer 1 
 blockchain, a communications service, and a task management service. The
-communications service `darkirc` is an anonymous IRC server. `tau` is a task 
+communications service `darkirc` is a peer-to-peer chat daemon with a local IRC
+interface and optional anonymity transports. `tau` is a task
 management app that gives users the ability to collaborate with others including 
 assigning and syncing tasks across different workspaces. DarkFi is built 
 with strong privacy, censorship-resistance, and free (as in freedom) and open 
@@ -51,8 +52,8 @@ general-purpose CPUs, and is also used by Monero. You can find more information
 about the DarkFi consensus process [here](../arch/consensus.md).
 
 ## How can I chat with DarkFi devs?
-Join [DarkIRC](darkirc/darkirc.md), our peer-to-peer anonymous implementation of 
-an IRC server. There are weekly `#dev` meetings on Mondays.
+Join [DarkIRC](darkirc/darkirc.md), our peer-to-peer chat daemon with a local IRC
+interface. There are weekly `#dev` meetings on Mondays.
 
 ## How can I contribute to the project or build something on top of DarkFi?
 You can visit [here](../dev/contrib/contrib.md)
@@ -68,4 +69,4 @@ If you are having trouble connecting DarkFi applications, please refer to
 Follow the [testnet guide](../testnet/node.md).
 
 ## How can I run my DarkFi nodes over Tor?
-You can setup a Tor enabled node [here](nodes/tor-guide.html).
+You can set up a Tor-enabled node [here](nodes/tor-guide.md).

+ 114 - 250
doc/src/misc/network-troubleshooting.md

@@ -1,309 +1,173 @@
 # Network troubleshooting
 
-If you're having network issues, refer to this page to debug various 
-issues. If you see inconsistencies in the docs: always trust 
-`${DARKFI_REPO}/bin/darkirc/darkirc_config.toml` or whichever respective 
-apps' repo config file. Documentation updates are a current WIP.
+This page uses DarkIRC examples. Other DarkFi daemons use the same P2P profile
+schema but have their own configuration files, seed endpoints, and ports.
 
-The default location for config files is `~/.config/darkfi`.
+For DarkIRC, the authoritative template is
+`bin/darkirc/darkirc_config.toml`. The generated configuration is
+`~/.config/darkfi/darkirc_config.toml` on Linux and
+`~/Library/Application Support/darkfi/darkirc_config.toml` on macOS.
 
-<u><b>Note</b></u>: throughout this page we generally assume you are using
-`darkirc` since it's our main p2p app currently. If you're
-using a different app such as `darkfid` or `taud`, the syntax remains
-but the app name will change (for example, if using `taud`, 
-the config file `~/.config/darkfi/darkirc_config.toml` 
-would become `~/.config/darkfi/taud_config.toml`).
+## Start with debug logs
 
-## Common net problems 
-
-The most common problem in connecting to `darkirc` is the following:
-
-```
-[ERROR] [EVENTGRAPH] Sync: Could not find any DAG tips
-[ERROR] Failed syncing DAG. Exiting.
-Error: DagSyncFailed
-```
-
-This generally indicates that we were *unable to establish any P2P
-connections*, and thus couldn't retrieve the message history required to
-sync our messages locally within the time limit (DAG sync failed).
-
-There are two main reasons why we would fail to establish a P2P connection:
-
-1. Seed node is down or rejecting our connection
-2. Our node does not have sufficient peers
-
-If the seed node is down, you will see this message in the debug output:
+Run DarkIRC with additional verbosity:
 
-```
-ERROR] [P2P] Network reseed failed!
-[WARN] [P2P] Unable to connect to seed [tcp+tls://lilith1.dark.fi:9600/]: IO error: connection refused
+```shell
+% ./darkirc -vv
 ```
 
-If it's a problem related to nodes, you will typically see a successful
-seed connection like so:
+Or configure a file log:
 
-```
-[INFO] [P2P] Connected seed [tcp+tls://lilith1.dark.fi:9600/]
-[INFO] [P2P] Disconnecting from seed [tcp+tls://lilith1.dark.fi:9600/]
-```
-
-Followed by multiple connection failed messages, like so:
-
-```
-[INFO] [P2P] Unable to connect outbound slot #5 [tcp+tls://example_peer:9600/]: IO error: connection refused
-[INFO] [P2P] Unable to connect outbound slot #6 [tcp+tls://example_peer2:9600/]: IO error: host unreachable
-```
-
-### Seed node is down
-
-If you get an error like this:
-
-```
-[WARN] [P2P] Unable to connect to seed [tcp+tls://lilith1.dark.fi:9600/]: IO error: connection refused
-```
-
-This means you are failing to establish a connection to the seed node.
-
-<u><b>Note</b></u>: the IO error might not always read `connection refused`
-but could be some other error such as `host unreachable`. Please note
-this IO error as it is useful debugging info.
-
-Here's what to do next:
-
-#### It's my first time connecting to the network
-
-If it's your first time connecting to the network, you local node does
-not have a record of other peers it can connect to in case the seed node
-is down. Please do the following:
-
-1. Take careful note of the `IO error` that is written after `Unable to
-connect to seed`.
-2. Refer to [Error reporting](#error-reporting) section below.
-3. You can set a peer such as `tcp+tls://example_peer:9600` in your
-config file. Ask in the telegram community channel for an active peer
-(here we are using a fake peer called `example_peer`. Then open the
-config file at `~/.config/darkfi/darkirc_config.toml` and modify the `peers`
-field with the provided peer as follows:
-
-```
-peers = ["tcp+tls://example_peer:9600"]
+```toml
+log = "/tmp/darkirc.log"
+verbose = 2
 ```
 
-#### It's not my first time connecting to the network
+Do not publish the complete configuration or datastore: they can contain IRC
+passwords, channel secrets, contact keys, and NickServ/RLN account secrets.
 
-If it's not your first time connecting to the network, you should be
-able to establish connections to peers even if the seed node is down.
+## DAG sync fails
 
-This is possible via a list of hosts that your darkirc node keeps locally.
-You can inspect the hostlist as follows:
+`DagSyncFailed` during initial static or rotating sync commonly means there
+are no usable peer channels, although incompatible or incomplete peer data can
+also cause it. Check the log in this order:
 
-```
-cat ~/.local/share/darkfi/darkirc/hostlist.tsv
-```
+1. Confirm the selected `[net].active_profiles` are defined by matching
+   `[net.profiles."..."]` tables.
+2. Confirm the profile has current `seeds` or `peers` entries.
+3. For proxy transports, confirm the local proxy is listening and the proxy
+   URL is correct.
+4. Check the host clock. Event Graph rotation uses timestamps; a materially
+   incorrect clock can select the wrong hourly DAG slots.
+5. Confirm the binary and Event Graph consensus parameters match the network.
 
-If the list is empty, open `~/.config/darkfi/darkirc_config` and ensure
-that the `hostlist` field is set with a path of your choosing.
+A manual peer belongs inside its transport profile, for example:
 
-For example:
+```toml
+[net]
+active_profiles = ["tcp+tls"]
 
+[net.profiles."tcp+tls"]
+seeds = ["tcp+tls://lilith0.dark.fi:9600", "tcp+tls://lilith1.dark.fi:9600"]
+peers = ["tcp+tls://known-peer.example:9600"]
 ```
-hostlist = "~/.local/share/darkfi/darkirc/hostlist.tsv"
-```
-
-<u><b>Note</b></u>: If you are editing a line that is commented out, don't forget
-to uncomment the line.
-
-Then follow the steps in the above section 
-[It's my first time connecting to the network](#its-my-first-time-connecting-to-the-network).
 
-If the hostlist is not empty, retry the `darkirc` connection and carefully
-note the connection errors that are happening from peers. See [Error reporting](#error-reporting) 
-section below to report errors.
-It might be simply the case that there are not enough peers on the
-network, or perhaps there is another issue we are not aware of.
+Use an endpoint supplied by an operator you trust; the example peer hostname
+above is only a placeholder.
 
-You can also check the liveness of peers using the `ping` tool. 
-Refer to the [Ping tool](#ping-tool) section below for instructions. 
+## Inspect the hostlist
 
-### Cannot establish peer connections
+DarkIRC's default hostlist is:
 
-If you're able to connect to the seed but are failing to establish peer
-connections, please retry the darkirc connection and carefully note the
-connection errors that are happening from peers. See the
-[Error reporting](#error-reporting) section to report errors.
-
-### Cannot establish Tor onion connections
-
-You may get an error like this:
-```
-[WARN] darError reportingkfi::net::transport::tor: error: tor: Onion Service not found: Failed to obtain hidden service circuit to ????.onion: Unable to download hidden service descriptor
+```text
+~/.local/share/darkfi/darkirc/p2p_hostlist.tsv
 ```
-This happens when [Arti](https://gitlab.torproject.org/tpo/core/arti/-/blob/main/README.md) 
-gets corrupted due to internet downtime or other triggers. To fix this, 
-we'll delete the directory:
-
-1. Stop `darkirc` 
-2. Stop `tor` daemon 
-3. Remove `arti` cache folder located at `~/.local/share/arti` 
-4. Start `tor` daemon 
-5. Start `darkirc`
-
-### I'm connected but my messages do not go through
 
-If you see something in the logs like this:
+Its path is configured in `[net]`:
 
+```toml
+[net]
+hostlist = "~/.local/share/darkfi/darkirc/p2p_hostlist.tsv"
 ```
-[INFO] [P2P] Outbound slot #1 connected [tcp-tls://example_peer:9600/] 
-```
-
-That means you are connected. You can verify that by writing `test` in
-#random and seeing do you get a `test back` message.
-
-If you do not get a `test back` message, that can mean either:
-
-1. You need to wait for your DAG to sync (this can take several minutes,
-especially over Tor or on days with high network activity).
 
-2. You need to update your system clock. To sync the event graph,
-darkirc requires that your system clock is correct. You can check your
-system time by running `date`.  The best way to ensure your clock does
-not drift is to run some timekeeping daemon like `chrony` or `ntpd`. If
-your clock is wrong, set this up and try to reconnect again.
+An empty hostlist on a first start is expected until the node learns peers. If
+seeds are unreachable and the hostlist has no usable entries, add a known
+manual peer to the appropriate profile.
 
-### DagSync spam
+## Test an endpoint
 
-If you see a many rapid `EventReq` messages in the log, it is possible that there is
-an incompatibility with your local `darkirc` database and the state of the network.
+The repository includes a transport-level ping utility. From
+`script/ping`, test a complete endpoint URL:
 
-This can be resolved by deleting `~/.local/share/darkfi/darkirc_db/`
-
-This is a known bug and we are working on a fix.
-
-## dnet
-
-dnet is a simple tui to explore darkfi p2p network topology. You can use 
-dnet to gather more network information. dnet displays:
-
-1. Active p2p nodes
-2. Outgoing, incoming, manual and seed sessions
-3. Each associated connection and recent messages.
-
-To install and learn to use dnet, go [here](../learn/dchat/network-tools/using-dnet.md).
-You can use dnet to view the network topology and see how your node 
-interacts within the network. dnet log information is created in 
-`${DARKFI_REPO}/bin/dnet/dnet.log`.
-
-## Ping tool
-
-You can ping any node to make sure it's online by using the provided
-`ping` tool located at `${DARKFI_REPO}/script/ping`. Select a peer from 
-your hostlist file. You can now use the `ping` tool by 
-running this command:
-
-```
-$ cargo run -- tcp+tls://example_peer:9600
+```shell
+% cargo run --release --all-features -- tcp+tls://known-peer.example:9600
 ```
 
-If the peers are reachable, you'll receive a `Connected!` output.
+A successful transport handshake prints `Connected!` and version information.
+Run the same test from a separate network when checking whether your advertised
+inbound endpoint is reachable. For example:
 
-## Inbound
-
-To see if your address is reachable to others in the network, you'll need 
-to use a separate device to `ping` your external address. 
-[You can generate an external address here](nodes/tor-guide.md#inbound-node-settings).
-For example purposes, let's assume your external address is 
-`jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd.onion`. In 
-`${DARKFI_REPO}/script/ping` we can attempt to `ping` your external address 
-from a separate device. 
-
-```
-$ cargo run -- tor://jamie3vkiwiskbhpjdyajmzeor4mc4i7yopvpo4p7cyd.onion
+```shell
+% cargo run --release --all-features -- tor://youraddress.onion:9600
 ```
 
-If your external address is reachable, you'll receive a `Connected!` prompt.
+This tests reachability, not whether the remote node has compatible DarkIRC
+history.
 
-## Check tor connection
+## Inbound nodes
 
-You can verify if your local node is running over Tor. Execute this 
-command in `${DARKFI_REPO}/script`. You'll need to install pysocks 
-`pip install pysocks` prior to running `tor-test.py` the first time:
+An inbound listener and its advertised address must describe the same reachable
+service. They are configured under the same profile:
 
-```
-$ python tor-test.py 
+```toml
+[net.profiles."tcp+tls"]
+inbound = ["tcp+tls://0.0.0.0:9600"]
+external_addrs = ["tcp+tls://chat.example:9600"]
 ```
 
-If your local node is running Tor, the response should be an IP address.
-An error will return if Tor isn't running.
+Open or forward the port in the host firewall and router. Test the
+`external_addrs` URL from another device. Do not advertise loopback or a
+private LAN address to the public network.
 
-You can also verify if your node is running over Tor with 
-dnet. If you run `dnet` and you see onion addresses as
-outbound connections, and localhost connections as inbound 
-connections, this means you're connected to Tor.
+For Tor static and ephemeral inbound configurations, see the
+[Tor guide](nodes/tor-guide.md#inbound-tor-node).
 
-## Helpful debug information
+## Tor connections
 
-If you're looking to debug an issue, try these helpful tools.
+The generated DarkIRC configuration uses the built-in `tor` profile, which is
+implemented with Arti and does not require a separately configured Tor SOCKS5
+daemon. If using the explicit `socks5` profile instead, the proxy in the URL
+must be running.
 
-### Logs in debug mode
-
-You can run any app in debug mode as follows:
-
-```
-$ ./darkirc -vv
-```
+When onion connections fail, distinguish between:
 
-Alternatively, modify the config file at `~/.config/darkfi/darkirc.toml` as follows:
+- no local internet route or blocked Tor bootstrap;
+- an unavailable onion service;
+- a stale or corrupted Arti state directory; and
+- a profile mismatch, such as enabling `tor` without a
+  `[net.profiles."tor"]` table.
 
-```toml
-# Log to file. Off by default.
-log = "/tmp/darkirc.log"
-# Set log level. 1 is info (default), 2 is debug, 3 is trace
-verbose = 2
-```
+Preserve logs before removing any cache. If resetting Arti state is necessary,
+stop every process using it first and move the state directory aside instead
+of deleting it immediately.
 
-### Peer Discovery
+## Connected but no messages arrive
 
-When running in debug mode, you will see `[INFO]` messages that indicate 
-`PEER DISCOVERY`. This is healthy and expected behavior.
+Wait for this startup message before testing IRC history or sending chat:
 
-```
-[INFO] net::outbound_session::peer_discovery(): [P2P] [PEER DISCOVERY] Asking peers for new peers to connect to...
+```text
+Event DAG synced successfully (full mode, 24 dag(s))
 ```
 
-### Config file
+If it never appears, investigate sync rather than the IRC client. If it does
+appear, confirm:
 
-Your config files are generated in your `~/.config/darkfi` directory. 
-You'll have to run each daemon once for the app to spawn a config file, 
-which you can review and edit. There is also helpful information within 
-the config files.
+- the IRC client is connected to the configured `irc_listen` address;
+- the client joined the intended channel;
+- encrypted-channel participants use the same channel secret; and
+- the contact label and keys match when testing a DM.
 
-If experiencing connection issues review the configuration file for any mistakes. 
-Check for duplicate variables.
+`fast_mode = true` fetches headers without message bodies, so it is not
+suitable when the local client needs complete history.
 
-### Node information script
-
-If you're looking for information about your node, including inbound, 
-outbound, and seed connections, execute this command in ``${DARKFI_REPO}/script``:
-
-```
-$ python node_get-info.py
-```
+## Repeated or corrupt DAG sync
 
-### Hostlist issues
+Do not immediately delete the DarkIRC datastore. Its default path is
+`~/.local/share/darkfi/darkirc/darkirc_db`, and it can contain local account
+secrets as well as public history.
 
-If you receive DAG sync issues, verify:
+Stop DarkIRC, preserve the entire datastore and configuration, and follow the
+[Event Graph recovery guide](event_graph/recovery.md). If a clean resync is
+appropriate, move the old directory aside and start with a new empty path.
+Never copy individual sled trees between datastores.
 
-1. A hostlist is set in the config file of the respective app.
-2. There are hosts in the hostlists (you should get hostlists from the 
-default seed on the first run). You can find the hostlist files within 
-the respective apps' repo. For example `darkirc`'s default hostlist location 
-is `~/.local/share/darkfi/darkirc/hostlist.tsv`.
+## Network viewers
 
-### Error reporting
+`dnet` displays inbound, outbound, manual, and seed sessions through the
+daemon's JSON-RPC interface. See [Using dnet](../learn/dchat/network-tools/using-dnet.md).
+The default DarkIRC RPC listener is `tcp://127.0.0.1:9605`; it is local-only
+unless explicitly changed. The generated config disables `p2p.get_info`; remove
+that method from `rpc_disabled_methods` and restart DarkIRC before using dnet.
 
-If you're receiving errors and need to report them, report using `darkirc` first. If you
-cannot connect, you can report these errors on the community telegram (t.me/darkfichat). 
-- Don't send screenshots.
-- Use [pastebin](https://pastebin.com/) (or [termbin](https://termbin.com/)
-or another paste service) for multi-line errors, or just copy-paste for a single line error.
+When reporting an issue, include the DarkIRC version, selected profiles,
+redacted logs, operating system, and whether the endpoint ping succeeded.

+ 25 - 101
doc/src/misc/nodes/i2p-guide.md

@@ -1,121 +1,45 @@
-# Set-up an I2p-enabled node
+# Run an I2P-enabled DarkIRC node
 
-_To connect to I2p network we use the socks5 proxy provided by [I2pd](https://i2pd.website/)_
+DarkFi reaches I2P services through an I2P SOCKS5 proxy. Install and configure
+an I2P router such as i2pd using its current upstream documentation, and verify
+that its SOCKS5 proxy is listening (commonly on `127.0.0.1:4447`).
 
-<u><b>Note</b></u>: This page is a general guide for `i2p` nodes in the DarkFi 
-ecosystem and is applicable to other apps such as `taud` and `darkfid`. We use 
-`darkirc` as our main example throughout this guide. Commands such as `./darkirc`
-and configuration filenames need to be adjusted if using different apps.
-If you're using another app, the network configurations remain the same except 
-for the seed nodes you connect to.
+DarkIRC's I2P profile is not enabled by default and the template does not
+provide a guaranteed official I2P seed. Obtain a current DarkIRC I2P seed or
+manual peer from a trusted operator before relying on this profile.
 
-## Generating configuration files
-
-For configuration file generation, see [Common Setup Steps](public-guide.md#generating-configuration-files). 
-
-## Configure network settings
-
-Modify the network settings located in the `~/.config/darkfi` directory. This 
-configuration allows your node to send and receive traffic only via I2p.
-
-<u><b>Note</b></u>: As you modify the file, if you notice some settings are missing, 
-simply add them. Some settings may be commented-out by default. In the example 
-configurations below, you will find the a placeholder `youraddress.b32.i2p` which 
-indicates you should replace them with your i2p address.
-
-First, you must install [I2pd](https://i2pd.readthedocs.io/en/latest/user-guide/install/). It can usually be
-installed with your package manager. For example on an `apt` based system we can run:
-
-```
-% sudo apt install apt-transport-https
-% wget -q -O - https://repo.i2pd.xyz/.help/add_repo | sudo bash -s -
-% sudo apt update
-% sudo apt install i2pd
-```
-
-### Outbound node settings
-
-These outbound node settings for your i2p node configuration is only for
-connecting to the network. You will not advertise an external address.
-Make sure `i2pd` is running, and it's `socks5` proxy is listening on `127.0.0.1:4447`
-before running `darkirc`.
-
-<!-- TODO: replace the i2p seed address with an official one-->
+## Outbound I2P node
 
 ```toml
-## connection settings
-outbound_peer_discovery_cooloff_time = 60
-
-## Outbound connection slots
-outbound_connections = 8
-
-## Whitelisted transports for outbound connections
+[net]
 active_profiles = ["i2p"]
-
-## I2p Socks5 proxy
 i2p_socks5_proxy = "socks5://127.0.0.1:4447"
 
 [net.profiles."i2p"]
-## Seed nodes to connect to
-seeds = [
-    "i2p://6l2rdfriixo2nh5pr5bt555lyz56qox2ikzia4kuzm4okje7gtmq.b32.i2p:9602"
-]
+seeds = ["i2p://CURRENT_DARKIRC_SEED.b32.i2p:9600"]
 ```
 
-### Inbound node settings
-
-With these settings your node becomes an I2p inbound node. The `inbound` 
-settings are optional, but enabling them will increase the strength and 
-reliability of the network. Using I2p, we can host anonymous nodes as I2p eepsites(hidden 
-services). To do this, we need to set up our I2p daemon and create a hidden service.
-The following instructions should work on any Linux system.
+The `i2p_socks5_proxy` belongs in `[net]`; seed and peer URLs belong in the
+`[net.profiles."i2p"]` table.
 
-After installing `I2pd`, Now we can set up the hidden service. 
-For hosting an anonymous `darkirc` node, go to `/var/lib/i2pd/tunnels.d`
-and create a file `darkirc.conf` with the following contents:
+## Inbound I2P node
 
-```
-[darkirc]
-type = server
-host = 127.0.0.1
-port = 9602
-keys = darkirc.dat
-```
-
-Then restart i2pd:
-
-```
-% systemctl restart i2pd
-```
-
-Find the hostname of your hidden service by running the following command:
-
-```
-% curl -s http://127.0.0.1:7070/?page=i2p_tunnels | grep -Eo "[a-zA-Z0-9./?=_%:-]*" | grep "9602"
-```
-
-The above configuration saves the `i2p` hidden service key in `/var/lib/i2pd/darkirc.dat`,
-you might want to back it up.
-
-Note your `.b32.i2p` address and the ports you used while setting up the
-hidden service, and add the following settings to your configuration file:
+Create an I2P server tunnel that forwards an externally reachable I2P port to
+a loopback TCP listener. The exact router configuration and file locations are
+router- and distribution-specific. If the tunnel maps public port 9600 to
+`127.0.0.1:9601`, configure:
 
 ```toml
-## Inbound connection slots
+[net]
+active_profiles = ["i2p"]
 inbound_connections = 64
+i2p_socks5_proxy = "socks5://127.0.0.1:4447"
 
 [net.profiles."i2p"]
-## Addresses we want to advertise to peers
-external_addrs = ["i2p://youraddress.b32.i2p:9602"]
-
-## P2P accept addresses
-inbound = ["tcp://127.0.0.1:9602"]
+inbound = ["tcp://127.0.0.1:9601"]
+external_addrs = ["i2p://YOUR_ADDRESS.b32.i2p:9600"]
 ```
 
-## Connect and test your node
-
-See [Common Setup Steps → Connect and test your node](public-guide.md#connect-and-test-your-node).
-
-## Troubleshooting
-
-See [Common Setup Steps → Troubleshooting](public-guide.md#troubleshooting).
+Back up the I2P destination keys if the address must remain stable. Restart
+DarkIRC after changing the profile, then test the advertised I2P endpoint from
+another I2P-connected host.

+ 6 - 4
doc/src/misc/nodes/node-configurations.md

@@ -23,12 +23,14 @@ This section provides configuration examples for hosting DarkFi P2P nodes.
 
 
 ###  `darkirc`
-- `6667`: IRC server (plaintext)
-- `6697`: IRC server (TLS)
+- `6667`: IRC server (plaintext by default)
+- `6697`: Conventional IRC TLS port (only when configured)
 - `9600`: Inbound P2P
 - `_ +1`: Inbound Tor/I2p/...
-- `9605`: Public RPC server
-- `9606`: Restricted RPC server
+- `9605`: RPC server
+
+DarkIRC does not enable an inbound P2P listener by default. It currently has
+one RPC listener rather than separate public and restricted RPC servers.
 
 
 ### `fud`

+ 22 - 120
doc/src/misc/nodes/nym-guide.md

@@ -1,133 +1,35 @@
-# Set-up a Nym-enabled node
+# Run DarkIRC through a Nym SOCKS5 proxy
 
-_To connect to through `nym` network we use the `socks5` proxy provided
-by [Nym](https://github.com/nymtech/nym)_
+DarkFi supports outbound clearnet TLS connections through a Nym-provided
+SOCKS5 proxy. This is an outbound-only DarkIRC configuration; it does not make
+the node a Nym service or advertise an inbound address.
 
-<u><b>Note</b></u>: This page is a general guide for `nym` nodes in the
-DarkFi ecosystem and is applicable to other apps such as `taud` and
-`darkfid`. We use `darkirc` as our main example throughout this guide.
-Commands such as `./darkirc` and configuration filenames need to be
-adjusted if using different apps. If you're using another app, the
-network configurations remain the same except for the seed nodes you
-connect to.
+Install and initialize a current Nym SOCKS5 client using Nym's upstream
+documentation. Nym's CLI names and provider-selection workflow can change, so
+verify those details against the installed version. The selected provider must
+permit connections to the DarkIRC P2P port. Start the client and confirm its
+local SOCKS5 listener, commonly `127.0.0.1:1080`.
 
-<u><b>Note</b></u>: Currently, we can set up only outbound nodes with
-`Nym`.
-
-## Generating configuration files
-
-For configuration file generation, see [Common Setup Steps](public-guide.md#generating-configuration-files).
-
-## Configure network settings
-
-Modify the network settings located in the `~/.config/darkfi`
-directory. This configuration allows your node to send and receive
-traffic only via `Nym`.
-
-<u><b>Note</b></u>: As you modify the file, if you notice some settings
-are missing, simply add them. Some settings may be commented-out by
-default.
-
-First, download
-[nym-socks5-client](https://github.com/nymtech/nym/releases).
-Then initialize it:
-
-```
-./nym-socks5-client init --id [YOUR_ID] --provider [YOUR_PROVIDER]
-```
-
-Replace `[YOUR_ID]` with your own preferred id and `[YOUR_PROVIDER]`
-with a `Nym network requester` provider address. You can find a list of
-network requester providers from
-[Nym Harbour Master](https://harbourmaster.nymtech.net/). Make sure the
-provider you select is able to connect to non-standard ports used by
-darkirc nodes.
-
-After you initialize it, run the socks5 client:
-
-```
-./nym-socks5-client run  --id [YOUR_ID]
-```
-
-### Outbound node settings
-
-These outbound node settings for your `nym` node configuration is only
-for connecting to the network. You will not advertise an external
-address. Make sure `nym-socks5-client` is running, and it's `socks5`
-proxy is listening on `127.0.0.1:1080` before running `darkirc`.
-To be able to route our darkirc connections through the `socks5` proxy
-provided by `Nym`, we need to enable the socks5 proxy transport in our
-settings:
+Configure DarkIRC as follows:
 
 ```toml
-## connection settings
-outbound_peer_discovery_cooloff_time = 60
-
-## Outbound connection slots
-outbound_connections = 8
-
-## Nym Socks5 proxy
+[net]
+active_profiles = ["socks5+tls"]
+mixed_profiles = ["tcp+tls"]
 nym_socks5_proxy = "socks5://127.0.0.1:1080"
 
 [net.profiles."socks5+tls"]
-## Seed nodes to connect to
 seeds = [
-    "socks5+tls://127.0.0.1:1080/lilith1.dark.fi:9603"
+    "socks5+tls://127.0.0.1:1080/lilith0.dark.fi:9600",
+    "socks5+tls://127.0.0.1:1080/lilith1.dark.fi:9600",
 ]
 ```
 
-## Connect and test your node
+The explicit seed URLs bootstrap through the Nym proxy. Declaring `tcp+tls` in
+`mixed_profiles` lets learned `tcp+tls` peer addresses be converted to the
+active `socks5+tls` transport using `nym_socks5_proxy`.
 
-See [Common Setup Steps → Connect and test your node](public-guide.md#connect-and-test-your-node).
-
-## Troubleshooting
-
-See [Common Setup Steps → Troubleshooting](public-guide.md#troubleshooting).
-
-## Running a Nym Network Requester
-_You can run a `Nym Network Requester` to support the Darkfi P2P
-network_
-
-A `Nym` network requester serves as an exit gateway in the `Nym`
-network, an equivalent of an exit node in the Tor network. You can
-provide your `Nym` network requester address to others so that they
-can use your node as an exit gateway. To set it up you need a VPS with
-a good internet connection.
-
-First, download
-[nym-network-requester](https://github.com/nymtech/nym/releases).
-Then initialize it:
-
-```
-./nym-network-requester init --id [YOUR_ID]
-```
-
-You will see some configuration output along with the address of your
-`Nym Network Requester`, make sure to record it. It is the address you
-will share with others.
-
-With it's default configuration the `Nym Network Requester` will not
-allow connections to pass through non-standard ports, like the one used
-by darkirc nodes. By default it uses this
-[exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt).
-To override this behavior you need to modify the config file found in
-`~/.nym/service-providers/network-requester/testing/config/config.toml`.
-Go to the line with `upstream_exit_policy` and set it to your exit
-policy url:
-
-```
-upstream_exit_policy_url = 'http://localhost/YOUR_EXIT_POLICY.txt'
-```
-
-Run `nginx` or a `simple http server` to host your exit policy.
-
-This is a sample `exit policy` that whitelists a node with the ip
-address `NODE_IP_ADDRESS` and rejects any other connections. Note that
-the `exit policy` requires an ip address to be set, it doesn't accept a
-domain name:
-
-```
-ExitPolicy accept [NODE_IP_ADDRESS]:*
-# reject everything else not covered by any of the previous rules
-ExitPolicy reject *:*
-```
+Do not add `tcp+tls` itself to `active_profiles` unless direct clearnet
+connections are also intended. Restart DarkIRC after changing network
+settings, and inspect sessions with `dnet` to confirm connections use
+`socks5+tls` rather than direct `tcp+tls`.

+ 57 - 305
doc/src/misc/nodes/public-guide.md

@@ -1,349 +1,101 @@
-# DarkFi public node guide
+# Run a public DarkIRC node
 
-Public nodes are nodes that reveal themselves to the network. They are publicly
-accessible so they can accept inbound connections from other nodes.
-Public nodes are important for the health of the DarkFi P2P network. This guide 
-explains how to run the optimal DarkFi public node configurations.
+A public node accepts inbound P2P connections and advertises a reachable
+address. A clearnet address exposes the node's IP or hostname to peers. Use the
+[Tor guide](tor-guide.md) if that is not acceptable.
 
-<u><b>Note</b></u>: If you do not want your IP address to be public you can run 
-a node using Tor.
+First follow the [normal-node](../darkirc/normal-node.md) or
+[archive-node](../darkirc/archive-node.md) guide. Stop DarkIRC before editing
+`~/.config/darkfi/darkirc_config.toml`.
 
-<u><b>Note</b></u>: This page is a general guide for public nodes in the DarkFi
-ecosystem and is applicable to other apps such as `taud` and `darkfid`. We use
-`darkirc` as our main example throughout this guide. Commands such as `./darkirc`
-and configuration filenames need to be adjusted if using different apps.
-If you're using another app, the network configurations remain the same except
-for the seed nodes you connect to.
+## Clearnet `tcp+tls`
 
-## Common Setup Steps
-
-### Generating configuration files
-
-After compiling, you can start the application so it can spawn its configuration file. We use `darkirc` as the application example going forward.
-
-```shell
-% ./darkirc
-```
-
-`darkirc` creates a configuration file `darkirc_config.toml` by default in `~/.config/darkfi/`. You will review and edit this configuration file for your preferred network settings.
-
-## Connect and test your node
-
-Run ./darkirc. Welcome to the dark forest.
-
-You can test if your node is configured properly on the network. Use [Dnet](../../learn/dchat/network-tools/using-dnet.md) and the [ping-tool](../network-troubleshooting.md#ping-tool) to test your node connections. You can view if your node is making inbound and outbound connections.
-
-### Troubleshooting
-
-Refer to [Network troubleshooting](../network-troubleshooting.md) for further troubleshooting resources.
-
----
-
-## Configure your network settings
-
-Edit your `darkirc_config.toml` file to reflect the network settings you want 
-to support. Listed below are different `darkirc_config.toml` configurations. You 
-can choose between a clearnet node, a fully anonymous Tor node, or a bridge 
-node which runs over clearnet & Tor, and is most beneficial for the health of 
-the network. Each mode can be activated by adding the appropriate profile to
-`active_profiles` net setting.
-
-<u><b>Note</b></u>: As you modify the file, if you notice some settings are missing, 
-simply add them. Some settings may be commented-out by default. In the example 
-configurations below, you will find the placeholders `MY_IP_V4`, `MY_IP_V6`, 
-`my.resolveable.address`, and `youraddress.onion` which indicates you should replace 
-them with your public IPv4, IPv6, domain or your onion address. If you don't 
-have some of them (for example: IPv6 or domain) remove the values entirely.
-
-### Clearnet node
-
-A clearnet node routes traffic over `tcp+tls`. It is enabled by adding
-`tcp+tls` to the `active_profiles` list. You can find a complete example config
-file for `darkirc-clearnet.toml` in `${DARKFI_REPO}/bin/darkirc/config`.
+This example listens on the conventional DarkIRC P2P port. Replace the
+advertised hostname, open TCP port 9600 in the firewall, and configure router
+port forwarding if necessary.
 
 ```toml
-## Outbound connection slots
+[net]
+active_profiles = ["tcp+tls"]
 outbound_connections = 8
-
-## Inbound connection slots
 inbound_connections = 64
 
-## Whitelisted transports for outbound connections
-active_profiles = ["tcp+tls"]
-
-## Transports to be mixed
-mixed_profiles = []
-
 [net.profiles."tcp+tls"]
-## Seed nodes to connect to
-seeds = ["tcp+tls://lilith1.dark.fi:9600"]
-
-## Addresses we want to advertise to peers
-external_addrs = ["tcp+tls://MY_IP_V4:9600", "tcp+tls://MY_IP_V6:9600", "tcp+tls://my.resolveable.address:9600"]
-
-## P2P accept addresses
+seeds = ["tcp+tls://lilith0.dark.fi:9600", "tcp+tls://lilith1.dark.fi:9600"]
 inbound = ["tcp+tls://0.0.0.0:9600", "tcp+tls://[::]:9600"]
-
-## Connection settings with default values applicable for each profile separately
-#outbound_connect_timeout = 15
-#channel_handshake_timeout = 10
-#channel_heartbeat_interval = 30
+external_addrs = ["tcp+tls://chat.example:9600"]
 ```
 
-### Fully anonymous Tor-enabled node
-
-A Tor-enabled node routes traffic over `tor`. It is enabled by adding
-`tor` to the `active_profiles` list. You can find a complete example config
-file for `darkirc-tor.toml` in `${DARKFI_REPO}/bin/darkirc/config`. This node 
-configuration is for users that would like to support `darkirc` over the Tor 
-network. A Tor node provides the best anonymity on the network.
-
-You need to configure Tor and launch your hidden service prior to running your 
-public node over Tor. Please refer to 
-[Tor Nodes](tor-guide.md#inbound-node-settings).
-
-```toml
-## connection settings
-outbound_peer_discovery_cooloff_time = 60
-
-## Outbound connection slots
-outbound_connections = 8
-
-## Inbound connection slots
-inbound_connections = 64
-
-## Whitelisted transports for outbound connections
-active_profiles = ["tor"]
-
-## Transports to be mixed
-mixed_profiles = []
-
-[net.profiles."tor"]
-## Seed nodes to connect to
-seeds = [
-    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:9601",
-    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:9601",
-]
-
-## Addresses we want to advertise to peers
-external_addrs = ["tor://youraddress.onion:9601"]
-
-## P2P accept addresses
-inbound = ["tcp://127.0.0.1:9601"]
-
-## Connection settings with default values applicable for each profile separately
-#outbound_connect_timeout = 60
-#channel_handshake_timeout = 55
-#channel_heartbeat_interval = 90
-```
-
-### Fully anonymous I2p-enabled node
-
-An I2p-enabled node routes traffic over `i2p`. It is enabled by adding
-`i2p` to the `active_profiles` list. You can find a complete example config
-file for `darkirc-i2p.toml` in `${DARKFI_REPO}/bin/darkirc/config`. This node
-configuration is for users that would like to support `darkirc` over the I2p
-network.
-
-You need to configure I2p and launch your eepsite(hidden service) prior to running your
-public node over I2p. Please refer to
-[I2p Nodes](i2p-guide.md#inbound-node-settings).
-
-```toml
-## connection settings
-outbound_peer_discovery_cooloff_time = 60
-
-## Outbound connection slots
-outbound_connections = 8
-
-## Inbound connection slots
-inbound_connections = 64
-
-## Whitelisted transports for outbound connections
-active_profiles = ["i2p"]
+Remove the IPv6 listener if the host has no working IPv6 route. An
+`external_addrs` value must resolve to this host and be reachable from outside
+its LAN. Do not advertise `0.0.0.0`, `[::]`, loopback, or a private address.
 
-## I2p Socks5 proxy
-i2p_socks5_proxy = "socks5://127.0.0.1:4447"
-
-[net.profiles."i2p"]
-## Seed nodes to connect to
-seeds = [
-    "i2p://6l2rdfriixo2nh5pr5bt555lyz56qox2ikzia4kuzm4okje7gtmq.b32.i2p:9602"
-]
+The complete clearnet example is
+`bin/darkirc/config/darkirc-clearnet.toml`.
 
-## Addresses we want to advertise to peers
-external_addrs = ["i2p://youraddress.b32.i2p:9602"]
+## Multi-transport bridge
 
-## P2P accept addresses
-inbound = ["tcp://127.0.0.1:9602"]
+A bridge can accept both clearnet and Tor connections. Each inbound transport
+needs a reachable external address:
 
-## Connection settings with default values applicable for each profile separately
-#outbound_connect_timeout = 60
-#channel_handshake_timeout = 55
-#channel_heartbeat_interval = 90
-```
-
-### Bridge node
-
-A bridge node is a node that offers connectivity via multiple transport layers. 
-This provides the most benefit for the health of the network. This is the most 
-maximally compatible node for people that wish to support the network. It can
-be enabled by adding multiple profiles to the `active_profiles` list. You can
-find a complete example config file for `darkirc-mixed.toml` in 
-`${DARKFI_REPO}/bin/darkirc/config`. Refer to 
-[Tor Nodes](tor-guide.md#inbound-node-settings) to configure Tor.
-
-<!-- TODO: replace the i2p seed address with an official one-->
 ```toml
-## connection settings
-outbound_peer_discovery_cooloff_time = 60
-
-## Outbound connection slots
+[net]
+active_profiles = ["tcp+tls", "tor"]
 outbound_connections = 8
-
-## Inbound connection slots
 inbound_connections = 64
 
-## I2p Socks5 proxy
-i2p_socks5_proxy = "socks5://127.0.0.1:4447"
-
-## Whitelisted transports for outbound connections
-active_profiles = ["tcp+tls", "tor", "i2p"]
-
-## Transports to be mixed
-mixed_profiles = []
-
 [net.profiles."tcp+tls"]
-## Seed nodes to connect to
 seeds = ["tcp+tls://lilith0.dark.fi:9600", "tcp+tls://lilith1.dark.fi:9600"]
-
-## P2P accept addresses
-inbound = ["tcp+tls://0.0.0.0:9600", "tcp+tls://[::]:9600"]
-
-## Addresses we want to advertise to peers
-external_addrs = ["tcp+tls://MY_IP_V4:9600", "tcp+tls://MY_IP_V6:9600", "tcp+tls://my.resolveable.address:9600"]
+inbound = ["tcp+tls://0.0.0.0:9600"]
+external_addrs = ["tcp+tls://chat.example:9600"]
 
 [net.profiles."tor"]
-## Seed nodes to connect to
 seeds = [
-    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:9601",
-    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:9601",
+    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:9600",
+    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:9600",
 ]
-
-## P2P accept addresses
-inbound = ["tcp://127.0.0.1:9601"]
-
-## Addresses we want to advertise to peers
-external_addrs = ["tor://youraddress.onion:9601"]
-
-[net.profiles."i2p"]
-## Seed nodes to connect to
-seeds = [
-    ##TODO: replace with an official seed address
-    "i2p://6l2rdfriixo2nh5pr5bt555lyz56qox2ikzia4kuzm4okje7gtmq.b32.i2p:9602"
-]
-
-## Manual peers to connect to
-#peers = []
-
-## P2P accept addresses
-inbound = ["tcp://127.0.0.1:9602"]
-
-## Addresses we want to advertise to peers
-external_addrs = ["i2p://youraddress.b32.i2p:9602"]
+inbound = ["tor://127.0.0.1:9601"]
 ```
 
-### Autohost
+The `tor://` inbound form asks the built-in Arti transport to create an
+ephemeral onion service. Its onion address changes when the service is
+recreated and does not need a manual `external_addrs` entry. For a stable onion
+address, use the [static Tor setup](tor-guide.md#static-onion-service).
 
-#### Ipv6
-You can autohost a public `IPv6` node without manually specifying your external
-`IPv6` address. Your `Ipv6` address will be automatically detected and used to configure
-an inbound node. To enable this, edit the `inbound` and `external_addrs` fields
-in the `tcp+tls` profile as shown below.
+Set `mixed_profiles` only when deliberately routing one endpoint scheme through
+another active transport. It is not required merely to enable two profiles.
 
-```toml
-[net.profiles."tcp+tls"]
-## P2P accept addresses
-inbound = ["tcp+tls://[::]:9600"]
-
-## Addresses we want to advertise to peers
-external_addrs = ["tcp+tls://[::]:9600"]
+## UPnP IGD
 
-## Seed nodes to connect to
-seeds = ["tcp+tls://lilith0.dark.fi:9600", "tcp+tls://lilith1.dark.fi:9600"]
-```
-
-#### Tor
-You can autohost a public `Tor` node using `Arti` without manually specifying
-your external address. `Arti` will automatically create an `ephemeral` onion
-address and an inbound node will be setup at that address. To enable this,
-edit the `inbound` field in the `tor` as shown below.
-
-```toml
-[net.profiles."tor"]
-## P2P accept addresses
-inbound = ["tor://127.0.0.1:9601"]
+UPnP can request a router port mapping and discover the external address. It
+increases trust in the local router and must be enabled at compile time. Build
+with all features:
 
-## Addresses we want to advertise to peers
-external_addrs = []
-
-## Seed nodes to connect to
-seeds = [
-    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:9601",
-    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:9601",
-]
+```shell
+% cargo build --release --all-features --package darkirc --bin darkirc
 ```
 
-#### UPnP
-
-UPnP (Universal Plug and Play) automatically configures port forwarding
-on compatible routers using the UPnP IGD protocol. This is particularly
-useful for home users behind NAT who cannot manually configure port
-forwarding, or mobile devices which often roam between different networks.
-
-> Note:
->
-> UPnP IGD support must be enabled at compile time using the
-> `upnp-igd` feature flag. For example, when compiling `darkirc`:
->
-> ```shell
-> % cp bin/darkirc/Cargo.toml bin/darkirc/Cargo.toml.back
-> % sed -i -e 's|default = \[\]|default = ["upnp-igd"]|' bin/darkirc/Cargo.toml
-> % make darkirc
-> % mv bin/darkirc/Cargo.toml.back bin/darkirc/Cargo.toml
-> ```
+This writes `target/release/darkirc`; run or install that binary so the
+feature-enabled build is the one actually used.
 
-To enable UPnP IGD just add `?upnp_igd=true` to your inbound address.
-UPnP IGD will also auto-discover your external address.
+Then add the query to a clearnet listener:
 
 ```toml
 [net.profiles."tcp+tls"]
-## P2P accept addresses with UPnP enabled
 inbound = ["tcp+tls://0.0.0.0:9600?upnp_igd=true"]
-
-## Seed nodes to connect to
-seeds = ["tcp+tls://lilith1.dark.fi:9600"]
 ```
 
-DarkFi's UPnP implementation supports these optional configuration parameters:
-
-- `upnp_igd_lease_duration` - Port mapping lease duration in seconds
-- `upnp_igd_timeout` - Gateway discovery timeout in seconds
-- `upnp_igd_description` - Description shown in router admin panel
-- `upnp_igd_ext_addr_refresh` - External address refresh interval in seconds
-
-Example with custom options:
-
-```toml
-inbound = ["tcp+tls://0.0.0.0:9600?upnp_igd=true&upnp_igd_lease_duration=600&upnp_igd_description=MyDarkFiNode"]
-```
+Optional URL parameters include `upnp_igd_lease_duration`,
+`upnp_igd_timeout`, `upnp_igd_description`, and
+`upnp_igd_ext_addr_refresh`. Verify the mapping from another network; a log
+message or local router entry alone does not prove reachability.
 
-> Tip:
->
-> When running multiple applications or nodes, use different `upnp_igd_description`
-> values for each one. This makes it easier to identify them in your router's
-> admin panel.
+## Verify the node
 
-> Tip:
->
-> When possible verify that UPnP is enabled in your router's admin panel.
+Restart DarkIRC, wait for Event Graph sync, and inspect sessions with `dnet`
+after enabling `p2p.get_info` as described in the
+[dnet guide](../../learn/dchat/network-tools/using-dnet.md).
+Test every advertised URL from a separate network using the
+[ping utility](../network-troubleshooting.md#test-an-endpoint). An archive is
+useful to peers only while it remains reachable and retains complete message
+bodies (`fast_mode = false`).

+ 55 - 135
doc/src/misc/nodes/tor-guide.md

@@ -1,173 +1,93 @@
-# Set up a Tor-enabled node
+# Run a Tor-enabled DarkIRC node
 
-_To connect to Tor, we use [Arti](https://gitlab.torproject.org/tpo/core/arti). 
-Arti is an experimental project with incomplete security features.
-See Arti's [roadmap](https://gitlab.torproject.org/tpo/core/arti#roadmap)
-for more  information._
+DarkFi includes an Arti-based `tor` transport. It can make outbound onion
+connections without a separately running Tor daemon. Tor hides the clearnet
+address used to reach DarkIRC peers, subject to Tor's own threat model; it does
+not encrypt public channel contents or make chat metadata unlinkable.
 
-<u><b>Note</b></u>: This page is a general guide for `tor` nodes in the
-DarkFi ecosystem and is applicable to other apps such as `taud` and
-`darkfid`. We use `darkirc` as our main example throughout this guide.
-If you're using another app, the network configurations remain the same
-except for the seed nodes (ports) you connect to.
+## Outbound-only Tor node
 
-## Generating configuration files
-
-For configuration file generation, see [Common Setup Steps](public-guide.md#generating-configuration-files). 
-
-## Configure network settings
-
-Modify the network settings located in the `~/.config/darkfi` directory.
-This  configuration allows your node to send and receive traffic only
-via Tor.
-
-<u><b>Note</b></u>: As you modify the file, if you notice some settings
-are missing, simply add them. Some settings may be commented-out by
-default.
-
-### Outbound node settings
-
-These outbound node settings for your `tor` node configuration is only
-for connecting to the network. You will not advertise an external
-address.
+The generated DarkIRC configuration uses this mode by default:
 
 ```toml
-## connection settings
-outbound_peer_discovery_cooloff_time = 60
-
-## Outbound connection slots
-outbound_connections = 8
-
-## Whitelisted transports for outbound connections
+[net]
 active_profiles = ["tor"]
 
-## Transports to be mixed
-mixed_profiles = []
-
 [net.profiles."tor"]
-## Seed nodes to connect to
 seeds = [
-    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:9601",
-    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:9601",
+    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:9600",
+    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:9600",
 ]
 ```
 
-#### SOCKS5 proxy node settings
-If we want to route all our connections through the `SOCKS5` proxy
-provided by Tor, we can add the `socks5` and `socks5+tls` profiles
-to `active_profiles` and enable transport mixing by adding `tor` and
-`tcp+tls` to `mixed_profiles`. Enabling transport mixing helps us
-connect to `tor` and `tcp+tls` endpoints through our SOCKS5 proxy.
+No `inbound` or `external_addrs` setting is needed for an outbound-only node.
 
-When using `Whonix`, this configuration helps prevent the `Tor over
-Tor` issue. Ensure that the `tor_socks5_proxy` field is correctly set.
+## Inbound Tor node
 
-<u><b>Note</b></u>: With this setup, our node will connect to both Tor
-and clearnet nodes through the Socks5 proxy.
+Inbound service improves network capacity. Choose an ephemeral Arti service or
+a static onion service.
 
-```toml
-## Whitelisted transports for outbound connections
-active_profiles = ["socks5", "socks5+tls", "tcp+tls", "tor"]
-## Transports to be mixed
-mixed_profiles = ["tor", "tcp+tls"]
-## Tor Socks5 proxy
-tor_socks5_proxy = "socks5://127.0.0.1:9050"
-```
+### Ephemeral Arti service
 
-If you prefer to connect only to `tor` nodes, modify the above config
-like below:
+Add a `tor://` listener to the Tor profile:
 
 ```toml
-## Whitelisted transports for outbound connections
-active_profiles = ["socks5", "tor"]
-## Transports to be mixed
-mixed_profiles = ["tor"]
-```
-
-### Inbound node settings
-
-With these settings your node becomes a Tor inbound node. The `inbound` 
-settings are optional, but enabling them will increase the strength and 
-reliability of the network.
-
-There are currently two methods of doing this, both documented below.
-The Arti method allows you to create ephemeral onions that will
-change each time you restart your node. Alternatively you can make a
-non-ephemeral service using the torrc method. In this case the address
-always stays the same, which is useful for nodes such as seed nodes that
-need to be found on the same onion adddress.
-
-#### Using Arti
-
-We can use Arti to create an ephemeral onion on each startup that we
-will receive Inbound connections on. Set this in your config file with
-a port number of your choice:
-
-```
+[net.profiles."tor"]
 inbound = ["tor://127.0.0.1:9601"]
 ```
 
-On running your node, you should get a message like this:
-
-```
-[INFO] [P2P] Starting Inbound session #0 on tor://127.0.0.1:9601/
-```
-
-This means your ephemeral onion is active and awaiting connections. 
+Arti creates and advertises an ephemeral onion service. Do not add a guessed
+`external_addrs` entry. The onion identity is not intended to survive service
+recreation, so this mode is unsuitable for a stable seed address.
 
-#### Using torrc
+### Static onion service
 
-Alternatively, we can set up a static Tor daemon and create a hidden
-service.  The following instructions should work on any Linux system.
+For a persistent onion identity, install and configure a Tor daemon according
+to the current Tor documentation. Map public onion port 9600 to a loopback
+listener, for example in `torrc`:
 
-First, you must install [Tor](https://www.torproject.org/). It can
-usually be installed with your package manager. For example on an `apt`
-based system we can run:
-
-```
-% apt install tor
-```
-
-This will install Tor. Now in `/etc/tor/torrc` we can set up the hidden
-service. For hosting an anonymous `darkirc` node, set up the following
-lines in the file:
-
-```
+```text
 HiddenServiceDir /var/lib/tor/darkfi_darkirc
-HiddenServicePort 9601 127.0.0.1:9601
+HiddenServicePort 9600 127.0.0.1:9601
 ```
 
-Then restart Tor:
-
-```
-% /etc/init.d/tor restart
-```
-
-Find the hostname of your hidden service from the directory:
-
-```
-% cat /var/lib/tor/darkfi_darkirc/hostname
-```
-
-Note your `.onion` address and the ports you used while setting up the
-hidden service, and add the following settings to your configuration file:
+After Tor creates the service, read its hostname using the permissions of the
+Tor service account. Configure DarkIRC to accept the forwarded TCP connection
+and advertise the onion address:
 
 ```toml
-## Inbound connection slots
+[net]
+active_profiles = ["tor"]
 inbound_connections = 64
 
 [net.profiles."tor"]
-## Addresses we want to advertise to peers
-external_addrs = ["tor://youraddress.onion:9601"]
-
-## P2P accept addresses
 inbound = ["tcp://127.0.0.1:9601"]
+external_addrs = ["tor://YOUR_ONION_ADDRESS.onion:9600"]
 ```
 
-## Connect and test your node
+Back up the Tor hidden-service keys securely if the stable onion identity must
+survive migration.
+
+## Use an external Tor SOCKS5 proxy
+
+This is an alternative to direct Arti onion dialing. Put the proxy address in
+each explicit SOCKS endpoint:
 
-See [Common Setup Steps → Connect and test your node](public-guide.md#connect-and-test-your-node).
+```toml
+[net]
+active_profiles = ["socks5"]
+
+[net.profiles."socks5"]
+seeds = [
+    "socks5://127.0.0.1:9050/g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:9600",
+    "socks5://127.0.0.1:9050/yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:9600",
+]
+```
 
-## Troubleshooting
+Confirm the proxy is listening before starting DarkIRC. Avoid accidentally
+wrapping a Tor connection in another Tor connection when the host environment
+already transparently routes traffic through Tor.
 
-See [Common Setup Steps → Troubleshooting](public-guide.md#troubleshooting).
+Restart DarkIRC after profile changes, wait for DAG sync, and use the
+[troubleshooting guide](../network-troubleshooting.md#tor-connections) if onion
+connections fail.

+ 13 - 11
doc/src/start-here.md

@@ -23,16 +23,17 @@ DarkFi loosely follows the standardized Unix directory structure.
 
 ## Using DarkFi
 
-Refer to the main [README](../index.html) file for instructions on how
+Refer to the main [README](README.md) file for instructions on how
 to install Rust and necessary dependencies.
 
 Then proceed to the [Running a Node](testnet/node.md) guide.
 
 ## Join the Community
 
-Although we have a Telegram, we don't believe in centralized
-proprietary apps, and our core community organizes through our own
-fully anonymous p2p chat system which has support for Tor and i2p.
+Our core community organizes through DarkIRC, our peer-to-peer chat daemon.
+DarkIRC supports multiple transports, including Tor and I2P. Public channels
+remain public, and the privacy properties depend on the configured transport
+and message-encryption settings.
 
 Every Monday at 14:00 UTC (DST) or 15:00 UTC (ST) in #dev we have our
 main project meeting.
@@ -63,8 +64,9 @@ Source code is under `src/` subdirectory. Main interesting modules are:
 * `net/` is our own p2p network. There are sessions such as incoming or
   outgoing that have channels (connections). Protocols are attached to
   channels depending on the session. The p2p network is also
-  multi-transport with support for TCP (+TLS), Tor and i2p. So you can
-  access the p2p fully anonymously (network level privacy).
+  multi-transport with support for TCP (+TLS), Tor and I2P. An anonymity
+  transport can hide a node's clearnet address from application peers, subject
+  to that transport's threat model.
 * `event_graph/` which is a DAG sync protocol used for ensuring
   eventual consistency of data, such as with chat systems (you don't
   drop any messages).
@@ -124,11 +126,11 @@ Inside `bin/` contains utilities and applications:
 * `tau/` is an anon p2p task manager which we use. We don't use Github
   issues, and seek to minimize our dependence on centralized services.
   Eventually we want to be fully p2p and attack resistant.
-* `darkirc/` is our main community chat. It uses [RLN](crypto/rln.md);
-  you stake money and if you post twice in an epoch then you get
-  slashed which prevents spam. There is a free tier. It uses the
-  `event_graph` for synchronizing the history. You can attach any IRC
-  frontend to use it.
+* `darkirc/` is our main community chat. It uses `event_graph` to synchronize
+  hourly message DAGs and exposes a local IRC interface. Optional
+  [RLN](crypto/rln.md) rate limiting exists but is disabled by default; the
+  current implementation uses compiled pregenerated identities rather than a
+  public staking/free-tier workflow.
 * `zkas/` is our ZK compiler.
 * `zkrunner/` contains our ZK debugger (run `zkrunner` with `--trace`),
   and `zkrender` which renders a graphic of the circuit layout.

+ 1 - 1
src/event_graph/deg.rs

@@ -17,7 +17,7 @@
  */
 
 //! Debug Event Graph (DEG) types for inspecting protocol-level
-//! message flow. When DEG is enabled on an [`EventGraph`] instance,
+//! message flow. When DEG is enabled on an [`crate::event_graph::EventGraph`] instance,
 //! every sent/received P2P message produces a [`DegEvent`] that can
 //! be observed through the DEG publisher.
 

+ 4 - 4
src/event_graph/mod.rs

@@ -572,7 +572,7 @@ pub(crate) fn filter_requested_event_rep(
     let requested_set: HashSet<blake3::Hash> = requested.iter().copied().collect();
     let mut by_id = HashMap::with_capacity(events.len());
 
-    for (event, blob) in events.into_iter().zip(blobs.into_iter()) {
+    for (event, blob) in events.into_iter().zip(blobs) {
         let event_id = event.id();
         if !requested_set.contains(&event_id) || by_id.insert(event_id, (event, blob)).is_some() {
             return Err(Error::DagSyncFailed)
@@ -1147,7 +1147,7 @@ impl EventGraph {
     }
 
     /// After header sync, event content can be fetched lazily via local
-    /// [`fetch_page`] or peer [`RangeReq`] responses with aligned blobs - the
+    /// [`Self::fetch_page`] or peer [`RangeReq`] responses with aligned blobs - the
     /// application pulls the events it actually wants to display or process,
     /// without downloading the entire content on every sync.
     pub async fn dag_sync_headers(&self, dag_ts: u64) -> Result<()> {
@@ -1379,7 +1379,7 @@ impl EventGraph {
     /// Sync only headers for the `count` most recent DAGs.
     ///
     /// Fast variant - gives a full DAG skeleton without downloading
-    /// event bodies. Pair with [`fetch_page`] to pull content on-demand.
+    /// event bodies. Pair with [`Self::fetch_page`] to pull content on-demand.
     pub async fn sync_selected_headers(&self, count: usize) -> Result<()> {
         let ts: Vec<u64> =
             self.dag_store.read().await.dag_timestamps().into_iter().rev().take(count).collect();
@@ -1946,7 +1946,7 @@ impl EventGraph {
         {
             let store = self.dag_store.read().await;
             let slot = store.get_slot(&dag_ts).ok_or(Error::DagSyncFailed)?;
-            for (event, blob) in events.into_iter().zip(blobs.into_iter()) {
+            for (event, blob) in events.into_iter().zip(blobs) {
                 if event.header.parents == NULL_PARENTS {
                     continue
                 }

+ 4 - 4
src/event_graph/proto.rs

@@ -308,7 +308,7 @@ impl RangeCursor {
 
 /// Bidirectional content pagination request.
 ///
-/// The responder uses its [`TimeIndex`] to serve events after the exclusive
+/// The responder uses its [`crate::event_graph::TimeIndex`] to serve events after the exclusive
 /// cursor in the requested direction, up to `limit` events. This is the
 /// primary message for lazy content fetching - the requester already has
 /// headers (DAG structure) and wants bodies.
@@ -344,7 +344,7 @@ impl_p2p_message!(RangeRep, "EventGraph::RangeRep", 0, 0, DEFAULT_METERING_CONFI
 ///
 /// * `handle_event_put` - real-time ingestion of new events,
 ///   including RLN proof verification and recursive parent fetching
-///   (bounded by [`MAX_PARENT_FETCH_DEPTH`]).
+///   (bounded by `MAX_PARENT_FETCH_DEPTH`).
 /// * `handle_static_put` - RLN registration and slashing events.
 /// * `handle_event_req` - serving event content to peers (only
 ///   for IDs we've previously broadcast, to prevent DAG enumeration).
@@ -356,7 +356,7 @@ impl_p2p_message!(RangeRep, "EventGraph::RangeRep", 0, 0, DEFAULT_METERING_CONFI
 ///   relay through a bounded channel with adaptive sleep.
 ///
 /// RLN share metadata is **not** stored on this struct - it lives on
-/// [`EventGraph::rln_state`] so that duplicate/reuse detection works
+/// [`crate::event_graph::EventGraph::rln_state`] so that duplicate/reuse detection works
 /// across all peer connections, not just the one that relayed a
 /// particular event.
 pub struct ProtocolEventGraph {
@@ -712,7 +712,7 @@ impl ProtocolEventGraph {
     /// should be rejected (proof invalid, duplicate, or slashable).
     ///
     /// The actual verification logic lives on
-    /// [`EventGraph::rln_verify_signal`] - this method is a thin
+    /// [`crate::event_graph::EventGraph::rln_verify_signal`] - this method is a thin
     /// wrapper that translates the [`rln::SignalCheck`] outcome
     /// into "accept or reject" plus the slash side effect.
     async fn verify_rln_signal(&self, event: &Event, blob: &[u8]) -> bool {

+ 3 - 3
src/event_graph/rln.rs

@@ -190,7 +190,7 @@ pub struct SlashBlob {
     pub merkle_root: pallas::Base,
 }
 
-/// Ephemeral data attached to an [`EventPut`] when RLN is active.
+/// Ephemeral data attached to an [`crate::event_graph::proto::EventPut`] when RLN is active.
 #[derive(Clone, SerialEncodable, SerialDecodable)]
 pub struct Blob {
     /// The RLN signal proof.
@@ -513,7 +513,7 @@ impl Default for RlnState {
     }
 }
 
-/// Outcome of [`EventGraph::rln_verify_signal`].
+/// Outcome of [`crate::event_graph::EventGraph::rln_verify_signal`].
 #[derive(Debug)]
 pub enum SignalCheck {
     /// Proof valid, no conflict; the share has been recorded.
@@ -533,7 +533,7 @@ pub enum SignalCheck {
     Slashable(Vec<(pallas::Base, pallas::Base)>),
 }
 
-/// Outcome of [`EventGraph::rln_verify_static_event`].
+/// Outcome of [`crate::event_graph::EventGraph::rln_verify_static_event`].
 ///
 /// Distinguishes "drop silently" (e.g. propagation race, unknown
 /// root) from "the sender is malicious" (e.g. attestation didn't

+ 3 - 1
src/event_graph/util.rs

@@ -124,7 +124,9 @@ pub fn generate_genesis(config: &EventGraphConfig) -> Result<Event> {
         config.initial_genesis
     } else {
         let passed = hours_since(config.initial_genesis)?;
-        let rotations = passed / config.hours_rotation;
+        let rotations = passed
+            .checked_div(config.hours_rotation)
+            .ok_or_else(|| Error::Custom("event graph rotation period cannot be 0".into()))?;
         let offset_hours = rotations.saturating_mul(config.hours_rotation);
         let offset_ms = offset_hours.saturating_mul(HOUR_MS);
         config.initial_genesis.saturating_add(offset_ms)