diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,675 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/LICENSE.md b/LICENSE.md
deleted file mode 100644
--- a/LICENSE.md
+++ /dev/null
@@ -1,595 +0,0 @@
-GNU General Public License
-==========================
-
-_Version 3, 29 June 2007_
-_Copyright © 2007 Free Software Foundation, Inc. &lt;<http://fsf.org/>&gt;_
-
-Everyone is permitted to copy and distribute verbatim copies of this license
-document, but changing it is not allowed.
-
-## Preamble
-
-The GNU General Public License is a free, copyleft license for software and other
-kinds of works.
-
-The licenses for most software and other practical works are designed to take away
-your freedom to share and change the works. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change all versions of a
-program--to make sure it remains free software for all its users. We, the Free
-Software Foundation, use the GNU General Public License for most of our software; it
-applies also to any other work released this way by its authors. You can apply it to
-your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Our General
-Public Licenses are designed to make sure that you have the freedom to distribute
-copies of free software (and charge for them if you wish), that you receive source
-code or can get it if you want it, that you can change the software or use pieces of
-it in new free programs, and that you know you can do these things.
-
-To protect your rights, we need to prevent others from denying you these rights or
-asking you to surrender the rights. Therefore, you have certain responsibilities if
-you distribute copies of the software, or if you modify it: responsibilities to
-respect the freedom of others.
-
-For example, if you distribute copies of such a program, whether gratis or for a fee,
-you must pass on to the recipients the same freedoms that you received. You must make
-sure that they, too, receive or can get the source code. And you must show them these
-terms so they know their rights.
-
-Developers that use the GNU GPL protect your rights with two steps: **(1)** assert
-copyright on the software, and **(2)** offer you this License giving you legal permission
-to copy, distribute and/or modify it.
-
-For the developers' and authors' protection, the GPL clearly explains that there is
-no warranty for this free software. For both users' and authors' sake, the GPL
-requires that modified versions be marked as changed, so that their problems will not
-be attributed erroneously to authors of previous versions.
-
-Some devices are designed to deny users access to install or run modified versions of
-the software inside them, although the manufacturer can do so. This is fundamentally
-incompatible with the aim of protecting users' freedom to change the software. The
-systematic pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we have designed
-this version of the GPL to prohibit the practice for those products. If such problems
-arise substantially in other domains, we stand ready to extend this provision to
-those domains in future versions of the GPL, as needed to protect the freedom of
-users.
-
-Finally, every program is threatened constantly by software patents. States should
-not allow patents to restrict development and use of software on general-purpose
-computers, but in those that do, we wish to avoid the special danger that patents
-applied to a free program could make it effectively proprietary. To prevent this, the
-GPL assures that patents cannot be used to render the program non-free.
-
-The precise terms and conditions for copying, distribution and modification follow.
-
-## TERMS AND CONDITIONS
-
-### 0. Definitions
-
-“This License” refers to version 3 of the GNU General Public License.
-
-“Copyright” also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-“The Program” refers to any copyrightable work licensed under this
-License. Each licensee is addressed as “you”. “Licensees” and
-“recipients” may be individuals or organizations.
-
-To “modify” a work means to copy from or adapt all or part of the work in
-a fashion requiring copyright permission, other than the making of an exact copy. The
-resulting work is called a “modified version” of the earlier work or a
-work “based on” the earlier work.
-
-A “covered work” means either the unmodified Program or a work based on
-the Program.
-
-To “propagate” a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for infringement under
-applicable copyright law, except executing it on a computer or modifying a private
-copy. Propagation includes copying, distribution (with or without modification),
-making available to the public, and in some countries other activities as well.
-
-To “convey” a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through a computer
-network, with no transfer of a copy, is not conveying.
-
-An interactive user interface displays “Appropriate Legal Notices” to the
-extent that it includes a convenient and prominently visible feature that **(1)**
-displays an appropriate copyright notice, and **(2)** tells the user that there is no
-warranty for the work (except to the extent that warranties are provided), that
-licensees may convey the work under this License, and how to view a copy of this
-License. If the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-### 1. Source Code
-
-The “source code” for a work means the preferred form of the work for
-making modifications to it. “Object code” means any non-source form of a
-work.
-
-A “Standard Interface” means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of interfaces
-specified for a particular programming language, one that is widely used among
-developers working in that language.
-
-The “System Libraries” of an executable work include anything, other than
-the work as a whole, that **(a)** is included in the normal form of packaging a Major
-Component, but which is not part of that Major Component, and **(b)** serves only to
-enable use of the work with that Major Component, or to implement a Standard
-Interface for which an implementation is available to the public in source code form.
-A “Major Component”, in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system (if any) on which
-the executable work runs, or a compiler used to produce the work, or an object code
-interpreter used to run it.
-
-The “Corresponding Source” for a work in object code form means all the
-source code needed to generate, install, and (for an executable work) run the object
-code and to modify the work, including scripts to control those activities. However,
-it does not include the work's System Libraries, or general-purpose tools or
-generally available free programs which are used unmodified in performing those
-activities but which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for the work, and
-the source code for shared libraries and dynamically linked subprograms that the work
-is specifically designed to require, such as by intimate data communication or
-control flow between those subprograms and other parts of the work.
-
-The Corresponding Source need not include anything that users can regenerate
-automatically from other parts of the Corresponding Source.
-
-The Corresponding Source for a work in source code form is that same work.
-
-### 2. Basic Permissions
-
-All rights granted under this License are granted for the term of copyright on the
-Program, and are irrevocable provided the stated conditions are met. This License
-explicitly affirms your unlimited permission to run the unmodified Program. The
-output from running a covered work is covered by this License only if the output,
-given its content, constitutes a covered work. This License acknowledges your rights
-of fair use or other equivalent, as provided by copyright law.
-
-You may make, run and propagate covered works that you do not convey, without
-conditions so long as your license otherwise remains in force. You may convey covered
-works to others for the sole purpose of having them make modifications exclusively
-for you, or provide you with facilities for running those works, provided that you
-comply with the terms of this License in conveying all material for which you do not
-control copyright. Those thus making or running the covered works for you must do so
-exclusively on your behalf, under your direction and control, on terms that prohibit
-them from making any copies of your copyrighted material outside their relationship
-with you.
-
-Conveying under any other circumstances is permitted solely under the conditions
-stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
-
-### 3. Protecting Users' Legal Rights From Anti-Circumvention Law
-
-No covered work shall be deemed part of an effective technological measure under any
-applicable law fulfilling obligations under article 11 of the WIPO copyright treaty
-adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention
-of such measures.
-
-When you convey a covered work, you waive any legal power to forbid circumvention of
-technological measures to the extent such circumvention is effected by exercising
-rights under this License with respect to the covered work, and you disclaim any
-intention to limit operation or modification of the work as a means of enforcing,
-against the work's users, your or third parties' legal rights to forbid circumvention
-of technological measures.
-
-### 4. Conveying Verbatim Copies
-
-You may convey verbatim copies of the Program's source code as you receive it, in any
-medium, provided that you conspicuously and appropriately publish on each copy an
-appropriate copyright notice; keep intact all notices stating that this License and
-any non-permissive terms added in accord with section 7 apply to the code; keep
-intact all notices of the absence of any warranty; and give all recipients a copy of
-this License along with the Program.
-
-You may charge any price or no price for each copy that you convey, and you may offer
-support or warranty protection for a fee.
-
-### 5. Conveying Modified Source Versions
-
-You may convey a work based on the Program, or the modifications to produce it from
-the Program, in the form of source code under the terms of section 4, provided that
-you also meet all of these conditions:
-
-* **a)** The work must carry prominent notices stating that you modified it, and giving a
-relevant date.
-* **b)** The work must carry prominent notices stating that it is released under this
-License and any conditions added under section 7. This requirement modifies the
-requirement in section 4 to “keep intact all notices”.
-* **c)** You must license the entire work, as a whole, under this License to anyone who
-comes into possession of a copy. This License will therefore apply, along with any
-applicable section 7 additional terms, to the whole of the work, and all its parts,
-regardless of how they are packaged. This License gives no permission to license the
-work in any other way, but it does not invalidate such permission if you have
-separately received it.
-* **d)** If the work has interactive user interfaces, each must display Appropriate Legal
-Notices; however, if the Program has interactive interfaces that do not display
-Appropriate Legal Notices, your work need not make them do so.
-
-A compilation of a covered work with other separate and independent works, which are
-not by their nature extensions of the covered work, and which are not combined with
-it such as to form a larger program, in or on a volume of a storage or distribution
-medium, is called an “aggregate” if the compilation and its resulting
-copyright are not used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work in an aggregate
-does not cause this License to apply to the other parts of the aggregate.
-
-### 6. Conveying Non-Source Forms
-
-You may convey a covered work in object code form under the terms of sections 4 and
-5, provided that you also convey the machine-readable Corresponding Source under the
-terms of this License, in one of these ways:
-
-* **a)** Convey the object code in, or embodied in, a physical product (including a
-physical distribution medium), accompanied by the Corresponding Source fixed on a
-durable physical medium customarily used for software interchange.
-* **b)** Convey the object code in, or embodied in, a physical product (including a
-physical distribution medium), accompanied by a written offer, valid for at least
-three years and valid for as long as you offer spare parts or customer support for
-that product model, to give anyone who possesses the object code either **(1)** a copy of
-the Corresponding Source for all the software in the product that is covered by this
-License, on a durable physical medium customarily used for software interchange, for
-a price no more than your reasonable cost of physically performing this conveying of
-source, or **(2)** access to copy the Corresponding Source from a network server at no
-charge.
-* **c)** Convey individual copies of the object code with a copy of the written offer to
-provide the Corresponding Source. This alternative is allowed only occasionally and
-noncommercially, and only if you received the object code with such an offer, in
-accord with subsection 6b.
-* **d)** Convey the object code by offering access from a designated place (gratis or for
-a charge), and offer equivalent access to the Corresponding Source in the same way
-through the same place at no further charge. You need not require recipients to copy
-the Corresponding Source along with the object code. If the place to copy the object
-code is a network server, the Corresponding Source may be on a different server
-(operated by you or a third party) that supports equivalent copying facilities,
-provided you maintain clear directions next to the object code saying where to find
-the Corresponding Source. Regardless of what server hosts the Corresponding Source,
-you remain obligated to ensure that it is available for as long as needed to satisfy
-these requirements.
-* **e)** Convey the object code using peer-to-peer transmission, provided you inform
-other peers where the object code and Corresponding Source of the work are being
-offered to the general public at no charge under subsection 6d.
-
-A separable portion of the object code, whose source code is excluded from the
-Corresponding Source as a System Library, need not be included in conveying the
-object code work.
-
-A “User Product” is either **(1)** a “consumer product”, which
-means any tangible personal property which is normally used for personal, family, or
-household purposes, or **(2)** anything designed or sold for incorporation into a
-dwelling. In determining whether a product is a consumer product, doubtful cases
-shall be resolved in favor of coverage. For a particular product received by a
-particular user, “normally used” refers to a typical or common use of
-that class of product, regardless of the status of the particular user or of the way
-in which the particular user actually uses, or expects or is expected to use, the
-product. A product is a consumer product regardless of whether the product has
-substantial commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-“Installation Information” for a User Product means any methods,
-procedures, authorization keys, or other information required to install and execute
-modified versions of a covered work in that User Product from a modified version of
-its Corresponding Source. The information must suffice to ensure that the continued
-functioning of the modified object code is in no case prevented or interfered with
-solely because modification has been made.
-
-If you convey an object code work under this section in, or with, or specifically for
-use in, a User Product, and the conveying occurs as part of a transaction in which
-the right of possession and use of the User Product is transferred to the recipient
-in perpetuity or for a fixed term (regardless of how the transaction is
-characterized), the Corresponding Source conveyed under this section must be
-accompanied by the Installation Information. But this requirement does not apply if
-neither you nor any third party retains the ability to install modified object code
-on the User Product (for example, the work has been installed in ROM).
-
-The requirement to provide Installation Information does not include a requirement to
-continue to provide support service, warranty, or updates for a work that has been
-modified or installed by the recipient, or for the User Product in which it has been
-modified or installed. Access to a network may be denied when the modification itself
-materially and adversely affects the operation of the network or violates the rules
-and protocols for communication across the network.
-
-Corresponding Source conveyed, and Installation Information provided, in accord with
-this section must be in a format that is publicly documented (and with an
-implementation available to the public in source code form), and must require no
-special password or key for unpacking, reading or copying.
-
-### 7. Additional Terms
-
-“Additional permissions” are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions. Additional
-permissions that are applicable to the entire Program shall be treated as though they
-were included in this License, to the extent that they are valid under applicable
-law. If additional permissions apply only to part of the Program, that part may be
-used separately under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-When you convey a copy of a covered work, you may at your option remove any
-additional permissions from that copy, or from any part of it. (Additional
-permissions may be written to require their own removal in certain cases when you
-modify the work.) You may place additional permissions on material, added by you to a
-covered work, for which you have or can give appropriate copyright permission.
-
-Notwithstanding any other provision of this License, for material you add to a
-covered work, you may (if authorized by the copyright holders of that material)
-supplement the terms of this License with terms:
-
-* **a)** Disclaiming warranty or limiting liability differently from the terms of
-sections 15 and 16 of this License; or
-* **b)** Requiring preservation of specified reasonable legal notices or author
-attributions in that material or in the Appropriate Legal Notices displayed by works
-containing it; or
-* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that
-modified versions of such material be marked in reasonable ways as different from the
-original version; or
-* **d)** Limiting the use for publicity purposes of names of licensors or authors of the
-material; or
-* **e)** Declining to grant rights under trademark law for use of some trade names,
-trademarks, or service marks; or
-* **f)** Requiring indemnification of licensors and authors of that material by anyone
-who conveys the material (or modified versions of it) with contractual assumptions of
-liability to the recipient, for any liability that these contractual assumptions
-directly impose on those licensors and authors.
-
-All other non-permissive additional terms are considered “further
-restrictions” within the meaning of section 10. If the Program as you received
-it, or any part of it, contains a notice stating that it is governed by this License
-along with a term that is a further restriction, you may remove that term. If a
-license document contains a further restriction but permits relicensing or conveying
-under this License, you may add to a covered work material governed by the terms of
-that license document, provided that the further restriction does not survive such
-relicensing or conveying.
-
-If you add terms to a covered work in accord with this section, you must place, in
-the relevant source files, a statement of the additional terms that apply to those
-files, or a notice indicating where to find the applicable terms.
-
-Additional terms, permissive or non-permissive, may be stated in the form of a
-separately written license, or stated as exceptions; the above requirements apply
-either way.
-
-### 8. Termination
-
-You may not propagate or modify a covered work except as expressly provided under
-this License. Any attempt otherwise to propagate or modify it is void, and will
-automatically terminate your rights under this License (including any patent licenses
-granted under the third paragraph of section 11).
-
-However, if you cease all violation of this License, then your license from a
-particular copyright holder is reinstated **(a)** provisionally, unless and until the
-copyright holder explicitly and finally terminates your license, and **(b)** permanently,
-if the copyright holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently
-if the copyright holder notifies you of the violation by some reasonable means, this
-is the first time you have received notice of violation of this License (for any
-work) from that copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of
-parties who have received copies or rights from you under this License. If your
-rights have been terminated and not permanently reinstated, you do not qualify to
-receive new licenses for the same material under section 10.
-
-### 9. Acceptance Not Required for Having Copies
-
-You are not required to accept this License in order to receive or run a copy of the
-Program. Ancillary propagation of a covered work occurring solely as a consequence of
-using peer-to-peer transmission to receive a copy likewise does not require
-acceptance. However, nothing other than this License grants you permission to
-propagate or modify any covered work. These actions infringe copyright if you do not
-accept this License. Therefore, by modifying or propagating a covered work, you
-indicate your acceptance of this License to do so.
-
-### 10. Automatic Licensing of Downstream Recipients
-
-Each time you convey a covered work, the recipient automatically receives a license
-from the original licensors, to run, modify and propagate that work, subject to this
-License. You are not responsible for enforcing compliance by third parties with this
-License.
-
-An “entity transaction” is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an organization, or
-merging organizations. If propagation of a covered work results from an entity
-transaction, each party to that transaction who receives a copy of the work also
-receives whatever licenses to the work the party's predecessor in interest had or
-could give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if the predecessor
-has it or can get it with reasonable efforts.
-
-You may not impose any further restrictions on the exercise of the rights granted or
-affirmed under this License. For example, you may not impose a license fee, royalty,
-or other charge for exercise of rights granted under this License, and you may not
-initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging
-that any patent claim is infringed by making, using, selling, offering for sale, or
-importing the Program or any portion of it.
-
-### 11. Patents
-
-A “contributor” is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The work thus
-licensed is called the contributor's “contributor version”.
-
-A contributor's “essential patent claims” are all patent claims owned or
-controlled by the contributor, whether already acquired or hereafter acquired, that
-would be infringed by some manner, permitted by this License, of making, using, or
-selling its contributor version, but do not include claims that would be infringed
-only as a consequence of further modification of the contributor version. For
-purposes of this definition, “control” includes the right to grant patent
-sublicenses in a manner consistent with the requirements of this License.
-
-Each contributor grants you a non-exclusive, worldwide, royalty-free patent license
-under the contributor's essential patent claims, to make, use, sell, offer for sale,
-import and otherwise run, modify and propagate the contents of its contributor
-version.
-
-In the following three paragraphs, a “patent license” is any express
-agreement or commitment, however denominated, not to enforce a patent (such as an
-express permission to practice a patent or covenant not to sue for patent
-infringement). To “grant” such a patent license to a party means to make
-such an agreement or commitment not to enforce a patent against the party.
-
-If you convey a covered work, knowingly relying on a patent license, and the
-Corresponding Source of the work is not available for anyone to copy, free of charge
-and under the terms of this License, through a publicly available network server or
-other readily accessible means, then you must either **(1)** cause the Corresponding
-Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the
-patent license for this particular work, or **(3)** arrange, in a manner consistent with
-the requirements of this License, to extend the patent license to downstream
-recipients. “Knowingly relying” means you have actual knowledge that, but
-for the patent license, your conveying the covered work in a country, or your
-recipient's use of the covered work in a country, would infringe one or more
-identifiable patents in that country that you have reason to believe are valid.
-
-If, pursuant to or in connection with a single transaction or arrangement, you
-convey, or propagate by procuring conveyance of, a covered work, and grant a patent
-license to some of the parties receiving the covered work authorizing them to use,
-propagate, modify or convey a specific copy of the covered work, then the patent
-license you grant is automatically extended to all recipients of the covered work and
-works based on it.
-
-A patent license is “discriminatory” if it does not include within the
-scope of its coverage, prohibits the exercise of, or is conditioned on the
-non-exercise of one or more of the rights that are specifically granted under this
-License. You may not convey a covered work if you are a party to an arrangement with
-a third party that is in the business of distributing software, under which you make
-payment to the third party based on the extent of your activity of conveying the
-work, and under which the third party grants, to any of the parties who would receive
-the covered work from you, a discriminatory patent license **(a)** in connection with
-copies of the covered work conveyed by you (or copies made from those copies), or **(b)**
-primarily for and in connection with specific products or compilations that contain
-the covered work, unless you entered into that arrangement, or that patent license
-was granted, prior to 28 March 2007.
-
-Nothing in this License shall be construed as excluding or limiting any implied
-license or other defenses to infringement that may otherwise be available to you
-under applicable patent law.
-
-### 12. No Surrender of Others' Freedom
-
-If conditions are imposed on you (whether by court order, agreement or otherwise)
-that contradict the conditions of this License, they do not excuse you from the
-conditions of this License. If you cannot convey a covered work so as to satisfy
-simultaneously your obligations under this License and any other pertinent
-obligations, then as a consequence you may not convey it at all. For example, if you
-agree to terms that obligate you to collect a royalty for further conveying from
-those to whom you convey the Program, the only way you could satisfy both those terms
-and this License would be to refrain entirely from conveying the Program.
-
-### 13. Use with the GNU Affero General Public License
-
-Notwithstanding any other provision of this License, you have permission to link or
-combine any covered work with a work licensed under version 3 of the GNU Affero
-General Public License into a single combined work, and to convey the resulting work.
-The terms of this License will continue to apply to the part which is the covered
-work, but the special requirements of the GNU Affero General Public License, section
-13, concerning interaction through a network will apply to the combination as such.
-
-### 14. Revised Versions of this License
-
-The Free Software Foundation may publish revised and/or new versions of the GNU
-General Public License from time to time. Such new versions will be similar in spirit
-to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies that
-a certain numbered version of the GNU General Public License “or any later
-version” applies to it, you have the option of following the terms and
-conditions either of that numbered version or of any later version published by the
-Free Software Foundation. If the Program does not specify a version number of the GNU
-General Public License, you may choose any version ever published by the Free
-Software Foundation.
-
-If the Program specifies that a proxy can decide which future versions of the GNU
-General Public License can be used, that proxy's public statement of acceptance of a
-version permanently authorizes you to choose that version for the Program.
-
-Later license versions may give you additional or different permissions. However, no
-additional obligations are imposed on any author or copyright holder as a result of
-your choosing to follow a later version.
-
-### 15. Disclaimer of Warranty
-
-THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER
-EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
-QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
-DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-### 16. Limitation of Liability
-
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
-COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
-PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
-INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE
-OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
-WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-### 17. Interpretation of Sections 15 and 16
-
-If the disclaimer of warranty and limitation of liability provided above cannot be
-given local legal effect according to their terms, reviewing courts shall apply local
-law that most closely approximates an absolute waiver of all civil liability in
-connection with the Program, unless a warranty or assumption of liability accompanies
-a copy of the Program in return for a fee.
-
-_END OF TERMS AND CONDITIONS_
-
-## How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest possible use to
-the public, the best way to achieve this is to make it free software which everyone
-can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to attach them
-to the start of each source file to most effectively state the exclusion of warranty;
-and each file should have at least the “copyright” line and a pointer to
-where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program does terminal interaction, make it output a short notice like this
-when it starts in an interactive mode:
-
-    <program>  Copyright (C) <year>  <name of author>
-    This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type 'show c' for details.
-
-The hypothetical commands `show w` and `show c` should show the appropriate parts of
-the General Public License. Of course, your program's commands might be different;
-for a GUI interface, you would use an “about box”.
-
-You should also get your employer (if you work as a programmer) or school, if any, to
-sign a “copyright disclaimer” for the program, if necessary. For more
-information on this, and how to apply and follow the GNU GPL, see
-&lt;<http://www.gnu.org/licenses/>&gt;.
-
-The GNU General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may consider it
-more useful to permit linking proprietary applications with the library. If this is
-what you want to do, use the GNU Lesser General Public License instead of this
-License. But first, please read
-&lt;<http://www.gnu.org/philosophy/why-not-lgpl.html>&gt;.
diff --git a/src/Network/Tox.lhs b/src/Network/Tox.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox.lhs
@@ -0,0 +1,3476 @@
+\chapter{Introduction}
+
+\begin{code}
+{-# LANGUAGE Safe #-}
+module Network.Tox where
+\end{code}
+
+This document is a textual specification of the Tox protocol and all the
+supporting modules required to implement it.  The goal of this document is to
+give enough guidance to permit a complete and correct implementation of the
+protocol.
+
+\section{Objectives}
+
+This section provides an overview of goals and non-goals of Tox.  It provides
+the reader with:
+
+\begin{itemize}
+  \item a basic understanding of what problems Tox intends to solve;
+  \item a means to validate whether those problems are indeed solved by the
+    protocol as specified;
+  \item the ability to make better tradeoffs and decisions in their own
+    reimplementation of the protocol.
+\end{itemize}
+
+\subsection{Goals}
+
+\begin{itemize}
+
+  \item \textbf{Authentication:} Tox aims to provide authenticated
+    communication. This means that during a communication session, both parties
+    can be sure of the other party's identity. Users are identified by their
+    public key. The initial key exchange is currently not in scope for the Tox
+    protocol. In the future, Tox may provide a means for initial authentication
+    using a challenge/response or shared secret based exchange.
+
+    If the secret key is compromised, the user's identity is compromised, and an
+    attacker can impersonate that user. When this happens, the user must create
+    a new identity with a new public key.
+
+  \item \textbf{End-to-end encryption:} The Tox protocol establishes end-to-end
+    encrypted communication links. Shared keys are deterministically derived
+    using a Diffie-Hellman-like method, so keys are never transferred over the
+    network.
+
+  \item \textbf{Forward secrecy}: Session keys are re-negotiated when the peer
+    connection is established.
+
+  \item \textbf{Privacy}: When Tox establishes a communication link, it aims to
+    avoid leaking to any third party the identities of the parties involved
+    (i.e. their public keys).
+
+    Furthermore, it aims to avoid allowing third parties to determine the IP
+    address of a given user.
+
+  \item \textbf{Resilience:}
+    \begin{itemize}
+      \item Independence of infrastructure: Tox avoids relying on servers as
+        much as possible. Communications are not transmitted via or stored on
+        central servers. Joining a Tox network requires connecting to a
+        well-known node called a bootstrap node. Anyone can run a bootstrap
+        node, and users need not put any trust in them.
+      \item Tox tries to establish communication paths in difficult network
+        situations. This includes connecting to peers behind a NAT or firewall.
+        Various techniques help achieve this, such as UDP hole-punching, UPnP,
+        NAT-PMP, other untrusted nodes acting as relays, and DNS tunnels.
+      \item Resistance to basic denial of service attacks: short timeouts make
+        the network dynamic and resilient against poisoning attempts.
+    \end{itemize}
+
+  \item \textbf{Minimum configuration:} Tox aims to be nearly zero-conf.
+    User-friendliness is an important aspect to security. Tox aims to make
+    security easy to achieve for average users.
+\end{itemize}
+
+\subsection{Non-goals}
+
+\begin{itemize}
+  \item \textbf{Anonymity} is not in scope for the Tox protocol itself, but it
+    provides an easy way to integrate with software providing anonymity, such as
+    Tor.
+
+    By default, Tox tries to establish direct connections between peers; as a
+    consequence, each is aware of the other's IP address, and third parties
+    may be able to determine that a connection has been established between
+    those IP addresses. One of the reasons for making direct connections is that
+    relaying real-time multimedia conversations over anonymity networks is not
+    feasible with the current network infrastructure.
+\end{itemize}
+
+\section{Threat model}
+
+TODO(iphydf): Define one.
+
+\section{Data types}
+
+All data types are defined before their first use, and their binary protocol
+representation is given.  The protocol representations are normative and must
+be implemented exactly as specified.  For some types, human-readable
+representations are suggested.  An implementation may choose to provide no such
+representation or a different one.  The implementation is free to choose any
+in-memory representation of the specified types.
+
+Binary formats are specified in tables with length, type, and content
+descriptions.  If applicable, specific enumeration types are used, so types may
+be self-explanatory in some cases.  The length can be either a fixed number in
+bytes (e.g. \texttt{32}), a number in bits (e.g. \texttt{7} bit), a choice of
+lengths (e.g. \texttt{4 $|$ 16}), or an inclusive range (e.g. \texttt{[0,
+100]}). Open ranges are denoted \texttt{[n,]} to mean a minimum length of
+\texttt{n} with no specified maximum length.
+
+\section{Integers}
+
+The protocol uses four bounded unsigned integer types.  Bounded means they have
+an upper bound beyond which incrementing is not defined.  The integer types
+support modular arithmetic, so overflow wraps around to zero.  Unsigned means
+their lower bound is 0.  Signed integer types are not used.  The binary
+encoding of all integer types is a fixed-width byte sequence with the integer
+encoded in \href{https://en.wikipedia.org/wiki/Endianness}{Big Endian} unless
+stated otherwise.
+
+\begin{tabular}{l|l|l|l}
+  Type name  & C type            & Length & Upper bound \\
+  \hline
+  Word8      & \texttt{uint8\_t}  & 1      & 255 (0xff) \\
+  Word16     & \texttt{uint16\_t} & 2      & 65535 (0xffff) \\
+  Word32     & \texttt{uint32\_t} & 4      & 4294967295 (0xffffffff) \\
+  Word64     & \texttt{uint64\_t} & 8      & 18446744073709551615 (0xffffffffffffffff) \\
+\end{tabular}
+
+\section{Strings}
+
+A String is a data structure used for human readable text.  Strings are
+sequences of glyphs.  A glyph consists of one non-zero-width unicode code point
+and zero or more zero-width unicode code points.  The human-readable
+representation of a String starts and ends with a quotation mark (\texttt{"})
+and contains all human-readable glyphs verbatim.  Control characters are
+represented in an isomorphic human-readable way.  I.e. every control character
+has exactly one human-readable representation, and a mapping exists from the
+human-readable representation to the control character.  Therefore, the use of
+Unicode Control Characters (U+240x) is not permitted without additional marker.
+
+\input{src/Network/Tox/Crypto.lhs}
+\input{src/Network/Tox/NodeInfo.lhs}
+\input{src/Network/Tox/Protocol.lhs}
+\input{src/Network/Tox/DHT.lhs}
+
+\chapter{LAN discovery}
+
+LAN discovery is a way to discover Tox peers that are on a local network.  If
+two Tox friends are on a local network, the most efficient way for them to
+communicate together is to use the local network.  If a Tox client is opened on
+a local network in which another Tox client exists then good behavior would be
+to bootstrap to the network using the Tox client on the local network.  This is
+what LAN discovery aims to accomplish.
+
+LAN discovery works by sending a UDP packet through the toxcore UDP socket to
+the interface broadcast address on IPv4, the global broadcast address
+(255.255.255.255) and the multicast address on IPv6 (FF02::1) on the default
+Tox UDP port (33445).
+
+The LAN Discovery packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (33) \\
+  \texttt{32}        & DHT public key \\
+\end{tabular}
+
+LAN Discovery packets contain the DHT public key of the sender.  When a LAN
+Discovery packet is received, a DHT get nodes packet will be sent to the sender
+of the packet.  This means that the DHT instance will bootstrap itself to every
+peer from which it receives one of these packets.  Through this mechanism, Tox
+clients will bootstrap themselves automatically from other Tox clients running
+on the local network.
+
+When enabled, toxcore sends these packets every 10 seconds to keep delays low.
+The packets could be sent up to every 60 seconds but this would make peer
+finding over the network 6 times slower.
+
+LAN discovery enables two friends on a local network to find each other as the
+DHT prioritizes LAN addresses over non LAN addresses for DHT peers.  Sending a
+get node request/bootstrapping from a peer successfully should also add them to
+the list of DHT peers if we are searching for them.  The peer must not be
+immediately added if a LAN discovery packet with a DHT public key that we are
+searching for is received as there is no cryptographic proof that this packet
+is legitimate and not maliciously crafted.  This means that a DHT get node or
+ping packet must be sent, and a valid response must be received, before we can
+say that this peer has been found.
+
+LAN discovery is how Tox handles and makes everything work well on LAN.
+
+\chapter{Messenger}
+
+Messenger is the module at the top of all the other modules.  It sits on top of
+\texttt{friend\_connection} in the hierarchy of toxcore.
+
+Messenger takes care of sending and receiving messages using the connection
+provided by \texttt{friend\_connection}.  The module provides a way for friends
+to connect and makes it usable as an instant messenger.  For example, Messenger
+lets users set a nickname and status message which it then transmits to friends
+when they are online.  It also allows users to send messages to friends and
+builds an instant messenging system on top of the lower level
+\texttt{friend\_connection} module.
+
+Messenger offers two methods to add a friend.  The first way is to add a friend
+with only their long term public key, this is used when a friend needs to be
+added but for some reason a friend request should not be sent.  The friend
+should only be added.  This method is most commonly used to accept friend
+requests but could also be used in other ways.  If two friends add each other
+using this function they will connect to each other.  Adding a friend using
+this method just adds the friend to \texttt{friend\_connection} and creates a
+new friend entry in Messenger for the friend.
+
+The Tox ID is used to identify peers so that they can be added as friends in
+Tox.  In order to add a friend, a Tox user must have the friend's Tox ID. The
+Tox ID contains the long term public key of the peer (32 bytes) followed by the
+4 byte nospam (see: \texttt{friend\_requests}) value and a 2 byte XOR checksum.
+The method of sending the Tox ID to others is up to the user and the client but
+the recommended way is to encode it in hexadecimal format and have the user
+manually send it to the friend using another program.
+
+Tox ID:
+
+\begin{figure}
+\includegraphics{res/images/tox-id.png}
+\caption{Tox ID}
+\end{figure}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{32}        & long term public key \\
+  \texttt{4}         & nospam \\
+  \texttt{2}         & checksum \\
+\end{tabular}
+
+The checksum is calculated by XORing the first two bytes of the ID with the
+next two bytes, then the next two bytes until all the 36 bytes have been XORed
+together.  The result is then appended to the end to form the Tox ID.
+
+The user must make sure the Tox ID is not intercepted and replaced in transit
+by a different Tox ID, which would mean the friend would connect to a malicious
+person instead of the user, though taking reasonable precautions as this is
+outside the scope of Tox.  Tox assumes that the user has ensured that they are
+using the correct Tox ID, belonging to the intended person, to add a friend.
+
+The second method to add a friend is by using their Tox ID and a message to be
+sent in a friend request.  This way of adding friends will try to send a friend
+request, with the set message, to the peer whose Tox ID was added.  The method
+is similar to the first one, except that a friend request is crafted and sent
+to the other peer.
+
+When a friend connection associated to a Messenger friend goes online, a ONLINE
+packet will be sent to them.  Friends are only set as online if an ONLINE
+packet is received.
+
+As soon as a friend goes online, Messenger will stop sending friend requests to
+that friend, if it was sending them, as they are redundant for this friend.
+
+Friends will be set as offline if either the friend connection associated to
+them goes offline or if an OFFLINE packet is received from the friend.
+
+Messenger packets are sent to the friend using the online friend connection to
+the friend.
+
+Should Messenger need to check whether any of the non lossy packets in the
+following list were received by the friend, for example to implement receipts
+for text messages, \texttt{net\_crypto} can be used.  The \texttt{net\_crypto}
+packet number, used to send the packets, should be noted and then
+\texttt{net\_crypto} checked later to see if the bottom of the send array is
+after this packet number.  If it is, then the friend has received them.  Note
+that \texttt{net\_crypto} packet numbers could overflow after a long time, so
+checks should happen within 2**32 \texttt{net\_crypto} packets sent with the
+same friend connection.
+
+Message receipts for action messages and normal text messages are implemented
+by adding the \texttt{net\_crypto} packet number of each message, along with the
+receipt number, to the top of a linked list that each friend has as they are
+sent.  Every Messenger loop, the entries are read from the bottom and entries
+are removed and passed to the client until an entry that refers to a packet not
+yet received by the other is reached, when this happens it stops.
+
+List of Messenger packets:
+
+\section{\texttt{ONLINE}}
+
+length: 1 byte
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x18) \\
+\end{tabular}
+
+Sent to a friend when a connection is established to tell them to mark us as
+online in their friends list.  This packet and the OFFLINE packet are necessary
+as \texttt{friend\_connections} can be established with non-friends who are part
+of a groupchat.  The two packets are used to differentiate between these peers,
+connected to the user through groupchats, and actual friends who ought to be
+marked as online in the friendlist.
+
+On receiving this packet, Messenger will show the peer as being online.
+
+\section{\texttt{OFFLINE}}
+
+length: 1 byte
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x19) \\
+\end{tabular}
+
+Sent to a friend when deleting the friend.  Prevents a deleted friend from
+seeing us as online if we are connected to them because of a group chat.
+
+On receiving this packet, Messenger will show this peer as offline.
+
+\section{\texttt{NICKNAME}}
+
+length: 1 byte to 129 bytes.
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x30) \\
+  \texttt{[0, 128]}  & Nickname as a UTF8 byte string \\
+\end{tabular}
+
+Used to send the nickname of the peer to others.  This packet should be sent
+every time to each friend every time they come online and each time the
+nickname is changed.
+
+\section{\texttt{STATUSMESSAGE}}
+
+length: 1 byte to 1008 bytes.
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x31) \\
+  \texttt{[0, 1007]} & Status message as a UTF8 byte string \\
+\end{tabular}
+
+Used to send the status message of the peer to others.  This packet should be
+sent every time to each friend every time they come online and each time the
+status message is changed.
+
+\section{\texttt{USERSTATUS}}
+
+length: 2 bytes
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x32) \\
+  \texttt{1}         & \texttt{uint8\_t} status (0 = online, 1 = away, 2 = busy) \\
+\end{tabular}
+
+Used to send the user status of the peer to others.  This packet should be sent
+every time to each friend every time they come online and each time the user
+status is changed.
+
+\section{\texttt{TYPING}}
+
+length: 2 bytes
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x33) \\
+  \texttt{1}         & \texttt{uint8\_t} typing status (0 = not typing, 1 = typing) \\
+\end{tabular}
+
+Used to tell a friend whether the user is currently typing or not.
+
+\section{\texttt{MESSAGE}}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x40) \\
+  \texttt{[0, 1372]} & Message as a UTF8 byte string \\
+\end{tabular}
+
+Used to send a normal text message to the friend.
+
+\section{\texttt{ACTION}}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x41) \\
+  \texttt{[0, 1372]} & Action message as a UTF8 byte string \\
+\end{tabular}
+
+Used to send an action message (like an IRC action) to the friend.
+
+\section{\texttt{MSI}}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x45) \\
+  \texttt{?}         & data \\
+\end{tabular}
+
+Reserved for Tox AV usage.
+
+\section{File Transfer Related Packets}
+
+\subsection{\texttt{FILE\_SENDREQUEST}}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x50) \\
+  \texttt{1}         & \texttt{uint8\_t} file number \\
+  \texttt{4}         & \texttt{uint32\_t} file type \\
+  \texttt{8}         & \texttt{uint64\_t} file size \\
+  \texttt{32}        & file id (32 bytes) \\
+  \texttt{[0, 255]}  & filename as a UTF8 byte string \\
+\end{tabular}
+
+Note that file type and file size are sent in big endian/network byte format.
+
+\subsection{\texttt{FILE\_CONTROL}}
+
+length: 4 bytes if \texttt{control\_type} isn't seek.  8 bytes if
+\texttt{control\_type} is seek.
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x51) \\
+  \texttt{1}         & \texttt{uint8\_t} \texttt{send\_receive} \\
+  \texttt{1}         & \texttt{uint8\_t} file number \\
+  \texttt{1}         & \texttt{uint8\_t} \texttt{control\_type} \\
+  \texttt{8}         & \texttt{uint64\_t} seek parameter \\
+\end{tabular}
+
+\texttt{send\_receive} is 0 if the control targets a file being sent (by the
+peer sending the file control), and 1 if it targets a file being received.
+
+\texttt{control\_type} can be one of: 0 = accept, 1 = pause, 2 = kill, 3 = seek.
+
+The seek parameter is only included when \texttt{control\_type} is seek (3).
+
+Note that if it is included the seek parameter will be sent in big
+endian/network byte format.
+
+\subsection{\texttt{FILE\_DATA}}
+
+length: 2 to 1373 bytes.
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x52) \\
+  \texttt{1}         & \texttt{uint8\_t} file number \\
+  \texttt{[0, 1371]} & file data piece \\
+\end{tabular}
+
+Files are transferred in Tox using File transfers.
+
+To initiate a file transfer, the friend creates and sends a
+\texttt{FILE\_SENDREQUEST} packet to the friend it wants to initiate a file
+transfer to.
+
+The first part of the \texttt{FILE\_SENDREQUEST} packet is the file number.  The
+file number is the number used to identify this file transfer.  As the file
+number is represented by a 1 byte number, the maximum amount of concurrent
+files Tox can send to a friend is 256.  256 file transfers per friend is enough
+that clients can use tricks like queueing files if there are more files needing
+to be sent.
+
+256 outgoing files per friend means that there is a maximum of 512 concurrent
+file transfers, between two users, if both incoming and outgoing file transfers
+are counted together.
+
+As file numbers are used to identify the file transfer, the Tox instance must
+make sure to use a file number that isn't used for another outgoing file
+transfer to that same friend when creating a new outgoing file transfer.  File
+numbers are chosen by the file sender and stay unchanged for the entire
+duration of the file transfer.  The file number is used by both
+\texttt{FILE\_CONTROL} and \texttt{FILE\_DATA} packets to identify which file
+transfer these packets are for.
+
+The second part of the file transfer request is the file type.  This is simply
+a number that identifies the type of file.  for example, tox.h defines the file
+type 0 as being a normal file and type 1 as being an avatar meaning the Tox
+client should use that file as an avatar.  The file type does not effect in any
+way how the file is transfered or the behavior of the file transfer.  It is set
+by the Tox client that creates the file transfers and send to the friend
+untouched.
+
+The file size indicates the total size of the file that will be transfered.  A
+file size of \texttt{UINT64\_MAX} (maximum value in a \texttt{uint64\_t}) means
+that the size of the file is undetermined or unknown.  For example if someone
+wanted to use Tox file transfers to stream data they would set the file size to
+\texttt{UINT64\_MAX}.  A file size of 0 is valid and behaves exactly like a
+normal file transfer.
+
+The file id is 32 bytes that can be used to uniquely identify the file
+transfer.  For example, avatar transfers use it as the hash of the avatar so
+that the receiver can check if they already have the avatar for a friend which
+saves bandwidth.  It is also used to identify broken file transfers across
+toxcore restarts (for more info see the file transfer section of tox.h).  The
+file transfer implementation does not care about what the file id is, as it is
+only used by things above it.
+
+The last part of the file transfer is the optional file name which is used to
+tell the receiver the name of the file.
+
+When a \texttt{FILE\_SENDREQUEST} packet is received, the implementation
+validates and sends the info to the Tox client which decides whether they
+should accept the file transfer or not.
+
+To refuse or cancel a file transfer, they will send a \texttt{FILE\_CONTROL}
+packet with \texttt{control\_type} 2 (kill).
+
+\texttt{FILE\_CONTROL} packets are used to control the file transfer.
+\texttt{FILE\_CONTROL} packets are used to accept/unpause, pause, kill/cancel
+and seek file transfers.  The \texttt{control\_type} parameter denotes what the
+file control packet does.
+
+The \texttt{send\_receive} and file number are used to identify a specific file
+transfer.  Since file numbers for outgoing and incoming files are not related
+to each other, the \texttt{send\_receive} parameter is used to identify if the
+file number belongs to files being sent or files being received.  If
+\texttt{send\_receive} is 0, the file number corresponds to a file being sent by
+the user sending the file control packet.  If \texttt{send\_receive} is 1, it
+corresponds to a file being received by the user sending the file control
+packet.
+
+\texttt{control\_type} indicates the purpose of the \texttt{FILE\_CONTROL}
+packet.  \texttt{control\_type} of 0 means that the \texttt{FILE\_CONTROL} packet
+is used to tell the friend that the file transfer is accepted or that we are
+unpausing a previously paused (by us) file transfer.  \texttt{control\_type} of
+1 is used to tell the other to pause the file transfer.
+
+If one party pauses a file transfer, that party must be the one to unpause it.
+Should both sides pause a file transfer, both sides must unpause it before the
+file can be resumed.  For example, if the sender pauses the file transfer, the
+receiver must not be able to unpause it.  To unpause a file transfer,
+\texttt{control\_type} 0 is used.  Files can only be paused when they are in
+progress and have been accepted.
+
+\texttt{control\_type} 2 is used to kill, cancel or refuse a file transfer.
+When a \texttt{FILE\_CONTROL} is received, the targeted file transfer is
+considered dead, will immediately be wiped and its file number can be reused.
+The peer sending the \texttt{FILE\_CONTROL} must also wipe the targeted file
+transfer from their side.  This control type can be used by both sides of the
+transfer at any time.
+
+\texttt{control\_type} 3, the seek control type is used to tell the sender of
+the file to start sending from a different index in the file than 0.  It can
+only be used right after receiving a \texttt{FILE\_SENDREQUEST} packet and
+before accepting the file by sending a \texttt{FILE\_CONTROL} with
+\texttt{control\_type} 0.  When this \texttt{control\_type} is used, an extra 8
+byte number in big endian format is appended to the \texttt{FILE\_CONTROL} that
+is not present with other control types.  This number indicates the index in
+bytes from the beginning of the file at which the file sender should start
+sending the file.  The goal of this control type is to ensure that files can be
+resumed across core restarts.  Tox clients can know if they have received a
+part of a file by using the file id and then using this packet to tell the
+other side to start sending from the last received byte.  If the seek position
+is bigger or equal to the size of the file, the seek packet is invalid and the
+one receiving it will discard it.
+
+To accept a file Tox will therefore send a seek packet, if it is needed, and
+then send a \texttt{FILE\_CONTROL} packet with \texttt{control\_type} 0 (accept)
+to tell the file sender that the file was accepted.
+
+Once the file transfer is accepted, the file sender will start sending file
+data in sequential chunks from the beginning of the file (or the position from
+the \texttt{FILE\_CONTROL} seek packet if one was received).
+
+File data is sent using \texttt{FILE\_DATA} packets.  The file number
+corresponds to the file transfer that the file chunks belong to.  The receiver
+assumes that the file transfer is over as soon as a chunk with the file data
+size not equal to the maximum size (1371 bytes) is received.  This is how the
+sender tells the receiver that the file transfer is complete in file transfers
+where the size of the file is unknown (set to \texttt{UINT64\_MAX}).  The
+receiver also assumes that if the amount of received data equals to the file
+size received in the \texttt{FILE\_SENDREQUEST}, the file sending is finished
+and has been successfully received.  Immediately after this occurs, the
+receiver frees up the file number so that a new incoming file transfer can use
+that file number.  The implementation should discard any extra data received
+which is larger than the file size received at the beginning.
+
+In 0 filesize file transfers, the sender will send one \texttt{FILE\_DATA}
+packet with a file data size of 0.
+
+The sender will know if the receiver has received the file successfully by
+checking if the friend has received the last \texttt{FILE\_DATA} packet sent
+(containing the last chunk of the file).  \texttt{net\_crypto} can be used to
+check whether packets sent through it have been received by storing the packet
+number of the sent packet and verifying later in \texttt{net\_crypto} to see
+whether it was received or not.  As soon as \texttt{net\_crypto} says the other
+received the packet, the file transfer is considered successful, wiped and the
+file number can be reused to send new files.
+
+\texttt{FILE\_DATA} packets should be sent as fast as the \texttt{net\_crypto}
+connection can handle it respecting its congestion control.
+
+If the friend goes offline, all file transfers are cleared in toxcore.  This
+makes it simpler for toxcore as it does not have to deal with resuming file
+transfers.  It also makes it simpler for clients as the method for resuming
+file transfers remains the same, even if the client is restarted or toxcore
+loses the connection to the friend because of a bad internet connection.
+
+\section{Group Chat Related Packets}
+
+\begin{tabular}{l|l}
+  Packet ID & Packet Name \\
+  \hline
+  0x60      & \texttt{INVITE\_GROUPCHAT} \\
+  0x61      & \texttt{ONLINE\_PACKET} \\
+  0x62      & \texttt{DIRECT\_GROUPCHAT} \\
+  0x63      & \texttt{MESSAGE\_GROUPCHAT} \\
+  0xC7      & \texttt{LOSSY\_GROUPCHAT} \\
+\end{tabular}
+
+Messenger also takes care of saving the friends list and other friend
+information so that it's possible to close and start toxcore while keeping all
+your friends, your long term key and the information necessary to reconnect to
+the network.
+
+Important information messenger stores includes: the long term private key, our
+current nospam value, our friends' public keys and any friend requests the user
+is currently sending.  The network DHT nodes, TCP relays and some onion nodes
+are stored to aid reconnection.
+
+In addition to this, a lot of optional data can be stored such as the usernames
+of friends, our current username, status messages of friends, our status
+message, etc... can be stored.  The exact format of the toxcore save is
+explained later.
+
+The TCP server is run from the toxcore messenger module if the client has
+enabled it.  TCP server is usually run independently as part of the bootstrap
+node package but it can be enabled in clients.  If it is enabled in toxcore,
+Messenger will add the running TCP server to the TCP relay.
+
+Messenger is the module that transforms code that can connect to friends based
+on public key into a real instant messenger.
+
+\chapter{TCP client}
+
+\texttt{TCP client} is the client for the TCP server.  It establishes and keeps
+a connection to the TCP server open.
+
+All the packet formats are explained in detail in \texttt{TCP server} so this
+section will only cover \texttt{TCP client} specific details which are not
+covered in the \texttt{TCP server} documentation.
+
+TCP clients can choose to connect to TCP servers through a proxy.  Most common
+types of proxies (SOCKS, HTTP) work by establishing a connection through a
+proxy using the protocol of that specific type of proxy.  After the connection
+through that proxy to a TCP server is established, the socket behaves from the
+point of view of the application exactly like a TCP socket that connects
+directly to a TCP server instance.  This means supporting proxies is easy.
+
+\texttt{TCP client} first establishes a TCP connection, either through a proxy
+or directly to a TCP server.  It uses the DHT public key as its long term key
+when connecting to the TCP server.
+
+It establishes a secure connection to the TCP server.  After establishing a
+connection to the TCP server, and when the handshake response has been received
+from the TCP server, the toxcore implementation immediately sends a ping
+packet.  Ideally the first packets sent would be routing request packets but
+this solution aids code simplicity and allows the server to confirm the
+connection.
+
+Ping packets, like all other data packets, are sent as encrypted packets.
+
+Ping packets are sent by the toxcore TCP client every 30 seconds with a timeout
+of 10 seconds, the same interval and timeout as toxcore TCP server ping
+packets.  They are the same because they accomplish the same thing.
+
+\texttt{TCP client} must have a mechanism to make sure important packets
+(routing requests, disconnection notifications, ping packets, ping response
+packets) don't get dropped because the TCP socket is full.  Should this happen,
+the TCP client must save these packets and prioritize sending them, in order,
+when the TCP socket on the server becomes available for writing again.
+\texttt{TCP client} must also take into account that packets might be bigger
+than the number of bytes it can currently write to the socket.  In this case,
+it must save the bytes of the packet that it didn't write to the socket and
+write them to the socket as soon as the socket allows so that the connection
+does not get broken.  It must also assume that it may receive only part of an
+encrypted packet.  If this occurs it must save the part of the packet it has
+received and wait for the rest of the packet to arrive before handling it.
+
+\texttt{TCP client} can be used to open up a route to friends who are connected
+to the TCP server.  This is done by sending a routing request to the TCP server
+with the DHT public key of the friend.  This tells the server to register a
+\texttt{connection\_id} to the DHT public key sent in the packet.  The server
+will then respond with a routing response packet.  If the connection was
+accepted, the \texttt{TCP client} will store the \texttt{connection id} for
+this connection.  The \texttt{TCP client} will make sure that routing response
+packets are responses to a routing packet that it sent by storing that it sent
+a routing packet to that public key and checking the response against it.  This
+prevents the possibility of a bad TCP server exploiting the client.
+
+The \texttt{TCP client} will handle connection notifications and disconnection
+notifications by alerting the module using it that the connection to the peer
+is up or down.
+
+\texttt{TCP client} will send a disconnection notification to kill a connection
+to a friend.  It must send a disconnection notification packet regardless of
+whether the peer was online or offline so that the TCP server will unregister
+the connection.
+
+Data to friends can be sent through the TCP relay using OOB (out of band)
+packets and connected connections.  To send an OOB packet, the DHT public key
+of the friend must be known.  OOB packets are sent in blind and there is no way
+to query the TCP relay to see if the friend is connected before sending one.
+OOB packets should be sent when the connection to the friend via the TCP relay
+isn't in an connected state but it is known that the friend is connected to
+that relay.  If the friend is connected via the TCP relay, then normal data
+packets must be sent as they are smaller than OOB packets.
+
+OOB recv and data packets must be handled and passed to the module using it.
+
+\chapter{TCP connections}
+
+\texttt{TCP\_connections} takes care of handling multiple TCP client instances
+to establish a reliable connection via TCP relays to a friend.  Connecting to a
+friend with only one relay would not be very reliable, so
+\texttt{TCP\_connections} provides the level of abstraction needed to manage
+multiple relays.  For example, it ensures that if a relay goes down, the
+connection to the peer will not be impacted.  This is done by connecting to the
+other peer with more than one relay.
+
+\texttt{TCP\_connections} is above \href{#tcp-client}{\texttt{TCP client}} and
+below \texttt{net\_crypto}.
+
+A TCP connection in \texttt{TCP\_connections} is defined as a connection to a
+peer though one or more TCP relays.  To connect to another peer with
+\texttt{TCP\_connections}, a connection in \texttt{TCP\_connections} to the peer
+with DHT public key X will be created.  Some TCP relays which we know the peer
+is connected to will then be associated with that peer.  If the peer isn't
+connected directly yet, these relays will be the ones that the peer has sent to
+us via the onion module.  The peer will also send some relays it is directly
+connected to once a connection is established, however, this is done by another
+module.
+
+\texttt{TCP\_connections} has a list of all relays it is connected to.  It tries
+to keep the number of relays it is connected to as small as possible in order
+to minimize load on relays and lower bandwidth usage for the client.  The
+desired number of TCP relay connections per peer is set to 3 in toxcore with
+the maximum number set to 6.  The reason for these numbers is that 1 would mean
+no backup relays and 2 would mean only 1 backup.  To be sure that the
+connection is reliable 3 seems to be a reasonable lower bound.  The maximum
+number of 6 is the maximum number of relays that can be tied to each peer.  If
+2 peers are connected each to the same 6+ relays and they both need to be
+connected to that amount of relays because of other friends this is where this
+maximum comes into play.  There is no reason why this number is 6 but in
+toxcore it has to be at least double than the desired number (3) because the
+code assumes this.
+
+If necessary, \texttt{TCP\_connections} will connect to TCP relays to use them
+to send onion packets.  This is only done if there is no UDP connection to the
+network.  When there is a UDP connection, packets are sent with UDP only
+because sending them with TCP relays can be less reliable.  It is also
+important that we are connected at all times to some relays as these relays
+will be used by TCP only peers to initiate a connection to us.
+
+In toxcore, each client is connected to 3 relays even if there are no TCP peers
+and the onion is not needed.  It might be optimal to only connect to these
+relays when toxcore is initializing as this is the only time when peers will
+connect to us via TCP relays we are connected to.  Due to how the onion works,
+after the initialization phase, where each peer is searched in the onion and
+then if they are found the info required to connect back (DHT pk, TCP relays)
+is sent to them, there should be no more peers connecting to us via TCP relays.
+This may be a way to further reduce load on TCP relays, however, more research
+is needed before it is implemented.
+
+\texttt{TCP\_connections} picks one relay and uses only it for sending data to
+the other peer.  The reason for not picking a random connected relay for each
+packet is that it severely deteriorates the quality of the link between two
+peers and makes performance of lossy video and audio transmissions really poor.
+For this reason, one relay is picked and used to send all data.  If for any
+reason no more data can be sent through that relay, the next relay is used.
+This may happen if the TCP socket is full and so the relay should not
+necessarily be dropped if this occurs.  Relays are only dropped if they time
+out or if they become useless (if the relay is one too many or is no longer
+being used to relay data to any peers).
+
+\texttt{TCP\_connections} in toxcore also contains a mechanism to make
+connections go to sleep.  TCP connections to other peers may be put to sleep if
+the connection to the peer establishes itself with UDP after the connection is
+established with TCP.  UDP is the method preferred by \texttt{net\_crypto} to
+communicate with other peers.  In order to keep track of the relays which were
+used to connect with the other peer in case the UDP connection fails, they are
+saved by \texttt{TCP\_connections} when the connection is put to sleep.  Any
+relays which were only used by this redundant connection are saved then
+disconnected from.  If the connection is awakened, the relays are reconnected
+to and the connection is reestablished.  Putting a connection to sleep is the
+same as saving all the relays used by the connection and removing the
+connection.  Awakening the connection is the same as creating a new connection
+with the same parameters and restoring all the relays.
+
+A method to detect potentially dysfunctional relays that try to disrupt the
+network by lying that they are connecting to a peer when they are not or that
+maliciously drop all packets should be considered.  Toxcore doesn't currently
+implement such a system and adding one requires more research and likely also
+requires extending the protocol.
+
+When TCP connections connects to a relay it will create a new
+\href{#tcp-client}{\texttt{TCP\_client}} instance for that relay.  At any time
+if the \texttt{TCP\_client} instance reports that it has disconnected, the TCP
+relay will be dropped.  Once the TCP relay reports that it is connected,
+\texttt{TCP\_connections} will find all the connections that are associated to
+the relay and announce to the relay that it wants to connect to each of them
+with routing requests.  If the relay reports that the peer for a connection is
+online, the connection number and relay will be used to send data in that
+connection with data packets.  If the peer isn't reported as online but the
+relay is associated to a connection, TCP OOB (out of band) packets will be used
+to send data instead of data packets.  TCP OOB packets are used in this case
+since the relay most likely has the peer connected but it has not sent a
+routing request to connect to us.
+
+\texttt{TCP\_connections} is used as the bridge between individual
+\texttt{TCP\_client} instances and \texttt{net\_crypto}, or the bridge between
+individual connections and something that requires an interface that looks like
+one connection.
+
+\chapter{TCP server}
+
+The TCP server in tox has the goal of acting like a TCP relay between clients
+who cannot connect directly to each other or who for some reason are limited to
+using the TCP protocol to connect to each other.  \texttt{TCP\_server} is
+typically run only on actual server machines but any Tox client could host one
+as the api to run one is exposed through the tox.h api.
+
+To connect to a hosted TCP server toxcore uses the TCP client module.
+
+The TCP server implementation in toxcore can currently either work on epoll on
+linux or using unoptimized but portable socket polling.
+
+TCP connections between the TCP client and the server are encrypted to prevent
+an outsider from knowing information like who is connecting to whom just be
+looking at someones connection to a TCP server.  This is useful when someone
+connects though something like Tor for example.  It also prevents someone from
+injecting data in the stream and makes it so we can assume that any data
+received was not tampered with and is exactly what was sent by the client.
+
+When a client first connects to a TCP server he opens up a TCP connection to
+the ip and port the TCP server is listening on.  Once the connection is
+established he then sends a handshake packet, the server then responds with his
+own and a secure connection is established.  The connection is then said to be
+unconfirmed and the client must then send some encrypted data to the server
+before the server can mark the connection as confirmed.  The reason it works
+like this is to prevent a type of attack where a peer would send a handshake
+packet and then time out right away.  To prevent this the server must wait a
+few seconds for a sign that the client received his handshake packet before
+confirming the connection.  The both can then communicate with each other using
+the encrypted connection.
+
+The TCP server essentially acts as just a relay between 2 peers.  When a TCP
+client connects to the server he tells the server which clients he wants the
+server to connect him to.  The server will only let two clients connect to each
+other if both have indicated to the server that they want to connect to each
+other.  This is to prevent non friends from checking if someone is connected to
+a TCP server.  The TCP server supports sending packets blindly through it to
+clients with a client with public key X (OOB packets) however the TCP server
+does not give any feedback or anything to say if the packet arrived or not and
+as such it is only useful to send data to friends who may not know that we are
+connected to the current TCP server while we know they are.  This occurs when
+one peer discovers the TCP relay and DHT public key of the other peer before
+the other peer discovers its DHT public key.  In that case OOB packets would be
+used until the other peer knows that the peer is connected to the relay and
+establishes a connection through it.
+
+In order to make toxcore work on TCP only the TCP server supports relaying
+onion packets from TCP clients and sending any responses from them to TCP
+clients.
+
+To establish a secure connection with a TCP server send the following 128 bytes
+of data or handshake packet to the server:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{32}        & DHT public key of client \\
+  \texttt{24}        & Nonce for the encrypted data \\
+  \texttt{72}        & Payload (plus MAC) \\
+\end{tabular}
+
+Payload is encrypted with the DHT private key of the client and public key of
+the server and the nonce:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{32}        & Public key \\
+  \texttt{24}        & Base nonce \\
+\end{tabular}
+
+The base nonce is the one TCP client wants the TCP server to use to decrypt the
+packets received from the TCP client.
+
+The first 32 bytes are the public key (DHT public key) that the TCP client is
+announcing itself to the server with.  The next 24 bytes are a nonce which the
+TCP client uses along with the secret key associated with the public key in the
+first 32 bytes of the packet to encrypt the rest of this 'packet'.  The
+encrypted part of this packet contains a temporary public key that will be used
+for encryption during the connection and will be discarded after.  It also
+contains a base nonce which will be used later for decrypting packets received
+from the TCP client.
+
+If the server decrypts successfully the encrypted data in the handshake packet
+and responds with the following handshake response of length 96 bytes:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{24}        & Nonce for the encrypted data \\
+  \texttt{72}        & Payload (plus MAC) \\
+\end{tabular}
+
+Payload is encrypted with the private key of the server and the DHT public key
+of the client and the nonce:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{32}        & Public key \\
+  \texttt{24}        & Base nonce \\
+\end{tabular}
+
+The base nonce is the one the TCP server wants the TCP client to use to decrypt
+the packets received from the TCP server.
+
+The client already knows the long term public key of the server so it is
+omitted in the response, instead only a nonce is present in the unencrypted
+part.  The encrypted part of the response has the same elements as the
+encrypted part of the request: a temporary public key tied to this connection
+and a base nonce which will be used later when decrypting packets received from
+the TCP client both unique for the connection.
+
+In toxcore the base nonce is generated randomly like all the other nonces, it
+must be randomly generated to prevent nonce reuse.  For example if the nonce
+used was 0 for both sides since both sides use the same keys to encrypt packets
+they send to each other, two packets would be encrypted with the same nonce.
+These packets could then be possibly replayed back to the sender which would
+cause issues.  A similar mechanism is used in \texttt{net\_crypto}.
+
+After this the client will know the connection temporary public key and base
+nonce of the server and the server will know the connection base nonce and
+temporary public key of the client.
+
+The client will then send an encrypted packet to the server, the contents of
+the packet do not matter and it must be handled normally by the server (ex: if
+it was a ping send a pong response.  The first packet must be any valid
+encrypted data packet), the only thing that does matter is that the packet was
+encrypted correctly by the client because it means that the client has
+correctly received the handshake response the server sent to it and that the
+handshake the client sent to the server really came from the client and not
+from an attacker replaying packets.  The server must prevent resource consuming
+attacks by timing out clients if they do not send any encrypted packets so the
+server to prove to the server that the connection was established correctly.
+
+Toxcore does not have a timeout for clients, instead it stores connecting
+clients in large circular lists and times them out if their entry in the list
+gets replaced by a newer connection.  The reasoning behind this is that it
+prevents TCP flood attacks from having a negative impact on the currently
+connected nodes.  There are however much better ways to do this and the only
+reason toxcore does it this way is because writing it was very simple.  When
+connections are confirmed they are moved somewhere else.
+
+When the server confirms the connection he must look in the list of connected
+peers to see if he is already connected to a client with the same announced
+public key.  If this is the case the server must kill the previous connection
+because this means that the client previously timed out and is reconnecting.
+Because of Toxcore design it is very unlikely to happen that two legitimate
+different peers will have the same public key so this is the correct behavior.
+
+Encrypted data packets look like this to outsiders:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{2}         & \texttt{uint16\_t} length of data \\
+  variable           & encrypted data \\
+\end{tabular}
+
+In a TCP stream they would look like:
+\texttt{[[length][data]][[length][data]][[length][data]]...}.
+
+Both the client and server use the following (temp public and private (client
+and server) connection keys) which are each generated for the connection and
+then sent to the other in the handshake and sent to the other.  They are then
+used like the next diagram shows to generate a shared key which is equal on
+both sides.
+
+\begin{verbatim}
+Client:                                     Server:
+generate_shared_key(                        generate_shared_key(
+[temp connection public key of server],     [temp connection public key of client],
+[temp connection private key of client])    [temp connection private key of server])
+=                                           =
+[shared key]                                [shared key]
+\end{verbatim}
+
+The generated shared key is equal on both sides and is used to encrypt and
+decrypt the encrypted data packets.
+
+each encrypted data packet sent to the client will be encrypted with the shared
+key and with a nonce equal to: (client base nonce + number of packets sent so
+for the first packet it is (starting at 0) nonce + 0, the second is nonce + 1
+and so on.  Note that nonces like all other numbers sent over the network in
+toxcore are numbers in big endian format so when increasing them by 1 the least
+significant byte is the last one)
+
+each packet received from the client will be decrypted with the shared key and
+with a nonce equal to: (server base nonce + number of packets sent so for the
+first packet it is (starting at 0) nonce + 0, the second is nonce + 1 and so
+on.  Note that nonces like all other numbers sent over the network in toxcore
+are numbers in big endian format so when increasing them by 1 the least
+significant byte is the last one)
+
+Encrypted data packets have a hard maximum size of 2 + 2048 bytes in the
+toxcore TCP server implementation, 2048 bytes is big enough to make sure that
+all toxcore packets can go through and leaves some extra space just in case the
+protocol needs to be changed in the future.  The 2 bytes represents the size of
+the data length and the 2048 bytes the max size of the encrypted part.  This
+means the maximum size is 2050 bytes.  In current toxcore, the largest
+encrypted data packets sent will be of size 2 + 1417 which is 1419 total.
+
+The logic behind the format of the handshake is that we:
+
+\begin{enumerate}
+\item need to prove to the server that we own the private key related to the public
+   key we are announcing ourselves with.
+\item need to establish a secure connection that has perfect forward secrecy
+\item prevent any replay, impersonation or other attacks
+\end{enumerate}
+
+How it accomplishes each of those points:
+
+\begin{enumerate}
+  \item If the client does not own the private key related to the public key they
+    will not be able to create the handshake packet.
+  \item Temporary session keys generated by the client and server in the encrypted
+    part of the handshake packets are used to encrypt/decrypt packets during the
+    session.
+  \item The following attacks are prevented:
+    \begin{itemize}
+      \item Attacker modifies any byte of the handshake packets: Decryption fail, no
+        attacks possible.
+      \item Attacker captures the handshake packet from the client and replays it
+        later to the server: Attacker will never get the server to confirm the
+        connection (no effect).
+      \item Attacker captures a server response and sends it to the client next time
+        they try to connect to the server: Client will never confirm the
+        connection. (See: \texttt{TCP\_client})
+      \item Attacker tries to impersonate a server: They won't be able to decrypt the
+        handshake and won't be able to respond.
+      \item Attacker tries to impersonate a client: Server won't be able to decrypt
+        the handshake.
+    \end{itemize}
+\end{enumerate}
+
+The logic behind the format of the encrypted packets is that:
+
+\begin{enumerate}
+  \item TCP is a stream protocol, we need packets.
+  \item Any attacks must be prevented
+\end{enumerate}
+
+How it accomplishes each of those points:
+
+\begin{enumerate}
+  \item 2 bytes before each packet of encrypted data denote the length.  We assume a
+     functioning TCP will deliver bytes in order which makes it work.  If the TCP
+     doesn't it most likely means it is under attack and for that see the next
+     point.
+  \item The following attacks are prevented:
+    \begin{itemize}
+      \item Modifying the length bytes will either make the connection time out
+        and/or decryption fail.
+      \item Modifying any encrypted bytes will make decryption fail.
+      \item Injecting any bytes will make decryption fail.
+      \item Trying to re order the packets will make decryption fail because of the
+        ordered nonce.
+      \item Removing any packets from the stream will make decryption fail because of
+        the ordered nonce.
+    \end{itemize}
+\end{enumerate}
+
+\section{Encrypted payload types}
+
+The folowing represents the various types of data that can be sent inside
+encrypted data packets.
+
+\subsection{Routing request (0x00)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x00) \\
+  \texttt{32}        & Public key \\
+\end{tabular}
+
+\subsection{Routing request response (0x01)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x01) \\
+  \texttt{1}         & \texttt{uint8\_t} rpid \\
+  \texttt{32}        & Public key \\
+\end{tabular}
+
+rpid is invalid \texttt{connection\_id} (0) if refused, \texttt{connection\_id} if accepted.
+
+\subsection{Connect notification (0x02)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x02) \\
+  \texttt{1}         & \texttt{uint8\_t} \texttt{connection\_id} of connection that got connected \\
+\end{tabular}
+
+\subsection{Disconnect notification (0x03)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x03) \\
+  \texttt{1}         & \texttt{uint8\_t} \texttt{connection\_id} of connection that got disconnected \\
+\end{tabular}
+
+\subsection{Ping packet (0x04)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x04) \\
+  \texttt{8}         & \texttt{uint64\_t} \texttt{ping\_id} (0 is invalid) \\
+\end{tabular}
+
+\subsection{Ping response (pong) (0x05)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x05) \\
+  \texttt{8}         & \texttt{uint64\_t} \texttt{ping\_id} (0 is invalid) \\
+\end{tabular}
+
+\subsection{OOB send (0x06)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x06) \\
+  \texttt{32}        & Destination public key \\
+  variable           & Data \\
+\end{tabular}
+
+\subsection{OOB recv (0x07)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x07) \\
+  \texttt{32}        & Sender public key \\
+  variable           & Data \\
+\end{tabular}
+
+\subsection{Onion packet (0x08)}
+
+Same format as initial onion packet but packet id is 0x08 instead of 0x80.
+
+\subsection{Onion packet response (0x09)}
+
+Same format as onion packet but packet id is 0x09 instead of 0x8e.
+
+\subsection{Data (0x10 and up)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} packet id \\
+  \texttt{1}         & \texttt{uint8\_t} connection id \\
+  variable           & data \\
+\end{tabular}
+
+The TCP server is set up in a way to minimize waste while relaying the many
+packets that might go between two tox peers hence clients must create
+connections to other clients on the relay.  The connection number is a
+\texttt{uint8\_t} and must be equal or greater to 16 in order to be valid.
+Because a \texttt{uint8\_t} has a maximum value of 256 it means that the maximum
+number of different connections to other clients that each connection can have
+is 240.  The reason valid \texttt{connection\_ids} are bigger than 16 is because
+they are the first byte of data packets.  Currently only number 0 to 9 are
+taken however we keep a few extras in case we need to extend the protocol
+without breaking it completely.
+
+Routing request (Sent by client to server): Send a routing request to the
+server that we want to connect to peer with public key where the public key is
+the public the peer announced themselves as.  The server must respond to this
+with a Routing response.
+
+Routing response (Sent by server to client): The response to the routing
+request, tell the client if the routing request succeeded (valid
+\texttt{connection\_id}) and if it did, tell them the id of the connection
+(\texttt{connection\_id}).  The public key sent in the routing request is also
+sent in the response so that the client can send many requests at the same time
+to the server without having code to track which response belongs to which
+public key.
+
+The only reason a routing request should fail is if the connection has reached
+the maximum number of simultaneous connections.  In case the routing request
+fails the public key in the response will be the public key in the failed
+request.
+
+Connect notification (Sent by server to client): Tell the client that
+\texttt{connection\_id} is now connected meaning the other is online and data
+can be sent using this \texttt{connection\_id}.
+
+Disconnect notification (Sent by client to server): Sent when client wants the
+server to forget about the connection related to the \texttt{connection\_id} in
+the notification.  Server must remove this connection and must be able to reuse
+the \texttt{connection\_id} for another connection.  If the connection was
+connected the server must send a disconnect notification to the other client.
+The other client must think that this client has simply disconnected from the
+TCP server.
+
+Disconnect notification (Sent by server to client): Sent by the server to the
+client to tell them that the connection with \texttt{connection\_id} that was
+connected is now disconnected.  It is sent either when the other client of the
+connection disconnect or when they tell the server to kill the connection (see
+above).
+
+Ping and Pong packets (can be sent by both client and server, both will
+respond): ping packets are used to know if the other side of the connection is
+still live.  TCP when established doesn't have any sane timeouts (1 week isn't
+sane) so we are obliged to have our own way to check if the other side is still
+live.  Ping ids can be anything except 0, this is because of how toxcore sets
+the variable storing the \texttt{ping\_id} that was sent to 0 when it receives a
+pong response which means 0 is invalid.
+
+The server should send ping packets every X seconds (toxcore
+\texttt{TCP\_server} sends them every 30 seconds and times out the peer if it
+doesn't get a response in 10).  The server should respond immediately to ping
+packets with pong packets.
+
+The server should respond to ping packets with pong packets with the same
+\texttt{ping\_id} as was in the ping packet.  The server should check that each
+pong packet contains the same \texttt{ping\_id} as was in the ping, if not the
+pong packet must be ignored.
+
+OOB send (Sent by client to server): If a peer with private key equal to the
+key they announced themselves with is connected, the data in the OOB send
+packet will be sent to that peer as an OOB recv packet.  If no such peer is
+connected, the packet is discarded.  The toxcore \texttt{TCP\_server}
+implementation has a hard maximum OOB data length of 1024.  1024 was picked
+because it is big enough for the \texttt{net\_crypto} packets related to the
+handshake and is large enough that any changes to the protocol would not
+require breaking TCP server.  It is however not large enough for the biggest
+\texttt{net\_crypto} packets sent with an established \texttt{net\_crypto}
+connection to prevent sending those via OOB packets.
+
+OOB recv (Sent by server to client): OOB recv are sent with the announced
+public key of the peer that sent the OOB send packet and the exact data.
+
+OOB packets can be used just like normal data packets however the extra size
+makes sending data only through them less efficient than data packets.
+
+Data: Data packets can only be sent and received if the corresponding
+\texttt{connection\_id} is connection (a Connect notification has been received
+from it) if the server receives a Data packet for a non connected or existent
+connection it will discard it.
+
+Why did I use different packet ids for all packets when some are only sent by
+the client and some only by the server? It's less confusing.
+
+\chapter{Friend connection}
+
+\texttt{friend\_connection} is the module that sits on top of the DHT, onion and
+\texttt{net\_crypto} modules and takes care of linking the 3 together.
+
+Friends in \texttt{friend\_connection} are represented by their real public key.
+When a friend is added in \texttt{friend\_connection}, an onion search entry is
+created for that friend.  This means that the onion module will start looking
+for this friend and send that friend their DHT public key, and the TCP relays
+it is connected to, in case a connection is only possible with TCP.
+
+Once the onion returns the DHT public key of the peer, the DHT public key is
+saved, added to the DHT friends list and a new \texttt{net\_crypto} connection
+is created.  Any TCP relays returned by the onion for this friend are passed to
+the \texttt{net\_crypto} connection.
+
+If the DHT establishes a direct UDP connection with the friend,
+\texttt{friend\_connection} will pass the IP/port of the friend to
+\texttt{net\_crypto} and also save it to be used to reconnect to the friend if
+they disconnect.
+
+If \texttt{net\_crypto} finds that the friend has a different DHT public key,
+which can happen if the friend restarted their client, \texttt{net\_crypto} will
+pass the new DHT public key to the onion module and will remove the DHT entry
+for the old DHT public key and replace it with the new one.  The current
+\texttt{net\_crypto} connection will also be killed and a new one with the
+correct DHT public key will be created.
+
+When the \texttt{net\_crypto} connection for a friend goes online,
+\texttt{friend\_connection} will tell the onion module that the friend is online
+so that it can stop spending resources looking for the friend.  When the friend
+connection goes offline, \texttt{friend\_connection} will tell the onion module
+so that it can start looking for the friend again.
+
+There are 2 types of data packets sent to friends with the \texttt{net\_crypto}
+connection handled at the level of \texttt{friend\_connection}, Alive packets
+and TCP relay packets.  Alive packets are packets with the packet id or first
+byte of data (only byte in this packet) being 16.  They are used in order to
+check if the other friend is still online.  \texttt{net\_crypto} does not have
+any timeout when the connection is established so timeouts are caught using
+this packet.  In toxcore, this packet is sent every 8 seconds.  If none of
+these packets are received for 32 seconds, the connection is timed out and
+killed.  These numbers seem to cause the least issues and 32 seconds is not too
+long so that, if a friend times out, toxcore won't falsely see them online for
+too long.  Usually when a friend goes offline they have time to send a
+disconnect packet in the \texttt{net\_crypto} connection which makes them appear
+offline almost instantly.
+
+The timeout for when to stop retrying to connect to a friend by creating new
+\texttt{net\_crypto} connections when the old one times out in toxcore is the
+same as the timeout for DHT peers (122 seconds).  However, it is calculated
+from the last time a DHT public key was received for the friend or time the
+friend's \texttt{net\_crypto} connection went offline after being online.  The
+highest time is used to calculate when the timeout is.  \texttt{net\_crypto}
+connections will be recreated (if the connection fails) until this timeout.
+
+\texttt{friend\_connection} sends a list of 3 relays (the same number as the
+target number of TCP relay connections in \texttt{TCP\_connections}) to each
+connected friend every 5 minutes in toxcore.  Immediately before sending the
+relays, they are associated to the current \texttt{net\_crypto->TCP\_connections}
+connection.  This facilitates connecting the two friends together using the
+relays as the friend who receives the packet will associate the sent relays to
+the \texttt{net\_crypto} connection they received it from.  When both sides do
+this they will be able to connect to each other using the relays.  The packet
+id or first byte of the packet of share relay packets is 0x11.  This is then
+followed by some TCP relays stored in packed node format.
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x11) \\
+  variable           & TCP relays in packed node format (see DHT) \\
+\end{tabular}
+
+If local IPs are received as part of the packet, the local IP will be replaced
+with the IP of the peer that sent the relay.  This is because we assume this is
+the best way to attempt to connect to the TCP relay.  If the peer that sent the
+relay is using a local IP, then the sent local IP should be used to connect to
+the relay.
+
+For all other data packets, are passed by \texttt{friend\_connection} up to the
+upper Messenger module.  It also separates lossy and lossless packets from
+\texttt{net\_crypto}.
+
+Friend connection takes care of establishing the connection to the friend and
+gives the upper messenger layer a simple interface to receive and send
+messages, add and remove friends and know if a friend is connected (online) or
+not connected (offline).
+
+\chapter{Friend requests}
+
+When a Tox user adds someone with Tox, toxcore will try sending a friend
+request to that person.  A friend request contains the long term public key of
+the sender, a nospam number and a message.
+
+Transmitting the long term public key is the primary goal of the friend request
+as it is what the peer needs to find and establish a connection to the sender.
+The long term public key is what the receiver adds to his friends list if he
+accepts the friend request.
+
+The nospam is a number used to prevent someone from spamming the network with
+valid friend requests.  It makes sure that the only people who have seen the
+Tox ID of a peer are capable of sending them a friend request.  The nospam is
+one of the components of the Tox ID.
+
+The nospam is a number or a list of numbers set by the peer, only received
+friend requests that contain a nospam that was set by the peer are sent to the
+client to be accepted or refused by the user.  The nospam prevents random peers
+in the network from sending friend requests to non friends.  The nospam is not
+long enough to be secure meaning an extremely resilient attacker could manage
+to send a spam friend request to someone.  4 bytes is large enough to prevent
+spam from random peers in the network.  The nospam could also allow Tox users
+to issue different Tox IDs and even change Tox IDs if someone finds a Tox ID
+and decides to send it hundreds of spam friend requests.  Changing the nospam
+would stop the incoming wave of spam friend requests without any negative
+effects to the users friends list.  For example if users would have to change
+their public key to prevent them from receiving friend requests it would mean
+they would have to essentially abandon all their current friends as friends are
+tied to the public key.  The nospam is not used at all once the friends have
+each other added which means changing it won't have any negative effects.
+
+Friend request:
+
+\begin{verbatim}
+[uint32_t nospam][Message (UTF8) 1 to ONION_CLIENT_MAX_DATA_SIZE bytes]
+\end{verbatim}
+
+Friend request packet when sent as an onion data packet:
+
+\begin{verbatim}
+[uint8_t (32)][Friend request]
+\end{verbatim}
+
+Friend request packet when sent as a \texttt{net\_crypto} data packet (If we are
+directly connected to the peer because of a group chat but are not friends with
+them):
+
+\begin{verbatim}
+[uint8_t (18)][Friend request]
+\end{verbatim}
+
+When a friend is added to toxcore with their Tox ID and a message, the friend
+is added in \texttt{friend\_connection} and then toxcore tries to send friend
+requests.
+
+When sending a friend request, toxcore will check if the peer which a friend
+request is being sent to is already connected to using a \texttt{net\_crypto}
+connection which can happen if both are in the same group chat.  If this is the
+case the friend request will be sent as a \texttt{net\_crypto} packet using that
+connection.  If not, it will be sent as an onion data packet.
+
+Onion data packets contain the real public key of the sender and if a
+\texttt{net\_crypto} connection is established it means the peer knows our real
+public key.  This is why the friend request does not need to contain the real
+public key of the peer.
+
+Friend requests are sent with exponentially increasing interval of 2 seconds, 4
+seconds, 8 seconds, etc... in toxcore.  This is so friend requests get resent
+but eventually get resent in intervals that are so big that they essentially
+expire.  The sender has no way of knowing if a peer refuses a friend requests
+which is why friend requests need to expire in some way.  Note that the
+interval is the minimum timeout, if toxcore cannot send that friend request it
+will try again until it manages to send it.  One reason for not being able to
+send the friend request would be that the onion has not found the friend in the
+onion and so cannot send an onion data packet to them.
+
+Received friend requests are passed to the client, the client is expected to
+show the message from the friend request to the user and ask the user if they
+want to accept the friend request or not.  Friend requests are accepted by
+adding the peer sending the friend request as a friend and refused by simply
+ignoring it.
+
+Friend requests are sent multiple times meaning that in order to prevent the
+same friend request from being sent to the client multiple times toxcore keeps
+a list of the last real public keys it received friend requests from and
+discards any received friend requests that are from a real public key that is
+in that list.  In toxcore this list is a simple circular list.  There are many
+ways this could be improved and made more efficient as a circular list isn't
+very efficient however it has worked well in toxcore so far.
+
+Friend requests from public keys that are already added to the friends list
+should also be discarded.
+
+\chapter{Group}
+
+Group chats in Tox work by temporarily adding some peers present in the group
+chat as temporary \texttt{friend\_connection} friends, that are deleted when the
+group chat is exited.
+
+Each peer in the group chat is identified by their real long term public key.
+Peers also transmit their DHT public keys to each other via the group chat in
+order to speed up the connection by making it unnecessary for the peers to find
+each other's DHT public keys with the onion, as would happen had they added each
+other as normal friends.
+
+The upside of using \texttt{friend\_connection} is that group chats do not have
+to deal with things like hole punching, peers only on TCP or other low level
+networking things.  The downside however is that every single peer knows each
+other's real long term public key and DHT public key, meaning that these group
+chats should only be used between friends.
+
+Each peer adds a \texttt{friend\_connection} for each of up to 4 other peers in
+the group. If the group chat has 5 participants or fewer, each of the peers will
+therefore have each of the others added to their list of friend connections, and
+a peer wishing to send a message to the group may communicate it directly to the
+other peers. When there are more than 5 peers, messages are relayed along friend
+connections.
+
+Since the maximum number of peers per groupchat that will be connected to with
+friend connections is 4, if the peers in the groupchat are arranged in a circle
+and each peer connects to the 2 peers that are closest to the right of them and
+the 2 peers that are closest to the left of them, then the peers should form a
+well-connected circle of peers.
+
+Group chats in toxcore do this by subtracting the real long term public key of
+the peer with all the others in the group (our PK - other peer PK), using
+modular arithmetic, and finding the two peers for which the result of this
+operation is the smallest. The operation is then inversed (other peer PK - our
+PK) and this operation is done again with all the public keys of the peers in
+the group. The 2 peers for which the result is again the smallest are picked.
+
+This gives 4 peers that are then added as a friend connection and associated to
+the group.  If every peer in the group does this, they will form a circle of
+perfectly connected peers.
+
+Once the peers are connected to each other in a circle they relay each other's
+messages.  Every time a peer leaves the group or a new peer joins, each member
+of the chat will recalculate the peers they should connect to.
+
+To join a group chat, a peer must first be invited to it by their friend.  To
+make a groupchat the peer will first create a groupchat and then invite people
+to this group chat.  Once their friends are in the group chat, those friends can
+invite their other friends to the chat, and so on.
+
+To create a group chat, a peer generates a random 32 byte id that is used to
+uniquely identify the group chat.  32 bytes is enough so that when randomly
+generated with a secure random number generator every groupchat ever created
+will have a different id.  The goal of this 32 byte id is so that peers have a
+way of identifying each group chat, so that they can prevent themselves from
+joining a groupchat twice for example.
+
+The groupchat will also have an unsigned 1 byte type.  This type indicates what
+kind of groupchat the groupchat is. The current types are:
+
+\begin{tabular}{l|l}
+  Type number       & Type \\
+  \hline
+  \texttt{0}        & text \\
+  \texttt{1}        & audio \\
+\end{tabular}
+
+Text groupchats are text only, while audio indicates that the groupchat supports
+sending audio to it as well as text.
+
+The groupchat will also be identified by a unique unsigned 2 byte integer, which
+in toxcore corresponds to the index of the groupchat in the array it is being
+stored in.  Every groupchat in the current instance must have a different
+number.  This number is used by groupchat peers that are directly connected to
+us to tell us which packets are for which groupchat.  Every groupchat packet
+contains a 2 byte groupchat number.  Putting a 32 byte groupchat id in each
+packet would increase bandwidth waste by a lot, and this is the reason why
+groupchat numbers are used instead.
+
+Using the group number as the index of the array used to store the groupchat
+instances is recommended, because this kind of access is usually most efficient
+and it ensures that each groupchat has a unique group number.
+
+When creating a new groupchat, the peer will add themselves as a groupchat peer
+with a peer number of 0 and their own long term public key and DHT public key.
+
+Invite packets:
+
+Invite packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x60) \\
+  \texttt{1}         & \texttt{uint8\_t} (0x00) \\
+  \texttt{2}         & \texttt{uint16\_t} group number \\
+  \texttt{33}        & Group chat identifier \\
+\end{tabular}
+
+Accept Invite packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x60) \\
+  \texttt{1}         & \texttt{uint8\_t} (0x01) \\
+  \texttt{2}         & \texttt{uint16\_t} group number (local) \\
+  \texttt{2}         & \texttt{uint16\_t} group number to join \\
+  \texttt{33}        & Group chat identifier \\
+\end{tabular}
+
+Member Information packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x60) \\
+  \texttt{1}         & \texttt{uint8\_t} (0x02) \\
+  \texttt{2}         & \texttt{uint16\_t} group number (local) \\
+  \texttt{2}         & \texttt{uint16\_t} group number to join \\
+  \texttt{33}        & Group chat identifier \\
+  \texttt{2}         & \texttt{uint16\_t} peer number \\
+\end{tabular}
+
+A group chat identifier consists of a 1-byte type and a 32-byte id concatenated:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} type \\
+  \texttt{32}        & \texttt{uint8\_t} groupchat id \\
+\end{tabular}
+
+To invite a friend to a group chat, an invite packet is sent to the friend.
+These packets are sent using Messenger (if you look at the Messenger packet id
+section, all the groupchat packet ids are in there).  Note that all numbers
+here, like all numbers sent using Tox packets, are sent in big endian format.
+
+The group chat number is as explained above, the number used to uniquely
+identify the groupchat instance from all the other groupchat instances the peer
+has.  It is sent in the invite packet because it is needed by the friend in
+order to send back groupchat related packets.
+
+What follows is the 33 byte group chat identifier.
+
+To refuse the invite, the friend receiving it will simply ignore and discard
+it.
+
+To accept the invite, the friend will create their own groupchat instance with
+the 1 byte type and 32 byte groupchat id sent in the request, and send an invite
+accept packet back.  The friend will also add the peer who sent the invite as
+a groupchat connection, and mark the connection as introducing the friend.
+
+If the friend being invited is already in the group, they will respond with a
+member information packet, add the peer who sent the invite as a groupchat
+connection, and mark the connection as introducing both the friend and the
+peer who sent the invite.
+
+The first group number in the invite accept packet is the group number of the
+groupchat the invited friend just created.  The second group number is the
+group number that was sent in the invite request.  What follows is the 33 byte
+group chat identifier which was sent in the invite request. The member
+information packet is the same, but includes also the current peer number of
+the invited friend.
+
+When a peer receives an invite accept packet they will check if the group
+identifier sent back corresponds to the group identifier of the groupchat with
+the group number also sent back.  If so, a new peer number will be generated for
+the peer that sent the invite accept packet.  Then the peer with their
+generated peer number, their long term public key and their DHT public key will
+be added to the peer list of the groupchat.  A new peer message packet will also
+be sent to tell everyone in the group chat about the new peer.  The peer will
+also be added as a groupchat connection, and the connection will be marked as
+introducing the peer.
+
+When a peer receives a member information packet they proceed as with an
+invite accept packet, but use the peer number in the packet rather than
+generating a new one, and mark the new connection as also introducing the peer
+receiving the member information packet.
+
+Peer numbers are used to uniquely identify each peer in the group chat.  They
+are used in groupchat message packets so that peers receiving them can know who
+or which groupchat peer sent them.  As groupchat message packets are relayed,
+they must contain something that is used by others to identify the sender. Since
+putting a 32 byte public key in each packet would be wasteful, a 2 byte peer
+number is used instead.  Each peer in the groupchat has a unique peer number.
+Toxcore generates each peer number randomly but makes sure newly generated peer
+numbers are not equal to current ones already used by other peers in the group
+chat. If two peers join the groupchat from two different endpoints there is a
+small possibility that both will be given the same peer number, but the
+probability of this occurring is low enough in practice that it is not an issue.
+
+Peer online packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x61) \\
+  \texttt{2}         & \texttt{uint16\_t} group number (local) \\
+  \texttt{33}        & Group chat identifier \\
+\end{tabular}
+
+Peer introduced packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x62) \\
+  \texttt{2}         & \texttt{uint16\_t} group number (local) \\
+  \texttt{1}         & \texttt{uint8\_t} (0x01) \\
+\end{tabular}
+
+For a groupchat connection to work, both peers in the groupchat must be
+attempting to connect directly to each other.
+
+Groupchat connections are established when both peers who want to connect to
+each other either create a new friend connection to connect to each other or
+reuse an existing friend connection that connects them together (if they are
+friends or already are connected together because of another group chat).
+
+As soon as the connection to the other peer is opened, a peer online packet is
+sent to the peer.  The goal of the online packet is to tell the peer that we
+want to establish the groupchat connection with them and tell them the
+groupchat number of our groupchat instance.  The peer online packet contains
+the group number and the 33 byte group chat identifier.  The group number is the
+group number the peer has for the group with the group id sent in the packet.
+
+When both sides send an online packet to the other peer, a connection is
+established.
+
+When an online packet is received from a peer, if the connection to the peer
+is already established (an online packet has been already received), or if
+there is no group connection to that peer being established, the packet is
+dropped. Otherwise, the group number to communicate with the group via the
+peer is saved, the connection is considered established, and an online packet
+is sent back to the peer. A ping message is sent to the group. If this is the
+first group connection to that group we establish, or the connection is marked
+as introducing us, we send a peer query packet back to the peer.  This is so
+we can get the list of peers from the group. If the connection is marked as
+introducing the peer, we send a new peer message to the group announcing the
+peer, and a name message reannouncing our name.
+
+A groupchat connection can be marked as introducing one or both of the peers it
+connects, to indicate that the connection should be maintained until that peer
+is well connected to the group. A peer maintains a groupchat connection to a
+second peer as long as the second peer is one of the four closest peers in the
+groupchat to the first, or the connection is marked as introducing a peer who
+still requires the connection. A peer requires a groupchat connection to a
+second peer which introduces the first peer until the first peer has more than
+4 groupchat connections and receives a message from the second peer via a
+different groupchat connection. The first peer then sends a peer introduced
+packet to the second peer to indicate that they no longer require the
+connection.
+
+Peer query packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x62) \\
+  \texttt{2}         & \texttt{uint16\_t} group number \\
+  \texttt{1}         & \texttt{uint8\_t} (0x08) \\
+\end{tabular}
+
+Peer response packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x62) \\
+  \texttt{2}         & \texttt{uint16\_t} group number \\
+  \texttt{1}         & \texttt{uint8\_t} (0x09) \\
+  variable           & Repeated times number of peers: Peer info \\
+\end{tabular}
+
+The Peer info structure is as follows:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{2}         & \texttt{uint16\_t} peer number \\
+  \texttt{32}        & Long term public key \\
+  \texttt{32}        & DHT public key \\
+  \texttt{1}         & \texttt{uint8\_t} Name length \\
+  \texttt{[0, 255]}  & Name \\
+\end{tabular}
+
+Title response packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x62) \\
+  \texttt{2}         & \texttt{uint16\_t} group number \\
+  \texttt{1}         & \texttt{uint8\_t} (0x0a) \\
+  variable           & Title \\
+\end{tabular}
+
+Message packets:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x63) \\
+  \texttt{2}         & \texttt{uint16\_t} group number \\
+  \texttt{2}         & \texttt{uint16\_t} peer number \\
+  \texttt{4}         & \texttt{uint32\_t} message number \\
+  \texttt{1}         & \texttt{uint8\_t} with a value representing id of message \\
+  variable           & Data \\
+\end{tabular}
+
+Lossy Message packets:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0xc7) \\
+  \texttt{2}         & \texttt{uint16\_t} group number \\
+  \texttt{2}         & \texttt{uint16\_t} peer number \\
+  \texttt{2}         & \texttt{uint16\_t} message number \\
+  \texttt{1}         & \texttt{uint8\_t} with a value representing id of message \\
+  variable           & Data \\
+\end{tabular}
+
+If a peer query packet is received, the receiver takes their list of peers and
+creates a peer response packet which is then sent to the other peer.  If there
+are too many peers in the group chat and the peer response packet would be
+larger than the maximum size of friend connection packets (1373 bytes), more
+than one peer response packet is sent back.  A Title response packet is also
+sent back.  This is how the peer that joins a group chat finds out the list of
+peers in the group chat and the title of the group chat right after joining.
+
+Peer response packets are straightforward and contain the information for each
+peer (peer number, real public key, DHT public key, name) appended to each
+other.  The title response is also straightforward.
+
+Both the maximum length of groupchat peer names and the groupchat title is 128
+bytes.  This is the same maximum length as names in all of toxcore.
+
+When a peer receives a peer response packet, they will add each of the
+received peers to their groupchat peer list, find the 4 closest peers to them
+and create groupchat connections to them as was explained previously. The DHT
+public key of an already known peer is updated to one given in the response
+packet if the peer is frozen, or if it has been frozen since its DHT public
+key was last updated.
+
+When a peer receives a title response packet, they update the title for the
+groupchat accordingly if the title has not already been set, or if since it
+was last set there has been a time at which all peers were frozen.
+
+If the peer does not yet know their own peer number, as is the case if they
+have just accepted an invitation, the peer will find themselves in the list of
+received peers and use the peer number assigned to them as their own. They are
+then able to send messages and invite other peers to the groupchat. They
+immediately send a name message to announce their name to the group.
+
+Message packets are used to send messages to all peers in the groupchat.  To
+send a message packet, a peer will first take their peer number and the message
+they want to send.  Each message packet sent will have a message number that is
+equal to the last message number sent + 1.  Like all other numbers (group chat
+number, peer number) in the packet, the message number in the packet will be in
+big endian format.
+
+When a Message packet is received, the peer receiving it will first check that
+the peer number of the sender is in their peer list. If not, the peer ignores
+the message but sends a peer query packet to the peer the packet was directly
+received from. That peer should have the message sender in their peer list,
+and so will send the sender's peer info back in a peer response.
+
+If the sender is in the receiver's peer list, the receiver now checks whether
+they have already seen a message with the same sender and message number. This
+is achieved by storing the 8 greatest message numbers received from a given
+sender peer number. If the message has lesser message number than any of those
+8, it is assumed to have been received. If the message has already been
+received according to this check, or if it is a name or title message and
+another message of the same type from the same sender with a greater message
+number has been received, then the packet is discarded. Otherwise, the
+message is processed as described below, and a Message packet with the message
+is sent (relayed) to all current group connections except the one that it was
+received from, and also to that one if that peer is the original sender of the
+message. The only thing that should change in the Message packet as it is
+relayed is the group number.
+
+Lossy message packets are used to send audio packets to others in audio group
+chats.  Lossy packets work the same way as normal relayed groupchat messages in
+that they are relayed to everyone in the group chat until everyone has them, but
+there are a few differences. Firstly, the message number is only a 2 byte
+integer. When receiving a lossy packet from a peer the receiving peer will first
+check if a message with that message number was already received from that peer.
+If it wasn't, the packet will be added to the list of received packets and then
+the packet will be passed to its handler and then sent to the 2 closest
+connected groupchat peers that are not the sender.  The reason for it to be 2
+instead of 4 (or 3 if we are not the original sender) as for lossless message
+packets is that it reduces bandwidth usage without lowering the quality of the
+received audio stream via lossy packets, at the cost of reduced robustness
+against connections failing. To check if a packet was already received, the last
+256 message numbers received from each peer are stored. If video was added
+meaning a much higher number of packets would be sent, this number would be
+increased.  If the packet number is in this list then it was received.
+
+\section{Message ids}
+
+\subsection{ping (0x00)}
+
+Sent approximately every 20 seconds by every peer.  Contains no data.
+
+\subsection{\texttt{new\_peer} (0x10)}
+
+Tell everyone about a new peer in the chat.
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{2}         & \texttt{uint16\_t} Peer number \\
+  \texttt{32}        & Long term public key \\
+  \texttt{32}        & DHT public key \\
+\end{tabular}
+
+\subsection{\texttt{kill\_peer} (0x11)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{2}         & \texttt{uint16\_t} Peer number \\
+\end{tabular}
+
+\subsection{\texttt{freeze\_peer} (0x12)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{2}         & \texttt{uint16\_t} Peer number \\
+\end{tabular}
+
+\subsection{Name change (0x30)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  variable           & Name (namelen) \\
+\end{tabular}
+
+\subsection{Groupchat title change (0x31)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  variable           & Title (titlelen) \\
+\end{tabular}
+
+\subsection{Chat message (0x40)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  variable           & Message (messagelen) \\
+\end{tabular}
+
+\subsection{Action (/me) (0x41)}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  variable           & Message (messagelen) \\
+\end{tabular}
+
+Ping messages are sent every 20 seconds by every peer.  This is how other
+peers know that the peers are still alive.
+
+When a new peer joins, the peer which invited the joining peer will send a new
+peer message to warn everyone that there is a new peer in the chat.  When a new
+peer message is received, the peer in the message must be added to the peer
+list if it is not there already, and its DHT public key must be set to that
+in the message.
+
+Kill peer messages are used to indicate that a peer has quit the group chat
+permanently. Freeze peer messages are similar, but indicate that the quitting
+peer may later return to the group. Each is sent by the one quitting the group
+chat right before they quit it.
+
+Name change messages are used to change or set the name of the peer sending it.
+They are also sent by a joining peer right after receiving the list of peers in
+order to tell others what their name is.
+
+Title change packets are used to change the title of the group chat and can be
+sent by anyone in the group chat.
+
+Chat and action messages are used by the group chat peers to send messages to
+others in the group chat.
+
+\section{Timeouts and reconnection}
+
+Groupchat connections may go down, and this may lead to a peer becoming
+disconnected from the group or the group otherwise splitting into multiple
+connected components. To ensure the group becomes fully connected again once
+suitable connections are re-established, peers keep track of peers who are no
+longer visible in the group ("frozen" peers), and try to re-integrate them
+into the group via any suitable friend connections which may come to be
+available. The rejoin packet is used for this.
+
+Rejoin packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x64) \\
+  \texttt{33}        & Group chat identifier \\
+\end{tabular}
+
+A peer in a groupchat is considered to be active when a group message or
+rejoin packet is received from it, or a new peer message is received for it.
+A peer which remains inactive for 60 seconds is set as frozen; this means it
+is removed from the peer list and added to a separate list of frozen peers.
+Frozen peers are disregarded for all purposes except those discussed below.
+
+If a frozen peer becomes active, we unfreeze it, meaning that we move it from
+the frozen peers list to the peer list, and we send a name message to the
+group.
+
+Whenever we make a new friend connection to a peer, we check whether the
+public key of the peer is that of any frozen peer. If so, we send a rejoin
+packet to the peer along the friend connection, and create a groupchat
+connection to the peer, marked as introducing us, and send a peer online
+packet to the peer.
+
+If we receive a rejoin packet from a peer along a friend connection, then,
+after unfreezing the peer if it was frozen, we update the peer's DHT public
+key in the groupchat peer list to the key in the friend connection, and create
+a groupchat connection for the peer, marked as introducing the peer, and send
+a peer online packet to the peer.
+
+When a peer is added to the peer list, any existing peer in the peer list or
+frozen peers list with the same public key is first removed.
+
+\input{src/Network/Tox/Application/GroupChats.lhs}
+
+\chapter{Net crypto}
+
+The Tox transport protocol is what Tox uses to establish and send data securely
+to friends and provides encryption, ordered delivery, and perfect forward
+secrecy.  It is a UDP protocol but it is also used when 2 friends connect over
+TCP relays.
+
+The reason the protocol for connections to friends over TCP relays and direct
+UDP is the same is for simplicity and so the connection can switch between both
+without the peers needing to disconnect and reconnect.  For example two Tox
+friends might first connect over TCP and a few seconds later switch to UDP when
+a direct UDP connection becomes possible.  The opening up of the UDP route or
+'hole punching' is done by the DHT module and the opening up of a relayed TCP
+connection is done by the \texttt{TCP\_connection} module.  The Tox transport
+protocol has the job of connecting two peers (tox friends) safely once a route
+or communications link between both is found.  Direct UDP is preferred over TCP
+because it is direct and isn't limited by possibly congested TCP relays.  Also,
+a peer can only connect to another using the Tox transport protocol if they
+know the real public key and DHT public key of the peer they want to connect
+to.  However, both the DHT and TCP connection modules require this information
+in order to find and open the route to the peer which means we assume this
+information is known by toxcore and has been passed to \texttt{net\_crypto} when
+the \texttt{net\_crypto} connection was created.
+
+Because this protocol has to work over UDP it must account for possible packet
+loss, packets arriving in the wrong order and has to implement some kind of
+congestion control.  This is implemented above the level at which the packets
+are encrypted.  This prevents a malicious TCP relay from disrupting the
+connection by modifying the packets that go through it.  The packet loss
+prevention makes it work very well on TCP relays that we assume may go down at
+any time as the connection will stay strong even if there is need to switch to
+another TCP relay which will cause some packet loss.
+
+Before sending the actual handshake packet the peer must obtain a cookie.  This
+cookie step serves as a way for the receiving peer to confirm that the peer
+initiating the connection can receive the responses in order to prevent certain
+types of DoS attacks.
+
+The peer receiving a cookie request packet must not allocate any resources to
+the connection.  They will simply respond to the packet with a cookie response
+packet containing the cookie that the requesting peer must then use in the
+handshake to initiate the actual connection.
+
+The cookie response must be sent back using the exact same link the cookie
+request packet was sent from.  The reason for this is that if it is sent back
+using another link, the other link might not work and the peer will not be
+expecting responses from another link.  For example, if a request is sent from
+UDP with ip port X, it must be sent back by UDP to ip port X.  If it was
+received from a TCP OOB packet it must be sent back by a TCP OOB packet via the
+same relay with the destination being the peer who sent the request.  If it was
+received from an established TCP relay connection it must be sent back via that
+same exact connection.
+
+When a cookie request is received, the peer must not use the information in the
+request packet for anything, he must not store it, he must only create a cookie
+and cookie response from it, then send the created cookie response packet and
+forget them.  The reason for this is to prevent possible attacks.  For example
+if a peer would allocate long term memory for each cookie request packet
+received then a simple packet flood would be enough to achieve an effective
+denial of service attack by making the program run out of memory.
+
+cookie request packet (145 bytes):
+
+\begin{verbatim}
+[uint8_t 24]
+[Sender's DHT Public key (32 bytes)]
+[Random nonce (24 bytes)]
+[Encrypted message containing:
+    [Sender's real public key (32 bytes)]
+    [padding (32 bytes)]
+    [uint64_t echo id (must be sent back untouched in cookie response)]
+]
+\end{verbatim}
+
+Encrypted message is encrypted with sender's DHT private key, receiver's DHT
+public key and the nonce.
+
+The packet id for cookie request packets is 24.  The request contains the DHT
+public key of the sender which is the key used (The DHT private key) (along
+with the DHT public key of the receiver) to encrypt the encrypted part of the
+cookie packet and a nonce also used to encrypt the encrypted part of the
+packet.  Padding is used to maintain backwards-compatibility with previous
+versions of the protocol.  The echo id in the cookie request must be sent back
+untouched in the cookie response.  This echo id is how the peer sending the
+request can be sure that the response received was a response to the packet
+that he sent.
+
+The reason for sending the DHT public key and real public key in the cookie
+request is that both are contained in the cookie sent back in the response.
+
+Toxcore currently sends 1 cookie request packet every second 8 times before it
+kills the connection if there are no responses.
+
+cookie response packet (161 bytes):
+
+\begin{verbatim}
+[uint8_t 25]
+[Random nonce (24 bytes)]
+[Encrypted message containing:
+    [Cookie]
+    [uint64_t echo id (that was sent in the request)]
+]
+\end{verbatim}
+
+Encrypted message is encrypted with the exact same symmetric key as the cookie
+request packet it responds to but with a different nonce.
+
+The packet id for cookie request packets is 25.  The response contains a nonce
+and an encrypted part encrypted with the nonce.  The encrypted part is
+encrypted with the same key used to decrypt the encrypted part of the request
+meaning the expensive shared key generation needs to be called only once in
+order to handle and respond to a cookie request packet with a cookie response.
+
+The Cookie (see below) and the echo id that was sent in the request are the
+contents of the encrypted part.
+
+The Cookie should be (112 bytes):
+
+\begin{verbatim}
+[nonce]
+[encrypted data:
+    [uint64_t time]
+    [Sender's real public key (32 bytes)]
+    [Sender's DHT public key (32 bytes)]
+]
+\end{verbatim}
+
+The cookie is a 112 byte piece of data that is created and sent to the
+requester as part of the cookie response packet.  A peer who wants to connect
+to another must obtain a cookie packet from the peer they are trying to connect
+to.  The only way to send a valid handshake packet to another peer is to first
+obtain a cookie from them.
+
+The cookie contains information that will both prove to the receiver of the
+handshake that the peer has received a cookie response and contains encrypted
+info that tell the receiver of the handshake packet enough info to both decrypt
+and validate the handshake packet and accept the connection.
+
+When toxcore is started it generates a symmetric encryption key that it uses to
+encrypt and decrypt all cookie packets (using NaCl authenticated encryption
+exactly like encryption everywhere else in toxcore).  Only the instance of
+toxcore that create the packets knows the encryption key meaning any cookie it
+successfully decrypts and validates were created by it.
+
+The time variable in the cookie is used to prevent cookie packets that are too
+old from being used.  Toxcore has a time out of 15 seconds for cookie packets.
+If a cookie packet is used more than 15 seconds after it is created toxcore
+will see it as invalid.
+
+When responding to a cookie request packet the sender's real public key is the
+known key sent by the peer in the encrypted part of the cookie request packet
+and the senders DHT public key is the key used to encrypt the encrypted part of
+the cookie request packet.
+
+When generating a cookie to put inside the encrypted part of the handshake: One
+of the requirements to connect successfully to someone else is that we know
+their DHT public key and their real long term public key meaning there is
+enough information to construct the cookie.
+
+Handshake packet:
+
+\begin{verbatim}
+[uint8_t 26]
+[Cookie]
+[nonce (24 bytes)]
+[Encrypted message containing:
+    [24 bytes base nonce]
+    [session public key of the peer (32 bytes)]
+    [sha512 hash of the entire Cookie sitting outside the encrypted part]
+    [Other Cookie (used by the other to respond to the handshake packet)]
+]
+\end{verbatim}
+
+The packet id for handshake packets is 26.  The cookie is a cookie obtained by
+sending a cookie request packet to the peer and getting a cookie response
+packet with a cookie in it.  It may also be obtained in the handshake packet by
+a peer receiving a handshake packet (Other Cookie).
+
+The nonce is a nonce used to encrypt the encrypted part of the handshake
+packet.  The encrypted part of the handshake packet is encrypted with the long
+term keys of both peers.  This is to prevent impersonation.
+
+Inside the encrypted part of the handshake packet there is a 'base nonce' and a
+session public key.  The 'base nonce' is a nonce that the other should use to
+encrypt each data packet, adding + 1 to it for each data packet sent.  (first
+packet is 'base nonce' + 0, next is 'base nonce' + 1, etc.  Note that for
+mathematical operations the nonce is considered to be a 24 byte number in big
+endian format).  The session key is the temporary connection public key that
+the peer has generated for this connection and it sending to the other.  This
+session key is used so that the connection has perfect forward secrecy.  It is
+important to save the private key counterpart of the session public key sent in
+the handshake, the public key received by the other and both the received and
+sent base nonces as they are used to encrypt/decrypt the data packets.
+
+The hash of the cookie in the encrypted part is used to make sure that an
+attacker has not taken an older valid handshake packet and then replaced the
+cookie packet inside with a newer one which would be bad as they could replay
+it and might be able to make a mess.
+
+The 'Other Cookie' is a valid cookie that we put in the handshake so that the
+other can respond with a valid handshake without having to make a cookie
+request to obtain one.
+
+The handshake packet is sent by both sides of the connection.  If a peer
+receives a handshake it will check if the cookie is valid, if the encrypted
+section decrypts and validates, if the cookie hash is valid, if long term
+public key belongs to a known friend.  If all these are true then the
+connection is considered 'Accepted' but not 'Confirmed'.
+
+If there is no existing connection to the peer identified by the long term
+public key to set to 'Accepted', one will be created with that status.  If a
+connection to such peer with a not yet 'Accepted' status to exists, this
+connection is set to accepted.  If a connection with a 'Confirmed' status
+exists for this peer, the handshake packet will be ignored and discarded (The
+reason for discarding it is that we do not want slightly late handshake packets
+to kill the connection) except if the DHT public key in the cookie contained in
+the handshake packet is different from the known DHT public key of the peer.
+If this happens the connection will be immediately killed because it means it
+is no longer valid and a new connection will be created immediately with the
+'Accepted' status.
+
+Sometimes toxcore might receive the DHT public key of the peer first with a
+handshake packet so it is important that this case is handled and that the
+implementation passes the DHT public key to the other modules (DHT,
+\texttt{TCP\_connection}) because this does happen.
+
+Handshake packets must be created only once during the connection but must be
+sent in intervals until we are sure the other received them.  This happens when
+a valid encrypted data packet is received and decrypted.
+
+The states of a connection:
+
+\begin{enumerate}
+  \item Not accepted: Send handshake packets.
+
+  \item Accepted: A handshake packet has been received from the other peer but
+    no encrypted packets: continue (or start) sending handshake packets because
+    the peer can't know if the other has received them.
+
+  \item Confirmed: A valid encrypted packet has been received from the other
+    peer: Connection is fully established: stop sending handshake packets.
+\end{enumerate}
+
+Toxcore sends handshake packets every second 8 times and times out the
+connection if the connection does not get confirmed (no encrypted packet is
+received) within this time.
+
+Perfect handshake scenario:
+
+\begin{verbatim}
+Peer 1                Peer 2
+Cookie request   ->
+                      <- Cookie response
+Handshake packet ->
+                      * accepts connection
+                      <- Handshake packet
+*accepts connection
+Encrypted packet ->   <- Encrypted packet
+*confirms connection  *confirms connection
+       Connection successful.
+Encrypted packets -> <- Encrypted packets
+
+More realistic handshake scenario:
+Peer 1                Peer 2
+Cookie request   ->   *packet lost*
+Cookie request   ->
+                      <- Cookie response
+                      *Peer 2 randomly starts new connection to peer 1
+                      <- Cookie request
+Cookie response  ->
+Handshake packet ->   <- Handshake packet
+*accepts connection   * accepts connection
+Encrypted packet ->   <- Encrypted packet
+*confirms connection  *confirms connection
+       Connection successful.
+Encrypted packets -> <- Encrypted packets
+\end{verbatim}
+
+The reason why the handshake is like this is because of certain design
+requirements:
+
+\begin{enumerate}
+  \item The handshake must not leak the long term public keys of the peers to a
+     possible attacker who would be looking at the packets but each peer must know
+     for sure that they are connecting to the right peer and not an impostor.
+  \item A connection must be able of being established if only one of the peers has
+     the information necessary to initiate a connection (DHT public key of the
+     peer and a link to the peer).
+  \item If both peers initiate a connection to each other at the same time the
+     connection must succeed without issues.
+  \item There must be perfect forward secrecy.
+  \item Must be resistant to any possible attacks.
+\end{enumerate}
+
+Due to how it is designed only one connection is possible at a time between 2
+peers.
+
+Encrypted packets:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x1b) \\
+  \texttt{2}         & \texttt{uint16\_t} The last 2 bytes of the nonce used to encrypt this \\
+  variable           & Payload \\
+\end{tabular}
+
+The payload is encrypted with the session key and 'base nonce' set by the
+receiver in their handshake + packet number (starting at 0, big endian math).
+
+The packet id for encrypted packets is 27.  Encrypted packets are the packets
+used to send data to the other peer in the connection.  Since these packets can
+be sent over UDP the implementation must assume that they can arrive out of
+order or even not arrive at all.
+
+To get the key used to encrypt/decrypt each packet in the connection a peer
+takes the session public key received in the handshake and the private key
+counterpart of the key it sent it the handshake and generates a shared key from
+it.  This shared key will be identical for both peers.  It is important to note
+that connection keys must be wiped when the connection is killed.
+
+To create an encrypted packet to be sent to the other peer, the data is
+encrypted with the shared key for this connection and the base nonce that the
+other peer sent in the handshake packet with the total number of encrypted
+packets sent in the connection added to it ('base nonce' + 0 for the first
+encrypted data packet sent, 'base nonce' + 1 for the second, etc.  Note that
+the nonce is treated as a big endian number for mathematical operations like
+additions).  The 2 byte (\texttt{uint16\_t}) number at the beginning of the
+encrypted packet is the last 2 bytes of this 24 byte nonce.
+
+To decrypt a received encrypted packet, the nonce the packet was encrypted with
+is calculated using the base nonce that the peer sent to the other and the 2
+byte number at the beginning of the packet.  First we assume that packets will
+most likely arrive out of order and that some will be lost but that packet loss
+and out of orderness will never be enough to make the 2 byte number need an
+extra byte.  The packet is decrypted using the shared key for the connection
+and the calculated nonce.
+
+Toxcore uses the following method to calculate the nonce for each packet:
+
+\begin{enumerate}
+  \item \texttt{diff} = (2 byte number on the packet) - (last 2 bytes of the current saved
+     base nonce) NOTE: treat the 3 variables as 16 bit unsigned ints, the result
+     is expected to sometimes roll over.
+  \item copy \texttt{saved\_base\_nonce} to \texttt{temp\_nonce}.
+  \item \texttt{temp\_nonce = temp\_nonce + diff}.  \texttt{temp\_nonce} is the correct nonce that
+     can be used to decrypt the packet.
+  \item \texttt{DATA\_NUM\_THRESHOLD} = (1/3 of the maximum number that can be stored in an
+     unsigned 2 bit integer)
+  \item if decryption succeeds and \texttt{diff > (DATA\_NUM\_THRESHOLD * 2)} then:
+    \begin{itemize}
+      \item \texttt{saved\_base\_nonce = saved\_base\_nonce + DATA\_NUM\_THRESHOLD}
+    \end{itemize}
+\end{enumerate}
+
+First it takes the difference between the 2 byte number on the packet and the
+last.  Because the 3 values are unsigned 16 bit ints and rollover is part of
+the math something like diff = (10 - 65536) means diff is equal to 11.
+
+Then it copies the saved base nonce to a temp nonce buffer.
+
+Then it adds diff to the nonce (the nonce is in big endian format).
+
+After if decryption was successful it checks if diff was bigger than 2/3 of the
+value that can be contained in a 16 bit unsigned int and increases the saved
+base nonce by 1/3 of the maximum value if it succeeded.
+
+This is only one of many ways that the nonce for each encrypted packet can be
+calculated.
+
+Encrypted packets that cannot be decrypted are simply dropped.
+
+The reason for exchanging base nonces is because since the key for encrypting
+packets is the same for received and sent packets there must be a cryptographic
+way to make it impossible for someone to do an attack where they would replay
+packets back to the sender and the sender would think that those packets came
+from the other peer.
+
+Data in the encrypted packets:
+
+\begin{verbatim}
+[our recvbuffers buffer_start, (highest packet number handled + 1), (big endian)]
+[uint32_t packet number if lossless, sendbuffer buffer_end if lossy, (big endian)]
+[data]
+\end{verbatim}
+
+Encrypted packets may be lossy or lossless.  Lossy packets are simply encrypted
+packets that are sent to the other.  If they are lost, arrive in the wrong
+order or even if an attacker duplicates them (be sure to take this into account
+for anything that uses lossy packets) they will simply be decrypted as they
+arrive and passed upwards to what should handle them depending on the data id.
+
+Lossless packets are packets containing data that will be delivered in order by
+the implementation of the protocol.  In this protocol, the receiver tells the
+sender which packet numbers he has received and which he has not and the sender
+must resend any packets that are dropped.  Any attempt at doubling packets will
+cause all (except the first received) to be ignored.
+
+Each lossless packet contains both a 4 byte number indicating the highest
+packet number received and processed and a 4 byte packet number which is the
+packet number of the data in the packet.
+
+In lossy packets, the layout is the same except that instead of a packet
+number, the second 4 byte number represents the packet number of a lossless
+packet if one were sent right after.  This number is used by the receiver to
+know if any packets have been lost.  (for example if it receives 4 packets with
+numbers (0, 1, 2, 5) and then later a lossy packet with this second number as:
+8 it knows that packets: 3, 4, 6, 7 have been lost and will request them)
+
+How the reliability is achieved:
+
+First it is important to say that packet numbers do roll over, the next number
+after 0xFFFFFFFF (maximum value in 4 bytes) is 0.  Hence, all the mathematical
+operations dealing with packet numbers are assumed to be done only on unsigned
+32 bit integer unless said otherwise.  For example 0 - 0xFFFFFFFF would equal
+to 1 because of the rollover.
+
+When sending a lossless packet, the packet is created with its packet number
+being the number of the last lossless packet created + 1 (starting at 0).  The
+packet numbers are used for both reliability and in ordered delivery and so
+must be sequential.
+
+The packet is then stored along with its packet number in order for the peer to
+be able to send it again if the receiver does not receive it.  Packets are only
+removed from storage when the receiver confirms they have received them.
+
+The receiver receives packets and stores them along with their packet number.
+When a receiver receives a packet he stores the packet along with its packet
+number in an array.  If there is already a packet with that number in the
+buffer, the packet is dropped.  If the packet number is smaller than the last
+packet number that was processed, the packet is dropped.  A processed packet
+means it was removed from the buffer and passed upwards to the relevant module.
+
+Assuming a new connection, the sender sends 5 lossless packets to the receiver:
+0, 1, 2, 3, 4 are the packet numbers sent and the receiver receives: 3, 2, 0, 2
+in that order.
+
+The receiver will save the packets and discards the second packet with the
+number 2, he has: 0, 2, 3 in his buffer.  He will pass the first packet to the
+relevant module and remove it from the array but since packet number 1 is
+missing he will stop there.  Contents of the buffer are now: 2, 3.  The
+receiver knows packet number 1 is missing and will request it from the sender
+by using a packet request packet:
+
+data ids:
+
+\begin{tabular}{l|l}
+  ID   & Data \\
+  \hline
+  0    & padding (skipped until we hit a non zero (data id) byte) \\
+  1    & packet request packet (lossy packet) \\
+  2    & connection kill packet (lossy packet) \\
+  ...  & ... \\
+  16+  & reserved for Messenger usage (lossless packets) \\
+  192+ & reserved for Messenger usage (lossy packets) \\
+  255  & reserved for Messenger usage (lossless packet) \\
+\end{tabular}
+
+Connection kill packets tell the other that the connection is over.
+
+Packet numbers are the first byte of data in the packet.
+
+packet request packet:
+
+\begin{verbatim}
+[uint8_t (1)][uint8_t num][uint8_t num][uint8_t num]...[uint8_t num]
+\end{verbatim}
+
+Packet request packets are used by one side of the connection to request
+packets from the other.  To create a full packet request packet, the one
+requesting the packet takes the last packet number that was processed (sent to
+the relevant module and removed from the array (0 in the example above)).
+Subtract the number of the first missing packet from that number (1 - 0) = 1.
+Which means the full packet to request packet number 1 will look like:
+
+\begin{verbatim}
+[uint32_t 1]
+[uint32_t 0]
+[uint8_t 1][uint8_t 1]
+\end{verbatim}
+
+If packet number 4 was being requested as well, take the difference between the
+packet number and the last packet number being requested (4 - 1) = 3.  So the
+packet will look like:
+
+\begin{verbatim}
+[uint32_t 1]
+[uint32_t 0]
+[uint8_t 1][uint8_t 1][uint8_t 3]
+\end{verbatim}
+
+But what if the number is greater than 255? Let's say the peer needs to request
+packets 3, 6, 1024, the packet will look like:
+
+\begin{verbatim}
+[uint32_t 1]
+[uint32_t 2]
+[uint8_t 1][uint8_t 3][uint8_t 3][uint8_t 0][uint8_t 0][uint8_t 0][uint8_t 253]
+\end{verbatim}
+
+Each 0 in the packet represents adding 255 until a non 0 byte is reached which
+is then added and the resulting requested number is what is left.
+
+This request is designed to be small when requesting packets in real network
+conditions where the requested packet numbers will be close to each other.
+Putting each requested 4 byte packet number would be very simple but would make
+the request packets unnecessarily large which is why the packets look like
+this.
+
+When a request packet is received, it will be decoded and all packets in
+between the requested packets will be assumed to be successfully received by
+the other.
+
+Packet request packets are sent at least every 1 second in toxcore and more
+when packets are being received.
+
+The current formula used is (note that this formula is likely sub-optimal):
+
+\begin{verbatim}
+REQUEST_PACKETS_COMPARE_CONSTANT = 50.0 double request_packet_interval =
+(REQUEST_PACKETS_COMPARE_CONSTANT /
+(((double)num_packets_array(&conn->recv_array) + 1.0) / (conn->packet_recv_rate
++ 1.0)));
+\end{verbatim}
+
+\texttt{num\_packets\_array(&conn->recv\_array)} returns the difference between
+the highest packet number received and the last one handled.  In the toxcore
+code it refers to the total size of the current array (with the holes which are
+the placeholders for not yet received packets that are known to be missing).
+
+\texttt{conn->packet\_recv\_rate} is the number of data packets successfully
+received per second.
+
+This formula was created with the logic that the higher the 'delay' in packets
+(\texttt{num\_packets\_array(&conn->recv\_array)}) vs the speed of packets
+received, the more request packets should be sent.
+
+Requested packets are resent every time they can be resent as in they will obey
+the congestion control and not bypass it.  They are resent once, subsequent
+request packets will be used to know if the packet was received or if it should
+be resent.
+
+The ping or rtt (round trip time) between two peers can be calculated by saving
+the time each packet was sent and taking the difference between the time the
+latest packet confirmed received by a request packet was sent and the time the
+request packet was received.  The rtt can be calculated for every request
+packet.  The lowest one (for all packets) will be the closest to the real ping.
+
+This ping or rtt can be used to know if a request packet that requests a packet
+we just sent should be resent right away or we should wait or not for the next
+one (to know if the other side actually had time to receive the packet).
+
+The congestion control algorithm has the goal of guessing how many packets can
+be sent through the link every second before none can be sent through anymore.
+How it works is basically to send packets faster and faster until none can go
+through the link and then stop sending them faster than that.
+
+Currently the congestion control uses the following formula in toxcore however
+that is probably not the best way to do it.
+
+The current formula is to take the difference between the current size of the
+send queue and the size of the send queue 1.2 seconds ago, take the total
+number of packets sent in the last 1.2 seconds and subtract the previous number
+from it.
+
+Then divide this number by 1.2 to get a packet speed per second.  If this speed
+is lower than the minimum send rate of 8 packets per second, set it to 8.
+
+A congestion event can be defined as an event when the number of requested
+packets exceeds the number of packets the congestion control says can be sent
+during this frame.  If a congestion event occurred during the last 2 seconds,
+the packet send rate of the connection is set to the send rate previously
+calculated, if not it is set to that send rate times 1.25 in order to increase
+the speed.
+
+Like I said this isn't perfect and a better solution can likely be found or the
+numbers tweaked.
+
+To fix the possible issue where it would be impossible to send very low
+bandwidth data like text messages when sending high bandwidth data like files
+it is possible to make priority packets ignore the congestion control
+completely by placing them into the send packet queue and sending them even if
+the congestion control says not to.  This is used in toxcore for all non file
+transfer packets to prevent file transfers from preventing normal message
+packets from being sent.
+
+\chapter{network.txt}
+
+The network module is the lowest file in toxcore that everything else depends
+on.  This module is basically a UDP socket wrapper, serves as the sorting
+ground for packets received by the socket, initializes and uninitializes the
+socket.  It also contains many socket, networking related and some other
+functions like a monotonic time function used by other toxcore modules.
+
+Things of note in this module are the maximum UDP packet size define
+(\texttt{MAX\_UDP\_PACKET\_SIZE}) which sets the maximum UDP packet size toxcore
+can send and receive.  The list of all UDP packet ids: \texttt{NET\_PACKET\_*}.
+UDP packet ids are the value of the first byte of each UDP packet and is how
+each packet gets sorted to the right module that can handle it.
+\texttt{networking\_registerhandler()} is used by higher level modules in order
+to tell the network object which packets to send to which module via a
+callback.
+
+It also contains datastructures used for ip addresses in toxcore.  IP4 and IP6
+are the datastructures for ipv4 and ipv6 addresses, IP is the datastructure for
+storing either (the family can be set to \texttt{AF\_INET} (ipv4) or
+\texttt{AF\_INET6} (ipv6).  It can be set to another value like
+\texttt{TCP\_ONION\_FAMILY}, \texttt{TCP\_INET}, \texttt{TCP\_INET6} or
+\texttt{TCP\_FAMILY} which are invalid values in the network modules but valid
+values in some other module and denote a special type of ip) and
+\texttt{IP\_Port} stores an IP datastructure with a port.
+
+Since the network module interacts directly with the underlying operating
+system with its socket functions it has code to make it work on windows, linux,
+etc... unlike most modules that sit at a higher level.
+
+The network module currently uses the polling method to read from the UDP
+socket.  The \texttt{networking\_poll()} function is called to read all the
+packets from the socket and pass them to the callbacks set using the
+\texttt{networking\_registerhandler()} function.  The reason it uses polling is
+simply because it was easier to write it that way, another method would be
+better here.
+
+The goal of this module is to provide an easy interface to a UDP socket and
+other networking related functions.
+
+\chapter{Onion}
+
+The goal of the onion module in Tox is to prevent peers that are not friends
+from finding out the temporary DHT public key from a known long term public key
+of the peer and to prevent peers from discovering the long term public key of
+peers when only the temporary DHT key is known.
+
+It makes sure only friends of a peer can find it and connect to it and
+indirectly makes sure non friends cannot find the ip address of the peer when
+knowing the Tox address of the friend.
+
+The only way to prevent peers in the network from associating the temporary DHT
+public key with the long term public key is to not broadcast the long term key
+and only give others in the network that are not friends the DHT public key.
+
+The onion lets peers send their friends, whose real public key they know as it
+is part of the Tox ID, their DHT public key so that the friends can then find
+and connect to them without other peers being able to identify the real public
+keys of peers.
+
+So how does the onion work?
+
+The onion works by enabling peers to announce their real public key to peers by
+going through the onion path.  It is like a DHT but through onion paths.  In
+fact it uses the DHT in order for peers to be able to find the peers with ids
+closest to their public key by going through onion paths.
+
+In order to announce its real public key anonymously to the Tox network while
+using the onion, a peer first picks 3 random nodes that it knows (they can be
+from anywhere: the DHT, connected TCP relays or nodes found while finding peers
+with the onion).  The nodes should be picked in a way that makes them unlikely
+to be operated by the same person perhaps by looking at the ip addresses and
+looking if they are in the same subnet or other ways.  More research is needed
+to make sure nodes are picked in the safest way possible.
+
+The reason for 3 nodes is that 3 hops is what they use in Tor and other
+anonymous onion based networks.
+
+These nodes are referred to as nodes A, B and C.  Note that if a peer cannot
+communicate via UDP, its first peer will be one of the TCP relays it is
+connected to, which will be used to send its onion packet to the network.
+
+TCP relays can only be node A or the first peer in the chain as the TCP relay
+is essentially acting as a gateway to the network.  The data sent to the TCP
+Client module to be sent as a TCP onion packet by the module is different from
+the one sent directly via UDP.  This is because it doesn't need to be encrypted
+(the connection to the TCP relay server is already encrypted).
+
+First I will explain how communicating via onion packets work.
+
+Note: nonce is a 24 byte nonce.  The nested nonces are all the same as the
+outer nonce.
+
+Onion packet (request):
+
+Initial (TCP) data sent as the data of an onion packet through the TCP client
+module:
+
+\begin{itemize}
+  \item \texttt{IP\_Port} of node B
+  \item A random public key PK1
+  \item Encrypted with the secret key SK1 and the public key of Node B and the nonce:
+    \begin{itemize}
+      \item \texttt{IP\_Port} of node C
+      \item A random public key PK2
+      \item Encrypted with the secret key SK2 and the public key of Node C and the nonce:
+        \begin{itemize}
+          \item \texttt{IP\_Port} of node D
+          \item Data to send to Node D
+        \end{itemize}
+    \end{itemize}
+\end{itemize}
+
+Initial (UDP) (sent from us to node A):
+
+\begin{itemize}
+  \item \texttt{uint8\_t} (0x80) packet id
+  \item Nonce
+  \item Our temporary DHT public key
+  \item Encrypted with our temporary DHT secret key and the public key of Node A and
+    the nonce:
+    \begin{itemize}
+      \item \texttt{IP\_Port} of node B
+      \item A random public key PK1
+      \item Encrypted with the secret key SK1 and the public key of Node B and the nonce:
+        \begin{itemize}
+          \item \texttt{IP\_Port} of node C
+          \item A random public key PK2
+          \item Encrypted with the secret key SK2 and the public key of Node C and the
+            nonce:
+            \begin{itemize}
+              \item \texttt{IP\_Port} of node D
+              \item Data to send to Node D
+            \end{itemize}
+        \end{itemize}
+    \end{itemize}
+\end{itemize}
+
+(sent from node A to node B):
+
+\begin{itemize}
+  \item \texttt{uint8\_t} (0x81) packet id
+  \item Nonce
+  \item A random public key PK1
+  \item Encrypted with the secret key SK1 and the public key of Node B and the nonce:
+    \begin{itemize}
+      \item \texttt{IP\_Port} of node C
+      \item A random public key PK2
+      \item Encrypted with the secret key SK2 and the public key of Node C and the nonce:
+        \begin{itemize}
+          \item \texttt{IP\_Port} of node D
+          \item Data to send to Node D
+        \end{itemize}
+    \end{itemize}
+  \item Nonce
+  \item Encrypted with temporary symmetric key of Node A and the nonce:
+    \begin{itemize}
+      \item \texttt{IP\_Port} (of us)
+    \end{itemize}
+\end{itemize}
+
+(sent from node B to node C):
+
+\begin{itemize}
+  \item \texttt{uint8\_t} (0x82) packet id
+  \item Nonce
+  \item A random public key PK1
+  \item Encrypted with the secret key SK1 and the public key of Node C and the nonce:
+    \begin{itemize}
+      \item \texttt{IP\_Port} of node D
+      \item Data to send to Node D
+    \end{itemize}
+  \item Nonce
+  \item Encrypted with temporary symmetric key of Node B and the nonce:
+    \begin{itemize}
+      \item \texttt{IP\_Port} (of Node A)
+      \item Nonce
+      \item Encrypted with temporary symmetric key of Node A and the nonce:
+        \begin{itemize}
+          \item \texttt{IP\_Port} (of us)
+        \end{itemize}
+    \end{itemize}
+\end{itemize}
+
+(sent from node C to node D):
+
+\begin{itemize}
+  \item Data to send to Node D
+  \item Nonce
+  \item Encrypted with temporary symmetric key of Node C and the nonce:
+    \begin{itemize}
+      \item \texttt{IP\_Port} (of Node B)
+      \item Nonce
+      \item Encrypted with temporary symmetric key of Node B and the nonce:
+        \begin{itemize}
+          \item \texttt{IP\_Port} (of Node A)
+          \item Nonce
+          \item Encrypted with temporary symmetric key of Node A and the nonce:
+            \begin{itemize}
+              \item \texttt{IP\_Port} (of us)
+            \end{itemize}
+        \end{itemize}
+    \end{itemize}
+\end{itemize}
+
+Onion packet (response):
+
+initial (sent from node D to node C):
+
+\begin{itemize}
+  \item \texttt{uint8\_t} (0x8c) packet id
+  \item Nonce
+  \item Encrypted with the temporary symmetric key of Node C and the nonce:
+    \begin{itemize}
+      \item \texttt{IP\_Port} (of Node B)
+      \item Nonce
+      \item Encrypted with the temporary symmetric key of Node B and the nonce:
+        \begin{itemize}
+          \item \texttt{IP\_Port} (of Node A)
+          \item Nonce
+          \item Encrypted with the temporary symmetric key of Node A and the nonce:
+            \begin{itemize}
+              \item \texttt{IP\_Port} (of us)
+            \end{itemize}
+        \end{itemize}
+    \end{itemize}
+  \item Data to send back
+\end{itemize}
+
+(sent from node C to node B):
+
+\begin{itemize}
+  \item \texttt{uint8\_t} (0x8d) packet id
+  \item Nonce
+  \item Encrypted with the temporary symmetric key of Node B and the nonce:
+    \begin{itemize}
+      \item \texttt{IP\_Port} (of Node A)
+      \item Nonce
+      \item Encrypted with the temporary symmetric key of Node A and the nonce:
+        \begin{itemize}
+          \item \texttt{IP\_Port} (of us)
+        \end{itemize}
+    \end{itemize}
+  \item Data to send back
+\end{itemize}
+
+(sent from node B to node A):
+
+\begin{itemize}
+  \item \texttt{uint8\_t} (0x8e) packet id
+  \item Nonce
+  \item Encrypted with the temporary symmetric key of Node A and the nonce:
+    \begin{itemize}
+      \item \texttt{IP\_Port} (of us)
+    \end{itemize}
+  \item Data to send back
+\end{itemize}
+
+(sent from node A to us):
+
+\begin{itemize}
+  \item Data to send back
+\end{itemize}
+
+Each packet is encrypted multiple times so that only node A will be able to
+receive and decrypt the first packet and know where to send it to, node B will
+only be able to receive that decrypted packet, decrypt it again and know where
+to send it and so on.  You will also notice a piece of encrypted data (the
+sendback) at the end of the packet that grows larger and larger at every layer
+with the IP of the previous node in it.  This is how the node receiving the end
+data (Node D) will be able to send data back.
+
+When a peer receives an onion packet, they will decrypt it, encrypt the
+coordinates (IP/port) of the source along with the already existing encrypted
+data (if it exists) with a symmetric key known only by the peer and only
+refreshed every hour (in toxcore) as a security measure to force expire paths.
+
+Here's a diagram how it works:
+
+\begin{verbatim}
+peer
+  -> [onion1[onion2[onion3[data]]]] -> Node A
+  -> [onion2[onion3[data]]][sendbackA] -> Node B
+  -> [onion3[data]][sendbackB[sendbackA]] -> Node C
+  -> [data][SendbackC[sendbackB[sendbackA]]]-> Node D (end)
+\end{verbatim}
+
+\begin{verbatim}
+Node D
+  -> [SendbackC[sendbackB[sendbackA]]][response] -> Node C
+  -> [sendbackB[sendbackA]][response] -> Node B
+  -> [sendbackA][response] -> Node A
+  -> [response] -> peer
+\end{verbatim}
+
+The random public keys in the onion packets are temporary public keys generated
+for and used for that onion path only.  This is done in order to make it
+difficult for others to link different paths together.  Each encrypted layer
+must have a different public key.  This is the reason why there are multiple
+keys in the packet definintions above.
+
+The nonce is used to encrypt all the layers of encryption.  This 24 byte nonce
+should be randomly generated.  If it isn't randomly generated and has a
+relation to nonces used for other paths it could be possible to tie different
+onion paths together.
+
+The \texttt{IP\_Port} is an ip and port in packed format:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{TOX\_AF\_INET} (2) for IPv4 or \texttt{TOX\_AF\_INET6} (10) for IPv6 \\
+  \texttt{4 $|$ 16}  & IP address (4 bytes if IPv4, 16 if IPv6) \\
+  \texttt{12 $|$ 0}  & Zeroes \\
+  \texttt{2}         & \texttt{uint16\_t} Port \\
+\end{tabular}
+
+If IPv4 the format is padded with 12 bytes of zeroes so that both IPv4 and IPv6
+have the same stored size.
+
+The \texttt{IP\_Port} will always end up being of size 19 bytes.  This is to
+make it hard to know if an ipv4 or ipv6 ip is in the packet just by looking at
+the size.  The 12 bytes of zeros when ipv4 must be set to 0 and not left
+uninitialized as some info may be leaked this way if it stays uninitialized.
+All numbers here are in big endian format.
+
+The \texttt{IP\_Port} in the sendback data can be in any format as long as the
+length is 19 bytes because only the one who writes it can decrypt it and read
+it, however, using the previous format is recommended because of code reuse.
+The nonce in the sendback data must be a 24 byte nonce.
+
+Each onion layers has a different packed id that identifies it so that an
+implementation knows exactly how to handle them.  Note that any data being sent
+back must be encrypted, appear random and not leak information in any way as
+all the nodes in the path will see it.
+
+If anything is wrong with the received onion packets (decryption fails) the
+implementation should drop them.
+
+The implementation should have code for each different type of packet that
+handles it, adds (or decrypts) a sendback and sends it to the next peer in the
+path.  There are a lot of packets but an implementation should be very
+straightforward.
+
+Note that if the first node in the path is a TCP relay, the TCP relay must put
+an identifier (instead of an IP/Port) in the sendback so that it knows that any
+response should be sent to the appropriate peer connected to the TCP relay.
+
+This explained how to create onion packets and how they are sent back.  Next is
+what is actually sent and received on top of these onion packets or paths.
+
+Note: nonce is a 24 byte nonce.
+
+announce request packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x83) \\
+  \texttt{24}        & Nonce \\
+  \texttt{32}        & A public key (real or temporary) \\
+  \texttt{?}         & Payload \\
+\end{tabular}
+
+The public key is our real long term public key if we want to announce
+ourselves, a temporary one if we are searching for friends.
+
+The payload is encrypted with the secret key part of the sent public key, the
+public key of Node D and the nonce, and contains:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{32}        & Ping ID \\
+  \texttt{32}        & Public key we are searching for \\
+  \texttt{32}        & Public key that we want those sending back data packets to use \\
+  \texttt{8}         & Data to send back in response \\
+\end{tabular}
+
+If the ping id is zero, respond with an announce response packet.
+
+If the ping id matches the one the node sent in the announce response and the
+public key matches the one being searched for, add the part used to send data
+to our list.  If the list is full make it replace the furthest entry.
+
+data to route request packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x85) \\
+  \texttt{32}        & Public key of destination node \\
+  \texttt{24}        & Nonce \\
+  \texttt{32}        & Temporary just generated public key \\
+  variable           & Payload \\
+\end{tabular}
+
+The payload is encrypted with that temporary secret key and the nonce and the
+public key from the announce response packet of the destination node.  If Node
+D contains the ret data for the node, it sends the stuff in this packet as a
+data to route response packet to the right node.
+
+The data in the previous packet is in format:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{32}        & Real public key of sender \\
+  variable           & Payload \\
+\end{tabular}
+
+The payload is encrypted with real secret key of the sender, the nonce in the
+data packet and the real public key of the receiver:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} id \\
+  variable           & Data (optional) \\
+\end{tabular}
+
+Data sent to us:
+
+announce response packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x84) \\
+  \texttt{8}         & Data to send back in response \\
+  \texttt{24}        & Nonce \\
+  variable           & Payload \\
+\end{tabular}
+
+The payload is encrypted with the DHT secret key of Node D, the public key in
+the request and the nonce:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} \texttt{is\_stored} \\
+  \texttt{32}        & Ping ID or Public Key \\
+  variable           & Maximum of 4 nodes in packed node format (see DHT) \\
+\end{tabular}
+
+The packet contains a ping ID if \texttt{is\_stored} is 0 or 2, or the public
+key that must be used to send data packets if \texttt{is\_stored} is 1.
+
+If the \texttt{is\_stored} is not 0, it means the information to reach the
+public key we are searching for is stored on this node.  \texttt{is\_stored} is
+2 as a response to a peer trying to announce himself to tell the peer that he
+is currently announced successfully.
+
+data to route response packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x86) \\
+  \texttt{24}        & Nonce \\
+  \texttt{32}        & Temporary just generated public key \\
+  variable           & Payload \\
+\end{tabular}
+
+The payload is encrypted with that temporary secret key, the nonce and the
+public key from the announce response packet of the destination node.
+
+There are 2 types of request packets and 2 'response' packets to go with them.
+The announce request is used to announce ourselves to a node and announce
+response packet is used by the node to respond to this packet.  The data to
+route request packet is a packet used to send packets through the node to
+another peer that has announced itself and that we have found.  The data to
+route response packet is what the node transforms this packet into.
+
+To announce ourselves to the network we must first find, using announce
+packets, the peers with the DHT public key closest to our real public key.  We
+must then announce ourselves to these peers.  Friends will then be able to send
+messages to us using data to route packets by sending them to these peers.  To
+find the peers we have announced ourselves to, our friends will find the peers
+closest to our real public key and ask them if they know us.  They will then be
+able to use the peers that know us to send us some messages that will contain
+their DHT public key (which we need to know to connect directly to them), TCP
+relays that they are connected to (so we can connect to them with these relays
+if we need to) and some DHT peers they are connected to (so we can find them
+faster in the DHT).
+
+Announce request packets are the same packets used slightly differently if we
+are announcing ourselves or searching for peers that know one of our friends.
+
+If we are announcing ourselves we must put our real long term public key in the
+packet and encrypt it with our long term private key.  This is so the peer we
+are announcing ourselves to can be sure that we actually own that public key.
+If we are looking for peers we use a temporary public key used only for packets
+looking for that peer in order to leak as little information as possible.  The
+\texttt{ping\_id} is a 32 byte number which is sent to us in the announce
+response and we must send back to the peer in another announce request.  This
+is done in order to prevent people from easily announcing themselves many times
+as they have to prove they can respond to packets from the peer before the peer
+will let them announce themselves.  This \texttt{ping\_id} is set to 0 when none
+is known.
+
+The public key we are searching for is set to our long term public key when
+announcing ourselves and set to the long term public key of the friend we are
+searching for if we are looking for peers.
+
+When announcing ourselves, the public key we want others to use to send us data
+back is set to a temporary public key and we use the private key part of this
+key to decrypt packet routing data sent to us.  This public key is to prevent
+peers from saving old data to route packets from previous sessions and be able
+to replay them in future Tox sessions.  This key is set to zero when searching
+for peers.
+
+The sendback data is an 8 byte number that will be sent back in the announce
+packet response.  Its goal is to be used to learn which announce request packet
+the response is responding to, and hence its location in the unencrypted part
+of the response.  This is needed in toxcore to find and check info about the
+packet in order to decrypt it and handle it correctly.  Toxcore uses it as an
+index to its special \texttt{ping\_array}.
+
+Why don't we use different packets instead of having one announce packet
+request and one response that does everything? It makes it a lot more difficult
+for possible attackers to know if we are merely announcing ourselves or if we
+are looking for friends as the packets for both look the same and are the same
+size.
+
+The unencrypted part of an announce response packet contains the sendback data,
+which was sent in the request this packet is responding to and a 24 byte random
+nonce used to encrypt the encrypted part.
+
+The \texttt{is\_stored} number is set to either 0, 1 or 2.  0 means that the
+public key that was being searched in the request isn't stored or known by this
+peer.  1 means that it is and 2 means that we are announced successfully at
+that node.  Both 1 and 2 are needed so that when clients are restarted it is
+possible to reannounce without waiting for the timeout of the previous
+announce.  This would not otherwise be possible as a client would receive
+response 1 without a \texttt{ping\_id} which is needed in order to reannounce
+successfully.
+
+When the \texttt{is\_stored} number is 0 or 2, the next 32 bytes is a
+\texttt{ping\_id}.  When \texttt{is\_stored} is 1 it corresponds to a public key
+(the send back data public key set by the friend in their announce request)
+that must be used to encrypt and send data to the friend.
+
+Then there is an optional maximum 4 nodes, in DHT packed nodes format (see
+DHT), attached to the response which denote the 4 DHT peers with the DHT public
+keys closest to the searched public key in the announce request known by the
+peer (see DHT).  To find these peers, toxcore uses the same function as is used
+to find peers for get node DHT responses.  Peers wanting to announce themselves
+or searching for peers that 'know' their friends will recursively query closer
+and closer peers until they find the closest possible and then either announce
+themselves to them or just ping them every once in a while to know if their
+friend can be contacted.  Note that the distance function used for this is the
+same as the Tox DHT.
+
+Data to route request packets are packets used to send data directly to another
+peer via a node that knows that peer.  The public key is the public key of the
+final destination where we want the packet to be sent (the real public key of
+our friend).  The nonce is a 24 byte random nonce and the public key is a
+random temporary public key used to encrypt the data in the packet and, if
+possible, only to send packets to this friend (we want to leak as little info
+to the network as possible so we use temp public keys as we don't want a peer
+to see the same public keys and be able to link things together).  The data is
+encrypted data that we want to send to the peer with the public key.
+
+The route response packets are just the last elements (nonce, public key,
+encrypted data) of the data to route request packet copied into a new packet
+and sent to the appropriate destination.
+
+To handle onion announce packets, toxcore first receives an announce packet and
+decrypts it.
+
+Toxcore generates \texttt{ping\_id}s by taking a 32 byte sha hash of the current
+time, some secret bytes generated when the instance is created, the current
+time divided by a 300 second timeout, the public key of the requester and the
+source ip/port that the packet was received from.  Since the ip/port that the
+packet was received from is in the \texttt{ping\_id}, the announce packets being
+sent with a ping id must be sent using the same path as the packet that we
+received the \texttt{ping\_id} from or announcing will fail.
+
+The reason for this 300 second timeout in toxcore is that it gives a reasonable
+time (300 to 600 seconds) for peers to announce themselves.
+
+Toxcore generates 2 different ping ids, the first is generated with the current
+time (divided by 300) and the second with the current time + 300 (divided by 300).
+The two ping ids are then compared to the ping ids in the received packets.
+The reason for doing this is that storing every ping id received might be
+expensive and leave us vulnerable to a DoS attack, this method makes sure that
+the other cannot generate \texttt{ping\_id}s and must ask for them.  The reason
+for the 2 \texttt{ping\_id}s is that we want to make sure that the timeout is at
+least 300 seconds and cannot be 0.
+
+If one of the two ping ids is equal to the ping id in the announce request,
+the sendback data public key and the sendback data are stored in the
+datastructure used to store announced peers.  If the implementation has a
+limit to how many announced entries it can store, it should only store the
+entries closest (determined by the DHT distance function) to its DHT public
+key.  If the entry is already there, the information will simply be updated
+with the new one and the timeout will be reset for that entry.
+
+Toxcore has a timeout of 300 seconds for announce entries after which they are
+removed which is long enough to make sure the entries don't expire prematurely
+but not long enough for peers to stay announced for extended amounts of time
+after they go offline.
+
+Toxcore will then copy the 4 DHT nodes closest to the public key being searched
+to a new packet (the response).
+
+Toxcore will look if the public key being searched is in the datastructure.  If
+it isn't it will copy the second generated \texttt{ping\_id} (the one generated
+with the current time plus 300 seconds) to the response, set the
+\texttt{is\_stored} number to 0 and send the packet back.
+
+If the public key is in the datastructure, it will check whether the public key
+that was used to encrypt the announce packet is equal to the announced public
+key, if it isn't then it means that the peer is searching for a peer and that
+we know it.  This means the \texttt{is\_stored} is set to 1 and the sending back
+data public key in the announce entry is copied to the packet.
+
+If it (key used to encrypt the announce packet) is equal (to the announced
+public key which is also the 'public key we are searching for' in the announce
+packet) meaning the peer is announcing itself and an entry for it exists, the
+sending back data public key is checked to see if it equals the one in the
+packet.  If it is not equal it means that it is outdated, probably because the
+announcing peer's toxcore instance was restarted and so their
+\texttt{is\_stored} is set to 0, if it is equal it means the peer is announced
+correctly so the \texttt{is\_stored} is set to 2.  The second generated
+\texttt{ping\_id} is then copied to the packet.
+
+Once the packet is contructed a random 24 byte nonce is generated, the packet
+is encrypted (the shared key used to decrypt the request can be saved and used
+to encrypt the response to save an expensive key derivation operation), the
+data to send back is copied to the unencrypted part and the packet is sent back
+as an onion response packet.
+
+In order to announce itself using onion announce packets toxcore first takes
+DHT peers, picks random ones and builds onion paths with them by saving 3
+nodes, calling it a path, generating some keypairs for encrypting the onion
+packets and using them to send onion packets.  If the peer is only connected
+with TCP, the initial nodes will be bootstrap nodes and connected TCP relays
+(for the first peer in the path).  Once the peer is connected to the onion he
+can fill up his list of known peers with peers sent in announce responses if
+needed.
+
+Onion paths have different timeouts depending on whether the path is confirmed
+or unconfirmed.  Unconfirmed paths (paths that core has never received any
+responses from) have a timeout of 4 seconds with 2 tries before they are deemed
+non working.  This is because, due to network conditions, there may be a large
+number of newly created paths that do not work and so trying them a lot would
+make finding a working path take much longer.  The timeout for a confirmed path
+(from which a response was received) is 10 seconds with 4 tries without a
+response.  A confirmed path has a maximum lifetime of 1200 seconds to make
+possible deanonimization attacks more difficult.
+
+Toxcore saves a maximum of 12 paths: 6 paths are reserved for announcing
+ourselves and 6 others are used to search for friends.  This may not be the
+safest way (some nodes may be able to associate friends together) however it is
+much more performant than having different paths for each friend.  The main
+benefit is that the announcing and searching are done with different paths,
+which makes it difficult to know that peer with real public key X is friends
+with Y and Z.  More research is needed to find the best way to do this.  At
+first toxcore did have different paths for each friend, however, that meant
+that each friend path was almost never used (and checked).  When using a low
+amount of paths for searching there is less resources needed to find good
+paths.  6 paths are used because 4 was too low and caused some performance
+issues because it took longer to find some good paths at the beginning because
+only 4 could be tried at a time.  A too high number meanwhile would mean each
+path is used (and tested) less.  The reason why the numbers are the same for
+both types of paths is for code simplification purposes.
+
+To search/announce itself to peers, toxcore keeps the 8 closest peers (12 for
+announcing) to each key it is searching (or announcing itself to).  To
+populate these it starts by sending announce requests to random peers for all
+the public keys it is searching for.  It then recursively searches closer and
+closer peers (DHT distance function) until it no longer finds any.  It is
+important to make sure it is not too aggressive at searching the peers as some
+might no longer be online but peers might still send announce responses with
+their information. Toxcore keeps lists of last pinged nodes for each key
+searched so as not to ping dead nodes too aggressively.
+
+Toxcore decides if it will send an announce packet to one of the 4 peers in the
+announce response by checking if the peer would be stored as one of the stored
+closest peers if it responded; if it would not be it doesn't send an announce
+request, if it would be it sends one.
+
+Peers are only put in the closest peers array if they respond to an announce
+request.  If the peers fail to respond to 3 announce requests they are deemed
+timed out and removed.  When sending an announce request to a peer to which we
+have been announcing ourselves for at least 90 seconds and which has failed to
+respond to the previous 2 requests, toxcore uses a random path for the request.
+This reduces the chances that a good node will be removed due to bad paths.
+
+The reason for the numbers of peers being 8 and 12 is that lower numbers might
+make searching for and announcing too unreliable and a higher number too
+bandwidth/resource intensive.
+
+Toxcore uses \texttt{ping\_array} (see \texttt{ping\_array}) for the 8 byte
+sendback data in announce packets to store information that it will need to
+handle the response (key to decrypt it, why was it sent? (to announce ourselves
+or to search? For what key? and some other info)).  For security purposes it
+checks to make sure the packet was received from the right ip/port and checks
+if the key in the unencrypted part of the packet is the right public key.
+
+For peers we are announcing ourselves to, if we are not announced to them
+toxcore tries every 3 seconds to announce ourselves to them until they return
+that we have announced ourselves to them, then initially toxcore sends an
+announce request packet every 15 seconds to see if we are still announced and
+reannounce ourselves at the same time.  Toxcore sends every announce packet
+with the \texttt{ping\_id} previously received from that peer with the same
+path (if possible).  Toxcore use a timeout of 120 seconds rather than 15
+seconds if we have been announcing to the peer for at least 90 seconds, and
+the onion path we are are using for the peer has also been alive for at least
+90 seconds, and we have not been waiting for at least 15 seconds for a
+response to a request sent to the peer, nor for at least 10 seconds for a
+response to a request sent via the path. The timeout of at most 120 seconds
+means a \texttt{ping\_id} received in the last packet will not have had time
+to expire (300 second minimum timeout) before it is resent 120 seconds later.
+
+For friends this is slightly different.  It is important to start searching for
+friends after we are fully announced.  Assuming a perfect network, we would
+only need to do a search for friend public keys only when first starting the
+instance (or going offline and back online) as peers starting up after us would
+be able to find us immediately just by searching for us.  If we start searching
+for friends after we are announced we prevent a scenario where 2 friends start
+their clients at the same time but are unable to find each other right away
+because they start searching for each other while they have not announced
+themselves.
+
+For this reason, after the peer is announced successfully, for 17 seconds
+announce packets are sent aggressively every 3 seconds to each known close peer
+(in the list of 8 peers) to search aggressively for peers that know the peer we
+are searching for.
+
+After this, toxcore sends requests once per 15 seconds initially, then
+uses linear backoff to increase the interval.  In detail, the interval used
+when searching for a given friend is at least 15 and at most 2400 seconds, and
+within these bounds is calculated as one quarter of the time since we began
+searching for the friend, or since the friend was last seen. For this purpose,
+a friend is considered to be seen when some peer reports that the friend is
+announced, or we receive a DHT Public Key packet from the friend, or we obtain
+a new DHT key for them from a group, or a friend connection for the friend
+goes offline.
+
+There are other ways this could be done and which would still work but, if
+making your own implementation, keep in mind that these are likely not the most
+optimized way to do things.
+
+If we find peers (more than 1) that know a friend we will send them an onion
+data packet with our DHT public key, up to 2 TCP relays we are connected to and
+2 DHT peers close to us to help the friend connect back to us.
+
+Onion data packets are packets sent as the data of data to route packets.
+
+Onion data packets:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{32}        & Long term public key of sender \\
+  variable           & Payload \\
+\end{tabular}
+
+The payload is encrypted with long term private key of the sender, the long
+term public key of the receiver and the nonce used in the data to route request
+packet used to send this onion data packet (shaves off 24 bytes).
+
+DHT public key packet:
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x9c) \\
+  \texttt{8}         & \texttt{uint64\_t} \texttt{no\_replay} \\
+  \texttt{32}        & Our DHT public key \\
+  \texttt{[39, 204]} & Maximum of 4 nodes in packed format \\
+\end{tabular}
+
+The packet will only be accepted if the \texttt{no\_replay} number is greater
+than the \texttt{no\_replay} number in the last packet received.
+
+The nodes sent in the packet comprise 2 TCP relays to which we are
+connected (or fewer if there are not 2 available) and a number of DHT nodes
+from our Close List, with the total number of nodes sent being at most 4. The
+nodes chosen from the Close List are those closest in DHT distance to us. This
+allows the friend to find us more easily in the DHT, or to connect to us via a
+TCP relay.
+
+Why another round of encryption? We have to prove to the receiver that we own
+the long term public key we say we own when sending them our DHT public key.
+Friend requests are also sent using onion data packets but their exact format
+is explained in Messenger.
+
+The \texttt{no\_replay} number is protection if someone tries to replay an older
+packet and should be set to an always increasing number.  It is 8 bytes so you
+should set a high resolution monotonic time as the value.
+
+We send this packet every 30 seconds if there is more than one peer (in the 8)
+that says they our friend is announced on them.  This packet can also be sent
+through the DHT module as a DHT request packet (see DHT) if we know the DHT
+public key of the friend and are looking for them in the DHT but have not
+connected to them yet.  30 second is a reasonable timeout to not flood the
+network with too many packets while making sure the other will eventually
+receive the packet.  Since packets are sent through every peer that knows the
+friend, resending it right away without waiting has a high likelihood of
+failure as the chances of packet loss happening to all (up to to 8) packets
+sent is low.
+
+When sent as a DHT request packet (this is the data sent in the DHT request
+packet):
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0x9c) \\
+  \texttt{32}        & Long term public key of sender \\
+  \texttt{24}        & Nonce \\
+  variable           & Encrypted payload \\
+\end{tabular}
+
+The payload is encrypted with long term private key of sender, the long term
+public key of receiver and the nonce, and contains the DHT public key packet.
+
+When sent as a DHT request packet the DHT public key packet is (before being
+sent as the data of a DHT request packet) encrypted with the long term keys of
+both the sender and receiver and put in that format.  This is done for the same
+reason as the double encryption of the onion data packet.
+
+Toxcore tries to resend this packet through the DHT every 20 seconds.  20
+seconds is a reasonable resend rate which isn't too aggressive.
+
+Toxcore has a DHT request packet handler that passes received DHT public key
+packets from the DHT module to this module.
+
+If we receive a DHT public key packet, we will first check if the DHT packet is
+from a friend, if it is not from a friend, it will be discarded.  The
+\texttt{no\_replay} will then be checked to see if it is good and no packet with
+a lower one was received during the session.  The DHT key, the TCP nodes in the
+packed nodes and the DHT nodes in the packed nodes will be passed to their
+relevant modules.  The fact that we have the DHT public key of a friend means
+this module has achieved its goal.
+
+If a friend is online and connected to us, the onion will stop all of its
+actions for that friend.  If the peer goes offline it will restart searching
+for the friend as if toxcore was just started.
+
+If toxcore goes offline (no onion traffic for 75 seconds) toxcore will
+aggressively reannounce itself and search for friends as if it was just
+started.
+
+\chapter{Ping array}
+
+Ping array is an array used in toxcore to store data for pings.  It enables the
+storage of arbitrary data that can then be retrieved later by passing the 8
+byte ping id that was returned when the data was stored.  It also frees data
+from pings that are older than a ping expiring delay set when initializing the
+array.
+
+Ping arrays are initialized with a size and a timeout parameter.  The size
+parameter denotes the maximum number of entries in the array and the timeout
+denotes the number of seconds to keep an entry in the array.  Timeout and size
+must be bigger than 0.
+
+Adding an entry to the ping array will make it return an 8 byte number that can
+be used as the ping number of a ping packet.  This number is generated by first
+generating a random 8 byte number (toxcore uses the cryptographic secure random
+number generator), dividing then multiplying it by the total size of the array
+and then adding the index of the element that was added.  This generates a
+random looking number that will return the index of the element that was added
+to the array.  This number is also stored along with the added data and the
+current time (to check for timeouts).  Data is added to the array in a cyclical
+manner (0, 1, 2, 3... (array size - 1), 0, 1, ...).  If the array is full, the
+oldest element is overwritten.
+
+To get data from the ping array, the ping number is passed to the function to
+get the data from the array.  The modulo of the ping number with the total size
+of the array will return the index at which the data is.  If there is no data
+stored at this index, the function returns an error.  The ping number is then
+checked against the ping number stored for this element, if it is not equal the
+function returns an error.  If the array element has timed out, the function
+returns an error.  If all the checks succeed the function returns the exact
+data that was stored and it is removed from the array.
+
+Ping array is used in many places in toxcore to efficiently keep track of sent
+packets.
+
+\input{src/Network/Tox/SaveData.lhs}
+\input{src/Network/Tox/Testing.lhs}
diff --git a/src/Network/Tox/Application/GroupChats.lhs b/src/Network/Tox/Application/GroupChats.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Application/GroupChats.lhs
@@ -0,0 +1,388 @@
+\chapter{DHT Group Chats}
+
+This document details the groupchat implementation, giving a high level overview
+of all the important features and aspects, as well as some important low level
+implementation details. This documentation reflects what is currently
+implemented at the time of writing; it is not speculative. For detailed API docs
+see the groupchats section of the tox.h header file.
+
+\section{Features}
+
+\begin{itemize}
+  \item Private messages
+  \item Action messages (/me)
+  \item Public groups (peers may join via a public key)
+  \item Private groups (peers require a friend invite)
+  \item Permanence (a group cannot 'die' as long as at least one peer retains
+    their group credentials)
+  \item Persistence across client restarts
+  \item Ability to set peer limits
+  \item Moderation (kicking, banning, silencing)
+  \item Permanent group names (set on creation)
+  \item Topics (may only be set by moderators and the founder)
+  \item Password protection
+  \item Self-repairing (auto-rejoin on disconnect, group split protection, state
+    syncing)
+  \item Identity separation from the Tox ID
+  \item Ability to ignore peers
+  \item Unique nicknames which can be set on a per-group basis
+  \item Peer statuses (online, away, busy) which can be set on a per-group basis
+  \item Custom parting/exit messages
+\end{itemize}
+
+\section{Group roles}
+
+There are four distinct roles which are hierarchical in nature (higher roles
+have all the privileges of lower roles).
+
+\begin{itemize}
+  \item \textbf{Founder} - The group's creator. May set all other peers roles
+    to anything except founder. May also set the group password, toggle the
+    privacy state, and set the peer limit.
+  \item \textbf{Moderator} - Promoted by the founder. May kick, ban and set
+    the user and observer roles for peers below this role. May also set the
+    topic.
+  \item \textbf{User} - Default non-founder role. May communicate with other
+    peers normally.
+  \item \textbf{Observer} - Demoted by moderators and the founder. May observe
+    the group and ignore peers; may not communicate with other peers or with the
+    group.
+\end{itemize}
+
+\section{Group types}
+
+Groups can have two types: private and public. The type can be set on creation,
+and may also be toggled by the group founder at any point after creation.
+(\emph{Note: password protection is completely independent of the group type})
+
+\subsection{Public}
+
+Anyone may join the group using the Chat ID. If the group is public, information
+about peers inside the group, including their IP addresses and group public keys
+(but not their Tox ID's) is visible to anyone with access to a node storing
+their DHT announcement. See the \href{#dht-announcements}{DHT Announcements}
+section for details.
+
+\subsection{Private}
+
+The only way to join a private group is by having someone in your friend list
+send you an invite. If the group is private, no peer/group information
+(mentioned in the Public section) is present in the DHT; the DHT is not used for
+any purpose at all. If a public group is set to private, all DHT information
+related to the group will expire within a few minutes.
+
+\section{Cryptography}
+
+Groupchats use the
+\href{https://en.wikipedia.org/wiki/NaCl_(software)}{NaCl/libsodium cryptography
+library} for all cryptography related operations. All group communication is
+end-to-end encrypted. Message confidentiality, integrity, and repudability are
+guaranteed via
+\href{https://en.wikipedia.org/wiki/Authenticated_encryption}{authenticated
+encryption}, and \href{https://en.wikipedia.org/wiki/Forward_secrecy}{perfect
+forward secrecy} is also provided.
+
+One of the most important security improvements from the old groupchat
+implementation is the removal of a message-relay mechanism that uses a
+group-wide shared key. Instead, connections are 1-to-1 (a complete graph),
+meaning an outbound message is sent once per peer, and encrypted/decrypted using
+a key unique to each peer. This prevents MITM attacks that were previously
+possible. This additionally ensures that private messages are truly private.
+
+Groups make use of 13 unique keys in total: Two permanent keypairs (encryption
+and signature), two group keypairs (encryption and signature), one session
+keypair (encryption), one shared symmetric key (encryption), and one temp DHT
+keypair (encryption).
+
+The Tox ID/Tox public key is not used for any purpose. As such, neither peers in
+a given group nor in the group DHT can be matched with their Tox ID. In other
+words, there is no way of identifying a peer aside from their IP address,
+nickname, and group public key. (\emph{Note: group nicknames can be different
+from the client's main nickname that their friends see}).
+
+\subsection{Permanent keypairs}
+
+When a peer creates or joins a group they generate two permanent keypairs: an
+encryption keypair and a signature keypair, both of which are unique to the
+group. The two public keys are the only guaranteed way to identify a peer, and
+both keypairs will persist for as long as a peer remains in the group (even
+across client restarts). If a peer exits the group these keypairs will be lost
+forever.
+
+This encryption keypair is not used for any encryption operations except for the
+initial handshake when connecting to another peer. For usage details on the
+signature key, see the \href{#moderation}{Moderation} section.
+
+\subsection{Session keypair/shared symmetric key}
+
+When two peers establish a connection they each generate a session encryption
+keypair and share one another's resulting public key. With their own session
+secret key and the other's session public key, they will both generate the same
+symmetric encryption key. This symmetric key will be used for all further
+encryption operations between them for the current session (i.e. until one of
+them disconnects).
+
+The purpose of this extra key exchange is to prevent an adversary from
+decrypting messages from previous sessions in event that a secret encryption key
+becomes compromised. This is known as forward secrecy.
+
+\subsection{Group keypairs}
+
+The group founder generates two additional permanent keypairs when the group is
+created: an encryption keypair, and a signature keypair. The public signature
+key is considered the \textbf{Chat ID} and is used as the group's permanent
+identifier, allowing other peers to join public groups via the DHT. Every peer
+in the group holds a copy of the group's public encryption key along with the
+public signature key/Chat ID.
+
+The group secret keys are similar to the permanent keypairs in that they will
+persist across client restarts, but will be lost forever if the founder exits
+the group. This is particularly important as administration related
+functionality will not work without these keys. See the
+\href{#founders}{Founders} section for usage details.
+
+\subsection{Temporary DHT keypair}
+
+All group related DHT procedures make use of toxcore's temp DHT keypair. This
+keypair is generated when the Tox object is initialized and does not persist
+across client restarts. See the \href{#dht-announcements}{DHT Announcements}
+section for further details.
+
+\section{Founders}
+
+The peer who creates the group is the group's founder. Founders have a set of
+admin privileges, including:
+
+\begin{itemize}
+  \item Promoting and demoting moderators
+  \item The ability to kick/ban moderators
+  \item Setting the peer limit
+  \item Setting the group's privacy state
+  \item Setting group passwords
+\end{itemize}
+
+\subsection{Shared state}
+
+Groups contain a data structure called the \textbf{shared state} which is given
+to every peer who joins the group. In this structure resides all data pertaining
+to the group that must only be modifiable by the group founder. This includes
+things like the group name, the group type, the peer limit, and the password.
+Additionally, the shared state holds a copy of the group founder's public
+encryption and signature keys, which is how other peers in the group are able to
+verify the identity of the group founder.
+
+The shared state is signed by the founder using the group secret signature key.
+As the founder is the only peer who holds this secret key, this ensures that the
+shared state may be safely shared by untrusted peers, even in the absence of the
+founder.
+
+When the founder modifies the shared state, he increments the shared state
+version, signs the new shared state data with the group secret signature key,
+and broadcasts the new shared state data along with its signature to the entire
+group. When a peer receives this broadcast, he uses the group public signature
+key to verify that the data was signed with the group secret signature key, and
+also verifies that the new version is not older than the current version.
+
+\subsection{Moderation}
+
+The founder has the ability to promote other peers to the moderator role.
+Moderators have all the privileges of normal users, and additionally have the
+power to kick, ban, and unban, as well as give peers below the moderator role
+the roles of user and observer (see the \href{#group-roles}{Group roles}
+section).  Moderators can also modify the group topic. Moderators have no power
+over one another; only the founder can kick, ban, or change the role of a
+moderator.
+
+\subsection{Kicks/bans}
+
+When a peer is kicked or banned from the group, his chat instance and all its
+associated data will be destroyed. This includes all public and secret keys.
+Additionally, the the peer will not receive any notifiactions; it will simply
+appear to them as if the group is inactive.
+
+\subsection{Moderator list}
+
+Each peer holds a copy of the \textbf{moderator list}, which is an array of
+public signature keys of peers who currently have the moderator role (including
+those who are offline). A hash (sha256) of this list called the
+\textbf{\verb'mod_list_hash'} is stored in the shared state, which is itself
+signed by the founder using the group secret signature key. This allows the
+moderator list to be shared between untrusted peers, even in the absence of the
+founder, while maintaining moderator verifiability.
+
+When the founder modifies the moderator list, he updates the
+\verb'mod_list_hash', increments the shared state version, signs the new shared
+state, broadcasts the new shared state data along with its signature to the
+entire group, then broadcasts the new moderator list to the entire group. When a
+peer receives this moderator list (having already verified the new shared
+state), he creates a hash of the new list and verifies that it is identical to
+the \verb'mod_list_hash'.
+
+\subsection{Sanctions list}
+
+Each peer holds a copy of the \textbf{sanctions list}. This list holds two
+sublists: Banned peers, and peers with the observer role, or the \textbf{ban
+list} and the \textbf{observer list} respectively. The ban list contains entries
+of peers who have been banned, including their last used nickname, IP
+address/port, and a unique ID. The sanctions list contains entries of peers who
+have been demoted to the observer role, including just their public encryption
+key.
+
+All entries additionally contain a timestamp of the time the entry was made, the
+public signature key of the peer who set the sanction, and a signature of the
+entry's data, which is signed by the peer who created the entry using their
+secret signature key. Individual entries are verified by ensuring that the
+entry's public signature key belongs to the founder or is present in the
+moderator list, and then verifying that the entry's data was signed by the owner
+of that key.
+
+Although each individual entry can be verified, we still need a way to verify
+that the list as a whole is complete and identical for every peer, otherwise any
+peer would be able to remove entries arbitrarily, or replace the list with an
+older version. Therefore each peer holds a copy of the \textbf{sanctions list
+credentials}. This is a data structure that holds the version, a hash (sha256)
+of all sanctions list entries plus the version, the public signature key of the
+last peer to have modified the sanctions list, and a signature of the hash,
+which is created by that key.
+
+When a moderator or founder modifies the sanctions list, he will increment the
+version, create a new hash, sign the hash+version with his secret signature key,
+and replace the old public signature key with his own. He will then broadcast
+the new changes (not the entire list) to the entire group along with the new
+credentials. When a peer receives this broadcast, he will verify that the new
+credentials version is not older than the current version and verify that the
+changes were made by a moderator or the founder. If adding an entry, he will
+verify that the entry was signed by the signature key of the entry's creator.
+
+When the founder kicks, bans or demotes a moderator, he will first go through
+the sanctions list and re-sign each entry made by that moderator with his own
+founder key, then re-broadcast the sanctions list to the entire group. This is
+necessary to guarantee that all sanctions list entries and its credentials are
+signed by a current moderator or the founder at all times.
+
+\textbf{Note:} \emph{The sanctions list is not saved to the Tox save file,
+meaning that if the group ever becomes empty, the sanctions list will be reset.
+This is in contrast to the shared state and moderator list, which are both saved
+and will persist even if the group becomes empty.}
+
+\section{Topics}
+
+Founders and moderators have the ability to set the \textbf{topic}, which is
+simply an arbitrary string of characters. The integrity of a topic is maintained
+in a similar manner as sanctions entries, using a data structure called
+\textbf{\verb'topic_info'}. This is a struct which contains the topic, a
+version, and the public key of the peer who set it.
+
+When a peer modifies the topic, they will increment the version, sign the new
+topic+version with their secret signature key, replace the public key with their
+own, then broadcast the new \verb'topic_info' data along with the signature to
+the entire group. When a peer receives this broadcast, they will first check if
+the public signature key of the setter either belongs to the founder, or is in
+the moderator list. They will then verify the signature using the setter's
+public signature key, and finally they will ensure that the version is not older
+than the current topic version.
+
+If the moderator who set the current topic is kicked, banned, or demoted, the
+founder will re-sign the topic using his own signature key, and rebroadcast it
+to the entire group.
+
+\section{State syncing}
+
+Peers send four unsigned 32-bit integers along with their ping packets: Their
+peer count\footnote{We use a "real" peer count, which is the number of confirmed
+peers in the peerlist (that is, peers who you have successfully handshaked and
+exchanged peer info with).}, their shared state version, their sanctions
+credentials version, and their topic version. If a peer receives a ping in which
+any of these values are greater than their own, this indicates that they may be
+out of sync with the rest of the group. In this case they will do one of two
+things: If they already have a sync request flagged for this peer, they will
+send a sync request.  Otherwise they will set the flag and wait until the next
+ping arrives (this waiting is to correct for false-positives in the case of high
+network latency).  The flag is reset after a sync request is sent, or whenever a
+ping is received in which all data is in sync.
+
+\section{Group syncing}
+
+In order to prevent entirely separate subgroups with the same Chat ID from being
+created, be it due to network issues or a malicious MITM attempt, it's necessary
+for groups to periodically search the DHT for announced nodes that match the
+group's Chat ID but are not present in the group. In case an unknown node is
+found, an attempt will be made to connect with it. If successful, the state sync
+mechanism will merge the subgroups shortly.
+
+Since we don't want to spam the DHT with a redundant number of requests that
+grows linearly with the size of the group, peers will take turns doing the
+search. Peers decide independently if it's their turn to search. Each peer has
+the same base timer T, and every interval of T they will do a search with a
+probability P which is inversely proportionate to the number of peers N. For
+example, if N=1 then P=1.0. If N=4 then P=0.25. If N=100 then P=0.01 and so on.
+This guarantees that a given group will do 1 search per T interval on average
+regardless of its size, and it also ensures that a full spectrum of the network
+is searched. Moreover, because peers act independently rather than in
+coordination, malicious peers have little exploit potential (e.g. attempting to
+stop the group from searching the DHT).
+
+In addition, peers who join a group via the DHT will attempt to connect to any
+nodes that are not in their freshly synced peer list.
+
+\section{DHT Announcements}
+
+Groupchats make use of the Tox DHT network in order to allow for groups that can
+be joined by anyone who possesses the Chat ID. As all of the information stored
+in or passed through the DHT can be viewed by any of the involved nodes, these
+types of groups are considered to be public. Private groups in contrast do not
+make use of the DHT for any purpose, and as such require a friend invite in
+order to join.
+
+\subsection{Announcement requests}
+
+When peers create or successfully join a public group they send an
+\textbf{announcement request}, containing information about the group that
+they're announcing and themselves to K of their close DHT nodes. The information
+in this request includes the announcer's group public encryption key and IP
+address/port, as well as the Chat ID of the group. The DHT attempts to store
+this announcement in the node that's closest to the Chat ID (\textbf{closeness}
+is calculated by the DHT's close function). DHT nodes can store up to N
+announcements each, after which they will replace the oldest announcements
+first. See the \href{#redundancy}{Redundancy} section for details on how DDoS
+attacks are mitigated.
+
+\subsection{Get nodes requests}
+
+When peers attempt to join a public group using the Chat ID they send a
+\textbf{get nodes request}, containing their IP/port, their group public
+encryption key, and the Chat ID to K of their close nodes. Those nodes will then
+check if any of their announcement entries match the supplied Chat ID. If not,
+they will relay the message to K of their own close nodes who will repeat the
+process (note that the close function guarantees that each successive relay will
+bring us closer to the Chat ID until we either find one of its entries, or have
+traversed the entire DHT network).
+
+Once a node finds an entry with the queried Chat ID it will send a \textbf{send
+nodes response} to the original node who made the request. The response will
+contain at least one entry (possibly more) which will hold the group public
+encryption key and the IP address/port of a peer who had previously made an
+announcement request for Chat ID. With this information the requester will
+automatically initiate the handshake protocol and attempt to join the group.
+
+\subsection{Redundancy}
+
+DHT nodes will send ping requests to all of their announcement entries
+periodically in order to ensure that they are still present in the
+network/group. When a peer goes offline or leaves a group, they no longer
+respond to these ping requests, and the nodes holding their entries will discard
+them.
+
+There are scenarios in which an announcement may be dropped from the network,
+such as if the sole node holding the entry goes offline, or in the case of DDOS
+attack which attempts to push all old entries out of the DHT. In order to ensure
+that those announcements are not permanently lost, announcers will periodically
+check when they last received a ping request for a given announcement. After a
+certain amount of time without receiving a ping request they will assume that
+their entry is no longer in the DHT network and re-announce themselves. This
+ensures that every peer present in a group has an active announcement in the DHT
+at all times, and it also ensures that a group cannot become 'lost'.
+
+\begin{code}
+module Network.Tox.Application.GroupChats where
+\end{code}
diff --git a/src/Network/Tox/Binary.hs b/src/Network/Tox/Binary.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Binary.hs
@@ -0,0 +1,209 @@
+{-# LANGUAGE LambdaCase          #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE Safe                #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module Network.Tox.Binary
+  ( typeName
+  , encode, encodeC, encodeS
+  , decode, decodeC, decodeS
+  ) where
+
+import           Data.Binary                            (Binary)
+import           Data.ByteString                        (ByteString)
+import           Data.MessagePack                       (MessagePack,
+                                                         fromObject, toObject)
+import qualified Data.MessagePack                       as MessagePack
+import           Data.Proxy                             (Proxy (..))
+import           Data.Typeable                          (Typeable)
+import qualified Data.Typeable                          as Typeable
+import           Data.Word                              (Word64)
+import           Network.MessagePack.Client             (Client)
+import qualified Network.MessagePack.Client             as Client
+import           Network.MessagePack.Server             (Server)
+import qualified Network.MessagePack.Server             as Server
+
+import qualified Network.Tox.Encoding                   as Encoding
+
+import qualified Network.Tox.Crypto.Box                 as T
+import qualified Network.Tox.Crypto.Key                 as T
+import qualified Network.Tox.Crypto.KeyPair             as T
+import qualified Network.Tox.DHT.DhtPacket              as T
+import qualified Network.Tox.DHT.DhtRequestPacket       as T
+import qualified Network.Tox.DHT.NodesRequest           as T
+import qualified Network.Tox.DHT.NodesResponse          as T
+import qualified Network.Tox.DHT.PingPacket             as T
+import qualified Network.Tox.DHT.RpcPacket              as T
+import qualified Network.Tox.NodeInfo.HostAddress       as T
+import qualified Network.Tox.NodeInfo.NodeInfo          as T
+import qualified Network.Tox.NodeInfo.PortNumber        as T
+import qualified Network.Tox.NodeInfo.SocketAddress     as T
+import qualified Network.Tox.NodeInfo.TransportProtocol as T
+import qualified Network.Tox.Protocol.Packet            as T
+import qualified Network.Tox.Protocol.PacketKind        as T
+
+
+typeName :: Typeable a => Proxy a -> String
+typeName (Proxy :: Proxy a) =
+  show . Typeable.typeOf $ (undefined :: a)
+
+
+data KnownType
+  = CipherText        T.CipherText
+  | DhtPacket         T.DhtPacket
+  | DhtRequestPacket  T.DhtRequestPacket
+  | HostAddress       T.HostAddress
+  | Word64            Word64
+  | Key               T.PublicKey
+  | KeyPair           T.KeyPair
+  | NodeInfo          T.NodeInfo
+  | NodesRequest      T.NodesRequest
+  | NodesResponse     T.NodesResponse
+  | Packet            (T.Packet Word64)
+  | PacketKind        T.PacketKind
+  | PingPacket        T.PingPacket
+  | PlainText         T.PlainText
+  | PortNumber        T.PortNumber
+  | RpcPacket         (T.RpcPacket Word64)
+  | SocketAddress     T.SocketAddress
+  | TransportProtocol T.TransportProtocol
+
+
+knownTypeToObject :: KnownType -> MessagePack.Object
+knownTypeToObject = \case
+  CipherText        x -> toObject x
+  DhtPacket         x -> toObject x
+  DhtRequestPacket  x -> toObject x
+  HostAddress       x -> toObject x
+  Word64            x -> toObject x
+  Key               x -> toObject x
+  KeyPair           x -> toObject x
+  NodeInfo          x -> toObject x
+  NodesRequest      x -> toObject x
+  NodesResponse     x -> toObject x
+  Packet            x -> toObject x
+  PacketKind        x -> toObject x
+  PingPacket        x -> toObject x
+  PlainText         x -> toObject x
+  PortNumber        x -> toObject x
+  RpcPacket         x -> toObject x
+  SocketAddress     x -> toObject x
+  TransportProtocol x -> toObject x
+
+
+knownTypeEncode :: KnownType -> ByteString
+knownTypeEncode = \case
+  CipherText        x -> encode x
+  DhtPacket         x -> encode x
+  DhtRequestPacket  x -> encode x
+  HostAddress       x -> encode x
+  Word64            x -> encode x
+  Key               x -> encode x
+  KeyPair           x -> encode x
+  NodeInfo          x -> encode x
+  NodesRequest      x -> encode x
+  NodesResponse     x -> encode x
+  Packet            x -> encode x
+  PacketKind        x -> encode x
+  PingPacket        x -> encode x
+  PlainText         x -> encode x
+  PortNumber        x -> encode x
+  RpcPacket         x -> encode x
+  SocketAddress     x -> encode x
+  TransportProtocol x -> encode x
+
+
+
+--------------------------------------------------------------------------------
+--
+-- :: decode
+--
+--------------------------------------------------------------------------------
+
+
+decode :: Binary a => ByteString -> Maybe a
+decode = Encoding.decode
+
+decodeC :: forall a. (Typeable a, MessagePack a)
+        => ByteString -> Client (Maybe a)
+decodeC = Client.call "Binary.decode" $ typeName (Proxy :: Proxy a)
+
+decodeS :: Server.Method IO
+decodeS = Server.method "Binary.decode"
+  (Server.MethodDocs
+    [ Server.MethodVal "typeName" "String"
+    , Server.MethodVal "encoded" "ByteString"
+    ] $ Server.MethodVal "value" "a")
+  decodeKnownType
+
+  where
+    decodeKnownType :: String -> ByteString -> Server (Maybe MessagePack.Object)
+    decodeKnownType = \case
+      "CipherText"        -> go CipherText
+      "DhtPacket"         -> go DhtPacket
+      "DhtRequestPacket"  -> go DhtRequestPacket
+      "HostAddress"       -> go HostAddress
+      "Word64"            -> go Word64
+      "Key PublicKey"     -> go Key
+      "KeyPair"           -> go KeyPair
+      "NodeInfo"          -> go NodeInfo
+      "NodesRequest"      -> go NodesRequest
+      "NodesResponse"     -> go NodesResponse
+      "Packet Word64"     -> go Packet
+      "PacketKind"        -> go PacketKind
+      "PingPacket"        -> go PingPacket
+      "PlainText"         -> go PlainText
+      "PortNumber"        -> go PortNumber
+      "RpcPacket Word64"  -> go RpcPacket
+      "SocketAddress"     -> go SocketAddress
+      "TransportProtocol" -> go TransportProtocol
+      tycon               -> fail $ "unknown type: " ++ tycon
+
+    go f = return . fmap (knownTypeToObject . f) . Encoding.decode
+
+
+--------------------------------------------------------------------------------
+--
+-- :: encode
+--
+--------------------------------------------------------------------------------
+
+
+encode :: Binary a => a -> ByteString
+encode = Encoding.encode
+
+encodeC :: forall a. (Typeable a, MessagePack a)
+        => a -> Client ByteString
+encodeC x = Client.call "Binary.encode" (show $ Typeable.typeOf x) x
+
+encodeS :: Server.Method IO
+encodeS = Server.method "Binary.encode"
+  (Server.MethodDocs
+    [ Server.MethodVal "typeName" "String"
+    , Server.MethodVal "value" "a"
+    ] $ Server.MethodVal "encoded" "ByteString")
+  encodeKnownType
+
+  where
+    encodeKnownType :: String -> MessagePack.Object -> Server ByteString
+    encodeKnownType = \case
+      "CipherText"        -> go CipherText
+      "DhtPacket"         -> go DhtPacket
+      "DhtRequestPacket"  -> go DhtRequestPacket
+      "HostAddress"       -> go HostAddress
+      "Word64"            -> go Word64
+      "Key PublicKey"     -> go Key
+      "KeyPair"           -> go KeyPair
+      "NodeInfo"          -> go NodeInfo
+      "NodesRequest"      -> go NodesRequest
+      "NodesResponse"     -> go NodesResponse
+      "Packet Word64"     -> go Packet
+      "PacketKind"        -> go PacketKind
+      "PingPacket"        -> go PingPacket
+      "PlainText"         -> go PlainText
+      "PortNumber"        -> go PortNumber
+      "RpcPacket Word64"  -> go RpcPacket
+      "SocketAddress"     -> go SocketAddress
+      "TransportProtocol" -> go TransportProtocol
+      tycon               -> fail $ "unknown type: " ++ tycon
+
+    go f = fmap (knownTypeEncode . f) . fromObject
diff --git a/src/Network/Tox/C.hs b/src/Network/Tox/C.hs
deleted file mode 100644
--- a/src/Network/Tox/C.hs
+++ /dev/null
@@ -1,10 +0,0 @@
-module Network.Tox.C
-  ( module M
-  ) where
-
-import           Network.Tox.C.Callbacks as M
-import           Network.Tox.C.Constants as M
-import           Network.Tox.C.Options   as M
-import           Network.Tox.C.Tox       as M
-import           Network.Tox.C.Type      as M
-import           Network.Tox.C.Version   as M
diff --git a/src/Network/Tox/C/CEnum.hs b/src/Network/Tox/C/CEnum.hs
deleted file mode 100644
--- a/src/Network/Tox/C/CEnum.hs
+++ /dev/null
@@ -1,37 +0,0 @@
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE ScopedTypeVariables        #-}
-{-# LANGUAGE Trustworthy                #-}
-module Network.Tox.C.CEnum where
-
-import           Control.Applicative   ((<$>))
-import           Foreign.C.Types       (CInt)
-import           Foreign.Marshal.Alloc (alloca)
-import           Foreign.Ptr           (Ptr)
-import           Foreign.Storable      (Storable (..))
-
-
-newtype CEnum a = CEnum { unCEnum :: CInt }
-  deriving (Storable)
-
-instance (Enum a, Show a) => Show (CEnum a) where
-  show cen = show (toEnum $ fromIntegral $ unCEnum cen :: a)
-
-
-toCEnum :: Enum a => a -> CEnum a
-toCEnum = CEnum . fromIntegral . fromEnum
-
-
-fromCEnum :: Enum a => CEnum a -> a
-fromCEnum = toEnum . fromIntegral . unCEnum
-
-
-type CErr err = Ptr (CEnum err)
-
-callErrFun :: (Eq err, Enum err, Bounded err)
-           => (CErr err -> IO r) -> IO (Either err r)
-callErrFun f = alloca $ \errPtr -> do
-  res <- f errPtr
-  err <- toEnum . fromIntegral . unCEnum <$> peek errPtr
-  return $ if err /= minBound
-    then Left  err
-    else Right res
diff --git a/src/Network/Tox/C/Callbacks.hs b/src/Network/Tox/C/Callbacks.hs
deleted file mode 100644
--- a/src/Network/Tox/C/Callbacks.hs
+++ /dev/null
@@ -1,89 +0,0 @@
-module Network.Tox.C.Callbacks where
-
-import           Control.Exception  (bracket)
-import           Foreign.Ptr        (freeHaskellFunPtr, nullFunPtr)
-
-import qualified Network.Tox.C.Tox  as Tox
-import           Network.Tox.C.Type (Tox)
-
-
--- | Low level event handler. The functions in this class are directly
--- registered with the corresponding C callback. We use 'StablePtr' to pass
--- opaque Haskell values around in C.
-class CHandler a where
-  cSelfConnectionStatus     :: Tox.SelfConnectionStatusCb     a
-  cSelfConnectionStatus _ _ = return
-  cFriendName               :: Tox.FriendNameCb               a
-  cFriendName _ _ _ = return
-  cFriendStatusMessage      :: Tox.FriendStatusMessageCb      a
-  cFriendStatusMessage _ _ _ = return
-  cFriendStatus             :: Tox.FriendStatusCb             a
-  cFriendStatus _ _ _ = return
-  cFriendConnectionStatus   :: Tox.FriendConnectionStatusCb   a
-  cFriendConnectionStatus _ _ _ = return
-  cFriendTyping             :: Tox.FriendTypingCb             a
-  cFriendTyping _ _ _ = return
-  cFriendReadReceipt        :: Tox.FriendReadReceiptCb        a
-  cFriendReadReceipt _ _ _ = return
-  cFriendRequest            :: Tox.FriendRequestCb            a
-  cFriendRequest _ _ _ = return
-  cFriendMessage            :: Tox.FriendMessageCb            a
-  cFriendMessage _ _ _ _ = return
-  cFileRecvControl          :: Tox.FileRecvControlCb          a
-  cFileRecvControl _ _ _ _ = return
-  cFileChunkRequest         :: Tox.FileChunkRequestCb         a
-  cFileChunkRequest _ _ _ _ _ = return
-  cFileRecv                 :: Tox.FileRecvCb                 a
-  cFileRecv _ _ _ _ _ _ = return
-  cFileRecvChunk            :: Tox.FileRecvChunkCb            a
-  cFileRecvChunk _ _ _ _ _ = return
-  cConferenceInvite         :: Tox.ConferenceInviteCb         a
-  cConferenceInvite _ _ _ _ = return
-  cConferenceMessage        :: Tox.ConferenceMessageCb        a
-  cConferenceMessage _ _ _ _ _ = return
-  cConferenceTitle          :: Tox.ConferenceTitleCb          a
-  cConferenceTitle _ _ _ _ = return
-  cConferencePeerName :: Tox.ConferencePeerNameCb a
-  cConferencePeerName _ _ _ _ = return
-  cConferencePeerListChanged :: Tox.ConferencePeerListChangedCb a
-  cConferencePeerListChanged _ _ = return
-  cFriendLossyPacket        :: Tox.FriendLossyPacketCb        a
-  cFriendLossyPacket _ _ _ = return
-  cFriendLosslessPacket     :: Tox.FriendLosslessPacketCb     a
-  cFriendLosslessPacket _ _ _ = return
-
-
--- | Installs an event handler into the passed 'Tox' instance. After performing
--- the IO action, all event handlers are reset to null. This function does not
--- save the original event handlers.
-withCHandler :: CHandler a => Tox a -> IO r -> IO r
-withCHandler tox =
-  install Tox.tox_callback_self_connection_status       (Tox.selfConnectionStatusCb      cSelfConnectionStatus     ) .
-  install Tox.tox_callback_friend_name                  (Tox.friendNameCb                cFriendName               ) .
-  install Tox.tox_callback_friend_status_message        (Tox.friendStatusMessageCb       cFriendStatusMessage      ) .
-  install Tox.tox_callback_friend_status                (Tox.friendStatusCb              cFriendStatus             ) .
-  install Tox.tox_callback_friend_connection_status     (Tox.friendConnectionStatusCb    cFriendConnectionStatus   ) .
-  install Tox.tox_callback_friend_typing                (Tox.friendTypingCb              cFriendTyping             ) .
-  install Tox.tox_callback_friend_read_receipt          (Tox.friendReadReceiptCb         cFriendReadReceipt        ) .
-  install Tox.tox_callback_friend_request               (Tox.friendRequestCb             cFriendRequest            ) .
-  install Tox.tox_callback_friend_message               (Tox.friendMessageCb             cFriendMessage            ) .
-  install Tox.tox_callback_file_recv_control            (Tox.fileRecvControlCb           cFileRecvControl          ) .
-  install Tox.tox_callback_file_chunk_request           (Tox.fileChunkRequestCb          cFileChunkRequest         ) .
-  install Tox.tox_callback_file_recv                    (Tox.fileRecvCb                  cFileRecv                 ) .
-  install Tox.tox_callback_file_recv_chunk              (Tox.fileRecvChunkCb             cFileRecvChunk            ) .
-  install Tox.tox_callback_conference_invite            (Tox.conferenceInviteCb          cConferenceInvite         ) .
-  install Tox.tox_callback_conference_message           (Tox.conferenceMessageCb         cConferenceMessage        ) .
-  install Tox.tox_callback_conference_title             (Tox.conferenceTitleCb           cConferenceTitle          ) .
-  install Tox.tox_callback_conference_peer_name         (Tox.conferencePeerNameCb        cConferencePeerName       ) .
-  install Tox.tox_callback_conference_peer_list_changed (Tox.conferencePeerListChangedCb cConferencePeerListChanged ) .
-  install Tox.tox_callback_friend_lossy_packet          (Tox.friendLossyPacketCb         cFriendLossyPacket        ) .
-  install Tox.tox_callback_friend_lossless_packet       (Tox.friendLosslessPacketCb      cFriendLosslessPacket     )
-  where
-    install cInstall wrapper action =
-      bracket wrapper (uninstall cInstall) $ \cb -> do
-        () <- cInstall tox cb
-        action
-
-    uninstall cInstall cb = do
-      freeHaskellFunPtr cb
-      cInstall tox nullFunPtr
diff --git a/src/Network/Tox/C/Constants.hs b/src/Network/Tox/C/Constants.hs
deleted file mode 100644
--- a/src/Network/Tox/C/Constants.hs
+++ /dev/null
@@ -1,50 +0,0 @@
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-{-# LANGUAGE Trustworthy #-}
-module Network.Tox.C.Constants where
-
-import           Data.Word (Word32)
-
-
---------------------------------------------------------------------------------
---
--- :: Numeric constants
---
---------------------------------------------------------------------------------
-
--- | The size of a Tox Public Key in bytes.
-foreign import ccall tox_public_key_size :: Word32
-
--- | The size of a Tox Secret Key in bytes.
-foreign import ccall tox_secret_key_size :: Word32
-
--- | The size of a Tox address in bytes. Tox addresses are in the format
--- [Public Key ('tox_public_key_size' bytes)][nospam (4 bytes)][checksum (2 bytes)].
---
--- The checksum is computed over the Public Key and the nospam value. The first
--- byte is an XOR of all the even bytes (0, 2, 4, ...), the second byte is an
--- XOR of all the odd bytes (1, 3, 5, ...) of the Public Key and nospam.
-foreign import ccall tox_address_size :: Word32
-
--- | Maximum length of a nickname in bytes.
-foreign import ccall tox_max_name_length :: Word32
-
--- | Maximum length of a status message in bytes.
-foreign import ccall tox_max_status_message_length :: Word32
-
--- | Maximum length of a friend request message in bytes.
-foreign import ccall tox_max_friend_request_length :: Word32
-
--- | Maximum length of a single message after which it should be split.
-foreign import ccall tox_max_message_length :: Word32
-
--- | Maximum size of custom packets. TODO: should be LENGTH?
-foreign import ccall tox_max_custom_packet_size :: Word32
-
--- | The number of bytes in a hash generated by tox_hash.
-foreign import ccall tox_hash_length :: Word32
-
--- | The number of bytes in a file id.
-foreign import ccall tox_file_id_length :: Word32
-
--- | Maximum file name length for file transfers.
-foreign import ccall tox_max_filename_length :: Word32
diff --git a/src/Network/Tox/C/Options.hs b/src/Network/Tox/C/Options.hs
deleted file mode 100644
--- a/src/Network/Tox/C/Options.hs
+++ /dev/null
@@ -1,317 +0,0 @@
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE LambdaCase    #-}
-{-# LANGUAGE Safe          #-}
-module Network.Tox.C.Options where
-
-import           Control.Applicative ((<$>))
-import           Control.Exception   (bracket)
-import           Data.ByteString     (ByteString)
-import qualified Data.ByteString     as BS
-import           Data.Default.Class  (Default (..))
-import           Data.Word           (Word16)
-import           Foreign.C.String    (CString, peekCString, withCString)
-import           Foreign.C.Types     (CInt (..), CSize (..))
-import           Foreign.Ptr         (Ptr, nullPtr)
-import           GHC.Generics        (Generic)
-
-import           Network.Tox.C.CEnum
-
---------------------------------------------------------------------------------
---
--- :: Startup options
---
---------------------------------------------------------------------------------
-
-
--- | Type of proxy used to connect to TCP relays.
-data ProxyType
-  = ProxyTypeNone
-    -- Don't use a proxy.
-  | ProxyTypeHttp
-    -- HTTP proxy using CONNECT.
-  | ProxyTypeSocks5
-    -- SOCKS proxy for simple socket pipes.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
-
-
--- Type of savedata to create the Tox instance from.
-data SavedataType
-  = SavedataTypeNone
-    -- No savedata.
-  | SavedataTypeToxSave
-    -- Savedata is one that was obtained from tox_get_savedata
-  | SavedataTypeSecretKey
-    -- Savedata is a secret key of length 'tox_secret_key_size'
-  deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
-
-
--- This struct contains all the startup options for Tox. You can either allocate
--- this object yourself, and pass it to tox_options_default, or call
--- tox_options_new to get a new default options object.
-data Options = Options
-  { ipv6Enabled  :: Bool
-    -- The type of socket to create.
-    --
-    -- If this is set to false, an IPv4 socket is created, which subsequently
-    -- only allows IPv4 communication.
-    -- If it is set to true, an IPv6 socket is created, allowing both IPv4 and
-    -- IPv6 communication.
-
-  , udpEnabled   :: Bool
-    -- Enable the use of UDP communication when available.
-    --
-    -- Setting this to false will force Tox to use TCP only. Communications will
-    -- need to be relayed through a TCP relay node, potentially slowing them
-    -- down. Disabling UDP support is necessary when using anonymous proxies or
-    -- Tor.
-
-  , proxyType    :: ProxyType
-    -- Pass communications through a proxy.
-
-  , proxyHost    :: String
-    -- The IP address or DNS name of the proxy to be used.
-    --
-    -- If used, this must be non-'nullPtr' and be a valid DNS name. The name
-    -- must not exceed 255 ('tox_max_filename_length') characters, and be in a
-    -- NUL-terminated C string format (255 chars + 1 NUL byte).
-    --
-    -- This member is ignored (it can be 'nullPtr') if proxy_type is
-    -- ProxyTypeNone.
-
-  , proxyPort    :: Word16
-    -- The port to use to connect to the proxy server.
-    --
-    -- Ports must be in the range (1, 65535). The value is ignored if proxy_type
-    -- is ProxyTypeNone.
-
-  , startPort    :: Word16
-    -- The start port of the inclusive port range to attempt to use.
-    --
-    -- If both 'startPort' and 'endPort' are 0, the default port range will be
-    -- used: [33445, 33545].
-    --
-    -- If either 'startPort' or 'endPort' is 0 while the other is non-zero, the
-    -- non-zero port will be the only port in the range.
-    --
-    -- Having 'startPort' > 'endport' will yield the same behavior as if
-    -- 'startPort' and 'endPort' were swapped.
-
-  , endPort      :: Word16
-    -- The end port of the inclusive port range to attempt to use.
-
-  , tcpPort      :: Word16
-    -- The port to use for the TCP server (relay). If 0, the TCP server is
-    -- disabled.
-    --
-    -- Enabling it is not required for Tox to function properly.
-    --
-    -- When enabled, your Tox instance can act as a TCP relay for other Tox
-    -- instance. This leads to increased traffic, thus when writing a client it
-    -- is recommended to enable TCP server only if the user has an option to
-    -- disable it.
-
-  , savedataType :: SavedataType
-    -- The type of savedata to load from.
-
-  , savedataData :: ByteString
-    -- The savedata bytes.
-  }
-  deriving (Eq, Read, Show, Generic)
-
-
-instance Default Options where
-  def = Options
-    { ipv6Enabled  = True
-    , udpEnabled   = True
-    , proxyType    = ProxyTypeNone
-    , proxyHost    = ""
-    , proxyPort    = 0
-    , startPort    = 0
-    , endPort      = 0
-    , tcpPort      = 0
-    , savedataType = SavedataTypeNone
-    , savedataData = BS.empty
-    }
-
-
-data OptionsStruct
-type OptionsPtr = Ptr OptionsStruct
-
-
-foreign import ccall tox_options_get_ipv6_enabled     :: OptionsPtr -> IO Bool
-foreign import ccall tox_options_get_udp_enabled      :: OptionsPtr -> IO Bool
-foreign import ccall tox_options_get_proxy_type       :: OptionsPtr -> IO (CEnum ProxyType)
-foreign import ccall tox_options_get_proxy_host       :: OptionsPtr -> IO CString
-foreign import ccall tox_options_get_proxy_port       :: OptionsPtr -> IO Word16
-foreign import ccall tox_options_get_start_port       :: OptionsPtr -> IO Word16
-foreign import ccall tox_options_get_end_port         :: OptionsPtr -> IO Word16
-foreign import ccall tox_options_get_tcp_port         :: OptionsPtr -> IO Word16
-foreign import ccall tox_options_get_savedata_type    :: OptionsPtr -> IO (CEnum SavedataType)
-foreign import ccall tox_options_get_savedata_data    :: OptionsPtr -> IO CString
-foreign import ccall tox_options_get_savedata_length  :: OptionsPtr -> IO CSize
-
-foreign import ccall tox_options_set_ipv6_enabled     :: OptionsPtr -> Bool -> IO ()
-foreign import ccall tox_options_set_udp_enabled      :: OptionsPtr -> Bool -> IO ()
-foreign import ccall tox_options_set_proxy_type       :: OptionsPtr -> CEnum ProxyType -> IO ()
-foreign import ccall tox_options_set_proxy_host       :: OptionsPtr -> CString -> IO ()
-foreign import ccall tox_options_set_proxy_port       :: OptionsPtr -> Word16 -> IO ()
-foreign import ccall tox_options_set_start_port       :: OptionsPtr -> Word16 -> IO ()
-foreign import ccall tox_options_set_end_port         :: OptionsPtr -> Word16 -> IO ()
-foreign import ccall tox_options_set_tcp_port         :: OptionsPtr -> Word16 -> IO ()
-foreign import ccall tox_options_set_savedata_type    :: OptionsPtr -> CEnum SavedataType -> IO ()
-foreign import ccall tox_options_set_savedata_data    :: OptionsPtr -> CString -> CSize -> IO ()
-foreign import ccall tox_options_set_savedata_length  :: OptionsPtr -> CSize -> IO ()
-
-
-data ErrOptionsNew
-  = ErrOptionsNewOk
-    -- The function returned successfully.
-
-  | ErrOptionsNewMalloc
-    -- The function was unable to allocate enough memory to store the internal
-    -- structures for the Tox options object.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Allocates a new Tox_Options object and initialises it with the default
--- options. This function can be used to preserve long term ABI compatibility by
--- giving the responsibility of allocation and deallocation to the Tox library.
---
--- Objects returned from this function must be freed using the tox_options_free
--- function.
---
--- @return A new 'OptionsPtr' with default options or 'nullPtr' on failure.
-foreign import ccall tox_options_new :: CErr ErrOptionsNew -> IO OptionsPtr
-
-toxOptionsNew :: IO (Either ErrOptionsNew OptionsPtr)
-toxOptionsNew = callErrFun tox_options_new
-
-
--- | Releases all resources associated with an options objects.
---
--- Passing a pointer that was not returned by tox_options_new results in
--- undefined behaviour.
-foreign import ccall tox_options_free :: OptionsPtr -> IO ()
-
-
-withToxOptions :: (OptionsPtr -> IO r) -> IO (Either ErrOptionsNew r)
-withToxOptions f =
-  bracket toxOptionsNew (either (const $ return ()) tox_options_free) $ \case
-    Left err -> return $ Left err
-    Right ok -> Right <$> f ok
-
-
--- | Read 'Options' from an 'OptionsPtr'.
---
--- If the passed pointer is 'nullPtr', the behaviour is undefined.
-peekToxOptions :: OptionsPtr -> IO Options
-peekToxOptions ptr = do
-  cIpv6Enabled    <- tox_options_get_ipv6_enabled    ptr
-  cUdpEnabled     <- tox_options_get_udp_enabled     ptr
-  cProxyType      <- tox_options_get_proxy_type      ptr
-  cProxyHost      <- tox_options_get_proxy_host      ptr >>= peekNullableString
-  cProxyPort      <- tox_options_get_proxy_port      ptr
-  cStartPort      <- tox_options_get_start_port      ptr
-  cEndPort        <- tox_options_get_end_port        ptr
-  cTcpPort        <- tox_options_get_tcp_port        ptr
-  cSavedataType   <- tox_options_get_savedata_type   ptr
-  cSavedataData   <- tox_options_get_savedata_data   ptr
-  cSavedataLength <- tox_options_get_savedata_length ptr
-  cSavedata       <- BS.packCStringLen
-                           ( cSavedataData
-                           , fromIntegral cSavedataLength)
-  return Options
-    { ipv6Enabled    = cIpv6Enabled
-    , udpEnabled     = cUdpEnabled
-    , proxyType      = fromCEnum cProxyType
-    , proxyHost      = cProxyHost
-    , proxyPort      = cProxyPort
-    , startPort      = cStartPort
-    , endPort        = cEndPort
-    , tcpPort        = cTcpPort
-    , savedataType   = fromCEnum cSavedataType
-    , savedataData   = cSavedata
-    }
-
-  where
-    -- 'peekCString' doesn't handle NULL strings as empty, unlike
-    -- 'packCStringLen', which ignores the pointer to zero-length 'CString's.
-    peekNullableString p =
-      if p == nullPtr
-        then return ""
-        else peekCString p
-
-
--- | Save the options from the passed 'OptionsPtr', perform an IO action, and
--- restore the original values.
---
--- If the passed pointer is 'nullPtr', the behaviour is undefined.
-saveToxOptions :: OptionsPtr -> IO r -> IO r
-saveToxOptions ptr =
-  bracket saveOptions restoreOptions . const
-  where
-    saveOptions = do
-      v0 <- tox_options_get_ipv6_enabled    ptr
-      v1 <- tox_options_get_udp_enabled     ptr
-      v2 <- tox_options_get_proxy_type      ptr
-      v3 <- tox_options_get_proxy_host      ptr
-      v4 <- tox_options_get_proxy_port      ptr
-      v5 <- tox_options_get_start_port      ptr
-      v6 <- tox_options_get_end_port        ptr
-      v7 <- tox_options_get_tcp_port        ptr
-      v8 <- tox_options_get_savedata_type   ptr
-      sd <- tox_options_get_savedata_data   ptr
-      sl <- tox_options_get_savedata_length ptr
-      return (v0, v1, v2, v3, v4, v5, v6, v7, v8, sd, sl)
-
-    restoreOptions (v0, v1, v2, v3, v4, v5, v6, v7, v8, sd, sl) = do
-      tox_options_set_ipv6_enabled    ptr v0
-      tox_options_set_udp_enabled     ptr v1
-      tox_options_set_proxy_type      ptr v2
-      tox_options_set_proxy_host      ptr v3
-      tox_options_set_proxy_port      ptr v4
-      tox_options_set_start_port      ptr v5
-      tox_options_set_end_port        ptr v6
-      tox_options_set_tcp_port        ptr v7
-      tox_options_set_savedata_type   ptr v8
-      tox_options_set_savedata_data   ptr sd sl
-      tox_options_set_savedata_length ptr sl
-
-
--- | Fill in the 'Options' values into the 'OptionsPtr' and perform the IO
--- action afterwards.
---
--- This function restores the original values from the 'OptionsPtr' after
--- performing the action.
---
--- If the passed pointer is 'nullPtr', the behaviour is undefined.
-pokeToxOptions :: Options -> OptionsPtr -> IO r -> IO r
-pokeToxOptions options ptr action =
-  saveToxOptions ptr $
-    withCString (proxyHost options) $ \host ->
-      BS.useAsCStringLen (savedataData options) $ \(saveData, saveLenInt) -> do
-        let saveLen = fromIntegral saveLenInt
-        tox_options_set_ipv6_enabled    ptr $ ipv6Enabled options
-        tox_options_set_udp_enabled     ptr $ udpEnabled options
-        tox_options_set_proxy_type      ptr $ toCEnum $ proxyType options
-        tox_options_set_proxy_host      ptr host
-        tox_options_set_proxy_port      ptr $ proxyPort options
-        tox_options_set_start_port      ptr $ startPort options
-        tox_options_set_end_port        ptr $ endPort options
-        tox_options_set_tcp_port        ptr $ tcpPort options
-        tox_options_set_savedata_type   ptr $ toCEnum $ savedataType options
-        tox_options_set_savedata_data   ptr saveData saveLen
-        tox_options_set_savedata_length ptr saveLen
-        action
-
-
--- | Allocate a new C options pointer, fills in the values from 'Options',
--- calls the processor function, and deallocates the options pointer.
---
--- The 'OptionsPtr' passed to the processor function is never 'nullPtr'. If
--- allocation fails, the IO action evaluates to 'Left' with an appropriate
--- error code.
-withOptions :: Options -> (OptionsPtr -> IO r) -> IO (Either ErrOptionsNew r)
-withOptions options f =
-  withToxOptions $ \ptr ->
-    pokeToxOptions options ptr (f ptr)
diff --git a/src/Network/Tox/C/Tox.hs b/src/Network/Tox/C/Tox.hs
deleted file mode 100644
--- a/src/Network/Tox/C/Tox.hs
+++ /dev/null
@@ -1,2405 +0,0 @@
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE Safe       #-}
--- | Public core API for Tox clients.
---
--- Every function that can fail takes a function-specific error code pointer
--- that can be used to diagnose problems with the Tox state or the function
--- arguments. The error code pointer can be 'nullPtr', which does not influence
--- the function's behaviour, but can be done if the reason for failure is
--- irrelevant to the client.
---
--- The exception to this rule are simple allocation functions whose only failure
--- mode is allocation failure. They return 'nullPtr' in that case, and do not
--- set an error code.
---
--- Every error code type has an OK value to which functions will set their error
--- code value on success. Clients can keep their error code uninitialised before
--- passing it to a function. The library guarantees that after returning, the
--- value pointed to by the error code pointer has been initialised.
---
--- Functions with pointer parameters often have a 'nullPtr' error code, meaning
--- they could not perform any operation, because one of the required parameters
--- was 'nullPtr'. Some functions operate correctly or are defined as effectless
--- on 'nullPtr'.
---
--- Some functions additionally return a value outside their return type domain,
--- or a bool containing true on success and false on failure.
---
--- All functions that take a Tox instance pointer will cause undefined behaviour
--- when passed a 'nullPtr' Tox pointer.
---
--- All integer values are expected in host byte order.
---
--- Functions with parameters with enum types cause unspecified behaviour if the
--- enumeration value is outside the valid range of the type. If possible, the
--- function will try to use a sane default, but there will be no error code, and
--- one possible action for the function to take is to have no effect.
---
--- \subsection events Events and callbacks
---
--- Events are handled by callbacks. One callback can be registered per event.
--- All events have a callback function type named `tox_{event}_cb` and a
--- function to register it named `tox_callback_{event}`. Passing a 'nullPtr'
--- callback will result in no callback being registered for that event. Only one
--- callback per event can be registered, so if a client needs multiple event
--- listeners, it needs to implement the dispatch functionality itself.
---
--- \subsection threading Threading implications
---
--- It is possible to run multiple concurrent threads with a Tox instance for
--- each thread. It is also possible to run all Tox instances in the same thread.
--- A common way to run Tox (multiple or single instance) is to have one thread
--- running a simple tox_iterate loop, sleeping for tox_iteration_interval
--- milliseconds on each iteration.
---
--- If you want to access a single Tox instance from multiple threads, access to
--- the instance must be synchronised. While multiple threads can concurrently
--- access multiple different Tox instances, no more than one API function can
--- operate on a single instance at any given time.
---
--- Functions that write to variable length byte arrays will always have a size
--- function associated with them. The result of this size function is only valid
--- until another mutating function (one that takes a pointer to non-const Tox)
--- is called. Thus, clients must ensure that no other thread calls a mutating
--- function between the call to the size function and the call to the retrieval
--- function.
---
--- E.g. to get the current nickname, one would write
---
--- \code
--- CSize length = tox_self_get_name_size(tox);
--- CString name = malloc(length);
--- if (!name) abort();
--- tox_self_get_name(tox, name);
--- \endcode
---
--- If any other thread calls tox_self_set_name while this thread is allocating
--- memory, the length may have become invalid, and the call to tox_self_get_name
--- may cause undefined behaviour.
---
-module Network.Tox.C.Tox where
-
-import           Control.Applicative     ((<$>))
-import           Control.Concurrent.MVar (MVar, modifyMVar_)
-import           Control.Exception       (bracket)
-import           Control.Monad           ((>=>))
-import qualified Data.ByteString         as BS
-import           Data.Word               (Word16, Word32, Word64)
-import           Foreign.C.String        (CString, peekCStringLen, withCString,
-                                          withCStringLen)
-import           Foreign.C.Types         (CChar (..), CInt (..), CSize (..),
-                                          CTime (..))
-import           Foreign.Marshal.Alloc   (alloca)
-import           Foreign.Marshal.Array   (allocaArray, peekArray)
-import           Foreign.Ptr             (FunPtr, Ptr, nullPtr)
-import           Foreign.StablePtr       (deRefStablePtr, freeStablePtr,
-                                          newStablePtr)
-import           Foreign.Storable        (peek)
-import           System.Posix.Types      (EpochTime)
-
-import           Network.Tox.C.CEnum
-import           Network.Tox.C.Constants
-import           Network.Tox.C.Options
-import           Network.Tox.C.Type
-
-
---------------------------------------------------------------------------------
---
--- :: Global types
---
---------------------------------------------------------------------------------
-
--- Should we introduce such types?
--- newtype FriendNum = FriendNum { friendNum :: Word32 } deriving (Eq, Ord, Read, Show)
--- newtype ConferenceNum = ConferenceNum { conferenceNum :: Word32 } deriving (Eq, Ord, Read, Show)
--- newtype PeerNum = PeerNum { peerNum :: Word32 } deriving (Eq, Ord, Read, Show)
--- newtype FileNum = FileNum { fileNum :: Word32 } deriving (Eq, Ord, Read, Show)
-
--- | Represents the possible statuses a client can have.
-data UserStatus
-  = UserStatusNone
-    -- ^ User is online and available.
-  | UserStatusAway
-    -- ^ User is away. Clients can set this e.g. after a user defined inactivity
-    -- time.
-  | UserStatusBusy
-    -- ^ User is busy. Signals to other clients that this client does not
-    -- currently wish to communicate.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Represents message types for tox_friend_send_message and group chat
--- messages.
-data MessageType
-  = MessageTypeNormal
-    -- ^ Normal text message. Similar to PRIVMSG on IRC.
-  | MessageTypeAction
-    -- ^ A message describing an user action. This is similar to /me (CTCP
-    -- ACTION) on IRC.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
---------------------------------------------------------------------------------
---
--- :: Creation and destruction
---
---------------------------------------------------------------------------------
-
-
-data ErrNew
-  = ErrNewOk
-    -- The function returned successfully.
-
-  | ErrNewNull
-    -- One of the arguments to the function was 'nullPtr' when it was not
-    -- expected.
-
-  | ErrNewMalloc
-    -- The function was unable to allocate enough memory to store the internal
-    -- structures for the Tox object.
-
-  | ErrNewPortAlloc
-    -- The function was unable to bind to a port. This may mean that all ports
-    -- have already been bound, e.g. by other Tox instances, or it may mean a
-    -- permission error. You may be able to gather more information from errno.
-
-  | ErrNewProxyBadType
-    -- proxy_type was invalid.
-
-  | ErrNewProxyBadHost
-    -- proxy_type was valid but the proxy_host passed had an invalid format or
-    -- was 'nullPtr'.
-
-  | ErrNewProxyBadPort
-    -- proxy_type was valid, but the proxy_port was invalid.
-
-  | ErrNewProxyNotFound
-    -- The proxy address passed could not be resolved.
-
-  | ErrNewLoadEncrypted
-    -- The byte array to be loaded contained an encrypted save.
-
-  | ErrNewLoadBadFormat
-    -- The data format was invalid. This can happen when loading data that was
-    -- saved by an older version of Tox, or when the data has been corrupted.
-    -- When loading from badly formatted data, some data may have been loaded,
-    -- and the rest is discarded. Passing an invalid length parameter also
-    -- causes this error.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- @brief Creates and initialises a new Tox instance with the options passed.
---
--- This function will bring the instance into a valid state. Running the event
--- loop with a new instance will operate correctly.
---
--- If loading failed or succeeded only partially, the new or partially loaded
--- instance is returned and an error code is set.
---
--- @param options An options object as described above. If this parameter is
---   'nullPtr', the default options are used.
---
--- @see tox_iterate for the event loop.
---
--- @return A new Tox instance pointer on success or 'nullPtr' on failure.
-foreign import ccall tox_new :: OptionsPtr -> CErr ErrNew -> IO (Tox a)
-
-toxNew :: OptionsPtr -> IO (Either ErrNew (Tox a))
-toxNew = callErrFun . tox_new
-
--- | Releases all resources associated with the Tox instance and disconnects
--- from the network.
---
--- After calling this function, the Tox pointer becomes invalid. No other
--- functions can be called, and the pointer value can no longer be read.
-foreign import ccall tox_kill :: Tox a -> IO ()
-
-toxKill :: Tox a -> IO ()
-toxKill = tox_kill
-
-withTox :: OptionsPtr -> (Tox a -> IO r) -> IO (Either ErrNew r)
-withTox options f =
-  bracket (toxNew options) (either (const $ return ()) toxKill) $ \case
-    Left err -> return $ Left err
-    Right ok -> Right <$> f ok
-
-
-withDefaultTox :: (Tox a -> IO r) -> IO (Either ErrNew r)
-withDefaultTox = withTox nullPtr
-
-
--- | Calculates the number of bytes required to store the tox instance with
--- tox_get_savedata. This function cannot fail. The result is always greater
--- than 0.
---
--- @see threading for concurrency implications.
-foreign import ccall tox_get_savedata_size :: Tox a -> IO CSize
-
--- | Store all information associated with the tox instance to a byte array.
---
--- @param data A memory region large enough to store the tox instance data.
---   Call tox_get_savedata_size to find the number of bytes required. If this
---   parameter is 'nullPtr', this function has no effect.
-foreign import ccall tox_get_savedata :: Tox a -> CString -> IO ()
-
-toxGetSavedata :: Tox a -> IO BS.ByteString
-toxGetSavedata tox = do
-  savedataLen <- tox_get_savedata_size tox
-  allocaArray (fromIntegral savedataLen) $ \savedataPtr -> do
-    tox_get_savedata tox savedataPtr
-    BS.packCStringLen (savedataPtr, fromIntegral savedataLen)
-
-
---------------------------------------------------------------------------------
---
--- :: Connection lifecycle and event loop
---
---------------------------------------------------------------------------------
-
-
-data ErrBootstrap
-  = ErrBootstrapOk
-    -- The function returned successfully.
-
-  | ErrBootstrapNull
-    -- One of the arguments to the function was 'nullPtr' when it was not
-    -- expected.
-
-  | ErrBootstrapBadHost
-    -- The address could not be resolved to an IP address, or the IP address
-    -- passed was invalid.
-
-  | ErrBootstrapBadPort
-    -- The port passed was invalid. The valid port range is (1, 65535).
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Sends a "get nodes" request to the given bootstrap node with IP, port, and
--- public key to setup connections.
---
--- This function will attempt to connect to the node using UDP. You must use
--- this function even if Options.udp_enabled was set to false.
---
--- @param address The hostname or IP address (IPv4 or IPv6) of the node.
--- @param port The port on the host on which the bootstrap Tox instance is
---   listening.
--- @param public_key The long term public key of the bootstrap node
---   ('tox_public_key_size' bytes).
--- @return true on success.
-foreign import ccall tox_bootstrap :: Tox a -> CString -> Word16 -> CString -> CErr ErrBootstrap -> IO ()
-
-callBootstrapFun
-  :: (Tox a -> CString -> Word16 -> CString -> CErr ErrBootstrap -> IO ())
-  -> Tox a -> String -> Word16 -> BS.ByteString -> IO (Either ErrBootstrap ())
-callBootstrapFun f tox address port pubKey =
-  withCString address $ \address' ->
-    BS.useAsCString pubKey $ \pubKey' ->
-      callErrFun $ f tox address' (fromIntegral port) pubKey'
-
-toxBootstrap :: Tox a -> String -> Word16 -> BS.ByteString -> IO (Either ErrBootstrap ())
-toxBootstrap = callBootstrapFun tox_bootstrap
-
-
--- | Adds additional host:port pair as TCP relay.
---
--- This function can be used to initiate TCP connections to different ports on
--- the same bootstrap node, or to add TCP relays without using them as
--- bootstrap nodes.
---
--- @param address The hostname or IP address (IPv4 or IPv6) of the TCP relay.
--- @param port The port on the host on which the TCP relay is listening.
--- @param public_key The long term public key of the TCP relay
---   ('tox_public_key_size' bytes).
--- @return true on success.
-foreign import ccall tox_add_tcp_relay :: Tox a -> CString -> Word16 -> CString -> CErr ErrBootstrap -> IO ()
-
-toxAddTcpRelay :: Tox a -> String -> Word16 -> BS.ByteString -> IO (Either ErrBootstrap ())
-toxAddTcpRelay = callBootstrapFun tox_add_tcp_relay
-
-
--- | Protocols that can be used to connect to the network or friends.
-data Connection
-  = ConnectionNone
-    -- There is no connection. This instance, or the friend the state change is
-    -- about, is now offline.
-
-  | ConnectionTcp
-    -- A TCP connection has been established. For the own instance, this means
-    -- it is connected through a TCP relay, only. For a friend, this means that
-    -- the connection to that particular friend goes through a TCP relay.
-
-  | ConnectionUdp
-    -- A UDP connection has been established. For the own instance, this means
-    -- it is able to send UDP packets to DHT nodes, but may still be connected
-    -- to a TCP relay. For a friend, this means that the connection to that
-    -- particular friend was built using direct UDP packets.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | @param connection_status Whether we are connected to the DHT.
-type SelfConnectionStatusCb a = Tox a -> Connection -> a -> IO a
-type CSelfConnectionStatusCb a = Tox a -> CEnum Connection -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapSelfConnectionStatusCb :: CSelfConnectionStatusCb a -> IO (FunPtr (CSelfConnectionStatusCb a))
-
-callSelfConnectionStatusCb :: SelfConnectionStatusCb a -> CSelfConnectionStatusCb a
-callSelfConnectionStatusCb f tox conn = deRefStablePtr >=> (`modifyMVar_` f tox (fromCEnum conn))
-
-selfConnectionStatusCb :: SelfConnectionStatusCb a -> IO (FunPtr (CSelfConnectionStatusCb a))
-selfConnectionStatusCb = wrapSelfConnectionStatusCb . callSelfConnectionStatusCb
-
-
--- | Set the callback for the `self_connection_status` event. Pass 'nullPtr' to
--- unset.
---
--- This event is triggered whenever there is a change in the DHT connection
--- state. When disconnected, a client may choose to call tox_bootstrap again, to
--- reconnect to the DHT. Note that this state may frequently change for short
--- amounts of time. Clients should therefore not immediately bootstrap on
--- receiving a disconnect.
---
--- TODO: how long should a client wait before bootstrapping again?
-foreign import ccall tox_callback_self_connection_status :: Tox a -> FunPtr (CSelfConnectionStatusCb a) -> IO ()
-
--- | Return the time in milliseconds before tox_iterate() should be called again
--- for optimal performance.
-foreign import ccall tox_iteration_interval :: Tox a -> IO Word32
-toxIterationInterval :: Tox a -> IO Word32
-toxIterationInterval = tox_iteration_interval
-
--- | The main loop that needs to be run in intervals of tox_iteration_interval()
--- milliseconds.
-foreign import ccall tox_iterate :: Tox a -> UserData a -> IO ()
-toxIterate :: Tox a -> MVar a -> IO ()
-toxIterate tox ud = bracket (newStablePtr ud) freeStablePtr (tox_iterate tox)
-
-
---------------------------------------------------------------------------------
---
--- :: Internal client information (Tox address/id)
---
---------------------------------------------------------------------------------
-
-
--- | Writes the Tox friend address of the client to a byte array. The address is
--- not in human-readable format. If a client wants to display the address,
--- formatting is required.
---
--- @param address A memory region of at least 'tox_address_size' bytes. If this
---   parameter is 'nullPtr', this function has no effect.
--- @see 'tox_address_size' for the address format.
-foreign import ccall tox_self_get_address :: Tox a -> CString -> IO ()
-
-toxSelfGetAddress :: Tox a -> IO BS.ByteString
-toxSelfGetAddress tox =
-  let addrLen = fromIntegral tox_address_size in
-  allocaArray addrLen $ \addrPtr -> do
-    tox_self_get_address tox addrPtr
-    BS.packCStringLen (addrPtr, addrLen)
-
--- | Set the 4-byte nospam part of the address.
---
--- @param nospam Any 32 bit unsigned integer.
-foreign import ccall tox_self_set_nospam :: Tox a -> Word32 -> IO ()
-toxSelfSetNospam :: Tox a -> Word32 -> IO ()
-toxSelfSetNospam = tox_self_set_nospam
-
--- | Get the 4-byte nospam part of the address.
-foreign import ccall tox_self_get_nospam :: Tox a -> IO Word32
-toxSelfGetNospam :: Tox a -> IO Word32
-toxSelfGetNospam = tox_self_get_nospam
-
--- | Copy the Tox Public Key (long term) from the Tox object.
---
--- @param public_key A memory region of at least 'tox_public_key_size' bytes. If
---   this parameter is 'nullPtr', this function has no effect.
-foreign import ccall tox_self_get_public_key :: Tox a -> CString -> IO ()
-
-toxSelfGetPublicKey :: Tox a -> IO BS.ByteString
-toxSelfGetPublicKey tox =
-  let pkLen = fromIntegral tox_public_key_size in
-  allocaArray pkLen $ \pkPtr -> do
-    tox_self_get_public_key tox pkPtr
-    BS.packCStringLen (pkPtr, pkLen)
-
--- | Copy the Tox Secret Key from the Tox object.
---
--- @param secret_key A memory region of at least 'tox_secret_key_size' bytes. If
---   this parameter is 'nullPtr', this function has no effect.
-foreign import ccall tox_self_get_secret_key :: Tox a -> CString -> IO ()
-
-toxSelfGetSecretKey :: Tox a -> IO BS.ByteString
-toxSelfGetSecretKey tox =
-  let skLen = fromIntegral tox_secret_key_size in
-  allocaArray skLen $ \skPtr -> do
-    tox_self_get_secret_key tox skPtr
-    BS.packCStringLen (skPtr, skLen)
-
-
---------------------------------------------------------------------------------
---
--- :: User-visible client information (nickname/status)
---
---------------------------------------------------------------------------------
-
-
--- | Common error codes for all functions that set a piece of user-visible
--- client information.
-data ErrSetInfo
-  = ErrSetInfoOk
-    -- The function returned successfully.
-
-  | ErrSetInfoNull
-    -- One of the arguments to the function was 'nullPtr' when it was not
-    -- expected.
-
-  | ErrSetInfoTooLong
-    -- Information length exceeded maximum permissible size.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Set the nickname for the Tox client.
---
--- Nickname length cannot exceed 'tox_max_name_length'. If length is 0, the name
--- parameter is ignored (it can be 'nullPtr'), and the nickname is set back to
--- empty.
---
--- @param name A byte array containing the new nickname.
--- @param length The size of the name byte array.
---
--- @return true on success.
-foreign import ccall tox_self_set_name :: Tox a -> CString -> CSize -> CErr ErrSetInfo -> IO ()
-callSelfSetNameFun :: (Tox a -> CString -> CSize -> CErr ErrSetInfo -> IO ()) ->
-                      Tox a -> String -> IO (Either ErrSetInfo ())
-callSelfSetNameFun f tox name =
-  withCStringLen name $ \(nameStr, nameLen) ->
-    callErrFun $ f tox nameStr (fromIntegral nameLen)
-
-toxSelfSetName :: Tox a -> String -> IO (Either ErrSetInfo ())
-toxSelfSetName = callSelfSetNameFun tox_self_set_name
-
-
--- | Return the length of the current nickname as passed to tox_self_set_name.
---
--- If no nickname was set before calling this function, the name is empty,
--- and this function returns 0.
---
--- @see threading for concurrency implications.
-foreign import ccall tox_self_get_name_size :: Tox a -> IO CSize
-
--- | Write the nickname set by tox_self_set_name to a byte array.
---
--- If no nickname was set before calling this function, the name is empty,
--- and this function has no effect.
---
--- Call tox_self_get_name_size to find out how much memory to allocate for
--- the result.
---
--- @param name A valid memory location large enough to hold the nickname.
---   If this parameter is NULL, the function has no effect.
-foreign import ccall tox_self_get_name :: Tox a -> CString -> IO ()
-
-toxSelfGetName :: Tox a -> IO String
-toxSelfGetName tox = do
-  nameLen <- tox_self_get_name_size tox
-  allocaArray (fromIntegral nameLen) $ \namePtr -> do
-    tox_self_get_name tox namePtr
-    peekCStringLen (namePtr, fromIntegral nameLen)
-
-
--- | Set the client's status message.
---
--- Status message length cannot exceed 'tox_max_status_message_length'. If
--- length is 0, the status parameter is ignored (it can be 'nullPtr'), and the
--- user status is set back to empty.
-foreign import ccall tox_self_set_status_message :: Tox a -> CString -> CSize -> CErr ErrSetInfo -> IO ()
-callSelfSetStatusMessageFun :: (Tox a -> CString -> CSize -> CErr ErrSetInfo -> IO ()) ->
-                               Tox a -> String -> IO (Either ErrSetInfo ())
-callSelfSetStatusMessageFun f tox statusMsg =
-  withCStringLen statusMsg $ \(statusMsgStr, statusMsgLen) ->
-    callErrFun $ f tox statusMsgStr (fromIntegral statusMsgLen)
-
-toxSelfSetStatusMessage :: Tox a -> String -> IO (Either ErrSetInfo ())
-toxSelfSetStatusMessage = callSelfSetStatusMessageFun tox_self_set_status_message
-
-
--- | Return the length of the current status message as passed to tox_self_set_status_message.
---
--- If no status message was set before calling this function, the status
--- is empty, and this function returns 0.
---
--- @see threading for concurrency implications.
-foreign import ccall tox_self_get_status_message_size :: Tox a -> IO CSize
-
-
--- | Write the status message set by tox_self_set_status_message to a byte array.
---
--- If no status message was set before calling this function, the status is
--- empty, and this function has no effect.
---
--- Call tox_self_get_status_message_size to find out how much memory to allocate for
--- the result.
---
--- @param status_message A valid memory location large enough to hold the
---   status message. If this parameter is NULL, the function has no effect.
-foreign import ccall tox_self_get_status_message :: Tox a -> CString -> IO ()
-
-toxSelfGetStatusMessage :: Tox a -> IO String
-toxSelfGetStatusMessage tox = do
-  statusMessageLen <- tox_self_get_status_message_size tox
-  allocaArray (fromIntegral statusMessageLen) $ \statusMessagePtr -> do
-    tox_self_get_status_message tox statusMessagePtr
-    peekCStringLen (statusMessagePtr, fromIntegral statusMessageLen)
-
-
--- | Set the client's user status.
---
--- @param user_status One of the user statuses listed in the enumeration above.
-foreign import ccall tox_self_set_status :: Tox a -> CEnum UserStatus -> IO ()
-toxSelfSetStatus :: Tox a -> UserStatus -> IO ()
-toxSelfSetStatus tox userStatus = tox_self_set_status tox $ toCEnum userStatus
-
-
---------------------------------------------------------------------------------
---
--- :: Friend list management
---
---------------------------------------------------------------------------------
-
-
-data ErrFriendAdd
-  = ErrFriendAddOk
-    -- The function returned successfully.
-
-  | ErrFriendAddNull
-    -- One of the arguments to the function was 'nullPtr' when it was not
-    -- expected.
-
-  | ErrFriendAddTooLong
-    -- The length of the friend request message exceeded
-    -- 'tox_max_friend_request_length'.
-
-  | ErrFriendAddNoMessage
-    -- The friend request message was empty. This, and the TooLong code will
-    -- never be returned from tox_friend_add_norequest.
-
-  | ErrFriendAddOwnKey
-    -- The friend address belongs to the sending client.
-
-  | ErrFriendAddAlreadySent
-    -- A friend request has already been sent, or the address belongs to a
-    -- friend that is already on the friend list.
-
-  | ErrFriendAddBadChecksum
-    -- The friend address checksum failed.
-
-  | ErrFriendAddSetNewNospam
-    -- The friend was already there, but the nospam value was different.
-
-  | ErrFriendAddMalloc
-    -- A memory allocation failed when trying to increase the friend list size.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Add a friend to the friend list and send a friend request.
---
--- A friend request message must be at least 1 byte long and at most
--- 'tox_max_friend_request_length'.
---
--- Friend numbers are unique identifiers used in all functions that operate on
--- friends. Once added, a friend number is stable for the lifetime of the Tox
--- object. After saving the state and reloading it, the friend numbers may not
--- be the same as before. Deleting a friend creates a gap in the friend number
--- set, which is filled by the next adding of a friend. Any pattern in friend
--- numbers should not be relied on.
---
--- If more than INT32_MAX friends are added, this function causes undefined
--- behaviour.
---
--- @param address The address of the friend (returned by tox_self_get_address of
---   the friend you wish to add) it must be 'tox_address_size' bytes.
--- @param message The message that will be sent along with the friend request.
--- @param length The length of the data byte array.
---
--- @return the friend number on success, UINT32_MAX on failure.
-foreign import ccall tox_friend_add :: Tox a -> CString -> CString -> CSize -> CErr ErrFriendAdd -> IO Word32
-callFriendAddFun :: (Tox a -> CString -> CString -> CSize -> CErr ErrFriendAdd -> IO Word32) ->
-                    Tox a -> BS.ByteString -> String -> IO (Either ErrFriendAdd Word32)
-callFriendAddFun f tox address message =
-  withCStringLen message $ \(msgStr, msgLen) ->
-    BS.useAsCString address $ \addr' ->
-      callErrFun $ f tox addr' msgStr (fromIntegral msgLen)
-
-toxFriendAdd :: Tox a -> BS.ByteString -> String -> IO (Either ErrFriendAdd Word32)
-toxFriendAdd = callFriendAddFun tox_friend_add
-
--- | Add a friend without sending a friend request.
---
--- This function is used to add a friend in response to a friend request. If the
--- client receives a friend request, it can be reasonably sure that the other
--- client added this client as a friend, eliminating the need for a friend
--- request.
---
--- This function is also useful in a situation where both instances are
--- controlled by the same entity, so that this entity can perform the mutual
--- friend adding. In this case, there is no need for a friend request, either.
---
--- @param public_key A byte array of length 'tox_public_key_size' containing the
---   Public Key (not the Address) of the friend to add.
---
--- @return the friend number on success, UINT32_MAX on failure.
--- @see tox_friend_add for a more detailed description of friend numbers.
-foreign import ccall tox_friend_add_norequest :: Tox a -> CString -> CErr ErrFriendAdd -> IO Word32
-callFriendAddNorequestFun :: (Tox a -> CString -> CErr ErrFriendAdd -> IO Word32) ->
-                    Tox a -> BS.ByteString -> IO (Either ErrFriendAdd Word32)
-callFriendAddNorequestFun f tox address =
-  BS.useAsCString address $ \addr' ->
-    callErrFun $ f tox addr'
-
-toxFriendAddNorequest :: Tox a -> BS.ByteString -> IO (Either ErrFriendAdd Word32)
-toxFriendAddNorequest = callFriendAddNorequestFun tox_friend_add_norequest
-
-
-data ErrFriendDelete
-  = ErrFriendDeleteOk
-    -- The function returned successfully.
-
-  | ErrFriendDeleteFriendNotFound
-    -- There was no friend with the given friend number. No friends were
-    -- deleted.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Remove a friend from the friend list.
---
--- This does not notify the friend of their deletion. After calling this
--- function, this client will appear offline to the friend and no communication
--- can occur between the two.
---
--- @param friend_number Friend number for the friend to be deleted.
---
--- @return true on success.
-foreign import ccall tox_friend_delete :: Tox a -> Word32 -> CErr ErrFriendDelete -> IO ()
-
-toxFriendDelete :: Tox a -> Word32 -> IO (Either ErrFriendDelete ())
-toxFriendDelete tox fn = callErrFun $ tox_friend_delete tox fn
-
-
---------------------------------------------------------------------------------
---
--- :: Friend list queries
---
---------------------------------------------------------------------------------
-
-
-data ErrFriendByPublicKey
-  = ErrFriendByPublicKeyOk
-    -- The function returned successfully.
-
-  | ErrFriendByPublicKeyNull
-    -- One of the arguments to the function was 'nullPtr' when it was not
-    -- expected.
-
-  | ErrFriendByPublicKeyNotFound
-    -- No friend with the given Public Key exists on the friend list.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Return the friend number associated with that Public Key.
---
--- @return the friend number on success, UINT32_MAX on failure.
--- @param public_key A byte array containing the Public Key.
-foreign import ccall tox_friend_by_public_key :: Tox a -> CString -> CErr ErrFriendByPublicKey -> IO Word32
-callFriendByPublicKey :: (Tox a -> CString -> CErr ErrFriendByPublicKey -> IO Word32) ->
-                         Tox a -> BS.ByteString -> IO (Either ErrFriendByPublicKey Word32)
-callFriendByPublicKey f tox address =
-  BS.useAsCString address $ \addr' ->
-    callErrFun $ f tox addr'
-
-toxFriendByPublicKey :: Tox a -> BS.ByteString -> IO (Either ErrFriendByPublicKey Word32)
-toxFriendByPublicKey = callFriendByPublicKey tox_friend_by_public_key
-
--- | Checks if a friend with the given friend number exists and returns true if
--- it does.
-foreign import ccall tox_friend_exists :: Tox a -> Word32 -> IO Bool
-toxFriendExists :: Tox a -> Word32 -> IO Bool
-toxFriendExists = tox_friend_exists
-
--- | Return the number of friends on the friend list.
---
--- This function can be used to determine how much memory to allocate for
--- tox_self_get_friend_list.
-foreign import ccall tox_self_get_friend_list_size :: Tox a -> IO CSize
-
--- | Copy a list of valid friend numbers into an array.
---
--- Call tox_self_get_friend_list_size to determine the number of elements to
--- allocate.
---
--- @param list A memory region with enough space to hold the friend list. If
---   this parameter is 'nullPtr', this function has no effect.
-foreign import ccall tox_self_get_friend_list :: Tox a -> Ptr Word32 -> IO ()
-
-toxSelfGetFriendList :: Tox a -> IO [Word32]
-toxSelfGetFriendList tox = do
-  friendListSize <- tox_self_get_friend_list_size tox
-  allocaArray (fromIntegral friendListSize) $ \friendListPtr -> do
-    tox_self_get_friend_list tox friendListPtr
-    peekArray (fromIntegral friendListSize) friendListPtr
-
-data ErrFriendGetPublicKey
-  = ErrFriendGetPublicKeyOk
-    -- The function returned successfully.
-
-  | ErrFriendGetPublicKeyFriendNotFound
-    -- No friend with the given number exists on the friend list.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Copies the Public Key associated with a given friend number to a byte
--- array.
---
--- @param friend_number The friend number you want the Public Key of.
--- @param public_key A memory region of at least 'tox_public_key_size' bytes. If
---   this parameter is 'nullPtr', this function has no effect.
---
--- @return true on success.
-foreign import ccall tox_friend_get_public_key :: Tox a -> Word32 -> CString -> CErr ErrFriendGetPublicKey -> IO Bool
-callFriendGetPublicKey :: (Tox a -> Word32 -> CString -> CErr ErrFriendGetPublicKey -> IO Bool) ->
-                          Tox a -> Word32 -> IO (Either ErrFriendGetPublicKey BS.ByteString)
-callFriendGetPublicKey f tox fn =
-  let pkLen = fromIntegral tox_public_key_size in
-  alloca $ \errPtr ->
-    allocaArray pkLen $ \pkPtr -> do
-      _ <- f tox fn pkPtr errPtr
-      callGetPublicKey errPtr pkPtr pkLen
-
-callGetPublicKey
-  :: (Bounded err, Enum err, Eq err)
-  => Ptr (CEnum err)
-  -> Ptr CChar
-  -> Int
-  -> IO (Either err BS.ByteString)
-callGetPublicKey errPtr pkPtr pkLen = do
-  err <- toEnum . fromIntegral . unCEnum <$> peek errPtr
-  str <- BS.packCStringLen (pkPtr, pkLen)
-  return $ if err /= minBound
-           then Left  err
-           else Right str
-
-toxFriendGetPublicKey :: Tox a -> Word32 -> IO (Either ErrFriendGetPublicKey BS.ByteString)
-toxFriendGetPublicKey = callFriendGetPublicKey tox_friend_get_public_key
-
-
-data ErrFriendGetLastOnline
-  = ErrFriendGetLastOnlineOk
-    -- The function returned successfully.
-
-  | ErrFriendGetLastOnlineFriendNotFound
-    -- No friend with the given number exists on the friend list.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Return a unix-time timestamp of the last time the friend associated with a given
--- friend number was seen online. This function will return UINT64_MAX on error.
---
--- @param friend_number The friend number you want to query.
-foreign import ccall tox_friend_get_last_online :: Tox a -> Word32 -> CErr ErrFriendGetLastOnline -> IO Word64
-callFriendGetLastOnline :: (Tox a -> Word32 -> CErr ErrFriendGetLastOnline -> IO Word64) ->
-                           Tox a -> Word32 -> IO (Either ErrFriendGetLastOnline EpochTime)
-callFriendGetLastOnline f tox fn = callErrFun (f tox fn >=> (return . CTime . fromIntegral))
-
-toxFriendGetLastOnline :: Tox a -> Word32 -> IO (Either ErrFriendGetLastOnline EpochTime)
-toxFriendGetLastOnline = callFriendGetLastOnline tox_friend_get_last_online
-
-
---------------------------------------------------------------------------------
---
--- :: Friend-specific state queries (can also be received through callbacks)
---
---------------------------------------------------------------------------------
-
-
--- | Common error codes for friend state query functions.
-data ErrFriendQuery
-  = ErrFriendQueryOk
-    -- The function returned successfully.
-
-  | ErrFriendQueryNull
-    -- The pointer parameter for storing the query result (name, message) was
-    -- NULL. Unlike the `_self_` variants of these functions, which have no effect
-    -- when a parameter is NULL, these functions return an error in that case.
-
-  | ErrFriendQueryFriendNotFound
-    -- The friend_number did not designate a valid friend.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Return the length of the friend's name. If the friend number is invalid, the
--- return value is unspecified.
---
--- The return value is equal to the `length` argument received by the last
--- `friend_name` callback.
-foreign import ccall tox_friend_get_name_size :: Tox a -> Word32 -> CErr ErrFriendQuery -> IO CSize
-
--- | Write the name of the friend designated by the given friend number to a byte
--- array.
---
--- Call tox_friend_get_name_size to determine the allocation size for the `name`
--- parameter.
---
--- The data written to `name` is equal to the data received by the last
--- `friend_name` callback.
---
--- @param name A valid memory region large enough to store the friend's name.
---
--- @return true on success.
-foreign import ccall tox_friend_get_name :: Tox a -> Word32 -> CString -> CErr ErrFriendQuery -> IO Bool
-
-toxFriendGetName :: Tox a -> Word32 -> IO (Either ErrFriendQuery String)
-toxFriendGetName tox fn = do
-  nameLenRes <- callErrFun $ tox_friend_get_name_size tox fn
-  case nameLenRes of
-    Left err -> return $ Left err
-    Right nameLen -> allocaArray (fromIntegral nameLen) $ \namePtr -> do
-      nameRes <- callErrFun $ tox_friend_get_name tox fn namePtr
-      case nameRes of
-        Left err -> return $ Left err
-        Right _ ->
-          Right <$> peekCStringLen (namePtr, fromIntegral nameLen)
-
-
--- | @param friend_number The friend number of the friend whose name changed.
--- @param name A byte array containing the same data as
---   tox_friend_get_name would write to its `name` parameter.
--- @param length A value equal to the return value of
---   tox_friend_get_name_size.
-type FriendNameCb a = Tox a -> Word32 -> String -> a -> IO a
-type CFriendNameCb a = Tox a -> Word32 -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFriendNameCb :: CFriendNameCb a -> IO (FunPtr (CFriendNameCb a))
-
-callFriendNameCb :: FriendNameCb a -> CFriendNameCb a
-callFriendNameCb f tox fn nameStr nameLen udPtr = do
-  ud <- deRefStablePtr udPtr
-  name <- peekCStringLen (nameStr, fromIntegral nameLen)
-  modifyMVar_ ud $ f tox fn name
-
-friendNameCb :: FriendNameCb a -> IO (FunPtr (CFriendNameCb a))
-friendNameCb = wrapFriendNameCb . callFriendNameCb
-
-
--- | Set the callback for the `friend_name` event. Pass 'nullPtr' to unset.
---
--- This event is triggered when a friend changes their name.
-foreign import ccall tox_callback_friend_name :: Tox a -> FunPtr (CFriendNameCb a) -> IO ()
-
-
--- | @param friend_number The friend number of the friend whose status message
---   changed.
--- @param message A byte array containing the same data as
---   tox_friend_get_status_message would write to its `status_message`
---   parameter.
--- @param length A value equal to the return value of
---   tox_friend_get_status_message_size.
-type FriendStatusMessageCb a = Tox a -> Word32 -> String -> a -> IO a
-type CFriendStatusMessageCb a = Tox a -> Word32 -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFriendStatusMessageCb :: CFriendStatusMessageCb a -> IO (FunPtr (CFriendStatusMessageCb a))
-
-callFriendStatusMessageCb :: FriendStatusMessageCb a -> CFriendStatusMessageCb a
-callFriendStatusMessageCb f tox fn statusMessageStr statusMessageLen udPtr = do
-  ud <- deRefStablePtr udPtr
-  statusMessage <- peekCStringLen (statusMessageStr, fromIntegral statusMessageLen)
-  modifyMVar_ ud $ f tox fn statusMessage
-
-friendStatusMessageCb :: FriendStatusMessageCb a -> IO (FunPtr (CFriendStatusMessageCb a))
-friendStatusMessageCb = wrapFriendStatusMessageCb . callFriendStatusMessageCb
-
-
--- | Return the length of the friend's status message. If the friend number is
--- invalid, the return value is SIZE_MAX.
-foreign import ccall tox_friend_get_status_message_size :: Tox a -> Word32 -> CErr ErrFriendQuery -> IO CSize
-
--- | Write the status message of the friend designated by the given friend number to a byte
--- array.
---
--- Call tox_friend_get_status_message_size to determine the allocation size for the `status_name`
--- parameter.
---
--- The data written to `status_message` is equal to the data received by the last
--- `friend_status_message` callback.
---
--- @param status_message A valid memory region large enough to store the friend's status message.
-foreign import ccall tox_friend_get_status_message :: Tox a -> Word32 -> CString -> CErr ErrFriendQuery -> IO Bool
-
-toxFriendGetStatusMessage :: Tox a -> Word32 -> IO (Either ErrFriendQuery String)
-toxFriendGetStatusMessage tox fn = do
-  statusMessageLenRes <- callErrFun $ tox_friend_get_status_message_size tox fn
-  case statusMessageLenRes of
-    Left err -> return $ Left err
-    Right statusMessageLen -> allocaArray (fromIntegral statusMessageLen) $ \statusMessagePtr -> do
-      statusMessageRes <- callErrFun $ tox_friend_get_status_message tox fn statusMessagePtr
-      case statusMessageRes of
-        Left err -> return $ Left err
-        Right _ ->
-          Right <$> peekCStringLen (statusMessagePtr, fromIntegral statusMessageLen)
-
-
--- | Set the callback for the `friend_status_message` event. Pass 'nullPtr' to
--- unset.
---
--- This event is triggered when a friend changes their status message.
-foreign import ccall tox_callback_friend_status_message :: Tox a -> FunPtr (CFriendStatusMessageCb a) -> IO ()
-
-
--- | @param friend_number The friend number of the friend whose user status
---   changed.
--- @param status The new user status.
-type FriendStatusCb a = Tox a -> Word32 -> UserStatus -> a -> IO a
-type CFriendStatusCb a = Tox a -> Word32 -> CEnum UserStatus -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFriendStatusCb :: CFriendStatusCb a -> IO (FunPtr (CFriendStatusCb a))
-
-callFriendStatusCb :: FriendStatusCb a -> CFriendStatusCb a
-callFriendStatusCb f tox fn status = deRefStablePtr >=> (`modifyMVar_` f tox fn (fromCEnum status))
-
-friendStatusCb :: FriendStatusCb a -> IO (FunPtr (CFriendStatusCb a))
-friendStatusCb = wrapFriendStatusCb . callFriendStatusCb
-
-
--- | Set the callback for the `friend_status` event. Pass 'nullPtr' to unset.
---
--- This event is triggered when a friend changes their user status.
-foreign import ccall tox_callback_friend_status :: Tox a -> FunPtr (CFriendStatusCb a) -> IO ()
-
-
--- | Check whether a friend is currently connected to this client.
---
--- The result of this function is equal to the last value received by the
--- `friend_connection_status` callback.
---
--- @param friend_number The friend number for which to query the connection
---   status.
---
--- @return the friend's connection status as it was received through the
---   `friend_connection_status` event.
-foreign import ccall tox_friend_get_connection_status :: Tox a -> Word32 -> CErr ErrFriendQuery -> IO (CEnum Connection)
-
-callFriendGetConnectionStatus :: (Tox a -> Word32 -> CErr ErrFriendQuery -> IO (CEnum Connection)) ->
-                                 Tox a -> Word32 -> IO (Either ErrFriendQuery Connection)
-callFriendGetConnectionStatus f tox fn = callErrFun (f tox fn >=> (return . fromCEnum))
-
-toxFriendGetConnectionStatus :: Tox a -> Word32 -> IO (Either ErrFriendQuery Connection)
-toxFriendGetConnectionStatus = callFriendGetConnectionStatus tox_friend_get_connection_status
-
-
--- | @param friend_number The friend number of the friend whose connection
---   status changed.
--- @param connection_status The result of calling
---   tox_friend_get_connection_status on the passed friend_number.
-type FriendConnectionStatusCb a = Tox a -> Word32 -> Connection -> a -> IO a
-type CFriendConnectionStatusCb a = Tox a -> Word32 -> CEnum Connection -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFriendConnectionStatusCb :: CFriendConnectionStatusCb a -> IO (FunPtr (CFriendConnectionStatusCb a))
-
-callFriendConnectionStatusCb :: FriendConnectionStatusCb a -> CFriendConnectionStatusCb a
-callFriendConnectionStatusCb f tox fn connectionStatus = deRefStablePtr >=> (`modifyMVar_` f tox fn (fromCEnum connectionStatus))
-
-friendConnectionStatusCb :: FriendConnectionStatusCb a -> IO (FunPtr (CFriendConnectionStatusCb a))
-friendConnectionStatusCb = wrapFriendConnectionStatusCb . callFriendConnectionStatusCb
-
-
--- | Set the callback for the `friend_connection_status` event. Pass 'nullPtr'
--- to unset.
---
--- This event is triggered when a friend goes offline after having been online,
--- or when a friend goes online.
---
--- This callback is not called when adding friends. It is assumed that when
--- adding friends, their connection status is initially offline.
-foreign import ccall tox_callback_friend_connection_status :: Tox a -> FunPtr (CFriendConnectionStatusCb a) -> IO ()
-
-
--- | Check whether a friend is currently typing a message.
---
--- @param friend_number The friend number for which to query the typing status.
---
--- @return true if the friend is typing.
--- @return false if the friend is not typing, or the friend number was
---   invalid. Inspect the error code to determine which case it is.
-foreign import ccall tox_friend_get_typing :: Tox a -> Word32 -> CErr ErrFriendQuery -> IO Bool
-
-callFriendGetTyping :: (Tox a -> Word32 -> CErr ErrFriendQuery -> IO Bool) ->
-                       Tox a -> Word32 -> IO (Either ErrFriendQuery Bool)
-callFriendGetTyping f tox fn = callErrFun $ f tox fn
-
-toxFriendGetTyping :: Tox a -> Word32 -> IO (Either ErrFriendQuery Bool)
-toxFriendGetTyping = callFriendGetTyping tox_friend_get_typing
-
-
--- | @param friend_number The friend number of the friend who started or stopped
---   typing.
--- @param is_typing The result of calling tox_friend_get_typing on the passed
---   friend_number.
-type FriendTypingCb a = Tox a -> Word32 -> Bool -> a -> IO a
-type CFriendTypingCb a = Tox a -> Word32 -> Bool -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFriendTypingCb :: CFriendTypingCb a -> IO (FunPtr (CFriendTypingCb a))
-
-callFriendTypingCb :: FriendTypingCb a -> CFriendTypingCb a
-callFriendTypingCb f tox fn typing = deRefStablePtr >=> (`modifyMVar_` f tox fn typing)
-
-friendTypingCb :: FriendTypingCb a -> IO (FunPtr (CFriendTypingCb a))
-friendTypingCb = wrapFriendTypingCb . callFriendTypingCb
-
--- | Set the callback for the `friend_typing` event. Pass 'nullPtr' to unset.
---
--- This event is triggered when a friend starts or stops typing.
-foreign import ccall tox_callback_friend_typing :: Tox a -> FunPtr (CFriendTypingCb a) -> IO ()
-
-
---------------------------------------------------------------------------------
---
--- :: Sending private messages
---
---------------------------------------------------------------------------------
-
-
-data ErrSetTyping
-  = ErrSetTypingOk
-    -- The function returned successfully.
-
-  | ErrSetTypingFriendNotFound
-    -- The friend number did not designate a valid friend.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Set the client's typing status for a friend.
---
--- The client is responsible for turning it on or off.
---
--- @param friend_number The friend to which the client is typing a message.
--- @param typing The typing status. True means the client is typing.
---
--- @return true on success.
-foreign import ccall tox_self_set_typing :: Tox a -> Word32 -> Bool -> CErr ErrSetTyping -> IO Bool
-callSelfSetTyping :: (Tox a -> Word32 -> Bool -> CErr ErrSetTyping -> IO Bool) ->
-                     Tox a -> Word32 -> Bool -> IO (Either ErrSetTyping Bool)
-callSelfSetTyping f tox fn typing = callErrFun $ f tox fn typing
-
-toxSelfSetTyping :: Tox a -> Word32 -> Bool -> IO (Either ErrSetTyping Bool)
-toxSelfSetTyping = callSelfSetTyping tox_self_set_typing
-
-data ErrFriendSendMessage
-  = ErrFriendSendMessageOk
-    -- The function returned successfully.
-
-  | ErrFriendSendMessageNull
-    -- One of the arguments to the function was 'nullPtr' when it was not
-    -- expected.
-
-  | ErrFriendSendMessageFriendNotFound
-    -- The friend number did not designate a valid friend.
-
-  | ErrFriendSendMessageFriendNotConnected
-    -- This client is currently not connected to the friend.
-
-  | ErrFriendSendMessageSendq
-    -- An allocation error occurred while increasing the send queue size.
-
-  | ErrFriendSendMessageTooLong
-    -- Message length exceeded 'tox_max_message_length'.
-
-  | ErrFriendSendMessageEmpty
-    -- Attempted to send a zero-length message.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Send a text chat message to an online friend.
---
--- This function creates a chat message packet and pushes it into the send
--- queue.
---
--- The message length may not exceed 'tox_max_message_length'. Larger messages
--- must be split by the client and sent as separate messages. Other clients can
--- then reassemble the fragments. Messages may not be empty.
---
--- The return value of this function is the message ID. If a read receipt is
--- received, the triggered `friend_read_receipt` event will be passed this
--- message ID.
---
--- Message IDs are unique per friend. The first message ID is 0. Message IDs are
--- incremented by 1 each time a message is sent. If UINT32_MAX messages were
--- sent, the next message ID is 0.
---
--- @param type Message type (normal, action, ...).
--- @param friend_number The friend number of the friend to send the message to.
--- @param message A non-'nullPtr' pointer to the first element of a byte array
---   containing the message text.
--- @param length Length of the message to be sent.
-foreign import ccall tox_friend_send_message :: Tox a -> Word32 -> CEnum MessageType -> CString -> CSize -> CErr ErrFriendSendMessage -> IO Word32
-callFriendSendMessage :: (Tox a -> Word32 -> CEnum MessageType -> CString -> CSize -> CErr ErrFriendSendMessage -> IO Word32) ->
-                         Tox a -> Word32 -> MessageType -> String -> IO (Either ErrFriendSendMessage Word32)
-callFriendSendMessage f tox fn messageType message =
-  withCStringLen message $ \(msgStr, msgLen) ->
-    callErrFun $ f tox fn (toCEnum messageType) msgStr (fromIntegral msgLen)
-
-toxFriendSendMessage :: Tox a -> Word32 -> MessageType -> String -> IO (Either ErrFriendSendMessage Word32)
-toxFriendSendMessage = callFriendSendMessage tox_friend_send_message
-
-
--- | @param friend_number The friend number of the friend who received the
---   message.
--- @param message_id The message ID as returned from tox_friend_send_message
---   corresponding to the message sent.
-type FriendReadReceiptCb a = Tox a -> Word32 -> Word32 -> a -> IO a
-type CFriendReadReceiptCb a = Tox a -> Word32 -> Word32 -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFriendReadReceiptCb :: CFriendReadReceiptCb a -> IO (FunPtr (CFriendReadReceiptCb a))
-
-callFriendReadReceiptCb :: FriendReadReceiptCb a -> CFriendReadReceiptCb a
-callFriendReadReceiptCb f tox fn msgId = deRefStablePtr >=> (`modifyMVar_` f tox fn msgId)
-
-friendReadReceiptCb :: FriendReadReceiptCb a -> IO (FunPtr (CFriendReadReceiptCb a))
-friendReadReceiptCb = wrapFriendReadReceiptCb . callFriendReadReceiptCb
-
-
--- | Set the callback for the `friend_read_receipt` event. Pass 'nullPtr' to
--- unset.
---
--- This event is triggered when the friend receives the message sent with
--- tox_friend_send_message with the corresponding message ID.
-foreign import ccall tox_callback_friend_read_receipt :: Tox a -> FunPtr (CFriendReadReceiptCb a) -> IO ()
-
-
---------------------------------------------------------------------------------
---
--- :: Receiving private messages and friend requests
---
---------------------------------------------------------------------------------
-
-
--- | @param public_key The Public Key of the user who sent the friend request.
--- @param time_delta A delta in seconds between when the message was composed
---   and when it is being transmitted. For messages that are sent immediately,
---   it will be 0. If a message was written and couldn't be sent immediately
---   (due to a connection failure, for example), the time_delta is an
---   approximation of when it was composed.
--- @param message The message they sent along with the request.
--- @param length The size of the message byte array.
-type FriendRequestCb a = Tox a -> BS.ByteString -> String -> a -> IO a
-type CFriendRequestCb a = Tox a -> CString -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFriendRequestCb :: CFriendRequestCb a -> IO (FunPtr (CFriendRequestCb a))
-
-callFriendRequestCb :: FriendRequestCb a -> CFriendRequestCb a
-callFriendRequestCb f tox addrPtr nameStr nameLen udPtr = do
-  let addrLen = fromIntegral tox_address_size
-  ud <- deRefStablePtr udPtr
-  addr <- BS.packCStringLen (addrPtr, addrLen)
-  name <- peekCStringLen (nameStr, fromIntegral nameLen)
-  modifyMVar_ ud $ f tox addr name
-
-friendRequestCb :: FriendRequestCb a -> IO (FunPtr (CFriendRequestCb a))
-friendRequestCb = wrapFriendRequestCb . callFriendRequestCb
-
-
--- | Set the callback for the `friend_request` event. Pass 'nullPtr' to unset.
---
--- This event is triggered when a friend request is received.
-foreign import ccall tox_callback_friend_request :: Tox a -> FunPtr (CFriendRequestCb a) -> IO ()
-
--- | @param friend_number The friend number of the friend who sent the message.
--- @param time_delta Time between composition and sending.
--- @param message The message data they sent.
--- @param length The size of the message byte array.
---
--- @see friend_request for more information on time_delta.
-type FriendMessageCb a = Tox a -> Word32 -> MessageType -> String -> a -> IO a
-type CFriendMessageCb a = Tox a -> Word32 -> CEnum MessageType -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFriendMessageCb :: CFriendMessageCb a -> IO (FunPtr (CFriendMessageCb a))
-
-callFriendMessageCb :: FriendMessageCb a -> CFriendMessageCb a
-callFriendMessageCb f tox fn msgType msgStr msgLen udPtr = do
-  ud <- deRefStablePtr udPtr
-  msg <- peekCStringLen (msgStr, fromIntegral msgLen)
-  modifyMVar_ ud $ f tox fn (fromCEnum msgType) msg
-
-friendMessageCb :: FriendMessageCb a -> IO (FunPtr (CFriendMessageCb a))
-friendMessageCb = wrapFriendMessageCb . callFriendMessageCb
-
-
--- | Set the callback for the `friend_message` event. Pass 'nullPtr' to unset.
---
--- This event is triggered when a message from a friend is received.
-foreign import ccall tox_callback_friend_message :: Tox a -> FunPtr (CFriendMessageCb a) -> IO ()
-
-
---------------------------------------------------------------------------------
---
--- :: File transmission: common between sending and receiving
---
---------------------------------------------------------------------------------
-
-
--- | Generates a cryptographic hash of the given data.
---
--- This function may be used by clients for any purpose, but is provided
--- primarily for validating cached avatars. This use is highly recommended to
--- avoid unnecessary avatar updates.
---
--- If hash is 'nullPtr' or data is 'nullPtr' while length is not 0 the function
--- returns false, otherwise it returns true.
---
--- This function is a wrapper to internal message-digest functions.
---
--- @param hash A valid memory location the hash data. It must be at least
---   'tox_hash_length' bytes in size.
--- @param data Data to be hashed or 'nullPtr'.
--- @param length Size of the data array or 0.
---
--- @return true if hash was not 'nullPtr'.
-foreign import ccall tox_hash :: CString -> CString -> CSize -> IO Bool
-
-toxHash :: BS.ByteString -> IO BS.ByteString
-toxHash d =
-  let hashLen = fromIntegral tox_hash_length in
-  allocaArray hashLen $ \hashPtr ->
-    BS.useAsCStringLen d $ \(dataPtr, dataLen) -> do
-      _ <- tox_hash hashPtr dataPtr (fromIntegral dataLen)
-      BS.packCStringLen (dataPtr, fromIntegral dataLen)
-
-data FileKind
-  = FileKindData
-    -- Arbitrary file data. Clients can choose to handle it based on the file
-    -- name or magic or any other way they choose.
-
-  | FileKindAvatar
-    -- Avatar file_id. This consists of tox_hash(image).  Avatar data. This
-    -- consists of the image data.
-    --
-    -- Avatars can be sent at any time the client wishes. Generally, a client
-    -- will send the avatar to a friend when that friend comes online, and to
-    -- all friends when the avatar changed. A client can save some traffic by
-    -- remembering which friend received the updated avatar already and only
-    -- send it if the friend has an out of date avatar.
-    --
-    -- Clients who receive avatar send requests can reject it (by sending
-    -- FileControlCancel before any other controls), or accept it (by sending
-    -- FileControlResume). The file_id of length 'tox_hash_length' bytes (same
-    -- length as 'tox_file_id_length') will contain the hash. A client can
-    -- compare this hash with a saved hash and send FileControlCancel to
-    -- terminate the avatar transfer if it matches.
-    --
-    -- When file_size is set to 0 in the transfer request it means that the
-    -- client has no avatar.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
-data FileControl
-  = FileControlResume
-    -- Sent by the receiving side to accept a file send request. Also sent after
-    -- a FileControlPause command to continue sending or receiving.
-
-  | FileControlPause
-    -- Sent by clients to pause the file transfer. The initial state of a file
-    -- transfer is always paused on the receiving side and running on the
-    -- sending side. If both the sending and receiving side pause the transfer,
-    -- then both need to send FileControlResume for the transfer to resume.
-
-  | FileControlCancel
-    -- Sent by the receiving side to reject a file send request before any other
-    -- commands are sent. Also sent by either side to terminate a file transfer.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
-data ErrFileControl
-  = ErrFileControlOk
-    -- The function returned successfully.
-
-  | ErrFileControlFriendNotFound
-    -- The friend_number passed did not designate a valid friend.
-
-  | ErrFileControlFriendNotConnected
-    -- This client is currently not connected to the friend.
-
-  | ErrFileControlNotFound
-    -- No file transfer with the given file number was found for the given friend.
-
-  | ErrFileControlNotPaused
-    -- A Resume control was sent, but the file transfer is running normally.
-
-  | ErrFileControlDenied
-    -- A Resume control was sent, but the file transfer was paused by the other
-    -- party. Only the party that paused the transfer can resume it.
-
-  | ErrFileControlAlreadyPaused
-    -- A Pause control was sent, but the file transfer was already paused.
-
-  | ErrFileControlSendq
-    -- Packet queue is full.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Sends a file control command to a friend for a given file transfer.
---
--- @param friend_number The friend number of the friend the file is being
---   transferred to or received from.
--- @param file_number The friend-specific identifier for the file transfer.
--- @param control The control command to send.
---
--- @return true on success.
-foreign import ccall tox_file_control :: Tox a -> Word32 -> Word32 -> CEnum FileControl -> CErr ErrFileControl -> IO Bool
-
-callFileControl :: (Tox a -> Word32 -> Word32 -> CEnum FileControl -> CErr ErrFileControl -> IO Bool) ->
-                   Tox a -> Word32 -> Word32 -> FileControl -> IO (Either ErrFileControl Bool)
-callFileControl f tox fn fileNum control = callErrFun $ f tox fn fileNum (toCEnum control)
-
-toxFileControl :: Tox a -> Word32 -> Word32 -> FileControl -> IO (Either ErrFileControl Bool)
-toxFileControl = callFileControl tox_file_control
-
--- | When receiving FileControlCancel, the client should release the
--- resources associated with the file number and consider the transfer failed.
---
--- @param friend_number The friend number of the friend who is sending the file.
--- @param file_number The friend-specific file number the data received is
---   associated with.
--- @param control The file control command received.
-type FileRecvControlCb a = Tox a -> Word32 -> Word32 -> FileControl -> a -> IO a
-type CFileRecvControlCb a = Tox a -> Word32 -> Word32 -> CEnum FileControl -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFileRecvControlCb :: CFileRecvControlCb a -> IO (FunPtr (CFileRecvControlCb a))
-
-callFileRecvControlCb :: FileRecvControlCb a -> CFileRecvControlCb a
-callFileRecvControlCb f tox fn fileNum ctrlCmd = deRefStablePtr >=> (`modifyMVar_` f tox fn fileNum (fromCEnum ctrlCmd))
-
-fileRecvControlCb :: FileRecvControlCb a -> IO (FunPtr (CFileRecvControlCb a))
-fileRecvControlCb = wrapFileRecvControlCb . callFileRecvControlCb
-
-
--- | Set the callback for the `file_recv_control` event. Pass 'nullPtr' to
--- unset.
---
--- This event is triggered when a file control command is received from a
--- friend.
-foreign import ccall tox_callback_file_recv_control :: Tox a -> FunPtr (CFileRecvControlCb a) -> IO ()
-
-data ErrFileSeek
-  = ErrFileSeekOk
-    -- The function returned successfully.
-
-  | ErrFileSeekFriendNotFound
-    -- The friend_number passed did not designate a valid friend.
-
-  | ErrFileSeekFriendNotConnected
-    -- This client is currently not connected to the friend.
-
-  | ErrFileSeekNotFound
-    -- No file transfer with the given file number was found for the given friend.
-
-  | ErrFileSeekDenied
-    -- File was not in a state where it could be seeked.
-
-  | ErrFileSeekInvalidPosition
-    -- Seek position was invalid
-
-  | ErrFileSeekSendq
-    -- Packet queue is full.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Sends a file seek control command to a friend for a given file transfer.
---
--- This function can only be called to resume a file transfer right before
--- FileControlResume is sent.
---
--- @param friend_number The friend number of the friend the file is being
---   received from.
--- @param file_number The friend-specific identifier for the file transfer.
--- @param position The position that the file should be seeked to.
-foreign import ccall tox_file_seek :: Tox a -> Word32 -> Word32 -> Word64 -> CErr ErrFileSeek -> IO Bool
-
-callFileSeek :: (Tox a -> Word32 -> Word32 -> Word64 -> CErr ErrFileSeek -> IO Bool) ->
-                   Tox a -> Word32 -> Word32 -> Word64 -> IO (Either ErrFileSeek Bool)
-callFileSeek f tox fn fileNum pos = callErrFun $ f tox fn fileNum pos
-
-toxFileSeek :: Tox a -> Word32 -> Word32 -> Word64 -> IO (Either ErrFileSeek Bool)
-toxFileSeek = callFileSeek tox_file_seek
-
-
-data ErrFileGet
-  = ErrFileGetOk
-    -- The function returned successfully.
-
-  | ErrFileGetNull
-    -- One of the arguments to the function was 'nullPtr' when it was not
-    -- expected.
-
-  | ErrFileGetFriendNotFound
-    -- The friend_number passed did not designate a valid friend.
-
-  | ErrFileGetNotFound
-    -- No file transfer with the given file number was found for the given friend.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Copy the file id associated to the file transfer to a byte array.
---
--- @param friend_number The friend number of the friend the file is being
---   transferred to or received from.
--- @param file_number The friend-specific identifier for the file transfer.
--- @param file_id A memory region of at least 'tox_file_id_length' bytes. If
---   this parameter is 'nullPtr', this function has no effect.
---
--- @return true on success.
-foreign import ccall tox_file_get_file_id :: Tox a -> Word32 -> Word32 -> CString -> CErr ErrFileGet -> IO Bool
-
-callFileGetFileId :: (Tox a -> Word32 -> Word32 -> CString -> CErr ErrFileGet -> IO Bool) ->
-                     Tox a -> Word32 -> Word32 -> IO (Either ErrFileGet BS.ByteString)
-callFileGetFileId f tox fn fileNum =
-  let fileIdLen = fromIntegral tox_file_id_length in
-  alloca $ \errPtr ->
-    allocaArray fileIdLen $ \fileIdPtr -> do
-      _ <- f tox fn fileNum fileIdPtr errPtr
-      err <- toEnum . fromIntegral . unCEnum <$> peek errPtr
-      fileId <- BS.packCStringLen (fileIdPtr, fileIdLen)
-      return $ if err /= minBound
-               then Left  err
-               else Right fileId
-
-toxFileGetFileId :: Tox a -> Word32 -> Word32 -> IO (Either ErrFileGet BS.ByteString)
-toxFileGetFileId = callFileGetFileId tox_file_get_file_id
-
-
---------------------------------------------------------------------------------
---
--- :: File transmission: sending
---
---------------------------------------------------------------------------------
-
-
-data ErrFileSend
-  = ErrFileSendOk
-    -- The function returned successfully.
-
-  | ErrFileSendNull
-    -- One of the arguments to the function was 'nullPtr' when it was not
-    -- expected.
-
-  | ErrFileSendFriendNotFound
-    -- The friend_number passed did not designate a valid friend.
-
-  | ErrFileSendFriendNotConnected
-    -- This client is currently not connected to the friend.
-
-  | ErrFileSendNameTooLong
-    -- Filename length exceeded 'tox_max_filename_length' bytes.
-
-  | ErrFileSendTooMany
-    -- Too many ongoing transfers. The maximum number of concurrent file transfers
-    -- is 256 per friend per direction (sending and receiving).
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Send a file transmission request.
---
--- Maximum filename length is 'tox_max_filename_length' bytes. The filename
--- should generally just be a file name, not a path with directory names.
---
--- If a non-UINT64_MAX file size is provided, it can be used by both sides to
--- determine the sending progress. File size can be set to UINT64_MAX for
--- streaming data of unknown size.
---
--- File transmission occurs in chunks, which are requested through the
--- `file_chunk_request` event.
---
--- When a friend goes offline, all file transfers associated with the friend are
--- purged from core.
---
--- If the file contents change during a transfer, the behaviour is unspecified
--- in general. What will actually happen depends on the mode in which the file
--- was modified and how the client determines the file size.
---
--- - If the file size was increased
---   - and sending mode was streaming (file_size = UINT64_MAX), the behaviour
---     will be as expected.
---   - and sending mode was file (file_size != UINT64_MAX), the
---     file_chunk_request callback will receive length = 0 when Core thinks
---     the file transfer has finished. If the client remembers the file size as
---     it was when sending the request, it will terminate the transfer normally.
---     If the client re-reads the size, it will think the friend cancelled the
---     transfer.
--- - If the file size was decreased
---   - and sending mode was streaming, the behaviour is as expected.
---   - and sending mode was file, the callback will return 0 at the new
---     (earlier) end-of-file, signalling to the friend that the transfer was
---     cancelled.
--- - If the file contents were modified
---   - at a position before the current read, the two files (local and remote)
---     will differ after the transfer terminates.
---   - at a position after the current read, the file transfer will succeed as
---     expected.
---   - In either case, both sides will regard the transfer as complete and
---     successful.
---
--- @param friend_number The friend number of the friend the file send request
---   should be sent to.
--- @param kind The meaning of the file to be sent.
--- @param file_size Size in bytes of the file the client wants to send,
---   UINT64_MAX if unknown or streaming.
--- @param file_id A file identifier of length 'tox_file_id_length' that can be
---   used to uniquely identify file transfers across core restarts. If
---   'nullPtr', a random one will be generated by core. It can then be obtained
---   by using tox_file_get_file_id().
--- @param filename Name of the file. Does not need to be the actual name. This
---   name will be sent along with the file send request.
--- @param filename_length Size in bytes of the filename.
---
--- @return A file number used as an identifier in subsequent callbacks. This
---   number is per friend. File numbers are reused after a transfer terminates.
---   On failure, this function returns UINT32_MAX. Any pattern in file numbers
---   should not be relied on.
-foreign import ccall tox_file_send :: Tox a -> Word32 -> CEnum FileKind -> Word64 -> CString -> CString -> CSize -> CErr ErrFileSend -> IO Word32
-callFileSend :: (Tox a -> Word32 -> CEnum FileKind -> Word64 -> CString -> CString -> CSize -> CErr ErrFileSend -> IO Word32) ->
-                Tox a -> Word32 -> FileKind -> Word64 -> String -> IO (Either ErrFileSend Word32)
-callFileSend f tox fn fileKind fileSize fileName =
-  withCStringLen fileName $ \(fileNamePtr, fileNameLen) ->
-    callErrFun $ f tox fn (toCEnum fileKind) fileSize nullPtr fileNamePtr (fromIntegral fileNameLen)
-
-toxFileSend :: Tox a -> Word32 -> FileKind -> Word64 -> String -> IO (Either ErrFileSend Word32)
-toxFileSend = callFileSend tox_file_send
-
-data ErrFileSendChunk
-  = ErrFileSendChunkOk
-    -- The function returned successfully.
-
-  | ErrFileSendChunkNull
-    -- The length parameter was non-zero, but data was 'nullPtr'.
-
-  | ErrFileSendChunkFriendNotFound
-    -- The friend_number passed did not designate a valid friend.
-
-  | ErrFileSendChunkFriendNotConnected
-    -- This client is currently not connected to the friend.
-
-  | ErrFileSendChunkNotFound
-    -- No file transfer with the given file number was found for the given friend.
-
-  | ErrFileSendChunkNotTransferring
-    -- File transfer was found but isn't in a transferring state: (paused, done,
-    -- broken, etc...) (happens only when not called from the request chunk
-    -- callback).
-
-  | ErrFileSendChunkInvalidLength
-    -- Attempted to send more or less data than requested. The requested data
-    -- size is adjusted according to maximum transmission unit and the expected
-    -- end of the file. Trying to send less or more than requested will return
-    -- this error.
-
-  | ErrFileSendChunkSendq
-    -- Packet queue is full.
-
-  | ErrFileSendChunkWrongPosition
-    -- Position parameter was wrong.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Send a chunk of file data to a friend.
---
--- This function is called in response to the `file_chunk_request` callback.
--- The length parameter should be equal to the one received though the
--- callback.  If it is zero, the transfer is assumed complete. For files with
--- known size, Core will know that the transfer is complete after the last byte
--- has been received, so it is not necessary (though not harmful) to send a
--- zero-length chunk to terminate. For streams, core will know that the
--- transfer is finished if a chunk with length less than the length requested
--- in the callback is sent.
---
--- @param friend_number The friend number of the receiving friend for this file.
--- @param file_number The file transfer identifier returned by tox_file_send.
--- @param position The file or stream position from which to continue reading.
--- @return true on success.
-foreign import ccall tox_file_send_chunk :: Tox a -> Word32 -> Word32 -> Word64 -> CString -> CSize -> CErr ErrFileSendChunk -> IO Bool
-callFileSendChunk :: (Tox a -> Word32 -> Word32 -> Word64 -> CString -> CSize -> CErr ErrFileSendChunk -> IO Bool) ->
-                Tox a -> Word32 -> Word32 -> Word64 -> BS.ByteString ->  IO (Either ErrFileSendChunk Bool)
-callFileSendChunk f tox fn fileNum pos d =
-  BS.useAsCStringLen d $ \(dataPtr, dataLen) ->
-    callErrFun $ f tox fn fileNum pos dataPtr (fromIntegral dataLen)
-
-toxFileSendChunk :: Tox a -> Word32 -> Word32 -> Word64 -> BS.ByteString -> IO (Either ErrFileSendChunk Bool)
-toxFileSendChunk = callFileSendChunk tox_file_send_chunk
-
--- | If the length parameter is 0, the file transfer is finished, and the
--- client's resources associated with the file number should be released. After
--- a call with zero length, the file number can be reused for future file
--- transfers.
---
--- If the requested position is not equal to the client's idea of the current
--- file or stream position, it will need to seek. In case of read-once streams,
--- the client should keep the last read chunk so that a seek back can be
--- supported. A seek-back only ever needs to read from the last requested
--- chunk.  This happens when a chunk was requested, but the send failed. A
--- seek-back request can occur an arbitrary number of times for any given
--- chunk.
---
--- In response to receiving this callback, the client should call the function
--- `tox_file_send_chunk` with the requested chunk. If the number of bytes sent
--- through that function is zero, the file transfer is assumed complete. A
--- client must send the full length of data requested with this callback.
---
--- @param friend_number The friend number of the receiving friend for this file.
--- @param file_number The file transfer identifier returned by tox_file_send.
--- @param position The file or stream position from which to continue reading.
--- @param length The number of bytes requested for the current chunk.
-type FileChunkRequestCb a = Tox a -> Word32 -> Word32 -> Word64 -> CSize -> a -> IO a
-type CFileChunkRequestCb a = Tox a -> Word32 -> Word32 -> Word64 -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFileChunkRequestCb :: CFileChunkRequestCb a -> IO (FunPtr (CFileChunkRequestCb a))
-
-callFileChunkRequestCb :: FileChunkRequestCb a -> CFileChunkRequestCb a
-callFileChunkRequestCb f tox fn fileNum pos len = deRefStablePtr >=> (`modifyMVar_` f tox fn fileNum pos len)
-
-fileChunkRequestCb :: FileChunkRequestCb a -> IO (FunPtr (CFileChunkRequestCb a))
-fileChunkRequestCb = wrapFileChunkRequestCb . callFileChunkRequestCb
-
--- | Set the callback for the `file_chunk_request` event. Pass 'nullPtr' to
--- unset.
---
--- This event is triggered when Core is ready to send more file data.
-foreign import ccall tox_callback_file_chunk_request :: Tox a -> FunPtr (CFileChunkRequestCb a) -> IO ()
-
-
---------------------------------------------------------------------------------
---
--- :: File transmission: receiving
---
---------------------------------------------------------------------------------
-
-
--- | The client should acquire resources to be associated with the file
--- transfer.  Incoming file transfers start in the Paused state. After this
--- callback returns, a transfer can be rejected by sending a FileControlCancel
--- control command before any other control commands. It can be accepted by
--- sending FileControlResume.
---
--- @param friend_number The friend number of the friend who is sending the file
---   transfer request.
--- @param file_number The friend-specific file number the data received is
---   associated with.
--- @param kind The meaning of the file to be sent.
--- @param file_size Size in bytes of the file the client wants to send,
---   UINT64_MAX if unknown or streaming.
--- @param filename Name of the file. Does not need to be the actual name. This
---   name will be sent along with the file send request.
--- @param filename_length Size in bytes of the filename.
-type FileRecvCb a = Tox a -> Word32 -> Word32 -> FileKind -> Word64 -> String -> a -> IO a
-type CFileRecvCb a = Tox a -> Word32 -> Word32 -> CEnum FileKind -> Word64 -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFileRecvCb :: CFileRecvCb a -> IO (FunPtr (CFileRecvCb a))
-
-callFileRecvCb :: FileRecvCb a -> CFileRecvCb a
-callFileRecvCb f tox fn fileNum fileKind fileSize fileNameStr fileNameLen udPtr = do
-  ud <- deRefStablePtr udPtr
-  fileName <- peekCStringLen (fileNameStr, fromIntegral fileNameLen)
-  modifyMVar_ ud $ f tox fn fileNum (fromCEnum fileKind) fileSize fileName
-
-fileRecvCb :: FileRecvCb a -> IO (FunPtr (CFileRecvCb a))
-fileRecvCb = wrapFileRecvCb . callFileRecvCb
-
-
--- | Set the callback for the `file_recv` event. Pass 'nullPtr' to unset.
---
--- This event is triggered when a file transfer request is received.
-foreign import ccall tox_callback_file_recv :: Tox a -> FunPtr (CFileRecvCb a) -> IO ()
-
--- | When length is 0, the transfer is finished and the client should release
--- the resources it acquired for the transfer. After a call with length = 0,
--- the file number can be reused for new file transfers.
---
--- If position is equal to file_size (received in the file_receive callback)
--- when the transfer finishes, the file was received completely. Otherwise, if
--- file_size was UINT64_MAX, streaming ended successfully when length is 0.
---
--- @param friend_number The friend number of the friend who is sending the file.
--- @param file_number The friend-specific file number the data received is
---   associated with.
--- @param position The file position of the first byte in data.
--- @param data A byte array containing the received chunk.
--- @param length The length of the received chunk.
-type FileRecvChunkCb a = Tox a -> Word32 -> Word32 -> Word64 -> BS.ByteString -> a -> IO a
-type CFileRecvChunkCb a = Tox a -> Word32 -> Word32 -> Word64 -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFileRecvChunkCb :: CFileRecvChunkCb a -> IO (FunPtr (CFileRecvChunkCb a))
-
-callFileRecvChunkCb :: FileRecvChunkCb a -> CFileRecvChunkCb a
-callFileRecvChunkCb f tox fn fileNum pos dataPtr dataLen udPtr = do
-  ud <- deRefStablePtr udPtr
-  d <- BS.packCStringLen (dataPtr, fromIntegral dataLen)
-  modifyMVar_ ud $ f tox fn fileNum pos d
-
-fileRecvChunkCb :: FileRecvChunkCb a -> IO (FunPtr (CFileRecvChunkCb a))
-fileRecvChunkCb = wrapFileRecvChunkCb . callFileRecvChunkCb
-
-
--- | Set the callback for the `file_recv_chunk` event. Pass 'nullPtr' to unset.
---
--- This event is first triggered when a file transfer request is received, and
--- subsequently when a chunk of file data for an accepted request was received.
-foreign import ccall tox_callback_file_recv_chunk :: Tox a -> FunPtr (CFileRecvChunkCb a) -> IO ()
-
-
---------------------------------------------------------------------------------
---
--- :: Conference management
---
---------------------------------------------------------------------------------
-
-
--- | Conference types for the conference_invite event.
-data ConferenceType
-  = ConferenceTypeText
-    -- Text-only conferences that must be accepted with the tox_conference_join function.
-
-  | ConferenceTypeAv
-    -- Video conference. The function to accept these is in toxav.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | The invitation will remain valid until the inviting friend goes offline
--- or exits the conference.
---
--- @param friend_number The friend who invited us.
--- @param type The conference type (text only or audio/video).
--- @param cookie A piece of data of variable length required to join the
---   conference.
--- @param length The length of the cookie.
-type ConferenceInviteCb a = Tox a -> Word32 -> ConferenceType -> BS.ByteString -> a -> IO a
-type CConferenceInviteCb a = Tox a -> Word32 -> CEnum ConferenceType -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapConferenceInviteCb :: CConferenceInviteCb a -> IO (FunPtr (CConferenceInviteCb a))
-
-callConferenceInviteCb :: ConferenceInviteCb a -> CConferenceInviteCb a
-callConferenceInviteCb f tox fn confType cookiePtr cookieLen udPtr = do
-  ud <- deRefStablePtr udPtr
-  cookie <- BS.packCStringLen (cookiePtr, fromIntegral cookieLen)
-  modifyMVar_ ud $ f tox fn (fromCEnum confType) cookie
-
-conferenceInviteCb :: ConferenceInviteCb a -> IO (FunPtr (CConferenceInviteCb a))
-conferenceInviteCb = wrapConferenceInviteCb . callConferenceInviteCb
-
-
--- | Set the callback for the `conference_invite` event. Pass NULL to unset.
---
--- This event is triggered when the client is invited to join a conference.
-foreign import ccall tox_callback_conference_invite :: Tox a -> FunPtr (CConferenceInviteCb a) -> IO ()
-
-
--- | @param conference_number The conference number of the conference the message is intended for.
--- @param peer_number The ID of the peer who sent the message.
--- @param type The type of message (normal, action, ...).
--- @param message The message data.
--- @param length The length of the message.
-type ConferenceMessageCb a = Tox a -> Word32 -> Word32 -> MessageType -> String -> a -> IO a
-type CConferenceMessageCb a = Tox a -> Word32 -> Word32 -> CEnum MessageType -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapConferenceMessageCb :: CConferenceMessageCb a -> IO (FunPtr (CConferenceMessageCb a))
-
-callConferenceMessageCb :: ConferenceMessageCb a -> CConferenceMessageCb a
-callConferenceMessageCb f tox gn pn msgType msgStr msgLen udPtr = do
-  ud <- deRefStablePtr udPtr
-  msg <- peekCStringLen (msgStr, fromIntegral msgLen)
-  modifyMVar_ ud $ f tox gn pn (fromCEnum msgType) msg
-
-conferenceMessageCb :: ConferenceMessageCb a -> IO (FunPtr (CConferenceMessageCb a))
-conferenceMessageCb = wrapConferenceMessageCb . callConferenceMessageCb
-
-
--- | Set the callback for the `conference_message` event. Pass NULL to unset.
---
--- This event is triggered when the client receives a conference message.
-foreign import ccall tox_callback_conference_message :: Tox a -> FunPtr (CConferenceMessageCb a) -> IO ()
-
-
--- | @param conference_number The conference number of the conference the title change is intended for.
--- @param peer_number The ID of the peer who changed the title.
--- @param title The title data.
--- @param length The title length.
-type ConferenceTitleCb a = Tox a -> Word32 -> Word32 -> String -> a -> IO a
-type CConferenceTitleCb a = Tox a -> Word32 -> Word32 -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapConferenceTitleCb :: CConferenceTitleCb a -> IO (FunPtr (CConferenceTitleCb a))
-
-callConferenceTitleCb :: ConferenceTitleCb a -> CConferenceTitleCb a
-callConferenceTitleCb f tox gn pn titleStr titleLen udPtr = do
-  ud <- deRefStablePtr udPtr
-  title <- peekCStringLen (titleStr, fromIntegral titleLen)
-  modifyMVar_ ud $ f tox gn pn title
-
-conferenceTitleCb :: ConferenceTitleCb a -> IO (FunPtr (CConferenceTitleCb a))
-conferenceTitleCb = wrapConferenceTitleCb . callConferenceTitleCb
-
-
--- | Set the callback for the `conference_title` event. Pass NULL to unset.
---
--- This event is triggered when a peer changes the conference title.
---
--- If peer_number == UINT32_MAX, then author is unknown (e.g. initial joining the conference).
-foreign import ccall tox_callback_conference_title :: Tox a -> FunPtr (CConferenceTitleCb a) -> IO ()
-
-
--- | @param conference_number The conference number of the conference the peer is in.
--- @param peer_number The ID of the peer who changed their name.
--- @param name The new name of the peer.
-type ConferencePeerNameCb a = Tox a -> Word32 -> Word32 -> String -> a -> IO a
-type CConferencePeerNameCb a = Tox a -> Word32 -> Word32 -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapConferencePeerNameCb :: CConferencePeerNameCb a -> IO (FunPtr (CConferencePeerNameCb a))
-
-callConferencePeerNameCb :: ConferencePeerNameCb a -> CConferencePeerNameCb a
-callConferencePeerNameCb f tox gn pn nameStr nameLen udPtr = do
-  ud <- deRefStablePtr udPtr
-  name <- peekCStringLen (nameStr, fromIntegral nameLen)
-  modifyMVar_ ud $ f tox gn pn name
-
-conferencePeerNameCb :: ConferencePeerNameCb a -> IO (FunPtr (CConferencePeerNameCb a))
-conferencePeerNameCb = wrapConferencePeerNameCb . callConferencePeerNameCb
-
-
--- | Set the callback for the `conference_peer_name` event. Pass NULL to unset.
---
--- This event is triggered when the peer changes their nickname.
-foreign import ccall tox_callback_conference_peer_name :: Tox a -> FunPtr (CConferencePeerNameCb a) -> IO ()
-
-
--- | @param conference_number The conference number of the conference for which the peer list changed.
-type ConferencePeerListChangedCb a = Tox a -> Word32 -> a -> IO a
-type CConferencePeerListChangedCb a = Tox a -> Word32 -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapConferencePeerListChangedCb :: CConferencePeerListChangedCb a -> IO (FunPtr (CConferencePeerListChangedCb a))
-
-callConferencePeerListChangedCb :: ConferencePeerListChangedCb a -> CConferencePeerListChangedCb a
-callConferencePeerListChangedCb f tox gn = deRefStablePtr >=> (`modifyMVar_` f tox gn)
-
-conferencePeerListChangedCb :: ConferencePeerListChangedCb a -> IO (FunPtr (CConferencePeerListChangedCb a))
-conferencePeerListChangedCb = wrapConferencePeerListChangedCb . callConferencePeerListChangedCb
-
-
--- | Set the callback for the `conference_peer_list_changed` event. Pass NULL to unset.
---
--- This event is triggered when the peer list changes (peer join, peer exit).
-foreign import ccall tox_callback_conference_peer_list_changed :: Tox a -> FunPtr (CConferencePeerListChangedCb a) -> IO ()
-
-
-data ErrConferenceNew
-  = ErrConferenceNewOk
-    -- The function returned successfully.
-
-  | ErrConferenceNewInit
-    -- The conference instance failed to initialize.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Creates a new conference.
---
--- This function creates a new text conference.
---
--- @return conference number on success, or UINT32_MAX on failure.
-foreign import ccall tox_conference_new :: Tox a -> CErr ErrConferenceNew -> IO Word32
-callConferenceNew :: (Tox a -> CErr ErrConferenceNew -> IO Word32) ->
-                     Tox a -> IO (Either ErrConferenceNew Word32)
-callConferenceNew f tox = callErrFun $ f tox
-
-toxConferenceNew :: Tox a -> IO (Either ErrConferenceNew Word32)
-toxConferenceNew = callConferenceNew tox_conference_new
-
-
-data ErrConferenceDelete
-  = ErrConferenceDeleteOk
-    -- The function returned successfully.
-
-  | ErrConferenceDeleteConferenceNotFound
-    -- The conference number passed did not designate a valid conference.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | This function deletes a conference.
---
--- @param conference_number The conference number of the conference to be deleted.
---
--- @return true on success.
-foreign import ccall tox_conference_delete :: Tox a -> Word32 -> CErr ErrConferenceDelete -> IO Bool
-callConferenceDelete :: (Tox a -> Word32 -> CErr ErrConferenceDelete -> IO Bool) ->
-                        Tox a -> Word32 -> IO (Either ErrConferenceDelete Bool)
-callConferenceDelete f tox gn = callErrFun $ f tox gn
-
-toxConferenceDelete :: Tox a -> Word32 -> IO (Either ErrConferenceDelete Bool)
-toxConferenceDelete = callConferenceDelete tox_conference_delete
-
-
--- | Error codes for peer info queries.
-data ErrConferencePeerQuery
-  = ErrConferencePeerQueryOk
-    -- The function returned successfully.
-
-  | ErrConferencePeerQueryConferenceNotFound
-    -- The conference number passed did not designate a valid conference.
-
-  | ErrConferencePeerQueryPeerNotFound
-    -- The peer number passed did not designate a valid peer.
-
-  | ErrConferencePeerQueryNoConnection
-    -- The client is not connected to the conference.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
-
--- | Return the number of peers in the conference. Return value is unspecified on failure.
-foreign import ccall tox_conference_peer_count :: Tox a -> Word32 -> CErr ErrConferencePeerQuery -> IO Word32
-callConferencePeerCount :: (Tox a -> Word32 -> CErr ErrConferencePeerQuery -> IO Word32) ->
-                           Tox a -> Word32 -> IO (Either ErrConferencePeerQuery Word32)
-callConferencePeerCount f tox gn = callErrFun $ f tox gn
-
-toxConferencePeerCount :: Tox a -> Word32 -> IO (Either ErrConferencePeerQuery Word32)
-toxConferencePeerCount = callConferencePeerCount tox_conference_peer_count
-
-
--- | Return the length of the peer's name. Return value is unspecified on failure.
-foreign import ccall tox_conference_peer_get_name_size :: Tox a -> Word32 -> Word32 -> CErr ErrConferencePeerQuery -> IO CSize
-
-
--- | Copy the name of peer_number who is in conference_number to name.
--- name must be at least TOX_MAX_NAME_LENGTH long.
---
--- @return true on success.
-foreign import ccall tox_conference_peer_get_name :: Tox a -> Word32 -> Word32 -> CString -> CErr ErrConferencePeerQuery -> IO Bool
-
-toxConferencePeerGetName :: Tox a -> Word32 -> Word32 -> IO (Either ErrConferencePeerQuery String)
-toxConferencePeerGetName tox gn pn = do
-  nameLenRes <- callErrFun $ tox_conference_peer_get_name_size tox gn pn
-  case nameLenRes of
-    Left err -> return $ Left err
-    Right nameLen -> allocaArray (fromIntegral nameLen) $ \namePtr -> do
-      nameRes <- callErrFun $ tox_conference_peer_get_name tox gn pn namePtr
-      case nameRes of
-        Left err -> return $ Left err
-        Right _ ->
-          Right <$> peekCStringLen (namePtr, fromIntegral nameLen)
-
-
--- | Copy the public key of peer_number who is in conference_number to public_key.
--- public_key must be TOX_PUBLIC_KEY_SIZE long.
---
--- @return true on success.
-foreign import ccall tox_conference_peer_get_public_key :: Tox a -> Word32 -> Word32 -> CString -> CErr ErrConferencePeerQuery -> IO Bool
-callConferencePeerGetPublicKey :: (Tox a -> Word32 -> Word32 -> CString -> CErr ErrConferencePeerQuery -> IO Bool) ->
-                          Tox a -> Word32 -> Word32 -> IO (Either ErrConferencePeerQuery BS.ByteString)
-callConferencePeerGetPublicKey f tox gn pn =
-  let pkLen = fromIntegral tox_public_key_size in
-  alloca $ \errPtr ->
-    allocaArray pkLen $ \pkPtr -> do
-      _ <- f tox gn pn pkPtr errPtr
-      callGetPublicKey errPtr pkPtr pkLen
-
-toxConferencePeerGetPublicKey :: Tox a -> Word32 -> Word32 -> IO (Either ErrConferencePeerQuery BS.ByteString)
-toxConferencePeerGetPublicKey = callConferencePeerGetPublicKey tox_conference_peer_get_public_key
-
-
--- | Return true if passed peer_number corresponds to our own.
-foreign import ccall tox_conference_peer_number_is_ours :: Tox a -> Word32 -> Word32 -> CErr ErrConferencePeerQuery -> IO Bool
-callConferencePeerNumberIsOurs :: (Tox a -> Word32 -> Word32 -> CErr ErrConferencePeerQuery -> IO Bool) ->
-                                  Tox a -> Word32 -> Word32 -> IO (Either ErrConferencePeerQuery Bool)
-callConferencePeerNumberIsOurs f tox gn pn = callErrFun $ f tox gn pn
-
-toxConferencePeerNumberIsOurs :: Tox a -> Word32 -> Word32 -> IO (Either ErrConferencePeerQuery Bool)
-toxConferencePeerNumberIsOurs = callConferencePeerNumberIsOurs tox_conference_peer_number_is_ours
-
-
-data ErrConferenceInvite
-  = ErrConferenceInviteOk
-    -- The function returned successfully.
-
-  | ErrConferenceInviteConferenceNotFound
-    -- The conference number passed did not designate a valid conference.
-
-  | ErrConferenceInviteFailSend
-    -- The invite packet failed to send.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
-
--- | Invites a friend to a conference.
---
--- @param friend_number The friend number of the friend we want to invite.
--- @param conference_number The conference number of the conference we want to invite the friend to.
---
--- @return true on success.
-foreign import ccall tox_conference_invite :: Tox a -> Word32 -> Word32 -> CErr ErrConferenceInvite -> IO Bool
-callConferenceInvite :: (Tox a -> Word32 -> Word32 -> CErr ErrConferenceInvite -> IO Bool) ->
-                        Tox a -> Word32 -> Word32 -> IO (Either ErrConferenceInvite Bool)
-callConferenceInvite f tox fn gn = callErrFun $ f tox fn gn
-
-toxConferenceInvite :: Tox a -> Word32 -> Word32 -> IO (Either ErrConferenceInvite Bool)
-toxConferenceInvite = callConferenceInvite tox_conference_invite
-
-
-data ErrConferenceJoin
-  = ErrConferenceJoinOk
-    -- The function returned successfully.
-
-  | ErrConferenceJoinInvalidLength
-    -- The cookie passed has an invalid length.
-
-  | ErrConferenceJoinWrongType
-    -- The conference is not the expected type. This indicates an invalid cookie.
-
-  | ErrConferenceJoinFriendNotFound
-    -- The friend number passed does not designate a valid friend.
-
-  | ErrConferenceJoinDuplicate
-    -- Client is already in this conference.
-
-  | ErrConferenceJoinInitFail
-    -- Conference instance failed to initialize.
-
-  | ErrConferenceJoinFailSend
-    -- The join packet failed to send.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
-
--- | Joins a conference that the client has been invited to.
---
--- @param friend_number The friend number of the friend who sent the invite.
--- @param cookie Received via the `conference_invite` event.
--- @param length The size of cookie.
---
--- @return conference number on success, UINT32_MAX on failure.
-foreign import ccall tox_conference_join :: Tox a -> Word32 -> CString -> CSize -> CErr ErrConferenceJoin -> IO Word32
-callConferenceJoin :: (Tox a -> Word32 -> CString -> CSize -> CErr ErrConferenceJoin -> IO Word32) ->
-                        Tox a -> Word32 -> BS.ByteString -> IO (Either ErrConferenceJoin Word32)
-callConferenceJoin f tox fn cookie =
-  BS.useAsCStringLen cookie $ \(cookiePtr, cookieLen) ->
-    callErrFun $ f tox fn cookiePtr (fromIntegral cookieLen)
-
-toxConferenceJoin :: Tox a -> Word32 -> BS.ByteString -> IO (Either ErrConferenceJoin Word32)
-toxConferenceJoin = callConferenceJoin tox_conference_join
-
-
-data ErrConferenceSendMessage
-  = ErrConferenceSendMessageOk
-    -- The function returned successfully.
-
-  | ErrConferenceSendMessageConferenceNotFound
-    -- The conference number passed did not designate a valid conference.
-
-  | ErrConferenceSendMessageTooLong
-    -- The message is too long.
-
-  | ErrConferenceSendMessageNoConnection
-    -- The client is not connected to the conference.
-
-  | ErrConferenceSendMessageFailSend
-    -- The message packet failed to send.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
-
--- | Send a text chat message to the conference.
---
--- This function creates a conference message packet and pushes it into the send
--- queue.
---
--- The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages
--- must be split by the client and sent as separate messages. Other clients can
--- then reassemble the fragments.
---
--- @param conference_number The conference number of the conference the message is intended for.
--- @param type Message type (normal, action, ...).
--- @param message A non-NULL pointer to the first element of a byte array
---   containing the message text.
--- @param length Length of the message to be sent.
---
--- @return true on success.
-foreign import ccall tox_conference_send_message :: Tox a -> Word32 -> CEnum MessageType -> CString -> CSize -> CErr ErrConferenceSendMessage -> IO Bool
-callConferenceSendMessage :: (Tox a -> Word32 -> CEnum MessageType -> CString -> CSize -> CErr ErrConferenceSendMessage -> IO Bool) ->
-                        Tox a -> Word32 -> MessageType -> String -> IO (Either ErrConferenceSendMessage Bool)
-callConferenceSendMessage f tox gn messageType message =
-  withCStringLen message $ \(msgPtr, msgLen) ->
-    callErrFun $ f tox gn (toCEnum messageType) msgPtr (fromIntegral msgLen)
-
-toxConferenceSendMessage :: Tox a -> Word32 -> MessageType -> String -> IO (Either ErrConferenceSendMessage Bool)
-toxConferenceSendMessage = callConferenceSendMessage tox_conference_send_message
-
-
-data ErrConferenceTitle
-  = ErrConferenceTitleOk
-    -- The function returned successfully.
-
-  | ErrConferenceTitleConferenceNotFound
-    -- The conference number passed did not designate a valid conference.
-
-  | ErrConferenceTitleInvalidLength
-    -- The title is too long or empty.
-
-  | ErrConferenceTitleFailSend
-    -- The title packet failed to send.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
-
--- | Return the length of the conference title. Return value is unspecified on failure.
---
--- The return value is equal to the `length` argument received by the last
--- `conference_title` callback.
-foreign import ccall tox_conference_get_title_size :: Tox a -> Word32 -> CErr ErrConferenceTitle -> IO CSize
-
-
--- | Write the title designated by the given conference number to a byte array.
---
--- Call tox_conference_get_title_size to determine the allocation size for the `title` parameter.
---
--- The data written to `title` is equal to the data received by the last
--- `conference_title` callback.
---
--- @param title A valid memory region large enough to store the title.
---   If this parameter is NULL, this function has no effect.
---
--- @return true on success.
-foreign import ccall tox_conference_get_title :: Tox a -> Word32 -> CString -> CErr ErrConferenceTitle -> IO Bool
-
-toxConferenceGetTitle :: Tox a -> Word32 -> IO (Either ErrConferenceTitle String)
-toxConferenceGetTitle tox gn = do
-  titleLenRes <- callErrFun $ tox_conference_get_title_size tox gn
-  case titleLenRes of
-    Left err -> return $ Left err
-    Right titleLen -> allocaArray (fromIntegral titleLen) $ \titlePtr -> do
-      titleRes <- callErrFun $ tox_conference_get_title tox gn titlePtr
-      case titleRes of
-        Left err -> return $ Left err
-        Right _ ->
-          Right <$> peekCStringLen (titlePtr, fromIntegral titleLen)
-
--- | Set the conference title and broadcast it to the rest of the conference.
---
--- Title length cannot be longer than TOX_MAX_NAME_LENGTH.
---
--- @return true on success.
-foreign import ccall tox_conference_set_title :: Tox a -> Word32 -> CString -> CSize -> CErr ErrConferenceTitle -> IO Bool
-callConferenceSetTitle :: (Tox a -> Word32 -> CString -> CSize -> CErr ErrConferenceTitle -> IO Bool) ->
-                        Tox a -> Word32 -> String -> IO (Either ErrConferenceTitle Bool)
-callConferenceSetTitle f tox gn title =
-  withCStringLen title $ \(titlePtr, titleLen) ->
-    callErrFun $ f tox gn titlePtr (fromIntegral titleLen)
-
-toxConferenceSetTitle :: Tox a -> Word32 -> String -> IO (Either ErrConferenceTitle Bool)
-toxConferenceSetTitle = callConferenceSetTitle tox_conference_set_title
-
-
--- | Return the number of conferences in the Tox instance.
--- This should be used to determine how much memory to allocate for `tox_conference_get_chatlist`.
-foreign import ccall tox_conference_get_chatlist_size :: Tox a -> IO CSize
-
-
--- | Copy a list of valid conference IDs into the array chatlist. Determine how much space
--- to allocate for the array with the `tox_conference_get_chatlist_size` function.
-foreign import ccall tox_conference_get_chatlist :: Tox a -> Ptr Word32 -> IO ()
-
-toxConferenceGetChatlist :: Tox a -> IO [Word32]
-toxConferenceGetChatlist tox = do
-  chatListSize <- tox_conference_get_chatlist_size tox
-  allocaArray (fromIntegral chatListSize) $ \chatListPtr -> do
-    tox_conference_get_chatlist tox chatListPtr
-    peekArray (fromIntegral chatListSize) chatListPtr
-
--- | Returns the type of conference (TOX_CONFERENCE_TYPE) that conference_number is. Return value is
--- unspecified on failure.
-data ErrConferenceGetType
-  = ErrConferenceGetTypeOk
-    -- The function returned successfully.
-
-  | ErrConferenceGetTypeConferenceNotFound
-    -- The conference number passed did not designate a valid conference.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-foreign import ccall tox_conference_get_type :: Tox a -> Word32 -> CErr ErrConferenceGetType -> IO (CEnum ConferenceType)
-callConferenceGetType :: (Tox a -> Word32 -> CErr ErrConferenceGetType -> IO (CEnum ConferenceType)) ->
-                         Tox a -> Word32 -> IO (Either ErrConferenceGetType ConferenceType)
-callConferenceGetType f tox gn = callErrFun (f tox gn >=> (return . fromCEnum))
-
-toxConferenceGetType :: Tox a -> Word32 -> IO (Either ErrConferenceGetType ConferenceType)
-toxConferenceGetType = callConferenceGetType tox_conference_get_type
-
-
---------------------------------------------------------------------------------
---
--- :: Low-level custom packet sending and receiving
---
---------------------------------------------------------------------------------
-
-
-data ErrFriendCustomPacket
-  = ErrFriendCustomPacketOk
-    -- The function returned successfully.
-
-  | ErrFriendCustomPacketNull
-    -- One of the arguments to the function was 'nullPtr' when it was not
-    -- expected.
-
-  | ErrFriendCustomPacketFriendNotFound
-    -- The friend number did not designate a valid friend.
-
-  | ErrFriendCustomPacketFriendNotConnected
-    -- This client is currently not connected to the friend.
-
-  | ErrFriendCustomPacketInvalid
-    -- The first byte of data was not in the specified range for the packet
-    -- type. This range is 200-254 for lossy, and 160-191 for lossless packets.
-
-  | ErrFriendCustomPacketEmpty
-    -- Attempted to send an empty packet.
-
-  | ErrFriendCustomPacketTooLong
-    -- Packet data length exceeded 'tox_max_custom_packet_size'.
-
-  | ErrFriendCustomPacketSendq
-    -- Packet queue is full.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Send a custom lossy packet to a friend.
---
--- The first byte of data must be in the range 200-254. Maximum length of a
--- custom packet is 'tox_max_custom_packet_size'.
---
--- Lossy packets behave like UDP packets, meaning they might never reach the
--- other side or might arrive more than once (if someone is messing with the
--- connection) or might arrive in the wrong order.
---
--- Unless latency is an issue, it is recommended that you use lossless custom
--- packets instead.
---
--- @param friend_number The friend number of the friend this lossy packet
---   should be sent to.
--- @param data A byte array containing the packet data.
--- @param length The length of the packet data byte array.
---
--- @return true on success.
-foreign import ccall tox_friend_send_lossy_packet :: Tox a -> Word32 -> CString -> CSize -> CErr ErrFriendCustomPacket -> IO Bool
-callFriendLossyPacket :: (Tox a -> Word32 -> CString -> CSize -> CErr ErrFriendCustomPacket -> IO Bool) ->
-                         Tox a -> Word32 -> BS.ByteString -> IO (Either ErrFriendCustomPacket Bool)
-callFriendLossyPacket f tox fn d =
-  BS.useAsCStringLen d $ \(dataPtr, dataLen) ->
-    callErrFun $ f tox fn dataPtr (fromIntegral dataLen)
-
-toxFriendLossyPacket :: Tox a -> Word32 -> BS.ByteString -> IO (Either ErrFriendCustomPacket Bool)
-toxFriendLossyPacket = callFriendLossyPacket tox_friend_send_lossy_packet
-
--- | Send a custom lossless packet to a friend.
---
--- The first byte of data must be in the range 160-191. Maximum length of a
--- custom packet is 'tox_max_custom_packet_size'.
---
--- Lossless packet behaviour is comparable to TCP (reliability, arrive in order)
--- but with packets instead of a stream.
---
--- @param friend_number The friend number of the friend this lossless packet
---   should be sent to.
--- @param data A byte array containing the packet data.
--- @param length The length of the packet data byte array.
---
--- @return true on success.
-foreign import ccall tox_friend_send_lossless_packet :: Tox a -> Word32 -> CString -> CSize -> CErr ErrFriendCustomPacket -> IO Bool
-callFriendLosslessPacket :: (Tox a -> Word32 -> CString -> CSize -> CErr ErrFriendCustomPacket -> IO Bool) ->
-                         Tox a -> Word32 -> BS.ByteString -> IO (Either ErrFriendCustomPacket Bool)
-callFriendLosslessPacket f tox fn d =
-  BS.useAsCStringLen d $ \(dataPtr, dataLen) ->
-    callErrFun $ f tox fn dataPtr (fromIntegral dataLen)
-
-toxFriendLosslessPacket :: Tox a -> Word32 -> BS.ByteString -> IO (Either ErrFriendCustomPacket Bool)
-toxFriendLosslessPacket = callFriendLosslessPacket tox_friend_send_lossless_packet
-
-callFriendCustomPacketCb :: FriendLosslessPacketCb a -> CFriendLosslessPacketCb a
-callFriendCustomPacketCb f tox fn dataPtr dataLen udPtr = do
-  ud <- deRefStablePtr udPtr
-  d <- BS.packCStringLen (dataPtr, fromIntegral dataLen)
-  modifyMVar_ ud $ f tox fn d
-
--- | @param friend_number The friend number of the friend who sent a lossy
--- packet.
--- @param data A byte array containing the received packet data.
--- @param length The length of the packet data byte array.
-type FriendLossyPacketCb a = Tox a -> Word32 -> BS.ByteString -> a -> IO a
-type CFriendLossyPacketCb a = Tox a -> Word32 -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFriendLossyPacketCb :: CFriendLossyPacketCb a -> IO (FunPtr (CFriendLossyPacketCb a))
-
-callFriendLossyPacketCb :: FriendLossyPacketCb a -> CFriendLossyPacketCb a
-callFriendLossyPacketCb = callFriendCustomPacketCb
-
-friendLossyPacketCb :: FriendLossyPacketCb a -> IO (FunPtr (CFriendLossyPacketCb a))
-friendLossyPacketCb = wrapFriendLossyPacketCb . callFriendLossyPacketCb
-
-
--- | Set the callback for the `friend_lossy_packet` event. Pass 'nullPtr' to
--- unset.
---
-foreign import ccall tox_callback_friend_lossy_packet :: Tox a -> FunPtr (CFriendLossyPacketCb a) -> IO ()
-
--- | @param friend_number The friend number of the friend who sent the packet.
--- @param data A byte array containing the received packet data.
--- @param length The length of the packet data byte array.
-type FriendLosslessPacketCb a = Tox a -> Word32 -> BS.ByteString -> a -> IO a
-type CFriendLosslessPacketCb a = Tox a -> Word32 -> CString -> CSize -> UserData a -> IO ()
-foreign import ccall "wrapper" wrapFriendLosslessPacketCb :: CFriendLosslessPacketCb a -> IO (FunPtr (CFriendLosslessPacketCb a))
-
-callFriendLosslessPacketCb :: FriendLosslessPacketCb a -> CFriendLosslessPacketCb a
-callFriendLosslessPacketCb = callFriendCustomPacketCb
-
-friendLosslessPacketCb :: FriendLosslessPacketCb a -> IO (FunPtr (CFriendLosslessPacketCb a))
-friendLosslessPacketCb = wrapFriendLosslessPacketCb . callFriendLosslessPacketCb
-
-
--- | Set the callback for the `friend_lossless_packet` event. Pass 'nullPtr' to
--- unset.
---
-foreign import ccall tox_callback_friend_lossless_packet :: Tox a -> FunPtr (CFriendLosslessPacketCb a) -> IO ()
-
-
---------------------------------------------------------------------------------
---
--- :: Low-level network information
---
---------------------------------------------------------------------------------
-
-
--- | Writes the temporary DHT public key of this instance to a byte array.
---
--- This can be used in combination with an externally accessible IP address and
--- the bound port (from tox_self_get_udp_port) to run a temporary bootstrap
--- node.
---
--- Be aware that every time a new instance is created, the DHT public key
--- changes, meaning this cannot be used to run a permanent bootstrap node.
---
--- @param dht_id A memory region of at least 'tox_public_key_size' bytes. If
---   this parameter is 'nullPtr', this function has no effect.
-foreign import ccall tox_self_get_dht_id :: Tox a -> CString -> IO ()
-
-toxSelfGetDhtId :: Tox a -> IO BS.ByteString
-toxSelfGetDhtId tox =
-  let idLen = fromIntegral tox_public_key_size in
-  allocaArray idLen $ \idPtr -> do
-    tox_self_get_dht_id tox idPtr
-    BS.packCStringLen (idPtr, idLen)
-
-data ErrGetPort
-  = ErrGetPortOk
-    -- The function returned successfully.
-
-  | ErrGetPortNotBound
-    -- The instance was not bound to any port.
-  deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
-
--- | Return the UDP port this Tox instance is bound to.
-foreign import ccall tox_self_get_udp_port :: Tox a -> CErr ErrGetPort -> IO Word16
-callSelfGetUdpPort :: (Tox a -> CErr ErrGetPort -> IO Word16) ->
-                        Tox a -> IO (Either ErrGetPort Word16)
-callSelfGetUdpPort f tox = callErrFun $ f tox
-
-toxSelfGetUdpPort :: Tox a -> IO (Either ErrGetPort Word16)
-toxSelfGetUdpPort = callSelfGetUdpPort tox_self_get_udp_port
-
--- | Return the TCP port this Tox instance is bound to. This is only relevant if
--- the instance is acting as a TCP relay.
-foreign import ccall tox_self_get_tcp_port :: Tox a -> CErr ErrGetPort -> IO Word16
-callSelfGetTcpPort :: (Tox a -> CErr ErrGetPort -> IO Word16) ->
-                        Tox a -> IO (Either ErrGetPort Word16)
-callSelfGetTcpPort f tox = callErrFun $ f tox
-
-toxSelfGetTcpPort :: Tox a -> IO (Either ErrGetPort Word16)
-toxSelfGetTcpPort = callSelfGetTcpPort tox_self_get_udp_port
diff --git a/src/Network/Tox/C/Type.hs b/src/Network/Tox/C/Type.hs
deleted file mode 100644
--- a/src/Network/Tox/C/Type.hs
+++ /dev/null
@@ -1,17 +0,0 @@
-{-# LANGUAGE Safe #-}
-module Network.Tox.C.Type where
-
-import           Control.Concurrent.MVar (MVar)
-import           Foreign.Ptr             (Ptr)
-import           Foreign.StablePtr       (StablePtr)
-
-
--- | The Tox instance type. All the state associated with a connection is held
--- within the instance. Multiple instances can exist and operate concurrently.
--- The maximum number of Tox instances that can exist on a single network device
--- is limited. Note that this is not just a per-process limit, since the
--- limiting factor is the number of usable ports on a device.
-data ToxStruct a
-type Tox a = Ptr (ToxStruct a)
-
-type UserData a = StablePtr (MVar a)
diff --git a/src/Network/Tox/C/Version.hs b/src/Network/Tox/C/Version.hs
deleted file mode 100644
--- a/src/Network/Tox/C/Version.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-{-# LANGUAGE Trustworthy #-}
-module Network.Tox.C.Version where
-
-import           Data.Word (Word32)
-
---------------------------------------------------------------------------------
---
--- :: API version
---
---------------------------------------------------------------------------------
-
--- | The major version number. Incremented when the API or ABI changes in an
--- incompatible way.
-foreign import ccall tox_version_major :: Word32
-
--- | The minor version number. Incremented when functionality is added without
--- breaking the API or ABI. Set to 0 when the major version number is
--- incremented.
-foreign import ccall tox_version_minor :: Word32
-
--- | The patch or revision number. Incremented when bugfixes are applied without
--- changing any functionality or API or ABI.
-foreign import ccall tox_version_patch :: Word32
-
--- | Return whether the compiled library version is compatible with the passed
--- version numbers.
-foreign import ccall tox_version_is_compatible :: Word32 -> Word32 -> Word32 -> Bool
diff --git a/src/Network/Tox/Crypto.lhs b/src/Network/Tox/Crypto.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Crypto.lhs
@@ -0,0 +1,16 @@
+\chapter{Crypto}
+
+\begin{code}
+{-# LANGUAGE Safe #-}
+module Network.Tox.Crypto where
+\end{code}
+
+The Crypto module contains all the functions and data types related to
+cryptography.  This includes random number generation, encryption and
+decryption, key generation, operations on nonces and generating random nonces.
+
+\input{src/Network/Tox/Crypto/Key.lhs}
+\input{src/Network/Tox/Crypto/KeyPair.lhs}
+\input{src/Network/Tox/Crypto/CombinedKey.lhs}
+\input{src/Network/Tox/Crypto/Nonce.lhs}
+\input{src/Network/Tox/Crypto/Box.lhs}
diff --git a/src/Network/Tox/Crypto/Box.lhs b/src/Network/Tox/Crypto/Box.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Crypto/Box.lhs
@@ -0,0 +1,184 @@
+\section{Box}
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable         #-}
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE LambdaCase                 #-}
+{-# LANGUAGE OverloadedStrings          #-}
+{-# LANGUAGE Trustworthy                #-}
+module Network.Tox.Crypto.Box
+  ( PlainText (..)
+  , CipherText
+  , cipherText
+  , unCipherText
+  , decode
+  , encode
+  , decrypt, decryptR
+  , encrypt, encryptR
+  ) where
+
+import           Control.Applicative               ((<$>), (<*>))
+import qualified Crypto.Saltine.Core.Box           as Sodium (boxAfterNM,
+                                                              boxOpenAfterNM)
+import qualified Crypto.Saltine.Internal.ByteSizes as ByteSizes
+import           Data.Binary                       (Binary, get, put)
+import           Data.Binary.Get                   (Decoder (..), pushChunk,
+                                                    runGetIncremental)
+import           Data.Binary.Put                   (runPut)
+import           Data.ByteString                   (ByteString)
+import qualified Data.ByteString                   as ByteString
+import qualified Data.ByteString.Base16            as Base16
+import qualified Data.ByteString.Lazy              as LazyByteString
+import           Data.MessagePack                  (MessagePack (..))
+import           Data.Typeable                     (Typeable)
+import           GHC.Generics                      (Generic)
+import           Network.MessagePack.Rpc           (Doc (..))
+import qualified Network.MessagePack.Rpc           as Rpc
+import           Test.QuickCheck.Arbitrary         (Arbitrary, arbitrary)
+import           Text.Read                         (readPrec)
+
+import           Network.Tox.Crypto.Key            (CombinedKey, Key (..),
+                                                    Nonce)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+\end{code}
+
+The Tox protocol differentiates between two types of text: Plain Text and
+Cipher Text.  Cipher Text may be transmitted over untrusted data channels.
+Plain Text can be Sensitive or Non Sensitive.  Sensitive Plain Text must be
+transformed into Cipher Text using the encryption function before it can be
+transmitted over untrusted data channels.
+
+\begin{code}
+
+
+newtype PlainText = PlainText { unPlainText :: ByteString }
+  deriving (Eq, Binary, Generic, Typeable)
+
+instance MessagePack PlainText
+
+instance Show PlainText where
+  show = show . Base16.encode . unPlainText
+
+instance Read PlainText where
+  readPrec = PlainText . fst . Base16.decode <$> readPrec
+
+
+newtype CipherText = CipherText { unCipherText :: ByteString }
+  deriving (Eq, Typeable)
+
+cipherText :: Monad m => ByteString -> m CipherText
+cipherText bs
+  | ByteString.length bs >= ByteSizes.boxMac = return $ CipherText bs
+  | otherwise                                = fail "ciphertext is too short"
+
+instance Binary CipherText where
+  put = put . unCipherText
+  get = get >>= cipherText
+
+instance MessagePack CipherText where
+  toObject = toObject . unCipherText
+  fromObject x = do
+    bs <- fromObject x
+    cipherText bs
+
+instance Show CipherText where
+  show = show . Base16.encode . unCipherText
+
+instance Read CipherText where
+  readPrec = fst . Base16.decode <$> readPrec >>= cipherText
+
+
+encode :: Binary a => a -> PlainText
+encode =
+  PlainText . LazyByteString.toStrict . runPut . put
+
+
+decode :: (Monad m, Binary a) => PlainText -> m a
+decode (PlainText bytes) =
+  finish $ pushChunk (runGetIncremental get) bytes
+  where
+    finish = \case
+      Done _ _ output -> return output
+      Fail _ _ msg    -> fail msg
+      Partial f       -> finish $ f Nothing
+
+
+\end{code}
+
+The encryption function takes a Combined Key, a Nonce, and a Plain Text, and
+returns a Cipher Text.  It uses \texttt{crypto\_box\_afternm} to perform the
+encryption.  The meaning of the sentence "encrypting with a secret key, a
+public key, and a nonce" is: compute a combined key from the secret key and the
+public key and then use the encryption function for the transformation.
+
+\begin{code}
+
+encrypt :: CombinedKey -> Nonce -> PlainText -> CipherText
+encrypt (Key ck) (Key nonce) (PlainText bytes) =
+  CipherText $ Sodium.boxAfterNM ck nonce bytes
+
+encryptR :: Rpc.Rpc (CombinedKey -> Nonce -> PlainText -> Rpc.Returns CipherText)
+encryptR =
+  Rpc.stubs "Box.encrypt"
+    (Arg "key" $ Arg "nonce" $ Arg "plain" $ Ret "encrypted")
+    encrypt
+
+\end{code}
+
+The decryption function takes a Combined Key, a Nonce, and a Cipher Text, and
+returns either a Plain Text or an error.  It uses
+\texttt{crypto\_box\_open\_afternm} from the NaCl library.  Since the cipher is
+symmetric, the encryption function can also perform decryption, but will not
+perform message authentication, so the implementation must be careful to use
+the correct functions.
+
+\begin{code}
+
+decrypt :: CombinedKey -> Nonce -> CipherText -> Maybe PlainText
+decrypt (Key ck) (Key nonce) (CipherText bytes) =
+  PlainText <$> Sodium.boxOpenAfterNM ck nonce bytes
+
+decryptR :: Rpc.Rpc (CombinedKey -> Nonce -> CipherText -> Rpc.Returns (Maybe PlainText))
+decryptR =
+  Rpc.stubs "Box.decrypt"
+    (Arg "key" $ Arg "nonce" $ Arg "encrypted" $ Ret "plain")
+    decrypt
+
+\end{code}
+
+\texttt{crypto\_box} uses xsalsa20 symmetric encryption and poly1305
+authentication.
+
+The create and handle request functions are the encrypt and decrypt functions
+for a type of DHT packets used to send data directly to other DHT nodes.  To be
+honest they should probably be in the DHT module but they seem to fit better
+here.  TODO: What exactly are these functions?
+
+
+\begin{code}
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary PlainText where
+  arbitrary = PlainText . ByteString.pack <$> arbitrary
+
+
+instance Arbitrary CipherText where
+  arbitrary = encrypt <$> arbitrary <*> arbitrary <*> arbitrary
+
+\end{code}
diff --git a/src/Network/Tox/Crypto/CombinedKey.lhs b/src/Network/Tox/Crypto/CombinedKey.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Crypto/CombinedKey.lhs
@@ -0,0 +1,58 @@
+\subsection{Combined Key}
+
+\begin{code}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE Trustworthy       #-}
+module Network.Tox.Crypto.CombinedKey where
+
+import qualified Crypto.Saltine.Core.Box as Sodium (beforeNM)
+import           Network.MessagePack.Rpc (Doc (..))
+import qualified Network.MessagePack.Rpc as Rpc
+
+import           Network.Tox.Crypto.Key  (CombinedKey, Key (..), PublicKey,
+                                          SecretKey)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+\end{code}
+
+A Combined Key is computed from a Secret Key and a Public Key using the NaCl
+function \texttt{crypto\_box\_beforenm}.  Given two Key Pairs KP1 (SK1, PK1) and
+KP2 (SK2, PK2), the Combined Key computed from (SK1, PK2) equals the one
+computed from (SK2, PK1).  This allows for symmetric encryption, as peers can
+derive the same shared key from their own secret key and their peer's public
+key.
+
+\begin{code}
+
+precompute :: SecretKey -> PublicKey -> CombinedKey
+precompute (Key sk) (Key pk) =
+  Key $ Sodium.beforeNM sk pk
+
+
+precomputeR :: Rpc.Rpc (SecretKey -> PublicKey -> Rpc.Returns CombinedKey)
+precomputeR =
+  Rpc.stubs "CombinedKey.precompute"
+    (Arg "sk" $ Arg "pk" $ Ret "key")
+    precompute
+
+
+\end{code}
+
+In the Tox protocol, packets are encrypted using the public key of the receiver
+and the secret key of the sender.  The receiver decrypts the packets using the
+receiver's secret key and the sender's public key.
+
+The fact that the same key is used to encrypt and decrypt packets on both sides
+means that packets being sent could be replayed back to the sender if there is
+nothing to prevent it.
+
+The shared key generation is the most resource intensive part of the
+encryption/decryption which means that resource usage can be reduced
+considerably by saving the shared keys and reusing them later as much as
+possible.
diff --git a/src/Network/Tox/Crypto/Key.lhs b/src/Network/Tox/Crypto/Key.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Crypto/Key.lhs
@@ -0,0 +1,152 @@
+\section{Key}
+
+\begin{code}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE DeriveDataTypeable  #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneDeriving  #-}
+{-# LANGUAGE Trustworthy         #-}
+module Network.Tox.Crypto.Key where
+
+import           Control.Applicative               ((<$>))
+import           Control.Monad                     ((>=>))
+import qualified Crypto.Saltine.Class              as Sodium (IsEncoding,
+                                                              decode, encode)
+import qualified Crypto.Saltine.Core.Box           as Sodium (CombinedKey,
+                                                              Nonce, PublicKey,
+                                                              SecretKey)
+import qualified Crypto.Saltine.Internal.ByteSizes as Sodium (boxBeforeNM,
+                                                              boxNonce, boxPK,
+                                                              boxSK)
+import           Data.Binary                       (Binary)
+import qualified Data.Binary                       as Binary (get, put)
+import qualified Data.Binary.Get                   as Binary (getByteString,
+                                                              runGet)
+import qualified Data.Binary.Put                   as Binary (putByteString)
+import qualified Data.ByteString                   as ByteString
+import qualified Data.ByteString.Base16            as Base16
+import qualified Data.ByteString.Lazy              as LazyByteString
+import           Data.MessagePack                  (MessagePack (..))
+import           Data.Proxy                        (Proxy (..))
+import           Data.Typeable                     (Typeable)
+import           Test.QuickCheck.Arbitrary         (Arbitrary, arbitrary)
+import qualified Test.QuickCheck.Arbitrary         as Arbitrary
+import           Text.Read                         (readPrec)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+\end{code}
+
+A Crypto Number is a large fixed size unsigned (non-negative) integer.  Its binary
+encoding is as a Big Endian integer in exactly the encoded byte size.  Its
+human-readable encoding is as a base-16 number encoded as String.  The NaCl
+implementation \href{https://github.com/jedisct1/libsodium}{libsodium} supplies
+the functions \texttt{sodium\_bin2hex} and \texttt{sodium\_hex2bin} to aid in
+implementing the human-readable encoding.  The in-memory encoding of these
+crypto numbers in NaCl already satisfies the binary encoding, so for
+applications directly using those APIs, binary encoding and decoding is the
+\href{https://en.wikipedia.org/wiki/Identity_function}{identity function}.
+
+\begin{code}
+
+class Sodium.IsEncoding a => CryptoNumber a where
+  encodedByteSize :: Proxy a -> Int
+
+\end{code}
+
+Tox uses four kinds of Crypto Numbers:
+
+\begin{tabular}{l|l|l}
+  Type         & Bits & Encoded byte size \\
+  \hline
+  Public Key   & 256  & 32 \\
+  Secret Key   & 256  & 32 \\
+  Combined Key & 256  & 32 \\
+  Nonce        & 192  & 24 \\
+\end{tabular}
+
+\begin{code}
+
+instance CryptoNumber Sodium.PublicKey   where { encodedByteSize Proxy = Sodium.boxPK       }
+instance CryptoNumber Sodium.SecretKey   where { encodedByteSize Proxy = Sodium.boxSK       }
+instance CryptoNumber Sodium.CombinedKey where { encodedByteSize Proxy = Sodium.boxBeforeNM }
+instance CryptoNumber Sodium.Nonce       where { encodedByteSize Proxy = Sodium.boxNonce    }
+
+deriving instance Typeable Sodium.PublicKey
+deriving instance Typeable Sodium.SecretKey
+deriving instance Typeable Sodium.CombinedKey
+deriving instance Typeable Sodium.Nonce
+
+newtype Key a = Key { unKey :: a }
+  deriving (Eq, Ord, Typeable)
+
+type PublicKey   = Key Sodium.PublicKey
+type SecretKey   = Key Sodium.SecretKey
+type CombinedKey = Key Sodium.CombinedKey
+type Nonce       = Key Sodium.Nonce
+
+instance Sodium.IsEncoding a => Sodium.IsEncoding (Key a) where
+  encode = Sodium.encode . unKey
+  decode = fmap Key . Sodium.decode
+
+
+keyToInteger :: Sodium.IsEncoding a => Key a -> Integer
+keyToInteger =
+  Binary.runGet Binary.get . encode
+  where
+    prefix = LazyByteString.pack
+      [ 0x01 -- Tag: big integer
+      , 0x01 -- Sign: positive
+      , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20 -- Length: 32 bytes
+      ]
+    encode =
+      LazyByteString.append prefix
+        . LazyByteString.reverse
+        . LazyByteString.fromStrict
+        . Sodium.encode
+
+
+decode :: (CryptoNumber a, Monad m) => ByteString.ByteString -> m (Key a)
+decode bytes =
+  case Sodium.decode bytes of
+    Just key -> return $ Key key
+    Nothing  -> fail "unable to decode ByteString to Key"
+
+
+instance CryptoNumber a => Binary (Key a) where
+  put (Key key) =
+    Binary.putByteString $ Sodium.encode key
+
+  get = do
+    bytes <- Binary.getByteString $ encodedByteSize (Proxy :: Proxy a)
+    decode bytes
+
+
+instance CryptoNumber a => Show (Key a) where
+  show (Key key) = show $ Base16.encode $ Sodium.encode key
+
+instance CryptoNumber a => Read (Key a) where
+  readPrec = fst . Base16.decode <$> readPrec >>= decode
+
+instance CryptoNumber a => MessagePack (Key a) where
+  toObject = toObject . Sodium.encode
+  fromObject = fromObject >=> decode
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance CryptoNumber a => Arbitrary (Key a) where
+  arbitrary = do
+    bytes <- fmap ByteString.pack $ Arbitrary.vector $ encodedByteSize (Proxy :: Proxy a)
+    decode bytes
+\end{code}
diff --git a/src/Network/Tox/Crypto/KeyPair.lhs b/src/Network/Tox/Crypto/KeyPair.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Crypto/KeyPair.lhs
@@ -0,0 +1,94 @@
+\subsection{Key Pair}
+
+A Key Pair is a pair of Secret Key and Public Key.  A new key pair is generated
+using the \texttt{crypto\_box\_keypair} function of the NaCl crypto library.  Two
+separate calls to the key pair generation function must return distinct key
+pairs.  See the \href{https://nacl.cr.yp.to/box.html}{NaCl documentation} for
+details.
+
+A Public Key can be computed from a Secret Key using the NaCl function
+\texttt{crypto\_scalarmult\_base}, which computes the scalar product of a
+standard group element and the Secret Key.  See the
+\href{https://nacl.cr.yp.to/scalarmult.html}{NaCl documentation} for details.
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE Trustworthy        #-}
+module Network.Tox.Crypto.KeyPair where
+
+import           Control.Applicative            ((<$>))
+import qualified Crypto.Saltine.Class           as Sodium (decode, encode)
+import qualified Crypto.Saltine.Core.Box        as Sodium (newKeypair)
+import qualified Crypto.Saltine.Core.ScalarMult as Sodium (multBase)
+import           Data.Binary                    (Binary)
+import           Data.MessagePack               (MessagePack (..))
+import           Data.Typeable                  (Typeable)
+import           GHC.Generics                   (Generic)
+import           Network.MessagePack.Rpc        (Doc (..))
+import qualified Network.MessagePack.Rpc        as Rpc
+import           Test.QuickCheck.Arbitrary      (Arbitrary, arbitrary)
+
+import           Network.Tox.Crypto.Key         (Key (..))
+import qualified Network.Tox.Crypto.Key         as Key
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+data KeyPair = KeyPair
+  { secretKey :: Key.SecretKey
+  , publicKey :: Key.PublicKey
+  }
+  deriving (Eq, Show, Read, Generic, Typeable)
+
+instance Binary KeyPair
+instance MessagePack KeyPair
+
+
+newKeyPair :: IO KeyPair
+newKeyPair = do
+  (sk, pk) <- Sodium.newKeypair
+  return $ KeyPair (Key sk) (Key pk)
+
+newKeyPairR :: Rpc.Rpc (Rpc.ReturnsM IO KeyPair)
+newKeyPairR =
+  Rpc.stubs "KeyPair.newKeyPair"
+    (RetM "keyPair")
+    newKeyPair
+
+
+fromSecretKey :: Key.SecretKey -> KeyPair
+fromSecretKey sk =
+  let
+    skBytes = Sodium.encode sk
+    Just skScalar = Sodium.decode skBytes
+    pkGroupElement = Sodium.multBase skScalar
+    pkBytes = Sodium.encode pkGroupElement
+    Just pk = Sodium.decode pkBytes
+  in
+  KeyPair sk pk
+
+fromSecretKeyR :: Rpc.Rpc (Key.SecretKey -> Rpc.Returns KeyPair)
+fromSecretKeyR =
+  Rpc.stubs "KeyPair.fromSecretKey"
+    (Arg "key" $ Ret "keyPair")
+    fromSecretKey
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary KeyPair where
+  arbitrary =
+    fromSecretKey <$> arbitrary
+\end{code}
diff --git a/src/Network/Tox/Crypto/Keyed.hs b/src/Network/Tox/Crypto/Keyed.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Crypto/Keyed.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE Safe                  #-}
+{-# LANGUAGE UndecidableInstances  #-}
+
+-- | Monad class for caching of combined keys
+module Network.Tox.Crypto.Keyed where
+
+import           Control.Applicative            (Applicative, pure, (<*>))
+import           Control.Monad                  (Monad)
+import           Control.Monad.Random           (RandT)
+import           Control.Monad.Reader           (ReaderT)
+import           Control.Monad.RWS              (RWST)
+import           Control.Monad.State            (StateT)
+import           Control.Monad.Trans            (lift)
+import           Control.Monad.Writer           (WriterT)
+import           Data.Monoid                    (Monoid)
+
+import qualified Network.Tox.Crypto.CombinedKey as CombinedKey
+import           Network.Tox.Crypto.Key         (CombinedKey, PublicKey,
+                                                 SecretKey)
+
+class (Monad m, Applicative m) => Keyed m where
+  getCombinedKey :: SecretKey -> PublicKey -> m CombinedKey
+
+instance Keyed m => Keyed (ReaderT r m) where
+  getCombinedKey = (lift .) . getCombinedKey
+instance (Monoid w, Keyed m) => Keyed (WriterT w m) where
+  getCombinedKey = (lift .) . getCombinedKey
+instance Keyed m => Keyed (StateT s m) where
+  getCombinedKey = (lift .) . getCombinedKey
+instance (Monoid w, Keyed m) => Keyed (RWST r w s m) where
+  getCombinedKey = (lift .) . getCombinedKey
+instance Keyed m => Keyed (RandT s m) where
+  getCombinedKey = (lift .) . getCombinedKey
+
+-- | trivial instance: the trivial monad, with no caching of keys
+newtype NullKeyed a = NullKeyed { runNullKeyed :: a }
+instance Functor NullKeyed where
+  fmap f (NullKeyed x) = NullKeyed (f x)
+instance Applicative NullKeyed where
+  pure = NullKeyed
+  (NullKeyed f) <*> (NullKeyed x) = NullKeyed (f x)
+instance Monad NullKeyed where
+  return = NullKeyed
+  NullKeyed x >>= f = f x
+instance Keyed NullKeyed where
+  getCombinedKey = (NullKeyed .) . CombinedKey.precompute
diff --git a/src/Network/Tox/Crypto/KeyedT.hs b/src/Network/Tox/Crypto/KeyedT.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Crypto/KeyedT.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE LambdaCase                 #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE Trustworthy                #-}
+{-# LANGUAGE TupleSections              #-}
+{-# LANGUAGE UndecidableInstances       #-}
+module Network.Tox.Crypto.KeyedT where
+
+import           Control.Applicative                  (Applicative, (<$>))
+import           Control.Monad                        (Monad)
+import           Control.Monad.IO.Class               (MonadIO)
+import           Control.Monad.State                  (MonadState, StateT,
+                                                       StateT (..), evalStateT,
+                                                       gets, modify, runStateT,
+                                                       state)
+import           Control.Monad.Trans                  (MonadTrans)
+import           Control.Monad.Writer                 (MonadWriter)
+
+import           Data.Map                             (Map)
+import qualified Data.Map                             as Map
+import qualified Network.Tox.Crypto.CombinedKey       as CombinedKey
+import           Network.Tox.Crypto.Key               (CombinedKey, PublicKey,
+                                                       SecretKey)
+import           Network.Tox.Crypto.Keyed             (Keyed (..))
+import           Network.Tox.Network.MonadRandomBytes (MonadRandomBytes)
+import           Network.Tox.Network.Networked        (Networked)
+import           Network.Tox.Timed                    (Timed)
+
+type KeyRing = Map (SecretKey, PublicKey) CombinedKey
+
+-- | caches computations of combined keys. Makes no attempt to delete old keys.
+newtype KeyedT m a = KeyedT (StateT KeyRing m a)
+  deriving (Monad, Applicative, Functor, MonadWriter w
+    , MonadRandomBytes, MonadTrans, MonadIO, Networked, Timed)
+
+runKeyedT :: Monad m => KeyedT m a -> KeyRing -> m (a, KeyRing)
+runKeyedT (KeyedT m) = runStateT m
+
+evalKeyedT :: Monad m => KeyedT m a -> KeyRing -> m a
+evalKeyedT (KeyedT m) = evalStateT m
+
+instance (MonadState s m, Applicative m) => MonadState s (KeyedT m) where
+  state f = KeyedT . StateT $ \s -> (, s) <$> state f
+
+instance (Monad m, Applicative m) => Keyed (KeyedT m) where
+  getCombinedKey secretKey publicKey =
+    let keys = (secretKey, publicKey)
+    in KeyedT $ gets (Map.lookup keys) >>= \case
+      Nothing ->
+        let shared = CombinedKey.precompute secretKey publicKey
+        in modify (Map.insert keys shared) >> return shared
+      Just shared -> return shared
diff --git a/src/Network/Tox/Crypto/Nonce.lhs b/src/Network/Tox/Crypto/Nonce.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Crypto/Nonce.lhs
@@ -0,0 +1,62 @@
+\subsection{Nonce}
+
+A random nonce is generated using the cryptographically secure random number
+generator from the NaCl library \texttt{randombytes}.
+
+A nonce is incremented by interpreting it as a Big Endian number and adding 1.
+If the nonce has the maximum value, the value after the increment is 0.
+
+Most parts of the protocol use random nonces.  This prevents new nonces from
+being associated with previous nonces.  If many different packets could be tied
+together due to how the nonces were generated, it might for example lead to
+tying DHT and onion announce packets together.  This would introduce a flaw in
+the system as non friends could tie some people's DHT keys and long term keys
+together.
+
+\begin{code}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE Trustworthy       #-}
+module Network.Tox.Crypto.Nonce where
+
+import           Control.Applicative     ((<$>))
+import qualified Crypto.Saltine.Class    as Sodium (decode, encode, nudge)
+import qualified Crypto.Saltine.Core.Box as Sodium (newNonce)
+import qualified Data.ByteString         as ByteString
+import           Network.MessagePack.Rpc (Doc (..))
+import qualified Network.MessagePack.Rpc as Rpc
+
+import           Network.Tox.Crypto.Key
+
+
+newNonce :: IO Nonce
+newNonce = Key <$> Sodium.newNonce
+
+newNonceR :: Rpc.Rpc (Rpc.ReturnsM IO Nonce)
+newNonceR =
+  Rpc.stubs "Nonce.newNonce"
+    (RetM "nonce")
+    newNonce
+
+
+reverseNonce :: Nonce -> Nonce
+reverseNonce (Key nonce) =
+  let Just reversed = Sodium.decode $ ByteString.reverse $ Sodium.encode nonce in
+  Key reversed
+
+
+nudge :: Nonce -> Nonce
+nudge =
+  Key . Sodium.nudge . unKey
+
+
+increment :: Nonce -> Nonce
+increment =
+  reverseNonce . nudge . reverseNonce
+
+incrementR :: Rpc.Rpc (Nonce -> Rpc.Returns Nonce)
+incrementR =
+  Rpc.stubs "Nonce.increment"
+    (Arg "nonce" $ Ret "incremented")
+    increment
+
+\end{code}
diff --git a/src/Network/Tox/DHT.lhs b/src/Network/Tox/DHT.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT.lhs
@@ -0,0 +1,179 @@
+\chapter{DHT}
+
+\begin{code}
+{-# LANGUAGE Safe #-}
+module Network.Tox.DHT where
+\end{code}
+
+The DHT is a self-organizing swarm of all nodes in the Tox network.  A node in
+the Tox network is also called a "Tox node".  When we talk about "peers", we mean
+any node that is not the local node (the subject).  This module takes care of
+finding the IP and port of nodes and establishing a route to them directly via
+UDP using \href{#hole-punching}{hole punching} if necessary.  The DHT only runs
+on UDP and so is only used if UDP works.
+
+Every node in the Tox DHT has an ephemeral Key Pair called the DHT Key Pair,
+consisting of the DHT Secret Key and the DHT Public Key.  The DHT Public Key
+acts as the node address.  The DHT Key Pair is renewed every time the Tox
+instance is closed or restarted.  An implementation may choose to renew the key
+more often, but doing so will disconnect all peers.
+
+The DHT public key of a friend is found using the \href{#onion}{onion} module.
+Once the DHT public key of a friend is known, the DHT is used to find them and
+connect directly to them via UDP.
+
+\input{src/Network/Tox/DHT/Distance.lhs}
+\input{src/Network/Tox/DHT/ClientList.lhs}
+\input{src/Network/Tox/DHT/KBuckets.lhs}
+\input{src/Network/Tox/DHT/DhtState.lhs}
+
+\section{Self-organisation}
+
+Self-organising in the DHT occurs through each DHT peer connecting to an
+arbitrary number of peers closest to their own DHT public key and some that are
+further away.
+
+If each peer in the network knows the peers with the DHT public key closest to
+its DHT public key, then to find a specific peer with public key X a peer just
+needs to recursively ask peers in the DHT for known peers that have the DHT
+public keys closest to X.  Eventually the peer will find the peers in the DHT
+that are the closest to that peer and, if that peer is online, they will find
+them.
+
+\input{src/Network/Tox/DHT/DhtPacket.lhs}
+
+\section{RPC Services}
+
+\input{src/Network/Tox/DHT/RpcPacket.lhs}
+\input{src/Network/Tox/DHT/PingPacket.lhs}
+
+\subsection{Nodes Service}
+
+The Nodes Service is used to query another DHT node for up to 4 nodes they know
+that are the closest to a requested node.
+
+The DHT Nodes RPC service uses the Packed Node Format.
+
+Only the UDP Protocol (IP Type \texttt{2} and \texttt{10}) is used in the DHT
+module when sending nodes with the packed node format.  This is because the TCP
+Protocol is used to send TCP relay information and the DHT is UDP only.
+
+\input{src/Network/Tox/DHT/NodesRequest.lhs}
+\input{src/Network/Tox/DHT/NodesResponse.lhs}
+
+\input{src/Network/Tox/DHT/Operation.lhs}
+
+\section{NATs}
+
+We assume that peers are either directly accessible or are behind one of 3
+types of NAT:
+
+Cone NATs: Assign one whole port to each UDP socket behind the NAT; any packet
+from any IP/port sent to that assigned port from the internet will be forwarded
+to the socket behind it.
+
+Restricted Cone NATs: Assign one whole port to each UDP socket behind the NAT.
+However, it will only forward packets from IPs that the UDP socket has sent a
+packet to.
+
+Symmetric NATs: The worst kind of NAT, they assign a new port for each IP/port
+a packet is sent to.  They treat each new peer you send a UDP packet to as a
+\texttt{'connection'} and will only forward packets from the IP/port of that
+\texttt{'connection'}.
+
+
+\section{Hole punching}
+
+Holepunching on normal cone NATs is achieved simply through the way in which
+the DHT functions.
+
+If more than half of the 8 peers closest to the friend in the DHT return an
+IP/port for the friend and we send a ping request to each of the returned
+IP/ports but get no response.  If we have sent 4 ping requests to 4 IP/ports
+that supposedly belong to the friend and get no response, then this is enough
+for toxcore to start the hole punching.  The numbers 8 and 4 are used in
+toxcore and were chosen based on feel alone and so may not be the best numbers.
+
+Before starting the hole punching, the peer will send a NAT ping packet to the
+friend via the peers that say they know the friend.  If a NAT ping response
+with the same random number is received the hole punching will start.
+
+If a NAT ping request is received, we will first check if it is from a friend.
+If it is not from a friend it will be dropped.  If it is from a friend, a
+response with the same 8 byte number as in the request will be sent back via
+the nodes that know the friend sending the request.  If no nodes from the
+friend are known, the packet will be dropped.
+
+Receiving a NAT ping response therefore means that the friend is both online
+and actively searching for us, as that is the only way they would know nodes
+that know us.  This is important because hole punching will work only if the
+friend is actively trying to connect to us.
+
+NAT ping requests are sent every 3 seconds in toxcore, if no response is
+received for 6 seconds, the hole punching will stop.  Sending them in longer
+intervals might increase the possibility of the other node going offline and
+ping packets sent in the hole punching being sent to a dead peer but decrease
+bandwidth usage.  Decreasing the intervals will have the opposite effect.
+
+There are 2 cases that toxcore handles for the hole punching.  The first case
+is if each 4+ peers returned the same IP and port.  The second is if the 4+
+peers returned same IPs but different ports.
+
+A third case that may occur is the peers returning different IPs and ports.
+This can only happen if the friend is behind a very restrictive NAT that cannot
+be hole punched or if the peer recently connected to another internet
+connection and some peers still have the old one stored.  Since there is
+nothing we can do for the first option it is recommended to just use the most
+common IP returned by the peers and to ignore the other IP/ports.
+
+In the case where the peers return the same IP and port it means that the other
+friend is on a restricted cone NAT.  These kinds of NATs can be hole punched by
+getting the friend to send a packet to our public IP/port.  This means that
+hole punching can be achieved easily and that we should just continue sending
+DHT ping packets regularly to that IP/port until we get a ping response.  This
+will work because the friend is searching for us in the DHT and will find us
+and will send us a packet to our public IP/port (or try to with the hole
+punching), thereby establishing a connection.
+
+For the case where peers do not return the same ports, this means that the
+other peer is on a symmetric NAT.  Some symmetric NATs open ports in sequences
+so the ports returned by the other peers might be something like: 1345, 1347,
+1389, 1395.  The method to hole punch these NATs is to try to guess which ports
+are more likely to be used by the other peer when they try sending us ping
+requests and send some ping requests to these ports.  Toxcore just tries all
+the ports beside each returned port (ex: for the 4 ports previously it would
+try: 1345, 1347, 1389, 1395, 1346, 1348, 1390, 1396, 1344, 1346...) getting
+gradually further and further away and, although this works, the method could
+be improved.  When using this method toxcore will try up to 48 ports every 3
+seconds until both connect.  After 5 tries toxcore doubles this and starts
+trying ports from 1024 (48 each time) along with the previous port guessing.
+This is because I have noticed that this seemed to fix it for some symmetric
+NATs, most likely because a lot of them restart their count at 1024.
+
+Increasing the amount of ports tried per second would make the hole punching go
+faster but might DoS NATs due to the large number of packets being sent to
+different IPs in a short amount of time.  Decreasing it would make the hole
+punching slower.
+
+This works in cases where both peers have different NATs.  For example, if A
+and B are trying to connect to each other: A has a symmetric NAT and B a
+restricted cone NAT.  A will detect that B has a restricted cone NAT and keep
+sending ping packets to his one IP/port.  B will detect that A has a symmetric
+NAT and will send packets to it to try guessing his ports.  If B manages to
+guess the port A is sending packets from they will connect together.
+
+\section{DHT Bootstrap Info (0xf0)}
+
+Bootstrap nodes are regular Tox nodes with a stable DHT public key. This means
+the DHT public key does not change across restarts. DHT bootstrap nodes have one
+additional request kind: Bootstrap Info. The request is simply a packet of
+length 78 bytes where the first byte is 0xf0. The other bytes are ignored.
+
+The response format is as follows:
+
+\begin{tabular}{l|l|l}
+  Length             & Type        & \href{#protocol-packet}{Contents} \\
+  \hline
+  \texttt{4}         & Word32      & Bootstrap node version \\
+  \texttt{256}       & Bytes       & Message of the day \\
+\end{tabular}
diff --git a/src/Network/Tox/DHT/ClientList.lhs b/src/Network/Tox/DHT/ClientList.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/ClientList.lhs
@@ -0,0 +1,156 @@
+\section{Client Lists}
+
+\begin{code}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE Safe           #-}
+module Network.Tox.DHT.ClientList where
+
+import           Control.Applicative           ((<$>), (<*>))
+import           Control.Monad                 (join)
+import           Data.List                     (sort)
+import           Data.Map                      (Map)
+import qualified Data.Map                      as Map
+import           Test.QuickCheck.Arbitrary     (Arbitrary, arbitrary,
+                                                arbitrarySizedNatural)
+import           Test.QuickCheck.Gen           (Gen)
+import qualified Test.QuickCheck.Gen           as Gen
+
+import           Network.Tox.Crypto.Key        (PublicKey)
+import           Network.Tox.DHT.ClientNode    (ClientNode)
+import qualified Network.Tox.DHT.ClientNode    as ClientNode
+import           Network.Tox.DHT.Distance      (Distance)
+import qualified Network.Tox.DHT.Distance      as Distance
+import           Network.Tox.NodeInfo.NodeInfo (NodeInfo)
+import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo
+import           Network.Tox.Time              (Timestamp)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+\end{code}
+
+A Client List of \textit{maximum size} \texttt{k} with a given public key as
+\textit{base key} is an ordered set of at most \texttt{k} nodes close to the
+base key.  The elements are sorted by \href{#distance}{distance} from the base
+key.  Thus, the first (smallest) element of the set is the closest one to the
+base key in that set, the last (greatest) element is the furthest away.  The
+maximum size and base key are constant throughout the lifetime of a Client
+List.
+
+
+\begin{code}
+
+data ClientList = ClientList
+  { baseKey :: PublicKey
+  , maxSize :: Int
+  , nodes   :: ClientNodes
+  }
+  deriving (Eq, Read, Show)
+
+type ClientNodes = Map Distance ClientNode
+
+nodeInfos :: ClientList -> [NodeInfo]
+nodeInfos = map ClientNode.nodeInfo . Map.elems . nodes
+
+empty :: PublicKey -> Int -> ClientList
+empty publicKey size = ClientList
+  { baseKey = publicKey
+  , maxSize = size
+  , nodes   = Map.empty
+  }
+
+isEmpty :: ClientList -> Bool
+isEmpty = Map.null . nodes
+
+updateClientNodes :: (ClientNodes -> ClientNodes) -> ClientList -> ClientList
+updateClientNodes f clientList@ClientList{ nodes } =
+  clientList{nodes = f nodes}
+
+lookup :: PublicKey -> ClientList -> Maybe NodeInfo
+lookup publicKey _cl@ClientList{ baseKey, nodes } =
+  ClientNode.nodeInfo <$> Distance.xorDistance publicKey baseKey `Map.lookup` nodes
+
+\end{code}
+
+
+A Client List is \textit{full} when the number of nodes it contains is the
+maximum size of the list.
+
+A node is \textit{viable} for entry if the Client List is not \textit{full} or the
+node's public key has a lower distance from the base key than the current entry
+with the greatest distance.
+
+If a node is \textit{viable} and the Client List is \textit{full}, the entry
+with the greatest distance from the base key is removed to keep the size below
+the maximum configured size.
+
+Adding a node whose key already exists will result in an update of the Node
+Info in the Client List.  Removing a node for which no Node Info exists in the
+Client List has no effect.  Thus, removing a node twice is permitted and has the
+same effect as removing it once.
+
+\begin{code}
+
+full :: ClientList -> Bool
+full ClientList{ nodes, maxSize } =
+  Map.size nodes >= maxSize
+
+addNode :: Timestamp -> NodeInfo -> ClientList -> ClientList
+addNode time nodeInfo clientList@ClientList{ baseKey, maxSize } =
+  (`updateClientNodes` clientList) $
+    mapTake maxSize
+    . Map.insert
+      (Distance.xorDistance (NodeInfo.publicKey nodeInfo) baseKey)
+      (ClientNode.newNode time nodeInfo)
+  where
+    -- | 'mapTake' is 'Data.Map.take' in >=containers-0.5.8, but we define it
+    -- for compatibility with older versions.
+    mapTake :: Int -> Map k a -> Map k a
+    mapTake n = Map.fromDistinctAscList . take n . Map.toAscList
+
+
+removeNode :: PublicKey -> ClientList -> ClientList
+removeNode publicKey clientList =
+  (`updateClientNodes` clientList) .
+    Map.delete . Distance.xorDistance publicKey $ baseKey clientList
+
+viable :: NodeInfo -> ClientList -> Bool
+viable nodeInfo ClientList{ baseKey, maxSize, nodes } =
+  let key = Distance.xorDistance (NodeInfo.publicKey nodeInfo) baseKey
+  in (key `elem`) . take maxSize . sort $ key : Map.keys nodes
+
+\end{code}
+
+The iteration order of a Client List is in order of distance from the base
+key.  I.e. the first node seen in iteration is the closest, and the last node
+is the furthest away in terms of the distance metric.
+
+\begin{code}
+
+foldNodes :: (a -> NodeInfo -> a) -> a -> ClientList -> a
+foldNodes f x = foldl f x . nodeInfos
+
+closeNodes :: PublicKey -> ClientList -> [ (Distance, NodeInfo) ]
+closeNodes publicKey ClientList{ baseKey, nodes } =
+  Map.toAscList . fmap ClientNode.nodeInfo $
+    Map.mapKeys (Distance.rebaseDistance baseKey publicKey) nodes
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+genClientList :: PublicKey -> Int -> Gen ClientList
+genClientList publicKey size =
+  foldl (flip $ uncurry addNode) (empty publicKey size) <$> Gen.listOf arbitrary
+
+
+instance Arbitrary ClientList where
+  arbitrary = join $ genClientList <$> arbitrary <*> arbitrarySizedNatural
+\end{code}
diff --git a/src/Network/Tox/DHT/ClientNode.lhs b/src/Network/Tox/DHT/ClientNode.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/ClientNode.lhs
@@ -0,0 +1,37 @@
+\begin{code}
+{-# LANGUAGE Safe #-}
+module Network.Tox.DHT.ClientNode where
+
+import           Control.Applicative           ((<$>), (<*>))
+import           Test.QuickCheck.Arbitrary     (Arbitrary, arbitrary)
+
+import           Network.Tox.NodeInfo.NodeInfo (NodeInfo)
+import           Network.Tox.Time              (Timestamp)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+data ClientNode = ClientNode
+  { nodeInfo   :: NodeInfo
+  , lastCheck  :: Timestamp
+  , checkCount :: Int
+  }
+  deriving (Eq, Read, Show)
+
+newNode :: Timestamp -> NodeInfo -> ClientNode
+newNode time node = ClientNode node time 0
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+instance Arbitrary ClientNode where
+  arbitrary = ClientNode <$> arbitrary <*> arbitrary <*> arbitrary
+
+\end{code}
diff --git a/src/Network/Tox/DHT/DhtPacket.lhs b/src/Network/Tox/DHT/DhtPacket.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/DhtPacket.lhs
@@ -0,0 +1,124 @@
+\section{DHT Packet}
+
+The DHT Packet contains the sender's DHT Public Key, an encryption Nonce, and
+an encrypted payload.  The payload is encrypted with the DHT secret key of the
+sender, the DHT public key of the receiver, and the nonce that is sent along
+with the packet.  DHT Packets are sent inside Protocol Packets with a varying
+Packet Kind.
+
+\begin{tabular}{l|l|l}
+  Length             & Type        & \href{#protocol-packet}{Contents} \\
+  \hline
+  \texttt{32}        & Public Key  & Sender DHT Public Key \\
+  \texttt{24}        & Nonce       & Random nonce \\
+  \texttt{[16,]}     & Bytes       & Encrypted payload \\
+\end{tabular}
+
+The encrypted payload is at least 16 bytes long, because the encryption
+includes a \href{https://en.wikipedia.org/wiki/Message_authentication_code}{MAC}
+of 16 bytes.  A 16 byte payload would thus be the empty message.  The DHT
+protocol never actually sends empty messages, so in reality the minimum size is
+27 bytes for the \href{#ping-service}{Ping Packet}.
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE NamedFieldPuns     #-}
+{-# LANGUAGE Safe               #-}
+module Network.Tox.DHT.DhtPacket where
+
+import           Control.Applicative        ((<$>), (<*>))
+import           Data.Binary                (Binary, get, put)
+import           Data.Binary.Get            (getRemainingLazyByteString)
+import           Data.Binary.Put            (putByteString, runPut)
+import qualified Data.ByteString.Lazy       as LazyByteString
+import           Data.MessagePack           (MessagePack)
+import           Data.Typeable              (Typeable)
+import           GHC.Generics               (Generic)
+import           Network.Tox.Crypto.Box     (CipherText, PlainText (..),
+                                             unCipherText)
+import qualified Network.Tox.Crypto.Box     as Box
+import           Network.Tox.Crypto.Key     (Nonce, PublicKey)
+import           Network.Tox.Crypto.Keyed   (Keyed)
+import qualified Network.Tox.Crypto.Keyed   as Keyed
+import           Network.Tox.Crypto.KeyPair (KeyPair (..))
+import           Test.QuickCheck.Arbitrary  (Arbitrary, arbitrary)
+
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+data DhtPacket = DhtPacket
+  { senderPublicKey  :: PublicKey
+  , encryptionNonce  :: Nonce
+  , encryptedPayload :: CipherText
+  }
+  deriving (Eq, Read, Show, Generic, Typeable)
+
+instance MessagePack DhtPacket
+
+
+instance Binary DhtPacket where
+  put packet = do
+    put $ senderPublicKey packet
+    put $ encryptionNonce packet
+    putByteString . unCipherText . encryptedPayload $ packet
+
+  get =
+    DhtPacket <$> get <*> get <*> (LazyByteString.toStrict <$> getRemainingLazyByteString >>= Box.cipherText)
+
+
+encrypt :: KeyPair -> PublicKey -> Nonce -> PlainText -> DhtPacket
+encrypt = (((Keyed.runNullKeyed .) .) .) . encryptKeyed
+
+encryptKeyed :: Keyed m => KeyPair -> PublicKey -> Nonce -> PlainText -> m DhtPacket
+encryptKeyed (KeyPair senderSecretKey senderPublicKey') receiverPublicKey nonce plainText =
+  (\combinedKey -> DhtPacket senderPublicKey' nonce $
+    Box.encrypt combinedKey nonce plainText) <$>
+  Keyed.getCombinedKey senderSecretKey receiverPublicKey
+
+
+encode :: Binary payload => KeyPair -> PublicKey -> Nonce -> payload -> DhtPacket
+encode = (((Keyed.runNullKeyed .) .) .) . encodeKeyed
+
+encodeKeyed :: (Binary payload, Keyed m) => KeyPair -> PublicKey -> Nonce -> payload -> m DhtPacket
+encodeKeyed keyPair receiverPublicKey nonce =
+  encryptKeyed keyPair receiverPublicKey nonce
+  . PlainText
+  . LazyByteString.toStrict
+  . runPut
+  . put
+
+
+decrypt :: KeyPair -> DhtPacket -> Maybe PlainText
+decrypt = (Keyed.runNullKeyed .) . decryptKeyed
+
+decryptKeyed :: Keyed m => KeyPair -> DhtPacket -> m (Maybe PlainText)
+decryptKeyed (KeyPair receiverSecretKey _) DhtPacket { senderPublicKey, encryptionNonce, encryptedPayload } =
+  (\combinedKey -> Box.decrypt combinedKey encryptionNonce encryptedPayload) <$>
+  Keyed.getCombinedKey receiverSecretKey senderPublicKey
+
+
+decode :: Binary payload => KeyPair -> DhtPacket -> Maybe payload
+decode = (Keyed.runNullKeyed .) . decodeKeyed
+
+decodeKeyed :: (Binary payload, Keyed m) => KeyPair -> DhtPacket -> m (Maybe payload)
+decodeKeyed keyPair packet = (>>= Box.decode) <$> decryptKeyed keyPair packet
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary DhtPacket where
+  arbitrary =
+    DhtPacket <$> arbitrary <*> arbitrary <*> arbitrary
+\end{code}
diff --git a/src/Network/Tox/DHT/DhtRequestPacket.lhs b/src/Network/Tox/DHT/DhtRequestPacket.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/DhtRequestPacket.lhs
@@ -0,0 +1,69 @@
+\section{DHT Request Packets}
+DHT Request packets are used to route encrypted data from a sender to another
+node, referred to as the addressee of the packet, via a third node.
+
+A DHT Request Packet is sent as the payload of a Protocol Packet with the
+corresponding Packet Kind. It contains the DHT Public Key of an addressee, and a
+DHT Packet which is to be received by the addressee.
+
+\begin{tabular}{l|l|l}
+  Length             & Type        & \href{#protocol-packet}{Contents} \\
+  \hline
+  \texttt{32}        & Public Key  & Addressee DHT Public Key \\
+  \texttt{[72,]}     & DHT Packet  & DHT Packet \\
+\end{tabular}
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE Safe               #-}
+module Network.Tox.DHT.DhtRequestPacket where
+
+import           Control.Applicative       ((<$>), (<*>))
+import           Data.Binary               (Binary, get, put)
+import           Data.MessagePack          (MessagePack)
+import           Data.Typeable             (Typeable)
+import           GHC.Generics              (Generic)
+
+import           Network.Tox.Crypto.Key    (PublicKey)
+import           Network.Tox.DHT.DhtPacket (DhtPacket)
+
+import           Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
+
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+data DhtRequestPacket = DhtRequestPacket
+  { addresseePublicKey :: PublicKey
+  , dhtPacket          :: DhtPacket
+  }
+  deriving (Eq, Read, Show, Generic, Typeable)
+
+instance MessagePack DhtRequestPacket
+
+
+instance Binary DhtRequestPacket where
+  put packet = do
+    put $ addresseePublicKey packet
+    put $ dhtPacket packet
+
+  get =
+    DhtRequestPacket <$> get <*> get
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary DhtRequestPacket where
+  arbitrary =
+    DhtRequestPacket <$> arbitrary <*> arbitrary
+\end{code}
diff --git a/src/Network/Tox/DHT/DhtState.lhs b/src/Network/Tox/DHT/DhtState.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/DhtState.lhs
@@ -0,0 +1,366 @@
+\section{DHT node state}
+
+\begin{code}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NamedFieldPuns        #-}
+{-# LANGUAGE RankNTypes            #-}
+{-# LANGUAGE Safe                  #-}
+module Network.Tox.DHT.DhtState where
+
+import           Control.Applicative            (Applicative, Const (..),
+                                                 getConst, (<$>), (<*>), (<|>))
+import           Data.Functor.Identity          (Identity (..))
+import           Data.List                      (nub, sortBy)
+import           Data.Map                       (Map)
+import qualified Data.Map                       as Map
+import qualified Data.Maybe                     as Maybe
+import           Data.Monoid                    (All (..), Monoid, getAll)
+import           Data.Ord                       (comparing)
+import           Data.Traversable               (traverse)
+import           Lens.Family2                   (Lens')
+import           Test.QuickCheck.Arbitrary      (Arbitrary, arbitrary, shrink)
+
+import           Network.Tox.Crypto.Key         (PublicKey)
+import           Network.Tox.Crypto.KeyPair     (KeyPair)
+import qualified Network.Tox.Crypto.KeyPair     as KeyPair
+import           Network.Tox.DHT.ClientList     (ClientList)
+import qualified Network.Tox.DHT.ClientList     as ClientList
+import           Network.Tox.DHT.Distance       (Distance)
+import           Network.Tox.DHT.KBuckets       (KBuckets)
+import qualified Network.Tox.DHT.KBuckets       as KBuckets
+import           Network.Tox.DHT.NodeList       (NodeList)
+import qualified Network.Tox.DHT.NodeList       as NodeList
+import           Network.Tox.DHT.PendingReplies (PendingReplies)
+import qualified Network.Tox.DHT.Stamped        as Stamped
+import           Network.Tox.NodeInfo.NodeInfo  (NodeInfo)
+import qualified Network.Tox.NodeInfo.NodeInfo  as NodeInfo
+import           Network.Tox.Time               (Timestamp)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+\end{code}
+
+Every DHT node contains the following state:
+
+\begin{itemize}
+  \item DHT Key Pair: The Key Pair used to communicate with other DHT nodes. It
+    is immutable throughout the lifetime of the DHT node.
+  \item DHT Close List: A set of Node Infos of nodes that are close to the
+    DHT Public Key (public part of the DHT Key Pair).  The Close List is
+    represented as a \href{#k-buckets}{k-buckets} data structure, with the DHT
+    Public Key as the Base Key.
+  \item DHT Search List: A list of Public Keys of nodes that the DHT node is
+    searching for, associated with a DHT Search Entry.
+\end{itemize}
+
+\begin{code}
+
+data ListStamp = ListStamp { listTime :: Timestamp, listBootstrappedTimes :: Int }
+  deriving (Eq, Read, Show)
+newListStamp :: Timestamp -> ListStamp
+newListStamp t = ListStamp t 0
+
+data DhtState = DhtState
+  { dhtKeyPair        :: KeyPair
+  , dhtCloseList      :: KBuckets
+  , dhtSearchList     :: Map PublicKey DhtSearchEntry
+
+  , dhtCloseListStamp :: ListStamp
+  , dhtPendingReplies :: PendingReplies
+  }
+  deriving (Eq, Read, Show)
+
+_dhtKeyPair :: Lens' DhtState KeyPair
+_dhtKeyPair f d@DhtState{ dhtKeyPair = a } =
+  (\a' -> d{ dhtKeyPair = a' }) <$> f a
+
+_dhtCloseListStamp :: Lens' DhtState ListStamp
+_dhtCloseListStamp f d@DhtState{ dhtCloseListStamp = a } =
+  (\a' -> d{ dhtCloseListStamp = a' }) <$> f a
+
+_dhtCloseList :: Lens' DhtState KBuckets
+_dhtCloseList f d@DhtState{ dhtCloseList = a } =
+  (\a' -> d{ dhtCloseList = a' }) <$> f a
+
+_dhtSearchList :: Lens' DhtState (Map PublicKey DhtSearchEntry)
+_dhtSearchList f d@DhtState{ dhtSearchList = a } =
+  (\a' -> d{ dhtSearchList = a' }) <$> f a
+
+_dhtPendingReplies :: Lens' DhtState PendingReplies
+_dhtPendingReplies f d@DhtState{ dhtPendingReplies = a } =
+  (\a' -> d{ dhtPendingReplies = a' }) <$> f a
+
+\end{code}
+
+A DHT node state is initialised using a Key Pair, which is stored in the state
+as DHT Key Pair and as base key for the Close List. Both the Close and Search
+Lists are initialised to be empty.
+
+\begin{code}
+
+empty :: Timestamp -> KeyPair -> DhtState
+empty time keyPair =
+  DhtState keyPair (KBuckets.empty $ KeyPair.publicKey keyPair)
+    Map.empty (newListStamp time) Stamped.empty
+
+\end{code}
+
+\subsection{DHT Search Entry}
+
+A DHT Search Entry contains a Client List with base key the searched node's
+Public Key.  Once the searched node is found, it is also stored in the Search
+Entry.
+
+The maximum size of the Client List is set to 8.
+(Must be the same or smaller than the bucket size of the close list to make
+sure all the closest peers found will know the node being searched
+(TODO(zugz): this argument is unclear.)).
+
+A DHT node state therefore contains one Client List for each bucket index in
+the Close List, and one Client List for each DHT Search Entry.
+These lists are not required to be disjoint - a node may be in multiple Client
+Lists simultaneously.
+
+\begin{code}
+
+data DhtSearchEntry = DhtSearchEntry
+  { searchNode       :: Maybe NodeInfo
+  , searchStamp      :: ListStamp
+  , searchClientList :: ClientList
+  }
+  deriving (Eq, Read, Show)
+
+_searchNode :: Lens' DhtSearchEntry (Maybe NodeInfo)
+_searchNode f d@DhtSearchEntry{ searchNode = a } =
+  (\a' -> d{ searchNode = a' }) <$> f a
+
+_searchStamp :: Lens' DhtSearchEntry ListStamp
+_searchStamp f d@DhtSearchEntry{ searchStamp = a } =
+  (\a' -> d{ searchStamp = a' }) <$> f a
+
+_searchClientList :: Lens' DhtSearchEntry ClientList
+_searchClientList f d@DhtSearchEntry{ searchClientList = a } =
+  (\a' -> d{ searchClientList = a' }) <$> f a
+
+searchEntryClientListSize :: Int
+searchEntryClientListSize = 8
+
+\end{code}
+
+A Search Entry is initialised with the searched-for Public Key. The contained
+Client List is initialised to be empty.
+
+\begin{code}
+
+emptySearchEntry :: Timestamp -> PublicKey -> DhtSearchEntry
+emptySearchEntry time publicKey =
+  DhtSearchEntry Nothing (newListStamp time) $
+    ClientList.empty publicKey searchEntryClientListSize
+
+\end{code}
+
+\subsection{Manipulating the DHT node state}
+
+Adding a search key to the DHT node state creates an empty entry in the Search
+Nodes list. If a search entry for the public key already existed, the "add"
+operation has no effect.
+
+\begin{code}
+
+addSearchKey :: Timestamp -> PublicKey -> DhtState -> DhtState
+addSearchKey time searchKey dhtState@DhtState { dhtSearchList } =
+  dhtState { dhtSearchList = updatedSearchList }
+  where
+    searchEntry =
+      Map.findWithDefault (emptySearchEntry time searchKey) searchKey dhtSearchList
+    updatedSearchList =
+      Map.insert searchKey searchEntry dhtSearchList
+
+\end{code}
+
+Removing a search key removes its search entry and all associated data
+structures from memory.
+
+\begin{code}
+
+removeSearchKey :: PublicKey -> DhtState -> DhtState
+removeSearchKey searchKey dhtState@DhtState { dhtSearchList } =
+  dhtState { dhtSearchList = Map.delete searchKey dhtSearchList }
+
+
+containsSearchKey :: PublicKey -> DhtState -> Bool
+containsSearchKey searchKey =
+  Map.member searchKey . dhtSearchList
+
+\end{code}
+
+\input{src/Network/Tox/DHT/NodeList.lhs}
+
+The iteration order over the DHT state is to first process the Close List
+k-buckets, then the Search List entry Client Lists. Each of these follows the
+iteration order in the corresponding specification.
+
+\begin{code}
+
+traverseNodeLists :: Applicative f =>
+  (forall l. NodeList l => l -> f l) -> DhtState -> f DhtState
+traverseNodeLists f dhtState@DhtState{ dhtCloseList, dhtSearchList } =
+  (\close' search' ->
+      dhtState{ dhtCloseList = close', dhtSearchList = search' }) <$>
+    f dhtCloseList <*>
+    traverse traverseEntry dhtSearchList
+  where
+    traverseEntry entry =
+      (\x -> entry{ searchClientList = x }) <$> f (searchClientList entry)
+
+foldMapNodeLists :: Monoid m =>
+  (forall l. NodeList l => l -> m) -> DhtState -> m
+foldMapNodeLists f = getConst . traverseNodeLists (Const . f)
+
+mapNodeLists :: (forall l. NodeList l => l -> l) -> DhtState -> DhtState
+mapNodeLists f = runIdentity . traverseNodeLists (Identity . f)
+
+\end{code}
+
+A node info is considered to be contained in the DHT State if it is contained
+in the Close List or in at least one of the Search Entries.
+
+The size of the DHT state is defined to be the number of node infos it
+contains, counted with multiplicity: node infos contained multiple times, e.g.
+in the close list and in various search entries, are counted as many times as
+they appear.  Search keys do not directly count towards the state size.  So
+the size of the state is the sum of the sizes of the Close List and the Search
+Entries.
+
+The state size is relevant to later pruning algorithms that decide when to
+remove a node info and when to request a ping from stale nodes. Search keys,
+once added, are never automatically pruned.
+
+\begin{code}
+
+size :: DhtState -> Int
+size = NodeList.foldNodes (flip $ const (1 +)) 0
+
+\end{code}
+
+Adding a Node Info to the state is done by adding the node to each Node List
+in the state.
+
+When adding a node info to the state, the search entry for the node's public
+key, if it exists, is updated to contain the new node info. All k-buckets and
+Client Lists that already contain the node info will also be updated. See the
+corresponding specifications for the update algorithms. However, a node info
+will not be added to a search entry when it is the node to which the search
+entry is associated (i.e. the node being search for).
+
+\begin{code}
+
+addNode :: Timestamp -> NodeInfo -> DhtState -> DhtState
+addNode time nodeInfo =
+  updateSearchNode (NodeInfo.publicKey nodeInfo) (Just nodeInfo)
+  . mapNodeLists addUnlessBase
+  where
+    addUnlessBase nodeList
+      | NodeInfo.publicKey nodeInfo == NodeList.baseKey nodeList = nodeList
+    addUnlessBase nodeList = NodeList.addNode time nodeInfo nodeList
+
+removeNode :: PublicKey -> DhtState -> DhtState
+removeNode publicKey =
+  updateSearchNode publicKey Nothing
+  . mapNodeLists (NodeList.removeNode publicKey)
+
+viable :: NodeInfo -> DhtState -> Bool
+viable nodeInfo = getAll . foldMapNodeLists (All . NodeList.viable nodeInfo)
+
+traverseClientLists ::
+  Applicative f => (ClientList -> f ClientList) -> DhtState -> f DhtState
+traverseClientLists f = traverseNodeLists $ NodeList.traverseClientLists f
+
+closeNodes :: PublicKey -> DhtState -> [ (Distance, NodeInfo) ]
+closeNodes publicKey =
+  nub . sortBy (comparing fst) . foldMapNodeLists (NodeList.closeNodes publicKey)
+
+-- | although it is not referred to as a Node List in the spec, we make DhtState
+-- an instance of NodeList so we can use the traversal and folding functions.
+instance NodeList DhtState where
+  addNode = addNode
+  removeNode = removeNode
+  viable = viable
+  baseKey = KeyPair.publicKey . dhtKeyPair
+  traverseClientLists = traverseClientLists
+  closeNodes = closeNodes
+
+takeClosestNodesTo :: Int -> PublicKey -> DhtState -> [ NodeInfo ]
+takeClosestNodesTo n publicKey = map snd . take n . closeNodes publicKey
+
+mapBuckets :: (KBuckets -> KBuckets) -> DhtState -> DhtState
+mapBuckets f dhtState@DhtState { dhtCloseList } =
+  dhtState
+    { dhtCloseList = f dhtCloseList
+    }
+
+mapSearchEntry :: (DhtSearchEntry -> DhtSearchEntry) -> DhtState -> DhtState
+mapSearchEntry f dhtState@DhtState { dhtSearchList } =
+  dhtState
+    { dhtSearchList = Map.map f dhtSearchList
+    }
+
+mapSearchClientLists :: (ClientList -> ClientList) -> DhtState -> DhtState
+mapSearchClientLists f =
+    mapSearchEntry $ \entry@DhtSearchEntry{ searchClientList } ->
+      entry { searchClientList = f searchClientList }
+
+updateSearchNode :: PublicKey -> Maybe NodeInfo -> DhtState -> DhtState
+updateSearchNode publicKey nodeInfo dhtState@DhtState { dhtSearchList } =
+  dhtState
+    { dhtSearchList = Map.adjust update publicKey dhtSearchList
+    }
+  where
+    update entry = entry { searchNode = nodeInfo }
+
+\end{code}
+
+Removing a node info from the state removes it from all k-buckets. If a search
+entry for the removed node's public key existed, the node info in that search
+entry is unset. The search entry itself is not removed.
+
+\begin{code}
+
+containsNode :: PublicKey -> DhtState -> Bool
+containsNode publicKey =
+  NodeList.foldNodes (\a x -> a || NodeInfo.publicKey x == publicKey) False
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary DhtState where
+  arbitrary =
+    initialise <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
+    where
+      initialise :: Timestamp -> KeyPair -> [(Timestamp, NodeInfo)] -> [(Timestamp, PublicKey)] -> DhtState
+      initialise time kp nis =
+        foldl (flip $ uncurry addSearchKey) (foldl (flip $ uncurry NodeList.addNode) (empty time kp) nis)
+
+  shrink dhtState =
+    Maybe.maybeToList shrunkNode ++ Maybe.maybeToList shrunkSearchKey
+    where
+      -- Remove the first node we can find in the state.
+      shrunkNode = do
+        firstPK <- NodeInfo.publicKey <$> NodeList.foldNodes (\a x -> a <|> Just x) Nothing dhtState
+        return $ NodeList.removeNode firstPK dhtState
+
+      shrunkSearchKey = Nothing
+
+\end{code}
diff --git a/src/Network/Tox/DHT/Distance.lhs b/src/Network/Tox/DHT/Distance.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/Distance.lhs
@@ -0,0 +1,103 @@
+\section{Distance}
+
+\begin{code}
+{-# LANGUAGE MagicHash   #-}
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHT.Distance where
+
+import           Control.Applicative       ((<$>))
+import           Control.Arrow             (first)
+import           Data.Bits                 (xor)
+import           Data.Monoid               (Monoid, mappend, mempty)
+import           Data.Semigroup            (Semigroup, (<>))
+import           GHC.Exts                  (Int (I#))
+import           GHC.Integer.Logarithms    (integerLog2#)
+import           Network.Tox.Crypto.Key    (PublicKey)
+import qualified Network.Tox.Crypto.Key    as Key (keyToInteger)
+import           Numeric                   (readHex, showHex)
+import           Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+\end{code}
+
+A Distance is a positive integer.  Its human-readable representation is a
+base-16 number.  Distance (type) is an
+\href{https://en.wikipedia.org/wiki/Ordered_semigroup}{ordered monoid} with the
+associative binary operator \texttt{+} and the identity element \texttt{0}.
+
+\begin{code}
+
+newtype Distance = Distance Integer
+  deriving (Eq, Ord)
+
+
+instance Semigroup Distance where
+  (Distance x) <> (Distance y) = Distance (x + y)
+
+instance Monoid Distance where
+  mempty = Distance 0
+  mappend (Distance x) (Distance y) = Distance (x + y)
+
+
+instance Show Distance where
+  show (Distance distance) = showHex distance ""
+
+instance Read Distance where
+  readsPrec _ string = map (first Distance) $ readHex string
+
+
+log2 :: Distance -> Maybe Int
+log2 (Distance 0) = Nothing
+log2 (Distance x) = Just $ I# (integerLog2# x)
+
+
+\end{code}
+
+The DHT uses a
+\href{https://en.wikipedia.org/wiki/Metric_(mathematics)}{metric} to determine
+the distance between two nodes.  The Distance type is the co-domain of this
+metric. The metric currently used by the Tox DHT is the \texttt{XOR} of the
+nodes' public keys: \texttt{distance(x, y) = x XOR y}.  For this computation,
+public keys are interpreted as Big Endian integers (see \href{#key-1}{Crypto
+Numbers}).
+
+When we speak of a "close node", we mean that its Distance to the node under
+consideration is small compared to the Distance to other nodes.
+
+\begin{code}
+
+xorDistance :: PublicKey -> PublicKey -> Distance
+xorDistance a b =
+  Distance $ Key.keyToInteger a `xor` Key.keyToInteger b
+
+-- | rebaseDistance a b (xorDistance a c) == xorDistance b c
+rebaseDistance :: PublicKey -> PublicKey -> Distance -> Distance
+rebaseDistance a b (Distance d) =
+  Distance $ d `xor` Key.keyToInteger a `xor` Key.keyToInteger b
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary Distance where
+  arbitrary = Distance . abs <$> arbitrary
+\end{code}
+
+An implementation is not required to provide a Distance type, so it has no
+specified binary representation.  For example, instead of computing a distance
+and comparing it against another distance, the implementation can choose to
+implement Distance as a pair of public keys and define an ordering on Distance
+without computing the complete integral value.  This works, because as soon as
+an ordering decision can be made in the most significant bits, further bits
+won't influence that decision.
+
+\input{test/Network/Tox/DHT/DistanceSpec.lhs}
diff --git a/src/Network/Tox/DHT/KBuckets.lhs b/src/Network/Tox/DHT/KBuckets.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/KBuckets.lhs
@@ -0,0 +1,235 @@
+\section{K-buckets}
+
+K-buckets is a data structure for efficiently storing a set of nodes close to a
+certain key called the base key.  The base key is constant throughout the
+lifetime of a k-buckets instance.
+
+\begin{code}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE NamedFieldPuns             #-}
+{-# LANGUAGE Trustworthy                #-}
+module Network.Tox.DHT.KBuckets where
+
+import           Control.Applicative           (Applicative, (<$>))
+import           Data.Binary                   (Binary)
+import           Data.Foldable                 (toList)
+import           Data.List                     (sortBy)
+import           Data.Map                      (Map)
+import qualified Data.Map                      as Map
+import           Data.Maybe                    (isJust)
+import           Data.Ord                      (comparing)
+import           Data.Traversable              (Traversable, mapAccumR,
+                                                traverse)
+import           Data.Word                     (Word8)
+import           Test.QuickCheck.Arbitrary     (Arbitrary, arbitrary)
+import           Test.QuickCheck.Gen           (Gen)
+import qualified Test.QuickCheck.Gen           as Gen
+
+import           Network.Tox.Crypto.Key        (PublicKey)
+import           Network.Tox.DHT.ClientList    (ClientList)
+import qualified Network.Tox.DHT.ClientList    as ClientList
+import           Network.Tox.DHT.Distance      (Distance)
+import qualified Network.Tox.DHT.Distance      as Distance
+import           Network.Tox.NodeInfo.NodeInfo (NodeInfo)
+import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo
+import           Network.Tox.Time              (Timestamp)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+\end{code}
+
+A k-buckets is a map from small integers \texttt{0 <= n < 256} to Client Lists
+of maximum size $k$. Each Client List is called a (k-)bucket. A k-buckets is
+equipped with a base key, and each bucket has this key as its base key.
+\texttt{k} is called the bucket size.  The default bucket size is 8.
+A large bucket size was chosen to increase the speed at which peers are found.  
+
+\begin{code}
+
+data KBuckets = KBuckets
+  { bucketSize :: Int
+  , buckets    :: Map KBucketIndex ClientList
+  , baseKey    :: PublicKey
+  }
+  deriving (Eq, Read, Show)
+
+
+defaultBucketSize :: Int
+defaultBucketSize = 8
+
+
+empty :: PublicKey -> KBuckets
+empty = KBuckets defaultBucketSize Map.empty
+
+\end{code}
+
+The above number \texttt{n} is the bucket index.  It is a non-negative integer
+with the range \texttt{[0, 255]}, i.e. the range of an 8 bit unsigned integer.
+
+\begin{code}
+
+
+newtype KBucketIndex = KBucketIndex Word8
+  deriving (Eq, Ord, Read, Show, Num, Binary, Enum)
+
+
+\end{code}
+
+\subsection{Bucket Index}
+
+The index of the bucket can be computed using the following function:
+\texttt{bucketIndex(baseKey, nodeKey) = 255 - log\_2(distance(baseKey,
+nodeKey))}.  This function is not defined when \texttt{baseKey == nodeKey},
+meaning k-buckets will never contain a Node Info about the base node.
+
+Thus, each k-bucket contains only Node Infos for whose keys the following
+holds: if node with key \texttt{nodeKey} is in k-bucket with index \texttt{n},
+then \texttt{bucketIndex(baseKey, nodeKey) == n}. Thus, n'th k-bucket consists
+of nodes for which distance to the base node lies in range
+\verb![2^n, 2^(n+1) - 1]!.
+
+The bucket index can be efficiently computed by determining the first bit at
+which the two keys differ, starting from the most significant bit.  So, if the
+local DHT key starts with e.g. \texttt{0x80} and the bucketed node key starts
+with \texttt{0x40}, then the bucket index for that node is 0.  If the second
+bit differs, the bucket index is 1.  If the keys are almost exactly equal and
+only the last bit differs, the bucket index is 255.
+
+\begin{code}
+
+
+bucketIndex :: PublicKey -> PublicKey -> Maybe KBucketIndex
+bucketIndex pk1 pk2 =
+  fmap (\index -> 255 - fromIntegral index) $ Distance.log2 $ Distance.xorDistance pk1 pk2
+
+
+\end{code}
+
+\subsection{Manipulating k-buckets}
+
+TODO: this is different from kademlia's least-recently-seen eviction policy; why
+the existing solution was chosen, how does it affect security, performance and
+resistance to poisoning? original paper claims that preference of old live nodes
+results in better persistence and resistance to basic DDoS attacks;
+
+Any update or lookup operation on a k-buckets instance that involves a single
+node requires us to first compute the bucket index for that node.  An update
+involving a Node Info with \texttt{nodeKey == baseKey} has no effect.  If the
+update results in an empty bucket, that bucket is removed from the map.
+
+\begin{code}
+
+
+updateBucketForKey :: KBuckets -> PublicKey -> (ClientList -> ClientList) -> KBuckets
+updateBucketForKey kBuckets key f =
+  case bucketIndex (baseKey kBuckets) key of
+    Nothing    -> kBuckets
+    Just index -> updateBucketForIndex kBuckets index f
+
+
+updateBucketForIndex :: KBuckets -> KBucketIndex -> (ClientList -> ClientList) -> KBuckets
+updateBucketForIndex kBuckets@KBuckets { buckets, baseKey, bucketSize } index f =
+  let
+    -- Find the old bucket or create a new empty one.
+    updatedBucket = f $ Map.findWithDefault (ClientList.empty baseKey bucketSize) index buckets
+    -- Replace old bucket with updated bucket or delete if empty.
+    updatedBuckets =
+      if ClientList.isEmpty updatedBucket
+      then Map.delete index buckets
+      else Map.insert index updatedBucket buckets
+  in
+  kBuckets { buckets = updatedBuckets }
+
+
+\end{code}
+
+Adding a node to, or removing a node from, a k-buckets consists of performing
+the corresponding operation on the Client List bucket whose index is that of
+the node's public key, except that adding a new node to a full bucket has no
+effect.  A node is considered \textit{viable} for entry if the corresponding
+bucket is not full.
+
+\begin{code}
+
+addNode :: Timestamp -> NodeInfo -> KBuckets -> KBuckets
+addNode time nodeInfo kBuckets =
+  updateBucketForKey kBuckets publicKey $ \clientList ->
+    let
+      full = ClientList.full clientList
+      alreadyIn = isJust $ ClientList.lookup publicKey clientList
+    in
+    if not full || alreadyIn
+      then ClientList.addNode time nodeInfo clientList
+      else clientList
+  where
+    publicKey = NodeInfo.publicKey nodeInfo
+
+removeNode :: PublicKey -> KBuckets -> KBuckets
+removeNode publicKey kBuckets =
+  updateBucketForKey kBuckets publicKey $ ClientList.removeNode publicKey
+
+viable :: NodeInfo -> KBuckets -> Bool
+viable nodeInfo KBuckets{ baseKey, buckets } =
+  case bucketIndex baseKey $ NodeInfo.publicKey nodeInfo of
+    Nothing    -> False
+    Just index -> case Map.lookup index buckets of
+      Nothing     -> True
+      Just bucket -> not $ ClientList.full bucket
+
+\end{code}
+
+Iteration order of a k-buckets instance is in order of distance from the base
+key.  I.e. the first node seen in iteration is the closest, and the last node
+is the furthest away in terms of the distance metric.
+
+\begin{code}
+
+traverseClientLists ::
+    Applicative f => (ClientList -> f ClientList) -> KBuckets -> f KBuckets
+traverseClientLists f kBuckets@KBuckets{ buckets } =
+  (\x -> kBuckets{ buckets = x }) <$> traverse f (reverseT buckets)
+  where
+    reverseT :: (Traversable t) => t a -> t a
+    reverseT t = snd (mapAccumR (\ (x:xs) _ -> (xs, x)) (toList t) t)
+
+closeNodes :: PublicKey -> KBuckets -> [ (Distance, NodeInfo) ]
+closeNodes publicKey KBuckets{ baseKey, buckets } =
+  let
+    (further, at, nearer) = case bucketIndex baseKey publicKey of
+      Nothing    -> (buckets, Nothing, Map.empty)
+      Just index -> Map.splitLookup index buckets
+    clientClose = ClientList.closeNodes publicKey
+    bucketsClose = sortBy (comparing fst) . concatMap clientClose
+  in
+    concat
+      [ maybe [] clientClose at
+      , bucketsClose $ Map.elems nearer
+      , bucketsClose $ Map.elems further
+      ]
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+getAllNodes :: KBuckets -> [NodeInfo]
+getAllNodes =
+  concatMap ClientList.nodeInfos . Map.elems . buckets
+
+
+genKBuckets :: PublicKey -> Gen KBuckets
+genKBuckets publicKey =
+  foldl (flip $ uncurry addNode) (empty publicKey) <$> Gen.listOf arbitrary
+
+
+instance Arbitrary KBuckets where
+  arbitrary = arbitrary >>= genKBuckets
+\end{code}
diff --git a/src/Network/Tox/DHT/NodeList.lhs b/src/Network/Tox/DHT/NodeList.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/NodeList.lhs
@@ -0,0 +1,75 @@
+The Close List and the Search Entries are termed the \texttt{Node Lists} of
+the DHT State.
+
+\begin{code}
+module Network.Tox.DHT.NodeList where
+
+import           Control.Applicative           (Applicative, Const (..),
+                                                getConst)
+import           Control.Monad                 (guard)
+import           Data.Maybe                    (listToMaybe)
+import           Data.Monoid                   (Dual (..), Endo (..), Monoid,
+                                                appEndo, getDual, mempty)
+
+import           Network.Tox.Crypto.Key        (PublicKey)
+import           Network.Tox.DHT.ClientList    (ClientList)
+import qualified Network.Tox.DHT.ClientList    as ClientList
+import           Network.Tox.DHT.Distance      (Distance)
+import           Network.Tox.DHT.KBuckets      (KBuckets)
+import qualified Network.Tox.DHT.KBuckets      as KBuckets
+import           Network.Tox.NodeInfo.NodeInfo (NodeInfo)
+import           Network.Tox.Time              (Timestamp)
+
+class NodeList l where
+  addNode :: Timestamp -> NodeInfo -> l -> l
+
+  removeNode :: PublicKey -> l -> l
+
+  viable :: NodeInfo -> l -> Bool
+
+  baseKey :: l -> PublicKey
+
+  traverseClientLists ::
+    Applicative f => (ClientList -> f ClientList) -> l -> f l
+
+  -- | 'closeNodes pub' returns the (pub',node) pairs of the Node List in
+  -- increasing order of distance of pub' from pub.
+  closeNodes :: PublicKey -> l -> [(Distance, NodeInfo)]
+
+  -- | copied from Data.Traversable.foldMapDefault
+  foldMapClientLists :: Monoid m => (ClientList -> m) -> l -> m
+  foldMapClientLists f = getConst . traverseClientLists (Const . f)
+
+  -- | copied from Data.Foldable.foldl
+  foldlClientLists :: (a -> ClientList -> a) -> a -> l -> a
+  foldlClientLists f z t =
+    appEndo (getDual (foldMapClientLists (Dual . Endo . flip f) t)) z
+
+  nodeListList :: l -> [NodeInfo]
+  nodeListList = foldMapClientLists ClientList.nodeInfos
+
+  foldNodes :: (a -> NodeInfo -> a) -> a -> l -> a
+  foldNodes = foldlClientLists . ClientList.foldNodes
+
+  lookupPublicKey :: PublicKey -> l -> Maybe NodeInfo
+  lookupPublicKey publicKey list = do
+    (dist,node) <- listToMaybe $ closeNodes publicKey list
+    guard (dist == mempty)
+    Just node
+
+instance NodeList ClientList where
+  addNode = ClientList.addNode
+  removeNode = ClientList.removeNode
+  viable = ClientList.viable
+  baseKey = ClientList.baseKey
+  traverseClientLists = id
+  closeNodes = ClientList.closeNodes
+
+instance NodeList KBuckets where
+  addNode = KBuckets.addNode
+  removeNode = KBuckets.removeNode
+  viable = KBuckets.viable
+  baseKey = KBuckets.baseKey
+  traverseClientLists = KBuckets.traverseClientLists
+  closeNodes = KBuckets.closeNodes
+\end{code}
diff --git a/src/Network/Tox/DHT/NodesRequest.lhs b/src/Network/Tox/DHT/NodesRequest.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/NodesRequest.lhs
@@ -0,0 +1,51 @@
+\subsubsection{Nodes Request (0x02)}
+
+\begin{tabular}{l|l|l}
+  Length             & Type        & \href{#rpc-services}{Contents} \\
+  \hline
+  \texttt{32}        & Public Key  & Requested DHT Public Key \\
+\end{tabular}
+
+The DHT Public Key sent in the request is the one the sender is searching for.
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE Safe               #-}
+module Network.Tox.DHT.NodesRequest where
+
+import           Control.Applicative       ((<$>))
+import           Data.Binary               (Binary)
+import           Data.MessagePack          (MessagePack)
+import           Data.Typeable             (Typeable)
+import           GHC.Generics              (Generic)
+import           Network.Tox.Crypto.Key    (PublicKey)
+import           Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+newtype NodesRequest = NodesRequest
+  { requestedKey :: PublicKey
+  }
+  deriving (Eq, Read, Show, Generic, Typeable)
+
+instance Binary NodesRequest
+instance MessagePack NodesRequest
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary NodesRequest where
+  arbitrary = NodesRequest <$> arbitrary
+\end{code}
diff --git a/src/Network/Tox/DHT/NodesResponse.lhs b/src/Network/Tox/DHT/NodesResponse.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/NodesResponse.lhs
@@ -0,0 +1,67 @@
+\subsubsection{Nodes Response (0x04)}
+
+\begin{tabular}{l|l|l}
+  Length             & Type        & \href{#rpc-services}{Contents} \\
+  \hline
+  \texttt{1}         & Int         & Number of nodes in the response (maximum 4) \\
+  \texttt{[39, 204]} & Node Infos  & Nodes in Packed Node Format \\
+\end{tabular}
+
+An IPv4 node is 39 bytes, an IPv6 node is 51 bytes, so the maximum size of the
+packed Node Infos is \texttt{51 * 4 = 204} bytes.
+
+Nodes responses should contain the 4 closest nodes that the sender of the
+response has in their lists of known nodes.
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE Safe               #-}
+module Network.Tox.DHT.NodesResponse where
+
+import           Control.Applicative           ((<$>))
+import           Data.Binary                   (Binary, get, put)
+import qualified Data.Binary.Get               as Binary (getWord8)
+import qualified Data.Binary.Put               as Binary (putWord8)
+import           Data.MessagePack              (MessagePack)
+import           Data.Typeable                 (Typeable)
+import           GHC.Generics                  (Generic)
+import           Network.Tox.NodeInfo.NodeInfo (NodeInfo)
+import           Test.QuickCheck.Arbitrary     (Arbitrary, arbitrary)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+newtype NodesResponse = NodesResponse
+  { foundNodes :: [NodeInfo]
+  }
+  deriving (Eq, Read, Show, Generic, Typeable)
+
+instance MessagePack NodesResponse
+
+
+instance Binary NodesResponse where
+  put res = do
+    Binary.putWord8 . fromInteger . toInteger . length . foundNodes $ res
+    mapM_ put (foundNodes res)
+
+  get = do
+    count <- Binary.getWord8
+    NodesResponse <$> mapM (const get) [1..count]
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary NodesResponse where
+  arbitrary = NodesResponse <$> arbitrary
+\end{code}
diff --git a/src/Network/Tox/DHT/Operation.lhs b/src/Network/Tox/DHT/Operation.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/Operation.lhs
@@ -0,0 +1,589 @@
+\section{DHT Operation}
+
+\begin{code}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NamedFieldPuns        #-}
+{-# LANGUAGE RankNTypes            #-}
+{-# LANGUAGE Safe                  #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
+module Network.Tox.DHT.Operation where
+
+import           Control.Applicative                  (Applicative, pure, (<$>),
+                                                       (<*>))
+import           Control.Monad                        (guard, msum, replicateM,
+                                                       unless, void, when)
+import           Control.Monad.Identity               (Identity, runIdentity)
+import           Control.Monad.Random                 (RandT, evalRandT)
+import           Control.Monad.State                  (MonadState, StateT,
+                                                       execStateT, get, gets,
+                                                       modify, put, runStateT)
+import           Control.Monad.Trans                  (lift)
+import           Control.Monad.Trans.Maybe            (MaybeT (..), runMaybeT)
+import           Control.Monad.Writer                 (MonadWriter, WriterT,
+                                                       execWriterT, tell)
+import           Data.Binary                          (Binary)
+import           Data.Foldable                        (for_)
+import           Data.Functor                         (($>))
+import           Data.Map                             (Map)
+import qualified Data.Map                             as Map
+import           Data.Maybe                           (isNothing)
+import           Data.Traversable                     (traverse)
+import           Lens.Family2                         (Lens')
+import           Lens.Family2.State                   (zoom, (%%=), (%=))
+import           System.Random                        (StdGen, mkStdGen)
+import           Test.QuickCheck.Arbitrary            (Arbitrary, arbitrary)
+
+import           Network.Tox.Crypto.Key               (PublicKey)
+import           Network.Tox.Crypto.Keyed             (Keyed)
+import           Network.Tox.Crypto.KeyedT            (KeyedT)
+import qualified Network.Tox.Crypto.KeyedT            as KeyedT
+import qualified Network.Tox.Crypto.KeyPair           as KeyPair
+import           Network.Tox.DHT.ClientList           (ClientList)
+import qualified Network.Tox.DHT.ClientList           as ClientList
+import           Network.Tox.DHT.ClientNode           (ClientNode)
+import qualified Network.Tox.DHT.ClientNode           as ClientNode
+import qualified Network.Tox.DHT.DhtPacket            as DhtPacket
+import           Network.Tox.DHT.DhtRequestPacket     (DhtRequestPacket (..))
+import           Network.Tox.DHT.DhtState             (DhtState)
+import qualified Network.Tox.DHT.DhtState             as DhtState
+import           Network.Tox.DHT.NodeList             (NodeList)
+import qualified Network.Tox.DHT.NodeList             as NodeList
+import           Network.Tox.DHT.NodesRequest         (NodesRequest (..))
+import           Network.Tox.DHT.NodesResponse        (NodesResponse (..))
+import qualified Network.Tox.DHT.PendingReplies       as PendingReplies
+import           Network.Tox.DHT.PingPacket           (PingPacket (..))
+import           Network.Tox.DHT.RpcPacket            (RpcPacket (..))
+import qualified Network.Tox.DHT.RpcPacket            as RpcPacket
+import qualified Network.Tox.DHT.Stamped              as Stamped
+import           Network.Tox.Network.MonadRandomBytes (MonadRandomBytes)
+import qualified Network.Tox.Network.MonadRandomBytes as MonadRandomBytes
+import           Network.Tox.Network.Networked        (Networked)
+import qualified Network.Tox.Network.Networked        as Networked
+import           Network.Tox.NodeInfo.NodeInfo        (NodeInfo)
+import qualified Network.Tox.NodeInfo.NodeInfo        as NodeInfo
+import           Network.Tox.Protocol.Packet          (Packet (..))
+import           Network.Tox.Protocol.PacketKind      (PacketKind)
+import qualified Network.Tox.Protocol.PacketKind      as PacketKind
+import           Network.Tox.Time                     (TimeDiff, Timestamp)
+import qualified Network.Tox.Time                     as Time
+import           Network.Tox.Timed                    (Timed)
+import qualified Network.Tox.Timed                    as Timed
+import           Network.Tox.TimedT                   (TimedT)
+import qualified Network.Tox.TimedT                   as TimedT
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+class
+  ( Networked m
+  , Timed m
+  , MonadRandomBytes m
+  , MonadState DhtState m
+  , Keyed m
+  ) => DhtNodeMonad m where {}
+
+data RequestInfo = RequestInfo
+  { requestTo     :: NodeInfo
+  , requestSearch :: PublicKey
+  }
+  deriving (Eq, Read, Show)
+
+sendDhtPacket :: (DhtNodeMonad m, Binary payload) =>
+  NodeInfo -> PacketKind -> payload -> m ()
+sendDhtPacket to kind payload = do
+  keyPair <- gets DhtState.dhtKeyPair
+  nonce <- MonadRandomBytes.randomNonce
+  Networked.sendPacket to . Packet kind =<<
+    DhtPacket.encodeKeyed keyPair (NodeInfo.publicKey to) nonce payload
+
+sendRpcRequest :: (DhtNodeMonad m, Binary payload) =>
+  NodeInfo -> PacketKind -> payload -> m ()
+sendRpcRequest to packetKind payload = do
+  requestId <- RpcPacket.RequestId <$> MonadRandomBytes.randomWord64
+  time <- Timed.askTime
+  DhtState._dhtPendingReplies %= PendingReplies.expectReply time to requestId
+  sendDhtPacket to packetKind $
+    RpcPacket payload requestId
+
+sendNodesRequest :: DhtNodeMonad m => RequestInfo -> m ()
+sendNodesRequest (RequestInfo to key) =
+  sendRpcRequest to PacketKind.NodesRequest $ NodesRequest key
+
+sendNodesResponse ::
+  DhtNodeMonad m => NodeInfo -> RpcPacket.RequestId -> [NodeInfo] -> m ()
+sendNodesResponse to requestId nodes =
+  sendDhtPacket to PacketKind.NodesResponse $
+    RpcPacket (NodesResponse nodes) requestId
+
+sendPingRequest :: DhtNodeMonad m => NodeInfo -> m ()
+sendPingRequest to =
+  sendRpcRequest to PacketKind.PingRequest PingRequest
+
+sendPingResponse ::
+  DhtNodeMonad m => NodeInfo -> RpcPacket.RequestId -> m ()
+sendPingResponse to requestId =
+  sendDhtPacket to PacketKind.PingResponse $
+    RpcPacket PingResponse requestId
+
+modifyM :: MonadState s m => (s -> m s) -> m ()
+modifyM = (put =<<) . (get >>=)
+
+-- | adapted from michaelt's lens-simple:
+-- zoom_ is like zoom but for convenience returns an mtl style
+-- abstracted MonadState state, rather than a concrete StateT, recapturing
+-- a bit more of the abstractness of Control.Lens.zoom
+zoom_ :: MonadState s' m => Lens' s' s -> StateT s m a -> m a
+-- full signature:
+-- zoom_ :: MonadState s' m =>
+--   LensLike' (Zooming m a) s' s -> StateT s m a -> m a
+zoom_ l f = abstract $ zoom l f
+  where
+    abstract :: MonadState s m => StateT s m a -> m a
+    abstract st = do
+      (a,s') <- runStateT st =<< get
+      put s'
+      return a
+
+\end{code}
+
+\subsection{DHT Initialisation}
+A new DHT node is initialised with a DHT State with a fresh random key pair, an
+empty close list, and a search list containing 2 empty search entries searching
+for the public keys of fresh random key pairs.
+
+\begin{code}
+
+initRandomSearches :: Int
+initRandomSearches = 2
+
+initDht :: (MonadRandomBytes m, Timed m) => m DhtState
+initDht = do
+  dhtState <- DhtState.empty <$> Timed.askTime <*> MonadRandomBytes.newKeyPair
+  time <- Timed.askTime
+  (`execStateT` dhtState) $ replicateM initRandomSearches $ do
+    publicKey <- KeyPair.publicKey <$> MonadRandomBytes.newKeyPair
+    DhtState._dhtSearchList %=
+      Map.insert publicKey (DhtState.emptySearchEntry time publicKey)
+
+bootstrapNode :: DhtNodeMonad m => NodeInfo -> m ()
+bootstrapNode nodeInfo =
+  sendNodesRequest . RequestInfo nodeInfo =<<
+    KeyPair.publicKey <$> gets DhtState.dhtKeyPair
+
+-- TODO
+--loadDHT :: ??
+
+\end{code}
+
+\subsection{Periodic sending of Nodes Requests}
+For each Nodes List in the DHT State, every 20 seconds we send a Nodes Request
+to a random node on the list, searching for the base key of the list.
+
+When a Nodes List first becomes populated with nodes, we send 5 such random
+Nodes Requests in quick succession.
+
+Random nodes are chosen since being able to predict which node a node will
+send a request to next could make some attacks that disrupt the network
+easier, as it adds a possible attack vector.
+
+\begin{code}
+
+randomRequestPeriod :: TimeDiff
+randomRequestPeriod = Time.seconds 20
+
+maxBootstrapTimes :: Int
+maxBootstrapTimes = 5
+
+randomRequests :: DhtNodeMonad m => WriterT [RequestInfo] m ()
+randomRequests = do
+  closeList <- gets DhtState.dhtCloseList
+  zoom_ DhtState._dhtCloseListStamp $ doList closeList
+  zoom_ DhtState._dhtSearchList .
+    modifyM . traverse . execStateT $ do
+      searchList <- gets DhtState.searchClientList
+      zoom_ DhtState._searchStamp $ doList searchList
+  where
+    doList ::
+      ( NodeList l
+      , Timed m
+      , MonadRandomBytes m
+      , MonadState DhtState.ListStamp m
+      , MonadWriter [RequestInfo] m
+      ) => l -> m ()
+    doList nodeList =
+      case NodeList.nodeListList nodeList of
+        [] -> return ()
+        nodes -> do
+          time <- Timed.askTime
+          DhtState.ListStamp lastTime bootstrapped <- get
+          when (time Time.- lastTime >= randomRequestPeriod
+              || bootstrapped < maxBootstrapTimes) $ do
+            node <- MonadRandomBytes.uniform nodes
+            tell [RequestInfo node $ NodeList.baseKey nodeList]
+            put $ DhtState.ListStamp time (bootstrapped + 1)
+
+\end{code}
+
+Furthermore, we periodically check every node for responsiveness by sending it a
+Nodes Request: for each Nodes List in the DHT State, we send each node on the
+list a Nodes Request every 60 seconds, searching for the base key of the list.
+We remove from the DHT State any node from which we persistently fail to receive
+Nodes Responses.
+
+c-toxcore's implementation of checking and timeouts:
+A Last Checked time is maintained for each node in each list. When a node is
+added to a list, if doing so evicts a node from the list then the Last Checked
+time is set to that of the evicted node, and otherwise it is set to 0. This
+includes updating an already present node. Nodes from which we have not
+received a Nodes Response for 122 seconds are considered Bad; they remain in the
+DHT State, but are preferentially overwritten when adding to the DHT State, and
+are ignored for all operations except the once-per-60s checking described above.
+If we have not received a Nodes Response for 182 seconds, the node is not even
+checked. So one check is sent after the node becomes Bad. In the special case
+that every node in the Close List is Bad, they are all checked once more.)
+
+hs-toxcore implementation of checking and timeouts:
+We maintain a Last Checked timestamp and a Checks Counter on each node on each
+Nodes List in the Dht State. When a node is added to a list, these are set
+respectively to the current time and to 0. This includes updating an already
+present node. We periodically pass through the nodes on the lists, and for each
+which is due a check, we: check it, update the timestamp, increment the counter,
+and, if the counter is then 2, remove the node from the list. This is pretty
+close to the behaviour of c-toxcore, but much simpler. TODO: currently hs-toxcore
+doesn't do anything to try to recover if the Close List becomes empty. We could
+maintain a separate list of the most recently heard from nodes, and repopulate
+the Close List with that if the Close List becomes empty.
+
+\begin{code}
+
+checkPeriod :: TimeDiff
+checkPeriod = Time.seconds 60
+
+maxChecks :: Int
+maxChecks = 2
+
+checkNodes :: forall m. DhtNodeMonad m => WriterT [RequestInfo] m ()
+checkNodes = modifyM $ DhtState.traverseClientLists checkNodes'
+  where
+    checkNodes' :: ClientList -> WriterT [RequestInfo] m ClientList
+    checkNodes' clientList =
+      (\x -> clientList{ ClientList.nodes = x }) <$>
+        traverseMaybe checkNode (ClientList.nodes clientList)
+      where
+        traverseMaybe :: Applicative f =>
+          (a -> f (Maybe b)) -> Map k a -> f (Map k b)
+        traverseMaybe f = (Map.mapMaybe id <$>) . traverse f
+
+        checkNode :: ClientNode -> WriterT [RequestInfo] m (Maybe ClientNode)
+        checkNode clientNode = Timed.askTime >>= \time ->
+          if time Time.- lastCheck < checkPeriod
+          then pure $ Just clientNode
+          else (tell [requestInfo] $>) $
+            if checkCount + 1 < maxChecks
+            then Just $ clientNode
+              { ClientNode.lastCheck = time
+              , ClientNode.checkCount = checkCount + 1
+              }
+            else Nothing
+          where
+            nodeInfo = ClientNode.nodeInfo clientNode
+            lastCheck = ClientNode.lastCheck clientNode
+            checkCount = ClientNode.checkCount clientNode
+            requestInfo = RequestInfo nodeInfo $ NodeList.baseKey clientList
+
+doDHT :: DhtNodeMonad m => m ()
+doDHT =
+  execWriterT (randomRequests >> checkNodes) >>= mapM_ sendNodesRequest
+
+
+\end{code}
+
+\subsection{Handling Nodes Response packets}
+When we receive a valid Nodes Response packet, we first check that it is a reply
+to a Nodes Request which we sent within the last 60 seconds to the node from
+which we received the response, and that no previous reply has been received. If
+this check fails, the packet is ignored. If the check succeeds, first we add to
+the DHT State the node from which the response was sent. Then, for each node
+listed in the response and for each Nodes List in the DHT State which does not
+currently contain the node and to which the node is viable for entry, we send a
+Nodes Request to the node with the requested public key being the base key of
+the Nodes List.
+
+An implementation may choose not to send every such Nodes Request.
+(c-toxcore only sends so many per list (8 for the Close List, 4 for a Search
+Entry) per 50ms, prioritising the closest to the base key).
+
+\begin{code}
+
+requireNodesResponseWithin :: TimeDiff
+requireNodesResponseWithin = Time.seconds 60
+
+handleNodesResponse ::
+  DhtNodeMonad m => NodeInfo -> RpcPacket NodesResponse -> m ()
+handleNodesResponse from (RpcPacket (NodesResponse nodes) requestId) = do
+  isReply <- checkPending requireNodesResponseWithin from requestId
+  when isReply $ do
+    time <- Timed.askTime
+    modify $ DhtState.addNode time from
+    for_ nodes $ \node ->
+      (>>= mapM_ sendNodesRequest) $ (<$> get) $ DhtState.foldMapNodeLists $
+        \nodeList ->
+          guard (isNothing (NodeList.lookupPublicKey
+              (NodeInfo.publicKey node) nodeList)
+            && NodeList.viable node nodeList) >>
+          [ RequestInfo node $ NodeList.baseKey nodeList ]
+
+\end{code}
+
+\subsection{Handling Nodes Request packets}
+When we receive a Nodes Request packet from another node, we reply with a Nodes
+Response packet containing the 4 nodes in the DHT State which are the closest to
+the public key in the packet. If there are fewer than 4 nodes in the state, we
+reply with all the nodes in the state. If there are no nodes in the state, no
+reply is sent.
+
+We also send a Ping Request when this is appropriate; see below.
+
+\begin{code}
+
+responseMaxNodes :: Int
+responseMaxNodes = 4
+
+handleNodesRequest ::
+  DhtNodeMonad m => NodeInfo -> RpcPacket NodesRequest -> m ()
+handleNodesRequest from (RpcPacket (NodesRequest key) requestId) = do
+  ourPublicKey <- gets $ KeyPair.publicKey . DhtState.dhtKeyPair
+  when (ourPublicKey /= NodeInfo.publicKey from) $ do
+    nodes <- gets (DhtState.takeClosestNodesTo responseMaxNodes key)
+    unless (null nodes) $ sendNodesResponse from requestId nodes
+    sendPingRequestIfAppropriate from
+
+\end{code}
+
+\subsection{Handling Ping Request packets}
+When a valid Ping Request packet is received, we reply with a Ping Response.
+
+We also send a Ping Request when this is appropriate; see below.
+
+\begin{code}
+
+handlePingRequest ::
+  DhtNodeMonad m => NodeInfo -> RpcPacket PingPacket -> m ()
+handlePingRequest from (RpcPacket PingRequest requestId) = do
+  sendPingResponse from requestId
+  sendPingRequestIfAppropriate from
+handlePingRequest _ _ = return ()
+
+\end{code}
+
+\subsection{Handling Ping Response packets}
+When we receive a valid Ping Response packet, we first check that it is a reply
+to a Ping Request which we sent within the last 5 seconds to the node from
+which we received the response, and that no previous reply has been received. If
+this check fails, the packet is ignored. If the check succeeds, we add to the
+DHT State the node from which the response was sent.
+
+\begin{code}
+
+requirePingResponseWithin :: TimeDiff
+requirePingResponseWithin = Time.seconds 5
+
+maxPendingTime :: TimeDiff
+maxPendingTime = maximum
+  [ requireNodesResponseWithin
+  , requirePingResponseWithin
+  ]
+
+checkPending :: DhtNodeMonad m =>
+  TimeDiff -> NodeInfo -> RpcPacket.RequestId -> m Bool
+checkPending timeLimit from requestId = do
+  oldTime <- (Time.+ negate maxPendingTime) <$> Timed.askTime
+  DhtState._dhtPendingReplies %= Stamped.dropOlder oldTime
+  recentCutoff <- (Time.+ negate timeLimit) <$> Timed.askTime
+  DhtState._dhtPendingReplies %%=
+    PendingReplies.checkExpectedReply recentCutoff from requestId
+
+handlePingResponse ::
+  DhtNodeMonad m => NodeInfo -> RpcPacket PingPacket -> m ()
+handlePingResponse from (RpcPacket PingResponse requestId) = do
+  isReply <- checkPending requirePingResponseWithin from requestId
+  ourPublicKey <- gets $ KeyPair.publicKey . DhtState.dhtKeyPair
+  when (isReply && ourPublicKey /= NodeInfo.publicKey from) $ do
+    time <- Timed.askTime
+    modify $ DhtState.addNode time from
+handlePingResponse _ _ = return ()
+
+\end{code}
+
+\subsection{Sending Ping Requests}
+When we receive a Nodes Request or a Ping Request, in addition to the handling
+described above, we sometimes send a Ping Request.
+Namely, we send a Ping Request to the node which sent the packet if the node is
+viable for entry to the Close List and is not already in the Close List.
+An implementation may (TODO: should?) choose not to send every such Ping
+Request.
+(c-toxcore sends at most 32 every 2 seconds, preferring closer nodes.)
+
+\begin{code}
+
+sendPingRequestIfAppropriate :: DhtNodeMonad m => NodeInfo -> m ()
+sendPingRequestIfAppropriate from = do
+  closeList <- gets DhtState.dhtCloseList
+  when
+    (isNothing (NodeList.lookupPublicKey (NodeInfo.publicKey from) closeList)
+      && NodeList.viable from closeList) $
+    sendPingRequest from
+
+\end{code}
+
+\input{src/Network/Tox/DHT/DhtRequestPacket.lhs}
+\subsection{Handling DHT Request packets}
+
+A DHT node that receives a DHT request packet checks whether the addressee
+public key is their DHT public key. If it is, they will decrypt and handle
+the packet.  Otherwise, they will check whether the addressee DHT public key
+is the DHT public key of one of the nodes in their Close List.  If it isn't,
+they will drop the packet.  If it is they will resend the packet, unaltered, to
+that DHT node.
+
+DHT request packets are used for DHT public key packets (see
+\href{#onion}{onion}) and NAT ping packets.
+
+\begin{code}
+
+handleDhtRequestPacket :: DhtNodeMonad m => NodeInfo -> DhtRequestPacket -> m ()
+handleDhtRequestPacket _from packet@DhtRequestPacket{ addresseePublicKey, dhtPacket } = do
+  keyPair <- gets DhtState.dhtKeyPair
+  if addresseePublicKey == KeyPair.publicKey keyPair
+  then void . runMaybeT $ msum
+    [ MaybeT (DhtPacket.decodeKeyed keyPair dhtPacket) >>= lift . handleNatPingPacket
+    , MaybeT (DhtPacket.decodeKeyed keyPair dhtPacket) >>= lift . handleDhtPKPacket
+    ]
+  else void . runMaybeT $ do
+    node :: NodeInfo <- MaybeT $
+      NodeList.lookupPublicKey addresseePublicKey <$> gets DhtState.dhtCloseList
+    lift . Networked.sendPacket node . Packet PacketKind.Crypto $ packet
+
+\end{code}
+
+\subsection{NAT ping packets}
+
+A NAT ping packet is sent as the payload of a DHT request packet.
+
+We use NAT ping packets to see if a friend we are not connected to directly is
+online and ready to do the hole punching.
+
+\subsubsection{NAT ping request}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0xfe) \\
+  \texttt{1}         & \texttt{uint8\_t} (0x00) \\
+  \texttt{8}         & \texttt{uint64\_t} random number \\
+\end{tabular}
+
+\subsubsection{NAT ping response}
+
+\begin{tabular}{l|l}
+  Length             & Contents \\
+  \hline
+  \texttt{1}         & \texttt{uint8\_t} (0xfe) \\
+  \texttt{1}         & \texttt{uint8\_t} (0x01) \\
+  \texttt{8}         & \texttt{uint64\_t} random number (the same that was received in request) \\
+\end{tabular}
+
+TODO: handling these packets.
+
+\begin{code}
+
+-- | TODO
+type NatPingPacket = ()
+handleNatPingPacket :: DhtNodeMonad m => NatPingPacket -> m ()
+handleNatPingPacket _ = return ()
+
+-- | TODO
+type DhtPKPacket = ()
+handleDhtPKPacket :: DhtNodeMonad m => DhtPKPacket -> m ()
+handleDhtPKPacket _ = return ()
+
+\end{code}
+
+\subsection{Effects of chosen constants on performance}
+If the bucket size of the k-buckets were increased, it would increase the
+amount of packets needed to check if each node is still alive, which would
+increase the bandwidth usage, but reliability would go up.  If the number of
+nodes were decreased, reliability would go down along with bandwidth usage.
+The reason for this relationship between reliability and number of nodes is
+that if we assume that not every node has its UDP ports open or is behind a
+cone NAT it means that each of these nodes must be able to store a certain
+number of nodes behind restrictive NATs in order for others to be able to find
+those nodes behind restrictive NATs.  For example if 7/8 nodes were behind
+restrictive NATs, using 8 nodes would not be enough because the chances of
+some of these nodes being impossible to find in the network would be too high.
+
+TODO(zugz): this seems a rather wasteful solution to this problem.
+
+If the ping timeouts and delays between pings were higher it would decrease the
+bandwidth usage but increase the amount of disconnected nodes that are still
+being stored in the lists.  Decreasing these delays would do the opposite.
+
+If the maximum size 8 of the DHT Search Entry Client Lists were increased
+would increase the bandwidth usage, might increase hole punching efficiency on
+symmetric NATs (more ports to guess from, see Hole punching) and might increase
+the reliability.  Lowering this number would have the opposite effect.
+
+The timeouts and number of nodes in lists for toxcore were picked by feeling
+alone and are probably not the best values.  This also applies to the behavior
+which is simple and should be improved in order to make the network resist
+better to sybil attacks.
+
+TODO: consider giving min and max values for the constants.
+
+\begin{code}
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+type TestDhtNodeMonad = KeyedT (TimedT (RandT StdGen (StateT DhtState (Networked.NetworkLogged Identity))))
+instance DhtNodeMonad TestDhtNodeMonad
+
+runTestDhtNode :: ArbStdGen -> Timestamp -> DhtState -> TestDhtNodeMonad a -> (a, DhtState)
+runTestDhtNode seed time s =
+  runIdentity
+    . Networked.evalNetworkLogged
+    . (`runStateT` s)
+    . (`evalRandT` unwrapArbStdGen seed)
+    . (`TimedT.runTimedT` time)
+    . (`KeyedT.evalKeyedT` Map.empty)
+
+evalTestDhtNode :: ArbStdGen -> Timestamp -> DhtState -> TestDhtNodeMonad a -> a
+evalTestDhtNode seed time s = fst . runTestDhtNode seed time s
+execTestDhtNode :: ArbStdGen -> Timestamp -> DhtState -> TestDhtNodeMonad a -> DhtState
+execTestDhtNode seed time s = snd . runTestDhtNode seed time s
+
+initTestDhtState :: ArbStdGen -> Timestamp -> DhtState
+initTestDhtState seed time =
+  runIdentity
+    . (`evalRandT` unwrapArbStdGen seed)
+    . (`TimedT.runTimedT` time)
+    $ initDht
+
+-- | wrap StdGen so the Arbitrary instance isn't an orphan
+newtype ArbStdGen = ArbStdGen { unwrapArbStdGen :: StdGen }
+  deriving (Read, Show)
+
+instance Arbitrary ArbStdGen
+  where arbitrary = ArbStdGen . mkStdGen <$> arbitrary
+
+\end{code}
+
diff --git a/src/Network/Tox/DHT/PendingReplies.lhs b/src/Network/Tox/DHT/PendingReplies.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/PendingReplies.lhs
@@ -0,0 +1,45 @@
+\subsection{Replies to RPC requests}
+A \textit{reply} to a Request packet is a Response packet with the Request ID in
+the Response packet set equal to the Request ID in the Request packet.  A
+response is accepted if and only if it is the first received reply to a request
+which was sent sufficiently recently, according to a time limit which depends on
+the service.
+
+\begin{code}
+{-# LANGUAGE Safe #-}
+module Network.Tox.DHT.PendingReplies where
+
+import qualified Network.Tox.DHT.RpcPacket     as RpcPacket
+import           Network.Tox.DHT.Stamped       (Stamped)
+import qualified Network.Tox.DHT.Stamped       as Stamped
+import           Network.Tox.NodeInfo.NodeInfo (NodeInfo)
+import           Network.Tox.Time              (Timestamp)
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+type PendingReplies = Stamped (NodeInfo, RpcPacket.RequestId)
+
+expectReply :: Timestamp -> NodeInfo -> RpcPacket.RequestId ->
+  PendingReplies -> PendingReplies
+expectReply time node requestId = Stamped.add time (node, requestId)
+
+checkExpectedReply :: Timestamp -> NodeInfo -> RpcPacket.RequestId ->
+  PendingReplies -> (Bool, PendingReplies)
+checkExpectedReply cutoff node requestId pendingReplies =
+  case filter (>= cutoff) $
+    Stamped.findStamps (== (node, requestId)) pendingReplies
+  of
+    []     -> (False, pendingReplies)
+    time:_ -> (True, Stamped.delete time (node, requestId) pendingReplies)
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+\end{code}
diff --git a/src/Network/Tox/DHT/PingPacket.lhs b/src/Network/Tox/DHT/PingPacket.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/PingPacket.lhs
@@ -0,0 +1,78 @@
+\subsection{Ping Service}
+
+The Ping Service is used to check if a node is responsive.
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE Safe               #-}
+module Network.Tox.DHT.PingPacket where
+
+import           Data.Binary               (Binary)
+import           Data.MessagePack          (MessagePack)
+import           Data.Typeable             (Typeable)
+import           GHC.Generics              (Generic)
+import           Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
+import qualified Test.QuickCheck.Gen       as Gen
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+\end{code}
+
+A Ping Packet payload consists of just a boolean value saying whether it is a
+request or a response.
+
+The one byte boolean inside the encrypted payload is added to prevent peers
+from creating a valid Ping Response from a Ping Request without decrypting the
+packet and encrypting a new one.  Since symmetric encryption is used, the
+encrypted Ping Response would be byte-wise equal to the Ping Request without
+the discriminator byte.
+
+\begin{tabular}{l|l|l}
+  Length             & Type        & \href{#rpc-services}{Contents} \\
+  \hline
+  \texttt{1}         & Bool        & Response flag: 0x00 for Request, 0x01 for Response \\
+\end{tabular}
+
+\subsubsection{Ping Request (0x00)}
+
+A Ping Request is a Ping Packet with the response flag set to False.  When a
+Ping Request is received and successfully decrypted, a Ping Response packet is
+created and sent back to the requestor.
+
+\subsubsection{Ping Response (0x01)}
+
+A Ping Response is a Ping Packet with the response flag set to True.
+
+\begin{code}
+
+
+data PingPacket
+  = PingRequest
+  | PingResponse
+  deriving (Eq, Read, Show, Generic, Typeable)
+
+instance Binary PingPacket
+instance MessagePack PingPacket
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary PingPacket where
+  arbitrary =
+    Gen.elements
+      [ PingRequest
+      , PingResponse
+      ]
+\end{code}
diff --git a/src/Network/Tox/DHT/RpcPacket.lhs b/src/Network/Tox/DHT/RpcPacket.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/RpcPacket.lhs
@@ -0,0 +1,85 @@
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable         #-}
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE Trustworthy                #-}
+module Network.Tox.DHT.RpcPacket where
+
+import           Control.Applicative       ((<$>), (<*>))
+import           Data.Binary               (Binary)
+import           Data.MessagePack          (MessagePack)
+import           Data.Typeable             (Typeable)
+import           Data.Word                 (Word64)
+import           GHC.Generics              (Generic)
+import           Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+\end{code}
+
+A DHT RPC Service consists of a Request packet and a Response packet.  A DHT
+RPC Packet contains a payload and a Request ID.  This ID is a 64 bit unsigned
+integer that helps identify the response for a given request.
+
+\begin{code}
+
+newtype RequestId = RequestId Word64
+  deriving (Eq, Read, Show, Binary, Arbitrary, Generic)
+
+instance MessagePack RequestId
+
+\end{code}
+
+\input{src/Network/Tox/DHT/PendingReplies.lhs}
+
+DHT RPC Packets are encrypted and transported within DHT Packets.
+
+\begin{tabular}{l|l|l}
+  Length             & Type               & \href{#dht-packet}{Contents} \\
+  \hline
+  \texttt{[0,]}      & Bytes              & Payload \\
+  \texttt{8}         & \texttt{uint64\_t}  & Request ID \\
+\end{tabular}
+
+The minimum payload size is 0, but in reality the smallest sensible payload
+size is 1.  Since the same symmetric key is used in both communication
+directions, an encrypted Request would be a valid encrypted Response if they
+contained the same plaintext.
+
+\begin{code}
+
+data RpcPacket payload = RpcPacket
+  { rpcPayload :: payload
+  , requestId  :: RequestId
+  }
+  deriving (Eq, Read, Show, Generic, Typeable)
+
+instance Binary payload => Binary (RpcPacket payload)
+instance MessagePack payload => MessagePack (RpcPacket payload)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary payload => Arbitrary (RpcPacket payload) where
+  arbitrary =
+    RpcPacket <$> arbitrary <*> arbitrary
+\end{code}
+
+Parts of the protocol using RPC packets must take care to make Request payloads
+not be valid Response payloads.  For instance, \href{#ping-service}{Ping
+Packets} carry a boolean flag that indicate whether the payload corresponds to
+a Request or a Response.
+
+The Request ID provides some resistance against replay attacks.  If there were
+no Request ID, it would be easy for an attacker to replay old responses and
+thus provide nodes with out-of-date information.  A Request ID should be
+randomly generated for each Request which is sent.
diff --git a/src/Network/Tox/DHT/Stamped.hs b/src/Network/Tox/DHT/Stamped.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/DHT/Stamped.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE Safe #-}
+module Network.Tox.DHT.Stamped where
+
+import qualified Data.Foldable    as F
+import           Data.List        ((\\))
+import           Data.Map         (Map)
+import qualified Data.Map         as Map
+
+import           Network.Tox.Time (Timestamp)
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+-- | a collection of objects associated with a timestamp.
+type Stamped a = Map Timestamp [a]
+
+empty :: Stamped a
+empty = Map.empty
+
+-- | add a timestamped object. There is no requirement that the stamp be later
+-- than that of previously added objects.
+add :: Timestamp -> a -> Stamped a -> Stamped a
+add time x = Map.insertWith (++) time [x]
+
+delete :: Eq a => Timestamp -> a -> Stamped a -> Stamped a
+delete time x = Map.adjust (\\ [x]) time
+
+findStamps :: (a -> Bool) -> Stamped a -> [Timestamp]
+findStamps p = Map.keys . Map.filter (any p)
+
+dropOlder :: Timestamp -> Stamped a -> Stamped a
+dropOlder time = Map.mapMaybeWithKey $
+  \t x -> if t < time then Nothing else Just x
+
+getList :: Stamped a -> [a]
+getList = F.concat
+
+popFirst :: Stamped a -> (Maybe (Timestamp, a), Stamped a)
+popFirst stamped =
+  case Map.toAscList stamped of
+    [] -> (Nothing, stamped)
+    assoc:assocs -> case assoc of
+      (_, []) -> popFirst $ Map.fromAscList assocs
+      (stamp, [a]) -> (Just (stamp, a), Map.fromAscList assocs)
+      (stamp, a:as) -> (Just (stamp, a), Map.fromAscList $ (stamp, as):assocs)
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
diff --git a/src/Network/Tox/Encoding.hs b/src/Network/Tox/Encoding.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Encoding.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE LambdaCase  #-}
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.Encoding where
+
+import           Data.Binary            (Binary, get, put)
+import           Data.Binary.Bits.Get   (BitGet)
+import           Data.Binary.Bits.Put   (BitPut)
+import           Data.Binary.Get        (Decoder (..), pushChunk,
+                                         runGetIncremental)
+import           Data.Binary.Put        (runPut)
+import           Data.ByteString        (ByteString)
+import qualified Data.ByteString        as ByteString
+import qualified Data.ByteString.Lazy   as LazyByteString
+import           Network.Tox.Crypto.Box (PlainText (..))
+
+
+class BitEncoding a where
+  bitGet :: BitGet a
+  bitPut :: a -> BitPut ()
+
+
+encode :: Binary a => a -> ByteString
+encode =
+  LazyByteString.toStrict . runPut . put
+
+
+decode :: (Monad m, Binary a) => ByteString -> m a
+decode bytes =
+  finish $ pushChunk (runGetIncremental get) bytes
+  where
+    finish = \case
+      Done unconsumed _ output ->
+        if ByteString.null unconsumed
+          then return output
+          else fail $ "unconsumed input: " ++ show (PlainText unconsumed)
+      Fail _ _ msg    -> fail msg
+      Partial f       -> finish $ f Nothing
diff --git a/src/Network/Tox/Network/MonadRandomBytes.hs b/src/Network/Tox/Network/MonadRandomBytes.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Network/MonadRandomBytes.hs
@@ -0,0 +1,105 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE Trustworthy         #-}
+
+module Network.Tox.Network.MonadRandomBytes where
+
+import           Control.Applicative        (Applicative, (<$>))
+import           Control.Monad.Random       (RandT, getRandoms)
+import           Control.Monad.Reader       (ReaderT)
+import           Control.Monad.RWS          (RWST)
+import           Control.Monad.State        (StateT)
+import           Control.Monad.Trans.Class  (lift)
+import           Control.Monad.Writer       (WriterT)
+import           Data.Binary                (get)
+import           Data.Binary.Get            (Get, getWord16be, getWord32be,
+                                             getWord64be, getWord8, runGet)
+import           Data.ByteString            (ByteString, pack, unpack)
+import           Data.ByteString.Lazy       (fromStrict)
+import           Data.Monoid                (Monoid)
+import           Data.Proxy                 (Proxy (..))
+import           Data.Word                  (Word16, Word32, Word64, Word8)
+import           System.Entropy             (getEntropy)
+import           System.Random              (RandomGen)
+
+
+import           Network.Tox.Crypto.Key     (Key)
+import qualified Network.Tox.Crypto.Key     as Key
+import           Network.Tox.Crypto.KeyPair (KeyPair)
+import qualified Network.Tox.Crypto.KeyPair as KeyPair
+
+class (Monad m, Applicative m) => MonadRandomBytes m where
+  randomBytes :: Int -> m ByteString
+
+  newKeyPair :: m KeyPair
+  newKeyPair = KeyPair.fromSecretKey <$> randomKey
+
+instance (Monad m, Applicative m, RandomGen s) => MonadRandomBytes (RandT s m) where
+  randomBytes n = pack . take n <$> getRandoms
+
+-- | cryptographically secure random bytes from system source
+instance MonadRandomBytes IO where
+  randomBytes = getEntropy
+  newKeyPair = KeyPair.newKeyPair
+
+instance MonadRandomBytes m => MonadRandomBytes (ReaderT r m) where
+  randomBytes = lift . randomBytes
+  newKeyPair = lift newKeyPair
+instance (Monoid w, MonadRandomBytes m) => MonadRandomBytes (WriterT w m) where
+  randomBytes = lift . randomBytes
+  newKeyPair = lift newKeyPair
+instance MonadRandomBytes m => MonadRandomBytes (StateT s m) where
+  randomBytes = lift . randomBytes
+  newKeyPair = lift newKeyPair
+instance (Monoid w, MonadRandomBytes m) => MonadRandomBytes (RWST r w s m) where
+  randomBytes = lift . randomBytes
+  newKeyPair = lift newKeyPair
+
+randomBinary :: MonadRandomBytes m => Get a -> Int -> m a
+randomBinary g len = runGet g . fromStrict <$> randomBytes len
+
+randomKey :: forall m a. (MonadRandomBytes m, Key.CryptoNumber a) => m (Key a)
+randomKey = randomBinary get $ Key.encodedByteSize (Proxy :: Proxy a)
+
+randomNonce :: MonadRandomBytes m => m Key.Nonce
+randomNonce = randomKey
+
+randomWord64 :: MonadRandomBytes m => m Word64
+randomWord64 = randomBinary getWord64be 8
+randomWord32 :: MonadRandomBytes m => m Word32
+randomWord32 = randomBinary getWord32be 4
+randomWord16 :: MonadRandomBytes m => m Word16
+randomWord16 = randomBinary getWord16be 2
+randomWord8 :: MonadRandomBytes m => m Word8
+randomWord8 = randomBinary getWord8 1
+
+-- produces Int uniformly distributed in range [0,bound)
+randomInt :: MonadRandomBytes m => Int -> m Int
+randomInt bound | bound <= 1 = return 0
+randomInt bound =
+  let
+    numBits = log2 bound
+    numBytes = 1 + (numBits - 1 `div` 8)
+  in do
+    r <- (`mod` 2^numBits) . makeInt . unpack <$> randomBytes numBytes
+    if r >= bound
+      then randomInt bound
+      else return r
+  where
+    log2 :: Int -> Int
+    log2 = ceiling . logBase 2 . (fromIntegral :: Int -> Double)
+    makeInt :: [Word8] -> Int
+    makeInt = foldr (\w -> (fromIntegral w +) . (256*)) 0
+
+-- produces Int uniformly distributed in range [low,high]
+randomIntR :: MonadRandomBytes m => (Int,Int) -> m Int
+randomIntR (low,high) = (low +) <$> randomInt (1 + high - low)
+
+-- | produces uniformly random element of a list
+uniform :: MonadRandomBytes m => [a] -> m a
+uniform [] = error "empty list in uniform"
+uniform as = (as!!) <$> randomInt (length as)
+
+uniformSafe :: MonadRandomBytes m => [a] -> m (Maybe a)
+uniformSafe [] = return Nothing
+uniformSafe as = Just <$> uniform as
+
diff --git a/src/Network/Tox/Network/Networked.hs b/src/Network/Tox/Network/Networked.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Network/Networked.hs
@@ -0,0 +1,61 @@
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE Trustworthy                #-}
+
+-- | Abstraction layer for network functionality.
+--
+-- The intention is to
+--   (i) separate the logic of the protocol from its binary encoding, and
+--   (ii) allow a simulated network in place of actual network IO.
+module Network.Tox.Network.Networked where
+
+import           Control.Applicative                  (Applicative, (<$>))
+import           Control.Monad.Random                 (RandT)
+import           Control.Monad.Reader                 (ReaderT)
+import           Control.Monad.State                  (MonadState, StateT)
+import           Control.Monad.Trans.Class            (lift)
+import           Control.Monad.Writer                 (WriterT, execWriterT,
+                                                       runWriterT, tell)
+import           Data.Binary                          (Binary)
+import           Data.Monoid                          (Monoid)
+
+import           Network.Tox.Network.MonadRandomBytes (MonadRandomBytes)
+import           Network.Tox.NodeInfo.NodeInfo        (NodeInfo)
+import           Network.Tox.Protocol.Packet          (Packet (..))
+import           Network.Tox.Timed                    (Timed)
+
+class Monad m => Networked m where
+  sendPacket :: (Binary payload, Show payload) => NodeInfo -> Packet payload -> m ()
+
+-- | actual network IO
+instance Networked (StateT NetworkState IO) where
+  -- | TODO
+  sendPacket _ _ = return ()
+
+-- | TODO: sockets etc
+type NetworkState = ()
+
+type NetworkEvent = String
+newtype NetworkLogged m a = NetworkLogged (WriterT [NetworkEvent] m a)
+  deriving (Monad, Applicative, Functor, MonadState s, MonadRandomBytes, Timed)
+
+runNetworkLogged :: Monad m => NetworkLogged m a -> m (a, [NetworkEvent])
+runNetworkLogged (NetworkLogged m) = runWriterT m
+evalNetworkLogged :: (Monad m, Applicative m) => NetworkLogged m a -> m a
+evalNetworkLogged = (fst <$>) . runNetworkLogged
+execNetworkLogged :: Monad m => NetworkLogged m a -> m [NetworkEvent]
+execNetworkLogged (NetworkLogged m) = execWriterT m
+
+-- | just log network events
+instance Monad m => Networked (NetworkLogged m) where
+  sendPacket to packet = NetworkLogged $
+    tell [">>> " ++ show to ++ " : " ++ show packet]
+
+instance Networked m => Networked (ReaderT r m) where
+  sendPacket = (lift .) . sendPacket
+instance (Monoid w, Networked m) => Networked (WriterT w m) where
+  sendPacket = (lift .) . sendPacket
+instance Networked m => Networked (RandT s m) where
+  sendPacket = (lift .) . sendPacket
+instance Networked m => Networked (StateT s m) where
+  sendPacket = (lift .) . sendPacket
diff --git a/src/Network/Tox/NodeInfo.lhs b/src/Network/Tox/NodeInfo.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/NodeInfo.lhs
@@ -0,0 +1,12 @@
+\chapter{Node Info}
+
+\begin{code}
+{-# LANGUAGE Safe #-}
+module Network.Tox.NodeInfo where
+\end{code}
+
+\input{src/Network/Tox/NodeInfo/TransportProtocol.lhs}
+\input{src/Network/Tox/NodeInfo/HostAddress.lhs}
+\input{src/Network/Tox/NodeInfo/PortNumber.lhs}
+\input{src/Network/Tox/NodeInfo/SocketAddress.lhs}
+\input{src/Network/Tox/NodeInfo/NodeInfo.lhs}
diff --git a/src/Network/Tox/NodeInfo/HostAddress.lhs b/src/Network/Tox/NodeInfo/HostAddress.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/NodeInfo/HostAddress.lhs
@@ -0,0 +1,102 @@
+\section{Host Address}
+
+A Host Address is either an IPv4 or an IPv6 address.  The binary representation
+of an IPv4 address is a Big Endian 32 bit unsigned integer (4 bytes).  For an
+IPv6 address, it is a Big Endian 128 bit unsigned integer (16 bytes).  The
+binary representation of a Host Address is a 7 bit unsigned integer specifying
+the address family (2 for IPv4, 10 for IPv6), followed by the address itself.
+
+Thus, when packed together with the Transport Protocol, the first bit of the
+packed byte is the protocol and the next 7 bits are the address family.
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE LambdaCase         #-}
+{-# LANGUAGE Trustworthy        #-}
+module Network.Tox.NodeInfo.HostAddress where
+
+import           Control.Applicative       ((<$>))
+import           Control.Arrow             ((&&&))
+import           Data.Binary               (Binary)
+import qualified Data.Binary               as Binary (get, put)
+import qualified Data.Binary.Bits.Get      as Bits
+import qualified Data.Binary.Bits.Put      as Bits
+import qualified Data.Binary.Get           as Bytes
+import qualified Data.Binary.Put           as Bytes
+import qualified Data.IP                   as IP
+import           Data.MessagePack          (MessagePack)
+import           Data.Typeable             (Typeable)
+import           GHC.Generics              (Generic)
+import qualified Network.Socket            as Socket (HostAddress, HostAddress6)
+import           Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
+import qualified Test.QuickCheck.Gen       as Gen
+import           Text.Read                 (readMaybe, readPrec)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+data HostAddress
+  = IPv4 Socket.HostAddress
+  | IPv6 Socket.HostAddress6
+  deriving (Eq, Ord, Generic, Typeable)
+
+instance Binary HostAddress
+instance MessagePack HostAddress
+
+
+instance Show HostAddress where
+  show (IPv4 addr) = show . show . IP.fromHostAddress  $ addr
+  show (IPv6 addr) = show . show . IP.fromHostAddress6 $ addr
+
+
+instance Read HostAddress where
+  readPrec = do
+    str <- readPrec
+    case readMaybe str of
+      Nothing             -> fail "HostAddress"
+      Just (IP.IPv4 ipv4) -> return . IPv4 . IP.toHostAddress  $ ipv4
+      Just (IP.IPv6 ipv6) -> return . IPv6 . IP.toHostAddress6 $ ipv6
+
+
+getHostAddressGetter :: Bits.BitGet (Bytes.Get HostAddress)
+getHostAddressGetter =
+  Bits.getWord8 7 >>= \case
+    2  -> return $ IPv4 <$> Binary.get
+    10 -> return $ IPv6 <$> Binary.get
+    n  -> fail $ "Invalid address family: " ++ show n
+
+
+putAddressFamily :: HostAddress -> Bits.BitPut ()
+putAddressFamily (IPv4 _) = Bits.putWord8 7 2
+putAddressFamily (IPv6 _) = Bits.putWord8 7 10
+
+
+putHostAddressValue :: HostAddress -> Bytes.Put
+putHostAddressValue (IPv4 addr) = Binary.put addr
+putHostAddressValue (IPv6 addr) = Binary.put addr
+
+
+putHostAddress :: HostAddress -> (Bits.BitPut (), Bytes.Put)
+putHostAddress = putAddressFamily &&& putHostAddressValue
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary HostAddress where
+  arbitrary =
+    Gen.oneof
+      [ IPv4 <$> arbitrary
+      , IPv6 <$> arbitrary
+      ]
+\end{code}
diff --git a/src/Network/Tox/NodeInfo/NodeInfo.lhs b/src/Network/Tox/NodeInfo/NodeInfo.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/NodeInfo/NodeInfo.lhs
@@ -0,0 +1,98 @@
+\section{Node Info (packed node format)}
+
+The Node Info data structure contains a Transport Protocol, a Socket Address,
+and a Public Key.  This is sufficient information to start communicating with
+that node.  The binary representation of a Node Info is called the "packed node
+format".
+
+\begin{tabular}{l|l|l}
+  Length             & Type               & Contents \\
+  \hline
+  \texttt{1} bit     & Transport Protocol & UDP = 0, TCP = 1 \\
+  \texttt{7} bit     & Address Family     & 2 = IPv4, 10 = IPv6 \\
+  \texttt{4 $|$ 16}  & IP address         & 4 bytes for IPv4, 16 bytes for IPv6 \\
+  \texttt{2}         & Port Number        & Port number \\
+  \texttt{32}        & Public Key         & Node ID \\
+\end{tabular}
+
+The packed node format is a way to store the node info in a small yet easy to
+parse format.  To store more than one node, simply append another one to the
+previous one: \texttt{[packed node 1][packed node 2][...]}.
+
+In the packed node format, the first byte (high bit protocol, lower 7 bits
+address family) are called the IP Type.  The following table is informative and
+can be used to simplify the implementation.
+
+\begin{tabular}{l|l|l}
+  IP Type               & Transport Protocol & Address Family \\
+  \hline
+  \texttt{2   (0x02)}   & UDP                & IPv4 \\
+  \texttt{10  (0x0a)}   & UDP                & IPv6 \\
+  \texttt{130 (0x82)}   & TCP                & IPv4 \\
+  \texttt{138 (0x8a)}   & TCP                & IPv6 \\
+\end{tabular}
+
+The number \texttt{130} is used for an IPv4 TCP relay and \texttt{138} is used
+to indicate an IPv6 TCP relay.
+
+The reason for these numbers is that the numbers on Linux for IPv4 and IPv6
+(the \texttt{AF\_INET} and \texttt{AF\_INET6} defines) are \texttt{2} and
+\texttt{10}.  The TCP numbers are just the UDP numbers \texttt{+ 128}.
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE Safe               #-}
+module Network.Tox.NodeInfo.NodeInfo where
+
+import           Control.Applicative                    ((<$>), (<*>))
+import           Data.Binary                            (Binary)
+import qualified Data.Binary                            as Binary (get, put)
+import           Data.MessagePack                       (MessagePack)
+import           Data.Typeable                          (Typeable)
+import           GHC.Generics                           (Generic)
+import           Test.QuickCheck.Arbitrary              (Arbitrary, arbitrary)
+
+import           Network.Tox.Crypto.Key                 (PublicKey)
+import           Network.Tox.NodeInfo.SocketAddress     (SocketAddress)
+import qualified Network.Tox.NodeInfo.SocketAddress     as SocketAddress
+import           Network.Tox.NodeInfo.TransportProtocol (TransportProtocol)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+data NodeInfo = NodeInfo
+  { protocol  :: TransportProtocol
+  , address   :: SocketAddress
+  , publicKey :: PublicKey
+  }
+  deriving (Eq, Ord, Show, Read, Generic, Typeable)
+
+instance MessagePack NodeInfo
+
+
+instance Binary NodeInfo where
+  get =
+    uncurry NodeInfo <$> SocketAddress.getSocketAddress <*> Binary.get
+
+  put ni = do
+    SocketAddress.putSocketAddress (protocol ni) (address ni)
+    Binary.put $ publicKey ni
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary NodeInfo where
+  arbitrary =
+    NodeInfo <$> arbitrary <*> arbitrary <*> arbitrary
+\end{code}
diff --git a/src/Network/Tox/NodeInfo/PortNumber.lhs b/src/Network/Tox/NodeInfo/PortNumber.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/NodeInfo/PortNumber.lhs
@@ -0,0 +1,50 @@
+\section{Port Number}
+
+A Port Number is a 16 bit number.  Its binary representation is a Big Endian 16
+bit unsigned integer (2 bytes).
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable         #-}
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE Trustworthy                #-}
+module Network.Tox.NodeInfo.PortNumber where
+
+import           Control.Applicative       ((<$>))
+import           Data.Binary               (Binary)
+import           Data.MessagePack          (MessagePack)
+import           Data.Typeable             (Typeable)
+import           Data.Word                 (Word16)
+import           GHC.Generics              (Generic)
+import           Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+newtype PortNumber = PortNumber Word16
+  deriving
+    ( Generic, Typeable
+    , Eq, Ord
+    , Show, Read
+    , Binary
+    , Num, Integral, Real, Bounded, Enum)
+
+instance MessagePack PortNumber
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary PortNumber where
+  arbitrary =
+    PortNumber . fromInteger <$> arbitrary
+\end{code}
diff --git a/src/Network/Tox/NodeInfo/SocketAddress.lhs b/src/Network/Tox/NodeInfo/SocketAddress.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/NodeInfo/SocketAddress.lhs
@@ -0,0 +1,76 @@
+\section{Socket Address}
+
+A Socket Address is a pair of Host Address and Port Number.  Together with a
+Transport Protocol, it is sufficient information to address a network port on
+any internet host.
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE Trustworthy        #-}
+module Network.Tox.NodeInfo.SocketAddress where
+
+import           Control.Applicative                    ((<$>), (<*>))
+import           Data.Binary                            (Binary, get, put)
+import qualified Data.Binary.Bits.Get                   as Bits (runBitGet)
+import qualified Data.Binary.Bits.Put                   as Bits (runBitPut)
+import qualified Data.Binary.Get                        as Binary (Get)
+import qualified Data.Binary.Put                        as Binary (Put)
+import           Data.MessagePack                       (MessagePack)
+import           Data.Typeable                          (Typeable)
+import           GHC.Generics                           (Generic)
+import           Network.Tox.Encoding                   (bitGet, bitPut)
+import           Network.Tox.NodeInfo.HostAddress       (HostAddress (..))
+import qualified Network.Tox.NodeInfo.HostAddress       as HostAddress
+import           Network.Tox.NodeInfo.PortNumber        (PortNumber)
+import           Network.Tox.NodeInfo.TransportProtocol (TransportProtocol)
+import           Test.QuickCheck.Arbitrary              (Arbitrary, arbitrary)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+data SocketAddress = SocketAddress HostAddress PortNumber
+  deriving (Eq, Ord, Show, Read, Generic, Typeable)
+
+instance Binary SocketAddress
+instance MessagePack SocketAddress
+
+
+putSocketAddress :: TransportProtocol -> SocketAddress -> Binary.Put
+putSocketAddress protocol (SocketAddress hostAddress portNumber) =
+  let (putAddressFamily, putHostAddress) = HostAddress.putHostAddress hostAddress in
+  do
+    Bits.runBitPut $ do
+      bitPut protocol -- first bit = protocol
+      putAddressFamily -- 7 bits = address family
+    putHostAddress
+    put portNumber
+
+
+getSocketAddress :: Binary.Get (TransportProtocol, SocketAddress)
+getSocketAddress = do
+  (protocol, getHostAddress) <- Bits.runBitGet $ do
+    protocol <- bitGet
+    getHostAddress <- HostAddress.getHostAddressGetter
+    return (protocol, getHostAddress)
+  hostAddress <- getHostAddress
+  portNumber <- get
+  return (protocol, SocketAddress hostAddress portNumber)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary SocketAddress where
+  arbitrary =
+    SocketAddress <$> arbitrary <*> arbitrary
+\end{code}
diff --git a/src/Network/Tox/NodeInfo/TransportProtocol.lhs b/src/Network/Tox/NodeInfo/TransportProtocol.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/NodeInfo/TransportProtocol.lhs
@@ -0,0 +1,65 @@
+\section{Transport Protocol}
+
+A Transport Protocol is a transport layer protocol directly below the Tox
+protocol itself.  Tox supports two transport protocols: UDP and TCP.  The
+binary representation of the Transport Protocol is a single bit: 0 for UDP, 1
+for TCP.  If encoded as standalone value, the bit is stored in the least
+significant bit of a byte.  If followed by other bit-packed data, it consumes
+exactly one bit.
+
+The human-readable representation for UDP is \texttt{UDP} and for TCP is
+\texttt{TCP}.
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE LambdaCase         #-}
+{-# LANGUAGE Trustworthy        #-}
+module Network.Tox.NodeInfo.TransportProtocol where
+
+import           Data.Binary               (Binary)
+import qualified Data.Binary.Bits.Get      as Bits (getBool)
+import qualified Data.Binary.Bits.Put      as Bits (putBool)
+import           Data.MessagePack          (MessagePack)
+import           Data.Typeable             (Typeable)
+import           GHC.Generics              (Generic)
+import           Network.Tox.Encoding      (BitEncoding, bitGet, bitPut)
+import           Test.QuickCheck.Arbitrary (Arbitrary (..))
+import qualified Test.QuickCheck.Gen       as Gen
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+data TransportProtocol
+  = UDP
+  | TCP
+  deriving (Eq, Ord, Show, Read, Generic, Typeable)
+
+instance Binary TransportProtocol
+instance MessagePack TransportProtocol
+
+instance BitEncoding TransportProtocol where
+  bitGet = fmap (\case
+      False -> UDP
+      True  -> TCP
+    ) Bits.getBool
+
+  bitPut UDP = Bits.putBool False
+  bitPut TCP = Bits.putBool True
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary TransportProtocol where
+  arbitrary = Gen.elements [UDP, TCP]
+\end{code}
diff --git a/src/Network/Tox/Protocol.lhs b/src/Network/Tox/Protocol.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Protocol.lhs
@@ -0,0 +1,9 @@
+\chapter{Protocol Packet}
+
+\begin{code}
+{-# LANGUAGE Safe #-}
+module Network.Tox.Protocol where
+\end{code}
+
+\input{src/Network/Tox/Protocol/Packet.lhs}
+\input{src/Network/Tox/Protocol/PacketKind.lhs}
diff --git a/src/Network/Tox/Protocol/Packet.lhs b/src/Network/Tox/Protocol/Packet.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Protocol/Packet.lhs
@@ -0,0 +1,72 @@
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE Safe               #-}
+module Network.Tox.Protocol.Packet where
+
+import           Control.Applicative             ((<$>), (<*>))
+import           Data.Binary                     (Binary)
+import           Data.MessagePack                (MessagePack)
+import           Data.Typeable                   (Typeable)
+import           GHC.Generics                    (Generic)
+import           Network.Tox.Protocol.PacketKind (PacketKind)
+import           Test.QuickCheck.Arbitrary       (Arbitrary, arbitrary)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+\end{code}
+
+A Protocol Packet is the top level Tox protocol element.  All other packet
+types are wrapped in Protocol Packets.  It consists of a Packet Kind and a
+payload.  The binary representation of a Packet Kind is a single byte (8 bits).
+The payload is an arbitrary sequence of bytes.
+
+\begin{tabular}{l|l|l}
+  Length             & Type        & Contents \\
+  \hline
+  \texttt{1}         & Packet Kind & The packet kind identifier \\
+  \texttt{[0,]}      & Bytes       & Payload \\
+\end{tabular}
+
+\begin{code}
+
+data Packet payload = Packet
+  { packetKind    :: PacketKind
+  , packetPayload :: payload
+  }
+  deriving (Eq, Read, Show, Generic, Typeable)
+
+instance Binary payload => Binary (Packet payload)
+instance MessagePack payload => MessagePack (Packet payload)
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary payload => Arbitrary (Packet payload) where
+  arbitrary =
+    Packet <$> arbitrary <*> arbitrary
+\end{code}
+
+These top level packets can be transported in a number of ways, the most common
+way being over the network using UDP or TCP.  The protocol itself does not
+prescribe transport methods, and an implementation is free to implement
+additional transports such as WebRTC, IRC, or pipes.
+
+In the remainder of the document, different kinds of Protocol Packet are
+specified with their packet kind and payload.  The packet kind is not repeated
+in the payload description (TODO: actually it mostly is, but later it won't).
+
+Inside Protocol Packets payload, other packet types can specify additional
+packet kinds.  E.g. inside a Crypto Data packet (\texttt{0x1b}), the
+\href{#messenger}{Messenger} module defines its protocols for messaging, file
+transfers, etc.  Top level Protocol Packets are themselves not encrypted,
+though their payload may be.
diff --git a/src/Network/Tox/Protocol/PacketKind.lhs b/src/Network/Tox/Protocol/PacketKind.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Protocol/PacketKind.lhs
@@ -0,0 +1,163 @@
+\section{Packet Kind}
+
+The following is an exhaustive list of top level packet kind names and their
+number.  Their payload is specified in dedicated sections.  Each section is
+named after the Packet Kind it describes followed by the byte value in
+parentheses, e.g. \href{#ping-request-0x00}{Ping Request (0x00)}.
+
+\begin{tabular}{l|l}
+  Byte value        & Packet Kind \\
+  \hline
+  \texttt{0x00}     & Ping Request \\
+  \texttt{0x01}     & Ping Response \\
+  \texttt{0x02}     & Nodes Request \\
+  \texttt{0x04}     & Nodes Response \\
+  \texttt{0x18}     & Cookie Request \\
+  \texttt{0x19}     & Cookie Response \\
+  \texttt{0x1a}     & Crypto Handshake \\
+  \texttt{0x1b}     & Crypto Data \\
+  \texttt{0x20}     & DHT Request \\
+  \texttt{0x21}     & LAN Discovery \\
+  \texttt{0x80}     & Onion Request 0 \\
+  \texttt{0x81}     & Onion Request 1 \\
+  \texttt{0x82}     & Onion Request 2 \\
+  \texttt{0x83}     & Announce Request \\
+  \texttt{0x84}     & Announce Response \\
+  \texttt{0x85}     & Onion Data Request \\
+  \texttt{0x86}     & Onion Data Response \\
+  \texttt{0x8c}     & Onion Response 3 \\
+  \texttt{0x8d}     & Onion Response 2 \\
+  \texttt{0x8e}     & Onion Response 1 \\
+  \texttt{0xf0}     & Bootstrap Info \\
+\end{tabular}
+
+\begin{code}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE LambdaCase         #-}
+{-# LANGUAGE Safe               #-}
+module Network.Tox.Protocol.PacketKind where
+
+import           Control.Arrow             ((&&&))
+import           Data.Binary               (Binary, get, put)
+import           Data.MessagePack          (MessagePack)
+import           Data.Typeable             (Typeable)
+import           Data.Word                 (Word8)
+import           GHC.Generics              (Generic)
+import           Test.QuickCheck.Arbitrary (Arbitrary, arbitrary,
+                                            arbitraryBoundedEnum)
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+
+data PacketKind
+  = PingRequest
+  | PingResponse
+  | NodesRequest
+  | NodesResponse
+  | CookieRequest
+  | CookieResponse
+  | CryptoHandshake
+  | CryptoData
+  | Crypto
+  | LanDiscovery
+  | OnionRequest0
+  | OnionRequest1
+  | OnionRequest2
+  | AnnounceRequest
+  | AnnounceResponse
+  | OnionDataRequest
+  | OnionDataResponse
+  | OnionResponse3
+  | OnionResponse2
+  | OnionResponse1
+  | BootstrapInfo
+  deriving (Eq, Read, Show, Bounded, Enum, Generic, Typeable)
+
+
+instance MessagePack PacketKind
+
+
+kindDescription :: PacketKind -> String
+kindDescription = \case
+  PingRequest       -> "Ping request"
+  PingResponse      -> "Ping response"
+  NodesRequest      -> "Nodes request"
+  NodesResponse     -> "Nodes response"
+  CookieRequest     -> "Cookie request"
+  CookieResponse    -> "Cookie response"
+  CryptoHandshake   -> "Crypto handshake"
+  CryptoData        -> "Crypto data"
+  Crypto            -> "Encrypted data"
+  LanDiscovery      -> "LAN discovery"
+  OnionRequest0     -> "Initial onion request"
+  OnionRequest1     -> "First level wrapped onion request"
+  OnionRequest2     -> "Second level wrapped onion request"
+  AnnounceRequest   -> "Announce request"
+  AnnounceResponse  -> "Announce response"
+  OnionDataRequest  -> "Onion data request"
+  OnionDataResponse -> "Onion data response"
+  OnionResponse3    -> "Third level wrapped onion response"
+  OnionResponse2    -> "Second level wrapped onion response"
+  OnionResponse1    -> "First level wrapped onion response"
+  BootstrapInfo     -> "Bootstrap node info request and response"
+
+
+kindToByte :: PacketKind -> Word8
+kindToByte = \case
+  PingRequest       -> 0x00
+  PingResponse      -> 0x01
+  NodesRequest      -> 0x02
+  NodesResponse     -> 0x04
+  CookieRequest     -> 0x18
+  CookieResponse    -> 0x19
+  CryptoHandshake   -> 0x1a
+  CryptoData        -> 0x1b
+  Crypto            -> 0x20
+  LanDiscovery      -> 0x21
+  OnionRequest0     -> 0x80
+  OnionRequest1     -> 0x81
+  OnionRequest2     -> 0x82
+  AnnounceRequest   -> 0x83
+  AnnounceResponse  -> 0x84
+  OnionDataRequest  -> 0x85
+  OnionDataResponse -> 0x86
+  OnionResponse3    -> 0x8c
+  OnionResponse2    -> 0x8d
+  OnionResponse1    -> 0x8e
+  BootstrapInfo     -> 0xf0
+
+
+byteToKind :: Word8 -> Maybe PacketKind
+byteToKind =
+  flip lookup mapping
+  where
+    mapping = map (kindToByte &&& id) [minBound..maxBound]
+
+
+instance Binary PacketKind where
+  put = put . kindToByte
+
+  get = do
+    byte <- get
+    case byteToKind byte of
+      Nothing   -> fail $ "no binary mapping for packet kind " ++ show byte
+      Just kind -> return kind
+
+
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+
+instance Arbitrary PacketKind where
+  arbitrary = arbitraryBoundedEnum
+\end{code}
diff --git a/src/Network/Tox/SaveData.lhs b/src/Network/Tox/SaveData.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/SaveData.lhs
@@ -0,0 +1,336 @@
+\chapter{State Format}
+
+\begin{code}
+{-# LANGUAGE DeriveGeneric   #-}
+{-# LANGUAGE LambdaCase      #-}
+{-# LANGUAGE RecordWildCards #-}
+module Network.Tox.SaveData
+    ( SaveData (..)
+    , Section (..)
+    , NospamKeys (..)
+    , Friends (..)
+    , Bytes (..)
+    ) where
+\end{code}
+
+The reference Tox implementation uses a custom binary format to save the state
+of a Tox client between restarts. This format is far from perfect and will be
+replaced eventually. For the sake of maintaining compatibility down the road,
+it is documented here.
+
+The binary encoding of all integer types in the state format is a fixed-width
+byte sequence with the integer encoded in Little Endian unless stated otherwise.
+
+\begin{code}
+
+import           Control.Arrow                    (second)
+import           Control.Monad                    (when)
+import           Data.Binary                      (Binary (..))
+import           Data.Binary.Get                  (Get)
+import qualified Data.Binary.Get                  as Get
+import           Data.Binary.Put                  (Put)
+import qualified Data.Binary.Put                  as Put
+import qualified Data.ByteString                  as BS
+import qualified Data.ByteString.Lazy             as LBS
+import           Data.Word                        (Word16, Word32, Word8)
+import           GHC.Generics                     (Generic)
+import           Network.Tox.Crypto.Key           (PublicKey, SecretKey)
+import           Network.Tox.SaveData.Conferences (Conferences)
+import           Network.Tox.SaveData.DHT         (DHT)
+import           Network.Tox.SaveData.Friend      (Friend)
+import           Network.Tox.SaveData.Nodes       (Nodes)
+import qualified Network.Tox.SaveData.Util        as Util
+import           Test.QuickCheck.Arbitrary        (Arbitrary (..),
+                                                   genericShrink)
+import qualified Test.QuickCheck.Gen              as Gen
+
+\end{code}
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{4}    & Zeroes \\
+  \texttt{4}    & \texttt{uint32\_t} (0x15ED1B1F) \\
+  \texttt{?}    & List of sections \\
+\end{tabular}
+
+\begin{code}
+
+saveDataMagic :: Word32
+saveDataMagic = 0x15ED1B1F
+
+newtype SaveData = SaveData [Section]
+    deriving (Eq, Show, Read, Generic)
+
+instance Binary SaveData where
+    get = do
+        zeroes <- Get.getWord32le
+        when (zeroes /= 0) $
+            fail $ "savedata should start with 32 zero-bits, but got "
+                ++ show zeroes
+
+        magic <- Get.getWord32le
+        when (magic /= saveDataMagic) $
+            fail $ "wrong magic number for savedata: "
+                ++ show magic ++ " != " ++ show saveDataMagic
+
+        SaveData <$> getSections
+
+    put (SaveData sections) = do
+        Put.putWord32le 0
+        Put.putWord32le saveDataMagic
+        putSections sections
+
+instance Arbitrary SaveData where
+    arbitrary = SaveData . (++ [SectionEOF]) <$> arbitrary
+    shrink    = filter (\(SaveData ss) -> SectionEOF `elem` ss) . genericShrink
+
+\end{code}
+
+\section{Sections}
+
+The core of the state format consists of a list of sections. Every section has
+its type and length specified at the beginning. In some cases, a section only
+contains one item and thus takes up the entire length of the section. This is
+denoted with '?'.
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{4}    & \texttt{uint32\_t} Length of this section \\
+  \texttt{2}    & \texttt{uint16\_t} Section type \\
+  \texttt{2}    & \texttt{uint16\_t} (0x01CE) \\
+  \texttt{?}    & Section \\
+\end{tabular}
+
+\begin{code}
+
+sectionMagic :: Word16
+sectionMagic = 0x01CE
+
+\end{code}
+
+Section types:
+
+\begin{tabular}{l|l}
+  Name          & Value \\
+  \hline
+  NospamKeys    & 0x01 \\
+  DHT           & 0x02 \\
+  Friends       & 0x03 \\
+  Name          & 0x04 \\
+  StatusMessage & 0x05 \\
+  Status        & 0x06 \\
+  TcpRelays     & 0x0A \\
+  PathNodes     & 0x0B \\
+  Conferences   & 0x14 \\
+  EOF           & 0xFF \\
+\end{tabular}
+
+\begin{code}
+
+getSections :: Get [Section]
+getSections = go
+  where
+    go = do
+        (len, ty) <- Util.getSectionHeader sectionMagic
+
+        let load f = (:) <$> (f <$> Get.isolate (fromIntegral len) get) <*> go
+
+        case ty of
+            0x01 -> load SectionNospamKeys
+            0x02 -> load SectionDHT
+            0x03 -> load SectionFriends
+            0x04 -> load SectionName
+            0x05 -> load SectionStatusMessage
+            0x06 -> load SectionStatus
+            0x0A -> load SectionTcpRelays
+            0x0B -> load SectionPathNodes
+            0x14 -> load SectionConferences
+            0xFF -> return [SectionEOF]
+            _    -> fail $ show ty
+
+putSections :: [Section] -> Put
+putSections = mapM_ go
+  where
+    go section = do
+        let (ty, bytes) = second Put.runPut $ putSection section
+
+        Util.putSectionHeader sectionMagic (fromIntegral $ LBS.length bytes) ty
+        Put.putLazyByteString bytes
+
+    putSection = \case
+        SectionNospamKeys    x -> (0x01, put x)
+        SectionDHT           x -> (0x02, put x)
+        SectionFriends       x -> (0x03, put x)
+        SectionName          x -> (0x04, put x)
+        SectionStatusMessage x -> (0x05, put x)
+        SectionStatus        x -> (0x06, put x)
+        SectionTcpRelays     x -> (0x0A, put x)
+        SectionPathNodes     x -> (0x0B, put x)
+        SectionConferences   x -> (0x14, put x)
+        SectionEOF             -> (0xFF, return ())
+
+\end{code}
+
+Not every section listed above is required to be present in order to restore
+from a state file. Only NospamKeys is required.
+
+\subsection{Nospam and Keys (0x01)}
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{4}    & \texttt{uint32\_t} Nospam \\
+  \texttt{32}   & Long term public key \\
+  \texttt{32}   & Long term secret key \\
+\end{tabular}
+
+\input{src/Network/Tox/SaveData/DHT.lhs}
+
+\subsection{Friends (0x03)}
+
+This section contains a list of friends. A friend can either be a peer we've
+sent a friend request to or a peer we've accepted a friend request from.
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{?}    & List of friends \\
+\end{tabular}
+
+\input{src/Network/Tox/SaveData/Friend.lhs}
+
+\subsection{Name (0x04)}
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{?}    & Name as a UTF-8 encoded string \\
+\end{tabular}
+
+\subsection{Status Message (0x05)}
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{?}    & Status message as a UTF-8 encoded string \\
+\end{tabular}
+
+\subsection{Status (0x06)}
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{1}    & \texttt{uint8\_t} User status (see also: \texttt{USERSTATUS}) \\
+\end{tabular}
+
+\subsection{Tcp Relays (0x0A)}
+
+This section contains a list of TCP relays.
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{?}    & List of TCP relays \\
+\end{tabular}
+
+The structure of a TCP relay is the same as \texttt{Node Info}. Note: this
+means that the integers stored in these nodes are stored in Big Endian as well.
+
+\subsection{Path Nodes (0x0B)}
+
+This section contains a list of path nodes used for onion routing.
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{?}    & List of path nodes \\
+\end{tabular}
+
+The structure of a path node is the same as \texttt{Node Info}. Note: this
+means that the integers stored in these nodes are stored in Big Endian as well.
+
+\input{src/Network/Tox/SaveData/Conferences.lhs}
+
+\subsection{EOF (0xFF)}
+
+This section indicates the end of the state file. This section doesn't have any
+content and thus its length is 0.
+
+\begin{code}
+
+data Section
+    = SectionNospamKeys NospamKeys
+    | SectionDHT DHT
+    | SectionFriends Friends
+    | SectionName Bytes
+    | SectionStatusMessage Bytes
+    | SectionStatus Word8
+    | SectionTcpRelays Nodes
+    | SectionPathNodes Nodes
+    | SectionConferences Conferences
+    | SectionEOF
+    deriving (Eq, Show, Read, Generic)
+
+instance Arbitrary Section where
+    arbitrary = Gen.oneof
+        [ SectionNospamKeys <$> arbitrary
+        , SectionDHT <$> arbitrary
+        , SectionFriends <$> arbitrary
+        , SectionName <$> arbitrary
+        , SectionStatusMessage <$> arbitrary
+        , SectionStatus <$> arbitrary
+        , SectionTcpRelays <$> arbitrary
+        , SectionPathNodes <$> arbitrary
+        , SectionConferences <$> arbitrary
+        ]
+    shrink = genericShrink
+
+data NospamKeys = NospamKeys
+    { nospam    :: Word32
+    , publicKey :: PublicKey
+    , secretKey :: SecretKey
+    }
+    deriving (Eq, Show, Read, Generic)
+
+instance Binary NospamKeys where
+    get = NospamKeys
+        <$> Get.getWord32le
+        <*> get
+        <*> get
+
+    put NospamKeys{..} = do
+        Put.putWord32le nospam
+        put publicKey
+        put secretKey
+
+instance Arbitrary NospamKeys where
+    arbitrary = NospamKeys
+        <$> arbitrary
+        <*> arbitrary
+        <*> arbitrary
+    shrink = genericShrink
+
+newtype Friends = Friends [Friend]
+    deriving (Eq, Show, Read, Generic)
+
+instance Binary Friends where
+    get = Friends <$> Util.getList
+    put (Friends xs) = mapM_ put xs
+
+instance Arbitrary Friends where
+    arbitrary = Friends <$> arbitrary
+    shrink = genericShrink
+
+newtype Bytes = Bytes LBS.ByteString
+    deriving (Eq, Show, Read, Generic)
+
+instance Binary Bytes where
+    get = Bytes <$> Get.getRemainingLazyByteString
+    put (Bytes bs) = Put.putLazyByteString bs
+
+instance Arbitrary Bytes where
+    arbitrary = Bytes . LBS.pack <$> arbitrary
+
+\end{code}
diff --git a/src/Network/Tox/SaveData/Conferences.lhs b/src/Network/Tox/SaveData/Conferences.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/SaveData/Conferences.lhs
@@ -0,0 +1,169 @@
+\subsection{Conferences (0x14)}
+
+This section contains a list of saved conferences.
+
+\begin{code}
+{-# LANGUAGE DeriveGeneric   #-}
+{-# LANGUAGE RecordWildCards #-}
+module Network.Tox.SaveData.Conferences where
+
+import           Data.Binary               (Binary (..))
+import qualified Data.Binary.Get           as Get
+import qualified Data.Binary.Put           as Put
+import qualified Data.ByteString           as BS
+import           Data.Word                 (Word16, Word32, Word64, Word8)
+import           GHC.Generics              (Generic)
+import           Network.Tox.Crypto.Key    (PublicKey)
+import qualified Network.Tox.SaveData.Util as Util
+import           Test.QuickCheck.Arbitrary (Arbitrary (..), genericShrink)
+import qualified Test.QuickCheck.Arbitrary as Arbitrary
+
+\end{code}
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{?}    & List of conferences \\
+\end{tabular}
+
+\begin{code}
+
+newtype Conferences = Conferences [Conference]
+    deriving (Eq, Show, Read, Generic)
+
+instance Binary Conferences where
+    get = Conferences <$> Util.getList
+    put (Conferences xs) = mapM_ put xs
+
+instance Arbitrary Conferences where
+    arbitrary = Conferences <$> arbitrary
+    shrink    = genericShrink
+
+\end{code}
+
+Conference:
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{1}    & \texttt{uint8\_t} Groupchat type \\
+  \texttt{32}   & Groupchat id \\
+  \texttt{4}    & \texttt{uint32\_t} Message number \\
+  \texttt{2}    & \texttt{uint16\_t} Lossy message number \\
+  \texttt{2}    & \texttt{uint16\_t} Peer number \\
+  \texttt{4}    & \texttt{uint32\_t} Number of peers \\
+  \texttt{1}    & \texttt{uint8\_t} Title length \\
+  \texttt{?}    & Title \\
+  \texttt{?}    & List of peers \\
+\end{tabular}
+
+All peers other than the saver are saved, including frozen peers. On reload,
+they all start as frozen.
+
+\begin{code}
+
+maxTitleLen :: Int
+maxTitleLen = 128
+
+data Conference = Conference
+    { conferenceType     :: Word8
+    , conferenceId       :: BS.ByteString
+    , messageNumber      :: Word32
+    , lossyMessageNumber :: Word16
+    , selfPeerNumber     :: Word16
+    , title              :: BS.ByteString
+    , peers              :: [Peer]
+    }
+    deriving (Eq, Show, Read)
+
+instance Binary Conference where
+    get = do
+        conferenceType     <- Get.getWord8
+        conferenceId       <- Get.getByteString 32
+        messageNumber      <- Get.getWord32le
+        lossyMessageNumber <- Get.getWord16le
+        selfPeerNumber     <- Get.getWord16le
+        peerCount          <- Get.getWord32le
+        titleLength        <- Get.getWord8
+        title              <- Get.getByteString (fromIntegral titleLength)
+        peers              <- mapM (const get) [1..peerCount]
+        return Conference{..}
+
+    put Conference{..} = do
+        Put.putWord8      conferenceType
+        Put.putByteString conferenceId
+        Put.putWord32le   messageNumber
+        Put.putWord16le   lossyMessageNumber
+        Put.putWord16le   selfPeerNumber
+        Put.putWord32le   (fromIntegral $ length peers)
+        Put.putWord8      (fromIntegral $ BS.length title)
+        Put.putByteString title
+        mapM_ put peers
+
+
+instance Arbitrary Conference where
+    arbitrary = Conference
+        <$> arbitrary
+        <*> (BS.pack <$> Arbitrary.vector 32)
+        <*> arbitrary
+        <*> arbitrary
+        <*> arbitrary
+        <*> (BS.pack . take maxTitleLen <$> arbitrary)
+        <*> arbitrary
+
+\end{code}
+
+Peer:
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{32}   & Long term public key \\
+  \texttt{32}   & DHT public key \\
+  \texttt{2}    & \texttt{uint16\_t} Peer number \\
+  \texttt{8}    & \texttt{uint64\_t} Last active timestamp \\
+  \texttt{1}    & \texttt{uint8\_t} Name length \\
+  \texttt{?}    & Name \\
+\end{tabular}
+
+\begin{code}
+
+maxNameLen :: Int
+maxNameLen = 128
+
+data Peer = Peer
+    { publicKey      :: PublicKey
+    , dhtPublicKey   :: PublicKey
+    , peerNumber     :: Word16
+    , lastActiveTime :: Word64
+    , name           :: BS.ByteString
+    }
+    deriving (Eq, Show, Read)
+
+instance Binary Peer where
+    get = do
+        publicKey      <- get
+        dhtPublicKey   <- get
+        peerNumber     <- Get.getWord16le
+        lastActiveTime <- Get.getWord64le
+        nameLength     <- Get.getWord8
+        name           <- Get.getByteString (fromIntegral nameLength)
+        return Peer{..}
+
+    put Peer{..} = do
+        put               publicKey
+        put               dhtPublicKey
+        Put.putWord16le   peerNumber
+        Put.putWord64le   lastActiveTime
+        Put.putWord8      (fromIntegral $ BS.length name)
+        Put.putByteString name
+
+instance Arbitrary Peer where
+    arbitrary = Peer
+        <$> arbitrary
+        <*> arbitrary
+        <*> arbitrary
+        <*> arbitrary
+        <*> (BS.pack . take maxNameLen <$> arbitrary)
+
+\end{code}
diff --git a/src/Network/Tox/SaveData/DHT.lhs b/src/Network/Tox/SaveData/DHT.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/SaveData/DHT.lhs
@@ -0,0 +1,120 @@
+\subsection{DHT (0x02)}
+
+\begin{code}
+module Network.Tox.SaveData.DHT (DHT) where
+
+import           Control.Arrow              (second)
+import           Control.Monad              (when)
+import           Data.Binary                (Binary (..))
+import           Data.Binary.Get            (Get)
+import qualified Data.Binary.Get            as Get
+import           Data.Binary.Put            (Put)
+import qualified Data.Binary.Put            as Put
+import qualified Data.ByteString.Lazy       as LBS
+import           Data.Word                  (Word16, Word32)
+import           Network.Tox.SaveData.Nodes (Nodes)
+import qualified Network.Tox.SaveData.Util  as Util
+import           Test.QuickCheck.Arbitrary  (Arbitrary, arbitrary)
+import qualified Test.QuickCheck.Gen        as Gen
+
+\end{code}
+
+This section contains a list of DHT-related sections.
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{4}    & \texttt{uint32\_t} (0x159000D) \\
+  \texttt{?}    & List of DHT sections \\
+\end{tabular}
+
+\subsubsection{DHT Sections}
+
+Every DHT section has the following structure:
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{4}    & \texttt{uint32\_t} Length of this section \\
+  \texttt{2}    & \texttt{uint16\_t} DHT section type \\
+  \texttt{2}    & \texttt{uint16\_t} (0x11CE) \\
+  \texttt{?}    & DHT section \\
+\end{tabular}
+
+DHT section types:
+
+\begin{tabular}{l|l}
+  Name  & Value \\
+  \hline
+  Nodes & 0x04 \\
+\end{tabular}
+
+\paragraph{Nodes (0x04)}
+
+This section contains a list of nodes. These nodes are used to quickly reconnect
+to the DHT after a Tox client is restarted.
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{?}    & List of nodes \\
+\end{tabular}
+
+The structure of a node is the same as \texttt{Node Info}. Note: this means
+that the integers stored in these nodes are stored in Big Endian as well.
+
+\begin{code}
+
+dhtMagic :: Word32
+dhtMagic = 0x0159000D
+
+sectionMagic :: Word16
+sectionMagic =  0x11CE
+
+newtype DHT = DHT [DhtSection]
+    deriving (Eq, Show, Read)
+
+instance Arbitrary DHT where
+    arbitrary = DHT <$> arbitrary
+
+instance Binary DHT where
+    get = do
+        magic <- Get.getWord32le
+        when (magic /= dhtMagic) $
+            fail $ "wrong magic number for DHT savedata: "
+                ++ show magic ++ " != " ++ show dhtMagic
+
+        DHT <$> Util.getList
+
+    put (DHT sections) = do
+        Put.putWord32le dhtMagic
+        mapM_ put sections
+
+
+newtype DhtSection
+    = DhtSectionNodes Nodes
+    deriving (Eq, Show, Read)
+
+instance Binary DhtSection where
+    get = do
+        (len, ty) <- Util.getSectionHeader sectionMagic
+        Get.isolate len $ case ty of
+            0x04 -> DhtSectionNodes <$> get
+            _    -> fail $ show ty
+
+    put section = do
+        let (ty, bytes) = second Put.runPut output
+
+        Util.putSectionHeader sectionMagic (fromIntegral $ LBS.length bytes) ty
+        Put.putLazyByteString bytes
+
+      where
+        output = case section of
+            DhtSectionNodes x -> (0x04, put x)
+
+instance Arbitrary DhtSection where
+    arbitrary = Gen.oneof
+        [ DhtSectionNodes <$> arbitrary
+        ]
+
+\end{code}
diff --git a/src/Network/Tox/SaveData/Friend.lhs b/src/Network/Tox/SaveData/Friend.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/SaveData/Friend.lhs
@@ -0,0 +1,136 @@
+\begin{code}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+module Network.Tox.SaveData.Friend where
+
+import           Data.Binary               (Binary (..))
+import qualified Data.Binary.Get           as Get
+import qualified Data.Binary.Put           as Put
+import qualified Data.ByteString           as BS
+import           Data.Monoid               ((<>))
+import           Data.Word                 (Word32, Word64, Word8)
+import           Network.Tox.Crypto.Key    (PublicKey)
+import           Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
+\end{code}
+
+Friend:
+
+The integers in this structure are stored in Big Endian format.
+
+\begin{tabular}{l|l}
+  Length        & Contents \\
+  \hline
+  \texttt{1}    & \texttt{uint8\_t} Status \\
+  \texttt{32}   & Long term public key \\
+  \texttt{1024} & Friend request message as a byte string \\
+  \texttt{1}    & PADDING \\
+  \texttt{2}    & \texttt{uint16\_t} Size of the friend request message \\
+  \texttt{128}  & Name as a byte string \\
+  \texttt{2}    & \texttt{uint16\_t} Size of the name \\
+  \texttt{1007} & Status message as a byte string \\
+  \texttt{1}    & PADDING \\
+  \texttt{2}    & \texttt{uint16\_t} Size of the status message \\
+  \texttt{1}    & \texttt{uint8\_t} User status (see also: \texttt{USERSTATUS}) \\
+  \texttt{3}    & PADDING \\
+  \texttt{4}    & \texttt{uint32\_t} Nospam (only used for sending a friend request) \\
+  \texttt{8}    & \texttt{uint64\_t} Last seen time \\
+\end{tabular}
+
+Status can be one of:
+
+\begin{tabular}{l|l}
+  Status & Meaning \\
+  \hline
+  0      & Not a friend \\
+  1      & Friend added \\
+  2      & Friend request sent \\
+  3      & Confirmed friend \\
+  4      & Friend online \\
+\end{tabular}
+
+\begin{code}
+
+data Friend = Friend
+    { status        :: Word8
+    , publicKey     :: PublicKey
+    , friendRequest :: BS.ByteString
+    , name          :: BS.ByteString
+    , statusMessage :: BS.ByteString
+    , userStatus    :: Word8
+    , nospam        :: Word32
+    , lastSeenTime  :: Word64
+    }
+    deriving (Eq, Show, Read)
+
+maxFriendRequestLen :: Int
+maxFriendRequestLen = 1024
+
+maxNameLen :: Int
+maxNameLen = 128
+
+maxStatusMessageLen :: Int
+maxStatusMessageLen = 1007
+
+instance Binary Friend where
+    get = do
+        status           <- Get.getWord8
+        publicKey        <- get
+        friendRequest'   <- Get.getByteString maxFriendRequestLen
+        _                <- Get.getWord8
+        friendRequestLen <- Get.getWord16be
+        name'            <- Get.getByteString maxNameLen
+        nameLen          <- Get.getWord16be
+        statusMessage'   <- Get.getByteString maxStatusMessageLen
+        _                <- Get.getWord8
+        statusMessageLen <- Get.getWord16be
+        userStatus       <- Get.getWord8
+        _                <- Get.getByteString 3
+        nospam           <- Get.getWord32be
+        lastSeenTime     <- Get.getWord64be
+
+        let friendRequest = BS.take (fromIntegral friendRequestLen) friendRequest'
+        let name = BS.take (fromIntegral nameLen) name'
+        let statusMessage = BS.take (fromIntegral statusMessageLen) statusMessage'
+
+        return Friend{..}
+
+    put Friend {..} = do
+        let friendRequestLen = BS.length friendRequest
+        let friendRequest' = friendRequest
+                <> BS.replicate (maxFriendRequestLen - friendRequestLen) 0
+
+        let nameLen = BS.length name
+        let name' = name
+                <> BS.replicate (maxNameLen - nameLen) 0
+
+        let statusMessageLen = BS.length statusMessage
+        let statusMessage' = statusMessage
+                <> BS.replicate (maxStatusMessageLen - statusMessageLen) 0
+
+        Put.putWord8            status
+        put                     publicKey
+        Put.putByteString       friendRequest'
+        Put.putWord8            0
+        Put.putWord16be         (fromIntegral friendRequestLen)
+        Put.putByteString       name'
+        Put.putWord16be         (fromIntegral nameLen)
+        Put.putByteString       statusMessage'
+        Put.putWord8            0
+        Put.putWord16be         (fromIntegral statusMessageLen)
+        Put.putWord8            userStatus
+        Put.putByteString       "\0\0\0"
+        Put.putWord32be         nospam
+        Put.putWord64be         lastSeenTime
+
+instance Arbitrary Friend where
+    arbitrary = Friend
+        <$> arbitrary
+        <*> arbitrary
+        <*> (BS.pack . take maxFriendRequestLen <$> arbitrary)
+        <*> (BS.pack . take maxNameLen <$> arbitrary)
+        <*> (BS.pack . take maxStatusMessageLen <$> arbitrary)
+        <*> arbitrary
+        <*> arbitrary
+        <*> arbitrary
+
+\end{code}
diff --git a/src/Network/Tox/SaveData/Nodes.hs b/src/Network/Tox/SaveData/Nodes.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/SaveData/Nodes.hs
@@ -0,0 +1,18 @@
+module Network.Tox.SaveData.Nodes
+    ( Nodes (..)
+    ) where
+
+import           Data.Binary                   (Binary (..))
+import           Network.Tox.NodeInfo.NodeInfo (NodeInfo)
+import qualified Network.Tox.SaveData.Util     as Util
+import           Test.QuickCheck.Arbitrary     (Arbitrary, arbitrary)
+
+newtype Nodes = Nodes [NodeInfo]
+    deriving (Eq, Show, Read)
+
+instance Binary Nodes where
+    get = Nodes <$> Util.getList
+    put (Nodes xs) = mapM_ put xs
+
+instance Arbitrary Nodes where
+    arbitrary = Nodes <$> arbitrary
diff --git a/src/Network/Tox/SaveData/Util.hs b/src/Network/Tox/SaveData/Util.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/SaveData/Util.hs
@@ -0,0 +1,38 @@
+module Network.Tox.SaveData.Util where
+
+import           Control.Monad   (when)
+import           Data.Binary     (Binary (get))
+import           Data.Binary.Get (Get)
+import qualified Data.Binary.Get as Get
+import           Data.Binary.Put (Put)
+import qualified Data.Binary.Put as Put
+import           Data.Word       (Word16, Word32)
+
+
+-- | Consumes the entire stream and parses some Binary out of it in a loop.
+getList :: (Binary a, Show a) => Get [a]
+getList = go []
+  where
+    go xs = do
+        isEmpty <- Get.isEmpty
+        if isEmpty
+            then return $ reverse xs
+            else go =<< (: xs) <$> get
+
+getSectionHeader :: Word16 -> Get (Int, Word16)
+getSectionHeader sectionMagic = do
+    len   <- Get.getWord32le
+    ty    <- Get.getWord16le
+    magic <- Get.getWord16le
+    when (magic /= sectionMagic) $
+        fail $ "wrong magic number for section: "
+            ++ show magic ++ " != " ++ show sectionMagic
+
+    return (fromIntegral len, ty)
+
+putSectionHeader :: Word16 -> Word32 -> Word16 -> Put
+putSectionHeader sectionMagic len ty = do
+    Put.putWord32le len
+    Put.putWord16le ty
+    Put.putWord16le sectionMagic
+
diff --git a/src/Network/Tox/Testing.lhs b/src/Network/Tox/Testing.lhs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Testing.lhs
@@ -0,0 +1,55 @@
+\chapter{Testing}
+
+The final part of the architecture is the test protocol. We use a
+\href{https://msgpack.org}{MessagePack} based RPC protocol to expose language
+agnostic interfaces to internal functions. Using property based testing with
+random inputs as well as specific edge case tests help ensure that an
+implementation of the Tox protocol following the architecture specified in this
+document is correct.
+
+See the \href{https://github.com/msgpack/msgpack/blob/master/spec.md}{spec} of
+msgpack for information on the binary representation.
+
+\begin{code}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE Safe       #-}
+module Network.Tox.Testing (serve, defaultPort) where
+
+import           Control.Applicative            ((<$>))
+import qualified Network.MessagePack.Rpc        as Rpc
+import qualified Network.MessagePack.Server     as Server
+import           System.Environment             (getArgs)
+import           Text.Read                      (readMaybe)
+
+import qualified Network.Tox.Binary             as Binary
+import qualified Network.Tox.Crypto.Box         as Box
+import qualified Network.Tox.Crypto.CombinedKey as CombinedKey
+import qualified Network.Tox.Crypto.KeyPair     as KeyPair
+import qualified Network.Tox.Crypto.Nonce       as Nonce
+
+
+defaultPort :: Int
+defaultPort = 1234
+
+
+services :: [Server.Method IO]
+services =
+  [ Binary.decodeS
+  , Binary.encodeS
+  , Rpc.method Box.decryptR
+  , Rpc.method Box.encryptR
+  , Rpc.method CombinedKey.precomputeR
+  , Rpc.method KeyPair.fromSecretKeyR
+  , Rpc.method KeyPair.newKeyPairR
+  , Rpc.method Nonce.incrementR
+  , Rpc.method Nonce.newNonceR
+  ]
+
+
+serve :: IO ()
+serve = map readMaybe <$> getArgs >>= \case
+    [Just port] -> Server.runServer port        services
+    _           -> Server.runServer defaultPort services
+\end{code}
+
+TODO(iphydf): Generate and add specifications of each test method here.
diff --git a/src/Network/Tox/Time.hs b/src/Network/Tox/Time.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Time.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE Safe #-}
+module Network.Tox.Time where
+
+import           Control.Applicative       ((<$>), (<*>))
+import qualified System.Clock              as Clock
+import           Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Implementation.
+ -
+ ------------------------------------------------------------------------------}
+
+newtype Timestamp = Timestamp Clock.TimeSpec
+  deriving (Eq, Ord, Show, Read)
+
+newtype TimeDiff = TimeDiff Clock.TimeSpec
+  deriving (Eq, Ord, Show, Read)
+
+instance Num TimeDiff where
+  TimeDiff t + TimeDiff t' = TimeDiff $ t Prelude.+ t'
+  TimeDiff t - TimeDiff t' = TimeDiff $ t Prelude.- t'
+  TimeDiff t * TimeDiff t' = TimeDiff $ t * t'
+  negate (TimeDiff t) = TimeDiff $ negate t
+  abs (TimeDiff t) = TimeDiff $ abs t
+  signum (TimeDiff t) = TimeDiff $ signum t
+  fromInteger = TimeDiff . fromInteger
+
+seconds :: Integer -> TimeDiff
+seconds s = TimeDiff $ Clock.TimeSpec (fromIntegral s) 0
+
+milliseconds :: Integer -> TimeDiff
+milliseconds = TimeDiff . Clock.TimeSpec 0 . (*10^(6::Integer)) . fromIntegral
+
+getTime :: IO Timestamp
+getTime = Timestamp <$> Clock.getTime Clock.Monotonic
+
+(-) :: Timestamp -> Timestamp -> TimeDiff
+Timestamp t - Timestamp t' = TimeDiff $ t Prelude.- t'
+
+(+) :: Timestamp -> TimeDiff -> Timestamp
+Timestamp t + TimeDiff t' = Timestamp $ t Prelude.+ t'
+
+{-------------------------------------------------------------------------------
+ -
+ - :: Tests.
+ -
+ ------------------------------------------------------------------------------}
+
+instance Arbitrary Timestamp
+  where arbitrary = (Timestamp <$>) $ Clock.TimeSpec <$> arbitrary <*> arbitrary
+
+instance Arbitrary TimeDiff
+  where arbitrary = (TimeDiff <$>) $ Clock.TimeSpec <$> arbitrary <*> arbitrary
diff --git a/src/Network/Tox/Timed.hs b/src/Network/Tox/Timed.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/Timed.hs
@@ -0,0 +1,31 @@
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE Safe                  #-}
+{-# LANGUAGE UndecidableInstances  #-}
+
+module Network.Tox.Timed where
+
+import           Control.Monad        (Monad)
+import           Control.Monad.Random (RandT)
+import           Control.Monad.Reader (ReaderT)
+import           Control.Monad.RWS    (RWST)
+import           Control.Monad.State  (StateT)
+import           Control.Monad.Trans  (lift)
+import           Control.Monad.Writer (WriterT)
+import           Data.Monoid          (Monoid)
+
+import           Network.Tox.Time     (Timestamp)
+
+class Monad m => Timed m where
+  askTime :: m Timestamp
+
+instance Timed m => Timed (ReaderT r m) where
+  askTime = lift askTime
+instance (Monoid w, Timed m) => Timed (WriterT w m) where
+  askTime = lift askTime
+instance Timed m => Timed (StateT s m) where
+  askTime = lift askTime
+instance (Monoid w, Timed m) => Timed (RWST r w s m) where
+  askTime = lift askTime
+instance Timed m => Timed (RandT s m) where
+  askTime = lift askTime
diff --git a/src/Network/Tox/TimedT.hs b/src/Network/Tox/TimedT.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Tox/TimedT.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE Trustworthy                #-}
+
+module Network.Tox.TimedT where
+
+import           Control.Applicative                  (Applicative)
+import           Control.Monad                        (Monad)
+import           Control.Monad.IO.Class               (MonadIO)
+import           Control.Monad.Reader                 (ReaderT, ask, runReaderT)
+import           Control.Monad.State                  (MonadState)
+import           Control.Monad.Trans                  (MonadTrans)
+import           Control.Monad.Writer                 (MonadWriter)
+
+import           Network.Tox.Crypto.Keyed             (Keyed)
+import           Network.Tox.Network.MonadRandomBytes (MonadRandomBytes)
+import           Network.Tox.Network.Networked        (Networked)
+import           Network.Tox.Time                     (Timestamp)
+import           Network.Tox.Timed                    (Timed (..))
+
+newtype TimedT m a = TimedT (ReaderT Timestamp m a)
+  deriving (Monad, Applicative, Functor, MonadState s, MonadWriter w
+    , MonadRandomBytes, MonadTrans, MonadIO, Networked, Keyed)
+
+runTimedT :: TimedT m a -> Timestamp -> m a
+runTimedT (TimedT m) = runReaderT m
+
+instance Monad m => Timed (TimedT m) where
+  askTime = TimedT ask
diff --git a/test/Data/Result.hs b/test/Data/Result.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/Result.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE DeriveFoldable    #-}
+{-# LANGUAGE DeriveFunctor     #-}
+{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE Safe              #-}
+module Data.Result
+    ( Result (..)
+    ) where
+
+import           Control.Applicative (Alternative (..), Applicative (..), (<$>),
+                                      (<*>))
+import           Control.Monad.Fail  (MonadFail (..))
+import           Data.Foldable       (Foldable)
+import           Data.Traversable    (Traversable)
+
+data Result a
+    = Success a
+    | Failure String
+    deriving (Read, Show, Eq, Functor, Foldable, Traversable)
+
+instance Applicative Result where
+    pure = Success
+
+    Success f   <*> x = fmap f x
+    Failure msg <*> _ = Failure msg
+
+instance Alternative Result where
+    empty = Failure "empty alternative"
+
+    s@Success {} <|> _ = s
+    _            <|> r = r
+
+instance Monad Result where
+    return = Success
+    fail = Failure
+
+    Success x   >>= f = f x
+    Failure msg >>= _ = Failure msg
+
+instance MonadFail Result where
+    fail = Failure
diff --git a/test/Network/Tox/C/ToxSpec.hs b/test/Network/Tox/C/ToxSpec.hs
deleted file mode 100644
--- a/test/Network/Tox/C/ToxSpec.hs
+++ /dev/null
@@ -1,119 +0,0 @@
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-{-# LANGUAGE Trustworthy #-}
-module Network.Tox.C.ToxSpec where
-
-import           Control.Applicative               ((<$>), (<*>))
-import qualified Crypto.Saltine.Internal.ByteSizes as Sodium (boxPK, boxSK)
-import qualified Data.ByteString                   as BS
-import           Data.ByteString.Arbitrary         (fromABS)
-import           Data.Default.Class                (def)
-import           Test.Hspec
-import           Test.QuickCheck
-import           Test.QuickCheck.Arbitrary         (arbitraryBoundedEnum,
-                                                    genericShrink)
-
-import qualified Network.Tox.C                     as C
-
-
-instance Arbitrary C.ProxyType where
-  shrink = genericShrink
-  arbitrary = arbitraryBoundedEnum
-
-instance Arbitrary C.SavedataType where
-  shrink = genericShrink
-  arbitrary = arbitraryBoundedEnum
-
-instance Arbitrary BS.ByteString where
-  shrink bs = if BS.null bs then [] else BS.inits bs
-  arbitrary = fromABS <$> arbitrary
-
--- | Ensure that the hostname has a chance of being valid.
-filterHost :: C.Options -> C.Options
-filterHost o@C.Options{C.proxyHost = h} = o{C.proxyHost = filter (`elem` hostChars) h}
-  where
-    hostChars = ".-_" ++ ['0'..'9'] ++ ['a'..'z'] ++ ['A'..'Z']
-
-instance Arbitrary C.Options where
-  shrink = map filterHost . genericShrink
-  arbitrary = fmap filterHost $ C.Options
-    <$> arbitrary
-    <*> arbitrary
-    <*> arbitrary
-    <*> arbitrary
-    <*> arbitrary
-    <*> arbitrary
-    <*> arbitrary
-    <*> arbitrary
-    <*> arbitrary
-    <*> arbitrary
-
-
-getRight :: (Monad m, Show a) => Either a b -> m b
-getRight (Left  l) = fail $ show l
-getRight (Right r) = return r
-
-
-must :: Show a => IO (Either a b) -> IO b
-must = (getRight =<<)
-
-
-spec :: Spec
-spec = do
-  describe "tox_version_is_compatible" $
-    it "is compatible with the major/minor/patch of the linked library" $
-      C.tox_version_is_compatible
-        C.tox_version_major
-        C.tox_version_minor
-        C.tox_version_patch
-      `shouldBe` True
-
-  describe "Constants" $
-    it "has constants equal to the hstox expected key size" $ do
-      fromIntegral C.tox_public_key_size `shouldBe` Sodium.boxPK
-      fromIntegral C.tox_secret_key_size `shouldBe` Sodium.boxSK
-      C.tox_address_size `shouldBe` C.tox_public_key_size + 6
-      C.tox_max_name_length `shouldBe` 128
-      C.tox_max_status_message_length `shouldBe` 1007
-      C.tox_max_friend_request_length `shouldBe` 1016
-      C.tox_max_message_length `shouldBe` C.tox_max_custom_packet_size - 1
-      C.tox_max_custom_packet_size `shouldBe` 1373
-      C.tox_max_filename_length `shouldBe` 255
-      C.tox_hash_length `shouldBe` C.tox_file_id_length
-
-  describe "Options" $ do
-    it "can be marshalled to C and back" $
-      property $ \options -> do
-        res <- C.withOptions options C.peekToxOptions
-        res `shouldBe` Right options
-
-    it "is saved correctly by pokeToxOptions" $
-      property $ \options0 options1 -> do
-        res <- C.withOptions options0 $ \ptr -> do
-          C.pokeToxOptions options1 ptr (return ())
-          C.peekToxOptions ptr
-        res `shouldBe` Right options0
-
-    it "has a 'def' that is equivalent to the C default options" $ do
-      res <- C.withToxOptions C.peekToxOptions
-      res `shouldBe` Right def
-
-  describe "nospam" $
-    it "can be retrieved after being set" $
-      property $ \nospam ->
-        must $ C.withDefaultTox $ \tox -> do
-          C.tox_self_set_nospam tox nospam
-          nospam' <- C.tox_self_get_nospam tox
-          nospam' `shouldBe` nospam
-
-  describe "public key" $ do
-    it "is a prefix of the address" $
-      must $ C.withDefaultTox $ \tox -> do
-        pk <- C.toxSelfGetPublicKey tox
-        addr <- C.toxSelfGetAddress tox
-        BS.unpack addr `shouldStartWith` BS.unpack pk
-
-    it "is not equal to the secret key" $
-      must $ C.withDefaultTox $ \tox -> do
-        pk <- C.toxSelfGetPublicKey tox
-        sk <- C.toxSelfGetSecretKey tox
-        pk `shouldNotBe` sk
diff --git a/test/Network/Tox/CSpec.hs b/test/Network/Tox/CSpec.hs
deleted file mode 100644
--- a/test/Network/Tox/CSpec.hs
+++ /dev/null
@@ -1,96 +0,0 @@
-{-# LANGUAGE FlexibleInstances          #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE Trustworthy                #-}
-module Network.Tox.CSpec where
-
-import           Control.Applicative     ((<$>))
-import           Control.Concurrent      (threadDelay)
-import           Control.Concurrent.MVar (MVar, newMVar, readMVar)
-import           Control.Exception       (bracket)
-import           Control.Monad           (when)
-import qualified Data.ByteString         as BS
-import qualified Data.ByteString.Base16  as Base16
-import           Data.String             (fromString)
-import           Foreign.StablePtr       (StablePtr, freeStablePtr,
-                                          newStablePtr)
-import           Foreign.Storable        (Storable (..))
-import           Test.Hspec
-
-import qualified Network.Tox.C           as C
-
-
-bootstrapKey :: BS.ByteString
-bootstrapKey =
-  fst . Base16.decode . fromString $
-    "15E9C309CFCB79FDDF0EBA057DABB49FE15F3803B1BFF06536AE2E5BA5E4690E"
-
-bootstrapHost :: String
-bootstrapHost = "tox.ngc.zone"
-
-
-options :: C.Options
-options = C.Options
-  { C.ipv6Enabled  = True
-  , C.udpEnabled   = True
-  , C.proxyType    = C.ProxyTypeNone
-  , C.proxyHost    = ""
-  , C.proxyPort    = 0
-  , C.startPort    = 33445
-  , C.endPort      = 33545
-  , C.tcpPort      = 3128
-  , C.savedataType = C.SavedataTypeNone
-  , C.savedataData = BS.empty
-  }
-
-
-while :: IO Bool -> IO () -> IO ()
-while cond io = do
-  continue <- cond
-  when continue $ io >> while cond io
-
-
-getRight :: (Monad m, Show a) => Either a b -> m b
-getRight (Left  l) = fail $ show l
-getRight (Right r) = return r
-
-
-must :: Show a => IO (Either a b) -> IO b
-must = (getRight =<<)
-
-
-newtype UserData = UserData Int
-  deriving (Eq, Storable, Read, Show)
-
-instance C.CHandler UserData where
-  cSelfConnectionStatus _ conn ud = do
-    print conn
-    print ud
-    return $ UserData 4321
-
-
-withStablePtr :: a -> (StablePtr a -> IO b) -> IO b
-withStablePtr x = bracket (newStablePtr x) freeStablePtr
-
-
-toxIterate :: MVar a -> C.Tox a -> IO ()
-toxIterate ud tox =
-  withStablePtr ud (C.tox_iterate tox)
-
-
-spec :: Spec
-spec =
-  describe "toxcore" $
-    it "can bootstrap" $
-      must $ C.withOptions options $ \optPtr ->
-        must $ C.withTox optPtr $ \tox -> do
-          must $ C.toxBootstrap   tox bootstrapHost 33445 bootstrapKey
-          must $ C.toxAddTcpRelay tox bootstrapHost 33445 bootstrapKey
-
-          C.withCHandler tox $ do
-            ud <- newMVar (UserData 1234)
-            while ((/= UserData 4321) <$> readMVar ud) $ do
-              toxIterate ud tox
-              putStrLn "tox_iterate"
-              interval <- C.tox_iteration_interval tox
-              threadDelay $ fromIntegral $ interval * 10000
-            putStrLn "done"
diff --git a/test/Network/Tox/Crypto/BoxSpec.hs b/test/Network/Tox/Crypto/BoxSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/Crypto/BoxSpec.hs
@@ -0,0 +1,76 @@
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE Trustworthy         #-}
+module Network.Tox.Crypto.BoxSpec where
+
+import           Control.Monad.IO.Class         (liftIO)
+import qualified Data.ByteString                as ByteString
+import           Data.Proxy                     (Proxy (..))
+import qualified Data.Result                    as R
+import           Network.MessagePack.Rpc        (local)
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Network.Tox.Crypto.Box         (CipherText, PlainText (..))
+import qualified Network.Tox.Crypto.Box         as Box
+import qualified Network.Tox.Crypto.CombinedKey as CombinedKey
+import           Network.Tox.Crypto.KeyPair     (KeyPair (..))
+import qualified Network.Tox.Crypto.KeyPair     as KeyPair
+import           Network.Tox.EncodingSpec
+
+
+spec :: Spec
+spec = do
+  describe "Text" $ do
+    rpcSpec (Proxy :: Proxy CipherText)
+    rpcSpec (Proxy :: Proxy PlainText)
+    binarySpec (Proxy :: Proxy CipherText)
+    binarySpec (Proxy :: Proxy PlainText)
+    readShowSpec (Proxy :: Proxy CipherText)
+    readShowSpec (Proxy :: Proxy PlainText)
+
+    it "encodes/decodes arbitrary texts" $
+      property $ \(bytes :: String) ->
+        Box.decode (Box.encode bytes) `shouldBe` Just bytes
+
+    it "should return an error message in a monad that supports fail" $
+      case Box.decode (PlainText (ByteString.pack [0x00])) of
+        R.Success success -> expectationFailure $ "Expected failure, but got success: " ++ success
+        R.Failure failure -> failure `shouldContain` "not enough bytes"
+
+  describe "encrypt" $
+    it "encrypts data with a random keypair" $
+      property $ \nonce plainText -> do
+        KeyPair sk pk <- local KeyPair.newKeyPairR
+        let combinedKey = local CombinedKey.precomputeR sk pk
+        let cipherText = local Box.encryptR combinedKey nonce plainText
+        let decryptedText = Box.decrypt combinedKey nonce cipherText
+        decryptedText `shouldBe` Just plainText
+
+  describe "decrypt" $ do
+    it "decrypts data encrypted with 'encrypt'" $
+      property $ \combinedKey nonce plainText -> do
+        let cipherText = local Box.encryptR combinedKey nonce plainText
+        let decryptedText = local Box.decryptR combinedKey nonce cipherText
+        decryptedText `shouldBe` Just plainText
+
+    it "decrypts encrypted data with a random keypair" $
+      property $ \nonce plainText -> do
+        KeyPair sk pk <- local KeyPair.newKeyPairR
+        let combinedKey = local CombinedKey.precomputeR sk pk
+        let cipherText = Box.encrypt combinedKey nonce plainText
+        let decryptedText = local Box.decryptR combinedKey nonce cipherText
+        decryptedText `shouldBe` Just plainText
+
+  it "supports communication with asymmetric keys" $
+    property $ \nonce plainText -> do
+      KeyPair sk1 pk1 <- local KeyPair.newKeyPairR
+      KeyPair sk2 pk2 <- local KeyPair.newKeyPairR
+
+      let key1 = local CombinedKey.precomputeR sk1 pk2
+      let key2 = CombinedKey.precompute sk2 pk1
+      key1 `shouldBe` key2
+
+      let cipherText = local Box.encryptR key1 nonce plainText
+      let decryptedText = Box.decrypt key2 nonce cipherText
+      decryptedText `shouldBe` Just plainText
diff --git a/test/Network/Tox/Crypto/CombinedKeySpec.hs b/test/Network/Tox/Crypto/CombinedKeySpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/Crypto/CombinedKeySpec.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.Crypto.CombinedKeySpec where
+
+import           Control.Monad.IO.Class         (liftIO)
+import           Network.MessagePack.Rpc        (local)
+import           Test.Hspec
+import           Test.QuickCheck
+
+import qualified Network.Tox.Crypto.CombinedKey as CombinedKey
+import           Network.Tox.Crypto.KeyPair     (KeyPair (..))
+
+
+spec :: Spec
+spec =
+  describe "precompute" $ do
+    it "always computes the same combined key for the same public/secret keys" $
+      property $ \sk pk -> do
+        let ck1 = local CombinedKey.precomputeR sk pk
+        let ck2 = local CombinedKey.precomputeR sk pk
+        ck1 `shouldBe` ck2
+
+    it "computes the same combined key for pk1/sk2 and pk2/sk1" $
+      property $ \(KeyPair sk1 pk1) (KeyPair sk2 pk2) -> do
+        let ck1 = local CombinedKey.precomputeR sk1 pk2
+        let ck2 = local CombinedKey.precomputeR sk2 pk1
+        ck1 `shouldBe` ck2
diff --git a/test/Network/Tox/Crypto/KeyPairSpec.hs b/test/Network/Tox/Crypto/KeyPairSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/Crypto/KeyPairSpec.hs
@@ -0,0 +1,61 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.Crypto.KeyPairSpec where
+
+import           Control.Monad.IO.Class         (liftIO)
+import qualified Crypto.Saltine.Class           as Sodium (encode)
+import           Data.Proxy                     (Proxy (..))
+import           Network.MessagePack.Rpc        (local)
+import           Test.Hspec
+import           Test.QuickCheck
+
+import qualified Network.Tox.Crypto.Box         as Box
+import qualified Network.Tox.Crypto.CombinedKey as CombinedKey
+import qualified Network.Tox.Crypto.Key         as Key
+import           Network.Tox.Crypto.KeyPair     (KeyPair (..))
+import qualified Network.Tox.Crypto.KeyPair     as KeyPair
+import           Network.Tox.EncodingSpec
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy KeyPair)
+  readShowSpec (Proxy :: Proxy KeyPair)
+
+  describe "newKeyPair" $ do
+    it "generates different key pairs on subsequent calls" $ do
+      kp1 <- local KeyPair.newKeyPairR
+      kp2 <- local KeyPair.newKeyPairR
+      kp1 `shouldNotBe` kp2
+
+    it "generates different secret keys on subsequent calls" $ do
+      KeyPair sk1 _ <- local KeyPair.newKeyPairR
+      KeyPair sk2 _ <- local KeyPair.newKeyPairR
+      sk1 `shouldNotBe` sk2
+
+    it "generates different public keys on subsequent calls" $ do
+      KeyPair _ pk1 <- local KeyPair.newKeyPairR
+      KeyPair _ pk2 <- local KeyPair.newKeyPairR
+      pk1 `shouldNotBe` pk2
+
+    it "generates a public key that is different from the secret key" $ do
+      kp <- local KeyPair.newKeyPairR
+      Sodium.encode (KeyPair.secretKey kp) `shouldNotBe` Sodium.encode (KeyPair.publicKey kp)
+
+  describe "fromSecretKey" $ do
+    it "doesn't modify the secret key" $
+      property $ \sk -> do
+        let KeyPair sk' _ = local KeyPair.fromSecretKeyR sk
+        sk' `shouldBe` sk
+
+    it "never computes a public key that is equal to the secret key" $
+      property $ \sk -> do
+        let KeyPair _ (Key.Key pk) = local KeyPair.fromSecretKeyR sk
+        Sodium.encode pk `shouldNotBe` Sodium.encode sk
+
+    it "computes a usable public key from an invalid secret key" $
+      property $ \plainText nonce -> do
+        let KeyPair sk pk = local KeyPair.fromSecretKeyR $ read "\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\""
+        let ck = local CombinedKey.precomputeR sk pk
+        let encrypted = local Box.encryptR ck nonce plainText
+        let decrypted = local Box.decryptR ck nonce encrypted
+        decrypted `shouldBe` Just plainText
diff --git a/test/Network/Tox/Crypto/KeySpec.hs b/test/Network/Tox/Crypto/KeySpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/Crypto/KeySpec.hs
@@ -0,0 +1,92 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE Trustworthy         #-}
+module Network.Tox.Crypto.KeySpec where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import qualified Crypto.Saltine.Class     as Sodium
+import           Data.Binary              (Binary)
+import           Data.ByteString          (ByteString)
+import qualified Data.ByteString          as ByteString
+import           Data.Proxy               (Proxy (..))
+import qualified Data.Result              as R
+import           Data.Typeable            (Typeable)
+import qualified Network.Tox.Binary       as Binary
+import           Network.Tox.Crypto.Key   (Key (..))
+import qualified Network.Tox.Crypto.Key   as Key
+import           Network.Tox.EncodingSpec
+import qualified Text.Read                as Read
+
+
+readMaybe :: String -> Maybe Key.PublicKey
+readMaybe = Read.readMaybe
+
+
+decodeM :: Monad m => ByteString -> m Key.PublicKey
+decodeM = Key.decode
+
+
+keyToInteger :: String -> Integer
+keyToInteger string =
+  Key.keyToInteger (read string :: Key.PublicKey)
+
+
+encodeDecodePublicKey :: Key.PublicKey -> Expectation
+encodeDecodePublicKey key =
+  Sodium.decode (Sodium.encode key) `shouldBe` Just key
+
+
+localEncodingSpec
+  :: (Typeable a, Read a, Show a, Binary a, Arbitrary a, Eq a)
+  => Proxy a -> Spec
+localEncodingSpec proxy =
+  describe (Binary.typeName proxy) $ do
+    binarySpec proxy
+    readShowSpec proxy
+
+
+spec :: Spec
+spec = do
+  -- PublicKey for RPC tests.
+  rpcSpec (Proxy :: Proxy Key.PublicKey)
+
+  -- All others only local tests.
+  localEncodingSpec (Proxy :: Proxy Key.CombinedKey)
+  localEncodingSpec (Proxy :: Proxy Key.Nonce)
+  localEncodingSpec (Proxy :: Proxy Key.PublicKey)
+  localEncodingSpec (Proxy :: Proxy Key.SecretKey)
+
+  describe "IsEncoding" $
+    it "decodes encoded public keys correctly" $
+      property encodeDecodePublicKey
+
+  describe "read" $ do
+    it "decodes valid hex string to PublicKey" $
+      let
+        actual = readMaybe "\"0100000000000000000000000000000000000000000000000000000000000010\""
+        Just expected = Sodium.decode $ ByteString.pack [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x10]
+      in
+      actual `shouldBe` Just (Key expected)
+
+    it "decodes empty string to Nothing" $ do
+      let actual = readMaybe ""
+      actual `shouldBe` Nothing
+      case decodeM ByteString.empty of
+        R.Failure msg -> msg `shouldStartWith` "unable to decode"
+        R.Success val -> expectationFailure $ "unexpected success: " ++ show val
+
+    it "decodes valid hex string of wrong length to Nothing" $
+      let actual = readMaybe "\"0110\"" in
+      actual `shouldBe` Nothing
+
+  describe "keyToInteger" $ do
+    it "converts keys to Integer in big endian" $ do
+      keyToInteger "\"fe00000000000000000000000000000000000000000000000000000000000000\""
+        `shouldBe`  0xfe00000000000000000000000000000000000000000000000000000000000000
+      keyToInteger "\"00000000000000000000000000000000000000000000000000000000000000fe\""
+        `shouldBe`  0x00000000000000000000000000000000000000000000000000000000000000fe
+
+    it "encodes all keys to positive Integers" $
+      property $ \key ->
+        Key.keyToInteger (key :: Key.PublicKey) `shouldSatisfy` (0 <=)
diff --git a/test/Network/Tox/Crypto/NonceSpec.hs b/test/Network/Tox/Crypto/NonceSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/Crypto/NonceSpec.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.Crypto.NonceSpec where
+
+import           Control.Monad.IO.Class   (liftIO)
+import           Network.MessagePack.Rpc  (local)
+import           Test.Hspec
+import           Test.QuickCheck
+
+import qualified Network.Tox.Crypto.Nonce as Nonce
+
+
+spec :: Spec
+spec = do
+  describe "newNonce" $
+    it "generates a different nonce on subsequent calls to newNonce" $ do
+      nonce1 <- local Nonce.newNonceR
+      nonce2 <- local Nonce.newNonceR
+      liftIO $ nonce1 `shouldNotBe` nonce2
+
+  describe "nudge" $
+    it "creates a nonce that is different from the passed nonce" $
+      property $ \nonce ->
+        Nonce.nudge nonce `shouldNotBe` nonce
+
+  describe "increment" $ do
+    it "generates a different nonce for arbitrary nonces" $
+      property $ \nonce -> do
+        let incremented = local Nonce.incrementR nonce
+        incremented `shouldNotBe` nonce
+
+    it "increments a 0 nonce to 1" $ do
+      let nonce = read "\"000000000000000000000000000000000000000000000000\""
+      let nonce' = read "\"000000000000000000000000000000000000000000000001\""
+      let incremented = local Nonce.incrementR nonce
+      incremented `shouldBe` nonce'
+
+    it "increments a max nonce to 0" $ do
+      let nonce = read "\"ffffffffffffffffffffffffffffffffffffffffffffffff\""
+      let nonce' = read "\"000000000000000000000000000000000000000000000000\""
+      let incremented = local Nonce.incrementR nonce
+      incremented `shouldBe` nonce'
+
+    it "increments a max-1 nonce to max" $ do
+      let nonce = read "\"fffffffffffffffffffffffffffffffffffffffffffffffe\""
+      let nonce' = read "\"ffffffffffffffffffffffffffffffffffffffffffffffff\""
+      let incremented = local Nonce.incrementR nonce
+      incremented `shouldBe` nonce'
+
+    it "increments a little endian max-1 nonce to little endian 255" $ do
+      let nonce = read "\"feffffffffffffffffffffffffffffffffffffffffffffff\""
+      let nonce' = read "\"ff0000000000000000000000000000000000000000000000\""
+      let incremented = local Nonce.incrementR nonce
+      incremented `shouldBe` nonce'
diff --git a/test/Network/Tox/CryptoSpec.hs b/test/Network/Tox/CryptoSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/CryptoSpec.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.CryptoSpec where
+
+import           Test.Hspec
+
+import qualified Network.Tox.Crypto as Crypto
+
+
+spec :: Spec
+spec = return ()
diff --git a/test/Network/Tox/DHT/ClientListSpec.hs b/test/Network/Tox/DHT/ClientListSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/ClientListSpec.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHT.ClientListSpec where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Control.Monad                 (unless, when)
+import           Data.List                     (sort, sortOn)
+import qualified Data.Map                      as Map
+import           Data.Ord                      (comparing)
+import           Data.Proxy                    (Proxy (..))
+import           Network.Tox.Crypto.Key        (PublicKey)
+import           Network.Tox.DHT.ClientList    (ClientList)
+import qualified Network.Tox.DHT.ClientList    as ClientList
+import qualified Network.Tox.DHT.Distance      as Distance
+import           Network.Tox.EncodingSpec
+import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo
+
+
+spec :: Spec
+spec = do
+  readShowSpec (Proxy :: Proxy ClientList)
+
+  it "has no more than maxSize elements" $
+    property $ \clientList ->
+      Map.size (ClientList.nodes clientList) `shouldSatisfy` (<= ClientList.maxSize clientList)
+
+  it "removing a node twice has no effect" $
+    property $ \baseKey time nodeInfo size ->
+      let
+        empty        = ClientList.empty baseKey
+        afterAdd     = ClientList.addNode time nodeInfo $ empty size
+        afterRemove0 = ClientList.removeNode (NodeInfo.publicKey nodeInfo) afterAdd
+        afterRemove1 = ClientList.removeNode (NodeInfo.publicKey nodeInfo) afterRemove0
+      in
+      afterRemove0 `shouldBe` afterRemove1
+
+  it "adding a node twice has no effect" $
+    property $ \baseKey time nodeInfo size ->
+      let
+        empty        = ClientList.empty baseKey
+        afterAdd0    = ClientList.addNode time nodeInfo $ empty size
+        afterAdd1    = ClientList.addNode time nodeInfo afterAdd0
+      in
+      afterAdd0 `shouldBe` afterAdd1
+
+  it "adding a non-viable node has no effect" $
+    property $ \clientList time nodeInfo ->
+      let
+        viable   = ClientList.viable nodeInfo clientList
+        afterAdd = ClientList.addNode time nodeInfo clientList
+      in
+      unless viable $ afterAdd `shouldBe` clientList
+
+  describe "addNode" $
+    it "keeps the k nodes closest to the base key" $
+      property $ \clientList time nodeInfo ->
+        let
+          allNodes          = (nodeInfo:) $ ClientList.nodeInfos clientList
+          keptNodes         = ClientList.nodeInfos $ ClientList.addNode time nodeInfo clientList
+          nodeDistance node = Distance.xorDistance (ClientList.baseKey clientList) (NodeInfo.publicKey node)
+          sortNodes         = sortOn nodeDistance
+        in
+        take (ClientList.maxSize clientList) (sortNodes allNodes) `shouldBe` sortNodes keptNodes
+
+  describe "foldNodes" $
+    it "iterates over nodes in order of distance from the base key" $
+      property $ \clientList ->
+        let
+          nodes             = reverse $ ClientList.foldNodes (flip (:)) [] clientList
+          nodeDistance node = Distance.xorDistance (ClientList.baseKey clientList) (NodeInfo.publicKey node)
+          sortNodes         = sortOn nodeDistance
+        in
+        nodes `shouldBe` sortNodes nodes
diff --git a/test/Network/Tox/DHT/DhtPacketSpec.hs b/test/Network/Tox/DHT/DhtPacketSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/DhtPacketSpec.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHT.DhtPacketSpec where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Data.Binary                   (Binary)
+import qualified Data.Binary                   as Binary (get, put)
+import qualified Data.Binary.Get               as Binary (runGet)
+import qualified Data.Binary.Put               as Binary (runPut)
+import           Data.Proxy                    (Proxy (..))
+import           Network.Tox.Crypto.Key        (Nonce)
+import           Network.Tox.Crypto.KeyPair    (KeyPair (..))
+import           Network.Tox.DHT.DhtPacket     (DhtPacket (..))
+import qualified Network.Tox.DHT.DhtPacket     as DhtPacket
+import           Network.Tox.EncodingSpec
+import           Network.Tox.NodeInfo.NodeInfo (NodeInfo)
+
+
+encodeAndDecode :: (Binary a, Binary b) => KeyPair -> KeyPair -> Nonce -> a -> Maybe b
+encodeAndDecode senderKeyPair receiverKeyPair nonce payload =
+  let
+    KeyPair _ receiverPublicKey = receiverKeyPair
+    packet = DhtPacket.encode senderKeyPair receiverPublicKey nonce payload
+    packet' = Binary.runGet Binary.get $ Binary.runPut $ Binary.put packet
+  in
+  DhtPacket.decode receiverKeyPair packet'
+
+
+encodeAndDecodeString :: KeyPair -> KeyPair -> Nonce -> String -> Maybe String
+encodeAndDecodeString = encodeAndDecode
+
+
+encodeCharAndDecodeString :: KeyPair -> KeyPair -> Nonce -> Char -> Maybe String
+encodeCharAndDecodeString = encodeAndDecode
+
+
+encodeIntAndDecodeNodeInfo :: KeyPair -> KeyPair -> Nonce -> Int -> Maybe NodeInfo
+encodeIntAndDecodeNodeInfo = encodeAndDecode
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy DhtPacket)
+  binarySpec (Proxy :: Proxy DhtPacket)
+  readShowSpec (Proxy :: Proxy DhtPacket)
+
+  it "encodes and decodes packets" $
+    property $ \senderKeyPair receiverKeyPair nonce payload ->
+      encodeAndDecodeString senderKeyPair receiverKeyPair nonce payload `shouldBe` Just payload
+
+  it "fails to decode packets with the wrong secret key" $
+    property $ \senderKeyPair (KeyPair _ receiverPublicKey) badSecretKey nonce payload ->
+      encodeAndDecodeString senderKeyPair (KeyPair badSecretKey receiverPublicKey) nonce payload `shouldBe` Nothing
+
+  it "fails to decode packets with the wrong payload type (Partial)" $
+    property $ \senderKeyPair receiverKeyPair nonce payload ->
+      encodeCharAndDecodeString senderKeyPair receiverKeyPair nonce payload `shouldBe` Nothing
+
+  it "fails to decode packets with the wrong payload type (Fail)" $
+    property $ \senderKeyPair receiverKeyPair nonce payload ->
+      encodeIntAndDecodeNodeInfo senderKeyPair receiverKeyPair nonce payload `shouldBe` Nothing
+
+  it "should decode empty CipherText correctly" $
+    expectDecoded
+      [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+      , 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+      , 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+      ] $
+      DhtPacket
+        (read "\"0000000000000000000000000000000000000000000000000000000000000000\"")
+        (read "\"000000000000000000000000000000000000000000000000\"")
+        (read "\"00000000000000000000000000000000\"")
diff --git a/test/Network/Tox/DHT/DhtRequestPacketSpec.hs b/test/Network/Tox/DHT/DhtRequestPacketSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/DhtRequestPacketSpec.hs
@@ -0,0 +1,16 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHT.DhtRequestPacketSpec where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Data.Proxy                       (Proxy (..))
+import           Network.Tox.DHT.DhtRequestPacket (DhtRequestPacket (..))
+import           Network.Tox.EncodingSpec
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy DhtRequestPacket)
+  binarySpec (Proxy :: Proxy DhtRequestPacket)
+  readShowSpec (Proxy :: Proxy DhtRequestPacket)
diff --git a/test/Network/Tox/DHT/DhtStateSpec.hs b/test/Network/Tox/DHT/DhtStateSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/DhtStateSpec.hs
@@ -0,0 +1,119 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHT.DhtStateSpec where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Control.Monad                 (unless, when)
+import           Data.List                     (nub, sort)
+import           Data.Proxy                    (Proxy (..))
+import qualified Data.Set                      as Set
+
+import qualified Network.Tox.Crypto.KeyPair    as KeyPair
+import           Network.Tox.DHT.DhtState      (DhtState)
+import qualified Network.Tox.DHT.DhtState      as DhtState
+import qualified Network.Tox.DHT.Distance      as Distance
+import qualified Network.Tox.DHT.NodeList      as NodeList
+import           Network.Tox.EncodingSpec
+import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo
+
+
+spec :: Spec
+spec = do
+  readShowSpec (Proxy :: Proxy DhtState)
+
+  it "the state can never contain itself" $
+    property $ \keyPair time nodeInfo ->
+      let
+        dhtState = DhtState.empty time keyPair
+        afterAdd = DhtState.addNode time
+          nodeInfo { NodeInfo.publicKey = KeyPair.publicKey keyPair }
+          dhtState
+      in
+      afterAdd `shouldBe` dhtState
+
+  describe "adding a node that was not yet contained" $ do
+    it "should result in a different state" $
+      property $ \keyPair time nodeInfo ->
+        let
+          dhtState = DhtState.empty time keyPair
+          afterAdd = DhtState.addNode time nodeInfo dhtState
+        in
+        unless (DhtState.containsNode (NodeInfo.publicKey nodeInfo) dhtState) $
+          afterAdd `shouldNotBe` dhtState
+
+    it "and removing it yields the same state" $
+      property $ \keyPair time nodeInfo ->
+        let
+          dhtState    = DhtState.empty time keyPair
+          afterAdd    = DhtState.addNode time nodeInfo dhtState
+          afterRemove = DhtState.removeNode (NodeInfo.publicKey nodeInfo) afterAdd
+        in
+        unless (DhtState.containsNode (NodeInfo.publicKey nodeInfo) dhtState) $
+          afterRemove `shouldBe` dhtState
+
+  describe "adding a node" $
+    it "and adding it again does not change the state twice" $
+      property $ \keyPair time nodeInfo ->
+        let
+          dhtState  = DhtState.empty time keyPair
+          afterAdd1 = DhtState.addNode time nodeInfo dhtState
+          afterAdd2 = DhtState.addNode time nodeInfo afterAdd1
+        in
+        afterAdd1 `shouldBe` afterAdd2
+
+  describe "adding a search node" $ do
+    it "should result in a different state" $
+      property $ \keyPair time publicKey ->
+        let
+          dhtState = DhtState.empty time keyPair
+          afterAdd = DhtState.addSearchKey time publicKey dhtState
+        in
+        afterAdd `shouldNotBe` dhtState
+
+    it "and removing it yields the same state" $
+      property $ \keyPair time publicKey ->
+        let
+          dhtState    = DhtState.empty time keyPair
+          afterAdd    = DhtState.addSearchKey time publicKey dhtState
+          afterRemove = DhtState.removeSearchKey publicKey afterAdd
+        in
+        afterRemove `shouldBe` dhtState
+
+    it "and adding it again does not change the state twice" $
+      property $ \keyPair time publicKey ->
+        let
+          dhtState  = DhtState.empty time keyPair
+          afterAdd1 = DhtState.addSearchKey time publicKey dhtState
+          afterAdd2 = DhtState.addSearchKey time publicKey afterAdd1
+        in
+        afterAdd1 `shouldBe` afterAdd2
+
+    it "and adding a node info for it will not add it to the search entry's Client List" $
+      property $ \keyPair time nodeInfo ->
+        let
+          dhtState = DhtState.empty time keyPair
+          afterAddSearchKey = DhtState.addSearchKey time
+            (NodeInfo.publicKey nodeInfo)
+            dhtState
+        in
+        DhtState.size (DhtState.addNode time nodeInfo afterAddSearchKey)
+        `shouldBe`
+        DhtState.size (DhtState.addNode time nodeInfo dhtState)
+
+  describe "takeClosestNodesTo" $ do
+    it "returns the requested number of nodes if there are enough nodes in the state" $
+      property $ \dhtState n publicKey -> n >= 0 ==>
+        let
+          taken = DhtState.takeClosestNodesTo n publicKey dhtState
+          nodes = NodeList.foldNodes (flip Set.insert) Set.empty dhtState
+        in
+        when (Set.size nodes >= n) $ length taken `shouldBe` n
+
+    it "returns distinct nodes sorted by distance from the given key" $
+      property $ \dhtState n publicKey -> n >= 0 ==>
+        let
+          taken = DhtState.takeClosestNodesTo n publicKey dhtState
+          dists = map (Distance.xorDistance publicKey . NodeInfo.publicKey) taken
+        in
+        dists `shouldBe` (nub . sort) dists
diff --git a/test/Network/Tox/DHT/DistanceSpec.lhs b/test/Network/Tox/DHT/DistanceSpec.lhs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/DistanceSpec.lhs
@@ -0,0 +1,169 @@
+\begin{code}
+{-# LANGUAGE LambdaCase  #-}
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHT.DistanceSpec where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Data.Monoid              (Monoid, mappend, mempty)
+import           Data.Proxy               (Proxy (..))
+import qualified Network.Tox.Crypto.Key   as Key
+import           Network.Tox.DHT.Distance
+import           Network.Tox.EncodingSpec
+
+\end{code}
+
+XOR is a valid metric, i.e. it satisfies the required conditions:
+
+\begin{enumerate}
+  \item Non-negativity \texttt{distance(x, y) >= 0}: Since public keys are
+    Crypto Numbers, which are by definition non-negative, their XOR is necessarily
+    non-negative.
+  \item Identity of indiscernibles \texttt{distance(x, y) == 0} iff \texttt{x ==
+    y}: The XOR of two integers is zero iff they are equal.
+  \item Symmetry \texttt{distance(x, y) == distance(y, x)}: XOR is a symmetric
+    operation.
+  \item Subadditivity \texttt{distance(x, z) <= distance(x, y) + distance(y,
+    z)}: follows from associativity, since \texttt{x XOR z = x XOR (y XOR y) XOR
+    z = distance(x, y) XOR distance(y, z)} which is not greater than
+    \texttt{distance(x, y) + distance(y, z)}.
+\end{enumerate}
+
+In addition, XOR has other useful properties:
+
+\begin{itemize}
+  \item Unidirectionality: given the key \texttt{x} and the distance \texttt{d}
+    there exist one and only one key \texttt{y} such that \texttt{distance(x,
+    y) = d}.
+
+    The implication is that repeated lookups are likely to pass along the same
+    way and thus caching makes sense.
+
+    Source:
+    \href{http://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf}{maymounkov-kademlia}
+\end{itemize}
+
+\begin{code}
+
+metricSpec :: ( Eq a, Arbitrary a, Show a
+              , Eq b, Ord b, Monoid b, Show b)
+           => (a -> a -> b) -> Spec
+metricSpec d = do
+  it "satisfies non-negativity" $
+    property $ \x y ->
+      d x y > mempty
+
+  it "satisfies identity of indiscernibles" $
+    property $ \x y ->
+      d x y == mempty `shouldBe` x == y
+
+  it "satisfies symmetry" $
+    property $ \x y ->
+      d x y `shouldBe` d y x
+
+  it "satisfies triangle inequality" $
+    property $ \x y z ->
+      d x z <= d x y `mappend` d y z
+
+
+zeroKey :: Key.PublicKey
+zeroKey = read "\"0000000000000000000000000000000000000000000000000000000000000000\""
+
+
+spec :: Spec
+spec = do
+  readShowSpec (Proxy :: Proxy Distance)
+
+  describe "xorDistance" $ do
+    metricSpec xorDistance
+
+    it "should not partition the network at 0x7f/0x80" $
+      let
+        o = zeroKey
+        x = read "\"8000000000000000000000000000000000000000000000000000000000000000\""
+        y = read "\"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\""
+
+        ox = xorDistance o x
+        oy = xorDistance o y
+      in
+
+      oy < ox
+\end{code}
+
+Example: Given three nodes with keys 2, 5, and 6:
+
+\begin{itemize}
+  \item \texttt{2 XOR 5 = 7}
+  \item \texttt{2 XOR 6 = 4}
+  \item \texttt{5 XOR 2 = 7}
+  \item \texttt{5 XOR 6 = 3}
+  \item \texttt{6 XOR 2 = 4}
+  \item \texttt{6 XOR 5 = 3}
+\end{itemize}
+
+The closest node from both 2 and 5 is 6.  The closest node from 6 is 5 with
+distance 3.  This example shows that a key that is close in terms of integer
+addition may not necessarily be close in terms of XOR.
+
+\begin{code}
+
+    it "should yield the values from the example from the spec" $
+      let
+        k1 = read "\"0000000000000000000000000000000000000000000000000000000000000002\""
+        k2 = read "\"0000000000000000000000000000000000000000000000000000000000000005\""
+        k3 = read "\"0000000000000000000000000000000000000000000000000000000000000006\""
+      in do
+
+      xorDistance k1 k2 `shouldBe` Distance 7
+      xorDistance k1 k3 `shouldBe` Distance 4
+      xorDistance k2 k1 `shouldBe` Distance 7
+      xorDistance k2 k3 `shouldBe` Distance 3
+      xorDistance k3 k1 `shouldBe` Distance 4
+      xorDistance k3 k2 `shouldBe` Distance 3
+
+  describe "log2" $ do
+    it "should result in 0 <= value for any Distance" $
+      property $ \distance ->
+        log2 distance `shouldSatisfy` \case
+          Nothing    -> True
+          Just value -> 0 <= value
+
+    it "should result in 0 <= value < 256 for public key distances" $
+      property $ \pk1 pk2 ->
+        log2 (xorDistance pk1 pk2) `shouldSatisfy` \case
+          Nothing    -> True
+          Just value -> 0 <= value && value < 256
+
+    it "should result in 255 for maximum distance" $
+      let
+        k1 = read "\"0000000000000000000000000000000000000000000000000000000000000000\""
+        k2 = read "\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\""
+      in
+      log2 (xorDistance k1 k2) `shouldBe` Just 255
+
+    it "should result in 255 for the highest bit set" $
+      let
+        k1 = read "\"0000000000000000000000000000000000000000000000000000000000000000\""
+        k2 = read "\"8000000000000000000000000000000000000000000000000000000000000000\""
+      in
+      log2 (xorDistance k1 k2) `shouldBe` Just 255
+
+    it "should result in 254 for the highest-but-one bit set" $
+      let
+        k1 = read "\"0000000000000000000000000000000000000000000000000000000000000000\""
+        k2 = read "\"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\""
+      in
+      log2 (xorDistance k1 k2) `shouldBe` Just 254
+
+    it "should result in Nothing for distance 0" $
+      let
+        k = read "\"0000000000000000000000000000000000000000000000000000000000000000\""
+      in
+      log2 (xorDistance k k) `shouldBe` Nothing
+
+  describe "rebaseDistance" $
+    it "should satisfy: rebaseDistance a b (xorDistance a c) == xorDistance b c" $
+      property $ \a b c ->
+        rebaseDistance a b (xorDistance a c) `shouldBe` xorDistance b c
+\end{code}
diff --git a/test/Network/Tox/DHT/KBucketsSpec.hs b/test/Network/Tox/DHT/KBucketsSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/KBucketsSpec.hs
@@ -0,0 +1,138 @@
+{-# LANGUAGE LambdaCase          #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE Trustworthy         #-}
+module Network.Tox.DHT.KBucketsSpec where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Control.Monad                 (unless, when)
+import           Data.List                     (sort, sortOn)
+import qualified Data.Map                      as Map
+import           Data.Ord                      (comparing)
+import           Data.Proxy                    (Proxy (..))
+import           Network.Tox.Crypto.Key        (PublicKey)
+import           Network.Tox.DHT.ClientList    (ClientList)
+import qualified Network.Tox.DHT.ClientList    as ClientList
+import qualified Network.Tox.DHT.Distance      as Distance
+import           Network.Tox.DHT.KBuckets      (KBuckets)
+import qualified Network.Tox.DHT.KBuckets      as KBuckets
+import qualified Network.Tox.DHT.NodeList      as NodeList
+import           Network.Tox.EncodingSpec
+import           Network.Tox.NodeInfo.NodeInfo (NodeInfo)
+import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo
+
+
+makeInputKey :: Int -> Char -> PublicKey
+makeInputKey pos digit =
+  read $ "\"" ++ map (const '0') [0 .. pos - 1] ++ digit : map (const '0') [pos .. 63] ++ "\""
+
+
+getAllBuckets :: KBuckets -> [[NodeInfo]]
+getAllBuckets kBuckets =
+  map ClientList.nodeInfos (Map.elems (KBuckets.buckets kBuckets))
+
+
+spec :: Spec
+spec = do
+  readShowSpec (Proxy :: Proxy KBuckets)
+
+  it "does not accept adding a NodeInfo with the baseKey as publicKey" $
+    property $ \kBuckets time nodeInfo ->
+      KBuckets.addNode time nodeInfo { NodeInfo.publicKey = KBuckets.baseKey kBuckets } kBuckets
+        `shouldBe`
+        kBuckets
+
+  it "adding a node to an empty k-buckets always succeeds if baseKey <> nodeKey" $
+    property $ \baseKey time nodeInfo ->
+      let
+        empty = KBuckets.empty baseKey
+        kBuckets = KBuckets.addNode time nodeInfo empty
+      in
+      if baseKey == NodeInfo.publicKey nodeInfo
+      then kBuckets `shouldBe` empty
+      else kBuckets `shouldNotBe` empty
+
+  it "removing a node twice has no effect" $
+    property $ \baseKey time nodeInfo ->
+      let
+        empty        = KBuckets.empty baseKey
+        afterAdd     = KBuckets.addNode time nodeInfo empty
+        afterRemove0 = KBuckets.removeNode (NodeInfo.publicKey nodeInfo) afterAdd
+        afterRemove1 = KBuckets.removeNode (NodeInfo.publicKey nodeInfo) afterRemove0
+      in
+      afterRemove0 `shouldBe` afterRemove1
+
+  it "adding a node twice has no effect" $
+    property $ \baseKey time nodeInfo ->
+      let
+        empty        = KBuckets.empty baseKey
+        afterAdd0    = KBuckets.addNode time nodeInfo empty
+        afterAdd1    = KBuckets.addNode time nodeInfo afterAdd0
+      in
+      afterAdd0 `shouldBe` afterAdd1
+
+  it "adding a non-viable node has no effect" $
+    property $ \(kBuckets::KBuckets) time nodeInfo ->
+      let
+        viable   = KBuckets.viable nodeInfo kBuckets
+        afterAdd = KBuckets.addNode time nodeInfo kBuckets
+      in
+      unless viable $ afterAdd `shouldBe` kBuckets
+
+  it "never contains a NodeInfo with the public key equal to the base key" $
+    property $ \kBuckets ->
+      notElem (KBuckets.baseKey kBuckets) $ concatMap (map NodeInfo.publicKey) $ getAllBuckets kBuckets
+
+  describe "each bucket list" $ do
+    it "has maximum size bucketSize" $
+      property $ \kBuckets ->
+        mapM_
+          (`shouldSatisfy` (== KBuckets.bucketSize kBuckets) . ClientList.maxSize)
+          . Map.elems $ KBuckets.buckets kBuckets
+    it "has base key baseKey" $
+      property $ \kBuckets ->
+        mapM_
+          (`shouldSatisfy` (== KBuckets.baseKey kBuckets) . ClientList.baseKey)
+          . Map.elems $ KBuckets.buckets kBuckets
+
+  describe "bucketIndex" $ do
+    it "returns an integer between 0 and 255 for any two non-equal keys" $
+      property $ \k1 k2 ->
+        when (k1 /= k2) $
+          -- In our implementation, this is guaranteed by the type system, as
+          -- we're using Word8, which can only represent values in this range.
+          KBuckets.bucketIndex k1 k2 `shouldSatisfy` \case
+            Nothing    -> False
+            Just index -> index >= 0 && index <= 255
+
+    it "is undefined for two equal keys" $
+      property $ \k ->
+        KBuckets.bucketIndex k k `shouldBe` Nothing
+
+    it "returns a larger index for smaller distances and smaller index for larger distances" $
+      property $ \k1 k2 k3 ->
+        let
+          d = Distance.xorDistance k1
+          i = KBuckets.bucketIndex k1
+        in
+        if d k2 <= d k3
+        then i k2 >= i k3
+        else i k2 <= i k3
+
+    it "produces indices 0..255 for each bit set in the key" $
+      let
+        zeroKey = read "\"0000000000000000000000000000000000000000000000000000000000000000\""
+        inputs  = zeroKey : concatMap (\pos -> map (makeInputKey pos) ['8', '4', '2', '1']) [0 .. 63]
+        outputs = Nothing : map Just [0 .. 255]
+      in
+      map (KBuckets.bucketIndex zeroKey) inputs `shouldBe` outputs
+
+  describe "foldNodes" $
+    it "iterates over nodes in order of distance from the base key" $
+      property $ \kBuckets ->
+        let
+          nodes             = reverse $ NodeList.foldNodes (flip (:)) [] kBuckets
+          nodeDistance node = Distance.xorDistance (KBuckets.baseKey kBuckets) (NodeInfo.publicKey node)
+        in
+          nodes `shouldBe` sortOn nodeDistance nodes
diff --git a/test/Network/Tox/DHT/NodesRequestSpec.hs b/test/Network/Tox/DHT/NodesRequestSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/NodesRequestSpec.hs
@@ -0,0 +1,21 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHT.NodesRequestSpec where
+
+import           Test.Hspec
+
+import           Data.Proxy                   (Proxy (..))
+import qualified Network.Tox.Crypto.KeyPair   as KeyPair
+import           Network.Tox.DHT.NodesRequest (NodesRequest (..))
+import           Network.Tox.EncodingSpec
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy NodesRequest)
+  binarySpec (Proxy :: Proxy NodesRequest)
+  readShowSpec (Proxy :: Proxy NodesRequest)
+
+  it "has a public key" $ do
+    kp <- KeyPair.newKeyPair
+    let req = NodesRequest (KeyPair.publicKey kp)
+    requestedKey req `shouldBe` KeyPair.publicKey kp
diff --git a/test/Network/Tox/DHT/NodesResponseSpec.hs b/test/Network/Tox/DHT/NodesResponseSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/NodesResponseSpec.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHT.NodesResponseSpec where
+
+import           Test.Hspec
+
+import           Data.Proxy                    (Proxy (..))
+import           Network.Tox.DHT.NodesResponse (NodesResponse)
+import           Network.Tox.EncodingSpec
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy NodesResponse)
+  binarySpec (Proxy :: Proxy NodesResponse)
+  readShowSpec (Proxy :: Proxy NodesResponse)
diff --git a/test/Network/Tox/DHT/OperationSpec.hs b/test/Network/Tox/DHT/OperationSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/OperationSpec.hs
@@ -0,0 +1,89 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE Trustworthy         #-}
+module Network.Tox.DHT.OperationSpec where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Control.Monad                 (mzero, when)
+import           Control.Monad.Writer          (execWriterT)
+import qualified Data.Map                      as Map
+import           Data.Proxy                    (Proxy (..))
+
+import           Network.Tox.Crypto.Key        (PublicKey)
+import qualified Network.Tox.Crypto.KeyPair    as KeyPair
+import           Network.Tox.DHT.DhtState      (DhtState)
+import qualified Network.Tox.DHT.DhtState      as DhtState
+import qualified Network.Tox.DHT.Operation     as Operation
+import           Network.Tox.EncodingSpec
+import           Network.Tox.NodeInfo.NodeInfo (NodeInfo)
+import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo
+import qualified Network.Tox.Time              as Time
+
+spec :: Spec
+spec = do
+  describe "a newly initialised DHT node" $ do
+    it "contains no nodes" $
+      property $ \time seed ->
+        DhtState.size (Operation.initTestDhtState seed time) `shouldBe` 0
+
+    it "has a search list containing initRandomSearches search entries" $
+      property $ \time seed ->
+        (Map.size . DhtState.dhtSearchList $ Operation.initTestDhtState seed time)
+        `shouldBe` Operation.initRandomSearches
+
+  describe "periodic nodes requests" $
+    it "are not generated for an empty DHT State" $
+      property $ \keyPair time time' seed ->
+        let
+          dhtState = DhtState.empty time keyPair
+          requests = Operation.evalTestDhtNode seed time' dhtState . execWriterT $
+            Operation.randomRequests >> Operation.checkNodes
+        in
+        requests `shouldBe` []
+
+  describe "randomRequests" $ do
+    it "generates a single Nodes Request to a node in the close list after randomRequestPeriod" $
+      property $ \keyPair time (nodeInfos::[NodeInfo]) seed ->
+        let
+          dhtState       = DhtState.empty time keyPair
+          afterAdd       = foldr (DhtState.addNode time) dhtState nodeInfos
+          time'          = time Time.+ Operation.randomRequestPeriod
+          randomRequests = Operation.evalTestDhtNode seed time' afterAdd
+            . execWriterT $ Operation.randomRequests
+        in
+        case randomRequests of
+          [] -> DhtState.size dhtState `shouldBe` 0
+          Operation.RequestInfo nodeInfo publicKey : rs -> do
+            rs `shouldSatisfy` null
+            nodeInfo `shouldSatisfy` (`elem` nodeInfos)
+            publicKey `shouldBe` KeyPair.publicKey (DhtState.dhtKeyPair dhtState)
+
+    it "generates a Nodes Request to a node in a new search list after randomRequestPeriod" $
+      property $ \time publicKey dhtState (nodeInfos::[NodeInfo]) seed ->
+        let
+          afterSearch       = DhtState.addSearchKey time publicKey dhtState
+          afterAdd          = foldr (DhtState.addNode time) afterSearch nodeInfos
+          nodeAddedToSearch = not $ all ((== publicKey) . NodeInfo.publicKey) nodeInfos
+          time'             = time Time.+ Operation.randomRequestPeriod
+          randomRequests    = Operation.evalTestDhtNode seed time' afterAdd
+            . execWriterT $ Operation.randomRequests
+
+          requestIsForSearch (Operation.RequestInfo nodeInfo publicKey') =
+            publicKey == publicKey' && nodeInfo `elem` nodeInfos &&
+              NodeInfo.publicKey nodeInfo /= publicKey
+        in
+        when nodeAddedToSearch $
+          randomRequests `shouldSatisfy` any requestIsForSearch
+
+  describe "checkNodes" $
+    it "generates a Nodes Request to a newly added node after checkPeriod" $
+      property $ \time dhtState nodeInfo seed ->
+        let
+          viable   = DhtState.viable nodeInfo dhtState
+          afterAdd = DhtState.addNode time nodeInfo dhtState
+          time'    = time Time.+ Operation.checkPeriod
+          checks   = Operation.evalTestDhtNode seed time' afterAdd
+            . execWriterT $ Operation.checkNodes
+        in
+        when viable $ map Operation.requestTo checks `shouldSatisfy` (nodeInfo `elem`)
diff --git a/test/Network/Tox/DHT/PendingRepliesSpec.hs b/test/Network/Tox/DHT/PendingRepliesSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/PendingRepliesSpec.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHT.PendingRepliesSpec where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Network.Tox.DHT.PendingReplies (PendingReplies)
+import           Network.Tox.DHT.PendingReplies as PendingReplies
+import           Network.Tox.DHT.Stamped        (Stamped)
+import qualified Network.Tox.DHT.Stamped        as Stamped
+
+spec :: Spec
+spec = do
+  it "Accepts a response with the same RequestID iff sent since the cutoff" $
+    property $ \time time' node requestID ->
+      let
+        expecting = PendingReplies.expectReply time node requestID Stamped.empty
+      in
+        fst (PendingReplies.checkExpectedReply time' node requestID expecting)
+        `shouldBe` time' <= time
+
+  it "Rejects a response with a different requestID" $
+    property $ \time node requestID requestID' ->
+      let
+        expecting = PendingReplies.expectReply time node requestID Stamped.empty
+      in
+        fst (PendingReplies.checkExpectedReply time node requestID' expecting)
+        `shouldBe` requestID == requestID'
+
+  it "Doesn't accept the same response twice" $
+    property $ \time node requestID ->
+      let
+        expecting = PendingReplies.expectReply time node requestID Stamped.empty
+        accepted  = snd $ PendingReplies.checkExpectedReply time node requestID expecting
+      in
+        fst (PendingReplies.checkExpectedReply time node requestID accepted)
+        `shouldBe` False
diff --git a/test/Network/Tox/DHT/PingPacketSpec.hs b/test/Network/Tox/DHT/PingPacketSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/PingPacketSpec.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHT.PingPacketSpec where
+
+import           Test.Hspec
+
+import           Data.Proxy                 (Proxy (..))
+import           Network.Tox.DHT.PingPacket (PingPacket)
+import           Network.Tox.EncodingSpec
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy PingPacket)
+  binarySpec (Proxy :: Proxy PingPacket)
+  readShowSpec (Proxy :: Proxy PingPacket)
diff --git a/test/Network/Tox/DHT/RpcPacketSpec.hs b/test/Network/Tox/DHT/RpcPacketSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHT/RpcPacketSpec.hs
@@ -0,0 +1,21 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHT.RpcPacketSpec where
+
+import           Test.Hspec
+
+import           Data.Proxy                (Proxy (..))
+import           Data.Word                 (Word64)
+import           Network.Tox.DHT.RpcPacket (RequestId (..), RpcPacket (..))
+import           Network.Tox.EncodingSpec
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy (RpcPacket Word64))
+  binarySpec (Proxy :: Proxy (RpcPacket Word64))
+  readShowSpec (Proxy :: Proxy (RpcPacket Word64))
+
+  it "has a payload and a request ID" $ do
+    let packet = RpcPacket ["heyo"] (RequestId 0x12345678)
+    rpcPayload packet `shouldBe` ["heyo"]
+    requestId packet `shouldBe` RequestId 0x12345678
diff --git a/test/Network/Tox/DHTSpec.hs b/test/Network/Tox/DHTSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/DHTSpec.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.DHTSpec where
+
+import           Test.Hspec
+
+import qualified Network.Tox.DHT as DHT
+
+
+spec :: Spec
+spec = return ()
diff --git a/test/Network/Tox/EncodingSpec.hs b/test/Network/Tox/EncodingSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/EncodingSpec.hs
@@ -0,0 +1,146 @@
+{-# LANGUAGE LambdaCase          #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE Trustworthy         #-}
+module Network.Tox.EncodingSpec
+    ( spec
+    , binarySpec
+    , binaryGetPutSpec
+    , bitEncodingSpec
+    , readShowSpec
+    , rpcSpec
+    , expectDecoded
+    , expectDecoderFail
+    ) where
+
+import           Control.Monad.IO.Class     (liftIO)
+import           Data.MessagePack           (MessagePack)
+import           Network.MessagePack.Client (Client)
+import           Test.Hspec
+import           Test.QuickCheck            (Arbitrary)
+import qualified Test.QuickCheck            as QC
+
+import           Data.Binary                (Binary)
+import qualified Data.Binary                as Binary (get, put)
+import qualified Data.Binary.Bits.Get       as Bits (BitGet, runBitGet)
+import qualified Data.Binary.Bits.Put       as Bits (BitPut, runBitPut)
+import qualified Data.Binary.Get            as Binary (Decoder (..), Get,
+                                                       pushChunk, runGet,
+                                                       runGetIncremental)
+import qualified Data.Binary.Put            as Binary (Put, runPut)
+import qualified Data.ByteString            as ByteString
+import qualified Data.ByteString.Lazy       as LazyByteString
+import           Data.Proxy                 (Proxy (..))
+import           Data.Typeable              (Typeable)
+import           Data.Word                  (Word64, Word8)
+
+import qualified Network.Tox.Binary         as Binary
+import           Network.Tox.Encoding       (BitEncoding, bitGet, bitPut)
+
+
+spec :: Spec
+spec =
+  rpcSpec (Proxy :: Proxy Word64)
+
+
+-- | Limit the number of tests we do with the encoders/decoders.
+--
+-- These are fairly expensive, and running very large tests for them is probably
+-- not very valuable.
+property :: QC.Testable prop => prop -> QC.Property
+property = QC.withMaxSuccess 50 . QC.property
+
+
+expectDecoded :: (Binary a, Eq a, Show a) => [Word8] -> a -> Expectation
+expectDecoded bytes expected =
+  Binary.runGet Binary.get (LazyByteString.pack bytes) `shouldBe` expected
+
+
+expectDecoderFail :: Binary.Get a -> [Word8] -> String -> Expectation
+expectDecoderFail getA bytes expectedMessage =
+  let decoder = Binary.runGetIncremental getA in
+  case Binary.pushChunk decoder $ ByteString.pack bytes of
+    Binary.Fail _ _ msg -> msg `shouldContain` expectedMessage
+    Binary.Partial _    -> expectationFailure "Not enough input to reach failure"
+    Binary.Done {}      -> expectationFailure "Input unexpectedly yielded a valid value"
+
+
+binaryEncodeAndDecode :: (Eq a, Show a) => Binary.Get a -> (a -> Binary.Put) -> a -> Expectation
+binaryEncodeAndDecode getA putA expected =
+  let bytes = LazyByteString.toStrict $ Binary.runPut $ putA expected in
+  finish $ Binary.pushChunk (Binary.runGetIncremental getA) bytes
+
+  where
+    finish = \case
+      Binary.Fail _ _ msg            -> expectationFailure msg
+      Binary.Partial next            -> finish $ next Nothing
+      Binary.Done remaining _ output -> do
+        remaining `shouldBe` ByteString.empty
+        output `shouldBe` expected
+
+
+binaryGetPutSpec :: (Arbitrary a, Eq a, Show a) => String -> Binary.Get a -> (a -> Binary.Put) -> Spec
+binaryGetPutSpec name getA putA =
+  describe name $ do
+    it "decodes encoded protocols correctly" $
+      property $ binaryEncodeAndDecode getA putA
+
+    it "handles arbitrary input" $
+      property $ \bytes ->
+        let
+          finish = \case
+            Binary.Fail {}         -> return ()
+            Binary.Partial f       -> finish $ f Nothing
+            Binary.Done _ _ output -> binaryEncodeAndDecode getA putA output
+        in
+        finish $ Binary.pushChunk (Binary.runGetIncremental getA) $ ByteString.pack bytes
+
+    it "should have a non-nullable packet grammar" $
+      let
+        bytes = []
+        decoder = Binary.runGetIncremental getA
+      in
+      case Binary.pushChunk decoder $ ByteString.pack bytes of
+        Binary.Fail _ _ msg -> expectationFailure msg
+        Binary.Partial _    -> return ()
+        Binary.Done {}      -> expectationFailure "Done with empty input; packet grammar appears to be nullable"
+
+
+binarySpec :: (Arbitrary a, Eq a, Show a, Binary a) => Proxy a -> Spec
+binarySpec (Proxy :: Proxy a) =
+  binaryGetPutSpec "Binary.{get,put}" (Binary.get :: Binary.Get a) (Binary.put :: a -> Binary.Put)
+
+
+bitEncodingSpec :: (Arbitrary a, Eq a, Show a, BitEncoding a) => Proxy a -> Spec
+bitEncodingSpec (Proxy :: Proxy a) =
+  let
+    bitGetA = (bitGet :: Bits.BitGet a)
+    bitPutA = (bitPut :: a -> Bits.BitPut ())
+  in
+  binaryGetPutSpec "BitEncoding.bit{Get,Put}" (Bits.runBitGet bitGetA) (Bits.runBitPut . bitPutA)
+
+
+readShowSpec :: (Arbitrary a, Eq a, Show a, Read a) => Proxy a -> Spec
+readShowSpec (Proxy :: Proxy a) =
+  let
+    showA = show :: a -> String
+    readA = read :: String -> a
+  in
+  describe "Read/Show" $
+    it "encodes and decodes correctly" $
+      property $ \expected ->
+        let output = readA $ showA expected in
+        output `shouldBe` expected
+
+
+rpcSpec
+    :: (Arbitrary a, Eq a, Show a, Typeable a, Binary a, MessagePack a)
+    => Proxy a
+    -> Spec
+rpcSpec (Proxy :: Proxy a) =
+    describe "MessagePack" $
+        it "encodes and decodes correctly" $ property $ \x ->
+            decodeA (encodeA x) `shouldBe` Just x
+
+  where
+    encodeA = Binary.encode :: a -> ByteString.ByteString
+    decodeA = Binary.decode :: ByteString.ByteString -> Maybe a
diff --git a/test/Network/Tox/NodeInfo/HostAddressSpec.hs b/test/Network/Tox/NodeInfo/HostAddressSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/NodeInfo/HostAddressSpec.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.NodeInfo.HostAddressSpec where
+
+import           Test.Hspec
+
+import           Data.Proxy                       (Proxy (..))
+import           Network.Tox.EncodingSpec
+import           Network.Tox.NodeInfo.HostAddress (HostAddress)
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy HostAddress)
+  binarySpec (Proxy :: Proxy HostAddress)
+  readShowSpec (Proxy :: Proxy HostAddress)
diff --git a/test/Network/Tox/NodeInfo/NodeInfoSpec.hs b/test/Network/Tox/NodeInfo/NodeInfoSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/NodeInfo/NodeInfoSpec.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.NodeInfo.NodeInfoSpec where
+
+import           Test.Hspec
+
+import qualified Data.Binary                   as Binary (get)
+import qualified Data.Binary.Get               as Binary (Get)
+import           Data.Proxy                    (Proxy (..))
+import           Network.Tox.EncodingSpec
+import           Network.Tox.NodeInfo.NodeInfo (NodeInfo)
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy NodeInfo)
+  binarySpec (Proxy :: Proxy NodeInfo)
+  readShowSpec (Proxy :: Proxy NodeInfo)
+
+  it "should handle invalid packets as failures" $ do
+    expectDecoderFailure [0x20] "Invalid address family: 32"
+    expectDecoderFailure [0xa0] "Invalid address family: 32"
+    expectDecoderFailure [0x00] "Invalid address family: 0"
+    expectDecoderFailure [0x01] "Invalid address family: 1"
+
+  where
+    expectDecoderFailure =
+      expectDecoderFail (Binary.get :: Binary.Get NodeInfo)
diff --git a/test/Network/Tox/NodeInfo/PortNumberSpec.hs b/test/Network/Tox/NodeInfo/PortNumberSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/NodeInfo/PortNumberSpec.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.NodeInfo.PortNumberSpec where
+
+import           Test.Hspec
+
+import           Data.Proxy                      (Proxy (..))
+import           Network.Tox.EncodingSpec
+import           Network.Tox.NodeInfo.PortNumber (PortNumber)
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy PortNumber)
+  binarySpec (Proxy :: Proxy PortNumber)
+  readShowSpec (Proxy :: Proxy PortNumber)
diff --git a/test/Network/Tox/NodeInfo/SocketAddressSpec.hs b/test/Network/Tox/NodeInfo/SocketAddressSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/NodeInfo/SocketAddressSpec.hs
@@ -0,0 +1,20 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.NodeInfo.SocketAddressSpec where
+
+import           Test.Hspec
+
+import           Data.Proxy                         (Proxy (..))
+import           Network.Tox.EncodingSpec
+import           Network.Tox.NodeInfo.SocketAddress (SocketAddress)
+import qualified Network.Tox.NodeInfo.SocketAddress as SocketAddress
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy SocketAddress)
+  binarySpec (Proxy :: Proxy SocketAddress)
+  readShowSpec (Proxy :: Proxy SocketAddress)
+
+  binaryGetPutSpec "{get,put}SocketAddress"
+    SocketAddress.getSocketAddress
+    (uncurry SocketAddress.putSocketAddress)
diff --git a/test/Network/Tox/NodeInfo/TransportProtocolSpec.hs b/test/Network/Tox/NodeInfo/TransportProtocolSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/NodeInfo/TransportProtocolSpec.hs
@@ -0,0 +1,16 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.NodeInfo.TransportProtocolSpec where
+
+import           Test.Hspec
+
+import           Data.Proxy                             (Proxy (..))
+import           Network.Tox.EncodingSpec
+import           Network.Tox.NodeInfo.TransportProtocol (TransportProtocol)
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy TransportProtocol)
+  binarySpec (Proxy :: Proxy TransportProtocol)
+  readShowSpec (Proxy :: Proxy TransportProtocol)
+  bitEncodingSpec (Proxy :: Proxy TransportProtocol)
diff --git a/test/Network/Tox/NodeInfoSpec.hs b/test/Network/Tox/NodeInfoSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/NodeInfoSpec.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.NodeInfoSpec where
+
+import           Test.Hspec
+
+import qualified Network.Tox.NodeInfo as NodeInfo
+
+
+spec :: Spec
+spec = return ()
diff --git a/test/Network/Tox/Protocol/PacketKindSpec.hs b/test/Network/Tox/Protocol/PacketKindSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/Protocol/PacketKindSpec.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.Protocol.PacketKindSpec where
+
+import           Test.Hspec
+
+import qualified Data.Binary                     as Binary (get)
+import qualified Data.Binary.Get                 as Binary (Get)
+import           Data.Proxy                      (Proxy (..))
+import           Network.Tox.EncodingSpec
+import           Network.Tox.Protocol.PacketKind (PacketKind)
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy PacketKind)
+  binarySpec (Proxy :: Proxy PacketKind)
+  readShowSpec (Proxy :: Proxy PacketKind)
+
+  it "should handle invalid packet kinds as failures" $ do
+    expectDecoderFailure [0xfe] "packet kind 254"
+    expectDecoderFailure [0xff] "packet kind 255"
+
+  where
+    expectDecoderFailure =
+      expectDecoderFail (Binary.get :: Binary.Get PacketKind)
diff --git a/test/Network/Tox/Protocol/PacketSpec.hs b/test/Network/Tox/Protocol/PacketSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/Protocol/PacketSpec.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.Protocol.PacketSpec where
+
+import           Test.Hspec
+
+import           Data.Proxy                      (Proxy (..))
+import           Data.Word                       (Word64)
+
+import           Network.Tox.EncodingSpec
+import           Network.Tox.Protocol.Packet     (Packet (..))
+import qualified Network.Tox.Protocol.PacketKind as PacketKind
+
+
+spec :: Spec
+spec = do
+  rpcSpec (Proxy :: Proxy (Packet Word64))
+  binarySpec (Proxy :: Proxy (Packet Word64))
+  readShowSpec (Proxy :: Proxy (Packet Word64))
+
+  it "has a kind and a payload" $ do
+    let packet = Packet PacketKind.NodesRequest ["heyo"]
+    packetKind packet `shouldBe` PacketKind.NodesRequest
+    packetPayload packet `shouldBe` ["heyo"]
diff --git a/test/Network/Tox/ProtocolSpec.hs b/test/Network/Tox/ProtocolSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/ProtocolSpec.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.ProtocolSpec where
+
+import           Test.Hspec
+
+import qualified Network.Tox.Protocol as Protocol
+
+
+spec :: Spec
+spec = return ()
diff --git a/test/Network/Tox/SaveDataSpec.hs b/test/Network/Tox/SaveDataSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Tox/SaveDataSpec.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE Trustworthy #-}
+module Network.Tox.SaveDataSpec where
+
+import           Test.Hspec
+
+import qualified Data.Binary              as Binary (get)
+import qualified Data.Binary.Get          as Binary (Get)
+import           Data.Proxy               (Proxy (..))
+import           Network.Tox.EncodingSpec (binarySpec)
+import qualified Network.Tox.EncodingSpec as EncodingSpec (expectDecoderFail)
+import           Network.Tox.SaveData     (SaveData)
+
+
+spec :: Spec
+spec = do
+  binarySpec (Proxy :: Proxy SaveData)
+
+  it "should handle invalid magic numbers" $ do
+    expectDecoderFail [0x00, 0x00, 0x00, 0x01]
+      "savedata should start with 32 zero-bits"
+    expectDecoderFail [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+      "wrong magic number"
+
+  where
+    expectDecoderFail =
+      EncodingSpec.expectDecoderFail (Binary.get :: Binary.Get SaveData)
diff --git a/tools/groupbot/Main.hs b/tools/groupbot/Main.hs
deleted file mode 100644
--- a/tools/groupbot/Main.hs
+++ /dev/null
@@ -1,143 +0,0 @@
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE LambdaCase                 #-}
-module Main (main) where
-
-import           Control.Concurrent      (threadDelay)
-import           Control.Concurrent.MVar (MVar, newMVar)
-import           Control.Exception       (AsyncException (UserInterrupt), catch,
-                                          throwIO)
-import           Control.Monad           (forever)
-import qualified Data.ByteString.Base16  as Base16
-import qualified Data.ByteString.Char8   as BS
-import           Data.String             (fromString)
-import           Data.Word               (Word32)
-import           Foreign.Storable        (Storable (..))
-import           System.Directory        (doesFileExist)
-import           System.Exit             (exitSuccess)
-
-import qualified Network.Tox.C           as C
-
-
-bootstrapKey :: BS.ByteString
-bootstrapKey =
-  fst . Base16.decode . fromString $
-    "15E9C309CFCB79FDDF0EBA057DABB49FE15F3803B1BFF06536AE2E5BA5E4690E"
-
-isMasterKey :: BS.ByteString -> Bool
-isMasterKey key =
-  (key ==) . fst . Base16.decode . fromString $
-    "040F75B5C8995F9525F9A8692A6C355286BBD3CF248C984560733421274F0365"
-
-botName :: String
-botName = "groupbot"
-
-bootstrapHost :: String
-bootstrapHost = "tox.ngc.zone"
-
-savedataFilename :: String
-savedataFilename = "groupbot.tox"
-
-options :: BS.ByteString -> C.Options
-options savedata = C.Options
-  { C.ipv6Enabled  = True
-  , C.udpEnabled   = True
-  , C.proxyType    = C.ProxyTypeNone
-  , C.proxyHost    = ""
-  , C.proxyPort    = 0
-  , C.startPort    = 33445
-  , C.endPort      = 33545
-  , C.tcpPort      = 3128
-  , C.savedataType = if savedata == BS.empty then C.SavedataTypeNone else C.SavedataTypeToxSave
-  , C.savedataData = savedata
-  }
-
-
-getRight :: (Monad m, Show a) => Either a b -> m b
-getRight (Left  l) = fail $ show l
-getRight (Right r) = return r
-
-
-must :: Show a => IO (Either a b) -> IO b
-must = (getRight =<<)
-
-
-newtype UserData = UserData Word32
-  deriving (Eq, Storable, Read, Show)
-
-instance C.CHandler UserData where
-  cSelfConnectionStatus _ conn ud = do
-    putStrLn "SelfConnectionStatusCb"
-    print conn
-    return ud
-
-  cFriendRequest tox pk msg ud = do
-    putStrLn "FriendRequestCb"
-    Right fn <- C.toxFriendAddNorequest tox pk
-    putStrLn $ (BS.unpack . Base16.encode) pk
-    putStrLn msg
-    print fn
-    return ud
-
-  cFriendConnectionStatus tox fn status ud@(UserData gn) = do
-    putStrLn "FriendConnectionStatusCb"
-    print fn
-    print status
-    if status /= C.ConnectionNone
-    then do
-      putStrLn "Inviting!"
-      _ <- C.toxConferenceInvite tox fn gn
-      return ()
-    else
-      putStrLn "Friend offline"
-    return ud
-
-  cFriendMessage tox fn msgType msg ud = do
-    putStrLn "FriendMessage"
-    print fn
-    print msgType
-    putStrLn msg
-    _ <- C.toxFriendSendMessage tox fn msgType msg
-    return ud
-
-  cConferenceInvite tox fn _confType cookie ud = do
-    putStrLn "ConferenceInvite"
-    print fn
-    pk <- getRight =<< C.toxFriendGetPublicKey tox fn
-    if isMasterKey pk
-    then do
-      putStrLn "Joining!"
-      gn <- getRight =<< C.toxConferenceJoin tox fn cookie
-      return $ UserData gn
-    else do
-      putStrLn "Not master!"
-      return ud
-
-
-loop :: MVar ud -> C.Tox ud -> IO a
-loop ud tox =
-  forever $ do
-    C.toxIterate tox ud
-    interval <- C.tox_iteration_interval tox
-    threadDelay $ fromIntegral $ interval * 10000
-
-
-main :: IO ()
-main = do
-  exists <- doesFileExist savedataFilename
-  loadedSavedata <- if exists then BS.readFile savedataFilename else return BS.empty
-  must $ C.withOptions (options loadedSavedata) $ \optPtr ->
-    must $ C.withTox optPtr $ \tox -> do
-      must $ C.toxBootstrap tox bootstrapHost 33445 bootstrapKey
-
-      C.withCHandler tox $ do
-        adr <- C.toxSelfGetAddress tox
-        putStrLn $ (BS.unpack . Base16.encode) adr
-        _ <- C.toxSelfSetName tox botName
-        gn <- getRight =<< C.toxConferenceNew tox
-        ud <- newMVar (UserData gn)
-        catch (loop ud tox) $ \case
-          e@UserInterrupt -> throwIO e
-          _ -> do
-            savedSavedata <- C.toxGetSavedata tox
-            BS.writeFile savedataFilename savedSavedata
-            exitSuccess
diff --git a/tools/toxsave-convert.hs b/tools/toxsave-convert.hs
new file mode 100644
--- /dev/null
+++ b/tools/toxsave-convert.hs
@@ -0,0 +1,21 @@
+module Main (main) where
+
+import           Control.Applicative        ((<$>), (<|>))
+import qualified Data.Binary                as Binary
+import qualified Data.ByteString.Lazy       as LBS
+import qualified Data.ByteString.Lazy.Char8 as LBS8
+import           Data.Maybe                 (fromMaybe)
+import           Network.Tox.SaveData       (SaveData)
+import           Text.Groom                 (groom)
+import           Text.Read                  (readMaybe)
+
+
+parse :: LBS.ByteString -> LBS.ByteString
+parse str = fromMaybe LBS.empty $
+    (Binary.encode <$> (readMaybe $ LBS8.unpack str :: Maybe SaveData))
+    <|>
+    (Just . LBS8.pack . (++ "\n") . groom $ (Binary.decode str :: SaveData))
+
+
+main :: IO ()
+main = parse <$> LBS.getContents >>= LBS.putStr
diff --git a/toxcore.cabal b/toxcore.cabal
--- a/toxcore.cabal
+++ b/toxcore.cabal
@@ -1,61 +1,121 @@
 name:                 toxcore
-synopsis:             Haskell bindings to the C reference implementation of Tox
-version:              0.2.0
+synopsis:             A Tox protocol implementation in Haskell
+version:              0.2.11
 cabal-version:        >= 1.10
 license:              GPL-3
-license-file:         LICENSE.md
+license-file:         LICENSE
 build-type:           Simple
 author:               iphy
 maintainer:           iphy
-copyright:            © 2016-2018 iphy
-homepage:             https://toktok.github.io
+copyright:            © 2016-2020 The TokTok Team
+homepage:             https://toktok.ltd
 category:             Network
-description:
-  Haskell bindings to the C reference implementation of Tox.
-  .
-  See <https://github.com/TokTok/toxcore>.
+description:          A Tox protocol implementation in Haskell
 
 source-repository head
   type: git
   location: https://github.com/TokTok/hs-toxcore
 
+flag library-only
+  description: Build only library, no executables or tests.
+  default: False
+
 library
   default-language: Haskell2010
   hs-source-dirs:
       src
   ghc-options:
       -Wall
-      -- -fno-warn-unused-imports
-  extra-libraries: toxcore
+      -fno-warn-unused-imports
+  exposed-modules:
+      Network.Tox
+      Network.Tox.Application.GroupChats
+      Network.Tox.Binary
+      Network.Tox.Crypto
+      Network.Tox.Crypto.Box
+      Network.Tox.Crypto.CombinedKey
+      Network.Tox.Crypto.Key
+      Network.Tox.Crypto.Keyed
+      Network.Tox.Crypto.KeyedT
+      Network.Tox.Crypto.KeyPair
+      Network.Tox.Crypto.Nonce
+      Network.Tox.DHT
+      Network.Tox.DHT.ClientList
+      Network.Tox.DHT.ClientNode
+      Network.Tox.DHT.DhtPacket
+      Network.Tox.DHT.DhtRequestPacket
+      Network.Tox.DHT.DhtState
+      Network.Tox.DHT.Distance
+      Network.Tox.DHT.KBuckets
+      Network.Tox.DHT.NodeList
+      Network.Tox.DHT.NodesRequest
+      Network.Tox.DHT.NodesResponse
+      Network.Tox.DHT.Operation
+      Network.Tox.DHT.PendingReplies
+      Network.Tox.DHT.PingPacket
+      Network.Tox.DHT.RpcPacket
+      Network.Tox.DHT.Stamped
+      Network.Tox.Encoding
+      Network.Tox.Network.Networked
+      Network.Tox.Network.MonadRandomBytes
+      Network.Tox.NodeInfo
+      Network.Tox.NodeInfo.HostAddress
+      Network.Tox.NodeInfo.NodeInfo
+      Network.Tox.NodeInfo.PortNumber
+      Network.Tox.NodeInfo.SocketAddress
+      Network.Tox.NodeInfo.TransportProtocol
+      Network.Tox.Protocol
+      Network.Tox.Protocol.Packet
+      Network.Tox.Protocol.PacketKind
+      Network.Tox.SaveData
+      Network.Tox.SaveData.Conferences
+      Network.Tox.SaveData.DHT
+      Network.Tox.SaveData.Friend
+      Network.Tox.SaveData.Nodes
+      Network.Tox.SaveData.Util
+      Network.Tox.Testing
+      Network.Tox.Time
+      Network.Tox.Timed
+      Network.Tox.TimedT
   build-depends:
       base < 5
+    , QuickCheck                >= 2.9.1
+    , base16-bytestring
+    , binary
+    , binary-bits
     , bytestring
-    , data-default-class
-  exposed-modules:
-      Network.Tox.C
-      Network.Tox.C.CEnum
-      Network.Tox.C.Callbacks
-      Network.Tox.C.Constants
-      Network.Tox.C.Options
-      Network.Tox.C.Tox
-      Network.Tox.C.Type
-      Network.Tox.C.Version
+    , clock                     >= 0.3
+    , containers
+    , entropy
+    , integer-gmp
+    , iproute
+    , lens-family
+    , MonadRandom
+    , msgpack-binary            >= 0.0.12
+    , msgpack-rpc-conduit       >= 0.0.5
+    , mtl
+    , network                   < 3
+    , saltine
+    , random
+    , transformers
 
-executable groupbot
+executable toxsave-convert
   default-language: Haskell2010
   hs-source-dirs:
-      tools/groupbot
+      tools
   ghc-options:
       -Wall
       -fno-warn-unused-imports
-  extra-libraries: toxcore
+  main-is: toxsave-convert.hs
+  if flag(library-only)
+    buildable: False
   build-depends:
       base < 5
-    , base16-bytestring
+    , binary
     , bytestring
-    , directory
+    , groom
+    , text
     , toxcore
-  main-is: Main.hs
 
 test-suite testsuite
   default-language: Haskell2010
@@ -65,17 +125,52 @@
   ghc-options:
       -Wall
       -fno-warn-unused-imports
+  main-is: testsuite.hs
+  other-modules:
+      Data.Result
+      Network.Tox.Crypto.BoxSpec
+      Network.Tox.Crypto.CombinedKeySpec
+      Network.Tox.Crypto.KeyPairSpec
+      Network.Tox.Crypto.KeySpec
+      Network.Tox.Crypto.NonceSpec
+      Network.Tox.CryptoSpec
+      Network.Tox.DHT.ClientListSpec
+      Network.Tox.DHT.DhtPacketSpec
+      Network.Tox.DHT.DhtRequestPacketSpec
+      Network.Tox.DHT.DhtStateSpec
+      Network.Tox.DHT.DistanceSpec
+      Network.Tox.DHT.KBucketsSpec
+      Network.Tox.DHT.NodesRequestSpec
+      Network.Tox.DHT.NodesResponseSpec
+      Network.Tox.DHT.OperationSpec
+      Network.Tox.DHT.PendingRepliesSpec
+      Network.Tox.DHT.PingPacketSpec
+      Network.Tox.DHT.RpcPacketSpec
+      Network.Tox.DHTSpec
+      Network.Tox.EncodingSpec
+      Network.Tox.NodeInfo.HostAddressSpec
+      Network.Tox.NodeInfo.NodeInfoSpec
+      Network.Tox.NodeInfo.PortNumberSpec
+      Network.Tox.NodeInfo.SocketAddressSpec
+      Network.Tox.NodeInfoSpec
+      Network.Tox.NodeInfo.TransportProtocolSpec
+      Network.Tox.Protocol.PacketKindSpec
+      Network.Tox.Protocol.PacketSpec
+      Network.Tox.ProtocolSpec
+      Network.Tox.SaveDataSpec
   build-depends:
       base < 5
-    , QuickCheck >= 2.9.1
-    , base16-bytestring
+    , QuickCheck
+    , async
+    , binary
+    , binary-bits
     , bytestring
-    , bytestring-arbitrary
-    , data-default-class
+    , containers
     , hspec
+    , msgpack-binary
+    , msgpack-rpc-conduit
+    , msgpack-types
+    , mtl
     , saltine
+    , text
     , toxcore
-  main-is: testsuite.hs
-  other-modules:
-      Network.Tox.C.ToxSpec
-      Network.Tox.CSpec
