diff --git a/docs/api/badgenet.rst b/docs/api/badgenet.rst
new file mode 100644
index 0000000000000000000000000000000000000000..42d941aced631637a3a5228b9dbdca67976ba2dc
--- /dev/null
+++ b/docs/api/badgenet.rst
@@ -0,0 +1,12 @@
+.. _Badge Net API:
+
+``badgenet`` module
+===================
+
+.. note::
+
+   See also: :ref:`Badge Net overview<Badge Net>`
+
+.. automodule:: badgenet
+   :members:
+   :undoc-members:
diff --git a/docs/badge/badge_link.rst b/docs/badge/badge_link.rst
index d279cb2b77034395b7fe4b27badb92c147bf360b..d5ad003c29901f9c99f59d1d9ad14b77078eaef2 100644
--- a/docs/badge/badge_link.rst
+++ b/docs/badge/badge_link.rst
@@ -30,6 +30,7 @@ Typical use cases include:
  * :ref:`badge-link-uart`
  * :ref:`badge-link-midi`
  * :ref:`badge-link-eurorack-cv`
+ * :ref:`badge net` (Ethernet/IPv6 over Badge Link)
 
 
 .. _badge-link-uart:
diff --git a/docs/badge/badge_net.rst b/docs/badge/badge_net.rst
new file mode 100644
index 0000000000000000000000000000000000000000..05310ecccf80f14b268baa1c730e055d012729cc
--- /dev/null
+++ b/docs/badge/badge_net.rst
@@ -0,0 +1,170 @@
+.. _Badge Net:
+
+Badge Net
+=========
+
+.. note::
+
+   See also: :ref:`Micropython API docs<Badge Net API>`
+
+
+Badge Net builds on top of :ref:`Badge Link` to provide Ethernet and IPv6
+connectivity between badges, using 3.5mm jacks. Multiple badges can be connected
+together and can all talk to eachother!
+
+Each badge always runs a Badge Net interface that's visible as a standard
+network interface to Micropython/ESP32.
+
+When Badge Net connectivity is established, normal `sockets` calls can be used
+to send and receive UDP packets, establish TCP connectivity, etc.
+
+Protocol Stack
+--------------
+
++-------------------------------------+---------------------+
+| Layer                               | Info                |
++=====================================+=====================+
+| **TCP/UDP**                         | You use this.       |
++-------------------------------------+---------------------+
+| **IPv6**                            | You use this.       |
++-------------------------------------+---------------------+
+| Ethernet                            | Badge Net!          |
++-------------------------------------+                     +
+| SLIP (packetization)                |                     |
++-------------------------------------+                     +
+| UART (115200 baud)                  |                     |
++-------------------------------------+---------------------+
+| **Badge Link** (ESP32 digital pins) | You configure this. |
++-------------------------------------+---------------------+
+
+Each badge runs an Ethernet switch (in software) which allows multiple badges to
+be connected together.
+
+Connecting Badges
+-----------------
+
+Currently, Badge Net needs to be manually configured for each jack. Because most
+3.5mm audio cables are not-crossed-over, and because we use UART under the hood,
+you will have to pay attention to the polarity of each configured jack.
+
+Each jack has two pins: a tip and a ring. Each jack needs to be configured to
+transmit on one and receive on another.
+
+Each jack can be configured in one of four modes:
+
++-------------------+-----------------------------------------------------------+
+| Mode              | Description                                               |
++===================+===========================================================+
+| Disabled          | No Badge Net on this jack.                                |
++-------------------+-----------------------------------------------------------+
+| Enabled, Auto     | Tip transmits on left jack, ring transmits on right jack. |
++-------------------+-----------------------------------------------------------+
+| Enabled, Tip TX   | Tip transmits on this jack.                               |
++-------------------+-----------------------------------------------------------+
+| Enabled, Ring  TX | Ring transmits on this jack.                              |
++-------------------+-----------------------------------------------------------+
+
+The simplest option is to configure all participating jacks in 'Auto' mode, and
+always connect the left jack of one badge to the right jack of another badge.
+
+To actually establish connectivity, use :py:func:`badgenet.configure_jack` to
+set a modde for a jack, then :py:mod:`badgelink` to enable Badge Link on the
+same jack. **Configuring the jack with Badge Net is not enough, it must also be
+enabled for Badge Link!**.
+
+For example, on badge A:
+
+.. code-block:: pycon
+
+  >>> import badgenet
+  >>> badgenet.configure_jack(badgenet.SIDE_LEFT, badgenet.MODE_ENABLE_AUTO)
+  >>> import badgelink
+  >>> badgelink.left.enable()
+
+And on badge B:
+
+.. code-block:: pycon
+
+  >>> import badgenet
+  >>> badgenet.configure_jack(badgenet.SIDE_RIGHT, badgenet.MODE_ENABLE_AUTO)
+  >>> import badgelink
+  >>> badgelink.right.enable()
+
+And then connect badge A's left jack to badge B's right jack.
+
+Sending Packets (broadcast)
+---------------------------
+
+The simplest option for communication is to use good old broadcast UDP.
+
+Well, there's no real broadcast with IPv6 - but here we'll use a 'standard'
+multicast group to get the equivalent functionality. Fell free to use different
+`Multicast Groups
+<https://en.wikipedia.org/wiki/IPv6_address#Multicast_addresses>`!
+
+You'll also have to suffix the multicast group address with ``%bl1``, or
+whatever the name of the badgelink interface is. This is an IPv6 scope, and it's
+required when dealing with link-local addresses.
+
+On badge A:
+
+.. code-block:: pycon
+
+  >>> addr = 'ff02::1%' + bagenet.get_interface().name()
+  >>> import socket
+  # IPv6, UDP
+  >>> s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+  # Listen on multicast, UDP, port 1337
+  >>> s.bind((addr, 1337))
+  # Block until data was received
+  >>> msg, addr = s.recvfrom(1024)
+
+On badge B:
+
+.. code-block:: pycon
+
+  >>> addr = 'ff02::1%' + bagenet.get_interface().name()
+  >>> import socket
+  # IPv6, UDP
+  >>> s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+  # Tell everyone something nice.
+  >>> s.sendto('hi, good to see you!', (addr, 1337))
+
+Sending Packets (unicast)
+-------------------------
+
+You can find out the link-local address of badge A, then listen on ``::`` and
+connect or send data to this badge via unicast packets (not broadcasting the
+data to everyone).
+
+.. code-block:: pycon
+
+  >>> badgenet.get_interface().ifconfig6()
+  ['fe80::3685:18ff:fe90:888f']
+  >>> s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+  >>> s.bind(('::', 1337))
+  >>> msg, addr = s.recvfrom(1024)
+
+Then, on another badge:
+
+.. code-block:: pycon
+
+  >>> addr = 'fe80::3685:18ff:fe90:888f%' + bagenet.get_interface().name()
+  >>> import socket
+  >>> s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+  >>> s.sendto('hi there, badge A', (addr, 1337))
+
+Implementing badge discovery using multicasts is an exercise left to the reader. :)
+
+In a similar fashion, TCP connections over unicast should just work.
+
+Known Issues
+------------
+
+Badge Net is very experimental. Here are some known issues that we'd like to
+address in future versions of the flow3r firmware:
+
+ - No polarity autodetection.
+ - No unified user settings.
+ - No unified discovery mechanism.
+ - Badge Net loops are undefined behaviour :)
\ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
index 80d8334b6600fe2c6eb55c0b4ba9827042d81173..271715cdab4d1ccdeb0eab47fcf8b739f6b4842f 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -17,6 +17,7 @@ Welcome to flow3r's documentation!
    badge/firmware.rst
    badge/firmware-development.rst
    badge/badge_link.rst
+   badge/badge_net.rst
    badge/bl00mbox.rst
 
 .. toctree::
@@ -25,6 +26,7 @@ Welcome to flow3r's documentation!
 
    api/audio.rst
    api/badgelink.rst
+   api/badgenet.rst
    api/captouch.rst
    api/ctx.rst
    api/leds.rst