WireGuard front for mitmproxy (WIP)

Overview

mitmguard

work-in-progress WireGuard front for mitmproxy

Architecture

DONE

  • multi-threaded / asynchronous WireGuard server using tokio:
    • one worker thread for WireGuard UDP connection
    • one worker thread for each configured WireGuard peer
  • (very) basic TCP/IPv4 functionality

TODO

  • hook up remaining entry points of the TCP stack
  • basic IPv6 support
  • expose Reader/Writer pairs for every socket connection
  • provide asyncio.start_server compatible python bindings with PyO3 (accept handler / callback function (reader, writer) as argument)
  • better error handling / logging
  • various other TODO and FIXME items documented in the source code

Hacking

Right now, the default logger is configured to print all messages with level DEBUG or higher. This will be raised to INFO once the project is no longer in the prototype phase. To set the logger verbosity manually, use the MG_LOG environment variable (i.e. MG_LOG=info).

The mitmguard binary provides support for using tokio-console to introspect the current state of the tokio runtime, with the default settings:

$ tokio-console http://localhost:6669

There should be no task that is busy when the program is idle, i.e. there should be no busy waiting.

Python stuffs

To set up a virtual environment, maturin, compile the Python extension, and launch the test.py script, do the following:

python3 -m venv venv
source ./venv/bin/activate
pip install maturin
maturin develop
python3 ./test.py
Comments
  • implement UDP equivalent of TcpStream

    implement UDP equivalent of TcpStream

    mitmproxy has its own asyncio.StreamReader/StreamWriter UDP analog wrapper implementation based on Python's asyncio module.

    To integrate the UDP functionality from mitmproxy_wireguard into mitmproxy, a similar wrapper interface (UdpStream?) around the existing UDP functionality will be required.

    opened by decathorpe 19
  • Mitigate

    Mitigate "TCP channel full" errors

    This PR is an attempt at fixing https://github.com/mitmproxy/mitmproxy/issues/5694:

    • First, we now try to consume more than one packet (if available) before iterating over the smoltcp sockets. Iterating over the smoltcp sockets is fast already, but batching makes things even faster.
    • Somewhat unrelatedly, I discovered during debugging that we create a second socket for resent SYN packets. We now make sure this doesn't happen anymore.
    • Remove a very expensive logging call (see below).
    • Finally, we simply increase the channel sizes. The previous settings have been a bit too conservative.

    These changes should mitigate the reported problem to the extent that I can reproduce it anymore, but unfortunately is not a complete fix. The bigger pressing problem seems to be that pyo3-log is slow and blocking - the following block takes about 100ms when mitmweb is running under load:

    for _ in 0..100 {
        log::debug!("slow");
    }
    

    It seems like mitmdump isn't affected, and numbers get much worse once I add more mitmweb WebSocket connections. So there's clearly some work that needs to be done on the Python side as well.

    opened by mhils 6
  • `pip install` fails on platforms without binary wheels

    `pip install` fails on platforms without binary wheels

    I'm running mitmdump inside Termux. While trying to update to the latest version, the mitmproxy-wireguard wheel failed to build locally:

    ~ $ pip install -U --upgrade-strategy eager mitmproxy
    Requirement already satisfied: mitmproxy in /data/data/com.termux/files/usr/lib/python3.10/site-packages (8.1.1)
    Collecting mitmproxy
      Using cached mitmproxy-9.0.0-py3-none-any.whl (1.6 MB)
    Requirement already satisfied: wsproto<1.3,>=1.0 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (1.1.0)
    Collecting wsproto<1.3,>=1.0
      Using cached wsproto-1.2.0-py3-none-any.whl (24 kB)
    Requirement already satisfied: urwid<2.2,>=2.1.1 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (2.1.2)
    Requirement already satisfied: pyperclip<1.9,>=1.6.0 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (1.8.2)
    Collecting pyOpenSSL<22.2,>=22.1
      Using cached pyOpenSSL-22.1.0-py3-none-any.whl (57 kB)
    Requirement already satisfied: msgpack<1.1.0,>=1.0.0 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (1.0.4)
    Requirement already satisfied: asgiref<3.6,>=3.2.10 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (3.5.2)
    Requirement already satisfied: flask<2.3,>=1.1.1 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (2.1.2)
    Collecting flask<2.3,>=1.1.1
      Using cached Flask-2.2.2-py3-none-any.whl (101 kB)
    Requirement already satisfied: hyperframe<7,>=6.0 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (6.0.1)
    Requirement already satisfied: protobuf<5,>=3.14 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (4.21.2)
    Collecting protobuf<5,>=3.14
      Using cached protobuf-4.21.9-py3-none-any.whl (291 kB)
    Requirement already satisfied: pyparsing<3.1,>=2.4.2 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (3.0.9)
    Requirement already satisfied: ruamel.yaml<0.18,>=0.16 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (0.17.21)
    Requirement already satisfied: sortedcontainers<2.5,>=2.3 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (2.4.0)
    Requirement already satisfied: passlib<1.8,>=1.6.5 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (1.7.4)
    Requirement already satisfied: zstandard<0.19,>=0.11 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (0.18.0)
    Collecting cryptography<38.1,>=38.0
      Using cached cryptography-38.0.1.tar.gz (599 kB)
      Installing build dependencies ... done
      Getting requirements to build wheel ... done
      Preparing metadata (pyproject.toml) ... done
    Requirement already satisfied: h11<0.15,>=0.11 in /data/data/com.termux/files/usr/lib/python3.10/site-packages (from mitmproxy) (0.12.0)
    Collecting h11<0.15,>=0.11
      Downloading h11-0.14.0-py3-none-any.whl (58 kB)
         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 58.3/58.3 kB 852.4 kB/s eta 0:00:00
    Collecting mitmproxy-wireguard<0.2,>=0.1.6
      Using cached mitmproxy_wireguard-0.1.14.tar.gz (25 kB)
      Installing build dependencies ... done
      Getting requirements to build wheel ... done
      Preparing metadata (pyproject.toml) ... error
      error: subprocess-exited-with-error
      
      × Preparing metadata (pyproject.toml) did not run successfully.
       exit code: 1
      ╰─> [12 lines of output]
          💥 maturin failed
            Caused by: Cargo metadata failed. Does your crate compile with `cargo build`?
            Caused by: `cargo metadata` exited with an error: error: failed to load manifest for workspace member `/data/data/com.termux/files/usr/tmp/pip-install-fyxt3ct_/mitmproxy-wireguard_05d31e8eb7024923b70ca8f0f700dfe5/test-client`
          
          Caused by:
            failed to read `/data/data/com.termux/files/usr/tmp/pip-install-fyxt3ct_/mitmproxy-wireguard_05d31e8eb7024923b70ca8f0f700dfe5/test-client/Cargo.toml`
          
          Caused by:
            No such file or directory (os error 2)
          Error running maturin: Command '['maturin', 'pep517', 'write-dist-info', '--metadata-directory', '/data/data/com.termux/files/usr/tmp/pip-modern-metadata-_vzn0zuw', '--interpreter', '/data/data/com.termux/files/usr/bin/python3']' returned non-zero exit status 1.
          Checking for Rust toolchain....
          Running `maturin pep517 write-dist-info --metadata-directory /data/data/com.termux/files/usr/tmp/pip-modern-metadata-_vzn0zuw --interpreter /data/data/com.termux/files/usr/bin/python3`
          [end of output]
      
      note: This error originates from a subprocess, and is likely not a problem with pip.
    error: metadata-generation-failed
    
    × Encountered error while generating package metadata.
    ╰─> See above for output.
    
    note: This is an issue with the package mentioned above, not pip.
    hint: See above for details.
    
    [notice] A new release of pip available: 22.2.2 -> 22.3
    [notice] To update, run: pip install --upgrade pip
    

    My best guess would be that this happens because https://github.com/decathorpe/mitmproxy_wireguard/blob/bbd32cd9b431dcec3e9486b88208f15d55cd1325/Cargo.toml#L65 is included in the PyPi source archive, but the test client code isn't.

    Software versions:

    ~ $ rustc -Vv
    rustc 1.64.0
    binary: rustc
    commit-hash: unknown
    commit-date: unknown
    host: aarch64-linux-android
    release: 1.64.0
    LLVM version: 15.0.1
    ~ $ python --version
    Python 3.10.8
    ~ $ maturin --version
    maturin 0.13.7
    
    opened by AgentConDier 4
  • Fix panic for too large packets

    Fix panic for too large packets

    See https://github.com/mitmproxy/mitmproxy/issues/5592#issuecomment-1292141069 😃

    Edit: With rustfmt complaining again I've also added an autofix.ci job. You may need to install the GitHub App for that. 🙃

    opened by mhils 4
  • Simplify CI

    Simplify CI

    This PR simplifies our CI setup and hardens it against supply chain attacks:

    • Most importantly, all third-party actions are removed. We shouldn't use those as they introduce unnecessary supply chain risk unless pinned and audited.
    • We now pin maturin and install twine via apt.
    • To simplify Linux builds, we now use zig for manylinux-compatible compilation.

    I've also taken the liberty to switch rustfmt to stable without any custom settings, which makes the CI setup easier and looks just as good to me. Feel free to revert this change and judge me for it, I don't have particular opinions other than a slight preference for the most simple approach.

    opened by mhils 4
  • provide fake stream-based interface for UDP  to match TCP

    provide fake stream-based interface for UDP to match TCP

    This PR adds an implementation of UdpStream, a fake stream interface for UDP packets. It provides the same interface as TcpStream, except that most methods don't do anything (since UDP is not connection-based), except:

    • UdpStream.read(n): returns up to n bytes from the payload of the received UDP packet, and zero bytes once the initial payload has been read
    • UdpStream.write(data): sends a datagram as response to the initially received datagram (or drops it if the network device's send queue is full)

    Adapted the test echo server for the new interface, and it seems to work as expected (i.e. it responds "HELLO" if you send a datagram containing the text "hello" with nc -u).

    Fixes #7

    opened by decathorpe 2
  • expose original source address of WireGuard UDP packets in TcpStream

    expose original source address of WireGuard UDP packets in TcpStream

    The source socket of WireGuard UDP packets (i.e. the socket address of the WireGuard client outside the WireGuard tunnel) can now be accessed with TcpStream.get_extra_info("original_src").

    There's currently no way to do the same for UDP packets, since the callback for received UDP packets only accepts (payload, src_addr, dst_addr) as arguments.

    opened by decathorpe 1
  • implement TcpStream.get_extra_info(

    implement TcpStream.get_extra_info("original_addr")

    The original packet destination address is required for implementing a "transparent mode" based on mitmproxy_wireguard. Its sockets are handled internally and not by the operating system, so it needs to expose this information in TcpStream instances.

    opened by decathorpe 1
  • docs++

    docs++

    I've been working on .pyi/PyO3 support for pdoc (https://github.com/mitmproxy/pdoc/issues/239), ended up with some initial docstrings here as a byproduct. :)

    opened by mhils 1
  • fix `truncated packet`, rig up PyO3

    fix `truncated packet`, rig up PyO3

    See individual commits below.

    Some thoughts on the next steps:

    • One thing that's slightly annoying about having multiple smoltcp interfaces is that we need to route
       server.tcp_send(connection_id=42, data=b"...")
      

      to the correct smoltcp interface. Maybe it's easier to just use one interface here and map from IPs to peers on the WireGuard side.

    • I haven't quite figured out what's the best way to get the raw TCP events out without intertwining everything. I'll keep going on that.

    If you want to coordinate on chat, send me an email with what works best for you (ideally Slack, Discord). Alternatively we can stay to mail. :)

    opened by mhils 1
  • Write before poll, fix CI failures

    Write before poll, fix CI failures

    This PR fixes the CI failures over at mitmproxy/mitmproxy: https://github.com/mitmproxy/mitmproxy/actions/runs/3374019278/jobs/5599200642

    The problem here is as follows:

    1. Write and Drain get batched into a single iteration.
    2. We first poll the interface (nothing to do yet) before calling sock.send
    3. We wait infinitely because we have no reason for another loop.

    Previously, the following Drain would always trigger another loop. This PR changes it so that we send before polling. I need to go over this tomorrow again with a fresh mind to check if that's enough. I could imagine some weird read/flow control interactions as well which may require us to poll first, then handle sockets, and then poll again.

    opened by mhils 0
  • Binding on both IPv4 and IPv6 does not work

    Binding on both IPv4 and IPv6 does not work

    Leaving the host empty defaults to binding to both 0.0.0.0 and ::1. However, rust's UDPSocket doesn't bind to both, it binds to the first successful.

    Happens here: https://github.com/decathorpe/mitmproxy_wireguard/blob/main/src/server.rs#L125..L135 Documentation here: https://doc.rust-lang.org/std/net/struct.UdpSocket.html#method.bind

    Best fix is probably to just support a single listen address, since multiple listen sockets opens another can of worms.

    opened by DaClemens 2
  • update GitHub actions to avoid actions running on deprecated NodeJS 12

    update GitHub actions to avoid actions running on deprecated NodeJS 12

    It looks like the unmaintained actions-rs actions will be affected by the deprecation of old NodeJS 12 based actions: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/

    The easiest solution is probably to run cargo commands directly, even if doing that means we'll lose commit and PR annotations :(


    Looks like we're also using old versions of the actions/checkout action, but updating from actions/checkout@v2 actions/checkout@v3 should solve the problem there.

    The same problem also seems to apply to actions/upload-artifact, actions/setup-python, and actions/download-artifact actions ... not sure if we can just bump to new versions, as for some actions, the way to pass them arguments has changed.

    opened by decathorpe 1
Releases(0.1.19)
  • 0.1.19(Nov 24, 2022)

    • Fix check that prevents initializing multiple TCP connections when receiving duplicate SYN packets.
    • Update dependencies (including tokio 1.22.0). Raises MSRV to 1.64.0.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.18(Nov 13, 2022)

    • Expose the "original" (i.e. not the address inside the WireGuard tunnel) source address of WireGuard UDP packets in TcpStream via TcpStream.get_extra_info("original_src").
    • Internal refactoring to simplify code for spawning TCP connection handler coroutines, which makes it possible to check whether they raised an exception (which were previously just silently ignored).
    • Update all Rust dependencies, including an update to PyO3 v0.17.3, which is the first release that marked support for Python 3.11 as official.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.17(Nov 4, 2022)

    • Ensure that the virtual network device does not block unnecessarily and that it is always polled when necessary. Fixes a regression that was introduced in version 0.1.16.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.16(Nov 2, 2022)

    • Optimize event processing in the internal network stack by always consuming as many events as possible before polling the virtual network device and processing open TCP sockets.
    • Ensure that only one TCP socket is created per connection, even if SYN packets are resent for some reason.
    • Channel sizes for processing events in the internal network stack are increased to avoid errors with full channels when some tasks don't keep up.
    • Logging calls are removed from the network task's hot loop unless the project is built in debug mode.
    • Failures to send to channels that were already closed when processing data that was received for sockets are now ignored to avoid crashes.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.15(Oct 29, 2022)

    With this version, source files for the test client binary are manually included in published sdists to ensure the sources which are published on PyPI can actually be built. d'oh ...

    Source code(tar.gz)
    Source code(zip)
  • 0.1.14(Oct 29, 2022)

    • Increase buffer size for WireGuard packets to accommodate large outgoing packets.
    • Check length of outgoing packets and drop packets that are larger than the maximum possible WireGuard packet payload (maximum packet size - WireGuard header length) to avoid crashes with super-sized packets.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.13(Oct 7, 2022)

    • Update dependencies to the latest versions (pyo3 v0.17, pyo3-asyncio v0.17, pyo3-log v0.7), now that pyo3-asyncio v0.17 was released with pyo3 v0.17 support.
    • Switch back from patched version of pyo3-asyncio to the official releases, since v0.17 incorporates our patch.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.12(Sep 28, 2022)

    • Fix a race condition in the shutdown code that could cause shutdown to never happen.
    • Make logger setup more robust and only try to initialize once.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.11(Sep 28, 2022)

  • 0.1.10(Sep 22, 2022)

    • Temporarily use a patched version of pyo3-asyncio to fix a race condition in the handling of Python Futures which caused frequent race conditions.
    • Implement is_closing(self) -> bool method on TcpStream to match asyncio.StreamWriter.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.9(Sep 22, 2022)

    • Simplified GitHub actions for CI and publishing wheels to PyPI.
    • Failed sub-tasks are now handled immediately and cause a server shutdown instead of silently returning and only yielding an error when shutting down the server manually.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.8(Sep 19, 2022)

  • 0.1.7(Sep 19, 2022)

    • Do not exit the network task when a draining TcpStream is already closed.
    • Make log messages for "no current WireGuard session" more user-friendly.
    • Attempt to build binary wheels for aarch64-unknown-linux-gnu for Raspberry Pi support.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.6(Sep 18, 2022)

  • 0.1.5(Sep 18, 2022)

  • 0.1.4(Sep 15, 2022)

  • 0.1.3(Sep 15, 2022)

    • Adapt test client to produce packets with correct checksums.
    • Build test client binaries in the publish GitHub Action.
    • Stop building binary wheels for 32-bit Linux and Windows targets.
    • Validate TCP checksums and reject invalid incoming packets early.
    • Lower priority of log messages for non-fatal TcpStream cleanup errors during server shutdown.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.2(Sep 15, 2022)

    • Revert addition of ChecksumCapabilities::ignored to the virtual network device. This change in v0.1.1 completely broke TCP connection handling.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.1(Sep 15, 2022)

    • Added a simple test client binary (mitm-wg-test-client).
    • Ignore TCP checksums in network device code, they are already checked in other places.
    • Port to boringtun v0.5.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Sep 15, 2022)

Owner
Fabio Valentini
Computer Science student | Programmer | Fedora Packaging Committee and FESCo member
Fabio Valentini
wireguard tool to manage / generate configuration. Maintain one yaml configuration file to quickly build wireguard network.

wgx wireguard tool to manage / generate configuration. Maintain one yaml configuration file to quickly build wireguard network. Usage wgx --h USAGE:

yinheli 6 Nov 3, 2022
The Rust bits in mitmproxy. 🦀

Note This repository is unreleased work-in-progress, but mitmproxy 9 ships with mitmproxy_wireguard already! mitmproxy_rs This repository contains mit

mitmproxy 10 Jan 8, 2023
A private network system that uses WireGuard under the hood.

innernet A private network system that uses WireGuard under the hood. See the announcement blog post for a longer-winded explanation. innernet is simi

Tonari, Inc 4.1k Dec 29, 2022
A cross-platform, user-space WireGuard port-forwarder that requires no system network configurations.

Cross-platform, user-space WireGuard port-forwarder that requires no system network configurations.

Aram Peres 629 Jan 4, 2023
A Prometheus exporter for WireGuard

wireguard_exporter An asynchronous Prometheus exporter for wireguard wireguard_exporter runs wg show [..] and scrapes the output to build Prometheus m

Kevin K. 15 Dec 29, 2022
A WireGuard UWP VPN plugin.

WireGuard UWP A Universal Windows Platform (UWP) VPN Plug-in for WireGuard® written in Rust. Windows provides a plug-in based model for adding 3rd-par

Luqman Aden 92 Dec 13, 2022
Rust utility crate for parsing, encoding and generating x25519 keys used by WireGuard

WireGuard Keys This is a utility crate for parsing, encoding and generating x25519 keys that are used by WireGuard. It exports custom types that can b

Fractal Networks 3 Aug 9, 2022
WireGuard gateway with SNI for portable connectivity.

Gateway This is a daemon that controls gateway servers. Gateway servers are servers that fulfil three major purposes: facilitating connectivity betwee

Fractal Networks 5 Aug 9, 2022
Rosenpass is a formally verified, post-quantum secure VPN that uses WireGuard to transport the actual data.

Rosenpass README This repository contains A description of the Rosenpass protocol The reference implementation of the protocol – the rosenpass tool A

Rosenpass 597 Mar 19, 2023
User-space Wireguard gateway allowing sharing network connection from environment where usual routing rules are inaccessible.

wgslirpy A command line tool (and a Rust library) for accepting incoming connections within a Wireguard link and routing them to external network usin

Vitaly Shukela 4 Aug 21, 2023
WIP / POC for using the BL602 wifi blob from Rust

BL602 Wifi Rust This is work in progress and currently more a proof of concept. The code is awfully hacked together - just enough to make it work. It

Björn Quentin 23 Sep 15, 2022
[WIP] wadachi scrapes your GitHub Activities.

wadachi wadachi scrapes your GitHub Activities. Usage | Examples | Docs This library is currently under development. Dependencies [dependencies] wadac

Takayuki Maeda 2 Mar 18, 2022
WIP / POC for using the ESP32C3 wifi drivers in bare-metal Rust

Wifi on ESP32C3 (on bare-metal Rust) About This is experimental and work-in-progress! You are welcome to contribute but probably shouldn't use this fo

Björn Quentin 135 Jan 2, 2023
WireGuard frontend for mitmproxy (WIP)

mitmproxy_wireguard Transparently proxy any device that can be configured as a WireGuard client! Work-In-Progress. Architecture DONE multi-threaded /

Fabio Valentini 20 Dec 29, 2022
wireguard tool to manage / generate configuration. Maintain one yaml configuration file to quickly build wireguard network.

wgx wireguard tool to manage / generate configuration. Maintain one yaml configuration file to quickly build wireguard network. Usage wgx --h USAGE:

yinheli 6 Nov 3, 2022
Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

null 294 Dec 23, 2022
The Rust bits in mitmproxy. 🦀

Note This repository is unreleased work-in-progress, but mitmproxy 9 ships with mitmproxy_wireguard already! mitmproxy_rs This repository contains mit

mitmproxy 10 Jan 8, 2023
Libreddit - An alternative private front-end to Reddit

Libreddit - An alternative private front-end to Reddit

Spike 3.9k Jan 6, 2023
Seed is a Rust front-end framework for creating fast and reliable web apps with an Elm-like architecture.

Seed is a Rust front-end framework for creating fast and reliable web apps with an Elm-like architecture.

null 3.6k Jan 6, 2023
Front-coding string dictionary in Rust

Front-coding string dictionary in Rust This is a Rust library of the (plain) front-coding string dictionary described in Martínez-Prieto et al., Pract

Shunsuke Kanda 6 Jul 14, 2022