Skip to content

DNS block/ban list contents #1242

@Rucknium

Description

@Rucknium

Proposal

I suggest that the DNS block/ban list, hosted in the TXT DNS records at

blocklist.moneropulse.se
blocklist.moneropulse.org
blocklist.moneropulse.net
blocklist.moneropulse.no
blocklist.moneropulse.fr
blocklist.moneropulse.de
blocklist.moneropulse.ch

be changed to include these IP address entries:

100.42.27.0/24
193.142.4.0/24
23.92.36.0/26
138.201.50.11

Since the DNS TXT records are at their maximum allowable length, four of the existing IP addresses would need to be removed to make room for the new entries. I don't have an opinion about which ones are best to remove, except the /24 subnet entries already on the list should not be removed. These entries are also on the MRL ban list. They are:

91.198.115.0/24
162.218.65.0/24
199.116.84.0/24
209.222.252.0/24

Reasoning

The change would (1) enable a ban of many suspected spy nodes (2) by many honest nodes (3) with minimum impact to the current DNS ban list.

(1) enable a ban of many suspected spy nodes

The Monero Research Lab (MRL) has recommended that all Monero node operators enable a spy node ban list to improve user privacy. The ban list is voluntary and requires node operators to execute a multi-step procedure. At the time of writing this issue, over six months has elapsed since it was announced, yet only about 7 percent of honest reachable nodes on the Monero network seem to have enabled it. Adding the MRL ban list's major subnets to the DNS ban list would improve privacy.

The DNS ban list already has four of the MRL ban list's /24 subnets:

91.198.115.0/24
162.218.65.0/24
199.116.84.0/24
209.222.252.0/24

The DNS ban list does not yet have the other two /24 subnets on the MRL ban list: 100.42.27.0/24 and 193.142.4.0/24. The MRL ban list also has a set of 60 singleton IP addresses that covers the /26 (twenty-six!) subnet of 23.92.36.0. They can be concisely added to the DNS ban list by adding this entry: 23.92.36.0/26.

/24 and /26 subnets have 254 and 62 usable IP addresses, respectively. Therefore, adding 100.42.27.0/24, 193.142.4.0/24 and 23.92.36.0/26 will add 570 total spy node IP addresses to the DNS ban list.

(2) by many honest nodes

The share of honest nodes that have enabled the DNS ban list by specifying the --enable-dns-blocklist node startup flag can be estimated by analyzing the peer lists disseminated by each node during a Levin protocol handshake. If a node has been running for a while, it should have the maximum 1,000 peers in its white_list. From the white_list, 250 peers are drawn randomly to share with a peer that requests a handshake. (Fewer than 250 will be drawn if the white_list has fewer than 250 peers.)

I will show that a node that disseminates zero peers on the DNS ban list in a Levin handshake is very likely to have actually enabled the DNS ban list (or, at least, to have enabled a ban list that is a superset or a near-superset of the DNS ban list.) Therefore, a reliable estimate of the number of honest reachable nodes that have enabled the DNS ban list is simply a count of the number of honest reachable nodes that disseminate zero peers on the DNS ban list.

In ideal conditions, the number of peers in a specified IP list that is disseminated during a handshake will follow a hypergeometric distribution because the node is drawing two different kind of elements from a finite set without replacement. $N$, the total number of elements to draw from, is 1,000. $n$, the number of elements to draw, is 250. $K$, the number of IP addresses on the specified list that are in the white_list, needs to be estimated from the data. Given that $N$ and $n$ are known, a simple method-of-moments estimator of $K$ is adequate.

The plot below shows the empirical distribution of the number of peers on the DNS ban list disseminated by each honest node in its Levin handshake, as a histogram. (Note that all of the IP addresses counted here are in the four /24 subnets that the DNS ban list has in common with the MRL ban list.) Overlayed is a theoretical hypergeometric distribution, as a red line. The empirical distribution has greater variance than the theoretical distribution. The discrepancy could be explained by the complicated meta process at work: peers must first enter (and exit) the white_list. Also, not all nodes have a completely full white_list. Therefore, $N$ is not exactly equal to 1,000.

disseminated-peers-DNS-ban-list_2025-07-21.png

Nevertheless, both the theoretical and empirical plots suggest that there is a very low probability that a node that has not enabled the ban list will disseminate zero ban-list IP addresses in a Levin handshake.

According to a network scan performed on 2025-07-21, about 54 percent of honest nodes have enabled the DNS ban list. Only 7 percent of honest nodes have enabled the MRL ban list. Adding more spy node IP addresses to the DNS ban list would make a significant positive impact on Monero's network-level privacy. The high share of nodes may have enabled the DNS ban list because @sethforprivacy recommends it in his monero node setup guides.

(3) with minimum impact to the current DNS ban list

According to @selsta , most of the IP addresses currently on the DNS ban list were involved in the 2020 attacks on the Monero network. According to data available to me, these nodes seemed to have disappeared from the network some time between May 2024 and July 2025.

I helped collect node log data on fluff-phase transaction relay behavior from about ten nodes between April 14, 2024 and May 23, 2024. The data is described in Section 9 of my "March 2024 Suspected Black Marble Flooding Against Monero: Privacy, User Experience, and Countermeasures". In these logs, almost all of the singleton IP addresses in the ban list (i.e. the entries that are not the /24 subnets that are also on the MRL ban list) appear as both inbound and outbound connections of the logging nodes.

In my recent active network scans of all reachable nodes on the network, none of these singleton IP addresses appear. It is possible that the nodes are still active but "hiding"from a network scan by no longer accepting inbound connections.

However, in the recent network scans I do see one node that is disseminating the IP addresses of these apparently dead nodes, which brings me to my final suggested new entry for the DNS ban list: 138.201.50.11. This IP address, which is associated with Hetzner hosting, disseminates about 65 of the "dead" node IP addresses on the DNS ban list during its Levin handshake. It is possibly a remnant of the infrastructure of the 2020 network attacker. During each daily network scan, I also see 0 to 4 other nodes disseminating a smaller number of these dead node IP addresses, but usually they are not the same nodes from day to day. It is possble that these are honest nodes that are unknowingly re-disseminating the peer lists that 138.201.50.11 has disseminated. However, according to my understanding of Monero's network protocol, a node using the default configuration should not insert peer addresses into its white_list that it personally has not verified is reachable. It is unclear to me what is causing these observations in the network scan data. 138.201.50.11 is the only node consistently disseminating the dead nodes' IP addresses, so it should be placed on the DNS ban list as a precaution in case the old attacking nodes reappear.

The future of the DNS ban list

The number of entries in the DNS ban list is limited by the total allowable size of a TXT DNS record. The number of entries could be increased by modifying the TXT records and the monerod to:

  1. express the entries in a shorter number of bytes, e.g. using integer notation or file compression, and/or

  2. put different entries in the 7 domains' DNS records instead of duplicating the same set across all 7 domains. (However, doing this would run the risk of nodes missing entries if one or more of the 7 domains' DNS records are not accessible.)

It could make sense to implement this change during a hard fork boundary because all nodes would have to update to the latest monerod version.

Now that we know that a majority of honest reachable nodes have enabled the DNS ban list, it is important to review the security measures in place to prevent the hijacking of the DNS ban list to do damage to the network. An adversary who gains control of the DNS records could instruct the nodes to block all other nodes by inserting large subnet entries into the DNS record, according to my understanding. That action would take the nodes offline and possibly enable eclipse or network partitioning attacks. The DNSSEC procedures for themoneropulse domains should be reviewed for possible vulnerabilities.

Steps to reproduce the analysis

Expand the collapsed section by clicking on it:

Steps to reproduce the analysis

You must build the monero-network-crawler binary. You can install the xmrnetscan R package or you can build the Rust binary directly. If you build directly and use Linux, you will probably need to install the libsqlite3-dev system package:

sudo apt-get install libsqlite3-dev

If you choose install the R package, follow the installation instructions here. Then discover the filepath of the installed binary by inputting this into the R terminal:

system.file("bin", "monero-network-crawler", package = "xmrnetscan")

Once monero-network-crawler is built, you will have to run it for about three hours to complete the network scan. You must use the --collect-peer-lists flag to collect the right data:

./monero-network-crawler --collect-peer-lists

For best compatibility with the next step of the analysis, run the binary in a new empty directory. You can run the network scan multiple times if you wish, each time creating a new empty directory within a common parent directory.

Wait until the program stops printing statements to the terminal. Then, halt the program with ctrl+c. The program should have created three files in the directory: crawler-netscan.db (a SQLite database file), bad_peers.txt, and good_peers.txt.

If you built the Rust binary directly instead of installing the xmrnetscan R package, you will now need to install the xmrpeers R package to perform the analysis.

Run the R code below from the parent directory of the directory(ies) that have the database file. If you want to directly paste into a terminal instead of using an R IDE, it is best to first save the script as a file, e.g. network-scan-analysis.R and run it in the R terminal by inputting source("network-scan-analysis.R").

The analysis results should be printed to the console.

# Working directory should be parent directory of the directory(ies)
# that contain the data files (crawler-netscan.db, bad_peers.txt, and good_peers.txt)

dirs <- sort(setdiff(list.dirs(full.names = FALSE), ""))

library(data.table)
library(xmrpeers)

for (dir in dirs) {
  
  cat("Analyzing data in ", dir, "\n", sep = "")
  
  data(ban_list)
  # MRL ban list from xmrpeers package
  
  
  # DNS blocklist can be obtained from:
  # Using the dig utoility on Linux machines:
  # dig @1.0.0.1 blocklist.moneropulse.se. TXT
  # or:
  # https://www.nslookup.io/domains/blocklist.moneropulse.se/dns-records/
  
  dns.blocklist <- c("23.88.126.42;23.88.34.81;23.88.34.86;23.88.35.29;23.88.36.64;23.88.37.110;23.88.38.196;23.88.39.157;23.88.40.186;23.88.40.60;23.88.41.0;23.88.41.111;23.88.42.188;23.88.43.143;49.12.10.221",
    "49.12.228.133;49.12.228.14;49.12.228.5;49.12.228.6;49.12.228.7;49.12.228.8;49.12.228.9;49.12.239.116;49.12.239.155;49.12.239.156;49.12.239.157;49.12.239.158;49.12.239.159;49.12.239.160;49.12.239.161",
    "159.65.28.9;159.65.82.164;161.35.59.213;161.35.88.140;161.35.90.212;162.218.65.0/24;165.22.10.49;165.22.10.5;165.22.12.133;165.22.15.144;165.22.2.129;165.22.2.201;165.22.2.255;165.22.2.48;165.22.8.167",
    "23.88.124.126;23.88.124.135;23.88.124.161;23.88.124.171;23.88.124.173;23.88.124.246;23.88.124.94;23.88.125.242;23.88.125.254;23.88.125.49;23.88.125.83;23.88.126.104;23.88.126.19;23.88.126.25;23.88.126.26",
    "65.108.49.24;65.108.49.82;65.108.50.7;65.108.51.229;65.108.56.225;65.108.58.135;65.108.60.175;65.108.61.145;65.108.80.101;65.108.80.216;65.108.81.118;65.108.81.124;65.108.81.125;65.108.83.46;65.108.85.204",
    "65.108.90.194;65.108.90.196;65.108.93.105;65.108.93.131;65.108.93.73;65.21.186.59;68.183.111.36;68.183.16.126;68.183.96.163;68.183.96.21;68.183.96.59;78.46.142.161;78.46.147.229;78.46.172.253;78.46.182.24",
    "78.46.186.96;78.46.188.228;78.46.188.82;78.46.189.158;78.46.189.74;78.46.190.238;78.46.191.8;78.46.192.174;78.46.192.204;78.47.43.59;78.47.45.21;91.198.115.0/24;95.216.136.170;95.216.138.15;95.216.139.114",
    "49.12.109.154;49.12.221.137;49.12.221.3;49.12.221.73;49.12.227.222;49.12.227.223;49.12.227.224;49.12.227.225;49.12.227.226;49.12.227.227;49.12.227.228;49.12.227.229;49.12.227.230;49.12.227.231;49.12.228.10",
    "49.12.228.11;49.12.228.12;49.12.228.120;49.12.228.122;49.12.228.123;49.12.228.124;49.12.228.125;49.12.228.126;49.12.228.127;49.12.228.128;49.12.228.129;49.12.228.13;49.12.228.130;49.12.228.131;49.12.228.132",
    "49.12.239.177;49.12.239.178;49.12.239.179;49.12.239.180;49.12.239.181;49.12.239.182;49.12.239.183;49.12.239.184;49.12.239.185;49.12.239.186;64.225.66.253;64.225.70.236;64.227.64.156;64.227.66.185;65.108.48.41",
    "49.12.239.162;49.12.239.163;49.12.239.164;49.12.239.165;49.12.239.166;49.12.239.167;49.12.239.168;49.12.239.169;49.12.239.170;49.12.239.171;49.12.239.172;49.12.239.173;49.12.239.174;49.12.239.175;49.12.239.176",
    "167.172.94.47;167.71.220.59;167.99.33.107;174.138.10.123;174.138.3.164;174.138.45.150;178.128.216.2;178.62.209.127;178.62.25.68;178.62.9.149;188.166.103.192;188.166.112.13;188.166.116.64;188.166.186.6;188.166.240.25",
    "188.166.36.103;199.116.84.0/24;206.189.9.131;209.222.252.0/24;209.97.135.247;209.97.185.45;23.88.113.138;23.88.118.41;23.88.119.226;23.88.120.188;23.88.121.112;23.88.122.101;23.88.122.248;23.88.123.202;23.88.123.242",
    "95.216.139.44;95.216.140.48;95.216.187.176;95.216.189.202;95.216.189.35;95.216.189.86;95.216.189.87;95.216.189.98;95.216.190.212;95.216.199.160;95.216.199.217;95.216.200.97;95.216.201.10;95.216.202.32;95.216.203.255",
    "135.181.86.200;135.181.86.201;135.181.86.204;135.181.86.208;135.181.86.255;135.181.86.63;135.181.86.88;135.181.86.95;135.181.86.98;135.181.87.18;135.181.87.25;138.201.191.218;138.201.191.237;138.201.191.244;138.201.191.51",
    "104.248.206.131;104.248.231.61;116.203.249.163;128.199.45.242;134.122.50.39;134.122.61.72;134.209.16.242;134.209.16.244;134.209.19.30;134.209.24.146;134.209.31.107;134.209.31.128;134.209.31.191;134.209.31.237;134.209.98.94",
    "135.181.86.105;135.181.86.113;135.181.86.114;135.181.86.127;135.181.86.146;135.181.86.148;135.181.86.158;135.181.86.164;135.181.86.167;135.181.86.178;135.181.86.184;135.181.86.187;135.181.86.188;135.181.86.193;135.181.86.198",
    "143.244.143.183;143.244.143.184;143.244.143.185;143.244.143.186;143.244.143.187;143.244.143.189;143.244.163.97;147.182.160.251;157.245.193.97;157.245.51.61;157.245.62.247;157.245.62.99;157.245.63.120;157.245.77.11;159.65.28.120",
    "165.232.177.22;165.232.180.169;165.232.181.14;165.232.181.42;165.232.185.205;165.232.190.122;165.232.190.183;165.232.190.251;167.172.71.178;167.172.72.198;167.172.82.181;167.172.82.200;167.172.82.213;167.172.90.178;167.172.90.70",
    "138.201.244.104;138.201.244.130;138.201.244.138;138.201.244.148;138.201.244.163;138.68.161.191;138.68.168.34;139.59.116.122;139.59.183.149;142.93.48.86;143.110.255.50;143.244.143.172;143.244.143.174;143.244.143.177;143.244.143.182")
  
  dns.blocklist <- unlist(strsplit(dns.blocklist, ";"))
  
  if ( ! file.exists(paste0(dir, "/crawler-netscan.db"))) {
    cat("\nSKIPPING DIR BECAUSE THERE IS NO crawler-netscan.db FILE: ", dir, "\n\n", sep = "")
    next
  }
  
  
  con <- DBI::dbConnect(RSQLite::SQLite(), paste0(dir, "/crawler-netscan.db"))
  
  peerlists <- DBI::dbGetQuery(con, "SELECT * FROM peerlists")
  
  data.table::setDT(peerlists)
  
  
  peerlists[, connected_node := gsub("(KnownAddr[(])|([)])", "", connected_node)]
  
  peerlists[, peerlist := gsub("(^[[])|([]]$)", "", peerlist)]
  
  split.peerlists <- strsplit(peerlists$peerlist, ", ")
  split.peerlists <- lapply(split.peerlists, function(x) {
    if (length(x) == 0) { x <- "127.0.0.1" } # Add so that peer list is not empty
    gsub("[:][0-9]+$", "", x) # Remove port
  })
  
  disseminated.peerlists <- setdiff(unique(unlist(split.peerlists)), "127.0.0.1")
  disseminated.peerlists <- disseminated.peerlists[ !is.na(IP::ipv4(disseminated.peerlists))]
  # Keep only IPv4 addresses
  
  mrl.ban.list.peers <- disseminated.peerlists[xmrpeers::in.ip.set(disseminated.peerlists, ban_list)]
  dns.ban.list.peers <- disseminated.peerlists[xmrpeers::in.ip.set(disseminated.peerlists, dns.blocklist)]
  
  banlist.status <- lapply(split.peerlists, function(x) {
    
    n_disseminated_peers_on_mrl_ban_list <- sum(x %in% mrl.ban.list.peers)
    n_disseminated_peers_on_dns_ban_list <- sum(x %in% dns.ban.list.peers)
    
    mrl_ban_list_enabled <- length(x) >= 200 & n_disseminated_peers_on_mrl_ban_list == 0
    dns_ban_list_enabled <- length(x) >= 200 & n_disseminated_peers_on_dns_ban_list == 0
    # Must have disseminated a peer list with 200 or more peers (there are 
    # 250 usually) or else there is greater risk of false positive
    
    data.table(
      n_disseminated_peers_on_mrl_ban_list,
      n_disseminated_peers_on_dns_ban_list,
      mrl_ban_list_enabled,
      dns_ban_list_enabled
    )
  })
  
  banlist.status <- cbind(
    connected_node = peerlists$connected_node,
    data.table::rbindlist(banlist.status)
  )
  
  
  banlist.status[, connected_node_ip := stringr::str_extract(connected_node,
    "[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}")]
  
  banlist.status.by.ip <- banlist.status[, .(
    mrl_ban_list_enabled = all(mrl_ban_list_enabled),
    dns_ban_list_enabled = all(dns_ban_list_enabled)
  ), by = "connected_node_ip"]
  
  cat("Percentage of honest reachable nodes that have the MRL ban list enabled:\n")
  print(
    100 * prop.table(table(banlist.status.by.ip[
      ! connected_node_ip %in% mrl.ban.list.peers, mrl_ban_list_enabled]))
  )
  
  cat("Percentage of honest reachable nodes that have the DNS ban list enabled:\n")
  print(
    100 * prop.table(table(banlist.status.by.ip[
      ! connected_node_ip %in% mrl.ban.list.peers, dns_ban_list_enabled]))
  )
  
  
  # This is the set IP addresses that tha MRL ban list and DNS ban list
  # have in common.
  # Note that this doesn't handle partial overlap of subnets. It
  # is just the literal intersection of the string representation of
  # the ban list items. There are no partial overlaps as
  # of writing this comment.
  shared.banlist <- intersect(ban_list, dns.blocklist)
  
  stopifnot(identical(
    sort(shared.banlist),
    sort(c("91.198.115.0/24", "162.218.65.0/24", "199.116.84.0/24", "209.222.252.0/24"))
  ))
  
  cat("Number of nodes with IP addresses that are on the DNS block list, excluding the IP addresses that are on both the DNS and MRL ban list:\n")
  print(
    table(banlist.status.by.ip[, xmrpeers::in.ip.set(connected_node_ip, dns.blocklist) &
        ! xmrpeers::in.ip.set(connected_node_ip, shared.banlist)])
  )
  
  
  dns.ban.list.peers.not.shared <- disseminated.peerlists[
    xmrpeers::in.ip.set(disseminated.peerlists, dns.blocklist) &
      ! xmrpeers::in.ip.set(disseminated.peerlists, shared.banlist)]
  
  
  banlist.status.not.shared <- lapply(split.peerlists, function(x) {
    
    n_disseminated_peers_on_mrl_ban_list <- sum(x %in% mrl.ban.list.peers)
    n_disseminated_peers_on_dns_ban_list <- sum(x %in% dns.ban.list.peers)
    n_disseminated_peers_on_dns_ban_list.not.shared <- sum(x %in% dns.ban.list.peers.not.shared)
    
    mrl_ban_list_enabled <- length(x) >= 200 & n_disseminated_peers_on_mrl_ban_list == 0
    dns_ban_list_enabled <- length(x) >= 200 & n_disseminated_peers_on_dns_ban_list == 0
    dns_ban_list_enabled.not.shared <- length(x) >= 200 & n_disseminated_peers_on_dns_ban_list.not.shared == 0
    # Must have disseminated a peer list with 200 or more peers (there are 
    # 250 usually) or else there is greater risk of false positive
    
    data.table(
      n_disseminated_peers_on_mrl_ban_list,
      n_disseminated_peers_on_dns_ban_list,
      n_disseminated_peers_on_dns_ban_list.not.shared,
      mrl_ban_list_enabled,
      dns_ban_list_enabled,
      dns_ban_list_enabled.not.shared
    )
  })
  
  
  banlist.status.not.shared <- data.table::rbindlist(banlist.status.not.shared)
  
  table(banlist.status.not.shared$n_disseminated_peers_on_dns_ban_list.not.shared > 0 )
  
  
  banlist.status.not.shared[ n_disseminated_peers_on_dns_ban_list.not.shared > 0, ]
  
  # Note: it is assumed that these two datasets have remained ordered correctly
  dns.banlist.disseminating.nodes <- peerlists$connected_node[
    banlist.status.not.shared$n_disseminated_peers_on_dns_ban_list.not.shared > 0]
  
  dns.banlist.disseminating.nodes.quantity <- banlist.status.not.shared[
    n_disseminated_peers_on_dns_ban_list.not.shared > 0, 
    n_disseminated_peers_on_dns_ban_list.not.shared]
  
  cat("Nodes disseminating DNS (minus MRL) ban list IP addresses, if any:\n")
  
  print(data.table(
    IP_address = dns.banlist.disseminating.nodes,
    N_DNS_ban_list_peers_disseminated = dns.banlist.disseminating.nodes.quantity
  ))
  
  cat("Note: Maximum of N_DNS_ban_list_peers_disseminated is 250 because a node disseminates 250 peers in its white_list during a Levin handshake.\n\n")
  
  
  png(paste0("disseminated-peers-DNS-ban-list_", dir, ".png"), width = 600, height = 600)
  
  dns.banlist.disseminating.nodes.ips <- 
    stringr::str_extract(dns.banlist.disseminating.nodes, "[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}")
  
  n_disseminated_peers_on_dns_ban_list.for.plotting <- 
    banlist.status[ n_disseminated_peers_on_dns_ban_list > 0 &
        (! connected_node_ip %in% c(mrl.ban.list.peers, dns.banlist.disseminating.nodes.ips)), 
      n_disseminated_peers_on_dns_ban_list]
  
  hist(n_disseminated_peers_on_dns_ban_list.for.plotting, freq = FALSE,
    breaks = 50, ylim = c(0, 0.1),
    main = paste0("Disseminated peers on DNS ban list,\nnonzero values from honest nodes only (", dir, ")"),
    xlab = "Number of disseminated peers on DNS ban list")
  
  K_hat <- floor(mean(n_disseminated_peers_on_dns_ban_list.for.plotting) * (1000/250))
  # Using notation/parameterization from https://en.wikipedia.org/wiki/Hypergeometric_distribution
  
  # white_list has 1000 peers by default. Levin handshake disseminates 250
  # peers from the white_list.
  hypergeometric.theoretical <- dhyper(
    0:max(n_disseminated_peers_on_dns_ban_list.for.plotting),
    m = K_hat,
    n = 1000 - K_hat,
    k = 250)
  
  lines(0:max(n_disseminated_peers_on_dns_ban_list.for.plotting),
    hypergeometric.theoretical, col = "red")
  
  legend("topright", legend = c("Empirical distribution", 
    substitute(paste("Hypergeometric distribution: N = 1000, n = 250, ", hat(K), " = ", K_hat), list(K_hat = K_hat))),
    fill = c("gray", NA), lty = c(NA, 1), col = c("black", "red"), border = c("black", NA))
  
  dev.off()
  
  cat("Plot PNG file created: disseminated-peers-DNS-ban-list_", dir, ".png", "\n", sep = "")
  
  cat("\n\n================================================\n\n\n")
  
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions