diff --git a/i2p2www/__init__.py b/i2p2www/__init__.py
index ce91718f08d5be8897b80d7777e5d98b1748fadb..af3d944015f04c8f5d9aa95f5d59b60eec42243c 100644
--- a/i2p2www/__init__.py
+++ b/i2p2www/__init__.py
@@ -105,6 +105,7 @@ GETTEXT_DOMAIN_MAPPING = {
 
 TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), 'pages')
 STATIC_DIR = os.path.join(os.path.dirname(__file__), 'static')
+SPEC_DIR = os.path.join(os.path.dirname(__file__), 'spec')
 BLOG_DIR = os.path.join(os.path.dirname(__file__), 'blog')
 MEETINGS_DIR = os.path.join(os.path.dirname(__file__), 'meetings/logs')
 SITE_DIR = os.path.join(TEMPLATE_DIR, 'site')
@@ -178,9 +179,12 @@ def pull_lang(endpoint, values):
 def set_lang(endpoint, values):
     if not values:
         return
-    if endpoint == 'static':
+    if endpoint == 'static' or \
+            endpoint.startswith('spec_'):
         # Static urls shouldn't have a lang flag
         # (causes complete reload on lang change)
+        # Spec urls shouldn't have a lang flag
+        # (adds a spurious ?lang=xx to the url)
         return
     if 'lang' in values:
         return
diff --git a/i2p2www/blog/helpers.py b/i2p2www/blog/helpers.py
index a2fdff74ec3fb9e6a48ddd913abdba84b07bcc0e..42ee5859dcc39af217e80abba0163002f3545405 100644
--- a/i2p2www/blog/helpers.py
+++ b/i2p2www/blog/helpers.py
@@ -6,16 +6,17 @@ import os
 import os.path
 
 from i2p2www import BLOG_DIR
+from i2p2www import helpers
 
 
-SUPPORTED_METATAGS = {
+BLOG_METATAGS = {
     'author': u'I2P devs',
     'category': None,
     'date': None,
     'excerpt': u'',
     }
 
-LIST_METATAGS = [
+BLOG_LIST_METATAGS = [
     'category',
     ]
 
@@ -108,11 +109,4 @@ def render_blog_post(slug):
     return publish_parts(source=rendered_content, source_path=BLOG_DIR, writer_name="html")
 
 def get_metadata_from_meta(meta):
-    metaLines = meta.split('\n')
-    ret = {}
-    for metaTag in SUPPORTED_METATAGS:
-        metaLine = [s for s in metaLines if 'name="%s"' % metaTag in s]
-        ret[metaTag] = metaLine[0].split('content="')[1].split('"')[0] if len(metaLine) > 0 else SUPPORTED_METATAGS[metaTag]
-        if metaTag in LIST_METATAGS and ret[metaTag]:
-            ret[metaTag] = [s.strip() for s in ret[metaTag].split(',')]
-    return ret
+    return helpers.get_metadata_from_meta(meta, BLOG_METATAGS, BLOG_LIST_METATAGS)
diff --git a/i2p2www/extensions.py b/i2p2www/extensions.py
index 51d8f727efb133ee1fe18fe6557ea6aca116bbb9..27d6f677903e5dca90d5785c8ca77ee5d046a0bb 100644
--- a/i2p2www/extensions.py
+++ b/i2p2www/extensions.py
@@ -17,7 +17,7 @@ except ImportError:
 
 from flask import g
 
-from i2p2www.formatters import I2PHtmlFormatter
+from i2p2www.formatters import I2PHtmlFormatter, TextSpecFormatter
 from i2p2www.lexers import DataSpecLexer
 
 
@@ -55,13 +55,14 @@ class HighlightExtension(Extension):
         # extract the language if available
         # Any additional parameters are passed to HtmlFormatter
         lang = None
+        formatter = None
         parameters = []
         while parser.stream.current.type != 'block_end':
             if lang or parameters:
                 parser.stream.expect('comma')
 
             name = parser.stream.expect('name')
-            if name.value in parameters or (name.value == 'lang' and lang):
+            if name.value in parameters or (name.value == 'lang' and lang) or (name.value == 'formatter' and formatter):
                 parser.fail('parameter %r defined twice.' %
                             name.value, name.lineno,
                             exc=TemplateAssertionError)
@@ -70,20 +71,24 @@ class HighlightExtension(Extension):
                 next(parser.stream)
                 if name.value == 'lang':
                     lang = parser.parse_expression()
+                elif name.value == 'formatter':
+                    formatter = parser.parse_expression()
                 else:
                     parameters.append(nodes.Pair(nodes.Const(name.value), parser.parse_expression()))
 
         if lang == None:
             lang = nodes.Const(None)
+        if formatter == None:
+            formatter = nodes.Const('html')
         parameters = nodes.Dict(parameters)
 
         # body of the block
         body = parser.parse_statements(['name:endhighlight'], drop_needle=True)
 
-        return nodes.CallBlock(self.call_method('_highlight', [lang, parameters]),
+        return nodes.CallBlock(self.call_method('_highlight', [lang, formatter, parameters]),
                                [], [], body).set_lineno(lineno)
 
-    def _highlight(self, lang, parameters, caller=None):
+    def _highlight(self, lang, formatter, parameters, caller=None):
         # highlight code using Pygments
         body = caller()
         try:
@@ -100,14 +105,17 @@ class HighlightExtension(Extension):
 
         if ctags:
             if 'tagsfile' not in parameters:
-                parameters['tagsfile'] = module_path() + '/pages/site/spectags'
+                parameters['tagsfile'] = module_path() + '/spec/spectags'
 
             if 'tagurlformat' not in parameters:
                 lang = 'en'
                 if hasattr(g, 'lang') and g.lang:
                     lang = g.lang
-                parameters['tagurlformat'] = '/' + lang + '/%(path)s%(fname)s'
+                parameters['tagurlformat'] = '/spec/%(path)s%(fname)s'
 
-        formatter = I2PHtmlFormatter(**parameters)
+        if formatter == 'textspec':
+            formatter = TextSpecFormatter(**parameters)
+        else:
+            formatter = I2PHtmlFormatter(**parameters)
         code = highlight(Markup(body).unescape(), lexer, formatter)
         return code
diff --git a/i2p2www/formatters.py b/i2p2www/formatters.py
index 2f03f5b57d401cf329a62868c7aa310d1fd248ba..8cd36825da48aacd057d0031985e4ccf89b2535d 100644
--- a/i2p2www/formatters.py
+++ b/i2p2www/formatters.py
@@ -23,7 +23,7 @@ try:
 except ImportError:
     ctags = None
 
-__all__ = ['I2PHtmlFormatter']
+__all__ = ['I2PHtmlFormatter', 'TextSpecFormatter']
 
 
 _escape_html_table = {
@@ -727,8 +727,8 @@ class I2PHtmlFormatter(Formatter):
                     filename, extension = os.path.splitext(filename)
                     url = self.tagurlformat % {'path': base, 'fname': filename,
                                                'fext': extension}
-                    parts[0] = "<a href=\"%s#%s_%s\">%s" % \
-                        (url, kinds[kind], value, parts[0])
+                    parts[0] = "<a href=\"%s#%s-%s\">%s" % \
+                        (url, kinds[kind], value.lower(), parts[0])
                     parts[-1] = parts[-1] + "</a>"
 
             # for all but the last line
@@ -829,3 +829,70 @@ class I2PHtmlFormatter(Formatter):
 
         for t, piece in source:
             outfile.write(piece)
+
+
+class TextSpecFormatter(Formatter):
+    """
+    Output the text unchanged without any formatting.
+    """
+    name = 'Text spec'
+    aliases = ['textspec']
+    filenames = ['*.txt']
+
+    def __init__(self, **options):
+        Formatter.__init__(self, **options)
+        self.tagsfile = self._decodeifneeded(options.get('tagsfile', ''))
+        self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', ''))
+
+        if self.tagsfile:
+            if not ctags:
+                raise RuntimeError('The "ctags" package must to be installed '
+                                   'to be able to use the "tagsfile" feature.')
+            self._ctags = ctags.CTags(self.tagsfile)
+
+    def _decodeifneeded(self, value):
+        if isinstance(value, bytes):
+            if self.encoding:
+                return value.decode(self.encoding)
+            return value.decode()
+        return value
+
+    def format(self, tokensource, outfile):
+        enc = self.encoding
+        tagsfile = self.tagsfile
+        refs = {}
+
+        for ttype, value in tokensource:
+            if tagsfile and ttype in Token.Name.Class:
+                filename, kind = self._lookup_ctag(value)
+                # Handle message types
+                if not kind and value.endswith('Message'):
+                    value = value[:-7]
+                    filename, kind = self._lookup_ctag(value)
+                if kind:
+                    base, filename = os.path.split(filename)
+                    if base:
+                        base += '/'
+                    filename, extension = os.path.splitext(filename)
+                    url = self.tagurlformat % {'path': base, 'fname': filename,
+                                               'fext': extension}
+                    refs[value] = '\n[%s]: %s#%s-%s' % (value, url, kinds[kind], value.lower())
+                    value = '[%s]' % value
+
+            if enc:
+                outfile.write(value.encode(enc))
+            else:
+                outfile.write(value)
+
+        for ref in refs.values():
+            if enc:
+                outfile.write(ref.encode(enc))
+            else:
+                outfile.write(ref)
+
+    def _lookup_ctag(self, token):
+        entry = ctags.TagEntry()
+        if self._ctags.find(entry, token, 0):
+            return entry['file'], entry['kind']
+        else:
+            return None, None
diff --git a/i2p2www/helpers.py b/i2p2www/helpers.py
index 04bd49ee6f77021bdadd21078aa0a964e6683fad..41e6c6a85c30b6df06b84cff781c075c2a76eb94 100644
--- a/i2p2www/helpers.py
+++ b/i2p2www/helpers.py
@@ -9,6 +9,16 @@ def get_for_page(items, page, per_page):
     to_item = page*per_page
     return items[from_item:to_item]
 
+def get_metadata_from_meta(meta, supported_metatags, list_metatags):
+    metaLines = meta.split('\n')
+    ret = {}
+    for metaTag in supported_metatags:
+        metaLine = [s for s in metaLines if 'name="%s"' % metaTag in s]
+        ret[metaTag] = metaLine[0].split('content="')[1].split('"')[0] if len(metaLine) > 0 else supported_metatags[metaTag]
+        if metaTag in list_metatags and ret[metaTag]:
+            ret[metaTag] = [s.strip() for s in ret[metaTag].split(',')]
+    return ret
+
 
 ########################
 # General helper classes
diff --git a/i2p2www/pages/global/macros b/i2p2www/pages/global/macros
index 4c470c8850442f8b4e21d61c106939a5f11ae714..b50e5541054610b63608608f69612ad7b74f7caa 100644
--- a/i2p2www/pages/global/macros
+++ b/i2p2www/pages/global/macros
@@ -1,5 +1,6 @@
 {%- macro change_lang(lang) -%}
 {%- if request.endpoint == 'site_show' -%}{{ url_for('site_show', lang=lang, page=page) }}
+{%- elif request.endpoint == 'spec_show' -%}{{ url_for('spec_show', name=name) }}
 {%- elif request.endpoint == 'blog_index' -%}
   {%- if category -%}{{ url_for('blog_index', lang=lang, category=category) }}
   {%- else -%}{{ url_for('blog_index', lang=lang) }}
diff --git a/i2p2www/pages/global/nav.html b/i2p2www/pages/global/nav.html
index f3a3893448ed3282d3185e2705a9bed5df596f8b..14d3f0cee98e24cbeed1572794d5872d4be11f15 100644
--- a/i2p2www/pages/global/nav.html
+++ b/i2p2www/pages/global/nav.html
@@ -29,23 +29,7 @@
           <li><a href="{{ site_url('docs/how/elgamal-aes') }}"><div class="menuitem"><span>{{ _('ElGamal/AES+SessionTags') }}</span></div></a></li>
         </ul>
       </li>
-      <li class="has-sub"><div class="menuitem"><span>{{ _('Specifications') }}</span></div>
-        <ul>
-          <li><a href="{{ site_url('docs/spec/blockfile') }}"><div class="menuitem"><span>{{ _('Blockfile') }}</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/common-structures') }}"><div class="menuitem"><span>{{ _('Common structures') }}</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/configuration') }}"><div class="menuitem"><span>{{ _('Configuration files') }}</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/datagrams') }}"><div class="menuitem"><span>{{ _('Datagrams') }}</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/geoip') }}"><div class="menuitem"><span>{{ _('GeoIP files') }}</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/i2cp') }}"><div class="menuitem"><span>I2CP</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/i2np') }}"><div class="menuitem"><span>I2NP</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/plugin') }}"><div class="menuitem"><span>{{ _('Plugins') }}</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/ssu') }}"><div class="menuitem"><span>SSU</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/streaming') }}"><div class="menuitem"><span>{{ _('Streaming library') }}</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/tunnel-creation') }}"><div class="menuitem"><span>{{ _('Tunnel creation') }}</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/tunnel-message') }}"><div class="menuitem"><span>{{ _('Tunnel messages') }}</span></div></a></li>
-          <li><a href="{{ site_url('docs/spec/updates') }}"><div class="menuitem"><span>{{ _('Software updates') }}</span></div></a></li>
-        </ul>
-      </li>
+      <li><a href="{{ url_for('spec_index') }}"><div class="menuitem"><span>{{ _('Specifications') }}</span></div></a></li>
       <li class="has-sub"><div class="menuitem"><span>{{ _('API') }}</span></div>
         <ul>
           <li><a href="{{ site_url('docs/api/i2ptunnel') }}"><div class="menuitem"><span>I2PTunnel</span></div></a></li>
diff --git a/i2p2www/pages/site/docs/how/cryptography.html b/i2p2www/pages/site/docs/how/cryptography.html
deleted file mode 100644
index e009dc314f506ca9ca5d9761e66409f4fb160773..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/how/cryptography.html
+++ /dev/null
@@ -1,523 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}{% trans %}Low-level Cryptography Details{% endtrans %}{% endblock %}
-{% block lastupdated %}{% trans %}December 2014{% endtrans %}{% endblock %}
-{% block accuratefor %}0.9.17{% endblock %}
-{% block content %}
-<p>{% trans -%}
-This page specifies the low-level details of the cryptography in I2P.
-{%- endtrans %}</p>
-
-<p>{% trans elgamalaes=site_url('docs/how/elgamal-aes') -%}
-There are a handful of cryptographic algorithms in use within I2P, but we have
-reduced them to a bare minimum to deal with our needs - one symmetric algorithm
-one asymmetric algorithm, one signing algorithm, and one hashing algorithm.  However, 
-we do combine them in some particular ways to provide message integrity (rather than
-relying on a MAC).  In addition, as much as we hate doing anything new in regards to 
-cryptography, we can't seem to find a reference discussing (or even naming) the 
-technique used in <a href="{{ elgamalaes }}">ElGamal/AES+SessionTag</a> (but we're sure others have done it).
-{%- endtrans %}</p>
-
-<h2><a name="elgamal">{% trans %}ElGamal encryption{% endtrans %}</a></h2>
-
-<p>{% trans %}
-ElGamal is used for asymmetric encryption.
-ElGamal is used in several places in I2P:
-{%- endtrans %}</p>
-<ul>
-<li>{% trans tunnelcreation=site_url('docs/spec/tunnel-creation') -%}
-To encrypt router-to-router <a href="{{ tunnelcreation }}">Tunnel Build Messages</a>
-{%- endtrans %}</li>
-<li>{% trans elgamalaes=site_url('docs/how/elgamal-aes'), commonstructures=site_url('docs/spec/common-structures') -%}
-For end-to-end (destination-to-destination) encryption as a part of <a href="{{ elgamalaes }}">ElGamal/AES+SessionTag</a>
-using the encryption key in the <a href="{{ commonstructures }}#struct_LeaseSet">LeaseSet</a>
-{%- endtrans %}</li>
-<li>{% trans netdb=site_url('docs/how/network-database'), elgamalaes=site_url('docs/how/elgamal-aes') -%}
-For encryption of some <a href="{{ netdb }}#delivery">netDb stores and queries sent to floodfill routers</a>
-as a part of <a href="{{ elgamalaes }}">ElGamal/AES+SessionTag</a>
-(destination-to-router or router-to-router).
-{%- endtrans %}</li>
-</ul>
-
-<p>{% trans -%}
-We use common primes for 2048 ElGamal encryption and decryption, as given by <a href="http://tools.ietf.org/html/rfc3526">IETF RFC-3526</a>.
-We currently only use ElGamal to encrypt the IV and session key in a single block, followed by the 
-AES encrypted payload using that key and IV.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-The unencrypted ElGamal contains: 
-{%- endtrans %}</p>
-{% highlight lang='dataspec' %}
-   +----+----+----+----+----+----+----+----+
-   |nonz|           H(data)                |
-   +----+                                  +
-   |                                       |
-   +                                       +
-   |                                       |
-   +                                       +
-   |                                       |
-   +    +----+----+----+----+----+----+----+
-   |    |  data...
-   +----+----+----+-//
-{% endhighlight %}
-
-<p>{% trans -%}
-The H(data) is the SHA256 of the data that is encrypted in the ElGamal block,
-and is preceded by a nonzero byte. 
-This byte could be random, but as implemented it is always 0xFF.
-It could possibly be used for flags in the future.
-The data encrypted in the block may be up to 222 bytes long.
-As the encrypted data may contain a substantial number of zeros if the
-cleartext is smaller than 222 bytes, it is recommended that higher layers pad
-the cleartext to 222 bytes with random data.
-Total length: typically 255 bytes.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-The encrypted ElGamal contains: 
-{%- endtrans %}</p>
-</p>
-{% highlight lang='dataspec' %}
-   +----+----+----+----+----+----+----+----+
-   |  zero padding...       |              |
-   +----+----+----+-//-+----+              +
-   |                                       |
-   +                                       +
-   |       ElG encrypted part 1            |
-   ~                                       ~
-   |                                       |
-   +    +----+----+----+----+----+----+----+
-   |    |   zero padding...      |         |
-   +----+----+----+----+-//-+----+         +
-   |                                       |
-   +                                       +
-   |       ElG encrypted part 2            |
-   ~                                       ~
-   |                                       |
-   +         +----+----+----+----+----+----+
-   |         +
-   +----+----+
-{% endhighlight %}
-
-<p>{% trans -%}
-Each encrypted part is prepended with zeros to a size of exactly 257 bytes.
-Total length: 514 bytes.
-In typical usage, higher layers pad the cleartext data to 222 bytes,
-resulting in an unencrypted block of 255 bytes.
-This is encoded as two 256-byte encrypted parts,
-and there is a single byte of zero padding before each part at this layer.
-{%- endtrans %}</p>
-
-<p>{% trans url='https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/ElGamalEngine.java' -%}
-See <a href="{{ url }}">the ElGamal code</a>.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-The shared prime is the 
-<a href="http://tools.ietf.org/html/rfc3526#section-3">[Oakley prime for 2048 bit keys]</a>
-{%- endtrans %}</p>
-<pre>
- 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
-</pre>
-<p>{% trans -%}
-or as a hexadecimal value:
-{%- endtrans %}</p>
-<pre>
- FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
- 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
- EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
- E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
- EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
- C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
- 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
- 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
- E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
- DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
- 15728E5A 8AACAA68 FFFFFFFF FFFFFFFF
-</pre>
-<p>{% trans -%}
-Using 2 as the generator.
-{%- endtrans %}</p>
-
-<h3><a name="exponent">{% trans %}Short Exponent{% endtrans %}</a></h3>
-<p>{% trans commonstructures=site_url('docs/spec/common-structures') -%}
-While the standard exponent size is 2048 bits (256 bytes) and the I2P
-<a href="{{ commonstructures }}#type_PrivateKey">PrivateKey</a>
-is a full 256 bytes, in some cases
-we use the short exponent size of 226 bits (28.25 bytes).
-{%- endtrans %}</p>
-
-<p>{% trans pdf='http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.5952&amp;rep=rep1&amp;type=pdf',
-benchmarks=site_url('misc/benchmarks'),
-oldbenchmarks='http://www.eskimo.com/~weidai/benchmarks.html' -%}
-This should be safe for use with the Oakley primes, per
-<a href="{{ pdf }}">On Diffie-Hellman Key Agreement with Short Exponents - van Oorschot, Weiner</a>
-at EuroCrypt 96, and <a href="{{ benchmarks }}">crypto++'s benchmarks</a>.
-Benchmarks originally at <a rel="nofollow" href="{{ oldbenchmarks }}">this link, now dead</a>,
-rescued from <a href="http://www.archive.org/">the wayback machine</a>, dated Apr 23, 2008.
-{%- endtrans %}</p>
-
-<p>{% trans book='http://www.springerlink.com/content/2jry7cftp5bpdghm/',
-fulltext='http://books.google.com/books?id=cXyiNZ2_Pa0C&amp;lpg=PA173&amp;ots=PNIz3dWe4g&amp;pg=PA173#v=onepage&amp;q&amp;f=false',
-thread='http://groups.google.com/group/sci.crypt/browse_thread/thread/1855a5efa7416677/339fa2f945cc9ba0#339fa2f945cc9ba0' -%}
-Also, <a href="{{ book }}">Koshiba &amp; Kurosawa: Short Exponent Diffie-Hellman Problems</a> (PKC 2004, LNCS 2947, pp. 173-186)
-<a href="{{ fulltext }}">(full text on Google Books)</a> apparently supports this, according to
-<a href="{{ thread }}">this sci.crypt thread</a>.
-The remainder of the PrivateKey is padded with zeroes.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-Prior to release 0.9.8, all routers used the short exponent.
-As of release 0.9.8, 64-bit x86 routers use a full 2048-bit exponent.
-Other routers continue to use the short exponent due to concerns about processor load.
-The transition to a longer exponent for these platforms is a topic for further study.
-{%- endtrans %}</p>
-
-<h4>{% trans %}Obsolescence{% endtrans %}</h4>
-<p>{% trans -%}
-The vulnerability of the network to an ElGamal attack and the impact of transitioning to a longer bit length is to be studied.
-It may be quite difficult to make any change backward-compatible.
-{%- endtrans %}</p>
-
-
-<h2><a name="AES">AES</a></h2>
-
-<p>{% trans -%}
-AES is used for symmetric encryption, in several cases:
-{%- endtrans %}</p>
-
-<ul>
-<li>{% trans -%}
-For <a href="#transports">transport encryption</a> after DH key exchange
-{%- endtrans %}</li>
-
-<li>{% trans elgamalaes=site_url('docs/how/elgamal-aes') -%}
-For end-to-end (destination-to-destination) encryption as a part of <a href="{{ elgamalaes }}">ElGamal/AES+SessionTag</a>
-{%- endtrans %}</li>
-
-<li>{% trans netdb=site_url('docs/how/network-database'), elgamalaes=site_url('docs/how/elgamal-aes') -%}
-For encryption of some <a href="{{ netdb }}#delivery">netDb stores and queries sent to floodfill routers</a>
-as a part of <a href="{{ elgamalaes }}">ElGamal/AES+SessionTag</a>
-(destination-to-router or router-to-router).
-{%- endtrans %}</li>
-
-<li>{% trans tunnelrouting=site_url('docs/how/tunnel-routing') -%}
-For encryption of <a href="{{ tunnelrouting }}#testing">periodic tunnel test messages</a> sent from the router to itself, through its own tunnels.
-{%- endtrans %}</li>
-</ul>
-
-<p>{% trans rfc2313='http://tools.ietf.org/html/rfc2313',
-code1='https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/CryptixAESEngine.java',
-code2='https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/CryptixRijndael_Algorithm.java',
-code3='https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/ElGamalAESEngine.java' -%}
-We use AES with 256 bit keys and 128 bit blocks in CBC mode.
-The padding used is specified in <a href="{{ rfc2313 }}">IETF RFC-2313 (PKCS#5 1.5, section 8.1 (for block type 02))</a>.
-In this case, padding exists of pseudorandomly generated octets to match 16 byte blocks.
-Specifically, see <a href="{{ code1 }}">[the CBC code]</a> and the Cryptix AES
-<a href="{{ code2 }}">[implementation]</a>, as well as the padding, found in the
-<a href="{{ code3 }}">ElGamalAESEngine.getPadding</a> function.
-{%- endtrans %}</p>
-
-<!-- *********************************************************************************
-     Believe it or not, we don't do this any more. If we ever did. safeEncode() and safeDecode() are unused.
-
-<p>
-In all cases, we know the size of the data to be sent, and we AES encrypt the following:
-<p>
-<pre>
-   +----+----+----+----+----+----+----+----+
-   |                H(data)                |
-   +                                       +
-   |                                       |
-   +                                       +
-   |                                       |
-   +                                       +
-   |                                       |
-   +----+----+----+----+----+----+----+----+
-   |        size       |    data ...       |
-   +----+----+----+----+                   +
-   |                                       |
-   ~                                       ~
-   |                                       |
-   +                                       +
-   |                                       |
-   +                        +----//---+----+
-   |                        |              |
-   +----+----+----//---+----+              +
-   |          Padding to 16 bytes          |
-   +----+----+----+----+----+----+----+----+
-
-H(data): 32-byte SHA-256 Hash of the data
-
-size: 4-byte Integer, number of data bytes to follow
-
-data: payload
-
-padding: random data, to a multiple of 16 bytes
-
-</pre>
-<p>
-After the data comes an application-specified number of randomly generated padding bytes.
-This application-specified number is rounded up to a multiple of 16.
-The entire segment (from H(data) through the end of the random bytes) is AES encrypted 
-(256 bit CBC w/ PKCS#5). 
-
-<p>
-This code is implemented in the safeEncrypt and safeDecrypt methods of 
-AESEngine but it is unused.
-</p>
-
-***************************************************************   -->
-
-
-<h4>{% trans %}Obsolescence{% endtrans %}</h4>
-<p>{% trans -%}
-The vulnerability of the network to an AES attack and the impact of transitioning to a longer bit length is to be studied.
-It may be quite difficult to make any change backward-compatible.
-{%- endtrans %}</p>
-
-<h4>{% trans %}References{% endtrans %}</h4>
-<ul>
-<li>
-<a href="{{ get_url('blog_post', slug='2006/02/07/status') }}">{% trans %}Feb. 7, 2006 Status Notes{% endtrans %}</a>
-</li>
-</ul>
-
-
-<h2><a name="sig">{% trans %}Digital Signatures{% endtrans %}</a></h2>
-
-<p>{% trans -%}
-DSA is the default signature algorithm, but we are in the process of migrating to more secure algorithms. See below.
-{%- endtrans %}</p>
-
-<h3><a name="DSA">DSA</a></h3>
-
-<p>{% trans code='https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/DSAEngine.java' -%}
-Signatures are generated and verified with 1024 bit DSA (L=1024, N=160), as implemented in
-<a href="{{ code }}">[DSAEngine]</a>.
-DSA was chosen because it is much faster for signatures than ElGamal.
-{%- endtrans %}</p>
-
-<h4>SEED</h4>
-
-<p>160 bit</p>
-
-<pre>
- 86108236b8526e296e923a4015b4282845b572cc
-</pre>
-
-<h4>Counter</h4>
-
-<pre>
- 33
-</pre>
-<p>
-<H4>DSA prime (p)</H4>
-
-<p>1024 bit</p>
-
-<p>
-<pre>
- 9C05B2AA 960D9B97 B8931963 C9CC9E8C 3026E9B8 ED92FAD0
- A69CC886 D5BF8015 FCADAE31 A0AD18FA B3F01B00 A358DE23
- 7655C496 4AFAA2B3 37E96AD3 16B9FB1C C564B5AE C5B69A9F
- F6C3E454 8707FEF8 503D91DD 8602E867 E6D35D22 35C1869C
- E2479C3B 9D5401DE 04E0727F B33D6511 285D4CF2 9538D9E3
- B6051F5B 22CC1C93
-</pre>
-<p>
-<H4>DSA quotient (q)</H4>
-
-<p>
-<pre>
- A5DFC28F EF4CA1E2 86744CD8 EED9D29D 684046B7
-</pre>
-<p>
-<H4>DSA generator (g)</H4>
-
-<p>1024 bit</p>
-
-<p>
-<pre>
- 0C1F4D27 D40093B4 29E962D7 223824E0 BBC47E7C 832A3923
- 6FC683AF 84889581 075FF908 2ED32353 D4374D73 01CDA1D2
- 3C431F46 98599DDA 02451824 FF369752 593647CC 3DDC197D
- E985E43D 136CDCFC 6BD5409C D2F45082 1142A5E6 F8EB1C3A
- B5D0484B 8129FCF1 7BCE4F7F 33321C3C B3DBB14A 905E7B2B
- 3E93BE47 08CBCC82
-</pre>
-
-<p>{% trans commonstructures=site_url('docs/spec/common-structures') -%}
-The <a href="{{ commonstructures }}#type_SigningPublicKey">Signing Public Key</a> is 1024 bits.
-The <a href="{{ commonstructures }}#type_SigningPrivateKey">Signing Private Key</a> is 160 bits.
-{%- endtrans %}</p>
-
-
-<h4>{% trans %}Obsolescence{% endtrans %}</h4>
-<p>{% trans pdf='http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57-Part1-revised2_Mar08-2007.pdf' -%}
-<a href="{{ pdf }}">NIST 800-57</a>
-recommends a minimum of (L=2048, N=224) for usage beyond 2010.
-This may be mitigated somewhat by the "cryptoperiod", or lifespan of a given key.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-The prime number was chosen <a href="#choosing_constants">in 2003</a>,
-and the person that chose the number (TheCrypto) is currently no longer an I2P developer.
-As such, we do not know if the prime chosen is a 'strong prime'.
-If a larger prime is chosen for future purposes, this should be a strong prime, and we will document the construction process.
-{%- endtrans %}</p>
-
-
-<h4>{% trans %}References{% endtrans %}</h4>
-<ul>
-<li>
-<a href="{{ get_url('meetings_show', id=51) }}">{% trans num=51 %}Meeting {{ num }}{% endtrans %}</a>
-<li>
-<a href="{{ get_url('meetings_show', id=52) }}">{% trans num=52 %}Meeting {{ num }}{% endtrans %}</a>
-<li>
-<a name="choosing_constants" href="http://article.gmane.org/gmane.comp.security.invisiblenet.iip.devel/343">{% trans %}Choosing the constants{% endtrans %}</a>
-<li>
-<a href="http://en.wikipedia.org/wiki/Digital_Signature_Algorithm">DSA</a>
-</ul>
-
-
-
-<h2>{% trans %}New Signature Algorithms{% endtrans %}</h2>
-<p>{% trans -%}
-As of release 0.9.12, the router supports additional signature algorithms that are more secure than 1024-bit DSA.
-The first usage is for Destinations; support for Router Identities was added in release 0.9.16.
-Support for migrating existing Destinations from old to new signatures will be added in a future release.
-Signature type is encoded in the Destination and Router Identity, so that new signature algorithms
-or curves may be added at any time.
-The current supported signature types are as follows:
-{%- endtrans %}</p>
-<ul>
-<li>DSA-SHA1</li>
-<li>ECDSA-SHA256-P256</li>
-<li>ECDSA-SHA384-P384</li>
-<li>ECDSA-SHA512-P521</li>
-<li>RSA-SHA256-2048</li>
-<li>RSA-SHA384-3072</li>
-<li>RSA-SHA512-4096</li>
-<li>EdDSA-SHA512-Ed25519 (as of release 0.9.15)</li>
-</ul>
-
-<h3>ECDSA</h3>
-
-<p>{% trans -%}
-ECDSA uses the standard NIST curves and standard SHA-2 hashes.
-We will migrate new destinations to ECDSA-SHA256-P256 in the 0.9.16 - 0.9.19 release time frame.
-Usage for Router Identities is supported as of release 0.9.16 and migration may occur in early 2015.
-{%- endtrans %}</p>
-
-
-<h3>RSA</h3>
-
-<p>{% trans -%}
-Standard RSA PKCS#1 v1.5 (RFC 2313) with the public exponent F4 = 65537.
-RSA is now used for signing all out-of-band trusted content, including router updates, reseeding, plugins, and news.
-The signatures are embedded in the "su3" format documented on the router updates page.
-4096-bit keys are recommended and used by all known signers.
-RSA is not used, or planned for use, in any in-network Destinations or Router Identities.
-{%- endtrans %}</p>
-
-
-<h3>EdDSA 25519</h3>
-
-<p>{% trans -%}
-Standard EdDSA using curve 25519 and standard 512-bit SHA-2 hashes.
-Supported as of release 0.9.15.
-Migration for Destinations and Router Identities is scheduled for mid-2015.
-{%- endtrans %}</p>
-
-
-
-<H2><a name="SHA256">SHA256</a></H2>
-
-<p>{% trans code='https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/SHA256Generator.java' -%}
-Hashes within I2P are plain old SHA256, as implemented in
-<a href="{{ code }}">[SHA256Generator]</a>
-{%- endtrans %}</p>
-
-<h4>{% trans %}Obsolescence{% endtrans %}</h4>
-<p>{% trans -%}
-The vulnerability of the network to a SHA-256 attack and the impact of transitioning to a longer hash is to be studied.
-It may be quite difficult to make any change backward-compatible.
-{%- endtrans %}</p>
-
-<h4>{% trans %}References{% endtrans %}</h4>
-<ul>
-<li>
-<a href="http://en.wikipedia.org/wiki/SHA-2">SHA-2</a>
-</ul>
-
-<h2 id="transports">{% trans %}Transports{% endtrans %}</h2>
-<p>{% trans -%}
-At the lowest protocol layer,
-point-to-point inter-router communication is protected by the transport layer security.
-Both transports use 256 byte (2048 bit) Diffie-Hellman key exchange
-using
-<a href="#elgamal">the same shared prime and generator as specified above for ElGamal</a>,
-followed by symmetric AES encryption as described above.
-This provides
-<a href="http://en.wikipedia.org/wiki/Perfect_forward_secrecy">perfect forward secrecy</a>
-on the transport links.
-{%- endtrans %}</p>
-
-<h3><a name="tcp">{% trans %}NTCP connections{% endtrans %}</a></h3>
-
-<p>{% trans elgamalaes=site_url('docs/how/elgamal-aes') -%}
-NTCP connections are negotiated with a 2048 Diffie-Hellman implementation,
-using the router's identity to proceed with a station to station agreement, followed by
-some encrypted protocol specific fields, with all subsequent data encrypted with AES
-(as above).
-The primary reason to do the DH negotiation instead of using <a href="{{ elgamalaes }}">ElGamalAES+SessionTag</a> is that it provides '<a href="http://en.wikipedia.org/wiki/Perfect_forward_secrecy">(perfect) forward secrecy</a>', while <a href="{{ elgamalaes }}">ElGamalAES+SessionTag</a> does not.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-In order to migrate to a more standardized implementation (TLS/SSL or even SSH), the following issues must be addressed:
-{%- endtrans %}</p>
-<ol>
-<li>{% trans -%}
-Can we somehow reestablish sessions securely (ala session tags) or do we need to do full negotiation each time?
-{%- endtrans %}</li>
-<li>{% trans -%}
-Can we simplify/avoid the x509 or other certificate formats and use our own RouterInfo structure (which 
-contains the ElGamal and DSA keys)?
-{%- endtrans %}</li>
-</ol>
-<p>{% trans ntcp=site_url('docs/transport/ntcp') -%}
-See <a href="{{ ntcp }}">the NTCP specification</a> for details.
-{%- endtrans %}</p>
-
-<h3><a name="udp">{% trans %}UDP connections{% endtrans %}</a></h3>
-<p>{% trans -%}
-SSU (the UDP transport) encrypts each packet with AES256/CBC with both an explicit IV and MAC 
-(HMAC-MD5-128) after agreeing upon an ephemeral session key through a 2048 bit 
-Diffie-Hellman exchange, station-to-station authentication with the other 
-router's DSA key, plus each network message has their own hash for local integrity 
-checking.
-{%- endtrans %}</p>
-
-<p>{% trans ssu=site_url('docs/transport/ssu') -%}
-See <a href="{{ ssu }}#keys">the SSU specification</a> for details.
-{%- endtrans %}</p>
-
-<p>{% trans statusnotes=get_url('blog_post', slug='2005/07/05/status') -%}
-WARNING - I2P's HMAC-MD5-128 used in SSU is apparently non-standard.
-Apparently, an early version of SSU used HMAC-SHA256, and then it was switched
-to MD5-128 for performance reasons, but left the 32-byte buffer size intact.
-See HMACGenerator.java and
-<a href="{{ statusnotes }}">the 2005-07-05 status notes</a>
-for details.
-{%- endtrans %}</p>
-
-
-<h2>{% trans %}References{% endtrans %}</h2>
-<ul>
-<li>
-<a href="http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57-Part1-revised2_Mar08-2007.pdf">NIST 800-57</a>
-</li>
-</ul>
-
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/index.html b/i2p2www/pages/site/docs/index.html
index 00c3883a5f8a68f71b5c80f9d934c66328ecb8cf..2d27d21b0de7ca94efa0002db13ed56efadfaa6c 100644
--- a/i2p2www/pages/site/docs/index.html
+++ b/i2p2www/pages/site/docs/index.html
@@ -39,13 +39,13 @@ If you find any inaccuracies in the documents linked below, please
 <li><a href="{{ site_url('get-involved/develop/applications') }}">Application Development Overview and Guide</a></li>
 <li><a href="{{ site_url('docs/naming') }}">{{ _('Naming and Addressbook') }}</a></li>
 <li><a href="{{ site_url('docs/plugins') }}">{{ _('Plugins Overview') }}</a></li>
-<li><a href="{{ site_url('docs/spec/plugin') }}">{{ _('Plugin Specification') }}</a></li>
+<li><a href="{{ spec_url('plugin') }}">{{ _('Plugin Specification') }}</a></li>
 <li><a href="{{ site_url('docs/applications/managed-clients') }}">{{ _('Managed Clients') }}</a></li>
 <li><a href="{{ site_url('docs/applications/embedding') }}">{{ _('Embedding the router in your application') }}</a></li>
 <li><a href="{{ site_url('docs/applications/bittorrent') }}">{{ _('Bittorrent over I2P') }}</a></li>
 <li><a href="{{ site_url('docs/api/i2pcontrol') }}">{{ _('I2PControl Plugin API') }}</a></li>
-<li><a href="{{ site_url('docs/spec/blockfile') }}">{{ _('hostsdb.blockfile Format') }}</a></li>
-<li><a href="{{ site_url('docs/spec/configuration') }}">{{ _('Configuration File Format') }}</a></li>
+<li><a href="{{ spec_url('blockfile') }}">{{ _('hostsdb.blockfile Format') }}</a></li>
+<li><a href="{{ spec_url('configuration') }}">{{ _('Configuration File Format') }}</a></li>
 </ul>
 
 <h3>{% trans %}Application Layer API and Protocols{% endtrans %}</h3>
@@ -55,7 +55,7 @@ If you find any inaccuracies in the documents linked below, please
 </li><li>
 <a href="{{ site_url('docs/api/i2ptunnel') }}">I2PTunnel</a>
 </li><li>
-<a href="{{ site_url('docs/spec/configuration') }}">{{ _('I2PTunnel Configuration') }}</a></li>
+<a href="{{ spec_url('configuration') }}">{{ _('I2PTunnel Configuration') }}</a></li>
 </li><li>
 <a href="{{ site_url('docs/api/socks') }}">SOCKS Proxy</a>
 </li><li>
@@ -85,11 +85,11 @@ HTTP Bidir Proxy
 <ul><li>
 <a href="{{ site_url('docs/api/streaming') }}">{{ _('Streaming Library') }}</a>
 </li><li>
-<a href="{{ site_url('docs/spec/streaming') }}">{{ _('Streaming Protocol Specification') }}</a>
+<a href="{{ spec_url('streaming') }}">{{ _('Streaming Protocol Specification') }}</a>
 </li><li>
 <a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/client/streaming/package-summary.html">{{ _('Streaming Javadoc') }}</a>
 </li><li>
-<a href="{{ site_url('docs/spec/datagrams') }}">{{ _('Datagrams') }}</a>
+<a href="{{ spec_url('datagrams') }}">{{ _('Datagrams') }}</a>
 </li><li>
 <a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/client/datagram/package-summary.html">{{ _('Datagram Javadoc') }}</a>
 </li></ul>
@@ -102,11 +102,11 @@ Traditionally used only by Java applications and higher-level APIs.
 <ul><li>
 <a href="{{ site_url('docs/protocol/i2cp') }}">{{ _('I2CP - I2P Control Protocol / API overview') }}</a>
 </li><li>
-<a href="{{ site_url('docs/spec/i2cp') }}">{{ _('I2CP Specification') }}</a>
+<a href="{{ spec_url('i2cp') }}">{{ _('I2CP Specification') }}</a>
 </li><li>
 <a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/client/package-summary.html">{{ _('I2CP API Javadoc') }}</a>
 </li><li>
-<a href="{{ site_url('docs/spec/common-structures') }}">{{ _('Common data structures specification') }}</a>
+<a href="{{ spec_url('common-structures') }}">{{ _('Common data structures specification') }}</a>
 </li><li>
 <a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/package-summary.html">{{ _('Data Structures Javadoc') }}</a>
 </li></ul>
@@ -124,7 +124,7 @@ Traditionally used only by Java applications and higher-level APIs.
 <li><a href="{{ site_url('docs/how/network-database') }}">{{ _('Network database overview, details, and threat analysis') }}</a></li>
 <li><a href="{{ site_url('docs/how/cryptography') }}#SHA256">{{ _('Cryptographic hashes') }}</a></li>
 <li><a href="{{ site_url('docs/how/cryptography') }}#sig">{{ _('Cryptographic signatures') }}</a></li>
-<li><a href="{{ site_url('docs/spec/updates') }}">{{ _('Router reseed specification') }}</a></li>
+<li><a href="{{ spec_url('updates') }}">{{ _('Router reseed specification') }}</a></li>
 </ul>
 
 <h3>{% trans %}Router Message Protocol{% endtrans %}</h3>
@@ -132,11 +132,11 @@ Traditionally used only by Java applications and higher-level APIs.
 <ul><li>
 <a href="{{ site_url('docs/protocol/i2np') }}">{{ _('I2NP - I2P Network Protocol Overview') }}</a>
 </li><li>
-<a href="{{ site_url('docs/spec/i2np') }}">{{ _('I2NP Specification') }}</a>
+<a href="{{ spec_url('i2np') }}">{{ _('I2NP Specification') }}</a>
 </li><li>
 <a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/i2np/package-summary.html">{{ _('I2NP Javadoc') }}</a>
 </li><li>
-<a href="{{ site_url('docs/spec/common-structures') }}">{{ _('Common data structures specification') }}</a>
+<a href="{{ spec_url('common-structures') }}">{{ _('Common data structures specification') }}</a>
 </li><li>
 <a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/package-summary.html">{{ _('Data Structures Javadoc') }}</a>
 </li></ul>
@@ -150,8 +150,8 @@ Traditionally used only by Java applications and higher-level APIs.
 <li><a href="{{ site_url('docs/tunnels/implementation') }}">{{ _('Tunnel building and encryption') }}</a></li>
 <li><a href="{{ site_url('docs/how/elgamal-aes') }}">{{ _('ElGamal/AES') }}</a> {{ _('for build request encryption') }}</li>
 <li><a href="{{ site_url('docs/how/cryptography') }}">{{ _('ElGamal and AES cryptography details') }}</a></li>
-<li><a href="{{ site_url('docs/spec/tunnel-creation') }}">{{ _('Tunnel building specification') }}</a></li>
-<li><a href="{{ site_url('docs/spec/tunnel-message') }}">{{ _('Low-level tunnel message specification') }}</a></li>
+<li><a href="{{ spec_url('tunnel-creation') }}">{{ _('Tunnel building specification') }}</a></li>
+<li><a href="{{ spec_url('tunnel-message') }}">{{ _('Low-level tunnel message specification') }}</a></li>
 <li><a href="{{ site_url('docs/tunnels/unidirectional') }}">{{ _('Unidirectional Tunnels') }}</a></li>
 <li><a href="{{ url_for('static', filename='pdf/I2P-PET-CON-2009.1.pdf') }}">{{ _('Peer Profiling and Selection in the I2P Anonymous Network') }}</a>
 {{ _('2009 paper (pdf), not current but still generally accurate') }}</li>
@@ -166,7 +166,7 @@ Traditionally used only by Java applications and higher-level APIs.
 </li><li>
 <a href="{{ site_url('docs/transport/ssu') }}">SSU</a> {{ _('UDP-based transport overview') }}
 </li><li>
-<a href="{{ site_url('docs/spec/ssu') }}">{{ _('SSU specification') }}</a>
+<a href="{{ spec_url('ssu') }}">{{ _('SSU specification') }}</a>
 </li><li>
 <a href="{{ site_url('docs/how/cryptography') }}#tcp">{{ _('NTCP transport encryption') }}</a>
 </li><li>
@@ -181,9 +181,9 @@ Traditionally used only by Java applications and higher-level APIs.
 
 <h3>{% trans %}Other Router Topics{% endtrans %}</h3>
 <ul><li>
-<a href="{{ site_url('docs/spec/updates') }}">{{ _('Router software updates') }}</a>
+<a href="{{ spec_url('updates') }}">{{ _('Router software updates') }}</a>
 </li><li>
-<a href="{{ site_url('docs/spec/updates') }}">{{ _('Router reseed specification') }}</a>
+<a href="{{ spec_url('updates') }}">{{ _('Router reseed specification') }}</a>
 </li><li>
 <a href="{{ site_url('misc/jbigi') }}">{{ _('Native BigInteger Library') }}</a>
 </li><li>
@@ -191,9 +191,9 @@ Traditionally used only by Java applications and higher-level APIs.
 </li><li>
 <a href="{{ site_url('about/performance') }}">{{ _('Performance') }}</a>
 </li><li>
-<a href="{{ site_url('docs/spec/configuration') }}">{{ _('Configuration File Format') }}</a></li>
+<a href="{{ spec_url('configuration') }}">{{ _('Configuration File Format') }}</a></li>
 </li><li>
-<a href="{{ site_url('docs/spec/geoip') }}">GeoIP File Format</a></li>
+<a href="{{ spec_url('geoip') }}">GeoIP File Format</a></li>
 </li></ul>
 
 <h3>{% trans %}Developer's Guides and Resources{% endtrans %}</h3>
diff --git a/i2p2www/pages/site/docs/spec/blockfile.html b/i2p2www/pages/site/docs/spec/blockfile.html
deleted file mode 100644
index 7be4db17e4af783259fae81ea84245167c7842e1..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/blockfile.html
+++ /dev/null
@@ -1,230 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}{% trans %}Blockfile and Hosts Database Specification{% endtrans %}{% endblock %}
-{% block lastupdated %}{% trans %}November 2014{% endtrans %}{% endblock %}
-{% block accuratefor %}0.9.17{% endblock %}
-{% block content %}
-<h3>{% trans %}Overview{% endtrans %}</h3>
-<p>{% trans naming=site_url('docs/naming') -%} 
-This document specifies
-the I2P blockfile file format
-and the tables in the hostsdb.blockfile used by the Blockfile <a href="{{ naming }}">Naming Service</a>.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-The blockfile provides fast Destination lookup in a compact format. While the blockfile page overhead is substantial,
-the destinations are stored in binary rather than in Base 64 as in the hosts.txt format.
-In addition, the blockfile provides the capability of arbitrary metadata storage
-(such as added date, source, and comments) for each entry.
-The metadata may be used in the future to provide advanced addressbook features.
-The blockfile storage requirement is a modest increase over the hosts.txt format, and the blockfile provides
-approximately 10x reduction in lookup times.
-{%- endtrans %}</p>
-
-<p>{% trans url='http://www.metanotion.net/software/sandbox/block.html' -%}
-A blockfile is simply on-disk storage of multiple sorted maps (key-value pairs),
-implemented as skiplists.
-The blockfile format is adopted from the
-<a href="{{ url }}">Metanotion Blockfile Database</a>.
-First we will define the file format, then the use of that format by the BlockfileNamingService.
-{%- endtrans %}</p>
-
-<h3>{% trans %}Blockfile Format{% endtrans %}</h3>
-<p>{% trans -%}
-The original blockfile spec was modified to add magic numbers to each page.
-The file is structured in 1024-byte pages. Pages are numbered starting from 1.
-The "superblock" is always at page 1, i.e. starting at byte 0 in the file.
-The metaindex skiplist is always at page 2, i.e. starting at byte 1024 in the file.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-All 2-byte integer values are unsigned.
-All 4-byte integer values (page numbers) are signed and negative values are illegal.
-All integer values are stored in network byte order (big endian).
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-The database is designed to be opened and accessed by a single thread.
-The BlockfileNamingService provides synchronization.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-Superblock format:
-{%- endtrans %}</p>
-{% highlight %}
-Byte	Contents
-0-5	Magic number	0x3141de493250 ("1A" 0xde "I2P")
-6	Major version	0x01
-7	Minor version	0x02
-8-15	File length	Total length in bytes
-16-19	First free list page
-20-21	Mounted flag	0x01 = yes
-22-23	Span size	Max number of key/value pairs per span (16 for hostsdb)
-			Used for new skip lists.
-24-27	Page size	As of version 1.2. Prior to 1.2, 1024 is assumed.
-28-1023	unused
-{% endhighlight %}
-
-<p>{% trans -%}
-Skip list block page format:
-{%- endtrans %}</p>
-{% highlight %}
-Byte	Contents
-0-7	Magic number	0x536b69704c697374 "SkipList"
-8-11	First span page
-12-15	First level page
-16-19	Size (total number of keys - may only be valid at startup)
-20-23	Spans (total number of spans - may only be valid at startup)
-24-27	Levels (total number of levels - may only be valid at startup)
-28-29	Span size - As of version 1.2. Max number of key/value pairs per span.
-                    Prior to that, specified for all skiplists in the superblock.
-                    Used for new spans in this skip list.
-30-1023	unused
-{% endhighlight %}
-
-
-<p>{% trans -%}
-Skip level block page format is as follows.
-All levels have a span. Not all spans have levels.
-{%- endtrans %}</p>
-{% highlight %}
-Byte	Contents
-0-7	Magic number	0x42534c6576656c73 "BSLevels"
-8-9	Max height
-10-11	Current height
-12-15	Span page
-16-	Next level pages ('current height' entries, 4 bytes each, lowest first)
-remaining bytes unused
-{% endhighlight %}
-
-
-<p>{% trans -%}
-Skip span block page format is as follows.
-Key/value structures are sorted by key within each span and across all spans.
-Key/value structures are sorted by key within each span.
-Spans other than the first span may not be empty.
-{%- endtrans %}</p>
-{% highlight %}
-Byte	Contents
-0-3	Magic number	0x5370616e "Span"
-4-7	First continuation page or 0
-8-11	Previous span page or 0
-12-15	Next span page or 0
-16-17	Max keys (16 for hostsdb)
-18-19	Size (current number of keys)
-20-1023	key/value structures
-{% endhighlight %}
-
-<p>{% trans -%}
-Span Continuation block page format:
-{%- endtrans %}</p>
-{% highlight %}
-Byte	Contents
-0-3	Magic number	0x434f4e54 "CONT"
-4-7	Next continuation page or 0
-8-1023	key/value structures
-{% endhighlight %}
-
-
-<p>{% trans -%}
-Key/value structure format is as follows.
-Key and value lengths must not be split across pages, i.e. all 4 bytes must be on the same page.
-If there is not enough room the last 1-3 bytes of a page are unused and the lengths will
-be at offset 8 in the continuation page.
-Key and value data may be split across pages.
-Max key and value lengths are 65535 bytes.
-{%- endtrans %}</p>
-{% highlight %}
-Byte	Contents
-0-1	key length in bytes
-2-3	value length in bytes
-4-	key data
-	value data
-{% endhighlight %}
-
-<p>{% trans -%}
-Free list block page format:
-{%- endtrans %}</p>
-{% highlight %}
-Byte	Contents
-0-7	Magic number	0x2366724c69737423 "#frList#"
-8-11	Next free list block or 0 if none
-12-15	Number of valid free pages in this block (0 - 252)
-16-1023	Free pages (4 bytes each), only the first (valid number) are valid
-{% endhighlight %}
-
-
-<p>{% trans -%}
-Free page block format:
-{%- endtrans %}</p>
-{% highlight %}
-Byte	Contents
-0-7	Magic number	0x7e2146524545217e "~!FREE!~"
-8-1023	unused
-{% endhighlight %}
-
-<p>{% trans -%}
-The metaindex (located at page 2) is a mapping of US-ASCII strings to 4-byte integers.
-The key is the name of the skiplist and the value is the page index of the skiplist.
-{%- endtrans %}</p>
-
-<h3>{% trans %}Blockfile Naming Service Tables{% endtrans %}</h3>
-<p>{% trans -%}
-The tables created and used by the BlockfileNamingService are as follows.
-The maximum number of entries per span is 16.
-{%- endtrans %}</p>
-
-<h4>{% trans %}Properties Skiplist{% endtrans %}</h4>
-<p>{% trans INFO='"%%__INFO__%%"' -%}
-{{ INFO }} is the master database skiplist with String/Properties key/value entries containing only one entry:
-{%- endtrans %}</p>
-<pre>
-    "info": a Properties (UTF-8 String/String Map), serialized as a <a href="{{ site_url('docs/spec/common-structures') }}#type_Mapping">Mapping</a>:
-            "version":   "3"
-            "created":   Java long time (ms)
-            "upgraded":  Java long time (ms) (as of database version 2)
-            "lists":     Comma-separated list of host databases, to be
-                         searched in-order for lookups. Almost always "privatehosts.txt,userhosts.txt,hosts.txt".
-</pre>
-
-<h4>{% trans %}Reverse Lookup Skiplist{% endtrans %}</h4>
-<p>{% trans REVERSE='"%%__REVERSE__%%"' -%}
-{{ REVERSE }} is the reverse lookup skiplist with Integer/Properties key/value entries
-    (as of database version 2):
-{%- endtrans %}</p>
-<pre>
-    The skiplist keys are 4-byte Integers, the first 4 bytes of the hash of the Destination.
-    The skiplist values are each a Properties (a UTF-8 String/String Map) serialized as a <a href="{{ site_url('docs/spec/common-structures') }}#type_Mapping">Mapping</a>
-        There may be multiple entries in the properties, each one is a reverse mapping,
-           as there may be more than one hostname for a given destination,
-           or there could be collisions with the same first 4 bytes of the hash.
-        Each property key is a hostname.
-        Each property value is the empty string.
-</pre>
-
-<h4>{% trans %}hosts.txt, userhosts.txt, and privatehosts.txt Skiplists{% endtrans %}</h4>
-<p>{% trans -%}
-For each host database, there is a skiplist containing
-the hosts for that database.
-The keys/values in these skiplists are as follows:
-{%- endtrans %}</p>
-<pre>
-     key:    a UTF-8 String (the hostname)
-     value:  a DestEntry, which is a Properties (a UTF-8 String/String Map) serialized as a <a href="{{ site_url('docs/spec/common-structures') }}#type_Mapping">Mapping</a>
-             followed by a binary Destination (serialized <a href="{{ site_url('docs/spec/common-structures') }}#struct_Destination">as usual</a>).
-</pre>
-
-<p>{% trans -%}
-The DestEntry Properties typically contains:
-{%- endtrans %}</p>
-<pre>
-            "a":     The time added (Java long time in ms)
-            "s":     The original source of the entry (typically a file name or subscription URL)
-            others:  TBD
-</pre>
-
-<p>{% trans -%}
-Hostname keys are stored in lower-case and always end in ".i2p".
-{%- endtrans %}</p>
-
-
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/common-structures.html b/i2p2www/pages/site/docs/spec/common-structures.html
deleted file mode 100644
index 8f25f9fa6100e4a76799a35a308801c9a16b8ea9..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/common-structures.html
+++ /dev/null
@@ -1,1068 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}{% trans %}Common structures Specification{% endtrans %}{% endblock %}
-{% block lastupdated %}{% trans %}February 2016{% endtrans %}{% endblock %}
-{% block accuratefor %}0.9.24{% endblock %}
-{% block content_nav %}
-<table border=1>
-<tr><th>{% trans %}Type{% endtrans %}</th></tr>
-<tr><td><a href="#type_Boolean">Boolean</a></td></tr>
-<tr><td><a href="#type_Certificate">Certificate</a></td></tr>
-<tr><td><a href="#type_Date">Date</a></td></tr>
-<tr><td><a href="#struct_DeliveryInstructions">Delivery Instructions</a></td></tr>
-<tr><td><a href="#struct_Destination">Destination</a></td></tr>
-<tr><td><a href="#type_Hash">Hash</a></td></tr>
-<tr><td><a href="#type_Integer">Integer</a></td></tr>
-<tr><td><a href="#struct_KeysAndCert">KeysAndCert</a></td></tr>
-<tr><td><a href="#struct_Lease">Lease</a></td></tr>
-<tr><td><a href="#struct_LeaseSet">LeaseSet</a></td></tr>
-<tr><td><a href="#type_Mapping">Mapping</a></td></tr>
-<tr><td><a href="#type_PrivateKey">PrivateKey</a></td></tr>
-<tr><td><a href="#type_PublicKey">PublicKey</a></td></tr>
-<tr><td><a href="#struct_RouterAddress">RouterAddress</a></td></tr>
-<tr><td><a href="#struct_RouterIdentity">RouterIdentity</a></td></tr>
-<tr><td><a href="#struct_RouterInfo">RouterInfo</a></td></tr>
-<tr><td><a href="#type_SessionTag">SessionTag</a></td></tr>
-<tr><td><a href="#type_Signature">Signature</a></td></tr>
-<tr><td><a href="#type_SigningPrivateKey">SigningPrivateKey</a></td></tr>
-<tr><td><a href="#type_SigningPublicKey">SigningPublicKey</a></td></tr>
-<tr><td><a href="#type_String">String</a></td></tr>
-<tr><td><a href="#type_TunnelId">TunnelId</a></td></tr>
-</table>
-{% endblock %}
-
-{% block content %}
-<p>{% trans i2np=site_url('docs/protocol/i2np'),
-i2cp=site_url('docs/protocol/i2cp'),
-ssu=site_url('docs/transport/ssu') -%}
-This document describes some data types common to all I2P protocols, like
-<a href="{{ i2np }}">I2NP</a>, <a href="{{ i2cp }}">I2CP</a>,
-<a href="{{ ssu }}">SSU</a>, etc.
-{%- endtrans %}</p>
-
-<h2 id="type_Integer">Integer</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-Represents a non-negative integer.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-1 to 8 bytes in network byte order representing an unsigned integer
-{% endtrans %}</p>
-
-<h2 id="type_Date">Date</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-The number of milliseconds since midnight on January 1, 1970 in the GMT timezone.
-If the number is 0, the date is undefined or null.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-8 byte <a href="#type_Integer">Integer</a>
-{% endtrans %}</p>
-
-<h2 id="type_String">String</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-Represents a UTF-8 encoded string.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-1 or more bytes where the first byte is the number of bytes (not characters!)
-in the string and the remaining 0-255 bytes are the non-null terminated UTF-8 encoded character array.
-Length limit is 255 bytes (not characters). Length may be 0.
-{% endtrans %}</p>
-
-<h2 id="type_Boolean">Boolean</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-A boolean value, supporting null/unknown representation
-0=false, 1=true, 2=unknown/null
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-1 byte <a href="#type_Integer">Integer</a>
-{% endtrans %}</p>
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<p>{% trans -%}
-Deprecated - unused
-{% endtrans %}</p>
-
-<h2 id="type_PublicKey">PublicKey</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans cryptography=site_url('docs/how/cryptography') -%}
-This structure is used in ElGamal encryption, representing only the exponent, not the primes, which are constant and defined in
-<a href="{{ cryptography }}#elgamal">the cryptography specification</a>.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-256 bytes
-{% endtrans %}</p>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/PublicKey.html">Javadoc</a></h4>
-
-<h2 id="type_PrivateKey">PrivateKey</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans cryptography=site_url('docs/how/cryptography') -%}
-This structure is used in ElGamal decryption, representing only the exponent, not the primes which are constant and defined in
-<a href="{{ cryptography }}#elgamal">the cryptography specification</a>.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-256 bytes
-{% endtrans %}</p>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/PrivateKey.html">Javadoc</a></h4>
-
-<h2 id="type_SessionKey">SessionKey</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-This structure is used for AES256 encryption and decryption.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-32 bytes
-{% endtrans %}</p>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/SessionKey.html">Javadoc</a></h4>
-
-
-<h2 id="type_SigningPublicKey">SigningPublicKey</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans cryptography=site_url('docs/how/cryptography') -%}
-This structure is used for verifying signatures.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-Key type and length are inferred from context or are specified in the Key Certificate of a Destination.
-{% endtrans %}
-{% trans -%}
-The default type is DSA_SHA1.
-{% endtrans %}
-{% trans -%}
-As of release 0.9.12, other types may be supported, depending on context.
-{% endtrans %}</p>
-
-<table border=1>
-<tr><th>{% trans %}Type{% endtrans %}</th><th>{% trans %}Length (bytes){% endtrans %}</th><th>{% trans %}Since{% endtrans %}</th><th>{% trans %}Usage{% endtrans %}</th></tr>
-<tr><td>DSA_SHA1</td><td align="center">128</td><td>&nbsp;</td>
-<td>Legacy Router Identities and Destinations</td></tr>
-<tr><td>ECDSA_SHA256_P256</td><td align="center">64</td><td align="center">0.9.12</td>
-<td>Recent Destinations</td></tr>
-<tr><td>ECDSA_SHA384_P384</td><td align="center">96</td><td align="center">0.9.12</td>
-<td>Rarely used for Destinations</td></tr>
-<tr><td>ECDSA_SHA512_P521</td><td align="center">132</td><td align="center">0.9.12</td>
-<td>Rarely used for Destinations</td></tr>
-<tr><td>RSA_SHA256_2048</td><td align="center">256</td><td align="center">0.9.12</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-<tr><td>RSA_SHA384_3072</td><td align="center">384</td><td align="center">0.9.12</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-<tr><td>RSA_SHA512_4096</td><td align="center">512</td><td align="center">0.9.12</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-<tr><td>EdDSA_SHA512_Ed25519</td><td align="center">32</td><td align="center">0.9.15</td>
-<td>Recent Router Identities and Destinations</td></tr>
-<tr><td>EdDSA_SHA512_Ed25519ph</td><td align="center">32</td><td align="center">0.9.25</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-</table>
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul>
-<li>{% trans -%}
-When a key is composed of two elements (for example points X,Y), it is serialized by
-padding each element to length/2 with leading zeros if necessary.
-{%- endtrans %}</li>
-<li>{% trans -%}
-All types are Big Endian, except for
-EdDSA, which is stored and transmitted in a Little Endian format.
-{%- endtrans %}</li>
-</ul>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/SigningPublicKey.html">Javadoc</a></h4>
-
-
-<h2 id="type_SigningPrivateKey">SigningPrivateKey</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans cryptography=site_url('docs/how/cryptography') -%}
-This structure is used for creating signatures.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-Key type and length are specified when created.
-{% endtrans %}
-{% trans -%}
-The default type is DSA_SHA1.
-{% endtrans %}
-{% trans -%}
-As of release 0.9.12, other types may be supported, depending on context.
-{% endtrans %}</p>
-
-<table border=1>
-<tr><th>{% trans %}Type{% endtrans %}</th><th>{% trans %}Length (bytes){% endtrans %}</th><th>{% trans %}Since{% endtrans %}</th><th>{% trans %}Usage{% endtrans %}</th></tr>
-<tr><td>DSA_SHA1</td><td align="center">20</td><td>&nbsp;</td>
-<td>Legacy Router Identities and Destinations</td></tr>
-<tr><td>ECDSA_SHA256_P256</td><td align="center">32</td><td align="center">0.9.12</td>
-<td>Recent Destinations</td></tr>
-<tr><td>ECDSA_SHA384_P384</td><td align="center">48</td><td align="center">0.9.12</td>
-<td>Rarely used for Destinations</td></tr>
-<tr><td>ECDSA_SHA512_P521</td><td align="center">66</td><td align="center">0.9.12</td>
-<td>Rarely used for Destinations</td></tr>
-<tr><td>RSA_SHA256_2048</td><td align="center">512</td><td align="center">0.9.12</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-<tr><td>RSA_SHA384_3072</td><td align="center">768</td><td align="center">0.9.12</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-<tr><td>RSA_SHA512_4096</td><td align="center">1024</td><td align="center">0.9.12</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-<tr><td>EdDSA_SHA512_Ed25519</td><td align="center">32</td><td align="center">0.9.15</td>
-<td>Recent Router Identities and Destinations</td></tr>
-<tr><td>EdDSA_SHA512_Ed25519ph</td><td align="center">32</td><td align="center">0.9.25</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-</table>
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul>
-<li>{% trans -%}
-When a key is composed of two elements (for example points X,Y), it is serialized by
-padding each element to length/2 with leading zeros if necessary.
-{%- endtrans %}</li>
-<li>{% trans -%}
-All types are Big Endian, except for
-EdDSA, which is stored and transmitted in a Little Endian format.
-{%- endtrans %}</li>
-</ul>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/SigningPrivateKey.html">Javadoc</a></h4>
-
-
-<h2 id="type_Signature">Signature</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans cryptography=site_url('docs/how/cryptography') -%}
-This structure represents the signature of some data.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-Signature type and length are inferred from the type of key used.
-{% endtrans %}
-{% trans -%}
-The default type is DSA_SHA1.
-{% endtrans %}
-{% trans -%}
-As of release 0.9.12, other types may be supported, depending on context.
-{% endtrans %}</p>
-
-<table border=1>
-<tr><th>{% trans %}Type{% endtrans %}</th><th>{% trans %}Length (bytes){% endtrans %}</th><th>{% trans %}Since{% endtrans %}</th><th>{% trans %}Usage{% endtrans %}</th></tr>
-<tr><td>DSA_SHA1</td><td align="center">40</td><td>&nbsp;</td>
-<td>Legacy Router Identities and Destinations</td></tr>
-<tr><td>ECDSA_SHA256_P256</td><td align="center">64</td><td align="center">0.9.12</td>
-<td>Recent Destinations</td></tr>
-<tr><td>ECDSA_SHA384_P384</td><td align="center">96</td><td align="center">0.9.12</td>
-<td>Rarely used for Destinations</td></tr>
-<tr><td>ECDSA_SHA512_P521</td><td align="center">132</td><td align="center">0.9.12</td>
-<td>Rarely used for Destinations</td></tr>
-<tr><td>RSA_SHA256_2048</td><td align="center">256</td><td align="center">0.9.12</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-<tr><td>RSA_SHA384_3072</td><td align="center">384</td><td align="center">0.9.12</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-<tr><td>RSA_SHA512_4096</td><td align="center">512</td><td align="center">0.9.12</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-<tr><td>EdDSA_SHA512_Ed25519</td><td align="center">64</td><td align="center">0.9.15</td>
-<td>Recent Router Identities and Destinations</td></tr>
-<tr><td>EdDSA_SHA512_Ed25519ph</td><td align="center">64</td><td align="center">0.9.25</td>
-<td>Offline signing, never used for Router Identities or Destinations</td></tr>
-</table>
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul>
-<li>{% trans -%}
-When a signature is composed of two elements (for example values R,S), it is serialized by
-padding each element to length/2 with leading zeros if necessary.
-{%- endtrans %}</li>
-<li>{% trans -%}
-All types are Big Endian, except for
-EdDSA, which is stored and transmitted in a Little Endian format.
-{%- endtrans %}</li>
-</ul>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/Signature.html">Javadoc</a></h4>
-
-
-<h2 id="type_Hash">Hash</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-Represents the SHA256 of some data.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-32 bytes
-{% endtrans %}</p>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/Hash.html">Javadoc</a></h4>
-
-<h2 id="type_SessionTag">Session Tag</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-A random number
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-32 bytes
-{% endtrans %}</p>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/SessionTag.html">Javadoc</a></h4>
-
-<h2 id="type_TunnelId">TunnelId</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-Defines an identifier that is unique to each router in a tunnel.
-{% endtrans %}
-{% trans -%}
-A Tunnel ID is generally greater than zero; do not use a value of zero except in special cases.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-4 byte <a href="#type_Integer">Integer</a>
-{% endtrans %}</p>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/TunnelId.html">Javadoc</a></h4>
-
-<h2 id="type_Certificate">Certificate</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-A certificate is a container for various receipts or proof of works used throughout the I2P network.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-1 byte <a href="#type_Integer">Integer</a> specifying certificate type, followed by a 2 byte <a href="#type_Integer">Integer</a> specifying the size of the certificate payload, then that many bytes.
-{% endtrans %}</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+-//
-|type| length  | payload
-+----+----+----+----+----+-//
-
-type :: `Integer`
-        length -> 1 byte
-
-        case 0 -> NULL
-        case 1 -> HASHCASH
-        case 2 -> HIDDEN
-        case 3 -> SIGNED
-        case 4 -> MULTIPLE
-        case 5 -> KEY
-
-length :: `Integer`
-          length -> 2 bytes
-
-payload :: data
-           length -> $length bytes
-{% endhighlight %}
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul>
-<li>{% trans -%}
-For <a href="#struct_RouterIdentity">Router Identities</a>, the Certificate is always NULL through version 0.9.15.
-As of 0.9.16, a Key Certificate may be used to specify the signing public key type. See below.
-{%- endtrans %}</li>
-
-<li>{% trans i2np=site_url('docs/spec/i2np') -%}
-For <a href="{{ i2np }}#struct_GarlicClove">Garlic Cloves</a>, the Certificate is always NULL, no others are currently implemented.
-{%- endtrans %}</li>
-
-<li>{% trans i2np=site_url('docs/spec/i2np') -%}
-For <a href="{{ i2np }}#msg_Garlic">Garlic Messages</a>, the Certificate is always NULL, no others are currently implemented.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-For <a href="#struct_Destination">Destinations</a>, the Certificate may be non-NULL.
-As of 0.9.12, a Key Certificate may be used to specify the signing public key type. See below.
-{%- endtrans %}</li>
-</ul>
-
-<h4>{% trans %}Certificate Types{% endtrans %}</h4>
-<p>The following certificate types are defined:</p>
-<table border=1>
-<tr><th>Type</th><th>Type Code</th><th>Payload Length</th><th>Total Length</th><th>Notes</th></tr>
-<tr><td>Null</td><td align="center">0</td><td align="center">0</td><td align="center">3</td><td></td></tr>
-<tr><td>HashCash</td><td align="center">1</td><td align="center">varies</td><td align="center">varies</td><td>
-  Experimental, unused. Payload contains an ASCII colon-separated hashcash string.
-</td></tr>
-<tr><td>Hidden</td><td align="center">2</td><td align="center">0</td><td align="center">3</td><td>
-  Experimental, unused. Hidden routers generally do not announce that they are hidden.
-</td></tr>
-<tr><td>Signed</td><td align="center">3</td><td align="center">40 or 72</td><td align="center">43 or 75</td><td>
-  Experimental, unused. Payload contains a 40-byte DSA signature,
-  optionally followed by the 32-byte Hash of the signing Destination.
-</td></tr>
-<tr><td>Multiple</td><td align="center">4</td><td align="center">varies</td><td align="center">varies</td><td>
-  Experimental, unused. Payload contains multiple certificates.
-</td></tr>
-<tr><td>Key</td><td align="center">5</td><td align="center">4+</td><td align="center">7+</td><td>
-  Since 0.9.12. See below for details.
-</td></tr>
-</table>
-
-
-<h4>{% trans %}Key Certificates{% endtrans %}</h4>
-<p>
-Key certificates were introduced in release 0.9.12.
-Prior to that release, all PublicKeys were 256-byte ElGamal keys, and
-all SigningPublicKeys were 128-byte DSA-SHA1 keys.
-A key certificate provides a mechanism to indicate the type of the PublicKey and SigningPublicKey
-in the Destination or RouterIdentity, and to package any key data in excess of the
-standard lengths.
-</p><p>
-By maintaining exactly 384 bytes before the certificate, and putting any excess key
-data inside the certificate, we maintain compatibility for any software that
-parses Destinations and Router Identities.
-
-</p><p>
-The key certificate payload contains:
-</p><table border=1>
-<tr><th>Data</th><th>Length</th></tr>
-<tr><td>Signing Public Key Type
-(<a href="#type_Integer">Integer</a>)
-</td><td align="center">2</td></tr>
-<tr><td>Crypto Public Key Type
-(<a href="#type_Integer">Integer</a>)
-</td><td align="center">2</td></tr>
-<tr><td>Excess Signing Public Key Data</td><td align="center">0+</td></tr>
-<tr><td>Excess Crypto Public Key Data</td><td align="center">0+</td></tr>
-</table>
-
-<p>
-The defined Signing Key types are:
-</p><table border=1>
-<tr><th>Type</th><th>Type Code</th><th>Total Public Key Length</th><th>{% trans %}Since{% endtrans %}</th><th>{% trans %}Usage{% endtrans %}</th></tr>
-<tr><td>DSA_SHA1</td><td align="center">0</td><td align="center">128</td><td align="center">0.9.12</td>
-<td>Legacy Router Identities and Destinations, never explicitly set</td></tr>
-<tr><td>ECDSA_SHA256_P256</td><td align="center">1</td><td align="center">64</td><td align="center">0.9.12</td>
-<td>Recent Destinations</td></tr>
-<tr><td>ECDSA_SHA384_P384</td><td align="center">2</td><td align="center">96</td><td align="center">0.9.12</td>
-<td>Sometimes used for Destinations</td></tr>
-<tr><td>ECDSA_SHA512_P521</td><td align="center">3</td><td align="center">132</td><td align="center">0.9.12</td>
-<td>Sometimes used for Destinations</td></tr>
-<tr><td>RSA_SHA256_2048</td><td align="center">4</td><td align="center">256</td><td align="center">0.9.12</td>
-<td>Offline only; never used in Key Certificates for Router Identities or Destinations</td></tr>
-<tr><td>RSA_SHA384_3072</td><td align="center">5</td><td align="center">384</td><td align="center">0.9.12</td>
-<td>Offline only; never used in Key Certificates for Router Identities or Destinations</td></tr>
-<tr><td>RSA_SHA512_4096</td><td align="center">6</td><td align="center">512</td><td align="center">0.9.12</td>
-<td>Offline only; never used in Key Certificates for Router Identities or Destinations</td></tr>
-<tr><td>EdDSA_SHA512_Ed25519</td><td align="center">7</td><td align="center">32</td><td align="center">0.9.15</td>
-<td>Recent Router Identities and Destinations</td></tr>
-<tr><td>EdDSA_SHA512_Ed25519ph</td><td align="center">8</td><td align="center">32</td><td align="center">0.9.25</td>
-<td>Offline only; never used in Key Certificates for Router Identities or Destinations</td></tr>
-<tr><td>reserved</td><td align="center">65280 - 65534</td><td align="center"></td><td align="center"></td>
-<td><i>Reserved for experimental use</i></td></tr>
-<tr><td>reserved</td><td align="center">65535</td><td align="center"></td><td align="center"></td>
-<td><i>Reserved for future expansion</i></td></tr>
-</table>
-
-<p>
-The defined Crypto Public Key types are:
-</p><table border=1>
-<tr><th>Type</th><th>Type Code</th><th>Total Public Key Length</th><th>{% trans %}Since{% endtrans %}</th><th>{% trans %}Usage{% endtrans %}</th></tr>
-<tr><td>ElGamal</td><td align="center">0</td><td align="center">256</td><td align="center">0.9.12</td>
-<td>All Router Identities and Destinations</td></tr>
-<tr><td>reserved</td><td align="center">65280 - 65534</td><td align="center"></td><td align="center"></td>
-<td><i>Reserved for experimental use</i></td></tr>
-<tr><td>reserved</td><td align="center">65535</td><td align="center"></td><td align="center"></td>
-<td><i>Reserved for future expansion</i></td></tr>
-</table>
-
-<p>
-When a Key Certificate is not present, the preceeding 384 bytes in the Destination or RouterIdentity
-are defined as the 256-byte ElGamal PublicKey followed by the 128-byte DSA-SHA1 SigningPublicKey.
-When a Key Certificate is present, the preceeding 384 bytes are redefined as follows:
-<ul><li>
-Complete or first portion of Crypto Public Key
-</li><li>
-Random padding if the total lengths of the two keys are less than 384 bytes
-</li><li>
-Complete or first portion of Signing Public Key
-</li></ul>
-</p><p>
-The Crypto Public Key is aligned at the start and the Signing Public Key is aligned at the end.
-The padding (if any) is in the middle.
-The lengths and boundaries of the initial key data, the padding, and the excess key data portions
-in the certificates are not explicitly specified, but are derived from
-the lengths of the specified key types.
-If the total lengths of the Crypto and Signing Public Keys exceed 384 bytes,
-the remainder will be contained in the Key Certificate.
-If the Crypto Public Key length is not 256 bytes, the method for determining
-the boundary between the two keys is to be specified in a future revision of this document.
-</p>
-
-<p>
-Example layouts using an ElGamal Crypto Public Key and the Signing Public Key type indicated:
-</p><table border=1>
-<tr><th>Signing Key Type</th><th>Padding Length</th><th>Excess Signing Key Data in Cert</th></tr>
-<tr><td>DSA_SHA1</td><td align="center">0</td><td align="center">0</td></tr>
-<tr><td>ECDSA_SHA256_P256</td><td align="center">64</td><td align="center">0</td></tr>
-<tr><td>ECDSA_SHA384_P384</td><td align="center">32</td><td align="center">0</td></tr>
-<tr><td>ECDSA_SHA512_P521</td><td align="center">0</td><td align="center">4</td></tr>
-<tr><td>RSA_SHA256_2048</td><td align="center">0</td><td align="center">128</td></tr>
-<tr><td>RSA_SHA384_3072</td><td align="center">0</td><td align="center">256</td></tr>
-<tr><td>RSA_SHA512_4096</td><td align="center">0</td><td align="center">384</td></tr>
-<tr><td>EdDSA_SHA512_Ed25519</td><td align="center">96</td><td align="center">0</td></tr>
-<tr><td>EdDSA_SHA512_Ed25519ph</td><td align="center">96</td><td align="center">0</td></tr>
-</table>
-
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/Certificate.html">Javadoc</a></h4>
-
-
-<h2 id="type_Mapping">Mapping</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-A set of key/value mappings or properties
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-A 2-byte size Integer followed by a series of String=String; pairs
-{% endtrans %}</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-|  size   |key_string (len + data) | =  |
-+----+----+----+----+----+----+----+----+
-| val_string (len + data)     | ;  | ...
-+----+----+----+----+----+----+----+
-size :: `Integer`
-        length -> 2 bytes
-        Total number of bytes that follow
-
-key_string :: `String`
-              A string (one byte length followed by UTF-8 encoded characters)
-
-= :: A single byte containing '='
-
-val_string :: `String`
-              A string (one byte length followed by UTF-8 encoded characters)
-
-; :: A single byte containing ';'
-
-{% endhighlight %}
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul>
-<li>{% trans -%}
-The encoding isn't optimal - we either need the '=' and ';' characters, or the string lengths, but not both
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-Some documentation says that the strings may not include '=' or ';' but this encoding supports them
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-Strings are defined to be UTF-8 but in the current implementation, I2CP uses UTF-8 but I2NP does not.
-For example,
-UTF-8 strings in a RouterInfo options mapping in a I2NP Database Store Message will be corrupted.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-The encoding allows duplicate keys, however in any usage where the mapping is signed,
-duplicates may cause a signature failure.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-Mappings contained in I2NP messages (i.e. in a RouterAddress or RouterInfo)
-must be sorted by key so that the signature will be invariant.
-Duplicate keys are not allowed.
-{%- endtrans %}</li>
-
-<li>{% trans i2cp=site_url('docs/spec/i2cp') -%}
-Mappings contained in an <a href="{{ i2cp }}#struct_SessionConfig">I2CP SessionConfig</a>
-must be sorted by key so that the signature will be invariant.
-Duplicate keys are not allowed.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-The sort method is defined as in Java String.compareTo(),
-using the Unicode value of the characters.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-While it is application-dependent, keys and values are generally case-sensitive.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-Key and value string length limits are 255 bytes (not characters) each, plus the length byte. Length byte may be 0.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-Total length limit is 65535 bytes, plus the 2 byte size field, or 65537 total.
-{%- endtrans %}</li>
-</ul>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/DataHelper.html">Javadoc</a></h4>
-
-
-
-<h1>{% trans %}Common structure specification{% endtrans %}</h1>
-
-<h2 id="struct_KeysAndCert">KeysAndCert</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-An encryption public key, a signing public key, and a certificate, used as either a RouterIdentity or a Destination.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-<a href="#type_PublicKey">PublicKey</a> followed by <a href="#type_SigningPublicKey">SigningPublicKey</a> and then a <a href="#type_Certificate">Certificate</a>
-{% endtrans %}</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-| public_key                            |
-+                                       +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| padding (optional)                    |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| signing_key                           |
-+                                       +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| certificate                           |
-+----+----+----+-//
-
-public_key :: `PublicKey` (partial or full)
-              length -> 256 bytes or as specified in key certificate
-
-padding :: random data
-              length -> 0 bytes or as specified in key certificate
-              padding length + signing_key length == 128 bytes
-
-signing__key :: `SigningPublicKey` (partial or full)
-              length -> 128 bytes or as specified in key certificate
-              padding length + signing_key length == 128 bytes
-
-certificate :: `Certificate`
-               length -> >= 3 bytes
-
-total length: 387+ bytes
-{% endhighlight %}
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul><li>
-Do not assume that these are always 387 bytes!
-They are 387 bytes plus the certificate length specified at bytes 385-386, which may be non-zero.
-</li><li>
-As of release 0.9.12, if the certificate is a Key Certificate, the boundaries of the
-key fields may vary. See the Key Certificate section above for details.
-</li><li>
-The Crypto Public Key is aligned at the start and the Signing Public Key is aligned at the end.
-The padding (if any) is in the middle.
-</li></ul>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/KeysAndCert.html">Javadoc</a></h4>
-
-<h2 id="struct_RouterIdentity">RouterIdentity</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-Defines the way to uniquely identify a particular router
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-Identical to KeysAndCert.
-{% endtrans %}</p>
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul><li>{% trans -%}
-The certificate for a RouterIdentity was always NULL until release 0.9.12.
-{%- endtrans %}</li><li>
-Do not assume that these are always 387 bytes!
-They are 387 bytes plus the certificate length specified at bytes 385-386, which may be non-zero.
-</li><li>
-As of release 0.9.12, if the certificate is a Key Certificate, the boundaries of the
-key fields may vary. See the Key Certificate section above for details.
-</li><li>
-The Crypto Public Key is aligned at the start and the Signing Public Key is aligned at the end.
-The padding (if any) is in the middle.
-</li></ul>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/router/RouterIdentity.html">Javadoc</a></h4>
-
-<h2 id="struct_Destination">Destination</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-A Destination defines a particular endpoint to which messages can be directed for secure delivery.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-Identical to KeysAndCert.
-{% endtrans %}</p>
-
-<h4>Notes</h4>
-<ul><li>
-The public key of the destination was used for the old i2cp-to-i2cp encryption
-which was disabled in version 0.6, it is currently unused
-except for the IV for LeaseSet encryption,
-which is deprecated. The public key in the LeaseSet is used instead.
-</li><li>
-Do not assume that these are always 387 bytes!
-They are 387 bytes plus the certificate length specified at bytes 385-386, which may be non-zero.
-</li><li>
-As of release 0.9.12, if the certificate is a Key Certificate, the boundaries of the
-key fields may vary. See the Key Certificate section above for details.
-</li><li>
-The Crypto Public Key is aligned at the start and the Signing Public Key is aligned at the end.
-The padding (if any) is in the middle.
-</li></ul>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/Destination.html">Javadoc</a></h4>
-
-<h2 id="struct_Lease">Lease</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-Defines the authorization for a particular tunnel to receive messages targeting a <a href="#struct_Destination">Destination</a>.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-SHA256 <a href="#type_Hash">Hash</a> of the
-<a href="#struct_RouterIdentity">RouterIdentity</a> of the gateway router, then the <a href="#type_TunnelId">TunnelId</a>, and finally an end <a href="#type_Date">Date</a>
-{% endtrans %}</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-| tunnel_gw                             |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-|     tunnel_id     |      end_date
-+----+----+----+----+----+----+----+----+
-                    |
-+----+----+----+----+
-
-tunnel_gw :: Hash of the `RouterIdentity` of the tunnel gateway
-             length -> 32 bytes
-
-tunnel_id :: `TunnelId`
-             length -> 4 bytes
-
-end_date :: `Date`
-            length -> 8 bytes
-{% endhighlight %}
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul>
-<li>{% trans -%}
-Total size: 44 bytes
-{%- endtrans %}</li>
-</ul>
-
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/Lease.html">Javadoc</a></h4>
-
-<h2 id="struct_LeaseSet">LeaseSet</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-Contains all of the currently authorized <a href="#struct_Lease">Lease</a>s for a particular <a href="#struct_Destination">Destination</a>, the <a href="#type_PublicKey">PublicKey</a> to which garlic messages can be encrypted,
-and then the <a href="#type_SigningPublicKey">public key</a> that can be used to revoke this particular version of the structure. The <a href="#struct_LeaseSet">LeaseSet</a> is one of the two structures stored in the network database(
-the other being <a href="#struct_RouterInfo">RouterInfo</a>), and is keyed under the SHA256 of the contained <a href="#struct_Destination">Destination</a>.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-<a href="#struct_Destination">Destination</a>, followed by a <a href="#type_PublicKey">PublicKey</a> for encryption, then a <a href="#type_SigningPublicKey">SigningPublicKey</a> which can be used to revoke this version of the <a href="#struct_LeaseSet">LeaseSet</a>,
-then a 1 byte <a href="#type_Integer">Integer</a> specifying how many <a href="#struct_Lease">Lease</a> structures are in the set, followed by the actual <a href="#struct_Lease">Lease</a> structures and finally a <a href="#type_Signature">Signature</a>  of the previous
-bytes signed by the <a href="#struct_Destination">Destination's</a> <a href="#type_SigningPrivateKey">SigningPrivateKey</a>
-{%- endtrans %}</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-| destination                           |
-+                                       +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| encryption_key                        |
-+                                       +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| signing_key                           |
-+                                       +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-|num | Lease 0                          |
-+----+                                  +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| Lease 1                               |
-+                                       +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| Lease ($num-1)                        |
-+                                       +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| signature                             |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-
-destination :: `Destination`
-               length -> >= 387 bytes
-
-encryption_key :: `PublicKey`
-                  length -> 256 bytes
-
-signing_key :: `SigningPublicKey`
-               length -> 128 bytes or as specified in destination's key certificate
-
-num :: `Integer`
-       length -> 1 byte
-       Number of leases to follow
-       value: 0 <= num <= 16
-
-leases :: [`Lease`]
-          length -> $num*44 bytes
-
-signature :: `Signature`
-             length -> 40 bytes or as specified in destination's key certificate
-
-{% endhighlight %}
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul>
-<li>{% trans -%}
-The public key of the destination was used for the old i2cp-to-i2cp encryption
-which was disabled in version 0.6, it is currently unused.
-{%- endtrans %}</li>
-
-<li>{% trans elgamalaes=site_url('docs/how/elgamal-aes') -%}
-The encryption key is used for end-to-end <a href="{{ elgamalaes }}">ElGamal/AES+SessionTag</a> encryption.
-It is currently generated anew at every router startup, it is not persistent.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-The signature may be verified using the signing public key of the destination.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-The signing_key is currently unused. It was intended for LeaseSet revocation, which is unimplemented.
-It is currently generated anew at every router startup, it is not persistent.
-The signing key type is always the same as the destination's signing key type.
-{%- endtrans %}</li>
-<li>{% trans -%}
-The earliest expiration of all the Leases is treated as the timestamp or version of the LeaseSet.
-Routers will generally not accept a store of a LeaseSet unless it is "newer" than the current one.
-Take care when publishing a new LeaseSet where the oldest Lease is the same as the oldest Lease
-in the previous LeaseSet. The publishing router should generally increment the expiration
-of the oldest Lease by at least 1 ms in that case.
-{%- endtrans %}</li>
-<li>{% trans -%}
-Prior to release 0.9.7, when included in a DatabaseStore Message sent by the originating router,
-the router set all the published leases' expirations to the same value, that of the
-earliest lease. As of release 0.9.7, the router publishes the actual lease expiration for each lease.
-This is an implementation detail and not part of the structures specification.
-{%- endtrans %}</li>
-</ul>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/LeaseSet.html">Javadoc</a></h4>
-
-
-
-<h2 id="struct_RouterAddress">RouterAddress</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-This structure defines the means to contact a router through a transport protocol.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-1 byte <a href="#type_Integer">Integer</a> defining the relative cost of using the address, where 0 is free and 255 is expensive, followed by the expiration <a href="#type_Date">Date</a> after which the address should not be used, or if null, the address never expires.
-After that comes a <a href="#type_String">String</a> defining the transport protocol this router address uses. Finally there is a <a href="#type_Mapping">Mapping</a> containing all of the transport specific options necessary to establish the connection, such as
-IP address, port number, email address, URL, etc.
-{% endtrans %}</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-|cost|           expiration
-+----+----+----+----+----+----+----+----+
-     |        transport_style           |
-+----+----+----+----+-//-+----+----+----+
-|                                       |
-+                                       +
-|               options                 |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-
-cost :: `Integer`
-        length -> 1 byte
-
-        case 0 -> free
-        case 255 -> expensive
-
-expiration :: `Date` (must be all zeros, see notes below)
-              length -> 8 bytes
-
-              case null -> never expires
-
-transport_style :: `String`
-                   length -> 1-256 bytes
-
-options :: `Mapping`
-{% endhighlight %}
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul>
-<li>{% trans -%}
-Cost is typically 5 or 6 for SSU, and 10 or 11 for NTCP.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-Expiration is currently unused, always null (all zeroes).
-As of release 0.9.3, the expiration is assumed zero and not stored, so any non-zero expiration
-will fail in the RouterInfo signature verification.
-Implementing expiration (or another use for these bytes) will be a backwards-incompatible change.
-Routers MUST set this field to all zeros.
-As of release 0.9.12, a non-zero expiration field is again recognized, however we must wait
-several releases to use this field, until the vast majority of the network recognizes it.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-The following options, while not required, are standard and expected to be present in most router addresses:
-"host" (an IPv4 or IPv6 address or host name) and "port".
-{%- endtrans %}</li>
-</ul>
-
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/router/RouterAddress.html">Javadoc</a></h4>
-
-<h2 id="struct_RouterInfo">RouterInfo</h2>
-<h4>{% trans %}Description{% endtrans %}</h4>
-<p>{% trans -%}
-Defines all of the data that a router wants to publish for the network to see. The <a href="#struct_RouterInfo">RouterInfo</a> is one of two structures stored in the network database(the other being <a href="#struct_LeaseSet">LeaseSet</a>, and is keyed under the SHA256 of
-the contained <a href="#struct_RouterIdentity">RouterIdentity</a>.
-{% endtrans %}</p>
-<h4>{% trans %}Contents{% endtrans %}</h4>
-<p>{% trans -%}
-<a href="#struct_RouterIdentity">RouterIdentity</a> followed by the <a href="#type_Date">Date</a>, when the entry was published
-{% endtrans %}</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-| router_ident                          |
-+                                       +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| published                             |
-+----+----+----+----+----+----+----+----+
-|size| RouterAddress 0                  |
-+----+                                  +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| RouterAddress 1                       |
-+                                       +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| RouterAddress ($size-1)               |
-+                                       +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+-//-+----+----+----+
-|psiz| options                          |
-+----+----+----+----+-//-+----+----+----+
-| signature                             |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-
-router_ident :: `RouterIdentity`
-                length -> >= 387 bytes
-
-published :: `Date`
-             length -> 8 bytes
-
-size :: `Integer`
-        length -> 1 byte
-        The number of RouterAddresses to follow, 0-255
-
-addresses :: [`RouterAddress`]
-             length -> varies
-
-peer_size :: `Integer`
-             length -> 1 byte
-             The number of peer Hashes to follow, 0-255, unused, always zero
-             value -> 0
-
-options :: `Mapping`
-
-signature :: `Signature`
-             length -> 40 bytes
-
-{% endhighlight %}
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul>
-<li>{% trans -%}
-The peer_size Integer may be followed by a list of that many router hashes.
-This is currently unused. It was intended for a form of restricted routes, which is unimplemented.
-{%- endtrans %}</li>
-
-<li>{% trans -%}
-The signature may be verified using the signing public key of the router_ident.
-{%- endtrans %}</li>
-
-<li>{% trans netdb=site_url('docs/how/network-database') -%}
-See <a href="{{ netdb }}#routerInfo">the network database page</a>
-for standard options that are expected to be present in all router infos.
-{%- endtrans %}</li>
-</ul>
-
-<h4><a href="http://{{ i2pconv('i2p-javadocs') }}/net/i2p/data/router/RouterInfo.html">Javadoc</a></h4>
-
-
-<h2 id="struct_DeliveryInstructions">Delivery Instructions</h2>
-<p>{% trans tunnelmessage=site_url('docs/spec/tunnel-message') -%}
-Tunnel Message Delivery Instructions are defined in the <a href="{{ tunnelmessage }}#struct_TunnelMessageDeliveryInstructions">Tunnel Message Specification</a>.
-{% endtrans %}</p>
-<p>{% trans i2npmessage=site_url('docs/spec/i2np') -%}
-Garlic Message Delivery Instructions are defined in the <a href="{{ i2npmessage }}#struct_GarlicCloveDeliveryInstructions">I2NP Message Specification</a>.
-{% endtrans %}</p>
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/configuration.html b/i2p2www/pages/site/docs/spec/configuration.html
deleted file mode 100644
index 018ea23bafddb074d9c81cd0c46b19eebf5739a9..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/configuration.html
+++ /dev/null
@@ -1,414 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}{% trans %}Configuration File Specification{% endtrans %}{% endblock %}
-{% block lastupdated %}{% trans %}February 2016{% endtrans %}{% endblock %}
-{% block accuratefor %}0.9.25{% endblock %}
-{% block content %}
-<h2>{% trans %}Overview{% endtrans %}</h2>
-<p>{% trans -%}
-This page provides a general specification of I2P configuration files,
-used by the router and various applications.
-It also gives an overview of the information contained in the various files,
-and links to detailed documentation where available.
-{%- endtrans %}</p>
-
-
-<h2>{% trans %}General Format{% endtrans %}</h2>
-<p>{% trans url='http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Properties.html#load%28java.io.InputStream%29' -%}
-An I2P configuration file is formatted as specified in
-<a href="{{ url }}">Java Properties</a>
-with the following exceptions:
-{%- endtrans %}</p>
-<ul>
-<li>{% trans %}Encoding must be UTF-8{% endtrans %}</li>
-<li>{% trans %}Does not use or recognize any escapes, including '\', so lines may not be continued{% endtrans %}</li>
-<li>{% trans %}'#' or ';' starts a comment, but '!' does not{% endtrans %}</li>
-<li>{% trans %}'#' starts a comment in any position but ';' must be in column 1 to start a comment{% endtrans %}</li>
-<li>{% trans %}Leading and trailing whitespace is not trimmed on keys{% endtrans %}</li>
-<li>{% trans %}Leading and trailing whitespace is trimmed on values{% endtrans %}</li>
-<li>{% trans %}'=' is the only key-termination character (not ':' or whitespace){% endtrans %}</li>
-<li>{% trans %}Lines without '=' are ignored. As of release 0.9.10, keys with a value of "" are supported.{% endtrans %}</li>
-<li>{% trans %}As there are no escapes, keys may not contain '#', '=', or '\n', or start with ';'{% endtrans %}</li>
-<li>{% trans %}As there are no escapes, values may not contain '#' or '\n', or start or end with '\r' or whitespace{% endtrans %}</li>
-</ul>
-
-<p>{% trans -%}
-The file need not be sorted, but most applications do sort by key when
-writing to the file, for ease of reading and manual editing.
-{%- endtrans %}</p>
-
-<p>{% trans url='http://'+i2pconv('i2p-javadocs.i2p')+'/net/i2p/data/DataHelper.html',
-commonstructures=site_url('docs/spec/common-structures') -%}
-Reads and writes are implemented in
-<a href="{{ url }}">DataHelper loadProps() and storeProps()</a>.
-Note that the file format is significantly different than the
-serialized format for I2P protocols specified in
-<a href="{{ commonstructures }}#type_Mapping">Mapping</a>.
-{%- endtrans %}</p>
-
-<h2>{% trans %}Core library and router{% endtrans %}</h2>
-
-<h3>{% trans %}Clients{% endtrans %} (clients.config)</h3>
-<p>{% trans -%}
-Configured via /configclients in the router console.
-{%- endtrans %}</p>
-<p>
-The format is as follows:
-</p><p>
-Lines are of the form clientApp.x.prop=val, where x is the app number.
-App numbers MUST start with 0 and be consecutive.
-</p><p>
-Properties are as follows:
-<pre>
-	main: Full class name. Required. The constructor or main() method in this
-	      class will be run, depending on whether the client is managed or unmanaged.
-              See below for details.
-	name: Name to be displayed on console.
-	args: Arguments to the main class, separated by spaces or tabs.
-	      Arguments containing spaces or tabs may be quoted with ' or "
-	delay: Seconds before starting, default 120
-	onBoot: {true|false}, default false, forces a delay of 0,
-	        overrides delay setting
-	startOnLoad: {true|false} Is the client to be run at all?
-	                   Default true
-</pre>
-</p><p>
-The following additional properties are used only by plugins:
-<pre>
-	stopargs: Arguments to stop the client.
-	uninstallargs: Arguments to uninstall the client.
-	classpath: Additional classpath elements for the client,
-	           separated by commas.
-</pre>
-</p><p>
-The following substitutions are made in the args, stopargs,
-uninstallargs, and classpath lines, for plugins only:
-<pre>
-	$I2P: The base I2P install directory
-	$CONFIG: The user's configuration directory (e.g. ~/.i2p)
-	$PLUGIN: This plugin's directory (e.g. ~/.i2p/plugins/foo)
-</pre>
-</p><p>
-All properties except "main" are optional.
-Lines starting with "#" are comments.
-</p><p>
-If the delay is less than zero, the client is run immediately,
-in the same thread, so that exceptions may be propagated to the console.
-In this case, the client should either throw an exception, return quickly,
-or spawn its own thread.
-If the delay is greater than or equal to zero, it will be run
-in a new thread, and exceptions will be logged but not propagated
-to the console.
-</p>
-<p>
-Clients may be "managed" or "unmanaged".
-</p>
-
-
-<h3>{% trans %}Logger{% endtrans %} (logger.config)</h3>
-<p>{% trans -%}
-Configured via /configlogging in the router console.
-{%- endtrans %}</p>
-<p>
-{% trans -%}
-Properties are as follows:
-{%- endtrans %}
-<pre>
-    # Default 20
-    logger.consoleBufferSize=n
-    # Default from locale; format as specified by Java SimpleDateFormat
-    logger.dateFormat=HH:mm:ss.SSS
-    # Default ERROR
-    logger.defaultLevel=CRIT|ERROR|WARN|INFO|DEBUG
-    # Default true
-    logger.displayOnScreen=true|false
-    # Default true
-    logger.dropDuplicates=true|false
-    # Default false
-    logger.dropOnOverflow=true|false
-    # As of 0.9.18. Default 29 (seconds)
-    logger.flushInterval=nnn
-    # d = date, c = class, t = thread name, p = priority, m = message
-    logger.format={dctpm}*
-    # Max to buffer before flushing. Default 1024
-    logger.logBufferSize=n
-    # Default logs/log-@.txt; @ replaced with number
-    logger.logFileName=name
-    logger.logFilenameOverride=name
-    # Default 10M
-    logger.logFileSize=nnn[K|M|G]
-    # Highest file number. Default 2
-    logger.logRotationLimit=n
-    # Default CRIT
-    logger.minimumOnScreenLevel=CRIT|ERROR|WARN|INFO|DEBUG
-    logger.record.{class}=CRIT|ERROR|WARN|INFO|DEBUG
-</pre>
-</p>
-
-<h3>{% trans %}Individual Plugin{% endtrans %} (xxx/plugin.config)</h3>
-<p>{% trans pluginspec=site_url('docs/spec/plugin') -%}
-See <a href="{{ pluginspec }}">the plugin specification</a>.
-{%- endtrans %}</p>
-
-<h3>{% trans %}Plugins{% endtrans %} (plugins.config)</h3>
-<p>{% trans -%}
-Enable/disable for each installed plugin.
-{%- endtrans %}
-{% trans -%}
-Properties are as follows:
-{%- endtrans %}
-<pre>
-    plugin.{name}.startOnLoad=true|false
-</pre>
-</p>
-
-<h3>{% trans %}Webapps{% endtrans %} (webapps.config)</h3>
-<p>{% trans -%}
-Enable/disable for each installed webapp.
-{%- endtrans %}
-{% trans -%}
-Properties are as follows:
-{%- endtrans %}
-<pre>
-    webapps.{name}.classpath=[space- or comma-separated paths]
-    webapps.{name}.startOnLoad=true|false
-</pre>
-</p>
-
-<h3>{% trans %}Router{% endtrans %} (router.config)</h3>
-<p>{% trans -%}
-Configured via /configadvanced in the router console.
-{%- endtrans %}</p>
-
-<h2>{% trans %}Applications{% endtrans %}</h2>
-
-<h3>{% trans %}Addressbook{% endtrans %} (addressbook/config.txt)</h3>
-<p>{% trans -%}
-See documentation in SusiDNS.
-{%- endtrans %}</p>
-
-<h3>I2PSnark (i2psnark.config)</h3>
-<p>{% trans -%}
-Configured via the application gui.
-{%- endtrans %}</p>
-
-<h3>I2PTunnel (i2ptunnel.config)</h3>
-<p>{% trans -%}
-Configured via the /i2ptunnel application in the router console.
-{%- endtrans %}
-{% trans -%}
-Properties are as follows:
-{%- endtrans %}
-<pre>
-# Display description for UI
-tunnel.N.description=
-
-# Router IP address or host name. Ignored if in router context.
-tunnel.N.i2cpHost=127.0.0.1
-
-# Router I2CP port. Ignored if in router context.
-tunnel.N.i2cpPort=nnnn
-
-# For clients only. Local listen IP address or host name.
-tunnel.N.interface=127.0.0.1
-
-# For clients only. Local listen port.
-tunnel.N.listenPort=nnnn
-
-# Display name for UI
-tunnel.N.name=
-
-# Servers only. Default false. Originate connections to local server with a unique IP per-remote-destination.
-tunnel.N.option.enableUniqueLocal=true|false
-
-# Servers only. The maximum size of the thread pool, default 65. Ignored for standard servers.
-tunnel.N.option.i2ptunnel.blockingHandlerCount=nnn
-
-# HTTP client only. Whether to use allow SSL connections to i2p addresses. Default false.
-tunnel.N.option.i2ptunnel.httpclient.allowInternalSSL=true|false
-
-# HTTP client only. Whether to disable address helper links. Default false.
-tunnel.N.option.i2ptunnel.httpclient.disableAddressHelper=true|false
-
-# HTTP client only. Comma- or space-separated list of jump server URLs.
-tunnel.N.option.i2ptunnel.httpclient.jumpServers=http://example.i2p/jump
-
-# HTTP client only. Whether to pass Accept* headers through. Default false.
-tunnel.N.option.i2ptunnel.httpclient.sendAccept=true|false
-
-# HTTP client only. Whether to pass Referer headers through. Default false.
-tunnel.N.option.i2ptunnel.httpclient.sendReferer=true|false
-
-# HTTP client only. Whether to pass User-Agent headers through. Default false.
-tunnel.N.option.i2ptunnel.httpclient.sendUserAgent=true|false
-
-# HTTP client only. Whether to pass Via headers through. Default false.
-tunnel.N.option.i2ptunnel.httpclient.sendVia=true|false
-
-# HTTP client only. Comma- or space-separated list of in-network SSL outproxies.
-tunnel.N.option.i2ptunnel.httpclient.SSLOutproxies=example.i2p
-
-# SOCKS client only. Comma- or space-separated list of in-network outproxies for any ports not specified.
-tunnel.N.option.i2ptunnel.socks.proxy.default=example.i2p
-
-# SOCKS client only. Comma- or space-separated list of in-network outproxies for port NNNN.
-tunnel.N.option.i2ptunnel.socks.proxy.NNNN=example.i2p
-
-# HTTP client only. Whether to use a registered local outproxy plugin. Default true.
-tunnel.N.option.i2ptunnel.useLocalOutproxy=true|false
-
-# Servers only. Whether to use a thread pool. Default true. Ignored for standard servers, always false.
-tunnel.N.option.i2ptunnel.usePool=true|false
-
-# IRC Server only. Only used if fakeHostname contains a %c.
-# If unset, cloak with a random value that is persistent for
-# the life of this tunnel. If set, cloak with the hash of this passphrase.
-# Use to have consistent mangling across restarts, or for multiple IRC servers cloak consistently to
-# be able to track users even when they switch servers.
-# Note: don't quote or put spaces in the passphrase, the i2ptunnel gui can't handle it.
-tunnel.N.option.ircserver.cloakKey=
-
-# IRC Server only. Set the fake hostname sent by I2PTunnel,
-# %f is the full B32 destination hash, %c is the cloaked hash.
-tunnel.N.option.ircserver.fakeHostname=%f.b32.i2p
-
-# IRC Server only. Default user.
-tunnel.N.option.ircserver.method=user|webirc
-
-# IRC Server only. The password to use for the webirc protocol.
-# Note: don't quote or put spaces in the passphrase, the i2ptunnel gui can't handle it.
-tunnel.N.option.ircserver.webircPassword=
-
-# IRC Server only.
-tunnel.N.option.ircserver.webircSpoofIP=
-
-# For clients only. Alias for the the private key in the keystore for the SSL socket.
-# Will be autogenerated if a new key is created.
-tunnel.N.option.keyAlias=
-
-# For clients only. Password for the private key for the SSL socket.
-# Will be autogenerated if a new key is created.
-tunnel.N.option.keyPassword=
-
-# For clients only. Path to the keystore file containing the private key for the SSL socket.
-# Will be autogenerated if a new keystore is created.
-# Relative to $(I2P_CONFIG_DIR)/keystore/ if not absolute.
-tunnel.N.option.keystoreFile=i2ptunnel-(random string).ks
-
-# For clients only. Password for the keystore containing the private key for the SSL socket.
-# Default is "changeit"
-tunnel.N.option.keystorePassword=changeit
-
-# HTTP Server only. Max number of POSTs allowed for one destination per postCheckTime. Default 0 (unlimited)
-tunnel.N.option.maxPosts=nnn
-
-# HTTP Server only. Max number of POSTs allowed for all destinations per postCheckTime. Default 0 (unlimited)
-tunnel.N.option.maxTotalPosts=nnn
-
-# HTTP Clients only. Whether to send authorization to an outproxy. Default false.
-tunnel.N.option.outproxyAuth=true|false
-
-# HTTP Clients only. The password for the outproxy authorization.
-tunnel.N.option.outproxyPassword=
-
-# HTTP Clients only. The username for the outproxy authorization.
-tunnel.N.option.outproxyUsername=
-
-# HTTP Clients only. Whether to send authorization to an outproxy. Default false.
-tunnel.N.option.outproxyAuth=true|false
-
-# Clients only. Whether to store a destination in a private key file and reuse it. Default false.
-tunnel.N.option.persistentClientKey=true|false
-
-# HTTP Server only. Time period for banning POSTs from a single destination after maxPosts is exceeded, in seconds. Default 1800 seconds.
-tunnel.N.option.postBanTime=nnn
-
-# HTTP Server only. Time period for checking maxPosts and maxTotalPosts, in seconds. Default 300 seconds.
-tunnel.N.option.postCheckTime=nnn
-
-# HTTP Server only. Time period for banning all POSTs after maxTotalPosts is exceeded, in seconds. Default 600 seconds.
-tunnel.N.option.postTotalBanTime=nnn
-
-# HTTP Clients only. Whether to require local authorization for the proxy. Default false. "true" is the same as "basic".
-tunnel.N.option.proxyAuth=true|false|basic|digest
-
-# HTTP Clients only. The MD5 of the password for local authorization for user USER.
-tunnel.N.option.proxy.auth.USER.md5=
-
-# HTTP Servers only. Whether to reject incoming connections apparently via an inproxy. Default false.
-tunnel.N.option.rejectInproxy=true|false
-
-# HTTP Servers only. Whether to reject incoming connections containing a referer header. Default false. Since 0.9.25.
-tunnel.N.option.rejectReferer=true|false
-
-# HTTP Servers only. Whether to reject incoming connections containing specific user-agent headers. Default false. Since 0.9.25.
-# See tunnel.N.option.userAgentRejectList
-tunnel.N.option.rejectUserAgents=true|false
-
-# Servers only. Overrides targetHost and targetPort for incoming port NNNN.
-tunnel.N.option.targetForPort.NNNN=hostnameOrIP:nnnn
-
-# HTTP Servers only. Comma-separated list of strings to match in the user-agent header. Since 0.9.25.
-# Example: "Mozilla,Opera". Case-sensitive. See tunnel.N.option.rejectuserAgents
-tunnel.N.option.userAgentRejectList=string1[,string2]*
-
-# Default false. For servers, use SSL for connections to local server.
-# For clients, SSL is required for connections from local clients.
-tunnel.N.option.useSSL=false
-
-# Each option is passed to I2CP and streaming with "tunnel.N.option." stripped off. See those docs.
-tunnel.N.option.*=
-
-# For servers and clients with persistent keys only. Absolute path or relative to config directory.
-tunnel.N.privKeyFile=filename
-
-# For proxies only. Comma- or space-separated host names.
-tunnel.N.proxyList=example.i2p[,example2.i2p]
-
-# For clients only. Default false.
-tunnel.N.sharedClient=true|false
-
-# For HTTP servers only. Host name to be passed to the local server in the HTTP headers.
-# Default is the base 32 hostname.
-tunnel.N.spoofedHost=example.i2p
-
-# For HTTP servers only. Host name to be passed to the local server in the HTTP headers.
-# Overrides above setting for incoming port NNNN, to allow virtual hosts.
-tunnel.N.spoofedHost.NNNN=example.i2p
-
-# Default true
-tunnel.N.startOnLoad=true|false
-
-# For clients only. Comma- or space-separated host names or host:port.
-tunnel.N.targetDestination=example.i2p[:nnnn][,example2.i2p[:nnnn]]
-
-# For servers only. Local IP address or host name to connect to.
-tunnel.N.targetHost=
-
-# For servers only. Port on targetHost to connect to.
-tunnel.N.targetPort=nnnn
-
-# The type of i2ptunnel
-tunnel.N.type=client|connectclient|httpbidirserver|httpclient|httpserver|ircclient|ircserver|
-              server|socksirctunnel|sockstunnel|streamrclient|streamrserver
-
-</pre>
-Note: Each 'N' is a tunnel number starting with 0.
-There may not be any gaps in numbering.
-</p>
-
-<h3>{% trans %}Router Console{% endtrans %}</h3>
-<p>{% trans -%}
-The router console uses the router.config file.
-{%- endtrans %}</p>
-
-<h3>SusiMail (susimail.config)</h3>
-<p>{% trans -%}
-See post on zzz.i2p.
-{%- endtrans %}</p>
-<!--
-<h3>Systray (systray.config)</h3>
-<p>
-TBD
-</p>
--->
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/datagrams.html b/i2p2www/pages/site/docs/spec/datagrams.html
deleted file mode 100644
index 93c5d63c9e70f452987467d50913f6c9db58ed7b..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/datagrams.html
+++ /dev/null
@@ -1,109 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}{% trans %}Datagram Specification{% endtrans %}{% endblock %}
-{% block lastupdated %}{% trans %}July 2014{% endtrans %}{% endblock %}
-{% block accuratefor %}0.9.14{% endblock %}
-{% block content %}
-<p><a href="{{ site_url('docs/api/datagrams') }}">{% trans -%}
-See the Datagrams page for an overview of the Datagrams API.
-{%- endtrans %}</a></p>
-
-<h2 id="spec">{% trans %}Specification{% endtrans %}</h2>
-
-<h3 id="raw">{% trans %}Non-Repliable Datagrams{% endtrans %}</h3>
-<p>{% trans -%}
-Non-repliable datagrams have no 'from' address and are not authenticated.
-They are also called "raw" datagrams.
-Strictly speaking, they are not "datagrams" at all, they are just raw data.
-They are not handled by the datagram API.
-However, SAM and the I2PTunnel classes support "raw datagrams".
-{%- endtrans %}</p>
-
-<h4>{% trans %}Format{% endtrans %}</h4>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----//
-| payload...
-+----+----+----+----+----//
-
-length: 0 - unlimited (see notes)
-{% endhighlight %}
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<p>{% trans tunnelmessage=site_url('docs/spec/tunnel-message'),
-transports=site_url('docs/transport') -%}
-The practical length is limited by lower layers of protocols - the
-<a href="{{ tunnelmessage }}#notes">tunnel message spec</a>
-limits messages to about 61.2 KB and the
-<a href="{{ transports }}">transports</a>
-currently limit messages to about 32 KB, although this may be raised in the future.
-{%- endtrans %}</p>
-
-<h3 id="repliable">{% trans %}Repliable Datagrams{% endtrans %}</h3>
-<p>{% trans -%}
-Repliable datagrams contain a 'from' address and a signature. These add at least 427 bytes of overhead.
-{%- endtrans %}</p>
-<h4>Format</h4>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-| from                                  |
-+                                       +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+                                       +
-|                                       |
-|                                       |
-+----+----+----+----+----+----+----+----+
-| signature                             |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-| payload...
-+----+----+----+----//
-
-
-from :: a `Destination`
-             length: 387+ bytes
-             The originator and signer of the datagram
-
-signature :: a `Signature`
-             Signature type must match the signing public key type of $from
-             length: 40+ bytes, as implied by the Signature type.
-             For the default DSA_SHA1 key type:
-                The DSA `Signature` of the SHA-256 hash of the payload.
-             For other key types:
-                The `Signature` of the payload.
-             The signature may be verified by the signing public key of $from
-
-payload ::  The data
-            Length: 0 to ~31.5 KB (see notes)
-
-Total length: Payload length + 427+
-{% endhighlight %}
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<ul>
-<li>{% trans transports=site_url('docs/transport') -%}
-The practical length is limited by lower layers of protocols - the
-<a href="{{ transports }}">transports</a>
-currently limit messages to about 32 KB, so the data length here is limited to about
-31.5 KB.
-{%- endtrans %}</li>
-<li>{% trans dgrams=site_url('docs/api/datagrams') -%}
-See <a href="{{ dgrams }}">important notes</a> about the reliability of large datagrams.
-For best results, limit the payload to about 10 KB or less.
-{%- endtrans %}</li>
-<li>{% trans -%}
-Signatures for types other than DSA_SHA1 were redefined in release 0.9.14.
-{%- endtrans %}</li>
-</ul>
-
-
-
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/geoip.html b/i2p2www/pages/site/docs/spec/geoip.html
deleted file mode 100644
index dc452f4a651739adbd86fc0d7d82615692649973..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/geoip.html
+++ /dev/null
@@ -1,81 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}{% trans %}GeoIP File Specification{% endtrans %}{% endblock %}
-{% block lastupdated %}{% trans %}December 2013{% endtrans %}{% endblock %}
-{% block accuratefor %}0.9.9{% endblock %}
-{% block content %}
-
-<h2>{% trans %}Overview{% endtrans %}</h2>
-<p>{% trans -%}
-This page specifies the format of the various GeoIP files,
-used by the router to look up a country for an IP.
-{%- endtrans %}</p>
-
-
-<h2>{% trans %}Country Name (countries.txt) Format{% endtrans %}</h2>
-<p>{% trans -%}
-This format is easily generated from data files available from many public sources.
-For example:
-{%- endtrans %}</p>
-{% highlight lang='bash' %}
-$ wget http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip
-$ unzip GeoIPCountryCSV.zip
-$ cut -d, -f5,6 < GeoIPCountryWhois.csv |sed 's/"//g' | sort | uniq > countries.txt
-{% endhighlight %}
-<ul>
-<li>Encoding is UTF-8
-<li>'#' in column 1 specifies a comment line
-<li>Entry lines are CountryCode,CountryName
-<li>CountryCode is the ISO two-letter code, upper case
-<li>CountryName is in English
-</ul>
-
-
-<h2>{% trans %}IPv4 (geoip.txt) Format{% endtrans %}</h2>
-<p>{% trans -%}
-This format is borrowed from Tor and is easily generated from data files available from many public sources.
-For example:
-{%- endtrans %}</p>
-{% highlight lang='bash' %}
-$ wget http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip
-$ unzip GeoIPCountryCSV.zip
-$ cut -d, -f3-5 < GeoIPCountryWhois.csv|sed 's/"//g' > geoip.txt
-$ cut -d, -f5,6 < GeoIPCountryWhois.csv |sed 's/"//g' | sort | uniq > countries.txt
-{% endhighlight %}
-<ul>
-<li>Encoding is ASCII
-<li>'#' in column 1 specifies a comment line
-<li>Entry lines are FromIP,ToIP,CountryCode
-<li>FromIP and ToIP are unsigned integer representations of the 4-byte IP
-<li>CountryCode is the ISO two-letter code, upper case
-<li>Entry lines must be sorted by numeric FromIP
-</ul>
-
-
-<h2>{% trans %}IPv6 (geoipv6.dat.gz) Format{% endtrans %}</h2>
-<p>{% trans -%}
-This is a compressed binary format designed for I2P.
-The file is gzipped. Ungzipped format:
-{%- endtrans %}</p>
-{% highlight %}
-   Bytes 0-9: Magic number "I2PGeoIPv6"
-   Bytes 10-11: Version (0x0001)
-   Bytes 12-15 Options (0x00000000) (future use)
-   Bytes 16-23: Creation date (ms since 1970-01-01)
-   Bytes 24-xx: Optional comment (UTF-8) terminated by zero byte
-   Bytes xx-255: null padding
-   Bytes 256-: 18 byte records:
-       8 byte from (/64)
-       8 byte to (/64)
-       2 byte ISO country code LOWER case (ASCII)
-{% endhighlight %}
-<p>{% trans %}NOTES:{% endtrans %}</p>
-<ul>
-<li>Data must be sorted (SIGNED long twos complement), no overlap.
-So the order is 80000000 ... FFFFFFFF 00000000 ... 7FFFFFFF.
-<li>
-The GeoIPv6.java class contains a program to generate this format from
-public sources such as the Maxmind GeoLite data.
-<li>
-IPv6 GeoIP lookup is supported as of release 0.9.8.
-</ul>
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/i2cp.html b/i2p2www/pages/site/docs/spec/i2cp.html
deleted file mode 100644
index deab2c2f5b451e3e991896e5c474af326a9b128c..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/i2cp.html
+++ /dev/null
@@ -1,1467 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}I2CP Specification{% endblock %}
-{% block lastupdated %}June 2015{% endblock %}
-{% block accuratefor %}0.9.21{% endblock %}
-{% block content %}
-<h2>Overview</h2>
-<p>
-This page specified the I2P Control Protocol (I2CP),
-which is the interface between clients and the router.
-Java clients will use the I2CP client API, which implements this protocol.
-Non-Java clients will most likely use a higher-layer protocol
-such as SAM or BOB.
-</p><p>
-The protocol is only serialized if the client and router are not in the same JVM;
-otherwise, I2CP message objects are passed via an internal JVM interface.
-</p><p>
-More information is on the <a href="{{ site_url('docs/protocol/i2cp') }}">I2CP Overview page</a>.
-</p>
-
-
-
-<h2>Sessions</h2>
-<p>
-The protocol was designed to handle multiple "sessions", each with a 2-byte session ID,
-over a single TCP connection,
-however, Multiple sessions were not implemented until version 0.9.21.
-See the <a href="#multisession">multisession section below</a>.
-Do not attempt to use multiple sessions on a single I2CP connection
-with routers older than version 0.9.21.
-</p>
-
-<p>
-It also appears that there are some provisions for a single client to talk to multiple
-routers over separate connections. This is also untested,
-and probably not useful.
-</p>
-
-<p>
-It does not appear that there is currently a way for a session to be
-maintained after a disconnect, or to be
-recovered on a different
-I2CP connection.
-</p>
-
-
-
-
-<h2>Example Message Sequences</h2>
-<p>
-Note: The examples below do not show the Protocol Byte (0x2a) that must be
-sent from the client to the router when first connecting.
-More information about connection initialization is
-on the <a href="{{ site_url('docs/protocol/i2cp') }}">I2CP Overview page</a>.
-</p>
-
-
-<h3>Standard Session Establish</h3>
-{% highlight %}
-    Client                                           Router
-
-                           --------------------->  Get Date Message
-        Set Date Message  <---------------------
-                           --------------------->  Create Session Message
-  Session Status Message  <---------------------
-Request LeaseSet Message  <---------------------
-                           --------------------->  Create LeaseSet Message
-{% endhighlight %}
-
-
-<h3>Get Bandwidth Limits (Simple Session)</h3>
-{% highlight %}
-    Client                                           Router
-
-                           --------------------->  Get Bandwidth Limits Message
-Bandwidth Limits Message  <---------------------
-{% endhighlight %}
-
-
-
-<h3>Destination Lookup (Simple Session)</h3>
-{% highlight %}
-    Client                                           Router
-
-                           --------------------->  Dest Lookup Message
-      Dest Reply Message  <---------------------
-{% endhighlight %}
-
-
-
-<h3>Outgoing Message</h3>
-<p>Existing session, with i2cp.messageReliability=none</p>
-{% highlight %}
-    Client                                           Router
-
-                           --------------------->  Send Message Message
-
-{% endhighlight %}
-
-<p>Existing session, with i2cp.messageReliability=none and nonzero nonce</p>
-{% highlight %}
-    Client                                           Router
-
-                           --------------------->  Send Message Message
-  Message Status Message  <---------------------
-  (succeeded)
-{% endhighlight %}
-
-<p>Existing session, with i2cp.messageReliability=BestEffort</p>
-{% highlight %}
-    Client                                           Router
-
-                           --------------------->  Send Message Message
-  Message Status Message  <---------------------
-  (accepted)
-  Message Status Message  <---------------------
-  (succeeded)
-{% endhighlight %}
-
-
-
-<h3>Incoming Message</h3>
-<p>Existing session, with i2cp.fastReceive=true (as of 0.9.4)</p>
-{% highlight %}
-    Client                                           Router
-
- Message Payload Message  <---------------------
-
-{% endhighlight %}
-<p>Existing session, with i2cp.fastReceive=false</p>
-{% highlight %}
-    Client                                           Router
-
-  Message Status Message  <---------------------
-  (available)
-                           --------------------->  Receive Message Begin Message
- Message Payload Message  <---------------------
-                           --------------------->  Receive Message End Message
-{% endhighlight %}
-
-
-
-
-<h2 id="multisession">Multisession Notes</h2>
-<p>
-Multiple sessions on a single I2CP connection are supported as of router version 0.9.21.
-The first session that is created is the "primary session".
-Additional sessions are "subsessions".
-Subsessions are used to support multiple destinations sharing a common set of tunnels.
-The initial application is for the primary session to use ECDSA signing keys,
-while the subsession uses DSA signing keys for communication with old eepsites.
-</p><p>
-Subsessions share the same inbound and outbound tunnel pools as the primary session.
-Subsessions must use the same encryption keys as the primary session.
-This applies both to the LeaseSet encryption keys and the (unused) Destination encryption keys.
-Subsessions must use different signing keys in the destination, so the destination hash is
-different from the primary session.
-As subsessions use the same encryption keys and tunnels as the primary session,
-it is apparent to all that the Destinations are running on the same router,
-so the usual anti-correlation anonymity guarantees do not apply.
-</p><p>
-Subsessions are created by sending a CreateSession message and receiving a SessionStatus message
-in reply, as usual. Subsessions must be created after the primary session is created.
-The SessionStatus response will, on success, contain a unique Session ID, distinct from
-the ID for the primary session.
-While CreateSession messages should be processed in-order,
-there is no sure way to correlate a CreateSession message with the response, so a client
-should not have multiple CreateSession messages outstanding simultaneously.
-SessionConfig options for the subsession may not be honored where they
-are different from the primary session.
-In particular, since subsessions use the same tunnel pool as the primary session,
-tunnel options may be ignored.
-</p><p>
-The router will send separate RequestVariableLeaseSet messages for each Destination to the client,
-and the client must reply with a CreateLeaseSet message for each.
-The leases for the two Destinations will not necessarily be identical,
-even though they are selected from the same tunnel pool.
-</p><p>
-A subsession may be destroyed with the DestroySession message as usual.
-This will not destroy the primary session or stop the I2CP connection.
-Destroying the primary session will, however, destroy all subsessions and
-stop the I2CP connection.
-A Disconnect message destroys all sessions.
-</p><p>
-Note that most, but not all, I2CP messages contain a Session ID.
-For the ones that do not, clients may need additional logic to
-properly handle router responses.
-DestLookup and DestReply do not contain Session IDs; use the newer HostLookup and HostReply instead.
-GetBandwidthLimts and BandwidthLimits do not contain session IDs, however the response
-is not session-specific.
-</p><p>
-Support for multiple sessions is preliminary and subject to change.
-Support may not be complete in other parts of the API and user interface, particularly streaming and i2ptunnel.
-Current support is primarily for clients (i.e. Destinations that do not publish their leaseset
-or accept incoming connections), and is incomplete and untested for servers.
-Future releases may provide additional features and options.
-</p>
-
-
-
-
-<h2 id="notes">Version Notes</h2>
-<p>
-The initial protocol version byte (0x2a) sent by the client is not expected to change.
-Prior to release 0.8.7, the router's version information was not available to the client,
-thus preventing new clients from working with old routers.
-As of release 0.8.7, the two parties' protocol version strings are exchanged in the Get/Set Date Messages.
-Going forward, clients may use this information to communicate correctly with old routers.
-Clients and routers should not send messages that are unsupported by the other side,
-as they generally disconnect the session upon reception of an unsupported message.
-</p><p>
-The exchanged version information is the "core" version or I2CP protocol version,
-and is not necessarily the router version.
-</p><p>
-A basic summary of the I2CP protocol versions is as follows. For details, see below.
-<table border=1>
-  <tr>
-    <th>Version</th>
-    <th>Required I2CP Features</th>
-  </tr><tr>
-    <td align="center">0.9.21</td>
-    <td align="left">Multiple sessions on a single I2CP connection supported</td>
-    </td>
-  </tr><tr>
-    <td align="center">0.9.20</td>
-    <td align="left">Additional SetDate messages may be sent to the client at any time</td>
-    </td>
-  </tr><tr>
-    <td align="center">0.9.16</td>
-    <td align="left">Authentication, if enabled, is required via GetDate before all other messages</td>
-    </td>
-  </tr><tr>
-    <td align="center">0.9.15</td>
-    <td align="left">Dest/LS key certs w/ EdDSA Ed25519 sig type supported</td>
-  </tr><tr>
-    <td align="center">0.9.14</td>
-    <td align="left">Per-message override of messageReliability=none with nonzero nonce</td>
-  </tr><tr>
-    <td align="center">0.9.12</td>
-    <td align="left">Dest/LS key certs w/ ECDSA P-256, P-384, and P-521 sig types supported<br>
-                     Note: RSA sig types also supported as of this version, but currently unused
-    </td>
-  </tr><tr>
-    <td align="center">0.9.11</td>
-    <td align="left">Host Lookup and Host Reply messages supported<br>
-                     Authentication mapping in Get Date message supported
-    </td>
-  </tr><tr>
-    <td align="center">0.9.7</td>
-    <td align="left">Request Variable Lease Set message supported</td>
-  </tr><tr>
-    <td align="center">0.9.5</td>
-    <td align="left">Additional Message Status codes defined</td>
-  </tr><tr>
-    <td align="center">0.9.4</td>
-    <td align="left">Send Message nonce=0 allowed<br>
-                     Fast receive mode is the default
-    </td>
-  </tr><tr>
-    <td align="center">0.9.2</td>
-    <td align="left">Send Message Expires flag tag bits supported</td>
-  </tr><tr>
-    <td align="center">0.9</td>
-    <td align="left">Supports up to 16 leases in a lease set (6 previously)</td>
-  </tr><tr>
-    <td align="center">0.8.7</td>
-    <td align="left">Get Date and Set Date version strings included.<br>
-                     If not present, the client or router is version 0.8.6 or older.
-    </td>
-  </tr><tr>
-    <td align="center">0.8.4</td>
-    <td align="left">Send Message Expires flag bits supported</td>
-  </tr><tr>
-    <td align="center">0.8.3</td>
-    <td align="left">Dest Lookup and Get Bandwidth messages supported in standard session<br>
-                     Concurrent Dest Lookup messages supported
-    </td>
-  </tr><tr>
-    <td align="center">0.8.1</td>
-    <td align="left">i2cp.messageReliability=none supported</td>
-  </tr><tr>
-    <td align="center">0.7.2</td>
-    <td align="left">Get Bandwidth Limits and Bandwidth Limits messages supported</td>
-  </tr><tr>
-    <td align="center">0.7.1</td>
-    <td align="left">Send Message Expires message supported<br>
-                     Reconfigure Session message supported
-    </td>
-  </tr><tr>
-    <td align="center">0.7</td>
-    <td align="left">Dest Lookup and Dest Reply messages supported</td>
-  </tr><tr>
-    <td align="center">0.6.5 or lower</td>
-    <td align="left">All messages and features not listed above</td>
-  </tr>
-</table>
-
-
-<h2 id="structures">Common structures</h2>
-
-
-
-<h3 id="struct_I2CPMessageHeader">I2CP message header</h3>
-<h4>Description</h4>
-<p>
-  Common header to all I2CP messages, containing the message length and message type.
-</p>
-<h4>Contents</h4>
-<ol><li>
-  4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> specifying the length of the message body
-</li><li>
-  1 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> specifying the message type.
-</li><li>
- The I2CP message body, 0 or more bytes
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-Actual message length limit is about 64 KB.
-</p>
-
-
-
-
-<h3 id="struct_MessageId">Message ID</h3>
-<h4>Description</h4>
-<p>
-Uniquely identifies a message waiting on a particular router at a
-point in time.
-This is always generated by the router and is NOT the same as the nonce generated by the client.
-</p>
-<h4>Contents</h4>
-<ol><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-Message IDs are unique within a session only; they are not globally unique.
-</p>
-
-
-
-
-<h3 id="struct_Payload">Payload</h3>
-<h4>Description</h4>
-<p>
-This structure is the content of a message being delivered from one
-Destination to another.
-</p>
-<h4>Contents</h4>
-<ol><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>  length
-</li><li>
-That many bytes
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-The payload is in a gzip format as specified on the
-<a href="{{ site_url('docs/protocol/i2cp') }}#format">I2CP Overview page</a>.
-</p>
-
-
-
-
-<h3 id="struct_SessionConfig">Session Config</h3>
-<h4>Description</h4>
-<p>
-Defines the configuration options for a particular client session.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#struct_Destination">Destination</a>
-</li><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#type_Mapping">Mapping</a> of options
-</li><li>
-Creation <a href="{{ site_url('docs/spec/common-structures') }}#type_Date">Date</a>
-</li><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#type_Signature">Signature</a> of the previous 3 fields, signed by the
-<a href="{{ site_url('docs/spec/common-structures') }}#type_SigningPrivateKey">Signing Private Key</a>
-</li></ol>
-
-<h4>Notes</h4>
-<ul><li>
-The options are specified on the
-<a href="{{ site_url('docs/protocol/i2cp') }}#options">I2CP Overview page</a>.
-</li><li>
-The <a href="{{ site_url('docs/spec/common-structures') }}#type_Mapping">Mapping</a>
-must be sorted by key so that the signature will be validated correctly in the router.
-</li><li>
-The creation date must be within +/- 30 seconds of the current time when processed by
-the router, or the config will be rejected.
-</li></ul>
-
-
-
-
-<h3 id="struct_SessionId">Session ID</h3>
-<h4>Description</h4>
-<p>
-Uniquely identifies a session on a particular router at a point in
-time.
-</p>
-<h4>Contents</h4>
-<ol><li>
-2 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-</p>
-
-
-
-
-<h2 id="messages">Messages</h2>
-See also the
-<a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/i2cp/package-summary.html">I2CP Javadocs</a>.
-<h3 id="types">Message Types</h3>
-<table border=1>
-<tr><th>Message<th>Direction<th>Type<th>Since
-<tr><td>
-<a href="#msg_BandwidthLimits">
-BandwidthLimitsMessage
-</a>
-</a>
-<td align="center">R-&gt;C
-<td align=right>23
-<td align="center">0.7.2
-<tr><td>
-<a href="#msg_CreateLeaseSet">
-CreateLeaseSetMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>4
-<td>&nbsp;
-<tr><td>
-<a href="#msg_CreateSession">
-CreateSessionMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>1
-<td>&nbsp;
-<tr><td>
-<a href="#msg_DestLookup">
-DestLookupMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>34
-<td align="center">0.7
-<tr><td>
-<a href="#msg_DestReply">
-DestReplyMessage
-</a>
-<td align="center">R-&gt;C
-<td align=right>35
-<td align="center">0.7
-<tr><td>
-<a href="#msg_DestroySession">
-DestroySessionMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>3
-<td>&nbsp;
-<tr><td>
-<a href="#msg_Disconnect">
-DisconnectMessage
-</a>
-<td align="center">bidir.
-<td align=right>30
-<td>&nbsp;
-<tr><td>
-<a href="#msg_GetBandwidthLimits">
-GetBandwidthLimitsMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>8
-<td align="center">0.7.2
-<tr><td>
-<a href="#msg_GetDate">
-GetDateMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>32
-<td>&nbsp;
-<tr><td>
-<a href="#msg_HostLookup">
-HostLookupMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>38
-<td align="center">0.9.11
-<tr><td>
-<a href="#msg_HostReply">
-HostReplyMessage
-</a>
-<td align="center">R-&gt;C
-<td align=right>39
-<td align="center">0.9.11
-<tr><td>
-<a href="#msg_MessagePayload">
-MessagePayloadMessage
-</a>
-<td align="center">R-&gt;C
-<td align=right>31
-<td>&nbsp;
-<tr><td>
-<a href="#msg_MessageStatus">
-MessageStatusMessage
-</a>
-<td align="center">R-&gt;C
-<td align=right>22
-<td>&nbsp;
-<tr><td>
-<a href="#msg_ReceiveMessageBegin">
-ReceiveMessageBeginMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>6
-<td>&nbsp;
-<tr><td>
-<a href="#msg_ReceiveMessageEnd">
-ReceiveMessageEndMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>7
-<td>&nbsp;
-<tr><td>
-<a href="#msg_ReconfigureSession">
-ReconfigureSessionMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>2
-<td align="center">0.7.1
-<tr><td>
-<a href="#msg_ReportAbuse">
-ReportAbuseMessage
-</a>
-<td align="center">bidir.
-<td align=right>29
-<td>&nbsp;
-<tr><td>
-<a href="#msg_RequestLeaseSet">
-RequestLeaseSetMessage
-</a>
-<td align="center">R-&gt;C
-<td align=right>21
-<td>&nbsp;
-<tr><td>
-<a href="#msg_RequestVariableLeaseSet">
-RequestVariableLeaseSetMessage
-</a>
-<td align="center">R-&gt;C
-<td align=right>37
-<td align="center">0.9.7
-<tr><td>
-<a href="#msg_SendMessage">
-SendMessageMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>5
-<td>&nbsp;
-<tr><td>
-<a href="#msg_SendMessageExpires">
-SendMessageExpiresMessage
-</a>
-<td align="center">C-&gt;R
-<td align=right>36
-<td align="center">0.7.1
-<tr><td>
-<a href="#msg_SessionStatus">
-SessionStatusMessage
-</a>
-<td align="center">R-&gt;C
-<td align=right>20
-<td>&nbsp;
-<tr><td>
-<a href="#msg_SetDate">
-SetDateMessage
-</a>
-<td align="center">R-&gt;C
-<td align=right>33
-<td>&nbsp;
-</table>
-
-
-
-<h3 id="msg_BandwidthLimits">Bandwidth Limits</h3>
-<h4>Description</h4>
-<p>
-Tell the client what the bandwidth limits are.
-Sent from Router to Client in response to a Get Bandwidth Limits Message.
-</p>
-<h4>Contents</h4>
-<ol><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>
-Client inbound limit (KBps)
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>
-Client outbound limit (KBps)
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>
-Router inbound limit (KBps)
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>
-Router inbound burst limit (KBps)
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>
-Router outbound limit (KBps)
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>
-Router outbound burst limit (KBps)
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>
-Router burst time (seconds)
-</li><li>
-Nine 4-byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integers</a>
-undefined
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-Currently, the client limits are the only values set,
-and are actually the router limits. All the values labeled as router limits are always 0.
-As of release 0.7.2.
-</p>
-
-
-
-<h3 id="msg_CreateLeaseSet">Create Lease Set</h3>
-<h4>Description</h4>
-<p>
-This message is sent in response to a
-<a href="#msg_RequestLeaseSet">Request Lease Set Message</a>
-or
-<a href="#msg_RequestVariableLeaseSet">Request Variable Lease Set Message</a>
-and contains all
-of the Lease structures that should be published to the I2NP Network Database.
-Sent from Client to Router.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#type_SigningPrivateKey">Signing Private Key</a>
-</li><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#type_PrivateKey">Private Key</a>
-</li><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#struct_LeaseSet">LeaseSet</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-The SigningPrivateKey matches the SigningPublicKey from within the
-LeaseSet,   as does the PrivateKey with the PublicKey. The Signing key is
-necessary to allow the router to revoke the LeaseSet if the client goes offline,
-and the encryption key is necessary for decrypting garlic routed messages. The
-LeaseSet granted may include Lease structures for tunnels pointing at another
-router if the client is actively connected to multiple routers with Leases granted
-to each.
-Really?
-Revocation is unimplemented.
-Connection to multiple routers is untested.
-</p>
-
-
-
-<h3 id="msg_CreateSession">Create Session</h3>
-<h4>Description</h4>
-<p>
-This message is sent from a client to initiate a session, where a session is defined
-as a single Destination's connection to the network, to which all messages for
-that Destination will be delivered and from which all messages that
-Destination sends to any other Destination will be sent through.
-Sent from Client to Router.
-The router responds with a <a href="#msg_SessionStatus">Session Status Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionConfig">Session Config</a>
-</li></ol>
-
-<h4>Notes</h4>
-<ul><li>
-This is the second message sent by the client. Previously the client sent a Get Date Message and received a Set Date Message response.
-</li><li>
-If the Date in the Session Config is too far (more than +/- 30 seconds) from the router's current time, the session will be rejected.
-</li><li>
-If there is already a session on the router for this Destination, the session will be rejected.
-</li><li>
-The <a href="{{ site_url('docs/spec/common-structures') }}#type_Mapping">Mapping</a> in the Session Config
-must be sorted by key so that the signature will be validated correctly in the router.
-</li></ul>
-
-
-
-<h3 id="msg_DestLookup">Dest Lookup</h3>
-<h4>Description</h4>
-<p>
-
-Sent from Client to Router.
-The router responds with a <a href="#msg_DestReply">Dest Reply Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#struct_Hash">SHA-256 Hash</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-As of release 0.7.
-As of release 0.8.3, multiple outstanding lookups are supported,
-and lookups are supported in both I2PSimpleSession and in standard sessions.
-<a href="#msg_HostLookup">Host Lookup Message</a> is preferred as of release 0.9.11.
-</p>
-
-
-
-<h3 id="msg_DestReply">Dest Reply</h3>
-<h4>Description</h4>
-<p>
-Sent from Router to Client in response to a Dest Lookup Message.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#struct_Destination">Destination</a>
-on success, or
-<a href="{{ site_url('docs/spec/common-structures') }}#struct_Hash">Hash</a>
-on failure
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-As of release 0.7.
-As of release 0.8.3,
-the requested Hash is returned if the lookup failed,
-so that the client may have multiple lookups outstanding and correlate the replies to the lookups.
-To correlate a Destination response with a request, take the Hash of the Destination.
-Prior to release 0.8.3, the response was empty on failure.
-</p>
-
-
-
-<h3 id="msg_Destroy Session">Destroy Session</h3>
-<h4>Description</h4>
-<p>
-This message is sent from a client to destroy a session.
-Sent from Client to Router.
-The router responds with a <a href="#msg_SessionStatus">Session Status Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-The router at this point should release all resources related to the session.
-</p>
-
-
-
-<h3 id="msg_Disconnect">Disconnect</h3>
-<h4>Description</h4>
-<p>
-Tell the other party that there are problems and the current connection is about to
-be destroyed. This does not necessarily end a session.
-Sent either from router to client or from client to router.
-</p>
-<h4>Contents</h4>
-<ol><li>
-Reason <a href="{{ site_url('docs/spec/common-structures') }}#struct_String">String</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-Only implemented in the router-to-client direction.
-Disconnecting probably does end a session, in practice.
-</p>
-
-
-
-<h3 id="msg_GetBandwidthLimits">Get Bandwidth Limits</h3>
-<h4>Description</h4>
-<p>
-Request that the router state what its current bandwidth limits are.
-Sent from Client to Router.
-The router responds with a <a href="#msg_BandwidthLimits">Bandwidth Limits Message</a>.
-</p>
-<h4>Contents</h4>
-<i>None</i>
-
-<h4>Notes</h4>
-<p>
-As of release 0.7.2.
-As of release 0.8.3,
-supported in both I2PSimpleSession and in standard sessions.
-</p>
-
-
-
-<h3 id="msg_GetDate">Get Date</h3>
-<h4>Description</h4>
-<p>
-Sent from Client to Router.
-The router responds with a <a href="#msg_SetDate">Set Date Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-I2CP Version <a href="{{ site_url('docs/spec/common-structures') }}#struct_String">String</a>
-</li><li>
-Authentication <a href="{{ site_url('docs/spec/common-structures') }}#type_Mapping">Mapping</a>
-(optional, as of release 0.9.11)
-</li></ol>
-
-<h4>Notes</h4>
-<ul><li>
-Generally the first message sent by the client after sending the protocol version byte.
-</li><li>
-The version string is included as of release 0.8.7.
-This is only useful if the client and router are not in the same JVM.
-If it is not present, the client is version 0.8.6 or earlier.
-</li><li>
-As of release 0.9.11, the authentication
-<a href="{{ site_url('docs/spec/common-structures') }}#type_Mapping">Mapping</a>
-may be included, with the keys i2cp.username and i2cp.password.
-The Mapping need not be sorted as this message is not signed.
-Prior to and including 0.9.10, authentication is included in the
-<a href="#struct_SessionConfig">Session Config</a> Mapping, and
-no authentication is enforced for GetDate, GetBandwidthLimits, or DestLookup.
-When enabled, authentication via GetDate is required before any other messages as of release 0.9.16.
-This is only useful outside router context.
-This is an incompatible change, but will only affect sessions outside
-router context with authentication, which should be rare.
-</li></ul>
-</p>
-
-
-
-<h3 id="msg_HostLookup">Host Lookup</h3>
-<h4>Description</h4>
-<p>
-Sent from Client to Router.
-The router responds with a <a href="#msg_HostReply">Host Reply Message</a>.
-This replaces the <a href="#msg_DestLookup">Dest Lookup Message</a>
-and adds a request ID, a timeout, and host name lookup support.
-As it also supports Hash lookups, it may be used for all lookups if the router supports it.
-For host name lookups, the router will query its context's naming service.
-This is only useful if the client is outside the router's context.
-Inside router context, the client should query the naming service itself,
-which is much more efficient.
-</p>
-
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> request ID
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> timeout (ms)
-</li><li>
-1 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> request type
-</li><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#struct_Hash">SHA-256 Hash</a>
-or
-host name <a href="{{ site_url('docs/spec/common-structures') }}#struct_String">String</a>
-</li></ol>
-
-<h4>Notes</h4>
-<ul><li>
-As of release 0.9.11.
-Use <a href="#msg_DestLookup">Dest Lookup Message</a> for older routers.
-</li><li>
-The session ID and request ID will be returned in the <a href="#msg_HostReply">Host Reply Message</a>.
-Use 0xFFFF for the session ID if there is no session.
-</li><li>
-Timeout is useful for Hash lookups. Recommended minimum 10,000 (10 sec.).
-In the future it may also be useful for remote naming service lookups.
-The value may be not be honored for local host name lookups, which should be fast.
-</li><li>
-The request type is 0 for Hash and 1 for host name.
-</li><li>
-Base 32 host name lookup is supported but it is preferred to convert
-it to a Hash first.
-</ul>
-
-
-
-<h3 id="msg_HostReply">Host Reply</h3>
-<h4>Description</h4>
-<p>
-Sent from Router to Client in response to a
-<a href="#msg_HostLookup">Host Lookup Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> request ID
-</li><li>
-1 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> result code
-</li><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#struct_Destination">Destination</a>,
-only present if result code is zero.
-</li></ol>
-
-<h4>Notes</h4>
-<ul><li>
-As of release 0.9.11.
-See Host Lookup notes.
-</li><li>
-The session ID and request ID are those from the Host Lookup.
-</li><li>
-The result code is 0 for success, 1-255 for failure.
-Only 1 is used for failure now, more specific failure codes may be defined in the future.
-</ul>
-
-
-
-
-<h3 id="msg_MessagePayload">Message Payload</h3>
-<h4>Description</h4>
-<p>
-Deliver the payload of a message to the client.
-Sent from Router to Client.
-The client responds with a <a href="#msg_ReceiveMessageEnd">Receive Message End Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-<a href="#struct_MessageId">Message ID</a>
-</li><li>
-<a href="#struct_Payload">Payload</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-</p>
-
-
-
-<h3 id="msg_MessageStatus">Message Status</h3>
-<h4>Description</h4>
-<p>
-Notify the client of the delivery status of an incoming or outgoing message.
-Sent from Router to Client.
-If this message indicates that an incoming message is available,
-the client responds with a <a href="#msg_ReceiveMessageBegin">Receive Message Begin Message</a>.
-For an outgoing message, this is a response to a
-<a href="#msg_SendMessage">Send Message Message</a> or
-<a href="#msg_SendMessageExpires">Send Message Expires Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-<a href="#struct_MessageId">Message ID</a> generated by the router
-</li><li>
-1 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> status
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> size
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> nonce previously generated by the client
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-Through version 0.9.4,
-the known status values are 0 for message is available, 1 for accepted, 2 for best
-effort succeeded, 3 for best effort failed, 4 for guaranteed succeeded, 5 for
-guaranteed failed. The size Integer specifies the size of the available
-message and is only relevant for status = 0.
-Even though guaranteed is unimplemented, (best effort is the only service), the current
-router implementation uses the guaranteed status codes, not the best effort codes.
-As of router version 0.9.5, additional status codes are defined,
-however they are not necessarily implemented.
-See the
-<a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/i2cp/MessageStatusMessage.html">MessageStatusMessage Javadocs</a>
-for details.
-All status codes:
-<table border=1>
-<tr><th>Status Code<th>As Of Release<th>Name<th>Description
-<tr><td align="center">0<td align="center">&nbsp;<td>Available<td>For incoming messages only.
-    The included size is the size in bytes of the available message.
-    This is unused in "fast receive" mode, which is the default as of release 0.9.4.
-    All other status codes below are for outgoing messages.
-<tr><td align="center">1<td align="center">&nbsp;<td>Accepted<td>Outgoing message accepted by the local router for delivery.
-    The included nonce matches the nonce in the
-    Send Message Message, and the included Message ID
-    will be used for subsequent success or failure notification.
-<tr><td align="center">2<td align="center">&nbsp;<td>Best Effort Success<td>Probable success (unused)
-<tr><td align="center">3<td align="center">&nbsp;<td>Best Effort Failure<td>Probable failure
-<tr><td align="center">4<td align="center">&nbsp;<td>Guaranteed Success<td>Probable success
-<tr><td align="center">5<td align="center">&nbsp;<td>Guaranteed Failure<td>Generic failure, specific cause unknown.
-    May not really be a guaranteed failure.
-<tr><td align="center">6<td align="center">0.9.5<td>Local Success<td>Local delivery successful.
-    The destination was another client on the same router.
-<tr><td align="center">7<td align="center">0.9.5<td>Local Failure<td>Local delivery failure.
-    The destination was another client on the same router.
-<tr><td align="center">8<td align="center">0.9.5<td>Router Failure<td>The local router is not ready, has shut down,
-    or has major problems.
-    This is a guaranteed failure.
-<tr><td align="center">9<td align="center">0.9.5<td>Network Failure<td>The local computer apparently has no network connectivity at all.
-    This is a guaranteed failure.
-<tr><td align="center">10<td align="center">0.9.5<td>Bad Session<td>The I2CP session is invalid or closed.
-    This is a guaranteed failure.
-<tr><td align="center">11<td align="center">0.9.5<td>Bad Message<td>The message payload is invalid or zero-length or too big.
-    This is a guaranteed failure.
-<tr><td align="center">12<td align="center">0.9.5<td>Bad Options<td>Something is invalid in the message options,
-    or the expiration is in the past or too far in the future.
-    This is a guaranteed failure.
-<tr><td align="center">13<td align="center">0.9.5<td>Overflow Failure<td>Some queue or buffer in the router is full and the message was dropped.
-    This is a guaranteed failure.
-<tr><td align="center">14<td align="center">0.9.5<td>Message Expired<td>The message expired before it could be sent.
-    This is a guaranteed failure.
-<tr><td align="center">15<td align="center">0.9.5<td>Bad Local Leaseset<td>The client has not yet signed a leaseset, or the local keys
-    are invalid, or it has expired, or it does not have any tunnels in it.
-    This is a guaranteed failure.
-<tr><td align="center">16<td align="center">0.9.5<td>No Local Tunnels<td>Local problems. No outbound tunnel to send through,
-    or no inbound tunnel if a reply is required.
-    This is a guaranteed failure.
-<tr><td align="center">17<td align="center">0.9.5<td>Unsupported Encryption<td>The certs or options in the destination or its leaseset
-    indicate that it uses an encryption format that we don't support, so we can't talk to it.
-    This is a guaranteed failure.
-<tr><td align="center">18<td align="center">0.9.5<td>Bad Destination<td>Something is wrong with the far-end destination.
-    Bad format, unsupported options, certificates, etc.
-    This is a guaranteed failure.
-<tr><td align="center">19<td align="center">0.9.5<td>Bad Leaseset<td>We got the far-end leaseset but something strange is wrong with it.
-    Unsupported options or certificates, no tunnels, etc.
-    This is a guaranteed failure.
-<tr><td align="center">20<td align="center">0.9.5<td>Expired Leaseset<td>We got the far-end leaseset but it's expired and we can't get a new one.
-    This is a guaranteed failure.
-<tr><td align="center">21<td align="center">0.9.5<td>No Leaseset<td>Could not find the far-end leaseset.
-    This is a common failure, equivalent to a DNS lookup failure.
-    This is a guaranteed failure.
-</table>
-</p>
-<p>
-When status = 1 (accepted), the nonce matches the nonce in the
-Send Message Message, and the included Message ID
-will be used for subsequent success or failure notification.
-Otherwise, the nonce may be ignored.
-</p>
-
-
-
-<h3 id="msg_ReceiveMessageBegin">Receive Message Begin</h3>
-<h4>Description</h4>
-<p>
-Request the router to deliver a message that it was previously notified of.
-Sent from Client to Router.
-The router responds with a <a href="#msg_MessagePayload">Message Payload Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-<a href="#struct_MessageId">Message ID</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-The ReceiveMessageBeginMessage is sent as a response to a
-MessageStatusMessage stating that a new message is available for pickup. If the
-message id specified in the ReceiveMessageBeginMessage is invalid or
-incorrect, the router may simply not reply, or it may send back a
-DisconnectMessage.
-</p>
-<p>
-This is unused in "fast receive" mode, which is the default as of release 0.9.4.
-</p>
-
-
-
-
-<h3 id="msg_ReceiveMessageEnd">Receive Message End</h3>
-<h4>Description</h4>
-<p>
-Tell the router that delivery of a message was completed successfully and that
-the router can discard the message.
-Sent from Client to Router.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-<a href="#struct_MessageId">Message ID</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-The ReceiveMessageBeginMessage is sent after a MessagePayloadMessage fully
-delivers a message's payload.
-</p>
-<p>
-This is unused in "fast receive" mode, which is the default as of release 0.9.4.
-</p>
-
-
-
-
-<h3 id="msg_ReconfigureSession">Reconfigure Session</h3>
-<h4>Description</h4>
-<p>
-
-Sent from Client to Router to update the session configuration.
-The router responds with a <a href="#msg_SessionStatus">Session Status Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-<a href="#struct_SessionConfig">Session Config</a>
-</li></ol>
-
-<h4>Notes</h4>
-<ul><li>
-As of release 0.7.1.
-</li><li>
-If the Date in the Session Config is too far (more than +/- 30 seconds) from the router's current time, the session will be rejected.
-</li><li>
-The <a href="{{ site_url('docs/spec/common-structures') }}#type_Mapping">Mapping</a> in the Session Config
-must be sorted by key so that the signature will be validated correctly in the router.
-</li><li>
-Some configuration options may only be set in the Create Session message, and changes here will not be recognized by the router.
-Changes to tunnel options inbound.* and outbound.* are always recognized.
-</li></ul>
-
-
-
-
-<h3 id="msg_ReportAbuse">Report Abuse</h3>
-<h4>Description</h4>
-<p>
-Tell the other party (client or router) that they are under attack, potentially with reference to a
-particular messageId. If the router is under attack, the client may decide to
-migrate to another router, and if a client is under attack, the router may rebuild
-its routers or banlist some of the peers that sent it messages delivering the attack.
-Sent either from router to client or from client to router.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-1 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> abuse severity
-(0 is minimally abusive, 255 being extremely abusive)
-</li><li>
-Reason <a href="{{ site_url('docs/spec/common-structures') }}#struct_String">String</a>
-</li><li>
-<a href="#struct_MessageId">Message ID</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-Unused.
-Not fully implemented. Both router and client can generate Report Abuse Messages,
-but neither has a handler for the message when received.
-</p>
-
-
-
-
-<h3 id="msg_RequestLeaseSet">Request LeaseSet</h3>
-<h4>Description</h4>
-<p>
-Request that a client authorize the inclusion of a particular set of inbound tunnels.
-Sent from Router to Client.
-The client responds with a <a href="#msg_CreateLeaseSet">Create LeaseSet Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-1 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> number of tunnels
-</li><li>
-  That many pairs of:
-  <ol><li>
-       <a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">Router Identity</a>
-  </li><li>
-       <a href="{{ site_url('docs/spec/common-structures') }}#type_TunnelId">Tunnel ID</a>
-  </li></ol>
-</li><li>
-End <a href="{{ site_url('docs/spec/common-structures') }}#type_Date">Date</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-This requests a LeaseSet with all Leases set to expire at the same time.
-For client versions 0.9.7 or higher, RequestVariableLeaseSet is preferred.
-</p>
-
-
-
-
-
-<h3 id="msg_RequestVariableLeaseSet">Request Variable LeaseSet</h3>
-<h4>Description</h4>
-<p>
-Request that a client authorize the inclusion of a particular set of inbound tunnels.
-Sent from Router to Client.
-The client responds with a <a href="#msg_CreateLeaseSet">Create LeaseSet Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-1 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> number of tunnels
-</li><li>
-  That many
-  <a href="{{ site_url('docs/spec/common-structures') }}#struct_Lease">Leases</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-This requests a LeaseSet with an individual expiration time for each Lease.
-As of release 0.9.7.
-For clients before that release, use RequestLeaseSet.
-</p>
-
-
-
-
-
-<h3 id="msg_SendMessage">Send Message</h3>
-<h4>Description</h4>
-<p>
-This is how a client sends a message (the payload) to the Destination.
-The router will use a default expiration.
-Sent from Client to Router.
-The router responds with a <a href="#msg_MessageStatus">Message Status Message</a>.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#struct_Destination">Destination</a>
-</li><li>
-<a href="#struct_Payload">Payload</a>
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> nonce
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-As soon as the SendMessageMessage arrives fully intact, the router should return
-a MessageStatusMessage stating that it has been accepted for delivery.
-That message will contain the same nonce sent here.
-Later on,
-based on the delivery guarantees of the session configuration, the router may
-additionally send back another MessageStatusMessage updating the status.
-</p><p>
-As of release 0.8.1, the router does not send either Message Status Message if
-i2cp.messageReliability=none.
-</p><p>
-Prior to release 0.9.4, a nonce value of 0 was not allowed.
-As of release 0.9.4, a nonce value of 0 is allowed, and tells to the router
-that it should not send either Message Status Message, i.e. it acts as if
-i2cp.messageReliability=none for this message only.
-</p><p>
-Prior to release 0.9.14, a session with i2cp.messageReliability=none could not be overridden on a per-message basis.
-As of release 0.9.14, in a session with i2cp.messageReliability=none,
-the client may request delivery of a Message Status Message with the delivery success or failure
-by setting the nonce to a nonzero value.
-The router will not send the "accepted" Message Status Message but it will later send the client
-a Message Status Message with the same nonce, and a success or failure value.
-</p>
-
-
-
-
-<h3 id="msg_SendMessageExpires">Send Message Expires</h3>
-<h4>Description</h4>
-<p>
-Sent from Client to Router. Same as Send Message Message, except includes an expiration and options.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#struct_Destination">Destination</a>
-</li><li>
-<a href="#struct_Payload">Payload</a>
-</li><li>
-4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> nonce
-</li><li>
-2 bytes of flags (options)
-</li><li>
-Expiration <a href="{{ site_url('docs/spec/common-structures') }}#type_Date">Date</a>
-truncated from 8 bytes to 6 bytes
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-As of release 0.7.1.
-</p><p>
-In "best effort" mode,
-as soon as the SendMessageExpiresMessage arrives fully intact, the router should return
-a MessageStatusMessage stating that it has been accepted for delivery.
-That message will contain the same nonce sent here.
-Later on,
-based on the delivery guarantees of the session configuration, the router may
-additionally send back another MessageStatusMessage updating the status.
-</p><p>
-As of release 0.8.1, the router does not send either Message Status Message if
-i2cp.messageReliability=none.
-</p><p>
-Prior to release 0.9.4, a nonce value of 0 was not allowed.
-As of release 0.9.4, a nonce value of 0 is allowed, and tells the router
-that it should not send either Message Status Message, i.e. it acts as if
-i2cp.messageReliability=none for this message only.
-</p><p>
-Prior to release 0.9.14, a session with i2cp.messageReliability=none could not be overridden on a per-message basis.
-As of release 0.9.14, in a session with i2cp.messageReliability=none,
-the client may request delivery of a Message Status Message with the delivery success or failure
-by setting the nonce to a nonzero value.
-The router will not send the "accepted" Message Status Message but it will later send the client
-a Message Status Message with the same nonce, and a success or failure value.
-</p>
-
-<h4>Flags Field</h4>
-<p>
-As of release 0.8.4, the upper two bytes of the Date are redefined to contain
-flags. The flags must default to all zeros for backward compatibility.
-The Date will not encroach on the flags field until the year 10889.
-The flags may be used by the application to provide hints to the router
-as to whether a LeaseSet and/or ElGamal/AES Session Tags should be delivered
-with the message. The settings will significantly affect the amount of
-protocol overhead and the reliability of message delivery.
-The individual flag bits are defined as follows, as of release 0.9.2.
-Definitions are subject to change. Use the SendMessageOptions class to construct the flags.
-</p><p>
-Bit order: 15...0
-</p><p>
-Bits 15-11: Unused, must be zero
-</p><p>
-Bits 10-9: Message Reliability Override (Unimplemented, to be removed).
-
-<table border=1>
-<tr><th>Field value<th>Description
-<tr><td align="center">00<td>Use session setting i2cp.messageReliabiltiy (default)
-<tr><td align="center">01<td>Use "best effort" message reliabiltiy for this message,
-    overriding the session setting. The router will send one or more MessageStatusMessages in response.
-    Unused. Use a nonzero nonce value to override a session setting of "none".
-<tr><td align="center">10<td>Use "guaranteed" message reliabiltiy for this message,
-    overriding the session setting. The router will send one or more MessageStatusMessages in response.
-    Unused. Use a nonzero nonce value to override a session setting of "none".
-<tr><td align="center">11<td>Unused. Use a nonce value of 0 to force "none" and override a session setting
-    of "best effort" or "guaranteed".
-</table>
-</p><p>
-Bit 8: If 1, don't bundle a lease set in the garlic with this message.
-If 0, the router may bundle a lease set at its discretion.
-</p><p>
-Bits 7-4: Low tag threshold. If there are less than this many tags available, send more.
-This is advisory and does not force tags to be delivered.
-<table border=1>
-<tr><th>Field value<th>Tag threshold
-<tr><td align="center">0000<td align="center">Use session key manager settings
-<tr><td align="center">0001<td align="center">2
-<tr><td align="center">0010<td align="center">3
-<tr><td align="center">0011<td align="center">6
-<tr><td align="center">0100<td align="center">9
-<tr><td align="center">0101<td align="center">14
-<tr><td align="center">0110<td align="center">20
-<tr><td align="center">0111<td align="center">27
-<tr><td align="center">1000<td align="center">35
-<tr><td align="center">1001<td align="center">45
-<tr><td align="center">1010<td align="center">57
-<tr><td align="center">1011<td align="center">72
-<tr><td align="center">1100<td align="center">92
-<tr><td align="center">1101<td align="center">117
-<tr><td align="center">1110<td align="center">147
-<tr><td align="center">1111<td align="center">192
-</table>
-</p><p>
-Bits 3-0: Number of tags to send if required.
-This is advisory and does not force tags to be delivered.
-<table border=1>
-<tr><th>Field value<th>Tags to send
-<tr><td align="center">0000<td align="center">Use session key manager settings
-<tr><td align="center">0001<td align="center">2
-<tr><td align="center">0010<td align="center">4
-<tr><td align="center">0011<td align="center">6
-<tr><td align="center">0100<td align="center">8
-<tr><td align="center">0101<td align="center">12
-<tr><td align="center">0110<td align="center">16
-<tr><td align="center">0111<td align="center">24
-<tr><td align="center">1000<td align="center">32
-<tr><td align="center">1001<td align="center">40
-<tr><td align="center">1010<td align="center">51
-<tr><td align="center">1011<td align="center">64
-<tr><td align="center">1100<td align="center">80
-<tr><td align="center">1101<td align="center">100
-<tr><td align="center">1110<td align="center">125
-<tr><td align="center">1111<td align="center">160
-</table>
-</p>
-
-
-
-
-<h3 id="msg_SessionStatus">Session Status</h3>
-<h4>Description</h4>
-<p>
-Instruct the client as to the status of its session.
-Sent from Router to Client, possibly in response to a Create Session or Reconfigure Session.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="#struct_SessionId">Session ID</a>
-</li><li>
-1 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> status
-</li></ol>
-
-<table border=1>
-<tr><th>Status<th>Definition
-<tr><td align="center">0<td>Destroyed: The session with the given ID is terminated.
-<tr><td align="center">1<td>Created: In response to a Create Session, a new session with the given ID is now active.
-<tr><td align="center">2<td>Updated: In response to a Reconfigure Session, an existing session with the given ID has been reconfigured.
-<tr><td align="center">3<td>Invalid:
-In response to a Create Session, the configuration is invalid. The included session ID should be ignored.
-In response to a Reconfigure Session, the new configuration is invalid for the session with the given ID.
-<tr><td align="center">4<td>Refused: In response to a Create Session, the router was unable to create the session,
-perhaps due to limits being exceeded. The included session ID should be ignored. Since 0.9.12.
-</table>
-
-<h4>Notes</h4>
-<p>
-Status values include 0 for destroyed, 1 for created, 2 for updated, and
-3 for invalid session.
-If created, the Session ID is the identifier to be used for the rest of the session.
-</p>
-
-
-
-<h3 id="msg_SetDate">Set Date</h3>
-<h4>Description</h4>
-<p>
-The current date and time.
-Sent from Router to Client as a part of the initial handshake.
-As of release 0.9.20, may also be sent at any time after the handshake to notify the client of a clock shift.
-</p>
-<h4>Contents</h4>
-<ol><li>
-<a href="{{ site_url('docs/spec/common-structures') }}#type_Date">Date</a>
-</li><li>
-I2CP Version <a href="{{ site_url('docs/spec/common-structures') }}#struct_String">String</a>
-</li></ol>
-
-<h4>Notes</h4>
-<p>
-This is generally the first message sent by the router.
-The version string is included as of release 0.8.7.
-This is only useful if the client and router are not in the same JVM.
-If it is not present, the router is version 0.8.6 or earlier.
-<p></p>
-Additional SetDate messages will not be sent to clients in the same JVM.
-</p>
-
-
-
-
-
-
-
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/i2np.html b/i2p2www/pages/site/docs/spec/i2np.html
deleted file mode 100644
index dfb3c85b2704a816ce332a33001924082542da48..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/i2np.html
+++ /dev/null
@@ -1,1281 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}I2NP Specification{% endblock %}
-{% block lastupdated %}January 2016{% endblock %}
-{% block accuratefor %}0.9.24{% endblock %}
-{% block content %}
-<p>
-The I2P Network Protocol (I2NP),
-which is sandwiched between I2CP and the various I2P transport protocols, manages the
-routing and mixing of messages between routers, as well as the selection of what
-transports to use when communicating with a peer for which there are multiple
-common transports supported.
-</p>
-
-<h2 id="versions">Protocol Versions</h2>
-<p>
-All routers must publish their I2NP protocol version in the "router.version" field in the RouterInfo properties.
-This version field indicates their level of support for various I2NP protocol features,
-and is not necessarily the actual router version.
-</p><p>
-If alternative (non-Java) routers
-wish to publish any version information about the actual router implementation,
-they must do so in another property.
-Versions other than those listed below are allowed. Support will be
-determined through a numeric comparison; for example, 0.9.13 implies support for 0.9.12 features.
-Note that the "coreVersion" property is not used for determination of the I2NP protocol version.
-</p><p>
-A basic summary of the I2NP protocol versions is as follows. For details, see below.
-<table border=1>
-  <tr>
-    <th>Version</th>
-    <th>Required I2NP Features</th>
-  </tr><tr>
-    <td align="center">0.9.18</td>
-    <td align="left">DSM type bits 7-1 ignored
-    </td>
-  </tr><tr>
-    <td align="center">0.9.16</td>
-    <td align="left">RI key certs / ECDSA and EdDSA sig types<br>
-                     Note: RSA sig types also supported as of this version, but currently unused<br>
-                     DLM lookup types (DLM flag bits 3-2)
-    </td>
-  </tr><tr>
-    <td align="center">0.9.15</td>
-    <td align="left">Dest/LS key certs w/ EdDSA Ed25519 sig type (if floodfill)</td>
-  </tr><tr>
-    <td align="center">0.9.12</td>
-    <td align="left">Dest/LS key certs w/ ECDSA P-256, P-384, and P-521 sig types (if floodfill)<br>
-                     Note: RSA sig types also supported as of this version, but currently unused<br>
-                     Nonzero expiration allowed in RouterAddress
-    </td>
-  </tr><tr>
-    <td align="center">0.9.7</td>
-    <td align="left">Encrypted DSM/DSRM replies supported (DLM flag bit 1) (if floodfill)</td>
-  </tr><tr>
-    <td align="center">0.9.6</td>
-    <td align="left">Nonzero DLM flag bits 7-1 allowed</td>
-  </tr><tr>
-    <td align="center">0.9.3</td>
-    <td align="left">Requires zero expiration in RouterAddress</td>
-  </tr><tr>
-    <td align="center">0.9</td>
-    <td align="left">Supports up to 16 leases in a DSM LS store (6 previously)</td>
-  </tr><tr>
-    <td align="center">0.7.12</td>
-    <td align="left">VTBM and VTBRM message support</td>
-  </tr><tr>
-    <td align="center">0.7.10</td>
-    <td align="left">Floodfill supports encrypted DSM stores</td>
-  </tr><tr>
-    <td align="center">0.7.9 or lower</td>
-    <td align="left">All messages and features not listed above</td>
-  </tr><tr>
-    <td align="center">0.6.1.10</td>
-    <td align="left">TBM and TBRM messages introduced<br>
-                     Minimum version compatible with current network
-    </td>
-  </tr>
-</table>
-
-</p><p>
-Note that there are also transport-related features and compatibility issues;
-see the NTCP and SSU transport documentation for details.
-</p>
-
-
-
-
-
-<h2 id="structures">Common structures</h2>
-The following structures are elements of multiple I2NP messages.
-They are not complete messages.
-
-<h3 id="struct_I2NPMessageHeader">I2NP message header</h3>
-<h4>Description</h4>
-<p>
-  Common header to all I2NP messages, which contains important information like a checksum, expiration date, etc.
-</p>
-<h4>Contents</h4>
-<p>
-  1 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> specifying the type of this message,
- followed by a 4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> specifying the message-id.
- After that there is an expiration <a href="{{ site_url('docs/spec/common-structures') }}#type_Date">Date</a>,
- followed by a 2 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a> specifying
- the length of the message payload, followed by a <a href="{{ site_url('docs/spec/common-structures') }}#type_Hash">Hash</a>,
- which is truncated to the first byte. After that the actual message data follows.
-</p>
-{% highlight lang='dataspec' %}
-Standard (16 bytes):
-
-+----+----+----+----+----+----+----+----+
-|type|      msg_id       |  expiration
-+----+----+----+----+----+----+----+----+
-                         |  size   |chks|
-+----+----+----+----+----+----+----+----+
-
-Short (SSU, 5 bytes):
-
-+----+----+----+----+----+
-|type| short_expiration  |
-+----+----+----+----+----+
-
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-type :: `Integer`
-        length -> 1 byte
-        
-        purpose -> identifies the message type (see table below)
-
-msg_id :: `Integer`
-          length -> 4 bytes
-
-          purpose -> uniquely identifies this message (for some time at least)
-                     This is usually a locally-generated random number, but for
-                     outgoing tunnel build messages it may be derived from
-                     the incoming message. See below.
-
-expiration :: `Date`
-           8 bytes
-           date this message will expire
-
-short_expiration :: `Integer`
-           4 bytes
-           date this message will expire (seconds since the epoch)
-
-size :: `Integer`
-        length -> 2 bytes
-        
-        purpose -> length of the payload
-
-chks :: `Integer`
-        length -> 1 byte
-     
-        purpose -> checksum of the payload
-                   SHA256 hash truncated to the first byte
-
-data ::
-        length -> $size bytes
-        
-        purpose -> actual message contents
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul><li>
-When transmitted over <a href="{{ site_url('docs/transport/ssu') }}">SSU</a>,
-the 16-byte standard header is not used.
-Only a 1-byte type and a 4-byte expiration in seconds is included.
-The message id and size are
-incorporated into various parts of the SSU data packet format.
-The checksum is not required since errors are caught in decryption.
-</li><li>
-The standard header is also required for I2NP messages contained in other messages and structures
-(Data, TunnelData, TunnelGateway, and GarlicClove).
-As of release 0.8.12, to reduce overhead, checksum verification is disabled at
-some places in the protocol stack.
-However, for compatibility with older versions, checksum generation is still required.
-It is a topic for future research to determine points in the protocol stack
-where the far-end router's version is known and checksum generation can be disabled.
-</li><li>
-The short expiration is unsigned and will wrap around on Feb. 7, 2106.
-As of that date, an offset must be added to get the correct time.
-</li></ul>
-
-
-<h3 id="struct_BuildRequestRecord">BuildRequestRecord</h3>
-<h4>Description</h4>
-<p>
-  One Record in a set of multiple records to request the creation of one hop in the tunnel. For more details see
-  <a href="{{ site_url('docs/tunnels/implementation') }}">the tunnel overview</a> and
-  <a href="{{ site_url('docs/spec/tunnel-creation') }}">the tunnel creation specification</a>.
-</p>
-<h4>Contents</h4>
-<p>
-  <a href="{{ site_url('docs/spec/common-structures') }}#type_TunnelId">TunnelId</a> to receive messages on, followed by the <a href="{{ site_url('docs/spec/common-structures') }}#type_Hash">Hash</a> of our <a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">RouterIdentity</a>. After that the <a href="{{ site_url('docs/spec/common-structures') }}#type_TunnelId">TunnelId</a> and the <a href="{{ site_url('docs/spec/common-structures') }}#type_Hash">Hash</a> of the next router's <a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">RouterIdentity</a> follow.
-</p>
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-Cleartext:
-+----+----+----+----+----+----+----+----+
-| receive_tunnel    | our_ident         |
-+----+----+----+----+                   +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                   +----+----+----+----+
-|                   | next_tunnel       |
-+----+----+----+----+----+----+----+----+
-| next_ident                            |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-| layer_key                             |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-| iv_key                                |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-| reply_key                             |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-| reply_iv                              |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-|flag| request_time      | send_msg_id
-+----+----+----+----+----+----+----+----+
-     |                                  |
-+----+                                  +
-|         29 bytes padding              |
-+                                       +
-|                                       |
-+                             +----+----+
-|                             |
-+----+----+----+----+----+----+
-
-
-ElGamal encrypted:
-+----+----+----+----+----+----+----+----+
-| toPeer                                |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-| encrypted data...                     |
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-
-
-ElGamal and AES encrypted:
-+----+----+----+----+----+----+----+----+
-| encrypted data...                     |
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-unencrypted:
-
-receive_tunnel :: `TunnelId`
-                  length -> 4 bytes
-
-our_ident :: `Hash`
-             length -> 32 bytes
-
-next_tunnel :: `TunnelId`
-               length -> 4 bytes
-
-next_ident :: `Hash`
-              length -> 32 bytes
-
-layer_key :: `SessionKey`
-             length -> 32 bytes
-
-iv_key :: `SessionKey`
-          length -> 32 bytes
-
-reply_key :: `SessionKey`
-             length -> 32 bytes
-
-reply_iv :: data
-            length -> 16 bytes
-
-flag :: `Integer`
-        length -> 1 byte
-
-request_time :: `Integer`
-                length -> 4 bytes
-                Hours since the epoch, i.e. current time / 3600
-
-send_message_id :: `Integer`
-                   length -> 4 bytes
-
-padding :: Data
-           length -> 29 bytes
-           source -> random
-
-total length: 222
-
-
-ElGamal encrypted:
-
-toPeer :: First 16 bytes of the SHA-256 Hash of the peer's router identity
-          length -> 16 bytes
-
-encrypted_data :: ElGamal-2048 encrypted data (see notes)
-                  length -> 512
-
-total length: 528
-
-
-ElGamal and AES encrypted:
-
-encrypted_data :: ElGamal and AES encrypted data
-                  length -> 528
-
-total length: 528
-
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul><li>
-In the 512-byte encrypted record,
-the ElGamal data contains bytes 1-256 and 258-513 of the
-<a href="{{ site_url('docs/how/cryptography') }}#elgamal">514-byte ElGamal encrypted block</a>.
-The two padding bytes from the block (the zero bytes at locations 0 and 257) are removed.
-</li><li>
-  See the <a href="{{ site_url('docs/spec/tunnel-creation') }}">tunnel creation specification</a> for details on field contents.
-</li></ul>
-</p>
-
-
-<h3 id="struct_BuildResponseRecord">BuildResponseRecord</h3>
-{% highlight lang='dataspec' %}
-unencrypted:
-+----+----+----+----+----+----+----+----+
-|                                       |
-+                                       +
-|                                       |
-+   SHA-256 Hash of following bytes     +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-| random data...                        |
-~                                       ~
-|                                       |
-+                                  +----+
-|                                  |ret |
-+----+----+----+----+----+----+----+----+
-{% endhighlight %}
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-unencrypted:
-bytes 0-31   : SHA-256 Hash of bytes 32-527
-bytes 32-526 : random data
-byte  527    : reply
-
-encrypted:
-bytes 0-527: AES-encrypted record (note: same size as `BuildRequestRecord`)
-
-total length: 528
-
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul><li>
-  The random data field could, in the future, be used to return congestion or peer connectivity information
-  back to the requestor.
-</li><li>
-  See the <a href="{{ site_url('docs/spec/tunnel-creation') }}">tunnel creation specification</a> for details on the reply field.
-</li></ul>
-
-
-<h3 id="struct_GarlicClove">GarlicClove</h3>
-{% highlight lang='dataspec' %}
-unencrypted:
-+----+----+----+----+----+----+----+----+
-| Delivery Instructions                 |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| I2NP Message                          |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-|    Clove ID       |     Expiration
-+----+----+----+----+----+----+----+----+
-                    | Certificate  |
-+----+----+----+----+----+----+----+
-{% endhighlight %}
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-unencrypted:
-Delivery Instructions :: as defined below
-       Length varies but is typically 1, 33, or 37 bytes
-
-I2NP Message :: Any I2NP Message
-
-Clove ID :: 4 byte `Integer`
-
-Expiration :: `Date` (8 bytes)
-
-Certificate :: Always NULL in the current implementation (3 bytes total, all zeroes)
-
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul>
-<li>
-  Cloves are never fragmented.
-  When used in a Garlic Clove, the first bit of the Delivery Instructions flag byte specifies encryption.
-  If this bit is 0, the clove is not encrypted.
-  If 1, the clove is encrypted, and a 32 byte Session Key immediately follows the flag byte.
-  Clove encryption is not fully implemented.
-<li>
-  See also the <a href="{{ site_url('docs/how/garlic-routing') }}">garlic routing specification</a>.
-<li>
-  Maximum length is a function of the total length of all the cloves and the
-  maximum length of the GarlicMessage.
-<li>
-  In the future, the certificate could possibly be used for a HashCash to "pay" for the routing.
-<li>
-  The message can be any I2NP message (including a GarlicMessage, although that is not used in practice).
-  The messages used in practice are DataMessage, DeliveryStatusMessage, and DatabaseStoreMessage.
-<li>
-  The Clove ID is generally set to a random number on transmit and is checked for
-  duplicates on receive (same message ID space as top-level Message IDs)
-</ul>
-
-
-<h3 id="struct_GarlicCloveDeliveryInstructions">Garlic Clove Delivery Instructions</h3>
-
-<p>
-This specification is for Delivery Instructions inside Garlic Cloves only.
-Note that "Delivery Instructions" are also used inside
-Tunnel Messages,
-where the format is significantly different.
-See the
-<a href="{{ site_url('docs/spec/tunnel-message') }}#struct_TunnelMessageDeliveryInstructions">Tunnel Message documentation</a>
-for details.
-Do NOT use the following specification for Tunnel Message Delivery Instructions!
-
-
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-|flag|                                  |
-+----+                                  +
-|                                       |
-+       Session Key (optional)          +
-|                                       |
-+                                       +
-|                                       |
-+    +----+----+----+----+--------------+
-|    |                                  |
-+----+                                  +
-|                                       |
-+         To Hash (optional)            +
-|                                       |
-+                                       +
-|                                       |
-+    +----+----+----+----+--------------+
-|    |  Tunnel ID (opt)  |  Delay (opt)  
-+----+----+----+----+----+----+----+----+
-     |
-+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-flag ::
-       1 byte
-       Bit order: 76543210
-       bit 7: encrypted? Unimplemented, always 0
-                If 1, a 32-byte encryption session key is included
-       bits 6-5: delivery type
-                0x0 = LOCAL, 0x01 = DESTINATION, 0x02 = ROUTER, 0x03 = TUNNEL
-       bit 4: delay included?  Not fully implemented, always 0
-                If 1, four delay bytes are included
-       bits 3-0: reserved, set to 0 for compatibility with future uses
-
-Session Key ::
-       32 bytes
-       Optional, present if encrypt flag bit is set. Unimplemented, never set, never present.
-
-To Hash ::
-       32 bytes
-       Optional, present if delivery type is DESTINATION, ROUTER, or TUNNEL
-          If DESTINATION, the SHA256 Hash of the destination
-          If ROUTER, the SHA256 Hash of the router
-          If TUNNEL, the SHA256 Hash of the gateway router
-
-Tunnel ID :: `TunnelId`
-       4 bytes
-       Optional, present if delivery type is TUNNEL
-       The destination tunnel ID
-
-Delay :: `Integer`
-       4 bytes
-       Optional, present if delay included flag is set
-       Not fully implemented. Specifies the delay in seconds.
-
-Total length: Typical length is:
-       1 byte for LOCAL delivery;
-       33 bytes for ROUTER / DESTINATION delivery;
-       37 bytes for TUNNEL delivery
-
-{% endhighlight %}
-
-
-
-<h2 id="messages">Messages</h2>
-<table border=1>
-  <tr>
-    <th>Message</th>
-    <th>Type</th>
-  </tr>
-  <tr>
-    <td><a href="#msg_DatabaseStore">DatabaseStore</a></td>
-    <td align="right">1</td>
-  </tr>
-  <tr>
-    <td><a href="#msg_DatabaseLookup">DatabaseLookup</a></td>
-    <td align="right">2</td>
-  </tr>
-  <tr>
-    <td><a href="#msg_DatabaseSearchReply">DatabaseSearchReply</a></td>
-    <td align="right">3</td>
-  </tr>
-  <tr>
-    <td><a href="#msg_DeliveryStatus">DeliveryStatus</a></td>
-    <td align="right">10</td>
-  </tr>
-  <tr>
-    <td><a href="#msg_Garlic">Garlic</a></td>
-    <td align="right">11</td>
-  </tr>
-  <tr>
-    <td><a href="#msg_TunnelData">TunnelData</a></td>
-    <td align="right">18</td>
-  </tr>
-  <tr>
-    <td><a href="#msg_TunnelGateway">TunnelGateway</a></td>
-    <td align="right">19</td>
-  </tr>
-  <tr>
-    <td><a href="#msg_Data">Data</a></td>
-    <td align="right">20</td>
-  </tr>
-  <tr>
-    <td><a href="#msg_TunnelBuild">TunnelBuild</a></td>
-    <td align="right">21</td>
-  </tr>
-  <tr>
-    <td><a href="#msg_TunnelBuildReply">TunnelBuildReply</a></td>
-    <td align="right">22</td>
-  </tr>
-  <tr>
-    <td><a href="#msg_VariableTunnelBuild">VariableTunnelBuild</a></td>
-    <td align="right">23</td>
-  </tr>
-  <tr>
-    <td><a href="#msg_VariableTunnelBuildReply">VariableTunnelBuildReply</a></td>
-    <td align="right">24</td>
-  </tr>
-  <tr>
-    <td><i>Reserved for experimental messages</i></td>
-    <td align="right">224 - 254</td>
-  </tr>
-  <tr>
-    <td><i>Reserved for future expansion</i></td>
-    <td align="right">255</td>
-  </tr>
-</table>
-
-
-
-<h3 id="msg_DatabaseStore">DatabaseStore</h3>
-<h4>Description</h4>
-<p>
-  An unsolicited database store, or the response to a successful Database Lookup Message
-</p>
-<h4>Contents</h4>
-<p>
-  An uncompressed LeaseSet or a compressed RouterInfo
-</p>
-{% highlight lang='dataspec' %}
-with reply token:
-+----+----+----+----+----+----+----+----+
-| SHA256 Hash as key                    |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-|type| reply token       |reply_tunnelId
-+----+----+----+----+----+----+----+----+
-     | SHA256 of the gateway RouterInfo |
-+----+                                  +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+    +----+----+----+----+----+----+----+
-|    | data ...
-+----+-//
-
-with reply token == 0:
-+----+----+----+----+----+----+----+----+
-| SHA256 Hash as key                    |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-|type|         0         | data ...
-+----+----+----+----+----+-//
-{% endhighlight %}
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-key ::
-    32 bytes
-    SHA256 hash
-
-type ::
-     1 byte
-     type identifier
-     bit 0:
-             0    `RouterInfo`
-             1    `LeaseSet`
-     bits 7-1:
-            Through release 0.9.17, must be 0
-            As of release 0.9.18, ignored, reserved for future options, set to 0 for compatibility
-
-reply token ::
-            4 bytes
-            If greater than zero, a `DeliveryStatusMessage`
-            is requested with the Message ID set to the value of the Reply Token.
-            A floodfill router is also expected to flood the data to the closest floodfill peers
-            if the token is greater than zero.
-
-reply_tunnelId ::
-               4 byte `TunnelId`
-               Only included if reply token &gt; 0
-               This is the `TunnelId` of the inbound gateway of the tunnel the response should be sent to
-               If $reply_tunnelId is zero, the reply is sent directy to the reply gateway router.
-
-reply gateway ::
-              32 bytes
-              Hash of the `RouterInfo` entry to reach the gateway
-              Only included if reply token &gt; 0
-              If $reply_tunnelId is nonzero, this is the router hash of the inbound gateway
-              of the tunnel the response should be sent to.
-              If $reply_tunnelId is zero, this is the router hash the response should be sent to.
-
-data ::
-     If type == 0, data is a 2-byte `Integer` specifying the number of bytes that follow,
-                   followed by a gzip-compressed `RouterInfo`.
-     If type == 1, data is an uncompressed `LeaseSet`.
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul><li>
-For security, the reply fields are ignored if the message is received down a tunnel.
-</li><li>
-The key is the "real" hash of the RouterIdentity or Destination, NOT the routing key.
-</li></ul>
-
-
-
-<h3 id="msg_DatabaseLookup">DatabaseLookup</h3>
-<h4>Description</h4>
-<p>
-  A request to look up an item in the network database.
-  The response is either a DatabaseStore or a DatabaseSearchReply.
-</p>
-<h4>Contents</h4>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-| SHA256 hash as the key to look up     |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-| SHA256 hash of the routerInfo         |
-+ who is asking, or the gateway to      +
-| send the reply to                     |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-|flag| reply_tunnelId    |size     |    |
-+----+----+----+----+----+----+----+    +
-| SHA256 of $key1 to exclude            |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                  +----+
-|                                  |    |
-+----+----+----+----+----+----+----+    +
-| SHA256 of $key2 to exclude            |
-+                                       +
-~                                       ~
-+                                  +----+
-|                                  |    |
-+----+----+----+----+----+----+----+    +
-|                                       |
-+                                       +
-|   Session key if reply encryption     |
-+   was requested                       +
-|                                       |
-+                                  +----+
-|                                  |tags|
-+----+----+----+----+----+----+----+----+
-|                                       |
-+                                       +
-|   Session tags if reply encryption    |
-+   was requested                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-key ::
-    32 bytes
-    SHA256 hash of the object to lookup
-
-from ::
-     32 bytes
-     if deliveryFlag == 0, the SHA256 hash of the routerInfo entry this request came from
-                           (to which the reply should be sent)
-     if deliveryFlag == 1, the SHA256 hash of the reply tunnel gateway
-                           (to which the reply should be sent)
-
-flags ::
-     1 byte
-     bit order: 76543210
-     bit 0: deliveryFlag
-             0  => send reply directly
-             1  => send reply to some tunnel
-     bit 1: encryptionFlag
-             through release 0.9.5, must be set to 0
-             as of release 0.9.6, ignored
-             as of release 0.9.7:
-             0  => send unencrypted reply
-             1  => send AES encrypted reply using enclosed key and tag
-     bits 3-2: lookup type flags
-             through release 0.9.5, must be set to 00
-             as of release 0.9.6, ignored
-             as of release 0.9.16:
-             00  => normal lookup, return `RouterInfo` or `LeaseSet` or `DatabaseSearchReplyMessage`
-             01  => LS lookup, return `LeaseSet` or `DatabaseSearchReplyMessage`
-             10  => RI lookup, return `RouterInfo` or `DatabaseSearchReplyMessage`
-             11  => exploration lookup, return `DatabaseSearchReplyMessage` containing non-floodfill routers only
-                    (replaces an excludedPeer of all zeroes)
-     bits 7-4:
-             through release 0.9.5, must be set to 0
-             as of release 0.9.6, ignored, set to 0 for compatibility with future uses and with older routers
-
-reply_tunnelId ::
-               4 byte `TunnelID`
-               only included if deliveryFlag == 1
-               tunnelId of the tunnel to send the reply to
-
-size ::
-     2 byte `Integer`
-     valid range: 0-512
-     number of peers to exclude from the `DatabaseSearchReplyMessage`
-
-excludedPeers ::
-              $size SHA256 hashes of 32 bytes each (total $size*32 bytes)
-              if the lookup fails, these peers are requested to be excluded from the list in
-              the `DatabaseSearchReplyMessage`.
-              if excludedPeers includes a hash of all zeroes, the request is exploratory, and
-              the `DatabaseSearchReplyMessage` is requested to list non-floodfill routers only.
-
-reply_key ::
-     32 byte `SessionKey`
-     only included if encryptionFlag == 1, only as of release 0.9.7
-
-tags ::
-     1 byte `Integer`
-     valid range: 1-32 (typically 1)
-     the number of reply tags that follow
-     only included if encryptionFlag == 1, only as of release 0.9.7
-
-reply_tags ::
-     one or more 32 byte `SessionTag`s (typically one)
-     only included if encryptionFlag == 1, only as of release 0.9.7
-
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul><li>
-Prior to 0.9.16,
-the key may be for a RouterInfo or LeaseSet, as they are in the same key space,
-and there was no flag to request only a particular type of data.
-</li><li>
-Encryption flag, reply key, and reply tags as of release 0.9.7.
-</li><li>
-Encrypted replies are only useful when the response is through a tunnel.
-</li><li>
-The number of included tags could be greater than one if alternative DHT lookup strategies
-(for example, recursive lookups) are implemented.
-</li><li>
-The lookup key and exclude keys are the "real" hashes, NOT routing keys.
-</li></ul>
-
-
-<h3 id="msg_DatabaseSearchReply">DatabaseSearchReply</h3>
-<h4>Description</h4>
-<p>
-  The response to a failed Database Lookup Message
-</p>
-<h4>Contents</h4>
-<p>
-  A list of router hashes closest to the requested key
-</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-| SHA256 hash as query key              |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+----+----+----+----+----+----+----+----+
-|num | peer_hashes                      |
-+----+                                  +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+    +----+----+----+----+----+----+----+
-|    | from                             |
-+----+                                  +
-|                                       |
-+                                       +
-|                                       |
-+                                       +
-|                                       |
-+    +----+----+----+----+----+----+----+
-|    |
-+----+
-
-
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-key ::
-    32 bytes
-    SHA256 of the object being searched
-
-num ::
-    1 byte `Integer`
-    number of peer hashes that follow, 0-255
-
-peer_hashes ::
-          $num SHA256 hashes of 32 bytes each (total $num*32 bytes)
-          SHA256 of the `RouterIdentity` that the other router thinks is close to the key
-
-from ::
-     32 bytes
-     SHA256 of the `RouterInfo` of the router this reply was sent from
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul><li>
-  The 'from' hash is unauthenticated and cannot be trusted.
-</li><li>
-  The returned peer hashes are not necessarily closer to the key
-  than the router being queried.
-</li><li>
-  Typical number of hashes returned: 3
-</li><li>
-The lookup key, peer hashes, and from hash are "real" hashes, NOT routing keys.
-</li>
-</ul>
-
-
-
-<h3 id="msg_DeliveryStatus">DeliveryStatus</h3>
-<h4>Description</h4>
-<p>
-  A simple message acknowledgment. Generally created by the message originator, and wrapped
-  in a Garlic Message with the message itself, to be returned by the destination.
-</p>
-<h4>Contents</h4>
-<p>
-  The ID of the delivered message, and the creation or arrival time.
-</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+----+----+----+----+
-|msg_id             |           time_stamp                  |
-+----+----+----+----+----+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-msg_id :: `Integer`
-       4 bytes
-       unique ID of the message we deliver the DeliveryStatus for (see `I2NPMessageHeader` for details)
-
-time_stamp :: `Date`
-             8 bytes
-             time the message was successfully created or delivered
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul><li>
-  It appears that the time stamp is always set by the creator to the current time.
-  However there are several uses of this in the code, and more may be added in the future.
-</li><li>
-  This message is also used as a session established confirmation in <a href="{{ site_url('docs/transport/ssu') }}#establishDirect">SSU</a>.
-  In this case, the message ID is set to a random number, and the
-  "arrival time" is set to the current network-wide ID, which is 2
-  (i.e. 0x0000000000000002).
-</ul>
-
-<h3 id="msg_Garlic">Garlic</h3>
-<h4>Description</h4>
-<p>
-  Used to wrap multiple encrypted I2NP Messages
-</p>
-<h4>Contents</h4>
-<p>
-  When decrypted, a series of <a href="#struct_GarlicClove">Garlic Cloves</a>.
-</p>
-{% highlight lang='dataspec' %}
-encrypted:
-+----+----+----+----+----+----+----+----+
-|      length       | data              |
-+----+----+----+----+                   +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-
-
-unencrypted data:
-+----+----+----+----+----+----+----+----+
-|num |  clove 1                         |
-+----+                                  +
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-|         clove 2 ...                   |
-~                                       ~
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-| Certificate  |   Message_ID      |     
-+----+----+----+----+----+----+----+----+
-          Expiration               |
-+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-Encrypted:
-
-length ::
-       4 byte `Integer`
-       number of bytes that follow 0 - 64 KB
-
-data ::
-     $length bytes
-     ElGamal encrypted data
-
-
-Unencrypted data:
-
-num ::
-     1 byte `Integer` number of `GarlicClove`s to follow
-
-clove ::  a `GarlicClove`
-
-Certificate :: always NULL in the current implementation (3 bytes total, all zeroes)
-
-Message_ID :: 4 byte `Integer`
-
-Expiration :: `Date` (8 bytes)
-
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul>
-<li>
-  When unencrypted, data contains one or more <a href="#struct_GarlicClove">Garlic Cloves</a>.
-<li>
-  The AES encrypted block is padded to a minimum of 128 bytes; with the 32-byte Session Tag
-  the minimum size of the encrypted message is 160 bytes; with the 4 length bytes
-  the minimum size of the Garlic Message is 164 bytes.
-<li>
-  Actual max length is less than 64 KB; see the <a href="{{ site_url('docs/protocol/i2np') }}">I2NP Overview</a>.
-<li>
-  See also the <a href="{{ site_url('docs/how/elgamal-aes') }}">ElGamal/AES specification</a>.
-<li>
-  See also the <a href="{{ site_url('docs/how/garlic-routing') }}">garlic routing specification</a>.
-<li>
-  The 128 byte minimum size of the AES encrypted block is not currently configurable,
-  however the minimum size of a DataMessage in a GarlicClove in a GarlicMessage, with
-  overhead, is 128 bytes anyway. A configurable option to increase the minimum size
-  may be added in the future.
-<li>
-  The message ID is generally set to a random number on transmit and
-  appears to be ignored on receive.
-<li>
-  In the future, the certificate could possibly be used for a HashCash to "pay" for the routing.
-</ul>
-
-
-<h3 id="msg_TunnelData">TunnelData</h3>
-<h4>Description</h4>
-<p>
-  A message sent from a tunnel's gateway or participant to the next participant or endpoint.
-  The data is of fixed length, containing I2NP messages that are fragmented, batched, padded, and encrypted.
-</p>
-<h4>Contents</h4>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-|     tunnnelID     | data              |
-+----+----+----+----+                   |
-|                                       |
-~                                       ~
-~                                       ~
-|                                       |
-+                   +----+----+----+----+
-|                   |
-+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-tunnelId ::
-         4 byte `TunnelId`
-         identifies the tunnel this message is directed at
-
-data ::
-     1024 bytes
-     payload data.. fixed to 1024 bytes
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul>
-<li>
-  The I2NP message ID for this message is set to a new random number at each hop.
-<li>
-  See also the <a href="{{ site_url('docs/spec/tunnel-message') }}">Tunnel Message Specification</a>
-</ul>
-
-
-<h3 id="msg_TunnelGateway">TunnelGateway</h3>
-<h4>Description</h4>
-<p>
-  Wraps another I2NP message to be sent into a tunnel at the tunnel's inbound gateway.
-</p>
-<h4>Contents</h4>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+-//
-| tunnelId          | length  | data...
-+----+----+----+----+----+----+----+-//
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-tunnelId ::
-         4 byte `TunnelId`
-         identifies the tunnel this message is directed at
-
-length ::
-       2 byte `Integer`
-       length of the payload
-
-data ::
-     $length bytes
-     actual payload of this message
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul>
-<li>
-  The payload is an I2NP message with a standard 16-byte header.
-</ul>
-
-
-<h3 id="msg_Data">Data</h3>
-<h4>Description</h4>
-<p>
-  Used by Garlic Messages and Garlic Cloves to wrap arbitrary data.
-</p>
-<h4>Contents</h4>
-<p>
-  A length Integer, followed by opaque data.
-</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+-//-+
-| length            | data... |
-+----+----+----+----+----+-//-+
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul>
-<li>
-  This message contains no routing information and will never be sent "unwrapped".
-  It is only used inside Garlic messages.
-</ul>
-
-
-
-
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-length ::
-       4 bytes
-       length of the payload
-
-data ::
-     $length bytes
-     actual payload of this message
-{% endhighlight %}
-
-<h3 id="msg_TunnelBuild">TunnelBuild</h3>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-| Record 0 ...                          |
-
-|                                       |
-+----+----+----+----+----+----+----+----+
-| Record 1 ...                          |
-
-.....
-+----+----+----+----+----+----+----+----+
-| Record 7 ...                          |
-
-|                                       |
-+----+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-Just 8 `BuildRequestRecord`s attached together
-record size: 528 bytes
-total size: 8*528 = 4224 bytes
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul>
-<li>
-  See also the <a href="{{ site_url('docs/spec/tunnel-creation') }}">tunnel creation specification</a>.
-<li>
-  The I2NP message ID for this message must be set according to the tunnel creation specification.
-<li>
-  While this message is rarely seen in today's network, having been replaced by the VariableTunnelBuild message,
-  it may still be used for very long tunnels, and has not been deprecated.
-  Routers must implement.
-</ul>
-
-
-<h3 id="msg_TunnelBuildReply">TunnelBuildReply</h3>
-{% highlight lang='dataspec' %}
-Same format as `TunnelBuildMessage`, with `BuildResponseRecord`s
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul>
-<li>
-  See also the <a href="{{ site_url('docs/spec/tunnel-creation') }}">tunnel creation specification</a>.
-<li>
-  The I2NP message ID for this message must be set according to the tunnel creation specification.
-<li>
-  While this message is rarely seen in today's network, having been replaced by the VariableTunnelBuildReply message,
-  it may still be used for very long tunnels, and has not been deprecated.
-  Routers must implement.
-</ul>
-
-<h3 id="msg_VariableTunnelBuild">VariableTunnelBuild</h3>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-|num | BuildRequestRecords...
-+----+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-Same format as `TunnelBuildMessage`, except for the addition of a $num field in front
-and $num number of `BuildRequestRecord`s instead of 8
-
-num ::
-       1 byte `Integer`
-       Valid values: 1-8
-
-record size: 528 bytes
-total size: 1+$num*528
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul>
-<li>
-  This message was introduced in router version 0.7.12, and may not be sent to tunnel participants earlier than that version.
-<li>
-  See also the <a href="{{ site_url('docs/spec/tunnel-creation') }}">tunnel creation specification</a>.
-<li>
-  The I2NP message ID for this message must be set according to the tunnel creation specification.
-<li>
-  Typical number of records in today's network is 5.
-</ul>
-
-<h3 id="msg_VariableTunnelBuildReply">VariableTunnelBuildReply</h3>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-|num | BuildResponseRecords...
-+----+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-Same format as `VariableTunnelBuildMessage`, with `BuildResponseRecord`s.
-{% endhighlight %}
-
-
-<h4>Notes</h4>
-<ul>
-<li>
-  This message was introduced in router version 0.7.12, and may not be sent to tunnel participants earlier than that version.
-<li>
-  See also the <a href="{{ site_url('docs/spec/tunnel-creation') }}">tunnel creation specification</a>.
-<li>
-  The I2NP message ID for this message must be set according to the tunnel creation specification.
-<li>
-  Typical number of records in today's network is 5.
-</ul>
-
-
-
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/plugin.html b/i2p2www/pages/site/docs/spec/plugin.html
deleted file mode 100644
index 037fe41da93fee842db8ed1816d3d714f5a0ab32..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/plugin.html
+++ /dev/null
@@ -1,607 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}I2P Plugin Specification{% endblock %}
-{% block lastupdated %}February 2016{% endblock %}
-{% block accuratefor %}0.9.25{% endblock %}
-{% block content %}
-<h3>Overview</h3>
-<p> 
-This document specifies
-a .xpi2p file format (like the Firefox .xpi), but with a simple
-plugin.config description file instead of an XML install.rdf file.
-This file format is used for both initial plugin installs and plugin updates.
-<p>
-In addition, this document provides a brief overview of how the router installs plugins,
-and policies and guidelines for plugin developers.
-<p>
-The basic .xpi2p file format is the same as a i2pupdate.sud file
-(the format used for router updates),
-but the installer will let the user install the
-addon even if it doesn't know the signer's key yet.
-<p>
-As of release 0.9.15, the <a href="updates">su3 file format</a> is supported and is preferred.
-This format enables stronger signing keys.
-
-
-<p>
-The standard directory structure will let users install the following types of addons:
-<ul>
-<li>
-	 console webapps
-<li>
-	 new eepsite with cgi-bin, webapps
-<li>
-	 console themes
-<li>
-	 console translations
-<li>
-	 Java programs
-<li>
-	 Java programs in a separate JVM
-<li>
-	 Any shell script or program
-</ul>
-
-<p>
-A plugin installs all its files in ~/.i2p/plugins/name/ (%APPDIR%\I2P\plugins\name\ on Windows). The installer will prevent
-installation anywhere else, although the plugin can access libraries elsewhere when running.
-
-<p>
-This should be viewed only as a way to make installation, uninstallation, and upgrading easier,
-and to lessen basic inter-plugin conflicts.
-
-<p>
-There is essentially no security model once the plugin is running, however. The plugin runs
-in the same JVM and with the same permissions as the router, and has full access to the file
-system, the router, executing external programs, etc.
-
-<h3>Details</h3>
-<p>
-foo.xpi2p is a <a href="{{ site_url('docs/spec/updates') }}">signed update (sud) file</a> containing the following:
-<h4>
-Standard .sud header prepended to the zip file, containing the following:
-</h4>
-<pre>
-        40-byte <a href="{{ site_url('docs/how/cryptography') }}#DSA">DSA signature</a>
-	16-byte plugin version in UTF-8, padded with trailing zeroes if necessary
-</pre>
-<h4>
-Zip file containing the following:
-</h4>
-<h5>
-plugin.config file
-</h5>
-<p>
-This file is required. It is a
-<a href="{{ site_url('docs/spec/configuration') }}">standard I2P configuration file</a>,
-containing the following properties:
-</p>
-</p><p>
-The following four are required properties.
-The first three must be identical to those in the installed plugin for an update plugin.
-<pre>
-
-	name (will be installed in this directory name)
-		For native plugins, you may want separate names in different packages -
-		foo-windows and foo-linux, for example
-
-	key (<a href="{{ site_url('docs/how/cryptography') }}#DSA">DSA public key</a> as 172 B64 chars ending with '=')
-		Omit for SU3 format.
-
-	signer (yourname@mail.i2p recommended)
-
-	version (must be in a format VersionComparator can parse, e.g. 1.2.3-4)
-		16 bytes max (must match sud version)
-		Valid number separators are '.', '-', and '_'
-		This must be greater than the one in the installed plugin for an update plugin.
-
-
-</pre>
-	Values for the following properties are displayed on /configclients in the router console if present:
-<pre>
-	date (Java time - long int)
-	author (yourname@mail.i2p recommended)
-	websiteURL (http://foo.i2p/)
-	updateURL (http://foo.i2p/foo.xpi2p)
-		The update checker will check bytes 41-56 at this URL
-		to determine whether a newer version is available
-		( Should the checker fetch with ?currentVersion=1.2.3?...
-		  No. If the dev wants to have the URL contain the current version, just
-		  set it in the config file, and remember to change it every release)
-	updateURL.su3 (http://foo.i2p/foo.su3)
-		The location of the su3-format update file, as of 0.9.15
-	description
-	description_xx (for language xx)
-	license
-	disableStop=true
-		Default false.
-		If true, the stop button will not be shown. Use this if there are no
-		webapps and no clients with stopargs.
-
-</pre>
-	The following properties are used to add a link on the console summary bar:
-<pre>
-
-	consoleLinkName (will be added to summary bar)
-	consoleLinkName_xx (for language xx)
-	consoleLinkURL (/appname/index.jsp)
-	consoleLinkTooltip (supported as of 0.7.12-6)
-	consoleLinkTooltip_xx (lang xx as of 0.7.12-6)
-		
-
-</pre>
-	The following optional properties may be used to add a custom icon on the console:
-<pre>
-
-	console-icon (supported as of 0.9.20) Only for webapps.
-		A path within the webapp to a 32x32 image, e.g. /icon.png
-                Applies to all webapps in the plugin.
-
-	icon-code (supported as of 0.9.25) 
-		Provides a console icon for plugins without web resources.
-		A B64 string produced by calling 'net.i2p.data.Base64 encode FILE' 
-		on a 32x32 image png file.
-
-</pre>
-	The following properties are used by the plugin installer:
-<pre>
-
-	type (app/theme/locale/webapp/...) (unimplemented, probably not necessary)
-	min-i2p-version
-	max-i2p-version
-	min-java-version
-	min-jetty-version (supported as of 0.8.13, use 6 for Jetty 6 webapps)
-	max-jetty-version (supported as of 0.8.13, use 5.99999 for Jetty 5 webapps)
-	required-platform-OS (unimplemented - perhaps will be displayed only, not verified)
-	other-requirements (unimplemented e.g. python x.y - not verified by the installer, just displayed to the user)
-	dont-start-at-install=true
-		Default false.
-		Won't start the plugin when it is installed or updated. On initial installation,
-                configures the plugin so the user must manually start it. An update will not change
-                the user's preference to start it if they choose to do so.
-	router-restart-required=true
-		Default false.
-		This does not restart the router or the plugin on an update, it just informs the user that a restart is required.
-		It has no effect on initial plugin installation.
-	update-only=true
-		Default false.
-		If true, will fail if an installation does not exist.
-	install-only=true
-		Default false.
-		If true, will fail if an installation exists.
-	min-installed-version (to update over, if an installation exists)
-	max-installed-version (to update over, if an installation exists)
-	depends=plugin1,plugin2,plugin3 (unimplemented - is this too hard? proposed by sponge)
-	depends-version=0.3.4,,5.6.7 (unimplemented)
-
-</pre>
-	The following property is used for translation plugins:
-<pre>
-	langs=xx,yy,Klingon,... (unimplemented) (yy is the country flag)
-
-</pre>
-<h5>
-Application Directories and Files
-</h5>
-<p>
-Each of the following directories or files is optional, but something must be there or it won't do anything:
-</p>
-<pre>
-console/
-	locale/
-		Only jars containing new resource bundles (translations) for apps in the base I2P installation.
-		Bundles for this plugin should go inside console/webapp/foo.war or lib/foo.jar
-
-	themes/
-		New themes for the router console
-		Place each theme in a subdirectory.
-
-	webapps/
-		(See important notes below about webapps)
-		.wars
-		These will be run at install time unless disabled in webapps.config
-		The war name does not have to be the same as the plugin name.
-		Do not duplicate war names in the base I2P installation.
-
-	webapps.config 
-		Same format as router's webapps.config
-		Also used to specify additional jars in $PLUGIN/lib/ or $I2P/lib for the webapp classpath,
-		with webapps.warname.classpath=$PLUGIN/lib/foo.jar,$I2P/lib/bar.jar
-		NOTE: Currently, the classpath line is only loaded if the warname is
-		the same as the plugin name.
-		NOTE: Prior to router version 0.7.12-9, the router looked for plugin.warname.startOnLoad instead
-		of webapps.warname.startOnLoad. For compatibility with older router versions, a plugin
-		wishing to disable a war should include both lines.
-
-eepsite/
-	(See important notes below about eepsites)
-	cgi-bin/
-	docroot/
-	logs/
-	webapps/
-	jetty.xml
-		The installer will have to do variable substitution in here to set the path
-		The location and name of this file doesn't really matter, as long as it is set in
-		clients.config - it may be more convenient to be up one level from here
-		(that's what the zzzot plugin does)
-
-lib/
-	Put any jars here, and specify them in a classpath line in console/webapps.config and/or clients.config
-</pre>
-<h5>
-clients.config file
-</h5>
-<p>
-This file is optional,
-and specifies clients that will be run when a plugin is started.
-It uses the same format as the router's clients.config file.
-See the <a href="{{ site_url('docs/spec/configuration') }}">clients.config configuration file specification</a>
-for more information about the format and important details
-about how clients are started and stopped.
-</p>
-<pre>
-	property clientApp.0.stopargs=foo bar stop baz
-		If present, the class will be called with these args to stop the client
-		All stop tasks are called with zero delay
-		Note: The router can't tell if your unmanaged clients are running or not.
-		Each should handle stopping an app that isn't running without complaint.
-		That probably goes for starting a client that is already started too.
-	property clientApp.0.uninstallargs=foo bar uninstall baz
-		If present, the class will be called with these args just before deleting $PLUGIN
-		All uninstall tasks are called with zero delay
-	property clientApp.0.classpath=$I2P/lib/foo.bar,$PLUGIN/lib/bar.jar
-	The plugin runner will do variable substitution in the args and stopargs lines as follows:
-		$I2P => i2p base installation dir;
-		$CONFIG => i2p config dir (typically ~/.i2p)
-		$PLUGIN => this plugin's installation dir (typically ~/.i2p/plugins/appname)
-	(See important notes below about running shell scripts or external programs)
-</pre>
-
-
-<h3>Plugin installer tasks</h3>
-This lists what happens when a plugin is installed by I2P.
-<ul>
-
-    <li>The .xpi2p file is downloaded.</li>
-    <li>The .sud signature is verified against stored keys.
-        As of release 0.9.14.1,
-        if there is no matching key, the installation fails, unless an advanced router property is set to allow all keys.</li>
-    <li>Verify the integrity of the zip file.</li>
-    <li>Extract the plugin.config file.</li>
-    <li>Verify the I2P version, to make sure the plugin will work.</li>
-    <li>Check that webapps don't duplicate the existing $I2P applications.</li>
-    <li>Stop the existing plugin (if present).</li>
-    <li>Verify that the install directory does not exist yet if update=false, or ask to overwrite.</li>
-    <li>Verify that the install directory does exist if update=true, or ask to create.</li>
-    <li>Unzip the plugin in to appDir/plugins/name/</li>
-    <li>Add the plugin to plugins.config</li>
-</ul>
-
-<h3>
-Plugin starter tasks
-</h3>
-This lists what happens when plugins are started.
-First, plugins.config is checked to see which plugins need to be started.
-For each plugin:
-<ul>
-    <li>Check clients.config, and load and start each item (add the configured jars to the classpath).</li>
-    <li>Check console/webapp and console/webapp.config. Load and start required items (add the configured jars to the classpath).</li>
-    <li>Add console/locale/foo.jar to the translation classpath if present.</li>
-    <li>Add console/theme to the theme search path if present.</li>
-    <li>Add the summary bar link.</li>
-</ul>
-
-<h3>
-Console webapp notes
-</h3>
-<p>
-Console webapps with background tasks should implement a ServletContextListener
-(see seedless or i2pbote for examples), or override destroy() in the servlet,
-so that they can be stopped.
-As of router version 0.7.12-3, console webapps will always be stopped before they
-are restarted, so you do not need to worry about multiple instances,
-as long as you do this.
-Also as of router version 0.7.12-3, console webapps will be stopped at router shutdown.
-<p>
-Don't bundle library jars in the webapp; put them in lib/ and put a classpath in webapps.config.
-Then you can make separate install and update plugins, where the update plugin does not
-contain the library jars.
-<p>
-Don't include .java or .jsp files; otherwise jetty will recompile them at installation.
-<p>
-For now, a webapp needing to add classpath files in $PLUGIN must be the same name as the plugin.
-For example, a webapp in plugin foo must be named foo.war.
-
-
-<h3>
-Eepsite notes
-</h3>
-<p>
-It isn't clear how to have a plugin install to an existing eepsite.
-The router has no hook to the eepsite, and it may or may not be running,
-and there may be more than one.
-Better is to start your own Jetty instance and I2PTunnel instance,
-for a brand new eepsite.
-<p>
- It can instantiate a new I2PTunnel (somewhat like the i2ptunnel CLI does),
-  but it won't appear in the i2ptunnel gui of course, that's a different instance.
-But that's ok. Then you can start and stop i2ptunnel and jetty together.
-<p>
-So don't count on the router to automatically merge this with some existing eepsite. It probably won't happen.
-Start a new I2PTunnel and Jetty from clients.config.
-The best examples of this are the zzzot and pebble plugins,
-available at <a href="http://{{ i2pconv('stats.i2p') }}/i2p/plugins/">zzz's plugins page</a>.
-<p>
-How to get path substitution into jetty.xml?
-See zzzot and pebble plugins for examples.
-
-
-<h3>
-Client start/stop notes
-</h3>
-<p>
-As of release 0.9.4, the router supports "managed" plugin clients.
-Managed plugin clients are instantiated and started by the <code>ClientAppManager</code>.
-The ClientAppManager maintains a reference to the client and receives updates on the client's state.
-Managed plugin client are preferred, as it is much easier to implement state tracking
-and to start and stop a client. It also is much easier to avoid static references in the client code
-which could lead to excessive memory usage after a client is stopped.
-See the <a href="{{ site_url('docs/spec/configuration') }}">clients.config configuration file specification</a>
-for more information on writing a managed client.
-</p><p>
-For "unmanaged" plugin clients,
-The router has no way to monitor the state of clients started via clients.config.
-The plugin author should handle multiple start or stop calls gracefully, if at all possible,
-by keeping a static state table, or using PID files, etc.
-Avoid logging or exceptions on multiple starts or stops.
-This also goes for a stop call without a previous start.
-As of router version 0.7.12-3, plugins will be stopped at router shutdown,
-which means that all clients with stopargs in clients.config will be called,
-whether or not they were previously started.
-
-
-<h3>
-Shell script and external program notes
-</h3>
-<p>
-To run shell scripts or other external programs, see <a href="http://{{ i2pconv('zzz.i2p') }}/topics/141">{{ i2pconv('zzz.i2p') }}</a>
-<p>
-To work on both Windows and Linux, write a small Java class that checks the OS type, then
-runs ShellCommand on either the .bat or a .sh file you provide.
-<p>
-External programs won't be stopped when the router stops, and a second copy will
-fire up when the router starts. To work around this, you could
-write a wrapper class or shell script that does the usual storage of the PID
-in a PID file, and check for it on start.
-
-
-
-
-<h3>
-Other plugin guidelines
-</h3>
-<ul>
-<li>
-See i2p.scripts branch or any of the sample plugins on zzz's page for a xpi2p file generator to make it easy.
-<li>
-Pack200 of jars and wars is strongly recommended for plugins, it generally shrinks plugins by 60-65&#37;.
-See any of the sample plugins on zzz's page for an example.
-Pack200 unpacking is supported on routers 0.7.11-5 or higher, which is essentially all routers that
-support plugins at all.
-
-<li>
- Plugins should not attempt to write anywhere in $I2P as it may be readonly, and that isn't good policy anyway.
-<li>
- Plugins may write to $CONFIG but keeping files in $PLUGIN only is recommended.
-All files in $PLUGIN will be deleted at uninstall.
-Files elsewhere will not be deleted at uninstall unless the plugin does it explicitly
-with a client in clients.config run with uninstallargs.
-If the user may want to save data after uninstallation, the uninstallargs hook
-could ask.
-
-<li>
- $CWD may be anywhere; do not assume it is in a particular place, do not attempt to read or write files relative to $CWD.
-<li>
- Java programs should find out where they are with the directory getters in I2PAppContext.
-<li>
- Plugin directory is I2PAppContext.getGlobalContext().getAppDir().getAbsolutePath() + "/plugins/" + appname,
-  or put a $PLUGIN argument in the args line in clients.config.
-There is no reliable way to find the i2p install or config or plugin directory without using the
-context API in i2p.jar.
-<li>
- See <a href="http://{{ i2pconv('zzz.i2p') }}/topics/16">Howto</a> for info on generating signing keys and generating/verifying keys and sud files
-<li>
- All config files must be UTF-8.
-<li>
- To run in a separate JVM, use ShellCommand with java -cp foo:bar:baz my.main.class arg1 arg2 arg3.
-Of course, it will be a lot harder to stop the plugin then...
-But with some trickery with PID files it should be possible.
-<li>
- As an alternative to stopargs in clients.config,
- a Java client may register a shutdown hook with I2PAppContext.addShutdownTask().
- But this wouldn't shut down a plugin when upgrading, so stopargs is recommended.
-  Also, set all created threads to daemon mode.
-<li>
- Do not include classes duplicating those in the standard installation. Extend the classes if necessary.
-<li>
- Beware of the different classpath definitions in wrapper.config between old and new installations -
- see classpath section below.
-<li>
- Clients will reject duplicate keys with different keynames, and duplicate keynames with different keys,
-  and different keys or keynames in upgrade packages. Safeguard your keys. Only generate them once.
-<li>
- Do not modify the plugin.config file at runtime as it will be overwritten on upgrade.
-  Use a different config file in the directory for storing runtime configuration.
-<li>
- In general, plugins should not require access to $I2P/lib/router.jar. Do not access router classes,
-unless you are doing something special.
-The router may in the future implement a restricted classpath for plugins that prevents
-access to router classes.
-<li>
-Since each version must be higher than the one before, you could enhance your build
-script to add a build number to the end of the version.
-This helps for testing. Most of zzz's plugins have that feature, check build.xml for an example.
-<li>
-Plugins must never call System.exit().
-<li>
-Please respect licenses by meeting license requirements for any software you bundle.
-<li>
-The router sets the JVM time zone to UTC. If a plugin needs to know the user's actual time zone,
-it is stored by the router in the I2PAppContext property "i2p.systemTimeZone".
-</ul>
-
-<h3>
-Classpaths
-</h3>
-
-The following jars in $I2P/lib can be assumed to be in the standard classpath for all I2P installations,
-no matter how old or how new the original installation:
-<table>
-<tr><th>Jar<th>Contains<th>Usage
-<tr><td>commons-logging.jar
-<td>Apache Logging<td>For plugins requiring Apache logging.
-Prior to Jetty 6 (release 0.9),
-this contained Apache Commons Logging only.
-From release 0.9 to release 0.9.23,
-this contained both Commons Logging and Tomcat JULI.
-As of release 0.9.24.
-this contains Apache Tomcat JULI logging only.
-
-<tr><td>commons-el.jar
-<td>JSP Expression Language<td>For plugins with JSPs that use EL
-
-<tr><td>i2p.jar
-<td>Core API<td>Almost all plugins will need
-
-<tr><td>i2ptunnel.jar
-<td>I2PTunnel<td>For plugins with HTTP or other servers
-
-<tr><td>jasper-compiler.jar
-<td>nothing<td>Empty since Jetty 6 (release 0.9)
-
-<tr><td>jasper-runtime.jar
-<td>Jasper Complier and Runtime, and some Tomcat utils<td>Needed for plugins with JSPs
-
-<tr><td>javax.servlet.jar
-<td>Servlet API<td>Needed for plugins with JSPs
-
-<tr><td>jbigi.jar
-<td>Binaries<td>No plugin should need
-
-<tr><td>mstreaming.jar
-<td>Streaming API<td>Almost all plugins will need
-
-<tr><td>org.mortbay.jetty.jar
-<td>Jetty Base<td>Only plugins starting their own Jetty instance will need.
-Recommended way of starting Jetty is with net.i2p.jetty.JettyStart in jetty-i2p.jar.
-
-<tr><td>router.jar
-<td>Router<td>Only plugins using router context will need; most will not
-
-<tr><td>sam.jar
-<td>SAM<td>No plugin should need
-
-<tr><td>streaming.jar
-<td>Streaming Implementation<td>Almost all plugins will need
-
-<tr><td>systray.jar
-<td>URL Launcher<td>Most plugins should not need
-
-<tr><td>systray4j.jar
-<td>Systray<td>No plugin should need
-
-<tr><td>wrapper.jar
-<td>Router<td>Most plugins should not need
-
-</table>
-
-<p>
-The following jars in $I2P/lib can be assumed to be present for all I2P installations,
-no matter how old or how new the original installation,
-but are not necessarily in the classpath:
-<p>
-<table>
-<tr><th>Jar<th>Contains<th>Usage
-<tr><td>jstl.jar
-<td>Standard Taglib<td>For plugins using JSP tags
-
-<tr><td>standard.jar
-<td>Standard Taglib<td>For plugins using JSP tags
-
-</table>
-
-<p>
- Anything not listed above may not be present in everybody's classpath, even if you
- have it in the classpath in YOUR version of i2p.
- If you need any jar not listed above, add $I2P/lib/foo.jar to the classpath specified
- in clients.config or webapps.config in your plugin.
-<p>
-Previously, a classpath entry specified in clients.config was added to the classpath for
-the entire JVM.
-However, as of 0.7.13-3, this was fixed using class loaders, and now, as originally intended,
-the specified classpath in clients.config is only for the particular thread.
-See the section on JVM crashes below, and
-<a href="http://{{ i2pconv('zzz.i2p') }}/topics/633">this thread on zzz.i2p</a> for background.
-Therefore, specify the full required classpath for each client.
-
-
-<h3>
-Java Version Notes
-</h3>
-I2P has required Java 7 since release 0.9.24 (January 2016).
-I2P has required Java 6 since release 0.9.12 (April 2014).
-Any I2P users on the latest release should be running a 1.7 (7.0) JVM.
-In early 2016,
-unless you require 1.7 language or library features, you should create your plugin so it works on 1.6.
-Later in the year, most of the network will be on 0.9.24 or higher with Java 7.
-<p>
-If your plugin <b>does not require 1.7</b>:
-<ul>
-<li>
-Ensure that all java and jsp files are compiled with source="1.6" target="1.6",
-or source="1.5" target="1.5",
-<li>
-Ensure that all bundled library jars are also for 1.6 or lower.
-</ul>
-
-<p>
-If your plugin <b>requires 1.7</b>:
-<ul>
-<li>
-Note that on your download page.
-<li>
-Add min-java-version=1.7 to your plugin.config
-</ul>
-
-<p>
-In any case, you <b>must</b> set a bootclasspath when compiling with Java 8
-to prevent runtime crashes.
-</p>
-
-
-<h3>
-JVM Crashes When Updating
-</h3>
-Note - this should all be fixed now.
-<p>
-The JVM has a tendency to crash when updating jars in a plugin if that plugin was running
-since i2p was started (even if the plugin was later stopped).
-This may have been fixed with the class loader implementation in 0.7.13-3, but it may not.
-For further testing.
-<p>
-The safest is to design your plugin with the jar inside the war (for a webapp), or to require a restart
-after update, or don't update the jars in your plugin.
-<p>
-Due to the way class loaders work inside a webapp, it _may_ be safe to have external jars if
-you specify the classpath in webapps.config.
-More testing is required to verify this.
-Don't specify the classpath with a 'fake' client in clients.config if it's only
-needed for a webapp - use webapps.config instead.
-<p>
-The least safe, and apparently the source of most crashes, is clients with plugin jars specified
-in the classpath in clients.config.
-
-<p>
-None of this should be a problem on initial install - you should not ever have to require a restart
-for an initial install of a plugin.
-
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/ssu.html b/i2p2www/pages/site/docs/spec/ssu.html
deleted file mode 100644
index 1bedd138b7a6405cd4ebccdabf83415c5fdd5755..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/ssu.html
+++ /dev/null
@@ -1,1148 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}SSU Protocol Specification{% endblock %}
-{% block lastupdated %}November 2015{% endblock %}
-{% block accuratefor %}0.9.24{% endblock %}
-{% block content %}
-
-<p>
-<a href="{{ site_url('docs/transport/ssu') }}">See the SSU page for an overview of the SSU transport</a>.
-</p>
-
-<h2 id="DH">DH Key Exchange</h2>
-<p>
-The initial 2048-bit DH key exchange is described on the
-<a href="{{ site_url('docs/transport/ssu') }}#keys">SSU page</a>.
-This exchange uses the same shared  prime as that used for I2P's
-<a href="{{ site_url('docs/how/cryptography') }}#elgamal">ElGamal encryption</a>.
-</p>
-
-
-<h2 id="header">Message Header</h2>
-
-<p>
-All UDP datagrams begin with a 16 byte MAC (Message Authentication Code)
-and a 16 byte IV (Initialization Vector)
-followed by a variable-size
-payload encrypted with the appropriate key.  The MAC used is 
-HMAC-MD5, truncated to 16 bytes, while the key is a full 32 byte AES256 
-key.  The specific construct of the MAC is the first 16 bytes from:</p>
-<pre>
-  HMAC-MD5(encryptedPayload + IV + (payloadLength ^ protocolVersion), macKey)
-</pre>
-where '+' means append and '^' means exclusive-or.
-</p><p>
-The IV is generated randomly for each packet.
-The encryptedPayload is the encrypted version of the message starting with the flag byte (encrypt-then-MAC).
-The payloadLength used in the MAC is a 2 byte unsigned integer.
-Note that protocolVersion is 0, so the exclusive-or is a no-op.
-The macKey is either the introduction key or is constructed from the
-exchanged DH key (see details below), as specified for each message below.
-<b>WARNING</b> - the HMAC-MD5-128 used here is non-standard,
-see <a href="{{ site_url('docs/how/cryptography') }}#udp">the cryptography page</a> for details.
-
-
-<p>The payload itself (that is, the message starting with the flag byte)
-is AES256/CBC encrypted with the IV and the 
-sessionKey, with replay prevention addressed within its body, 
-explained below.
-</p>
-  
-<p>The protocolVersion is a 2 byte unsigned integer
-and is currently set to 0.  Peers using a different protocol version will
-not be able to communicate with this peer, though earlier versions not
-using this flag are.</p>
-
-<h3>HMAC Specification</h3>
-<ul><li>
-Inner padding: 0x36...
-</li><li>
-Outer padding: 0x5C...
-</li><li>
-Key: 32 bytes
-</li><li>
-Hash digest function: MD5, 16 bytes
-</li><li>
-Block size: 64 bytes
-</li><li>
-MAC size: 16 bytes
-</li><li>
-Example C implementations:
-hmac.h in <a href="https://github.com/orignal/i2pd">i2pd</a>
-and
-I2PHMAC.cpp in <a href="https://github.com/i2pcpp/i2pcpp">i2pcpp</a>.
-</li><li>
-Example Java implementation:
-I2PHMac.java in <a href="https://github.com/i2p/i2p.i2p">i2p</a>
-</li></ul>
-
-
-<h3>Session Key Details</h3>
-The 32-byte session key is created as follows:
-<ol><li>
-Take the exchanged DH key, represented as a positive minimal-length BigInteger byte array (two's complement big-endian)
-</li><li>
-If the most significant bit is 1 (i.e. array[0] & 0x80 != 0),
-prepend a 0x00 byte, as in Java's BigInteger.toByteArray() representation
-</li><li>
-If the byte array is greater than or equal to 32 bytes, use the first (most significant) 32 bytes
-</li><li>
-If the byte array is less than 32 bytes, append 0x00 bytes to extend to 32 bytes.
-<i>Very unlikely - See note below.</i>
-</li></ol>
-
-<h3>MAC Key Details</h3>
-The 32-byte MAC key is created as follows:
-<ol><li>
-Take the exchanged DH key byte array, prepended with a 0x00 byte if necessary,
-from step 2 in the Session Key Details above.
-</li><li>
-If that byte array is greater than or equal to 64 bytes, the MAC key is
-bytes 33-64 from that byte array.
-</li><li>
-If that byte array is less than 64 bytes, the MAC key is the SHA-256 Hash of that byte array.
-<i>As of release 0.9.8. See note below.</i>
-</li></ol>
-Important note: Code before release 0.9.8 was broken and did not correctly handle DH key byte arrays
-between 32 and 63 bytes (steps 3 and 4 above) and the connection will fail.
-As these cases didn't ever work, they were redefined as described above for release 0.9.8,
-and the 0-32 byte case was redefined as well.
-Since the nominal exchanged DH key is 256 bytes, the chances of the mininimal representation
-being less than 64 bytes is vanishingly small.
-
-
-<h3>Header Format</h3>
-<p>Within the AES encrypted payload, there is a minimal common structure
-to the various messages - a one byte flag and a four byte sending 
-timestamp (seconds since the unix epoch).
-</p>
-
-<p>The header format is:</p>
-
-{% highlight lang='dataspec' %}
- Header: 37+ bytes
- Encryption starts with the flag byte.
- +----+----+----+----+----+----+----+----+
- |                  MAC                  |
- +                                       +
- |                                       |
- +----+----+----+----+----+----+----+----+
- |                   IV                  |
- +                                       +
- |                                       |
- +----+----+----+----+----+----+----+----+
- |flag|        time       |              |
- +----+----+----+----+----+              +
- | keying material (optional)            |
- +                                       +
- |                                       |
- ~                                       ~
- |                                       |
- +                        +----+----+----+
- |                        |#opt|         |
- +----+----+----+----+----+----+         +
- | #opt extended option bytes (optional) |
- ~                                       ~
- ~                                       ~
- +----+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-<p>The flag byte contains the following bitfields:</p>
-{% highlight %}
-  Bit order: 76543210 (bit 7 is MSB)
-
-  bits 7-4: payload type
-     bit 3: If 1, rekey data is included. Always 0, unimplemented
-     bit 2: If 1, extended options are included. Always 0 before release 0.9.24.
-  bits 1-0: reserved, set to 0 for compatibility with future uses
-{% endhighlight %}
-
-<p>
-Without rekeying and extended options, the header size is 37 bytes.
-</p>
-
-
-<h3 id="rekey">Rekeying</h3>
-<p>If the rekey flag is set, 64 bytes of keying material follow the 
-timestamp.
-
-<p>When rekeying, the first 32 bytes of the keying material is fed 
-into a SHA256 to produce the new MAC key, and the next 32 bytes are
-fed into a SHA256 to produce the new session key, though the keys are
-not immediately used.  The other side should also reply with the 
-rekey flag set and that same keying material.  Once both sides have 
-sent and received those values, the new keys should be used and the 
-previous keys discarded.  It may be useful to keep the old keys 
-around briefly, to address packet loss and reordering.</p>
-
-<p>NOTE: Rekeying is currently unimplemented.</p>
-
-<h3 id="extend">Extended Options</h3>
-<p>
-If the extended options flag is set, a one byte option 
-size value is appended, followed by that many extended option 
-bytes.
-Extended options has always been part of the specification, but was unimplemented until release 0.9.24.
-When present, the option format is specific to the message type.
-See message documentation below on whether extended options are expected
-for the given message, and the specified format.
-While Java routers have always recognized the flag and options length,
-other implementations have not. Therefore, do not send extended options to
-routers older than release 0.9.24.
-</p>
-
-<h2 id="padding">Padding</h2>
-<p>
-All messages contain 0 or more bytes of padding.
-Each message must be padded to a 16 byte boundary, as required by the <a href="{{ site_url('docs/how/cryptography') }}#AES">AES256 encryption layer</a>.
-Through release 0.9.7, messages were only padded to the next 16 byte boundary,
-and messages not a multiple of 16 bytes could possibly be invalid.
-As of release 0.9.7, messages may be padded to any length as long as the current MTU is honored.
-Any extra 1-15 padding bytes beyond the last block of 16 bytes cannot be encrypted or decrypted and will be ignored.
-However, the full length and all padding is included in the MAC calculation.
-As of release 0.9.8, transmitted messages are not necessarily a multiple of 16 bytes.
-The SessionConfirmed message is an exception, see below.
-</p>
-
-
-<h2 id="keys">Keys</h2>
-<p>
-Signatures in the SessionCreated and SessionConfirmed messages are generated using
-the
-<a href="{{ site_url('docs/spec/common-structures') }}#type_SigningPublicKey">Signing Public Key</a>
-from the
-<a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">Router Identity</a>
-which is distributed out-of-band by publishing in the network database, and the associated
-<a href="{{ site_url('docs/spec/common-structures') }}#type_SigningPrivateKey">Signing Private Key</a>.
-Through release 0.9.15, the signature algorithm was always DSA, with a 40 byte signature.
-As of release 0.9.16, the signature algorithm may be specified by a
-a <a href="{{ site_url('docs/spec/common-structures') }}#type_Certificate">Key Certificate</a>
-in Bob's <a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">Router Identity</a>.
-</p><p>
-Both introduction keys and session keys are 32 bytes,
-and are defined by the
-<a href="{{ site_url('docs/spec/common-structures') }}#type_SessionKey">Common structures specification</a>.
-The key used for the MAC and encryption is specified for each message below.
-</p>
-<p>Introduction keys are delivered through an external channel 
-(the network database, where they are identical to the router Hash for now).
-</p>
-
-
-<h2 id="notes">Notes</h2>
-
-<h3 id="ipv6">IPv6 Notes</h3>
-The protocol specification allows both 4-byte IPv4 and 16-byte IPv6 addresses.
-SSU-over-IPv6 is supported as of version 0.9.8.
-See the documentation of individual messages below for details on IPv6 support.
-
-<h3 id="time">Timestamps</h3>
-While most of I2P uses 8-byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Date">Date</a> timestamps with
-millisecond resolution, SSU uses 4-byte unsigned integer timestamps with one-second resolution.
-Because these values are unsigned, they will not roll over until February 2106.
-
-
-
-
-<h2 id="messages">Messages</h2>
-<p>
-There are 10 messages (payload types) defined:
-</p><p>
-<table border="1">
-<tr><th>Type<th>Message<th>Notes
-<tr><td align="center">0<td>SessionRequest<td>
-<tr><td align="center">1<td>SessionCreated<td>
-<tr><td align="center">2<td>SessionConfirmed<td>
-<tr><td align="center">3<td>RelayRequest<td>
-<tr><td align="center">4<td>RelayResponse<td>
-<tr><td align="center">5<td>RelayIntro<td>
-<tr><td align="center">6<td>Data<td>
-<tr><td align="center">7<td>PeerTest<td>
-<tr><td align="center">8<td>SessionDestroyed<td>Implemented as of 0.8.9
-<tr><td align="center">n/a<td>HolePunch<td>
-</table>
-</p>
-
-<h3 id="sessionRequest">SessionRequest (type 0)</h3>
-<p>
-This is the first message sent to establish a session.
-</p>
-
-<table border="1">
-<tr><td align="right" valign="top"><b>Peer:</b></td>
-    <td>Alice to Bob</td></tr>
-<tr><td align="right" valign="top"><b>Data:</b></td>
-    <td><ul>
-        <li>256 byte X, to begin the DH agreement</li>
-        <li>1 byte IP address size</li>
-        <li>that many byte representation of Bob's IP address</li>
-        <li>N bytes, currently uninterpreted</li>
-	</ul></td></tr>
-<tr><td align="right" valign="top"><b>Crypto Key used:</b></td>
-    <td>Bob's introKey, as retrieved from the network database</td></tr>
-<tr><td align="right" valign="top"><b>MAC Key used:</b></td>
-    <td>Bob's introKey, as retrieved from the network database</td></tr>
-</table>
-
-<p>Message format:</p>
-
-{% highlight lang='dataspec' %}
- +----+----+----+----+----+----+----+----+
- |         X, as calculated from DH      |
- ~                .  .  .                ~
- |                                       |
- +----+----+----+----+----+----+----+----+
- |size| that many byte IP address (4-16) |
- +----+----+----+----+----+----+----+----+
- | arbitrary amount of uninterpreted data|
- ~                .  .  .                ~
-{% endhighlight %}
-
-<p>
-Typical size including header, in current implementation: 304 (IPv4) or 320 (IPv6) bytes
-(before non-mod-16 padding)
-</p>
-
-<h4>Extended Options</h4>
-<p>
-<i>Note: This is a proposal, to be implemented in 0.9.24. Subject to change.</i>
-</p>
-<ul><li>
-Minimum length: 3 (option length byte + 2 bytes)
-</li><li>Option length: 2 minimum
-</li><li>2 bytes flags:<pre>
-Bit order: 15...76543210 (bit 15 is MSB)
-   bit 0: 1 for Alice to request a relay tag from Bob in the SessionCreated response,
-          0 if Alice does not need a relay tag.
-          Note that "1" is the default if no extended options are present
-   bits 15-1: unused, set to 0 for compatibility with future uses
-</pre></ul>
-
-
-<h4>Notes</h4>
-<ul><li>
-IPv4 and IPv6 addresses are supported.
-</li><li>
-The uninterpreted data could possibly be used in the future for challenges.
-</li></ul>
-
-
-
-<h3 id="sessionCreated">SessionCreated (type 1)</h3>
-<p>
-This is the response to a Session Request.
-</p>
-
-<table border="1">
-<tr><td align="right" valign="top"><b>Peer:</b></td>
-    <td>Bob to Alice</td></tr>
-<tr><td align="right" valign="top"><b>Data:</b></td>
-    <td><ul>
-        <li>256 byte Y, to complete the DH agreement</li>
-	<li>1 byte IP address size</li>
-	<li>that many byte representation of Alice's IP address</li>
-	<li>2 byte Alice's port number</li>
-        <li>4 byte relay (introduction) tag which Alice can publish (else 0x00000000)</li>
-        <li>4 byte timestamp (seconds from the epoch) for use in the DSA 
-            signature</li>
-        <li>Bob's <a href="{{ site_url('docs/spec/common-structures') }}#type_Signature">Signature</a> of the critical exchanged data 
-            (X + Y + Alice's IP + Alice's port + Bob's IP + Bob's port + Alice's
-            new relay tag + Bob's signed on time), encrypted with another 
-            layer of encryption using the negotiated sessionKey.  The IV 
-            is reused here.
-            See notes for length information.
-        </li>
-        <li>0-15 bytes of padding of the signature, using random data,
-            to a multiple of 16 bytes, so that the signature + padding may be
-            encrypted with an additional layer of encryption
-            using the negotiated session key as part of the DSA block.
-        </li>
-        <li>N bytes, currently uninterpreted</li>
-	</ul></td></tr>
-<tr><td align="right" valign="top"><b>Crypto Key used:</b></td>
-    <td>Bob's introKey, with an additional layer of encryption over the 40 byte
-        signature and the following 8 bytes padding.</td></tr>
-<tr><td align="right" valign="top"><b>MAC Key used:</b></td>
-    <td>Bob's introKey</td></tr>
-</table>
-
-<p>Message format:</p>
-
-{% highlight lang='dataspec' %}
- +----+----+----+----+----+----+----+----+
- |         Y, as calculated from DH      |
- ~                .  .  .                ~
- |                                       |
- +----+----+----+----+----+----+----+----+
- |size| that many byte IP address (4-16) |
- +----+----+----+----+----+----+----+----+
- | Port (A)| public relay tag  |  signed
- +----+----+----+----+----+----+----+----+
-   on time |                             |
- +----+----+                             +
- |                                       |
- +                                       +
- |             signature                 |
- +                                       +
- |                                       |
- +                                       +
- |                                       |
- +         +----+----+----+----+----+----+
- |         |   (0-15 bytes of padding) 
- +----+----+----+----+----+----+----+----+
-           |                             |
- +----+----+                             +
- |           arbitrary amount            |
- ~        of uninterpreted data          ~
- ~                .  .  .                ~
-{% endhighlight %}
-
-<p>
-Typical size including header, in current implementation: 368 bytes (IPv4 or IPv6)
-(before non-mod-16 padding)
-</p>
-
-<h4>Notes</h4>
-<ul><li>
-IPv4 and IPv6 addresses are supported.
-</li><li>
-If the relay tag is nonzero, Bob is offering to act as an introducer for Alice.
-Alice may subsequently publish Bob's address and the relay tag in the network database.
-</li><li>
-For the signature, Bob must use his external port, as that what Alice will use to verify.
-If Bob's NAT/firewall has mapped his internal port to a different external port,
-and Bob is unaware of it, the verification by Alice will fail.
-</li><li>
-See <a href="#keys">the Keys section above</a> for details on signatures.
-Alice already has Bob's public signing key, from the network database.
-</li><li>
-Through release 0.9.15, the signature was always a 40 byte DSA signature and the
-padding was always 8 bytes. As of release 0.9.16, the signature type and length
-are implied by the type of the <a href="{{ site_url('docs/spec/common-structures') }}#type_SigningPublicKey">Signing Public Key</a>
-in Bob's <a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">Router Identity</a>.
-The padding is as necessary to a multiple of 16 bytes.
-</li><li>
-This is the only message that uses the sender's intro key.
-All others use the receiver's intro key or the established session key.
-</li><li>
-Signed-on time appears to be unused or unverified in the current implementation.
-</li><li>
-The uninterpreted data could possibly be used in the future for challenges.
-</li><li>
-Extended options in the header: Not expected, undefined.
-</li></ul>
-
-
-
-<h3 id="sessionConfirmed">SessionConfirmed (type 2)</h3>
-<p>
-This is the response to a Session Created message and the last step in establishing a session.
-There may be multiple Session Confirmed messages required if the Router Identity must be fragmented.
-</p>
-
-<table border="1">
-<tr><td align="right" valign="top"><b>Peer:</b></td>
-    <td>Alice to Bob</td></tr>
-<tr><td align="right" valign="top"><b>Data:</b></td>
-    <td><ul>
-        <li>1 byte identity fragment info:<pre>
-Bit order: 76543210 (bit 7 is MSB)
-bits 7-4: current identity fragment # 0-14
-bits 3-0: total identity fragments (F) 1-15</pre></li>
-        <li>2 byte size of the current identity fragment</li>
-        <li>that many byte fragment of Alice's
-           <a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">Router Identity</a>
-        </li>
-        <li>After the last identity fragment only:
-            <ul><li>4 byte signed-on time
-            </li></ul>
-        </li>
-        <li>N bytes padding, currently uninterpreted</li>
-        <li>After the last identity fragment only:
-            <ul><li>The remaining bytes contain
-            Alice's <a href="{{ site_url('docs/spec/common-structures') }}#type_Signature">Signature</a> of the critical exchanged 
-            data (X + Y + Alice's IP + Alice's port + Bob's IP + Bob's port
-            + Alice's new relay tag + Alice's signed on time)
-            See notes for length information.
-            </li></ul>
-        </li>
-    </ul></td></tr>
-<tr><td align="right" valign="top"><b>Crypto Key used:</b></td>
-    <td>Alice/Bob sessionKey, as generated from the DH exchange</td></tr>
-<tr><td align="right" valign="top"><b>MAC Key used:</b></td>
-    <td>Alice/Bob MAC Key, as generated from the DH exchange</td></tr>
-</table>
-
-<p>
- <b>Fragment 0 through F-2</b>
-(only if F &gt; 1; currently unused, see notes below) :
-</p>
-{% highlight lang='dataspec' %}
- +----+----+----+----+----+----+----+----+
- |info| cursize |                        |
- +----+----+----+                        +
- |      fragment of Alice's full         |
- ~            Router Identity            ~
- ~                .  .  .                ~
- |                                       |
- +----+----+----+----+----+----+----+----+
- | arbitrary amount of uninterpreted data|
- ~                .  .  .                ~
-{% endhighlight %}
- 
-<p>
- <b>Fragment F-1 (last or only fragment):</b>
-</p>
-{% highlight lang='dataspec' %}
- +----+----+----+----+----+----+----+----+
- |info| cursize |                        |
- +----+----+----+                        +
- |     last fragment of Alice's full     |
- ~            Router Identity            ~
- ~                .  .  .                ~
- |                                       |
- +----+----+----+----+----+----+----+----+
- |  signed on time   |                   |
- +----+----+----+----+                   +
- |  arbitrary amount of uninterpreted    |
- ~      data, until the signature at     ~
- ~       end of the current packet       ~
- |  Packet length must be mult. of 16    |
- +----+----+----+----+----+----+----+----+
- +                                       +
- |                                       |
- +                                       +
- |             signature                 |
- +                                       +
- |                                       |
- +                                       +
- |                                       |
- +----+----+----+----+----+----+----+----+
-{% endhighlight %}
- 
-<p>
-Typical size including header, in current implementation: 480 bytes
-(before non-mod-16 padding)
-</p>
-
-<h4>Notes</h4>
-<ul><li>
-In the current implementation, the maximum fragment size is 512 bytes.
-This should be extended so that longer signatures will work without fragmentation.
-The current implementation does not correctly process signatures split across two fragments.
-</li><li>
-The typical <a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">Router Identity</a>
-is 387 bytes, so no fragmentation is ever necessary.
-If new crypto extends the size of the RouterIdentity, the fragmentation scheme
-must be tested carefully.
-</li><li>
-There is no mechanism for requesting or redelivering missing fragments.
-</li><li>
-The total fragments field F must be set identically in all fragments.
-</li><li>
-See <a href="#keys">the Keys section above</a> for details on DSA signatures.
-</li><li>
-Signed-on time appears to be unused or unverified in the current implementation.
-</li><li>
-Since the signature is at the end, the padding in the last or only packet must pad the total packet to
-a multiple of 16 bytes, or the signature will not get decrypted correctly.
-This is different from all the other message types, where the padding is at the end.
-</li><li>
-Through release 0.9.15, the signature was always a 40 byte DSA signature.
-As of release 0.9.16, the signature type and length
-are implied by the type of the <a href="{{ site_url('docs/spec/common-structures') }}#type_SigningPublicKey">Signing Public Key</a>
-in Alice's <a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">Router Identity</a>.
-The padding is as necessary to a multiple of 16 bytes.
-</li><li>
-Extended options in the header: Not expected, undefined.
-</li></ul>
-
-
-
-
-<h3 id="sessionDestroyed">SessionDestroyed (type 8)</h3>
-<p>
-The Session Destroyed message was implemented (reception only) in release 0.8.1,
-and is sent as of release 0.8.9.
-</p>
-
-<table border="1">
-<tr><td align="right" valign="top"><b>Peer:</b></td>
-    <td>Alice to Bob or Bob to Alice</td></tr>
-<tr><td align="right" valign="top"><b>Data:</b></td>
-    <td>none
-	</td></tr>
-<tr><td align="right" valign="top"><b>Crypto Key used:</b></td>
-    <td>Alice/Bob sessionKey</td></tr>
-<tr><td align="right" valign="top"><b>MAC Key used:</b></td>
-    <td>Alice/Bob MAC Key</td></tr>
-</table>
-
-
-<p>
-This message does not contain any data.
-Typical size including header, in current implementation: 48 bytes
-(before non-mod-16 padding)
-</p>
-
-<h4>Notes</h4>
-<ul><li>
-Destroy messages received with the sender's or receiver's intro key will be ignored.
-</li><li>
-Extended options in the header: Not expected, undefined.
-</li></ul>
-
-
-<h3 id="relayRequest">RelayRequest (type 3)</h3>
-<p>
-This is the first message sent from Alice to Bob to request an introduction to Charlie.
-</p>
-
-<table border="1">
-<tr><td align="right" valign="top"><b>Peer:</b></td>
-    <td>Alice to Bob</td></tr>
-<tr><td align="right" valign="top"><b>Data:</b></td>
-    <td><ul>
-        <li>4 byte relay (introduction) tag, nonzero, as received by Alice in the Session Created message from Bob</li>
-        <li>1 byte IP address size</li>
-        <li>that many byte representation of Alice's IP address</li>
-        <li>2 byte port number (of Alice)</li>
-        <li>1 byte challenge size</li>
-        <li>that many bytes to be relayed to Charlie in the intro</li>
-        <li>Alice's 32-byte introduction key (so Bob can reply with Charlie's info)</li>
-        <li>4 byte nonce of Alice's relay request</li>
-        <li>N bytes, currently uninterpreted</li>
-	</ul></td></tr>
-<tr><td align="right" valign="top"><b>Crypto Key used:</b></td>
-    <td>Bob's introKey, as retrieved from the network database (or Alice/Bob sessionKey, if established)</td></tr>
-<tr><td align="right" valign="top"><b>MAC Key used:</b></td>
-    <td>Bob's introKey, as retrieved from the network database (or Alice/Bob MAC Key, if established)</td></tr>
-</table>
- 
-<p>Message format:</p>
-
-{% highlight lang='dataspec' %}
- +----+----+----+----+----+----+----+----+
- |      relay tag    |size| Alice IP addr
- +----+----+----+----+----+----+----+----+
-      | Port (A)|size| challenge bytes   |
- +----+----+----+----+                   +
- |      to be delivered to Charlie       |
- +----+----+----+----+----+----+----+----+
- | Alice's intro key                     |
- +                                       +
- |                                       |
- +                                       +
- |                                       |
- +                                       +
- |                                       |
- +----+----+----+----+----+----+----+----+
- |       nonce       |                   |
- +----+----+----+----+                   +
- | arbitrary amount of uninterpreted data|
- ~                .  .  .                ~
-{% endhighlight %}
-
-<p>
-Typical size including header, in current implementation: 96 bytes (no Alice IP included) or 112 bytes (4-byte Alice IP included)
-(before non-mod-16 padding)
-</p>
-
-<h4>Notes</h4>
-<ul><li>
-The IP address is only included if it is be different than the
-packet's source address and port. In the current implementation, the
-IP length is always 0 and the port is always 0, and the receiver should
-use the packet's source address and port.
-</li><li>
-This message may be sent via IPv4 or IPv6. If IPv6, Alice must include her IPv4 address and port.
-</li><li>
-If Alice includes her address/port, Bob may perform additional validation before continuing.
-Prior to release 0.9.24, Java I2P rejected any address or port that was different from the connection.
-</li><li>
-Challenge is unimplemented, challenge size is always zero
-</li><li>
-There are no plans to implement relaying for IPv6.
-</li><li>
-Prior to release 0.9.12, Bob's intro key was always used.
-As of release 0.9.12, the session key is used if there is an established session
-between Alice and Bob.
-In practice, there must be an established session, as Alice will only get the
-nonce (introduction tag) from the session created message, and
-Bob will mark the introduction tag invalid once the session is destroyed.
-</li><li>
-Extended options in the header: Not expected, undefined.
-</li></ul>
-
-
-<h3 id="relayResponse">RelayResponse (type 4)</h3>
-<p>
-This is the response to a Relay Request and is sent from Bob to Alice.
-</p>
-
-<table border="1">
-<tr><td align="right" valign="top"><b>Peer:</b></td>
-    <td>Bob to Alice</td></tr>
-<tr><td align="right" valign="top"><b>Data:</b></td>
-    <td><ul>
-        <li>1 byte IP address size</li>
-        <li>that many byte representation of Charlie's IP address</li>
-        <li>2 byte Charlie's port number</li>
-        <li>1 byte IP address size</li>
-        <li>that many byte representation of Alice's IP address</li>
-        <li>2 byte Alice's port number</li>
-        <li>4 byte nonce sent by Alice</li>
-        <li>N bytes, currently uninterpreted</li>
-	</ul></td></tr>
-<tr><td align="right" valign="top"><b>Crypto Key used:</b></td>
-    <td>Alice's introKey, as received in the Relay Request (or Alice/Bob sessionKey, if established)</td></tr>
-<tr><td align="right" valign="top"><b>MAC Key used:</b></td>
-    <td>Alice's introKey, as received in the Relay Request (or Alice/Bob MAC Key, if established)</td></tr>
-</table>
-
-<p>Message format:</p>
-
-{% highlight lang='dataspec' %}
- +----+----+----+----+----+----+----+----+
- |size|    Charlie IP     | Port (C)|size|
- +----+----+----+----+----+----+----+----+
- |    Alice IP       | Port (A)|  nonce
- +----+----+----+----+----+----+----+----+
-           |   arbitrary amount of       |
- +----+----+                             +
- |          uninterpreted data           |
- ~                .  .  .                ~
-{% endhighlight %}
-
-<p>
-Typical size including header, in current implementation: 64 (Alice IPv4) or 80 (Alice IPv6) bytes
-(before non-mod-16 padding)
-</p>
-
-<h4>Notes</h4>
-<ul><li>
-This message may be sent via IPv4 or IPv6.
-</li><li>
-Alice's IP address/port are the apparent IP/port that Bob received the RelayRequest on
-(not necessarily the IP Alice included in the RelayRequest),
-and may be IPv4 or IPv6. Alice currently ignores these on receive.
-</li><li>
-Charlie's IP address must be IPv4, as that is the address that Alice will send
-the SessionRequest to after the Hole Punch.
-</li><li>
-There are no plans to implement relaying for IPv6.
-</li><li>
-Prior to release 0.9.12, Alice's intro key was always used.
-As of release 0.9.12, the session key is used if there is an established session
-between Alice and Bob.
-</li><li>
-Extended options in the header: Not expected, undefined.
-</li></ul>
-
-
-
-<h3 id="relayIntro">RelayIntro (type 5)</h3>
-<p>
-This is the introduction for Alice, which is sent from Bob to Charlie.
-</p>
-
-<table border="1">
-<tr><td align="right" valign="top"><b>Peer:</b></td>
-    <td>Bob to Charlie</td></tr>
-<tr><td align="right" valign="top"><b>Data:</b></td>
-    <td><ul>
-        <li>1 byte IP address size</li>
-        <li>that many byte representation of Alice's IP address</li>
-        <li>2 byte port number (of Alice)</li>
-        <li>1 byte challenge size</li>
-        <li>that many bytes relayed from Alice</li>
-        <li>N bytes, currently uninterpreted</li>
-	</ul></td></tr>
-<tr><td align="right" valign="top"><b>Crypto Key used:</b></td>
-    <td>Bob/Charlie sessionKey</td></tr>
-<tr><td align="right" valign="top"><b>MAC Key used:</b></td>
-    <td>Bob/Charlie MAC Key</td></tr>
-</table>
-
-<p>Message format:</p>
-
-{% highlight lang='dataspec' %}
- +----+----+----+----+----+----+----+----+
- |size|     Alice IP      | Port (A)|size|
- +----+----+----+----+----+----+----+----+
- |      that many bytes of challenge     |
- +                                       +
- |        data relayed from Alice        |
- +----+----+----+----+----+----+----+----+
- | arbitrary amount of uninterpreted data|
- ~                .  .  .                ~
-{% endhighlight %}
-
-<p>
-Typical size including header, in current implementation: 48 bytes
-(before non-mod-16 padding)
-</p>
-
-<h4>Notes</h4>
-<ul><li>
-Alice's IP address is always 4 bytes in the current implementation, because Alice is trying to connect
-to Charlie via IPv4.
-</li><li>
-This message must be sent via an established IPv4 connection, as that's the only way that
-Bob knows Charlie's IPv4 address to return to Alice in the RelayResponse.
-</li><li>
-Challenge is unimplemented, challenge size is always zero
-</li><li>
-Extended options in the header: Not expected, undefined.
-</li></ul>
-
-
-
-
-<h3 id="data">Data (type 6)</h3>
-<p>
-This message is used for data transport and acknowledgment.
-</p>
-
-<table border="1">
-<tr><td align="right" valign="top"><b>Peer:</b></td>
-    <td>Any</td></tr>
-<tr><td align="right" valign="top"><b>Data:</b></td>
-    <td><ul>
-        <li>1 byte flags:<pre>
-   Bit order: 76543210 (bit 7 is MSB)
-   bit 7: explicit ACKs included
-   bit 6: ACK bitfields included
-   bit 5: reserved
-   bit 4: explicit congestion notification (ECN)
-   bit 3: request previous ACKs
-   bit 2: want reply
-   bit 1: extended data included (unused, never set)
-   bit 0: reserved</pre></li>
-        <li>if explicit ACKs are included:<ul>
-	  <li>a 1 byte number of ACKs</li>
-          <li>that many 4 byte MessageIds being fully ACKed</li>
-	  </ul></li>
-        <li>if ACK bitfields are included:<ul>
-          <li>a 1 byte number of ACK bitfields</li>
-          <li>that many 4 byte MessageIds + a 1 or more byte ACK bitfield.
-              The bitfield uses the 7 low bits of each byte, with the high
-              bit specifying whether an additional bitfield byte follows it
-              (1 = true, 0 = the current bitfield byte is the last).  These
-              sequence of 7 bit arrays represent whether a fragment has been
-              received - if a bit is 1, the fragment has been received.  To 
-              clarify, assuming fragments 0, 2, 5, and 9 have been received,
-              the bitfield bytes would be as follows:
-<pre>
-byte 0:
-   Bit order: 76543210 (bit 7 is MSB)
-   bit 7: 1 (further bitfield bytes follow)
-   bit 6: 0 (fragment 6 not received)
-   bit 5: 1 (fragment 5 received)
-   bit 4: 0 (fragment 4 not received)
-   bit 3: 0 (fragment 3 not received)
-   bit 2: 1 (fragment 2 received)
-   bit 1: 0 (fragment 1 not received)
-   bit 0: 1 (fragment 0 received)
-byte 1:
-   Bit order: 76543210 (bit 7 is MSB)
-   bit 7: 0 (no further bitfield bytes)
-   bit 6: 0 (fragment 13 not received)
-   bit 5: 0 (fragment 12 not received)
-   bit 4: 0 (fragment 11 not received)
-   bit 3: 0 (fragment 10 not received)
-   bit 2: 1 (fragment 9 received)
-   bit 1: 0 (fragment 8 not received)
-   bit 0: 0 (fragment 7 not received)
-</pre></li>
-	  </ul></li>
-        <li>If extended data included:<ul>
-          <li>1 byte data size</li>
-          <li>that many bytes of extended data (currently uninterpreted)</li></ul></li>
-        <li>1 byte number of fragments (can be zero)</li>
-        <li>If nonzero, that many message fragments. Each fragment contains:<ul>
-          <li>4 byte messageId</li>
-          <li>3 byte fragment info:<pre>
-   Bit order: 76543210 (bit 7 is MSB)
-   bits 23-17: fragment # 0 - 127
-   bit 16: isLast (1 = true)
-   bits 15-14: unused, set to 0 for compatibility with future uses
-   bits 13-0: fragment size 0 - 16383</pre></li>
-          <li>that many bytes</li></ul>
-        <li>N bytes padding, uninterpreted</li>
-	</ul></td></tr>
-<tr><td align="right" valign="top"><b>Crypto Key used:</b></td>
-    <td>Alice/Bob sessionKey</td></tr>
-<tr><td align="right" valign="top"><b>MAC Key used:</b></td>
-    <td>Alice/Bob MAC Key</td></tr>
-</table>
-
-<p>Message format:</p>
-
-{% highlight lang='dataspec' %}
- +----+----+----+----+----+----+----+----+
- |flag| (additional headers, determined  |
- +----+                                  +
- ~ by the flags, such as ACKs or         ~
- | bitfields                             |
- +----+----+----+----+----+----+----+----+
- |#frg|     messageId     |   frag info  |
- +----+----+----+----+----+----+----+----+
- | that many bytes of fragment data      |
- ~                .  .  .                ~
- |                                       |
- +----+----+----+----+----+----+----+----+
- |     messageId     |   frag info  |    |
- +----+----+----+----+----+----+----+    +
- | that many bytes of fragment data      |
- ~                .  .  .                ~
- |                                       |
- +----+----+----+----+----+----+----+----+
- |     messageId     |   frag info  |    |
- +----+----+----+----+----+----+----+    +
- | that many bytes of fragment data      |
- ~                .  .  .                ~
- |                                       |
- +----+----+----+----+----+----+----+----+
- | arbitrary amount of uninterpreted data|
- ~                .  .  .                ~
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul><li>
-The current implementation adds a limited number of duplicate acks for
-messages previously acked, if space is available.
-</li><li>
-If the number of fragments is zero, this is an ack-only or keepalive message.
-</li><li>
-The ECN feature is unimplemented, and the bit is never set.
-</li><li>
-In the current implementation, the want reply bit is set when the number of
-fragments is greater then zero, and not set when there are no fragments.
-</li><li>
-Extended data is unimplemented and never present.
-</li><li>
-Reception of multiple fragments is supported in all releases.
-Transmission of multiple fragments is implemented in release 0.9.16.
-</li><li>
-As currently implemented, maximum fragments is 64
-(maximum fragment number = 63).
-</li><li>
-As currently implemented, maximum fragment size is of course
-less than the MTU.
-</li><li>
-Take care not to exceed the maximum MTU even if there is a large number of
-ACKs to send.
-</li><li>
-The protocol allows zero-length fragments but there's no reason to send them.
-</li><li>
-In SSU, the data uses a short 5-byte I2NP header followed by the payload
-of the I2NP message instead of the standard 16-byte I2NP header.
-The short I2NP header consists only of
-the one-byte I2NP type and 4-byte expiration in seconds.
-The I2NP message ID is used as the message ID for the fragment.
-The I2NP size is assembled from the fragment sizes.
-The I2NP checksum is not required as UDP message integrity is ensured by decryption.
-</li><li>
-Message IDs are not sequence numbers and are not consecutive.
-SSU does not guarantee in-order delivery.
-While we use the I2NP message ID as the SSU message ID, from the SSU
-protocol view, they are random numbers.
-In fact, since the router uses a single Bloom filter for all peers,
-the message ID must be an actual random number.
-</li><li>
-Because there are no sequence numbers, there is no way to be sure an ACK was received.
-The current implementation routinely sends a large amount of duplicate ACKs.
-Duplicate ACKs should not be taken as an indication of congestion.
-</li><li>
-ACK Bitfield notes:
-The receiver of a data packet does not know how many fragments are in the message unless it has
-received the last fragment. Therefore, the number of bitfield bytes sent in response may be less or
-more than the number of fragments divided by 7.
-For example, if the highest fragment the receiver has seen is number 4, only
-one byte is required to be sent, even if there may be 13 fragments total.
-Up to 10 bytes (i.e. (64 / 7) + 1) may be included for each message ID acked.
-</li><li>
-Extended options in the header: Not expected, undefined.
-</li></ul>
-
-
-
-<h3 id="peerTest">PeerTest (type 7)</h3>
-<p>
-See <a href="{{ site_url('docs/transport/ssu') }}#peerTesting">the SSU overview page</a> for details.
-</p>
-
-<table border="1">
-<tr><td align="right" valign="top"><b>Peer:</b></td>
-    <td>Any</td></tr>
-<tr><td align="right" valign="top"><b>Data:</b></td>
-    <td><ul>
-        <li>4 byte nonce</li>
-        <li>1 byte IP address size (may be zero)</li>
-        <li>that many byte representation of Alice's IP address, if size &gt; 0</li>
-        <li>2 byte Alice's port number</li>
-        <li>Alice's or Charlie's 32-byte introduction key</li>
-        <li>N bytes, currently uninterpreted</li>
-	</ul></td></tr>
-<tr><td align="right" valign="top"><b>Crypto Key used:</b></td>
-    <td>
-Listed in order of occurrence:
-<ol><li>
-When sent from Alice to Bob:
-Alice/Bob sessionKey
-(The protocol also permits Bob's introKey if Alice and Bob do not have an established session,
- but in the current implementation Alice always selects a Bob that is established.
- As of release 0.9.15, Bob will reject PeerTests from peers without an established session.)
-</li><li>
-When sent from Bob to Charlie:
-Bob/Charlie sessionKey
-</li><li>
-When sent from Charlie to Bob:
-Bob/Charlie sessionKey
-</li><li>
-When sent from Bob to Alice:
-Alice's introKey, as received in the Peer Test message from Alice
-</li><li>
-When sent from Charlie to Alice:
-Alice's introKey, as received in the Peer Test message from Bob
-</li><li>
-When sent from Alice to Charlie:
-Charlie's introKey, as received in the Peer Test message from Charlie
-</li></ol>
-    </td></tr>
-<tr><td align="right" valign="top"><b>MAC Key used:</b></td>
-    <td>
-Listed in order of occurrence:
-<ol><li>
-When sent from Alice to Bob:
-Alice/Bob MAC Key
-(The protocol also permits Bob's introKey if Alice and Bob do not have an established session,
- but in the current implementation Alice always selects a Bob that is established.
- As of release 0.9.15, Bob will reject PeerTests from peers without an established session.)
-</li><li>
-When sent from Bob to Charlie:
-Bob/Charlie MAC Key
-</li><li>
-When sent from Charlie to Bob:
-Bob/Charlie MAC Key
-</li><li>
-When sent from Bob to Alice:
-Alice's introKey, as received in the Peer Test message from Alice
-</li><li>
-When sent from Charlie to Alice:
-Alice's introKey, as received in the Peer Test message from Bob
-</li><li>
-When sent from Alice to Charlie:
-Charlie's introKey, as received in the Peer Test message from Charlie
-</li></ol>
-    </td></tr>
-</table>
-
-<p>Message format:</p>
-
-{% highlight lang='dataspec' %}
- +----+----+----+----+----+----+----+----+
- |    test nonce     |size| Alice IP addr
- +----+----+----+----+----+----+----+----+
-      | Port (A)|                        |
- +----+----+----+                        +
- | Alice or Charlie's                    |
- + introduction key (Alice's is sent to  +
- | Bob and Charlie, while Charlie's is   |
- + sent to Alice)                        +
- |                                       |
- +              +----+----+----+----+----+
- |              | arbitrary amount of    |
- +----+----+----+                        |
- | uninterpreted data                    |
- ~                .  .  .                ~
-{% endhighlight %}
-
-<p>
-Typical size including header, in current implementation: 80 bytes
-(before non-mod-16 padding)
-</p>
-
-<h4>Notes</h4>
-<ul><li>
-When sent by Alice, IP address size is 0, IP address is not present, and port is 0,
-as Bob and Charlie do not use the data;
-the point is to determine Alice's true IP address/port and tell Alice;
-Bob and Charlie don't care what Alice thinks her address is.
-</li><li>
-When sent by Bob or Charlie, IP and port are present, and
-IP address is always 4 bytes in the current implementation.
-IPv6 testing is not currently supported.
-</li><li>
-IPv6 Notes:
-Only testing of IPv4 addresses is supported.
-Therefore, all Alice-Bob and Alice-Charlie communication must be via IPv4.
-Bob-Charlie communication, however, may be via IPv4 or IPv6.
-Alice's address, when specified in the PeerTest message, must be 4 bytes.
-</li><li>
-A peer must maintain a table of active test states (nonces).
-On reception of a Peer Test message, look up the nonce in the table.
-If found, it's an existing test and you know your role (Alice, Bob, or Charlie).
-Otherwise, if the IP is not present and the port is 0, this is a new test and you are Bob.
-Otherwise, this is a new test and you are Charlie.
-</li><li>
-As of release 0.9.15, Alice must have an established session with Bob and use the session key.
-</li><li>
-Extended options in the header: Not expected, undefined.
-</li></ul>
-
-<h3 id="holePunch">HolePunch</h3>
-<p>
-A HolePunch is simply a UDP packet with no data.
-It is unauthenticated and unencrypted.
-It does not contain a SSU header, so it does not have a message type number.
-It is sent from Charlie to Alice as a part of the Introduction sequence.
-</p>
-
-
-<h2><a name="sampleDatagrams">Sample datagrams</a></h2>
-
-<b>Minimal data message (no fragments, no ACKs, no NACKs, etc)</b><br />
-<i>(Size: 39 bytes)</i>
-
-{% highlight lang='dataspec' %}
- +----+----+----+----+----+----+----+----+
- |                  MAC                  |
- +                                       +
- |                                       |
- +----+----+----+----+----+----+----+----+
- |                   IV                  |
- +                                       +
- |                                       |
- +----+----+----+----+----+----+----+----+
- |flag|        time       |flag|#frg|    |
- +----+----+----+----+----+----+----+    +
- |  padding to fit a full AES256 block   |
- +----+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-<b>Minimal data message with payload</b><br />
-<i>(Size: 46+fragmentSize bytes)</i>
-
-{% highlight lang='dataspec' %}
- +----+----+----+----+----+----+----+----+
- |                  MAC                  |
- +                                       +
- |                                       |
- +----+----+----+----+----+----+----+----+
- |                   IV                  |
- +                                       +
- |                                       |
- +----+----+----+----+----+----+----+----+
- |flag|        time       |flag|#frg|
- +----+----+----+----+----+----+----+----+
-   messageId    |   frag info  |         |
- +----+----+----+----+----+----+         +
- | that many bytes of fragment data      |
- ~                .  .  .                ~
- |                                       |
- +----+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/streaming.html b/i2p2www/pages/site/docs/spec/streaming.html
deleted file mode 100644
index 9925a40580fa3a9b847d6250254b5bb1c67b9f0a..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/streaming.html
+++ /dev/null
@@ -1,169 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}{% trans %}Streaming Library Specification{% endtrans %}{% endblock %}
-{% block lastupdated %}{% trans %}June 2015{% endtrans %}{% endblock %}
-{% block accuratefor %}0.9.20{% endblock %}
-{% block content %}
-<p><a href="{{ site_url('docs/api/streaming') }}">{% trans -%}
-See the Streaming page for an overview of the Streaming Library.
-{%- endtrans %}</a></p>
-
-<h2>{% trans %}Protocol Specification{% endtrans %}</h2>
-<h3>{% trans %}Packet Format{% endtrans %}</h3>
-<p>{% trans -%}
-The format of a single packet in the streaming protocol is:
-{%- endtrans %}</p>
-{% highlight lang='dataspec' %}
-
-+----+----+----+----+----+----+----+----+
-| send Stream ID    | rcv Stream ID     |
-+----+----+----+----+----+----+----+----+
-| sequence  Num     | ack Through       |
-+----+----+----+----+----+----+----+----+
-| nc |   NACKs ...
-+----+----+----+----+----+----+----+----+
-     | rd |  flags  | opt size| opt data
-+----+----+----+----+----+----+----+----+
-   ...                                  |
-+----+----+----+----+----+----+----+----+
-|   payload ...
-+----+----+----+-//
-
-
-{% endhighlight %}
-
-<table>
-<tr><th>{{ _('Field') }}<th>{{ _('Length') }}<th>{{ _('Contents') }}
-<tr><td>sendStreamId <td>4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>
-<td>Random number selected by the packet recipient before sending the first SYN reply packet
-and constant for the life of the connection.
-0 in the SYN message sent by the connection originator, and in subsequent messages, until a SYN reply is received,
-containing the peer's stream ID.
-
-<tr><td>receiveStreamId <td>4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a>
-<td>Random number selected by the packet originator before sending the first SYN packet
-and constant for the life of the connection. May be 0 if unknown, for example in a RESET packet.
-
-<tr><td>sequenceNum <td>4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a><td>
-The sequence for this message, starting at 0 in the SYN message,
-and incremented by 1 in each message except for plain ACKs and retransmissions.
-If the sequenceNum is 0 and the SYN flag is not set, this is a plain ACK 
-packet that should not be ACKed.
-
-<tr><td>ackThrough <td>4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a><td>
-The highest packet sequence number that was received
-on the receiveStreamId.  This field is ignored on the initial
-connection packet (where receiveStreamId is the unknown id) or
-if the NO_ACK flag set.
-All packets up to and including this sequence number are ACKed,
-EXCEPT for those listed in NACKs below.
-
-<tr><td>NACK count<td>1 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a><td>
-The number of 4-byte NACKs in the next field
-
-<tr><td>NACKs <td>n * 4 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integers</a><td>
-Sequence numbers less than ackThrough that are not yet received.
-Two NACKs of a packet is a request for a 'fast retransmit' of that packet.
-
-<tr><td>resendDelay<td>1 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a><td>
-How long is the creator of this packet going to wait before
-resending this packet (if it hasn't yet been ACKed).  The 
-value is seconds since the packet was created.
-Currently ignored on receive.
-
-<tr><td>flags <td>2 byte value<td>
-See below.
-
-<tr><td>option size<td>2 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a><td>
-The number of bytes in the next field
-
-<tr><td>option data<td>0 or more bytes<td>
-As specified by the flags. See below.
-
-<tr><td>payload <td>remaining packet size<td>
-</table>
-
-<h3>{% trans %}Flags and Option Data Fields{% endtrans %}</h3>
-<p>{% trans -%}
-The flags field above specifies some metadata about the packet, and in
-turn may require certain additional data to be included.  The flags are
-as follows. Any data structures specified must be added to the options area
-in the given order.
-{%- endtrans %}</p>
-
-<p>
-Bit order: 15....0 (15 is MSB)
-</p>
-<table>
-<tr><th>Bit<th>Flag<th>Option Order<th>Option Data<th>Function
-<tr><td>0<td>SYNCHRONIZE<td align="center">--<td align="center">--<td>
-Similar to TCP SYN. Set in the initial packet and in the first response.
-FROM_INCLUDED and SIGNATURE_INCLUDED must be set also.
-<tr><td>1<td>CLOSE<td align="center">--<td align="center">--<td>
-Similar to TCP FIN. If the response to a SYNCHRONIZE fits in a single message, the response
-will contain both SYNCHRONIZE and CLOSE.
-SIGNATURE_INCLUDED must be set also.
-<tr><td>2<td>RESET<td align="center">--<td align="center">--<td>
-Abnormal close.
-SIGNATURE_INCLUDED must be set also.
-Prior to release 0.9.20, due to a bug, FROM_INCLUDED must also be set.
-<tr><td>3<td>SIGNATURE_INCLUDED<td align="center">4<td>variable length <a href="{{ site_url('docs/spec/common-structures') }}#type_Signature">Signature</a>
-<td>
-Currently sent only with SYNCHRONIZE, CLOSE, and RESET, where it is required,
-and with ECHO, where it is required for a ping.
-The signature uses the Destination's <a href="{{ site_url('docs/spec/common-structures') }}#type_SigningPublicKey">signing keys</a> 
-to sign the entire header and payload with the space in the option data field 
-for the signature being set to all zeroes.
-<br>
-Prior to release 0.9.11, the signature was always 40 bytes.
-As of release 0.9.11, the signature may be variable-length, see below for details.
-<tr><td>4<td>SIGNATURE_REQUESTED<td align="center">--<td align="center">--<td>
-Unused. Requests every packet in the other direction to have SIGNATURE_INCLUDED
-<tr><td>5<td>FROM_INCLUDED<td align="center">2<td>387+ byte <a href="{{ site_url('docs/spec/common-structures') }}#struct_Destination">Destination</a>
-<td>
-Currently sent only with SYNCHRONIZE, where it is required,
-and with ECHO, where it is required for a ping.
-Prior to release 0.9.20, due to a bug, must also be sent with RESET.
-<tr><td>6<td>DELAY_REQUESTED<td align="center">1<td>2 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a><td>
-Optional delay.
-How many milliseconds the sender of this packet wants the recipient
-to wait before sending any more data.
-A value greater than 60000 indicates choking.
-<tr><td>7<td>MAX_PACKET_SIZE_INCLUDED<td align="center">3<td>2 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Integer">Integer</a><td>
-Currently sent with SYNCHRONIZE only.
-Was also sent in retransmitted packets until release 0.9.1.
-<tr><td>8<td>PROFILE_INTERACTIVE<td align="center">--<td align="center">--<td>
-Unused or ignored; the interactive profile is unimplemented.
-<tr><td>9<td>ECHO<td align="center">--<td align="center">--<td>
-Unused except by ping programs.
-If set, most other options are ignored. See
-<a href="{{ site_url('docs/api/streaming') }}">the streaming docs</a>.
-<tr><td>10<td>NO_ACK<td align="center">--<td align="center">--<td>
-This flag simply tells the recipient to ignore the ackThrough field in the header.
-Currently set in the inital SYN packet, otherwise the ackThrough field is always valid.
-Note that this does not save any space, the ackThrough field is before the flags
-and is always present.
-<tr><td>11-15<td>unused<td><td><td>
-Set to zero for compatibility with future uses.
-</table>
-
-<h4>Variable Length Signature Notes</h4>
-<p>
-Prior to release 0.9.11, the signature in the option field was always 40 bytes.
-As of release 0.9.11, the signature is variable length.
-The Signature type and length are inferred from the type of key used in the FROM_INCLUDED option
-and the <a href="{{ site_url('docs/spec/common-structures') }}#type_Signature">Signature documentation</a>.
-<ul><li>
-When a packet contains both FROM_INCLUDED and SIGNATURE_INCLUDED (as in SYNCHRONIZE), the inference may be made directly.
-</li><li>
-When a packet does not contain FROM_INCLUDED, the inference must be made from a previous SYNCHRONIZE packet.
-</li><li>
-When a packet does not contain FROM_INCLUDED, and there was no previous SYNCHRONIZE packet
-(for example a stray CLOSE or RESET packet), the inference can be made from the length of the remaining options
-(since SIGNATURE_INCLUDED is the last option), but the packet will probably be discarded anyway, since there is no FROM available
-to validate the signature.
-If more option fields are defined in the future, they must be accounted for.
-</li></ul>
-</p>
-
-
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/tunnel-creation.html b/i2p2www/pages/site/docs/spec/tunnel-creation.html
deleted file mode 100644
index eb233ac158aa18ff552b605d3a394ccba2c75b5f..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/tunnel-creation.html
+++ /dev/null
@@ -1,355 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}Tunnel Creation{% endblock %}
-{% block lastupdated %}January 2016{% endblock %}
-{% block accuratefor %}0.9.24{% endblock %}
-{% block content %}
-
-This page documents the current tunnel build implementation.
-
-<h2 id="tunnelCreate.overview">Tunnel Creation Specification</h2>
-
-<p>
-This document specifies the details of the encrypted tunnel build messages
-used to create tunnels using a "non-interactive telescoping" method.
-See <a href="{{ site_url('docs/tunnels/implementation') }}">the tunnel build document</a>
-for an overview of the process, including peer selection and ordering methods.
-
-<p>The tunnel creation is accomplished by a single message passed along
-the path of peers in the tunnel, rewritten in place, and transmitted
-back to the tunnel creator.  This single tunnel message is made up
-of a variable number of records (up to 8) - one for each potential peer in
-the tunnel.   Individual records are asymmetrically
-<a href="{{ site_url('docs/how/cryptography') }}#elgamal">(ElGamal)</a>
-encrypted to be
-read only by a specific peer along the path, while an additional
-symmetric layer of encryption
-<a href="{{ site_url('docs/how/cryptography') }}#AES">(AES)</a>
-is added at each hop so as to expose
-the asymmetrically encrypted record only at the appropriate time.</p>
-
-<h3 id="number">Number of Records</h3>
-Not all records must contain valid data.
-The build message for a 3-hop tunnel, for example, may contain more records
-to hide the actual length of the tunnel from the participants.
-There are two build message types. The original
-<a href="{{ site_url('docs/spec/i2np') }}#msg_TunnelBuild">Tunnel Build Message</a> (TBM)
-contains 8 records, which is more than enough for any practical tunnel length.
-The newer
-<a href="{{ site_url('docs/spec/i2np') }}#msg_VariableTunnelBuild">Variable Tunnel Build Message</a> (VTBM)
-contains 1 to 8 records. The originator may trade off the size of the message
-with the desired amount of tunnel length obfuscation.
-<p>
-In the current network, most tunnels are 2 or 3 hops long.
-The current implementation uses a 5-record VTBM to build tunnels of 4 hops or less,
-and the 8-record TBM for longer tunnels.
-The 5-record VTBM (which, when fragmented, fits in three 1KB tunnel messaages) reduces network traffic
-and increases  build sucess rate, because smaller messages are less likely to be dropped.
-<p>
-The reply message must be the same type and length as the build message.
-
-
-<h3 id="tunnelCreate.requestRecord">Request Record Specification</h3>
-
-Also specified in the
-<a href="{{ site_url('docs/spec/i2np') }}#struct_BuildRequestRecord">I2NP Specification</a>
-
-<p>Cleartext of the record, visible only to the hop being asked:</p><pre>
-  bytes     0-3: tunnel ID to receive messages as
-  bytes    4-35: local router identity hash
-  bytes   36-39: next tunnel ID
-  bytes   40-71: next router identity hash
-  bytes  72-103: AES-256 tunnel layer key
-  bytes 104-135: AES-256 tunnel IV key
-  bytes 136-167: AES-256 reply key
-  bytes 168-183: AES-256 reply IV
-  byte      184: flags
-  bytes 185-188: request time (in hours since the epoch, rounded down)
-  bytes 189-192: next message ID
-  bytes 193-221: uninterpreted / random padding</pre>
-
-<p>The next tunnel ID and next router identity hash fields are used to
-specify the next hop in the tunnel, though for an outbound tunnel
-endpoint, they specify where the rewritten tunnel creation reply
-message should be sent.  In addition, the next message ID specifies the
-message ID that the message (or reply) should use.</p>
-
-<p>
-The tunnel layer key, tunnel IV key, reply key, and reply IV
-are each random 32-byte values generated by the creator,
-for use in this build request record only.
-</p>
-
-<p>The flags field contains the following:
-<pre>
- Bit order: 76543210 (bit 7 is MSB)
- bit 7: if set, allow messages from anyone
- bit 6: if set, allow messages to anyone, and send the reply to the
-        specified next hop in a Tunnel Build Reply Message
- bits 5-0: Undefined, must set to 0 for compatibility with future options
-</pre>
-<p>
-Bit 7 indicates that the hop will be an inbound gateway (IBGW).
-Bit 6 indicates that the hop will be an outbound endpoint (OBEP).
-If neither bit is set, the hop will be an intermediate participant.
-Both cannot be set at once.
-</p>
-
-<h4>Request Record Creation</h4>
-<p>
-Every hop gets a random Tunnel ID.
-The current and next-hop Tunnel IDs are filled in.
-Every record gets a random tunnel IV key, reply IV, layer key, and reply key.
-</p>
-
-
-<h4 id="encryption">Request Record Encryption</h4>
-
-<p>That cleartext record is <a href="{{ site_url('docs/how/cryptography') }}#elgamal">ElGamal 2048 encrypted</a> with the hop's
-public encryption key and formatted into a 528 byte record:</p><pre>
-  bytes   0-15: First 16 bytes of the SHA-256 of the current hop's router identity
-  bytes 16-527: ElGamal-2048 encrypted request record</pre>
-<p>
-In the 512-byte encrypted record,
-the ElGamal data contains bytes 1-256 and 258-513 of the
-<a href="{{ site_url('docs/how/cryptography') }}#elgamal">514-byte ElGamal encrypted block</a>.
-The two padding bytes from the block (the zero bytes at locations 0 and 257) are removed.
-</p>
-<p>Since the cleartext uses the full field, there is no need for
-additional padding beyond <code>SHA256(cleartext) + cleartext</code>.</p>
-
-<p>
-Each 528-byte record is then iteratively encrypted
-(using AES decryption, with the reply key and reply IV for each hop) so that the router identity will only be in cleartext
-for the hop in question.
-</p>
-
-<h3 id="tunnelCreate.hopProcessing">Hop Processing and Encryption</h3>
-
-<p>When a hop receives a TunnelBuildMessage, it looks through the
-records contained within it for one starting with their own identity
-hash (trimmed to 16 bytes).  It then decrypts the ElGamal block from
-that record and retrieves the protected cleartext.  At that point,
-they make sure the tunnel request is not a duplicate by feeding the 
-AES-256 reply key into a Bloom filter.
-Duplicates or invalid requests are dropped.
-Records that are not stamped with the current hour,
-or the previous hour if shortly after the top of the hour,
-must be dropped.
-For example, take the hour in the timestamp, convert to a full time,
-then if it's more than 65 minutes behind or 5 minutes ahead of the
-current time, it is invalid.
-The Bloom filter must have a duration of at least one hour
-(plus a few minutes, to allow for clock skew),
-so that duplicate records in the current hour that are not rejected
-by checking the hour timestamp in the record, will be rejected by the filter.
-</p>
-
-<p>After deciding whether they will agree to participate in the tunnel
-or not, they replace the record that had contained the request with
-an encrypted reply block.  All other records are <a href="{{ site_url('docs/how/cryptography') }}#AES">AES-256
-encrypted</a> with the included reply key and IV. Each is
-AES/CBC encrypted separately with the same reply key and reply IV.
-The CBC mode is not continued (chained) across records.</p>
-
-<p>
-Each hop knows only its own response.
-If it agrees, it will maintain the tunnel until expiration,
-even if it will not be used,
-as it cannot know whether all other hops agreed.
-</p>
-
-
-<h4 id="tunnelCreate.replyRecord">Reply Record Specification</h4>
-
-<p>After the current hop reads their record, they replace it with a
-reply record stating whether or not they agree to participate in the
-tunnel, and if they do not, they classify their reason for
-rejection.  This is simply a 1 byte value, with 0x0 meaning they
-agree to participate in the tunnel, and higher values meaning higher
-levels of rejection.
-<p>
-The following rejection codes are defined:
-<ul>
-<li>
-TUNNEL_REJECT_PROBABALISTIC_REJECT = 10
-<li>
-TUNNEL_REJECT_TRANSIENT_OVERLOAD = 20
-<li>
-TUNNEL_REJECT_BANDWIDTH = 30
-<li>
-TUNNEL_REJECT_CRIT = 50
-</ul>
-To hide other causes, such as router shutdown, from peers, the current implementation
-uses TUNNEL_REJECT_BANDWIDTH for almost all rejections.
-
-<p>
-  The reply is encrypted with the AES session
-key delivered to it in the encrypted block, padded with 495 bytes of random data
-to reach the full record size.
-The padding is placed before the status byte:
-</p><pre>
-  AES-256-CBC(SHA-256(padding+status) + padding + status, key, IV)
-
-  bytes   0-31 : SHA-256 of bytes 32-527
-  bytes 32-526 : Random padding
-  byte 527     : Reply value
-</pre>
-<p>
-This is also described in the
-<a href="{{ site_url('docs/spec/i2np') }}#struct_BuildResponseRecord">I2NP spec</a>.
-</p>
-
-<h3 id="tunnelCreate.requestPreparation">Tunnel Build Message Preparation</h3>
-
-<p>When building a new Tunnel Build Message, all of the Build Request Records must first be 
-built and asymmetrically encrypted using
-<a href="{{ site_url('docs/how/cryptography') }}#elgamal">ElGamal</a>.
-Each record is then
-premptively decrypted with the reply keys and IVs of the hops earlier in the
-path, using
-<a href="{{ site_url('docs/how/cryptography') }}#AES">AES</a>.
-That decryption should be run in reverse order so that the
-asymmetrically encrypted data will show up in the clear at the
-right hop after their predecessor encrypts it.</p>
-
-<p>The excess records not needed for individual requests are simply
-filled with random data by the creator.</p>
-
-<h3 id="tunnelCreate.requestDelivery">Tunnel Build Message Delivery</h3>
-
-<p>For outbound tunnels, the delivery is done directly from the tunnel
-creator to the first hop, packaging up the TunnelBuildMessage as if
-the creator was just another hop in the tunnel.  For inbound
-tunnels, the delivery is done through an existing outbound tunnel.
-The outbound tunnel is generally from the same pool as the new tunnel being built.
-If no outbound tunnel is available in that pool, an outbound exploratory tunnel is used.
-At startup, when no outbound exploratory tunnel exists yet, a fake 0-hop
-outbound tunnel is used.</p>
-
-<h3 id="tunnelCreate.endpointHandling">Tunnel Build Message Endpoint Handling</h3>
-
-<p>
-For creation of an outbound tunnel,
-when the request reaches an outbound endpoint (as determined by the
-'allow messages to anyone' flag), the hop is processed as usual,
-encrypting a reply in place of the record and encrypting all of the
-other records, but since there is no 'next hop' to forward the
-TunnelBuildMessage on to, it instead places the encrypted reply
-records into a
-<a href="{{ site_url('docs/spec/i2np') }}#msg_TunnelBuildReply">TunnelBuildReplyMessage</a>
-or
-<a href="{{ site_url('docs/spec/i2np') }}#msg_VariableTunnelBuildReply">VariableTunnelBuildReplyMessage</a>
-(the type of message and number of records must match that of the request)
-and delivers it to the
-reply tunnel specified within the request record.  That reply tunnel
-forwards the Tunnel Build Reply Message back to the tunnel creator,
-<a href="{{ site_url('docs/tunnels/implementation') }}#tunnel.operation">just as for any other message</a>.
-The tunnel creator then
-processes it, as described below.</p>
-
-<p>The reply tunnel was selected by the creator as follows:
-Generally it is an inbound tunnel from the same pool as the new outbound tunnel being built.
-If no inbound tunnel is available in that pool, an inbound exploratory tunnel is used.
-At startup, when no inbound exploratory tunnel exists yet, a fake 0-hop
-inbound tunnel is used.</p>
-
-<p>
-For creation of an inbound tunnel,
-when the request reaches the inbound endpoint (also known as the
-tunnel creator), there is no need to generate an explicit Tunnel Build Reply Message, and
-the router processes each of the replies, as below.</p>
-
-<h3 id="tunnelCreate.replyProcessing">Tunnel Build Reply Message Processing</h3>
-
-<p>To process the reply records, the creator simply has to AES decrypt
-each record individually, using the reply key and IV of each hop in
-the tunnel after the peer (in reverse order).  This then exposes the
-reply specifying whether they agree to participate in the tunnel or
-why they refuse.  If they all agree, the tunnel is considered
-created and may be used immediately, but if anyone refuses, the
-tunnel is discarded.</p>
-
-<p>
-The agreements and rejections are noted in each peer's
-<a href="{{ site_url('docs/how/peer-selection') }}">profile</a>, to be used in future assessments
-of peer tunnel capacity.
-
-
-<h2 id="tunnelCreate.notes">History and Notes</h2>
-<p>
-This strategy came about during a discussion on the I2P mailing list
-    between Michael Rogers, Matthew Toseland (toad), and jrandom regarding
-    the predecessor attack.  See: <ul>
-    <li><a href="http://osdir.com/ml/network.i2p/2005-10/msg00138.html">Summary</a></li>
-    <li><a href="http://osdir.com/ml/network.i2p/2005-10/msg00129.html">Reasoning</a></li>
-    </ul></li>
-It was introduced in release 0.6.1.10 on 2006-02-16, which was the last time
-a non-backward-compatible change was made in I2P.
-</p>
-
-<p>
-Notes:
-<ul>
-<li>This design does not prevent two hostile peers within a tunnel from
-tagging one or more request or reply records to detect that they are
-within the same tunnel, but doing so can be detected by the tunnel
-creator when reading the reply, causing the tunnel to be marked as 
-invalid.</li>
-<li>This design does not include a proof of work on the asymmetrically
-encrypted section, though the 16 byte identity hash could be cut in
-half with the latter replaced by a hashcash function of up to 2^64
-cost.</li>
-<li>This design alone does not prevent two hostile peers within a tunnel from
-using timing information to determine whether they are in the same
-tunnel.  The use of batched and synchronized request delivery
-could help (batching up requests and sending them off on the
-(ntp-synchronized) minute).  However, doing so lets peers 'tag' the
-requests by delaying them and detecting the delay later in the
-tunnel, though perhaps dropping requests not delivered in a small
-window would work (though doing that would require a high degree of
-clock synchronization).  Alternately, perhaps individual hops could
-inject a random delay before forwarding on the request?</li>
-<li>Are there any nonfatal methods of tagging the request?</li>
-<li>
-The timestamp with a one-hour resolution is used for replay prevention.
-The constraint was not enforced until release 0.9.16.
-</li>
-</ul>
-
-<h2 id="ref">References</h2>
-<ul>
-<li>
-<a href="http://forensics.umass.edu/pubs/wright-tissec.pdf">Predecessor 
-attack</a>
-<li>
-<a href="http://forensics.umass.edu/pubs/wright.tissec.2008.pdf">2008 
-update</a>
-<li>
-<a href="http://www-users.cs.umn.edu/~hopper/hashing_it_out.pdf">Hashing it out in Public</a>
-</ul>
-
-<h2 id="future">Future Work</h2>
-<ul>
-<li>
-In the current implementation, the originator leaves one record empty
-for itself. Thus a message of n records can only build a
-tunnel of n-1 hops.
-This appears to be necessary for inbound tunnels (where the next-to-last hop
-can see the hash prefix for the next hop), but not for outbound tunnels.
-This is to be researched and verified.
-If it is possible to use the remaining record without compromising anonymity,
-we should do so.
-<li>
-Further analysis of possible tagging and timing attacks described in the above notes.
-</li><li>
-Use only VTBM; do not select old peers that don't support it.
-</li><li>
-The Build Request Record does not specify a tunnel lifetime or expiration;
-each hop expires the tunnel after 10 minutes, which is a network-wide hardcoded constant.
-We could use a bit in the flag field
-and take 4 (or 8) bytes out of the padding to specify a lifetime or expiration.
-The requestor would only specify this option if all participants supported it.
-</li></ul>
-
-
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/tunnel-message.html b/i2p2www/pages/site/docs/spec/tunnel-message.html
deleted file mode 100644
index 4f4e00c95078d27d2ee2074d1ceb44fb9d55174e..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/tunnel-message.html
+++ /dev/null
@@ -1,318 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}Tunnel Message Specification{% endblock %}
-{% block lastupdated %}February 2014{% endblock %}
-{% block accuratefor %}0.9.11{% endblock %}
-{% block content %}
-<p>
-This document specifies the format of tunnel messages.
-For general information about tunnels see
-<a href="{{ site_url('docs/tunnels/implementation') }}">the tunnel documentation</a>.
-</p>
-
-<h2>Message preprocessing</h2>
-
-
-A <i>tunnel gateway</i> is the entrance, or first hop, of a tunnel.
-For an outbound tunnel, the gateway is the creator of the tunnel.
-For an inbound tunnel, the gateway is at the opposite end from the creator of the tunnel.
-
-<p>
-A gateway <i>preprocesses</i> <a href="{{ site_url('docs/protocol/i2np') }}">I2NP messages</a>
-by fragmenting and combining them into tunnel messages.
-
-<p>
-While I2NP messages are variable size from 0 to almost 64 KB,
-tunnel messages are fixed-size, approximately 1 KB.
-Fixed message size
-restricts several types of attacks that are possible from
-observing message size.
-
-<p>
-After the tunnel messages are created, they are encrypted as described in
-<a href="{{ site_url('docs/tunnels/implementation') }}">the tunnel documentation</a>.
-
-<h2 id="msg_Tunnel">Tunnel Message (Encrypted)</h2>
-These are the contents of a tunnel data message after encryption.
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-|    Tunnel ID      |       IV          |
-+----+----+----+----+                   +
-|                                       |
-+                   +----+----+----+----+
-|                   |                   |
-+----+----+----+----+                   +
-|                                       |
-+           Encrypted Data              +
-~                                       ~
-|                                       |
-+                   +-------------------+
-|                   |
-+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-Tunnel ID :: `TunnelId`
-       4 bytes
-       the ID of the next hop
-
-IV ::
-       16 bytes
-       the initialization vector
-
-Encrypted Data ::
-       1008 bytes
-       the encrypted tunnel message
-
-total size: 1028 Bytes
-
-{% endhighlight %}
-
-
-<h2>Tunnel Message (Decrypted)</h2>
-These are the contents of a tunnel data message when decrypted.
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-|    Tunnel ID      |       IV          |
-+----+----+----+----+                   +
-|                                       |
-+                   +----+----+----+----+
-|                   |     Checksum      |
-+----+----+----+----+----+----+----+----+
-|          nonzero padding...           |
-~                                       ~
-|                                       |
-+                                  +----+
-|                                  |zero|
-+----+----+----+----+----+----+----+----+
-|                                       |
-|       Delivery Instructions  1        |
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-|                                       |
-+       I2NP Message Fragment 1         +
-|                                       |
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-|                                       |
-|       Delivery Instructions 2...      |
-~                                       ~
-|                                       |
-+----+----+----+----+----+----+----+----+
-|                                       |
-+       I2NP Message Fragment 2...      +
-|                                       |
-~                                       ~
-|                                       |
-+                   +-------------------+
-|                   |
-+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-Tunnel ID :: `TunnelId`
-       4 bytes
-       the ID of the next hop
-
-IV ::
-       16 bytes
-       the initialization vector
-
-Checksum ::
-       4 bytes
-       the first 4 bytes of the SHA256 hash of (the contents of the message (after the zero byte) + IV)
-
-Nonzero padding ::
-       0 or more bytes
-       random nonzero data for padding
-
-Zero ::
-       1 byte
-       the value 0x00
-
-Delivery Instructions :: `TunnelMessageDeliveryInstructions`
-       length varies but is typically 7, 39, 43, or 47 bytes
-       Indicates the fragment and the routing for the fragment
-
-Message Fragment ::
-       1 to 996 bytes, actual maximum depends on delivery instruction size
-       A partial or full I2NP Message
-
-total size: 1028 Bytes
-
-{% endhighlight %}
-
-<h4>Notes</h4>
-<ul><li>
-The padding, if any, must be before the instruction/message pairs.
-There is no provision for padding at the end.
-</li><li>
-The checksum does NOT cover the padding or the zero byte.
-Take the message starting at the first delivery instructions, concatenate the IV,
-and take the Hash of that.
-</li></ul>
-
-
-<h2 id="struct_TunnelMessageDeliveryInstructions">Tunnel Message Delivery Instructions</h2>
-
-<p>The instructions are encoded with a single control byte, followed by any
-necessary additional information.  The first bit (MSB) in that control byte determines
-how the remainder of the header is interpreted - if it is not set, the message 
-is either not fragmented or this is the first fragment in the message.  If it is
-set, this is a follow on fragment.</p>
-
-<p>
-This specification is for Delivery Instructions inside Tunnel Messages only.
-Note that "Delivery Instructions" are also used inside
-<a href="{{ site_url('docs/spec/i2np') }}#struct_GarlicClove">Garlic Cloves</a>,
-where the format is significantly different.
-See the
-<a href="{{ site_url('docs/spec/i2np') }}#struct_GarlicCloveDeliveryInstructions">I2NP documentation</a>
-for details.
-Do NOT use the following specification for Garlic Clove Delivery Instructions!
-
-
-<h3>First Fragment Delivery Instructions</h3>
-<p>If the MSB of the first byte is 0, this is an initial I2NP message fragment,
-or a complete (unfragmented) I2NP message, and the instructions are:</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+----+
-|flag|  Tunnel ID (opt)  |              |
-+----+----+----+----+----+              +
-|                                       |
-+                                       +
-|         To Hash (optional)            |
-+                                       +
-|                                       |
-+                        +--------------+
-|                        |dly | Message  
-+----+----+----+----+----+----+----+----+
- ID (opt) |extended opts (opt)|  size   |
-+----+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-flag ::
-       1 byte
-       Bit order: 76543210
-       bit 7: 0 to specify an initial fragment or an unfragmented message
-       bits 6-5: delivery type
-                 0x0 = LOCAL, 0x01 = TUNNEL, 0x02 = ROUTER, 0x03 = unused, invalid
-                 Note: LOCAL is used for inbound tunnels only, unimplemented for outbound tunnels
-       bit 4: delay included?  Unimplemented, always 0
-                           If 1, a delay byte is included
-       bit 3: fragmented?  If 0, the message is not fragmented, what follows is the entire message
-                           If 1, the message is fragmented, and the instructions contain a Message ID
-       bit 2: extended options?  Unimplemented, always 0
-                           If 1, extended options are included
-       bits 1-0: reserved, set to 0 for compatibility with future uses
-
-Tunnel ID :: `TunnelId`
-       4 bytes
-       Optional, present if delivery type is TUNNEL
-       The destination tunnel ID
-
-To Hash ::
-       32 bytes
-       Optional, present if delivery type is DESTINATION, ROUTER, or TUNNEL
-          If DESTINATION, the SHA256 Hash of the destination
-          If ROUTER, the SHA256 Hash of the router
-          If TUNNEL, the SHA256 Hash of the gateway router
-
-Delay ::
-       1 byte
-       Optional, present if delay included flag is set
-       In tunnel messages: Unimplemented, never present; original specification:
-          bit 7: type (0 = strict, 1 = randomized)
-          bits 6-0: delay exponent (2^value minutes)
-
-Message ID ::
-       4 bytes
-       Optional, present if this message is the first of 2 or more fragments
-          (i.e. if the fragmented bit is 1)
-       An ID that uniquely identifies all fragments as belonging to a single message
-       (the current implementation uses `I2NPMessageHeader.msg_id`)
-
-Extended Options ::
-       2 or more bytes
-       Optional, present if extend options flag is set
-       Unimplemented, never present; original specification:
-       One byte length and then that many bytes
-
-size ::
-       2 bytes
-       The length of the fragment that follows
-       Valid values: 1 to approx. 960 in a tunnel message
-
-Total length: Typical length is:
-       3 bytes for LOCAL delivery (tunnel message);
-       35 bytes for ROUTER / DESTINATION delivery or 39 bytes for TUNNEL delivery (unfragmented tunnel message);
-       39 bytes for ROUTER delivery or 43 bytes for TUNNEL delivery (first fragment)
-
-{% endhighlight %}
-
-<h3>Follow-on Fragment Delivery Instructions</h3>
-<p>If the MSB of the first byte is 1, this is a follow-on fragment, and the instructions are:</p>
-{% highlight lang='dataspec' %}
-+----+----+----+----+----+----+----+
-|frag|     Message ID    |  size   |
-+----+----+----+----+----+----+----+
-{% endhighlight %}
-
-<h4>Definition</h4>
-{% highlight lang='dataspec' %}
-frag ::
-       1 byte
-       Bit order: 76543210
-       binary 1nnnnnnd
-              bit 7: 1 to indicate this is a follow-on fragment
-              bits 6-1: nnnnnn is the 6 bit fragment number from 1 to 63
-              bit 0: d is 1 to indicate the last fragment, 0 otherwise
-
-Message ID ::
-       4 bytes
-       Identifies the fragment sequence that this fragment belongs to.
-       This will match the message ID of an initial fragment (a fragment
-       with flag bit 7 set to 0 and flag bit 3 set to 1).
-
-size ::
-       2 bytes
-       the length of the fragment that follows
-       valid values: 1 to 996
-
-total length: 7 bytes
-{% endhighlight %}
-
-<h3><a href="http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/i2np/DeliveryInstructions.html">Delivery Instructions Javadoc</a></h3>
-
-<h2 id="notes">Notes</h2>
-<h3>I2NP Message Maximum Size</h3>
-<p>
-While the maximum I2NP message size is nominally 64 KB, the size is further constrained by the
-method of fragmenting I2NP messages into multiple 1 KB tunnel messages.
-The maximum number of fragments is 64, and the initial fragment may not
-be perfectly aligned at the start of a tunnel message.
-So the message must nominally fit in 63 fragments.
-<p>
-The maximum size of an initial fragment is 956 bytes (assuming TUNNEL delivery mode);
-the maximum size of a follow-on fragment is 996 bytes.
-Therefore the maximum size is approximately 956 + (62 * 996) = 62708 bytes, or 61.2 KB.
-</p>
-
-<h3>Ordering, Batching, Packing</h3>
-Tunnel messages may be dropped or reordered.
-The tunnel gateway, who creates tunnel messages, is free to implement any
-batching, mixing, or reordering strategy to fragment I2NP messages and
-efficiently pack fragments into tunnel messages.
-In general, an optimal packing is not possible (the "packing problem").
-The gateways may implement various delay and reordering strategies.
-
-<h3>Cover Traffic</h3>
-Tunnel messages may contain only padding (i.e. no delivery instructions or message fragments at all)
-for cover traffic. This is unimplemented.
-
-{% endblock %}
diff --git a/i2p2www/pages/site/docs/spec/updates.html b/i2p2www/pages/site/docs/spec/updates.html
deleted file mode 100644
index bde99bf33ac969902bcf65503a9e191ac4bd8921..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/docs/spec/updates.html
+++ /dev/null
@@ -1,572 +0,0 @@
-{% extends "global/layout.html" %}
-{% block title %}{% trans %}I2P Software Update Specification{% endtrans %}{% endblock %}
-{% block lastupdated %}{% trans %}May 2015{% endtrans %}{% endblock %}
-{% block accuratefor %}0.9.20{% endblock %}
-{% block content %}
-<h3>{% trans %}Overview{% endtrans %}</h3>
-<p>{% trans -%}
-I2P uses a simple, yet secure, system for automated software update.
-The router console periodically pulls a news file from a configurable I2P URL.
-There is a hardcoded backup URL pointing to the project website, in case
-the default project news host goes down.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-The contents of the news file are displayed on the home page of the router console.
-In addition, the news file contains the most recent version number of the software.
-If the version is higher than the router's version number, it will
-display an indication to the user that an update is available.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-The router may optionally download, or download and install, the new version
-if configured to do so.
-{%- endtrans %}</p>
-
-<h3>{% trans %}Old News File Specification{% endtrans %}</h3>
-<p>{% trans -%}
-This format is replaced by the su3 news format as of release 0.9.17.
-{%- endtrans %}</p>
-<p>{% trans -%}
-The news.xml file may contain the following elements:
-{%- endtrans %}</p>
-<pre>
-&lt;i2p.news date="$Date: 2010-01-22 00:00:00 $" /&gt;
-&lt;i2p.release version="0.7.14" date="2010/01/22" minVersion="0.6" /&gt;
-</pre>
-
-<p>{% trans -%}
-Parameters in the i2p.release entry are as follows.
-All keys are case-insensitive. All values must be enclosed in double quotes.
-{%- endtrans %}</p>
-
-<ul>
-<li>
-date: The release date of the router version. Unused. Format not specified.
-</li><li>
-minJavaVersion: The minimum version of Java required to run the current version.
-As of release 0.9.9.
-</li><li>
-minVersion: The minimum version of the router required to update to the current version.
-If a router is older than this, the user must (manually?) update to an intermediate version first.
-As of release 0.9.9.
-</li><li>
-su3Clearnet: One or more HTTP URLs where the .su3 update file may
-be found on the clearnet (non-I2P).
-Multiple URLs must be separated by a space or comma.
-As of release 0.9.9.
-</li><li>
-su3SSL: One or more HTTPS URLs where the .su3 update file may
-be found on the clearnet (non-I2P).
-Multiple URLs must be separated by a space or comma.
-As of release 0.9.9.
-</li><li>
-sudTorrent: The magnet link for the .sud (non-pack200) torrent of the update.
-As of release 0.9.4.
-</li><li>
-su2Torrent: The magnet link for the .su2 (pack200) torrent of the update.
-As of release 0.9.4.
-</li><li>
-su3Torrent: The magnet link for the .su3 (new format) torrent of the update.
-As of release 0.9.9.
-</li><li>
-version: Required. The latest current router version available.
-</li></ul>
-
-<p>{% trans -%}
-The elements may be included inside XML comments to prevent interpretation by browsers.
-The i2p.release element and version are required. All others are optional.
-NOTE: Due to parser limitations an entire element must be on a single line.
-{%- endtrans %}</p>
-
-
-<h3>{% trans %}Update File Specification{% endtrans %}</h3>
-<p>{% trans -%}
-As of release 0.9.9, the signed update file, named i2pupdate.su3, will
-use the "su3" file format specified below.
-Approved release signers will use 4096-bit RSA keys.
-The X.509 public key certificates for these signers are distributed in the router installation packages.
-The updates may contain certificates for new, approved signers, and/or contain
-a list of certificates to delete for revocation.
-{%- endtrans %}</p>
-
-
-<h3>{% trans %}Old Update File Specification{% endtrans %}</h3>
-<p>{% trans -%}
-This format is obsolete as of release 0.9.9.
-{%- endtrans %}</p>
-<p>{% trans -%}
-The signed update file, traditionally named i2pupdate.sud,
-is simply a zip file with a prepended 56 byte header.
-The header contains:
-{%- endtrans %}</p>
-<ul>
-<li>{% trans commonstructures=site_url('docs/spec/common-structures') -%}
-A 40-byte <a href="{{ commonstructures }}#type_signature">DSA signature</a>
-{%- endtrans %}</li>
-<li>{% trans -%}
-A 16-byte I2P version in UTF-8, padded with trailing zeroes if necessary
-{%- endtrans %}</li>
-</ul>
-
-<p>{% trans commonstructures=site_url('docs/spec/common-structures') -%}
-The signature covers only the zip archive - not the prepended version.
-The signature must match one of the <a href="{{ commonstructures }}#type_SigningPublicKey">DSA public keys</a> configured into the router,
-which has a hardcoded default list of keys of the current project release managers.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-For version comparison purposes, version fields contain [0-9]*, field separators are
-'-', '_', and '.', and all other characters are ignored.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-As of version 0.8.8, the version must also be specified as a zip file comment in UTF-8,
-without the trailing zeroes.
-The updating router verifes that the version in the header (not covered by the signature)
-matches the version in the zip file comment, which is covered by the signature.
-This prevents spoofing of the version number in the header.
-{%- endtrans %}</p>
-
-<h3>{% trans %}Download and Installation{% endtrans %}</h3>
-<p>{% trans -%}
-The router first downloads the header of the update file from one in a configurable list of I2P URLs,
-using the built-in HTTP client and proxy,
-and checks that the version is newer.
-This prevents the problem of update hosts that do not have the latest file.
-The router then downloads the full update file.
-The router verifies that the update file version is newer before installation.
-It also, of course, verifies the signature, and
-verifes that the zip file comment matches the header version, as explained above.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-The zip file is extracted and copied to "i2pupdate.zip" in the I2P configuration directory (~/.i2p on Linux).
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-As of release 0.7.12, the router supports Pack200 decompression.
-Files inside the zip archive with a .jar.pack or .war.pack suffix
-are transparently decompressed to a .jar or .war file.
-Update files containing .pack files are traditionally named with a '.su2' suffix.
-Pack200 shrinks the update files by about 60&#37;.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-As of release 0.8.7, the router will delete the libjbigi.so and libjcpuid.so files
-if the zip archive contains a lib/jbigi.jar file, so that the new files will
-be extracted from jbigi.jar.
-{%- endtrans %}</p>
-
-<p>{% trans -%}
-As of release 0.8.12, if the zip archive contains a file deletelist.txt, the router will
-delete the files listed there. The format is:
-{%- endtrans %}</p>
-<ul>
-<li>{% trans %}One file name per line{% endtrans %}</li>
-<li>{% trans %}All file names are relative to the installation directory; no absolute file names allowed, no files starting with ".."{% endtrans %}</li>
-<li>{% trans %}Comments start with '#'{% endtrans %}</li>
-</ul>
-
-<p>{% trans -%}
-The router will then delete the deletelist.txt file.
-{%- endtrans %}</p>
-
-
-
-<h3 id="su3">{% trans %}SU3 File Specification{% endtrans %}</h3>
-
-<p>{% trans -%}
-This specification is used for router updates as of release 0.9.9, reseed data as of release 0.9.14,
-plugins as of release 0.9.15, and the news file as of release 0.9.17.
-{%- endtrans %}</p>
-
-<h4>{% trans %}Issues with the previous .sud/.su2 format:{% endtrans %}</h4>
-<ul>
-<li>{% trans -%}
-No magic number or flags
-{%- endtrans %}</li>
-<li>{% trans -%}
-No way to specify compression, pack200 or not, or signing algo
-{%- endtrans %}</li>
-<li>{% trans -%}
-Version is not covered by signature, so it is enforced by requiring it
-to be in the zip file comment (for router files) or in the plugin.config
-file (for plugins)
-{%- endtrans %}</li>
-<li>{% trans -%}
-Signer not specified so verifier must try all known keys
-{%- endtrans %}</li>
-<li>{% trans -%}
-Signature-before-data format requires two passes to generate file
-{%- endtrans %}</li>
-</ul>
-
-
-<h4>{% trans %}Goals:{% endtrans %}</h4>
-
-<ul>
-<li>{% trans -%}
-Fix above problems
-{%- endtrans %}</li>
-<li>{% trans -%}
-Migrate to more secure signature algorithm
-{%- endtrans %}</li>
-<li>{% trans -%}
-Keep version info in same format and offset for compatibility with
-existing version checkers
-{%- endtrans %}</li>
-<li>{% trans -%}
-One-pass signature verification and file extraction
-{%- endtrans %}</li>
-</ul>
-
-<h4>{% trans %}Specification:{% endtrans %}</h4>
-
-<table><tr>
-<th>Bytes<th>Contents
-<tr><td>
-0-5	<td>Magic number "I2Psu3"
-<tr><td>
-6	<td>unused = 0
-<tr><td>
-7	<td>su3 file format version = 0
-<tr><td>
-8-9	<td>Signature type
-		<ul><li>0x0000 = DSA-SHA1
-		</li><li>0x0001 = ECDSA-SHA256-P256
-		</li><li>0x0002 = ECDSA-SHA384-P384
-		</li><li>0x0003 = ECDSA-SHA512-P521
-		</li><li>0x0004 = RSA-SHA256-2048
-		</li><li>0x0005 = RSA-SHA384-3072
-		</li><li>0x0006 = RSA-SHA512-4096
-		</li></ul>
-<tr><td>
-10-11	<td>Signature length, e.g. 40 (0x0028) for DSA-SHA1.
-            Must match that specified for the signature type on the
-            <a href="{{ site_url('docs/spec/common-structures') }}#type_Signature">common structures page</a>.
-<tr><td>
-12	<td>unused = 0
-<tr><td>
-13	<td>Version length (in bytes not chars, including padding)
-        must be at least 16 (0x10) for compatibility
-<tr><td>
-14	<td>unused = 0
-<tr><td>
-15	<td>Signer ID length (in bytes not chars)
-<tr><td>
-16-23	<td>Content length (not including header or sig)
-<tr><td>
-24	<td>unused = 0
-<tr><td>
-25	<td>File type
-		<ul><li>0x00 = zip file
-		</li><li>0x01 = xml file (as of 0.9.15)
-		</li><li>0x02 = html file (as of 0.9.17)
-		</li><li>0x03 = xml.gz file (as of 0.9.17)
-		</li></ul>
-<tr><td>
-26	<td>unused = 0
-<tr><td>
-27	<td>Content type
-		<ul><li>0x00 = unknown
-		</li><li>0x01 = router update
-		</li><li>0x02 = plugin or plugin update
-		</li><li>0x03 = reseed data
-		</li><li>0x04 = news feed (as of 0.9.15)
-		</li></ul>
-<tr><td>
-28-39	<td>unused = 0
-<tr><td>
-40-55+	<td>Version, UTF-8 padded with trailing 0x00, 16 bytes minimum, length specified at byte 13.
-            Do not append 0x00 bytes if the length is 16 or more.
-<tr><td>
-xx+	<td>ID of signer, (e.g. "zzz@mail.i2p") UTF-8, not padded, length specified at byte 15
-<tr><td>
-xx+	<td>Content: Length specified in header at bytes 16-23,
-                     format specified in header at byte 25,
-                     content specified in header at byte 27
-<tr><td>
-xx+	<td>Signature: Length is specified in header at bytes 10-11, covers everything starting at byte 0
-</table>
-
-<p>{% trans -%}
-All unused fields must be set to 0 for compatibility with future versions.
-{%- endtrans %}</p>
-
-
-<h4>{% trans %}Signature Details{% endtrans %}</h4>
-<p>
-The signature covers the entire header starting at byte 0,
-through the end of the content.
-We use raw signatures. Take the hash of the data (using the hash type
-implied by the signature type at bytes 8-9) and pass that
-to a "raw" sign or verify function (e.g. "NONEwithRSA" in Java).
-<p></p>
-While signature verification and content extraction may be implemented in one pass,
-an implementation must read and buffer the first 10 bytes to determine the hash type
-before starting to verify.
-<p></p>
-Signature lengths for the various signature types are specified on the
-<a href="{{ site_url('docs/spec/common-structures') }}#type_Signature">common structures page</a>.
-Pad the signature with leading zeros if necessary.
-See <a href="{{ site_url('docs/how/cryptography') }}#sig">the cryptography details page</a>
-for parameters of the various signature types.
-</p>
-
-
-
-<h4>{% trans %}Notes{% endtrans %}</h4>
-<p>
-The content type specifies the trust domain.
-For each content type, clients maintain a set of X.509 public key certificates for
-parties trusted to sign that content.
-Only certificates for the specified content type may be used.
-The certificate is looked up by the ID of the signer.
-Clients must verify that the content type is that expected for the application.
-<p></p>
-All values are in network byte order (big endian).
-</p>
-
-
-
-
-<h3>{% trans %}SU3 Router Update File Specification{% endtrans %}</h3>
-
-<p><b>SU3 Details:</b></p>
-<ul><li>
-SU3 Content Type: 1 (ROUTER UPDATE)
-</li><li>
-SU3 File Type: 0 (ZIP)
-</li><li>
-SU3 Version: The router version
-</li><li>
-Jar and war files in the zip are compressed with pack200 as documented above for "su2" files.
-If the client does not support pack200, it must download the update in a "sud" format.
-</li></ul>
-
-<p><b>Notes:</b></p>
-<ul><li>
-For releases, the SU3 version is the "base" router version, e.g. "0.9.20".
-</li><li>
-For development builds, which are supported as of release 0.9.20,
-the SU3 version is the "full" router version, e.g. "0.9.20-5" or "0.9.20-5-rc".
-See RouterVersion.java.
-</li></ul>
-
-
-
-<h3>{% trans %}SU3 Reseed File Specification{% endtrans %}</h3>
-<p>{% trans -%}
-As of 0.9.14, reseed data is delivered in an "su3" file format.
-{%- endtrans %}</p>
-
-<h4>{% trans %}Goals:{% endtrans %}</h4>
-
-<ul>
-<li>{% trans -%}
-Signed files with strong signatures and trusted certificates
-to prevent man-in-the-middle attacks that could boot victims into a separate, untrusted network.
-{%- endtrans %}</li>
-<li>{% trans -%}
-Use su3 file format already used for updates, reseeding, and plugins
-{%- endtrans %}</li>
-<li>{% trans -%}
-Single compressed file to speed up reseeding, which was slow to fetch 200 files
-{%- endtrans %}</li>
-</ul>
-
-<h4>{% trans %}Specification:{% endtrans %}</h4>
-
-<p><ol>
-<li>{% trans -%}
-The file must be named "i2pseeds.su3".
-{%- endtrans %}</li>
-<li>{% trans -%}
-The file must be in the same directory as the router infos on the web server.
-{%- endtrans %}</li>
-<li>{% trans -%}
-A router will first try to fetch (index URL)/i2pseeds.su3; if that fails it will fetch the index URL
-and then fetch the individual router info files found in the links.
-{%- endtrans %}</li>
-</ol></p>
-
-<p><b>SU3 Details:</b></p>
-<ul><li>
-SU3 Content Type: 3 (RESEED)
-</li><li>
-SU3 File Type: 0 (ZIP)
-</li><li>
-SU3 Version: Seconds since the epoch, in ASCII (date +%s)
-</li><li>
-Router info files in the zip file must be at the "top level". No directories are in the zip file.
-</li><li>
-Router info files must be named "routerInfo-(44 character base 64 router hash).dat", as in the old reseed mechanism.
-The I2P base 64 alphabet must be used.
-</li></ul>
-
-
-
-
-<h3>{% trans %}SU3 Plugin File Specification{% endtrans %}</h3>
-
-<p>{% trans -%}
-As of 0.9.15, plugins may be packaged in an "su3" file format.
-{%- endtrans %}</p>
-
-<p><b>SU3 Details:</b></p>
-<ul><li>
-SU3 Content Type: 2 (PLUGIN)
-</li><li>
-SU3 File Type: 0 (ZIP)
-See <a href="plugin">the plugin specification</a> for details.
-</li><li>
-SU3 Version: The plugin version, must match that in plugin.config.
-</li><li>
-Jar and war files in the zip are compressed with pack200 as documented above for "su2" files.
-</li></ul>
-
-
-
-
-
-
-<h3>{% trans %}SU3 News File Specification{% endtrans %}</h3>
-<p>{% trans -%}
-As of 0.9.17, the news is delivered in an "su3" file format.
-{%- endtrans %}</p>
-
-<h4>{% trans %}Goals:{% endtrans %}</h4>
-
-<ul>
-<li>{% trans -%}
-Signed news with strong signatures and trusted certificates
-{%- endtrans %}</li>
-<li>{% trans -%}
-Use su3 file format already used for updates, reseeding, and plugins
-{%- endtrans %}</li>
-<li>{% trans -%}
-Standard XML format for use with standard parsers
-{%- endtrans %}</li>
-<li>{% trans -%}
-Standard Atom format for use with standard feed readers and generators
-{%- endtrans %}</li>
-<li>{% trans -%}
-Sanitization and verification of HTML before displaying on console
-{%- endtrans %}</li>
-<li>{% trans -%}
-Suitable for easy implementation on Android and other platforms without an HTML console
-{%- endtrans %}</li>
-</ul>
-
-<h4>{% trans %}Specification:{% endtrans %}</h4>
-
-<p><b>SU3 Details:</b></p>
-<ul><li>
-SU3 Content Type: 4 (NEWS)
-</li><li>
-SU3 File Type: 1 (XML) or 3 (XML.GZ)
-</li><li>
-SU3 Version: Seconds since the epoch, in ASCII (date +%s)
-</li><li>
-File Format: XML or gzipped XML, containing an <a href="http://tools.ietf.org/html/rfc4287">RFC 4287 (Atom) XML Feed</a>.
-Charset must be UTF-8.
-</li></ul>
-
-
-<p><b>Atom &lt;feed&gt; Details:</b></p>
-The following &lt;feed&gt; elements are used:
-<ul><li>
-&lt;entry&gt; A news item. See below.
-</li><li>
-&lt;i2p:release&gt; I2P update metadata. See below.
-</li><li>
-&lt;updated&gt; Timestamp for the feed (conforming to
-<a href="http://tools.ietf.org/html/rfc4287">RFC 4287 (Atom) section 3.3</a> and
-<a href="http://tools.ietf.org/html/rfc3339">RFC 3339</a>. (required)
-</li></ul>
-
-
-<p><b>Atom &lt;entry&gt; Details:</b></p>
-Each Atom &lt;entry&gt; in the news feed may be parsed and displayed in the router console.
-The following elements are used:
-<ul><li>
-&lt;author&gt; (optional) containing:
-  <ul><li>
-  &lt;name&gt; The name of the entry author
-  </li></ul>
-</li><li>
-&lt;content&gt; Content, must be type="xhtml".
-The XHTML will be sanitized with a whitelist of allowed elements
-and a blacklist of disallowed attributes.
-Clients may ignore an element, or the enclosing entry, or the entire feed
-when a non-whitelisted element is encountered. (required)
-</li><li>
-&lt;link&gt; Link for further information (optional)
-</li><li>
-&lt;summary&gt; Short summary, suitable for a tooltip (optional)
-</li><li>
-&lt;title&gt; Title of the news entry (required)
-</li><li>
-&lt;updated&gt; Timestamp for this entry (conforming to
-<a href="http://tools.ietf.org/html/rfc4287">RFC 4287 (Atom) section 3.3</a> and
-<a href="http://tools.ietf.org/html/rfc3339">RFC 3339</a>. (required)
-</li></ul>
-
-
-<p><b>Atom &lt;i2p:release&gt; Details:</b></p>
-There must be at least one &lt;i2p:release&gt; entity in the feed. Each contains the following attributes and entities:
-<ul><li>
-date (attribute): Timestamp for this entry (conforming to
-<a href="http://tools.ietf.org/html/rfc4287">RFC 4287 (Atom) section 3.3</a> and
-<a href="http://tools.ietf.org/html/rfc3339">RFC 3339</a>. (required)
-The date also may be in truncated format yyyy-mm-dd (without the 'T'); this is the "full-date" format in RFC 3339.
-In this format the time is assumed to be 00:00:00 UTC for any processing.
-</li><li>
-minJavaVersion (attribute): If present, the minimum version of Java required to run the current version.
-</li><li>
-minVersion (attribute):
-If present, the minimum version of the router required to update to the current version.
-If a router is older than this, the user must (manually?) update to an intermediate version first.
-</li><li>
-&lt;i2p:version&gt; Required. The latest current router version available.
-</li><li>
-&lt;i2p:update&gt; An update file (one or more). It must contain at least one child.
-  <ul><li>
-  type (attribute): "sud", "su2", or "su3". Must be unique across all &lt;i2p:update&gt; elements.
-  </li><li>
-  &lt;i2p:clearnet&gt; Out-of-network direct download links (zero or more)
-    <ul><li>
-    href (attribute): a standard clearnet http link
-    </li></ul>
-  </li><li>
-  &lt;i2p:clearnetssl&gt; Out-of-network direct download links (zero or more)
-    <ul><li>
-    href (attribute): a standard clearnet https link
-    </li></ul>
-  </li><li>
-  &lt;i2p:torrent&gt; In-network magnet link
-    <ul><li>
-    href (attribute): a magnet link
-    </li></ul>
-  </li><li>
-  &lt;i2p:url&gt; In-network direct download links (zero or more)
-    <ul><li>
-    href (attribute): an in-network http .i2p link
-    </li></ul>
-  </li></ul>
-</li></ul>
-
-
-
-<h3>{% trans %}Future Work{% endtrans %}</h3>
-<ul>
-<li>{% trans -%}
-The router update mechanism is part of the web router console.
-There is currently no provision for updates of an embedded router lacking the router console.
-{%- endtrans %}</li>
-</ul>
-
-
-
-{% endblock %}
diff --git a/i2p2www/pages/site/spectags b/i2p2www/pages/site/spectags
deleted file mode 100644
index 5033c3ba165f8b2fe6f4ad8347b02a1efc67e430..0000000000000000000000000000000000000000
--- a/i2p2www/pages/site/spectags
+++ /dev/null
@@ -1,76 +0,0 @@
-!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;" to lines/
-!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/
-!_TAG_PROGRAM_AUTHOR	Darren Hiebert	/dhiebert@users.sourceforge.net/
-!_TAG_PROGRAM_NAME	Exuberant Ctags	//
-!_TAG_PROGRAM_URL	http://ctags.sourceforge.net	/official site/
-!_TAG_PROGRAM_VERSION	5.9~svn20110310	//
-BandwidthLimits	docs/spec/i2cp.html	588;"	m
-Boolean	docs/spec/common-structures.html	74;"	t
-BuildRequestRecord	docs/spec/i2np.html	183;"	s
-BuildResponseRecord	docs/spec/i2np.html	353;"	s
-Certificate	docs/spec/common-structures.html	296;"	t
-CreateLeaseSet	docs/spec/i2cp.html	630;"	m
-CreateSession	docs/spec/i2cp.html	668;"	m
-Data	docs/spec/i2np.html	1118;"	m
-DatabaseLookup	docs/spec/i2np.html	688;"	m
-DatabaseSearchReply	docs/spec/i2np.html	835;"	m
-DatabaseStore	docs/spec/i2np.html	590;"	m
-Date	docs/spec/common-structures.html	51;"	t
-DeliveryInstructions	docs/spec/common-structures.html	1010;"	s
-DeliveryStatus	docs/spec/i2np.html	910;"	m
-DestLookup	docs/spec/i2cp.html	697;"	m
-DestReply	docs/spec/i2cp.html	719;"	m
-Destination	docs/spec/common-structures.html	639;"	s
-Disconnect	docs/spec/i2cp.html	763;"	m
-Garlic	docs/spec/i2np.html	948;"	m
-GarlicClove	docs/spec/i2np.html	395;"	s
-GarlicCloveDeliveryInstructions	docs/spec/i2np.html	454;"	s
-GetBandwidthLimits	docs/spec/i2cp.html	783;"	m
-GetDate	docs/spec/i2cp.html	802;"	m
-Hash	docs/spec/common-structures.html	257;"	t
-HostLookup	docs/spec/i2cp.html	840;"	m
-HostReply	docs/spec/i2cp.html	889;"	m
-I2CPMessageHeader	docs/spec/i2cp.html	302;"	s
-I2NPMessageHeader	docs/spec/i2np.html	93;"	s
-Integer	docs/spec/common-structures.html	41;"	t
-KeysAndCert	docs/spec/common-structures.html	549;"	s
-Lease	docs/spec/common-structures.html	668;"	s
-LeaseSet	docs/spec/common-structures.html	713;"	s
-Mapping	docs/spec/common-structures.html	461;"	t
-MessageId	docs/spec/i2cp.html	324;"	s
-MessagePayload	docs/spec/i2cp.html	921;"	m
-MessageStatus	docs/spec/i2cp.html	943;"	m
-Payload	docs/spec/i2cp.html	344;"	s
-PrivateKey	docs/spec/common-structures.html	102;"	t
-PublicKey	docs/spec/common-structures.html	89;"	t
-ReceiveMessageBegin	docs/spec/i2cp.html	1048;"	m
-ReceiveMessageEnd	docs/spec/i2cp.html	1077;"	m
-ReconfigureSession	docs/spec/i2cp.html	1103;"	m
-ReportAbuse	docs/spec/i2cp.html	1133;"	m
-RequestLeaseSet	docs/spec/i2cp.html	1164;"	m
-RequestVariableLeaseSet	docs/spec/i2cp.html	1197;"	m
-RouterAddress	docs/spec/common-structures.html	843;"	s
-RouterIdentity	docs/spec/common-structures.html	613;"	s
-RouterInfo	docs/spec/common-structures.html	910;"	s
-SendMessage	docs/spec/i2cp.html	1225;"	m
-SendMessageExpires	docs/spec/i2cp.html	1272;"	m
-SessionConfig	docs/spec/i2cp.html	366;"	s
-SessionId	docs/spec/i2cp.html	398;"	s
-SessionKey	docs/spec/common-structures.html	115;"	t
-SessionStatus	docs/spec/i2cp.html	1403;"	m
-SessionTag	docs/spec/common-structures.html	269;"	t
-SetDate	docs/spec/i2cp.html	1437;"	m
-Signature	docs/spec/common-structures.html	214;"	t
-SigningPrivateKey	docs/spec/common-structures.html	171;"	t
-SigningPublicKey	docs/spec/common-structures.html	128;"	t
-String	docs/spec/common-structures.html	62;"	t
-Tunnel	docs/spec/tunnel-message.html	34;"	m
-TunnelBuild	docs/spec/i2np.html	1144;"	m
-TunnelBuildReply	docs/spec/i2np.html	1177;"	m
-TunnelData	docs/spec/i2np.html	1044;"	m
-TunnelGateway	docs/spec/i2np.html	1084;"	m
-TunnelId	docs/spec/common-structures.html	281;"	t
-TunnelMessageDeliveryInstructions	docs/spec/tunnel-message.html	159;"	s
-VariableTunnelBuild	docs/spec/i2np.html	1190;"	m
-VariableTunnelBuildReply	docs/spec/i2np.html	1220;"	m
-sampleDatagrams	docs/spec/ssu.html	1062;"	a
diff --git a/i2p2www/pages/spec/index.html b/i2p2www/pages/spec/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..c98d239a2b4775a94e6a0629872935f0ceb977ff
--- /dev/null
+++ b/i2p2www/pages/spec/index.html
@@ -0,0 +1,40 @@
+{% extends "global/layout.html" %}
+{% block title %}I2P Specification Documents{% endblock %}
+{% block content %}
+This page provides the specifications for various components of the I2P network
+and router software. These are living documents, and the specifications are
+updated as modifications are made to the network and software.
+
+<ul><li>
+"Last updated" is the last date when the specification given within a document
+was altered in any way, except for changes to the "accurate for" information.
+</li><li>
+The "accurate for" column gives the version of the I2P network and reference
+Java implementation that the document is verified to be valid for. Because the
+documents are usually only updated when changes are made, the listed versions
+can sometimes be several releases behind. This does not mean that documents with
+old listed versions are necessarily inaccurate, but small differences may creep
+in during the course of development. Periodic reviews are conducted to update
+the "accurate for" information.
+</li></ul>
+
+<table>
+  <tr>
+    <th>Title</th>
+    <th>Last updated</th>
+    <th>Accurate for</th>
+    <th>Link</th>
+  </tr>
+  {% for spec in specs %}
+  <tr>
+    <td>{{ spec.title }}</td>
+    <td><time>{{ spec.lastupdated }}</time></td>
+    <td>{{ spec.accuratefor }}</td>
+    <td>
+      <a href="{{ url_for('spec_show', name=spec.name) }}">HTML</a> |
+      <a href="{{ url_for('spec_show_txt', name=spec.name) }}">TXT</a>
+    </td>
+  </tr>
+{% endfor %}
+</table>
+{% endblock %}
diff --git a/i2p2www/pages/spec/show.html b/i2p2www/pages/spec/show.html
new file mode 100644
index 0000000000000000000000000000000000000000..1d9cf1a683ee7d8b6a00ca2708ee6f73e7d1c74a
--- /dev/null
+++ b/i2p2www/pages/spec/show.html
@@ -0,0 +1,15 @@
+{% extends "global/layout.html" %}
+{%- from "global/macros" import render_categories with context -%}
+{% block title %}{{ title }}{% endblock %}
+{% block lastupdated %}{{ meta.lastupdated }}{% endblock %}
+{% block accuratefor %}{{ meta.accuratefor }}{% endblock %}
+{% block content_nav %}
+{% autoescape false %}
+{{ toc }}
+{% endautoescape %}
+{% endblock %}
+{% block content %}
+{% autoescape false %}
+{{ body }}
+{% endautoescape %}
+{% endblock %}
diff --git a/i2p2www/spec/__init__.py b/i2p2www/spec/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/i2p2www/spec/blockfile.rst b/i2p2www/spec/blockfile.rst
new file mode 100644
index 0000000000000000000000000000000000000000..91984e5f02f018afca28380d711de6b4812a68e9
--- /dev/null
+++ b/i2p2www/spec/blockfile.rst
@@ -0,0 +1,249 @@
+==========================================
+Blockfile and Hosts Database Specification
+==========================================
+.. meta::
+    :lastupdated: November 2014
+    :accuratefor: 0.9.17
+
+.. contents::
+
+
+Overview
+========
+
+This document specifies the I2P blockfile file format and the tables in the
+hostsdb.blockfile used by the Blockfile Naming Service [NAMING]_.
+
+The blockfile provides fast Destination lookup in a compact format. While the
+blockfile page overhead is substantial, the destinations are stored in binary
+rather than in Base 64 as in the hosts.txt format.  In addition, the blockfile
+provides the capability of arbitrary metadata storage (such as added date,
+source, and comments) for each entry.  The metadata may be used in the future
+to provide advanced addressbook features.  The blockfile storage requirement is
+a modest increase over the hosts.txt format, and the blockfile provides
+approximately 10x reduction in lookup times.
+
+A blockfile is simply on-disk storage of multiple sorted maps (key-value
+pairs), implemented as skiplists.  The blockfile format is adopted from the
+Metanotion Blockfile Database [METANOTION]_.  First we will define the file
+format, then the use of that format by the BlockfileNamingService.
+
+
+Blockfile Format
+================
+
+The original blockfile spec was modified to add magic numbers to each page.
+The file is structured in 1024-byte pages. Pages are numbered starting from 1.
+The "superblock" is always at page 1, i.e. starting at byte 0 in the file.  The
+metaindex skiplist is always at page 2, i.e. starting at byte 1024 in the file.
+
+All 2-byte integer values are unsigned.  All 4-byte integer values (page
+numbers) are signed and negative values are illegal.  All integer values are
+stored in network byte order (big endian).
+
+The database is designed to be opened and accessed by a single thread.  The
+BlockfileNamingService provides synchronization.
+
+Superblock format:
+
+.. raw:: html
+
+  {% highlight %}Byte	Contents
+  0-5	Magic number	0x3141de493250 ("1A" 0xde "I2P")
+  6	Major version	0x01
+  7	Minor version	0x02
+  8-15	File length	Total length in bytes
+  16-19	First free list page
+  20-21	Mounted flag	0x01 = yes
+  22-23	Span size	Max number of key/value pairs per span (16 for hostsdb)
+  			Used for new skip lists.
+  24-27	Page size	As of version 1.2. Prior to 1.2, 1024 is assumed.
+  28-1023	unused
+{% endhighlight %}
+
+Skip list block page format:
+
+.. raw:: html
+
+  {% highlight %}Byte	Contents
+  0-7	Magic number	0x536b69704c697374 "SkipList"
+  8-11	First span page
+  12-15	First level page
+  16-19	Size (total number of keys - may only be valid at startup)
+  20-23	Spans (total number of spans - may only be valid at startup)
+  24-27	Levels (total number of levels - may only be valid at startup)
+  28-29	Span size - As of version 1.2. Max number of key/value pairs per span.
+                      Prior to that, specified for all skiplists in the superblock.
+                      Used for new spans in this skip list.
+  30-1023	unused
+{% endhighlight %}
+
+Skip level block page format is as follows.
+All levels have a span. Not all spans have levels.
+
+.. raw:: html
+
+  {% highlight %}Byte	Contents
+  0-7	Magic number	0x42534c6576656c73 "BSLevels"
+  8-9	Max height
+  10-11	Current height
+  12-15	Span page
+  16-	Next level pages ('current height' entries, 4 bytes each, lowest first)
+  remaining bytes unused
+{% endhighlight %}
+
+Skip span block page format is as follows.
+Key/value structures are sorted by key within each span and across all spans.
+Key/value structures are sorted by key within each span.
+Spans other than the first span may not be empty.
+
+.. raw:: html
+
+  {% highlight %}Byte	Contents
+  0-3	Magic number	0x5370616e "Span"
+  4-7	First continuation page or 0
+  8-11	Previous span page or 0
+  12-15	Next span page or 0
+  16-17	Max keys (16 for hostsdb)
+  18-19	Size (current number of keys)
+  20-1023	key/value structures
+{% endhighlight %}
+
+Span Continuation block page format:
+
+.. raw:: html
+
+  {% highlight %}Byte	Contents
+  0-3	Magic number	0x434f4e54 "CONT"
+  4-7	Next continuation page or 0
+  8-1023	key/value structures
+{% endhighlight %}
+
+Key/value structure format is as follows.
+Key and value lengths must not be split across pages, i.e. all 4 bytes must be on the same page.
+If there is not enough room the last 1-3 bytes of a page are unused and the lengths will
+be at offset 8 in the continuation page.
+Key and value data may be split across pages.
+Max key and value lengths are 65535 bytes.
+
+.. raw:: html
+
+  {% highlight %}Byte	Contents
+  0-1	key length in bytes
+  2-3	value length in bytes
+  4-	key data
+  	value data
+{% endhighlight %}
+
+Free list block page format:
+
+.. raw:: html
+
+  {% highlight %}Byte	Contents
+  0-7	Magic number	0x2366724c69737423 "#frList#"
+  8-11	Next free list block or 0 if none
+  12-15	Number of valid free pages in this block (0 - 252)
+  16-1023	Free pages (4 bytes each), only the first (valid number) are valid
+{% endhighlight %}
+
+Free page block format:
+
+.. raw:: html
+
+  {% highlight %}Byte	Contents
+  0-7	Magic number	0x7e2146524545217e "~!FREE!~"
+  8-1023	unused
+{% endhighlight %}
+
+The metaindex (located at page 2) is a mapping of US-ASCII strings to 4-byte integers.
+The key is the name of the skiplist and the value is the page index of the skiplist.
+
+
+Blockfile Naming Service Tables
+===============================
+
+The tables created and used by the BlockfileNamingService are as follows.
+The maximum number of entries per span is 16.
+
+Properties Skiplist
+-------------------
+
+"%%__INFO__%%" is the master database skiplist with String/Properties key/value
+entries containing only one entry:
+
+    info
+        a Properties (UTF-9 String/String Map), serialized as a [Mapping]_:
+
+        version
+            "3"
+
+        created
+            Java long time (ms)
+
+        upgraded
+            Java long time (ms) (as of database version 2)
+
+        lists
+            Comma-separated list of host databases, to be searched in-order for
+            lookups. Almost always "privatehosts.txt,userhosts.txt,hosts.txt".
+
+Reverse Lookup Skiplist
+-----------------------
+
+"%%__REVERSE__%%" is the reverse lookup skiplist with Integer/Properties
+key/value entries (as of database version 2):
+
+* The skiplist keys are 4-byte Integers, the first 4 bytes of the hash of the
+  [Destination]_.
+
+* The skiplist values are each a Properties (a UTF-8 String/String Map)
+  serialized as a [Mapping]_
+
+  * There may be multiple entries in the properties, each one is a reverse
+    mapping, as there may be more than one hostname for a given destination, or
+    there could be collisions with the same first 4 bytes of the hash.
+
+  * Each property key is a hostname.
+
+  * Each property value is the empty string.
+
+hosts.txt, userhosts.txt, and privatehosts.txt Skiplists
+--------------------------------------------------------
+
+For each host database, there is a skiplist containing the hosts for that
+database.  The keys/values in these skiplists are as follows:
+
+    key
+        a UTF-8 String (the hostname)
+
+    value
+        a DestEntry, which is a Properties (a UTF-8 String/String Map)
+        serialized as a [Mapping]_ followed by a binary [Destination]_
+        (serialized as usual).
+
+The DestEntry Properties typically contains:
+
+    "a"
+        The time added (Java long time in ms)
+
+    "s"
+        The original source of the entry (typically a file name or subscription
+        URL)
+
+Hostname keys are stored in lower-case and always end in ".i2p".
+
+
+References
+==========
+
+.. [Destination]
+    {{ ctags_url('Destination') }}
+
+.. [Mapping]
+    {{ ctags_url('Mapping') }}
+
+.. [METANOTION]
+    http://www.metanotion.net/software/sandbox/block.html
+
+.. [NAMING]
+    {{ site_url('docs/naming', True) }}
diff --git a/i2p2www/spec/common-structures.rst b/i2p2www/spec/common-structures.rst
new file mode 100644
index 0000000000000000000000000000000000000000..3ec8bd0f7b3a0525e107fe23f418b6604a851040
--- /dev/null
+++ b/i2p2www/spec/common-structures.rst
@@ -0,0 +1,1066 @@
+===============================
+Common structures Specification
+===============================
+.. meta::
+    :lastupdated: February 2016
+    :accuratefor: 0.9.24
+
+.. contents::
+
+
+This document describes some data types common to all I2P protocols, like
+[I2NP]_, [I2CP]_, [SSU]_, etc.
+
+
+Common type specification
+=========================
+
+.. _type-Integer:
+
+Integer
+-------
+
+Description
+```````````
+Represents a non-negative integer.
+
+Contents
+````````
+1 to 8 bytes in network byte order representing an unsigned integer
+
+.. _type-Date:
+
+Date
+----
+
+Description
+```````````
+The number of milliseconds since midnight on January 1, 1970 in the GMT timezone.
+If the number is 0, the date is undefined or null.
+
+Contents
+````````
+8 byte Integer_
+
+.. _type-String:
+
+String
+------
+
+Description
+```````````
+Represents a UTF-8 encoded string.
+
+Contents
+````````
+1 or more bytes where the first byte is the number of bytes (not characters!)
+in the string and the remaining 0-255 bytes are the non-null terminated UTF-8
+encoded character array.  Length limit is 255 bytes (not characters). Length
+may be 0.
+
+.. _type-Boolean:
+
+Boolean
+-------
+
+Description
+```````````
+A boolean value, supporting null/unknown representation
+0=false, 1=true, 2=unknown/null
+
+Contents
+````````
+1 byte Integer_
+
+Notes
+`````
+Deprecated - unused
+
+.. _type-PublicKey:
+
+PublicKey
+---------
+
+Description
+```````````
+This structure is used in ElGamal encryption, representing only the exponent,
+not the primes, which are constant and defined in the cryptography
+specification [ELGAMAL]_.
+
+Contents
+````````
+256 bytes
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/PublicKey.html
+
+.. _type-PrivateKey:
+
+PrivateKey
+----------
+
+Description
+```````````
+This structure is used in ElGamal decryption, representing only the exponent,
+not the primes which are constant and defined in the cryptography specification
+[ELGAMAL]_.
+
+Contents
+````````
+256 bytes
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/PrivateKey.html
+
+.. _type-SessionKey:
+
+SessionKey
+----------
+
+Description
+```````````
+This structure is used for AES256 encryption and decryption.
+
+Contents
+````````
+32 bytes
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/SessionKey.html
+
+.. _type-SigningPublicKey:
+
+SigningPublicKey
+----------------
+
+Description
+```````````
+This structure is used for verifying signatures.
+
+Contents
+````````
+Key type and length are inferred from context or are specified in the Key
+Certificate of a Destination.  The default type is DSA_SHA1.  As of release
+0.9.12, other types may be supported, depending on context.
+
+======================  ==============  ======  =====
+         Type           Length (bytes)  Since   Usage
+======================  ==============  ======  =====
+DSA_SHA1                     128                Legacy Router Identities and Destinations
+ECDSA_SHA256_P256             64        0.9.12  Recent Destinations
+ECDSA_SHA384_P384             96        0.9.12  Rarely used for Destinations
+ECDSA_SHA512_P521            132        0.9.12  Rarely used for Destinations
+RSA_SHA256_2048              256        0.9.12  Offline signing, never used for Router Identities or Destinations
+RSA_SHA384_3072              384        0.9.12  Offline signing, never used for Router Identities or Destinations
+RSA_SHA512_4096              512        0.9.12  Offline signing, never used for Router Identities or Destinations
+EdDSA_SHA512_Ed25519          32        0.9.15  Recent Router Identities and Destinations
+EdDSA_SHA512_Ed25519ph        32        0.9.25  Offline signing, never used for Router Identities or Destinations
+======================  ==============  ======  =====
+
+Notes
+`````
+* When a key is composed of two elements (for example points X,Y), it is
+  serialized by padding each element to length/2 with leading zeros if
+  necessary.
+
+* All types are Big Endian, except for EdDSA, which is stored and transmitted
+  in a Little Endian format.
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/SigningPublicKey.html
+
+.. _type-SigningPrivateKey:
+
+SigningPrivateKey
+-----------------
+
+Description
+```````````
+This structure is used for creating signatures.
+
+Contents
+````````
+Key type and length are specified when created.  The default type is DSA_SHA1.
+As of release 0.9.12, other types may be supported, depending on context.
+
+======================  ==============  ======  =====
+         Type           Length (bytes)  Since   Usage
+======================  ==============  ======  =====
+DSA_SHA1                      20                Legacy Router Identities and Destinations
+ECDSA_SHA256_P256             32        0.9.12  Recent Destinations
+ECDSA_SHA384_P384             48        0.9.12  Rarely used for Destinations
+ECDSA_SHA512_P521             66        0.9.12  Rarely used for Destinations
+RSA_SHA256_2048              512        0.9.12  Offline signing, never used for Router Identities or Destinations
+RSA_SHA384_3072              768        0.9.12  Offline signing, never used for Router Identities or Destinations
+RSA_SHA512_4096             1024        0.9.12  Offline signing, never used for Router Identities or Destinations
+EdDSA_SHA512_Ed25519          32        0.9.15  Recent Router Identities and Destinations
+EdDSA_SHA512_Ed25519ph        32        0.9.25  Offline signing, never used for Router Identities or Destinations
+======================  ==============  ======  =====
+
+Notes
+`````
+* When a key is composed of two elements (for example points X,Y), it is
+  serialized by padding each element to length/2 with leading zeros if
+  necessary.
+
+* All types are Big Endian, except for EdDSA, which is stored and transmitted
+  in a Little Endian format.
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/SigningPrivateKey.html
+
+.. _type-Signature:
+
+Signature
+---------
+
+Description
+```````````
+This structure represents the signature of some data.
+
+Contents
+````````
+Signature type and length are inferred from the type of key used.  The default
+type is DSA_SHA1.  As of release 0.9.12, other types may be supported,
+depending on context.
+
+======================  ==============  ======  =====
+         Type           Length (bytes)  Since   Usage
+======================  ==============  ======  =====
+DSA_SHA1                      40                Legacy Router Identities and Destinations
+ECDSA_SHA256_P256             64        0.9.12  Recent Destinations
+ECDSA_SHA384_P384             96        0.9.12  Rarely used for Destinations
+ECDSA_SHA512_P521            132        0.9.12  Rarely used for Destinations
+RSA_SHA256_2048              256        0.9.12  Offline signing, never used for Router Identities or Destinations
+RSA_SHA384_3072              384        0.9.12  Offline signing, never used for Router Identities or Destinations
+RSA_SHA512_4096              512        0.9.12  Offline signing, never used for Router Identities or Destinations
+EdDSA_SHA512_Ed25519          64        0.9.15  Recent Router Identities and Destinations
+EdDSA_SHA512_Ed25519ph        64        0.9.25  Offline signing, never used for Router Identities or Destinations
+======================  ==============  ======  =====
+
+Notes
+`````
+* When a signature is composed of two elements (for example values R,S), it is
+  serialized by padding each element to length/2 with leading zeros if
+  necessary.
+
+* All types are Big Endian, except for EdDSA, which is stored and transmitted
+  in a Little Endian format.
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/Signature.html
+
+.. _type-Hash:
+
+Hash
+----
+
+Description
+```````````
+Represents the SHA256 of some data.
+
+Contents
+````````
+32 bytes
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/Hash.html
+
+.. _type-SessionTag:
+
+Session Tag
+-----------
+
+Description
+```````````
+A random number
+
+Contents
+````````
+32 bytes
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/SessionTag.html
+
+.. _type-TunnelId:
+
+TunnelId
+--------
+
+Description
+```````````
+Defines an identifier that is unique to each router in a tunnel.  A Tunnel ID
+is generally greater than zero; do not use a value of zero except in special
+cases.
+
+Contents
+````````
+4 byte Integer_
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/TunnelId.html
+
+.. _type-Certificate:
+
+Certificate
+-----------
+
+Description
+```````````
+A certificate is a container for various receipts or proof of works used
+throughout the I2P network.
+
+Contents
+````````
+1 byte Integer_ specifying certificate type, followed by a 2 byte Integer_
+specifying the size of the certificate payload, then that many bytes.
+
+.. raw:: html
+
+  {% highlight lang='dataspec' -%}
++----+----+----+----+----+-//
+  |type| length  | payload
+  +----+----+----+----+----+-//
+
+  type :: `Integer`
+          length -> 1 byte
+
+          case 0 -> NULL
+          case 1 -> HASHCASH
+          case 2 -> HIDDEN
+          case 3 -> SIGNED
+          case 4 -> MULTIPLE
+          case 5 -> KEY
+
+  length :: `Integer`
+            length -> 2 bytes
+
+  payload :: data
+             length -> $length bytes
+{% endhighlight %}
+
+Notes
+`````
+* For `Router Identities`_, the Certificate is always NULL through version
+  0.9.15. As of 0.9.16, a Key Certificate may be used to specify the signing
+  public key type. See below.
+
+* For `Garlic Cloves`_, the Certificate is always NULL, no others are currently
+  implemented.
+
+* For `Garlic Messages`_, the Certificate is always NULL, no others are
+  currently implemented.
+
+* For `Destinations`_, the Certificate may be non-NULL. As of 0.9.12, a Key
+  Certificate may be used to specify the signing public key type. See below.
+
+.. _Router Identities: #struct_RouterIdentity
+.. _Garlic Cloves: {{ site_url('docs/spec/i2np') }}#struct_GarlicClove
+.. _Garlic Messages: {{ site_url('docs/spec/i2np') }}#msg_Garlic
+.. _Destinations: #struct_Destination
+
+Certificate Types
+`````````````````
+The following certificate types are defined:
+
+========  =========  ==============  ============  =====
+Type      Type Code  Payload Length  Total Length  Notes
+========  =========  ==============  ============  =====
+Null          0             0              3
+HashCash      1          varies         varies     Experimental, unused. Payload contains an ASCII colon-separated hashcash string.
+Hidden        2             0              3       Experimental, unused. Hidden routers generally do not announce that they are hidden.
+Signed        3         40 or 72       43 or 75    Experimental, unused. Payload contains a 40-byte DSA signature,
+                                                   optionally followed by the 32-byte Hash of the signing Destination.
+Multiple      4          varies         varies     Experimental, unused. Payload contains multiple certificates.
+Key           5             4+             7+      Since 0.9.12. See below for details.
+========  =========  ==============  ============  =====
+
+
+Key Certificates
+````````````````
+Key certificates were introduced in release 0.9.12.  Prior to that release, all
+PublicKeys were 256-byte ElGamal keys, and all SigningPublicKeys were 128-byte
+DSA-SHA1 keys.  A key certificate provides a mechanism to indicate the type of
+the PublicKey and SigningPublicKey in the Destination or RouterIdentity, and to
+package any key data in excess of the standard lengths.
+
+By maintaining exactly 384 bytes before the certificate, and putting any excess
+key data inside the certificate, we maintain compatibility for any software
+that parses Destinations and Router Identities.
+
+The key certificate payload contains:
+
+==================================  ======
+              Data                  Length
+==================================  ======
+Signing Public Key Type (Integer_)    2
+Crypto Public Key Type (Integer_)     2
+Excess Signing Public Key Data        0+
+Excess Crypto Public Key Data         0+
+==================================  ======
+
+The defined Signing Public Key types are:
+
+======================  ===========  =======================  ======  =====
+        Type             Type Code   Total Public Key Length  Since   Usage
+======================  ===========  =======================  ======  =====
+DSA_SHA1                     0                  128           0.9.12  Legacy Router Identities and Destinations, never explicitly set
+ECDSA_SHA256_P256            1                   64           0.9.12  Recent Destinations
+ECDSA_SHA384_P384            2                   96           0.9.12  Sometimes used for Destinations
+ECDSA_SHA512_P521            3                  132           0.9.12  Sometimes used for Destinations
+RSA_SHA256_2048              4                  256           0.9.12  Offline only; never used in Key Certificates for Router Identities or Destinations
+RSA_SHA384_3072              5                  384           0.9.12  Offline only; never used in Key Certificates for Router Identities or Destinations
+RSA_SHA512_4096              6                  512           0.9.12  Offline only; never used in Key Certificates for Router Identities or Destinations
+EdDSA_SHA512_Ed25519         7                   32           0.9.15  Recent Router Identities and Destinations
+EdDSA_SHA512_Ed25519ph       8                   32           0.9.25  Offline only; never used in Key Certificates for Router Identities or Destinations
+reserved                65280-65534                                   Reserved for experimental use
+reserved                   65535                                      Reserved for future expansion
+======================  ===========  =======================  ======  =====
+
+The defined Crypto Public Key types are:
+
+========  ===========  =======================  =====
+  Type     Type Code   Total Public Key Length  Usage
+========  ===========  =======================  =====
+ElGamal        0                 256            All Router Identities and Destinations
+reserved  65280-65534                           Reserved for experimental use
+reserved     65535                              Reserved for future expansion
+========  ===========  =======================  =====
+
+When a Key Certificate is not present, the preceeding 384 bytes in the
+Destination or RouterIdentity are defined as the 256-byte ElGamal PublicKey
+followed by the 128-byte DSA-SHA1 SigningPublicKey.  When a Key Certificate is
+present, the preceeding 384 bytes are redefined as follows:
+
+* Complete or first portion of Crypto Public Key
+
+* Random padding if the total lengths of the two keys are less than 384 bytes
+
+* Complete or first portion of Signing Public Key
+
+The Crypto Public Key is aligned at the start and the Signing Public Key is
+aligned at the end.  The padding (if any) is in the middle.  The lengths and
+boundaries of the initial key data, the padding, and the excess key data
+portions in the certificates are not explicitly specified, but are derived from
+the lengths of the specified key types.  If the total lengths of the Crypto and
+Signing Public Keys exceed 384 bytes, the remainder will be contained in the
+Key Certificate.  If the Crypto Public Key length is not 256 bytes, the method
+for determining the boundary between the two keys is to be specified in a
+future revision of this document.
+
+Example layouts using an ElGamal Crypto Public Key and the Signing Public Key
+type indicated:
+
+======================  ==============  ===============================
+   Signing Key Type     Padding Length  Excess Signing Key Data in Cert
+======================  ==============  ===============================
+DSA_SHA1                       0                        0
+ECDSA_SHA256_P256             64                        0
+ECDSA_SHA384_P384             32                        0
+ECDSA_SHA512_P521              0                        4
+RSA_SHA256_2048                0                      128
+RSA_SHA384_3072                0                      256
+RSA_SHA512_4096                0                      384
+EdDSA_SHA512_Ed25519          96                        0
+EdDSA_SHA512_Ed25519ph        96                        0
+======================  ==============  ===============================
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/Certificate.html
+
+.. _type-Mapping:
+
+Mapping
+-------
+
+Description
+```````````
+A set of key/value mappings or properties
+
+Contents
+````````
+A 2-byte size Integer followed by a series of String=String; pairs
+
+.. raw:: html
+
+  {% highlight lang='dataspec' -%}
++----+----+----+----+----+----+----+----+
+  |  size   | key_string (len + data)| =  |
+  +----+----+----+----+----+----+----+----+
+  | val_string (len + data)     | ;  | ...
+  +----+----+----+----+----+----+----+
+  size :: `Integer`
+          length -> 2 bytes
+          Total number of bytes that follow
+
+  key_string :: `String`
+                A string (one byte length followed by UTF-8 encoded characters)
+
+  = :: A single byte containing '='
+
+  val_string :: `String`
+                A string (one byte length followed by UTF-8 encoded characters)
+
+  ; :: A single byte containing ';'
+{% endhighlight %}
+
+Notes
+`````
+* The encoding isn't optimal - we either need the '=' and ';' characters, or
+  the string lengths, but not both
+
+* Some documentation says that the strings may not include '=' or ';' but this
+  encoding supports them
+
+* Strings are defined to be UTF-8 but in the current implementation, I2CP uses
+  UTF-8 but I2NP does not. For example, UTF-8 strings in a RouterInfo options
+  mapping in a I2NP Database Store Message will be corrupted.
+
+* The encoding allows duplicate keys, however in any usage where the mapping is
+  signed, duplicates may cause a signature failure.
+
+* Mappings contained in I2NP messages (i.e. in a RouterAddress or RouterInfo)
+  must be sorted by key so that the signature will be invariant. Duplicate keys
+  are not allowed.
+
+* Mappings contained in an `I2CP SessionConfig`_ must be sorted by key so that
+  the signature will be invariant. Duplicate keys are not allowed.
+
+* The sort method is defined as in Java String.compareTo(), using the Unicode
+  value of the characters.
+
+* While it is application-dependent, keys and values are generally
+  case-sensitive.
+
+* Key and value string length limits are 255 bytes (not characters) each, plus
+  the length byte. Length byte may be 0.
+
+* Total length limit is 65535 bytes, plus the 2 byte size field, or 65537
+  total.
+
+.. _I2CP SessionConfig: {{ site_url('docs/spec/i2cp') }}#struct_SessionConfig
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/DataHelper.html
+
+
+Common structure specification
+==============================
+
+.. _struct-KeysAndCert:
+
+KeysAndCert
+-----------
+
+Description
+```````````
+An encryption public key, a signing public key, and a certificate, used as
+either a RouterIdentity or a Destination.
+
+Contents
+````````
+A PublicKey_ followed by a SigningPublicKey_ and then a Certificate_.
+
+.. raw:: html
+
+  {% highlight lang='dataspec' -%}
++----+----+----+----+----+----+----+----+
+  | public_key                            |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | padding (optional)                    |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | signing_key                           |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | certificate                           |
+  +----+----+----+-//
+
+  public_key :: `PublicKey` (partial or full)
+                length -> 256 bytes or as specified in key certificate
+
+  padding :: random data
+             length -> 0 bytes or as specified in key certificate
+             padding length + signing_key length == 128 bytes
+
+  signing__key :: `SigningPublicKey` (partial or full)
+                  length -> 128 bytes or as specified in key certificate
+                  padding length + signing_key length == 128 bytes
+
+  certificate :: `Certificate`
+                 length -> >= 3 bytes
+
+  total length: 387+ bytes
+{% endhighlight %}
+
+Notes
+`````
+* Do not assume that these are always 387 bytes! They are 387 bytes plus the
+  certificate length specified at bytes 385-386, which may be non-zero.
+
+* As of release 0.9.12, if the certificate is a Key Certificate, the boundaries
+  of the key fields may vary. See the Key Certificate section above for
+  details.
+
+* The Crypto Public Key is aligned at the start and the Signing Public Key is
+  aligned at the end. The padding (if any) is in the middle.
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/KeysAndCert.html
+
+.. _struct-RouterIdentity:
+
+RouterIdentity
+--------------
+
+Description
+```````````
+Defines the way to uniquely identify a particular router
+
+Contents
+````````
+Identical to KeysAndCert.
+
+Notes
+`````
+* The certificate for a RouterIdentity was always NULL until release 0.9.12.
+
+* Do not assume that these are always 387 bytes! They are 387 bytes plus the
+  certificate length specified at bytes 385-386, which may be non-zero.
+
+* As of release 0.9.12, if the certificate is a Key Certificate, the boundaries
+  of the key fields may vary. See the Key Certificate section above for
+  details.
+
+* The Crypto Public Key is aligned at the start and the Signing Public Key is
+  aligned at the end. The padding (if any) is in the middle.
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/router/RouterIdentity.html
+
+.. _struct-Destination:
+
+Destination
+-----------
+
+Description
+```````````
+A Destination defines a particular endpoint to which messages can be directed
+for secure delivery.
+
+Contents
+````````
+Identical to KeysAndCert_.
+
+Notes
+`````
+* The public key of the destination was used for the old i2cp-to-i2cp
+  encryption which was disabled in version 0.6, it is currently unused except
+  for the IV for LeaseSet encryption, which is deprecated. The public key in
+  the LeaseSet is used instead.
+
+* Do not assume that these are always 387 bytes! They are 387 bytes plus the
+  certificate length specified at bytes 385-386, which may be non-zero.
+
+* As of release 0.9.12, if the certificate is a Key Certificate, the boundaries
+  of the key fields may vary. See the Key Certificate section above for
+  details.
+
+* The Crypto Public Key is aligned at the start and the Signing Public Key is
+  aligned at the end. The padding (if any) is in the middle.
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/Destination.html
+
+.. _struct-Lease:
+
+Lease
+-----
+
+Description
+```````````
+Defines the authorization for a particular tunnel to receive messages targeting
+a Destination_.
+
+Contents
+````````
+SHA256 Hash_ of the RouterIdentity_ of the gateway router, then the TunnelId_,
+and finally an end Date_.
+
+.. raw:: html
+
+  {% highlight lang='dataspec' -%}
++----+----+----+----+----+----+----+----+
+  | tunnel_gw                             |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |     tunnel_id     |      end_date
+  +----+----+----+----+----+----+----+----+
+                      |
+  +----+----+----+----+
+
+  tunnel_gw :: Hash of the `RouterIdentity` of the tunnel gateway
+               length -> 32 bytes
+
+  tunnel_id :: `TunnelId`
+               length -> 4 bytes
+
+  end_date :: `Date`
+              length -> 8 bytes
+{% endhighlight %}
+
+Notes
+`````
+* Total size: 44 bytes
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/Lease.html
+
+.. _struct-LeaseSet:
+
+LeaseSet
+--------
+
+Description
+```````````
+Contains all of the currently authorized Leases_ for a particular Destination_,
+the PublicKey_ to which garlic messages can be encrypted, and then the
+SigningPublicKey_ that can be used to revoke this particular version of the
+structure. The LeaseSet is one of the two structures stored in the network
+database (the other being RouterInfo_), and is keyed under the SHA256 of the
+contained Destination_.
+
+.. _Leases: _Lease
+
+Contents
+````````
+Destination_, followed by a PublicKey_ for encryption, then a SigningPublicKey_
+which can be used to revoke this version of the LeaseSet, then a 1 byte
+Integer_ specifying how many Lease_ structures are in the set, followed by the
+actual Lease_ structures and finally a Signature_ of the previous bytes signed
+by the Destination_'s SigningPrivateKey_.
+
+.. raw:: html
+
+  {% highlight lang='dataspec' -%}
++----+----+----+----+----+----+----+----+
+  | destination                           |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | encryption_key                        |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | signing_key                           |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | num| Lease 0                          |
+  +----+                                  +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | Lease 1                               |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | Lease ($num-1)                        |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | signature                             |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+
+  destination :: `Destination`
+                 length -> >= 387 bytes
+
+  encryption_key :: `PublicKey`
+                    length -> 256 bytes
+
+  signing_key :: `SigningPublicKey`
+                 length -> 128 bytes or as specified in destination's key
+                           certificate
+
+  num :: `Integer`
+         length -> 1 byte
+         Number of leases to follow
+         value: 0 <= num <= 16
+
+  leases :: [`Lease`]
+            length -> $num*44 bytes
+
+  signature :: `Signature`
+               length -> 40 bytes or as specified in destination's key
+                         certificate
+{% endhighlight %}
+
+Notes
+`````
+* The public key of the destination was used for the old I2CP-to-I2CP
+  encryption which was disabled in version 0.6, it is currently unused.
+
+* The encryption key is used for end-to-end ElGamal/AES+SessionTag encryption
+  [ELGAMAL-AES]_. It is currently generated anew at every router startup, it is
+  not persistent.
+
+* The signature may be verified using the signing public key of the
+  destination.
+
+* The signing_key is currently unused. It was intended for LeaseSet revocation,
+  which is unimplemented. It is currently generated anew at every router
+  startup, it is not persistent. The signing key type is always the same as the
+  destination's signing key type.
+
+* The earliest expiration of all the Leases is treated as the timestamp or
+  version of the LeaseSet. Routers will generally not accept a store of a
+  LeaseSet unless it is "newer" than the current one. Take care when publishing
+  a new LeaseSet where the oldest Lease is the same as the oldest Lease in the
+  previous LeaseSet. The publishing router should generally increment the
+  expiration of the oldest Lease by at least 1 ms in that case.
+
+* Prior to release 0.9.7, when included in a DatabaseStore Message sent by the
+  originating router, the router set all the published leases' expirations to
+  the same value, that of the earliest lease. As of release 0.9.7, the router
+  publishes the actual lease expiration for each lease. This is an
+  implementation detail and not part of the structures specification.
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/LeaseSet.html
+
+.. _struct-RouterAddress:
+
+RouterAddress
+-------------
+
+Description
+```````````
+This structure defines the means to contact a router through a transport
+protocol.
+
+Contents
+````````
+1 byte Integer_ defining the relative cost of using the address, where 0 is
+free and 255 is expensive, followed by the expiration Date_ after which the
+address should not be used, or if null, the address never expires. After that
+comes a String_ defining the transport protocol this router address uses.
+Finally there is a Mapping_ containing all of the transport specific options
+necessary to establish the connection, such as IP address, port number, email
+address, URL, etc.
+
+.. raw:: html
+
+  {% highlight lang='dataspec' -%}
++----+----+----+----+----+----+----+----+
+  |cost|           expiration
+  +----+----+----+----+----+----+----+----+
+       |        transport_style           |
+  +----+----+----+----+-//-+----+----+----+
+  |                                       |
+  +                                       +
+  |               options                 |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+
+  cost :: `Integer`
+          length -> 1 byte
+
+          case 0 -> free
+          case 255 -> expensive
+
+  expiration :: `Date` (must be all zeros, see notes below)
+                length -> 8 bytes
+
+                case null -> never expires
+
+  transport_style :: `String`
+                     length -> 1-256 bytes
+
+  options :: `Mapping`
+{% endhighlight %}
+
+Notes
+`````
+* Cost is typically 5 or 6 for SSU, and 10 or 11 for NTCP.
+
+* Expiration is currently unused, always null (all zeroes). As of release
+  0.9.3, the expiration is assumed zero and not stored, so any non-zero
+  expiration will fail in the RouterInfo signature verification. Implementing
+  expiration (or another use for these bytes) will be a backwards-incompatible
+  change. Routers MUST set this field to all zeros. As of release 0.9.12, a
+  non-zero expiration field is again recognized, however we must wait several
+  releases to use this field, until the vast majority of the network recognizes
+  it.
+
+* The following options, while not required, are standard and expected to be
+  present in most router addresses: "host" (an IPv4 or IPv6 address or host
+  name) and "port".
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/router/RouterAddress.html
+
+.. _struct-RouterInfo:
+
+RouterInfo
+----------
+
+Description
+```````````
+Defines all of the data that a router wants to publish for the network to see.
+The RouterInfo_ is one of two structures stored in the network database (the
+other being LeaseSet_), and is keyed under the SHA256 of the contained
+RouterIdentity_.
+
+Contents
+````````
+RouterIdentity_ followed by the Date_, when the entry was published
+
+.. raw:: html
+
+  {% highlight lang='dataspec' -%}
++----+----+----+----+----+----+----+----+
+  | router_ident                          |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | published                             |
+  +----+----+----+----+----+----+----+----+
+  |size| RouterAddress 0                  |
+  +----+                                  +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | RouterAddress 1                       |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | RouterAddress ($size-1)               |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+-//-+----+----+----+
+  |psiz| options                          |
+  +----+----+----+----+-//-+----+----+----+
+  | signature                             |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+
+  router_ident :: `RouterIdentity`
+                  length -> >= 387 bytes
+
+  published :: `Date`
+               length -> 8 bytes
+
+  size :: `Integer`
+          length -> 1 byte
+          The number of `RouterAddress`es to follow, 0-255
+
+  addresses :: [`RouterAddress`]
+               length -> varies
+
+  peer_size :: `Integer`
+               length -> 1 byte
+               The number of peer `Hash`es to follow, 0-255, unused, always zero
+               value -> 0
+
+  options :: `Mapping`
+
+  signature :: `Signature`
+               length -> 40 bytes
+{% endhighlight %}
+
+Notes
+`````
+* The peer_size Integer_ may be followed by a list of that many router hashes.
+  This is currently unused. It was intended for a form of restricted routes,
+  which is unimplemented.
+
+* The signature may be verified using the signing public key of the
+  router_ident.
+
+* See the network database page [NETDB-ROUTERINFO]_ for standard options that
+  are expected to be present in all router infos.
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/router/RouterInfo.html
+
+.. _struct-DeliveryInstructions:
+
+Delivery Instructions
+---------------------
+
+Tunnel Message Delivery Instructions are defined in the Tunnel Message
+Specification [TUNNEL-DELIVERY]_.
+
+Garlic Message Delivery Instructions are defined in the I2NP Message
+Specification [GARLIC-DELIVERY]_.
+
+
+References
+==========
+
+.. [ELGAMAL]
+    {{ site_url('docs/how/cryptography', True) }}#elgamal
+
+.. [ELGAMAL-AES]
+    {{ site_url('docs/how/elgamal-aes', True) }}
+
+.. [GARLIC-DELIVERY]
+    {{ ctags_url('GarlicCloveDeliveryInstructions') }}
+
+.. [I2CP]
+    {{ site_url('docs/protocol/i2cp', True) }}
+
+.. [I2NP]
+    {{ site_url('docs/protocol/i2np', True) }}
+
+.. [NETDB-ROUTERINFO]
+    {{ site_url('docs/how/network-database', True) }}#routerInfo
+
+.. [SSU]
+    {{ site_url('docs/transport/ssu', True) }}
+
+.. [TUNNEL-DELIVERY]
+    {{ ctags_url('TunnelMessageDeliveryInstructions') }}
diff --git a/i2p2www/spec/configuration.rst b/i2p2www/spec/configuration.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d909b0502e491cdd00c17d145f418796ceb812d7
--- /dev/null
+++ b/i2p2www/spec/configuration.rst
@@ -0,0 +1,468 @@
+================================
+Configuration File Specification
+================================
+.. meta::
+    :lastupdated: February 2016
+    :accuratefor: 0.9.25
+
+.. contents::
+
+
+Overview
+========
+
+This page provides a general specification of I2P configuration files, used by
+the router and various applications.  It also gives an overview of the
+information contained in the various files, and links to detailed documentation
+where available.
+
+
+General Format
+==============
+
+An I2P configuration file is formatted as specified in Java [Properties]_ with
+the following exceptions:
+
+* Encoding must be UTF-8
+
+* Does not use or recognize any escapes, including '\', so lines may not be
+  continued
+
+* '#' or ';' starts a comment, but '!' does not
+
+* '#' starts a comment in any position but ';' must be in column 1 to start a
+  comment
+
+* Leading and trailing whitespace is not trimmed on keys
+
+* Leading and trailing whitespace is trimmed on values
+
+* '=' is the only key-termination character (not ':' or whitespace)
+
+* Lines without '=' are ignored. As of release 0.9.10, keys with a value of ""
+  are supported.
+
+* As there are no escapes, keys may not contain '#', '=', or '\n', or start with
+  ';'
+
+* As there are no escapes, values may not contain '#' or '\n', or start or end
+  with '\r' or whitespace
+
+The file need not be sorted, but most applications do sort by key when writing
+to the file, for ease of reading and manual editing.
+
+Reads and writes are implemented in DataHelper loadProps() and storeProps()
+[DATAHELPER]_.  Note that the file format is significantly different than the
+serialized format for I2P protocols specified in [Mapping]_.
+
+
+Core library and router
+=======================
+
+Clients (clients.config)
+------------------------
+
+Configured via /configclients in the router console.
+
+The format is as follows:
+
+Lines are of the form clientApp.x.prop=val, where x is the app number.  App
+numbers MUST start with 0 and be consecutive.
+
+Properties are as follows:
+
+    main
+        Full class name. Required.
+
+        The constructor or main() method in this class will be run, depending on
+        whether the client is managed or unmanaged. See below for details.
+
+    name
+        Name to be displayed on console.
+
+    args
+        Arguments to the main class, separated by spaces or tabs.
+        Arguments containing spaces or tabs may be quoted with ' or "
+
+    delay
+        Seconds before starting, default 120
+
+    onBoot
+        {true|false},
+
+        Default false, forces a delay of 0, overrides delay setting
+
+    startOnLoad:
+        {true|false}
+
+        Is the client to be run at all? Default true
+
+The following additional properties are used only by plugins:
+
+    stopargs
+        Arguments to stop the client.
+
+    uninstallargs
+        Arguments to uninstall the client.
+
+    classpath
+        Additional classpath elements for the client, separated by commas.
+
+The following substitutions are made in the args, stopargs,
+uninstallargs, and classpath lines, for plugins only:
+
+    $I2P
+        The base I2P install directory
+
+    $CONFIG
+        The user's configuration directory (e.g. ~/.i2p)
+
+    $PLUGIN
+        This plugin's directory (e.g. ~/.i2p/plugins/foo)
+
+All properties except "main" are optional.  Lines starting with "#" are
+comments.
+
+If the delay is less than zero, the client is run immediately, in the same
+thread, so that exceptions may be propagated to the console.  In this case, the
+client should either throw an exception, return quickly, or spawn its own
+thread.
+
+If the delay is greater than or equal to zero, it will be run in a new thread,
+and exceptions will be logged but not propagated to the console.
+
+Clients may be "managed" or "unmanaged".
+
+Logger (logger.config)
+----------------------
+
+Configured via /configlogging in the router console.
+
+Properties are as follows::
+
+    # Default 20
+    logger.consoleBufferSize=n
+    # Default from locale; format as specified by Java SimpleDateFormat
+    logger.dateFormat=HH:mm:ss.SSS
+    # Default ERROR
+    logger.defaultLevel=CRIT|ERROR|WARN|INFO|DEBUG
+    # Default true
+    logger.displayOnScreen=true|false
+    # Default true
+    logger.dropDuplicates=true|false
+    # Default false
+    logger.dropOnOverflow=true|false
+    # As of 0.9.18. Default 29 (seconds)
+    logger.flushInterval=nnn
+    # d = date, c = class, t = thread name, p = priority, m = message
+    logger.format={dctpm}*
+    # Max to buffer before flushing. Default 1024
+    logger.logBufferSize=n
+    # Default logs/log-@.txt; @ replaced with number
+    logger.logFileName=name
+    logger.logFilenameOverride=name
+    # Default 10M
+    logger.logFileSize=nnn[K|M|G]
+    # Highest file number. Default 2
+    logger.logRotationLimit=n
+    # Default CRIT
+    logger.minimumOnScreenLevel=CRIT|ERROR|WARN|INFO|DEBUG
+    logger.record.{class}=CRIT|ERROR|WARN|INFO|DEBUG
+
+Individual Plugin (xxx/plugin.config)
+-------------------------------------
+
+See the plugin specification [PLUGIN]_.
+
+Plugins (plugins.config)
+------------------------
+
+Enable/disable for each installed plugin.
+
+Properties are as follows::
+
+    plugin.{name}.startOnLoad=true|false
+
+Webapps (webapps.config)
+------------------------
+
+Enable/disable for each installed webapp.
+
+Properties are as follows::
+
+    webapps.{name}.classpath=[space- or comma-separated paths]
+    webapps.{name}.startOnLoad=true|false
+
+Router (router.config)
+----------------------
+
+Configured via /configadvanced in the router console.
+
+
+Applications
+============
+
+Addressbook (addressbook/config.txt)
+------------------------------------
+
+See documentation in SusiDNS.
+
+I2PSnark (i2psnark.config)
+--------------------------
+
+Configured via the application gui.
+
+I2PTunnel (i2ptunnel.config)
+----------------------------
+
+Configured via the /i2ptunnel application in the router console.
+
+Properties are as follows::
+
+    # Display description for UI
+    tunnel.N.description=
+
+    # Router IP address or host name. Ignored if in router context.
+    tunnel.N.i2cpHost=127.0.0.1
+
+    # Router I2CP port. Ignored if in router context.
+    tunnel.N.i2cpPort=nnnn
+
+    # For clients only. Local listen IP address or host name.
+    tunnel.N.interface=127.0.0.1
+
+    # For clients only. Local listen port.
+    tunnel.N.listenPort=nnnn
+
+    # Display name for UI
+    tunnel.N.name=
+
+    # Servers only. Default false. Originate connections to local server with a
+    # unique IP per-remote-destination.
+    tunnel.N.option.enableUniqueLocal=true|false
+
+    # Servers only. The maximum size of the thread pool, default 65. Ignored
+    # for standard servers.
+    tunnel.N.option.i2ptunnel.blockingHandlerCount=nnn
+
+    # HTTP client only. Whether to use allow SSL connections to i2p addresses.
+    # Default false.
+    tunnel.N.option.i2ptunnel.httpclient.allowInternalSSL=true|false
+
+    # HTTP client only. Whether to disable address helper links. Default false.
+    tunnel.N.option.i2ptunnel.httpclient.disableAddressHelper=true|false
+
+    # HTTP client only. Comma- or space-separated list of jump server URLs.
+    tunnel.N.option.i2ptunnel.httpclient.jumpServers=http://example.i2p/jump
+
+    # HTTP client only. Whether to pass Accept* headers through. Default false.
+    tunnel.N.option.i2ptunnel.httpclient.sendAccept=true|false
+
+    # HTTP client only. Whether to pass Referer headers through. Default false.
+    tunnel.N.option.i2ptunnel.httpclient.sendReferer=true|false
+
+    # HTTP client only. Whether to pass User-Agent headers through. Default
+    # false.
+    tunnel.N.option.i2ptunnel.httpclient.sendUserAgent=true|false
+
+    # HTTP client only. Whether to pass Via headers through. Default false.
+    tunnel.N.option.i2ptunnel.httpclient.sendVia=true|false
+
+    # HTTP client only. Comma- or space-separated list of in-network SSL
+    # outproxies.
+    tunnel.N.option.i2ptunnel.httpclient.SSLOutproxies=example.i2p
+
+    # SOCKS client only. Comma- or space-separated list of in-network
+    # outproxies for any ports not specified.
+    tunnel.N.option.i2ptunnel.socks.proxy.default=example.i2p
+
+    # SOCKS client only. Comma- or space-separated list of in-network
+    # outproxies for port NNNN.
+    tunnel.N.option.i2ptunnel.socks.proxy.NNNN=example.i2p
+
+    # HTTP client only. Whether to use a registered local outproxy plugin.
+    # Default true.
+    tunnel.N.option.i2ptunnel.useLocalOutproxy=true|false
+
+    # Servers only. Whether to use a thread pool. Default true. Ignored for
+    # standard servers, always false.
+    tunnel.N.option.i2ptunnel.usePool=true|false
+
+    # IRC Server only. Only used if fakeHostname contains a %c.  If unset,
+    # cloak with a random value that is persistent for the life of this tunnel.
+    # If set, cloak with the hash of this passphrase.  Use to have consistent
+    # mangling across restarts, or for multiple IRC servers cloak consistently
+    # to be able to track users even when they switch servers.  Note: don't
+    # quote or put spaces in the passphrase, the i2ptunnel gui can't handle it.
+    tunnel.N.option.ircserver.cloakKey=
+
+    # IRC Server only. Set the fake hostname sent by I2PTunnel, %f is the full
+    # B32 destination hash, %c is the cloaked hash.
+    tunnel.N.option.ircserver.fakeHostname=%f.b32.i2p
+
+    # IRC Server only. Default user.
+    tunnel.N.option.ircserver.method=user|webirc
+
+    # IRC Server only. The password to use for the webirc protocol.  Note:
+    # don't quote or put spaces in the passphrase, the i2ptunnel gui can't
+    # handle it.
+    tunnel.N.option.ircserver.webircPassword=
+
+    # IRC Server only.
+    tunnel.N.option.ircserver.webircSpoofIP=
+
+    # For clients only. Alias for the private key in the keystore for the SSL
+    # socket. Will be autogenerated if a new key is created.
+    tunnel.N.option.keyAlias=
+
+    # For clients only. Password for the private key for the SSL socket. Will be
+    # autogenerated if a new key is created.
+    tunnel.N.option.keyPassword=
+
+    # For clients only. Path to the keystore file containing the private key for
+    # the SSL socket. Will be autogenerated if a new keystore is created.
+    # Relative to $(I2P_CONFIG_DIR)/keystore/ if not absolute.
+    tunnel.N.option.keystoreFile=i2ptunnel-(random string).ks
+
+    # For clients only. Password for the keystore containing the private key for
+    # the SSL socket. Default is "changeit".
+    tunnel.N.option.keystorePassword=changeit
+
+    # HTTP Server only. Max number of POSTs allowed for one destination per
+    # postCheckTime. Default 0 (unlimited)
+    tunnel.N.option.maxPosts=nnn
+
+    # HTTP Server only. Max number of POSTs allowed for all destinations per
+    # postCheckTime. Default 0 (unlimited)
+    tunnel.N.option.maxTotalPosts=nnn
+
+    # HTTP Clients only. Whether to send authorization to an outproxy. Default
+    # false.
+    tunnel.N.option.outproxyAuth=true|false
+
+    # HTTP Clients only. The password for the outproxy authorization.
+    tunnel.N.option.outproxyPassword=
+
+    # HTTP Clients only. The username for the outproxy authorization.
+    tunnel.N.option.outproxyUsername=
+
+    # HTTP Clients only. Whether to send authorization to an outproxy. Default
+    # false.
+    tunnel.N.option.outproxyAuth=true|false
+
+    # Clients only. Whether to store a destination in a private key file and
+    # reuse it. Default false.
+    tunnel.N.option.persistentClientKey=true|false
+
+    # HTTP Server only. Time period for banning POSTs from a single destination
+    # after maxPosts is exceeded, in seconds. Default 1800 seconds.
+    tunnel.N.option.postBanTime=nnn
+
+    # HTTP Server only. Time period for checking maxPosts and maxTotalPosts, in
+    # seconds. Default 300 seconds.
+    tunnel.N.option.postCheckTime=nnn
+
+    # HTTP Server only. Time period for banning all POSTs after maxTotalPosts
+    # is exceeded, in seconds. Default 600 seconds.
+    tunnel.N.option.postTotalBanTime=nnn
+
+    # HTTP Clients only. Whether to require local authorization for the proxy.
+    # Default false. "true" is the same as "basic".
+    tunnel.N.option.proxyAuth=true|false|basic|digest
+
+    # HTTP Clients only. The MD5 of the password for local authorization for
+    # user USER.
+    tunnel.N.option.proxy.auth.USER.md5=
+
+    # HTTP Servers only. Whether to reject incoming connections apparently via
+    # an inproxy. Default false.
+    tunnel.N.option.rejectInproxy=true|false
+
+    # HTTP Servers only. Whether to reject incoming connections containing a
+    # referer header. Default false. Since 0.9.25.
+    tunnel.N.option.rejectReferer=true|false
+
+    # HTTP Servers only. Whether to reject incoming connections containing
+    # specific user-agent headers. Default false. Since 0.9.25. See
+    # tunnel.N.option.userAgentRejectList
+    tunnel.N.option.rejectUserAgents=true|false
+
+    # Servers only. Overrides targetHost and targetPort for incoming port NNNN.
+    tunnel.N.option.targetForPort.NNNN=hostnameOrIP:nnnn
+
+    # HTTP Servers only. Comma-separated list of strings to match in the
+    # user-agent header. Since 0.9.25. Example: "Mozilla,Opera". Case-sensitive.
+    # See tunnel.N.option.rejectUserAgents
+    tunnel.N.option.userAgentRejectList=string1[,string2]*
+
+    # Default false. For servers, use SSL for connections to local server. For
+    # clients, SSL is required for connections from local clients.
+    tunnel.N.option.useSSL=false
+
+    # Each option is passed to I2CP and streaming with "tunnel.N.option."
+    # stripped off. See those docs.
+    tunnel.N.option.*=
+
+    # For servers and clients with persistent keys only. Absolute path or
+    # relative to config directory.
+    tunnel.N.privKeyFile=filename
+
+    # For proxies only. Comma- or space-separated host names.
+    tunnel.N.proxyList=example.i2p[,example2.i2p]
+
+    # For clients only. Default false.
+    tunnel.N.sharedClient=true|false
+
+    # For HTTP servers only. Host name to be passed to the local server in the
+    # HTTP headers.  Default is the base 32 hostname.
+    tunnel.N.spoofedHost=example.i2p
+
+    # For HTTP servers only. Host name to be passed to the local server in the
+    # HTTP headers.  Overrides above setting for incoming port NNNN, to allow
+    # virtual hosts.
+    tunnel.N.spoofedHost.NNNN=example.i2p
+
+    # Default true
+    tunnel.N.startOnLoad=true|false
+
+    # For clients only. Comma- or space-separated host names or host:port.
+    tunnel.N.targetDestination=example.i2p[:nnnn][,example2.i2p[:nnnn]]
+
+    # For servers only. Local IP address or host name to connect to.
+    tunnel.N.targetHost=
+
+    # For servers only. Port on targetHost to connect to.
+    tunnel.N.targetPort=nnnn
+
+    # The type of i2ptunnel
+    tunnel.N.type=client|connectclient|httpbidirserver|httpclient|httpserver|ircclient|ircserver|
+              server|socksirctunnel|sockstunnel|streamrclient|streamrserver
+
+Note: Each 'N' is a tunnel number starting with 0.
+There may not be any gaps in numbering.
+
+Router Console
+--------------
+
+The router console uses the router.config file.
+
+SusiMail (susimail.config)
+--------------------------
+
+See post on zzz.i2p.
+
+
+References
+==========
+
+.. [DATAHELPER]
+    http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/DataHelper.html
+
+.. [Mapping]
+    {{ ctags_url('Mapping') }}
+
+.. [PLUGIN]
+    {{ spec_url('plugin') }}
+
+.. [Properties]
+    http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Properties.html#load%28java.io.InputStream%29
diff --git a/i2p2www/spec/cryptography.rst b/i2p2www/spec/cryptography.rst
new file mode 100644
index 0000000000000000000000000000000000000000..651b5a9d1ed8c3d0016bd349f1b378245209a5bc
--- /dev/null
+++ b/i2p2www/spec/cryptography.rst
@@ -0,0 +1,547 @@
+====================================
+Low-level Cryptography Specification
+====================================
+.. meta::
+    :lastupdated: December 2014
+    :accuratefor: 0.9.17
+
+.. contents::
+
+
+Overview
+========
+
+This page specifies the low-level details of the cryptography in I2P.
+
+There are a handful of cryptographic algorithms in use within I2P, but we have
+reduced them to a bare minimum to deal with our needs - one symmetric algorithm
+one asymmetric algorithm, one signing algorithm, and one hashing algorithm.
+However, we do combine them in some particular ways to provide message
+integrity (rather than relying on a MAC).  In addition, as much as we hate
+doing anything new in regards to cryptography, we can't seem to find a
+reference discussing (or even naming) the technique used in
+ElGamal/AES+SessionTag [ELG-AES]_ (but we're sure others have done it).
+
+
+Asymmetric encryption
+=====================
+
+ElGamal
+-------
+
+ElGamal is used for asymmetric encryption.  ElGamal is used in several places
+in I2P:
+
+* To encrypt router-to-router [TunnelBuild]_ messages
+
+* For end-to-end (destination-to-destination) encryption as a part of
+  ElGamal/AES+SessionTag [ELG-AES]_ using the encryption key in the [LeaseSet]_
+
+* For encryption of some netDb stores and queries sent to floodfill routers
+  [NETDB-DELIVERY]_ as a part of ElGamal/AES+SessionTag [ELG-AES]_
+  (destination-to-router or router-to-router).
+
+We use common primes for 2048 ElGamal encryption and decryption, as given by
+IETF [RFC-3526]_.  We currently only use ElGamal to encrypt the IV and session
+key in a single block, followed by the AES encrypted payload using that key and
+IV.
+
+The unencrypted ElGamal contains: 
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |nonz|           H(data)                |
+  +----+                                  +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +    +----+----+----+----+----+----+----+
+  |    |  data...
+  +----+----+----+-//
+{% endhighlight %}
+
+The H(data) is the SHA256 of the data that is encrypted in the ElGamal block,
+and is preceded by a nonzero byte.  This byte could be random, but as
+implemented it is always 0xFF.  It could possibly be used for flags in the
+future.  The data encrypted in the block may be up to 222 bytes long.  As the
+encrypted data may contain a substantial number of zeros if the cleartext is
+smaller than 222 bytes, it is recommended that higher layers pad the cleartext
+to 222 bytes with random data.  Total length: typically 255 bytes.
+
+The encrypted ElGamal contains: 
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |  zero padding...       |              |
+  +----+----+----+-//-+----+              +
+  |                                       |
+  +                                       +
+  |       ElG encrypted part 1            |
+  ~                                       ~
+  |                                       |
+  +    +----+----+----+----+----+----+----+
+  |    |   zero padding...      |         |
+  +----+----+----+----+-//-+----+         +
+  |                                       |
+  +                                       +
+  |       ElG encrypted part 2            |
+  ~                                       ~
+  |                                       |
+  +         +----+----+----+----+----+----+
+  |         +
+  +----+----+
+{% endhighlight %}
+
+Each encrypted part is prepended with zeros to a size of exactly 257 bytes.
+Total length: 514 bytes.  In typical usage, higher layers pad the cleartext
+data to 222 bytes, resulting in an unencrypted block of 255 bytes.  This is
+encoded as two 256-byte encrypted parts, and there is a single byte of zero
+padding before each part at this layer.
+
+See the ElGamal code [ElGamalEngine]_.
+
+The shared prime is the Oakley prime for 2048 bit keys [RFC-3526-S3]_::
+
+    2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
+
+or as a hexadecimal value::
+
+    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+    29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+    EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+    E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+    EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
+    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
+    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
+    15728E5A 8AACAA68 FFFFFFFF FFFFFFFF
+
+Using 2 as the generator.
+
+.. _exponent:
+
+Short Exponent
+``````````````
+While the standard exponent size is 2048 bits (256 bytes) and the I2P
+[PrivateKey]_ is a full 256 bytes, in some cases we use the short exponent size
+of 226 bits (28.25 bytes).  This should be safe for use with the Oakley primes
+[vanOorschot1996]_ [BENCHMARKS]_.
+
+Also, [Koshiba2004]_ apparently supports this, according to this sci.crypt
+thread [SCI.CRYPT]_.  The remainder of the PrivateKey is padded with zeroes.
+
+Prior to release 0.9.8, all routers used the short exponent.  As of release
+0.9.8, 64-bit x86 routers use a full 2048-bit exponent.  Other routers continue
+to use the short exponent due to concerns about processor load.  The transition
+to a longer exponent for these platforms is a topic for further study.
+
+Obsolescence
+````````````
+The vulnerability of the network to an ElGamal attack and the impact of
+transitioning to a longer bit length is to be studied.  It may be quite
+difficult to make any change backward-compatible.
+
+
+Symmetric encryption
+====================
+
+AES
+---
+
+AES is used for symmetric encryption, in several cases:
+
+* For transport encryption (see section "`Transports`_") after DH key exchange
+
+* For end-to-end (destination-to-destination) encryption as a part of
+  ElGamal/AES+SessionTag [ELG-AES]_
+
+* For encryption of some netDb stores and queries sent to floodfill routers
+  [NETDB-DELIVERY]_ as a part of ElGamal/AES+SessionTag [ELG-AES]_
+  (destination-to-router or router-to-router).
+
+* For encryption of periodic tunnel test messages [TUNNEL-TESTING]_ sent from
+  the router to itself, through its own tunnels.
+
+We use AES with 256 bit keys and 128 bit blocks in CBC mode.  The padding used
+is specified in IETF [RFC-2313]_ (PKCS#5 1.5, section 8.1 (for block type 02)).
+In this case, padding exists of pseudorandomly generated octets to match 16
+byte blocks.  Specifically, see the CBC code [CryptixAESEngine]_ and the
+Cryptix AES implementation [CryptixRijndael_Algorithm]_, as well as the
+padding, found in the ElGamalAESEngine.getPadding function [ElGamalAESEngine]_.
+
+.. Believe it or not, we don't do this any more. If we ever did. safeEncode() and safeDecode() are unused.
+
+.. In all cases, we know the size of the data to be sent, and we AES encrypt the following:
+
+.. .. raw:: html
+
+..   % highlight lang='dataspec' %}
+.. +----+----+----+----+----+----+----+----+
+  |                H(data)                |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |        size       |    data ...       |
+  +----+----+----+----+                   +
+  |                                       |
+  ~                                       ~
+  |                                       |
+  +                                       +
+  |                                       |
+  +                        +----//---+----+
+  |                        |              |
+  +----+----+----//---+----+              +
+  |          Padding to 16 bytes          |
+  +----+----+----+----+----+----+----+----+
+
+..  H(data) :: 32-byte SHA-256 `Hash` of the data
+
+.. . size :: 4-byte `Integer`, number of data bytes to follow
+
+.. . data :: payload
+
+.. . padding :: random data, to a multiple of 16 bytes
+.. % endhighlight %}
+
+.. After the data comes an application-specified number of randomly generated
+ padding bytes.  This application-specified number is rounded up to a multiple
+ of 16.  The entire segment (from H(data) through the end of the random bytes)
+ is AES encrypted (256 bit CBC w/ PKCS#5). 
+
+.. This code is implemented in the safeEncrypt and safeDecrypt methods of
+ AESEngine but it is unused.
+
+
+Obsolescence
+````````````
+The vulnerability of the network to an AES attack and the impact of
+transitioning to a longer bit length is to be studied.  It may be quite
+difficult to make any change backward-compatible.
+
+References
+``````````
+* [STATUS-AES]_
+
+
+.. _sig:
+
+Signatures
+==========
+
+DSA is the default signature algorithm, but we are in the process of migrating
+to more secure algorithms. See below.
+
+DSA
+---
+
+Signatures are generated and verified with 1024 bit [DSA]_ (L=1024, N=160), as
+implemented in [DSAEngine]_.  DSA was chosen because it is much faster for
+signatures than ElGamal.
+
+SEED
+````
+160 bit::
+
+    86108236b8526e296e923a4015b4282845b572cc
+
+Counter
+```````
+::
+
+    33
+
+DSA prime (p)
+`````````````
+1024 bit::
+
+    9C05B2AA 960D9B97 B8931963 C9CC9E8C 3026E9B8 ED92FAD0
+    A69CC886 D5BF8015 FCADAE31 A0AD18FA B3F01B00 A358DE23
+    7655C496 4AFAA2B3 37E96AD3 16B9FB1C C564B5AE C5B69A9F
+    F6C3E454 8707FEF8 503D91DD 8602E867 E6D35D22 35C1869C
+    E2479C3B 9D5401DE 04E0727F B33D6511 285D4CF2 9538D9E3
+    B6051F5B 22CC1C93
+
+DSA quotient (q)
+````````````````
+::
+
+    A5DFC28F EF4CA1E2 86744CD8 EED9D29D 684046B7
+
+DSA generator (g)
+`````````````````
+1024 bit::
+
+    0C1F4D27 D40093B4 29E962D7 223824E0 BBC47E7C 832A3923
+    6FC683AF 84889581 075FF908 2ED32353 D4374D73 01CDA1D2
+    3C431F46 98599DDA 02451824 FF369752 593647CC 3DDC197D
+    E985E43D 136CDCFC 6BD5409C D2F45082 1142A5E6 F8EB1C3A
+    B5D0484B 8129FCF1 7BCE4F7F 33321C3C B3DBB14A 905E7B2B
+    3E93BE47 08CBCC82
+
+The [SigningPublicKey]_ is 1024 bits.  The [SigningPrivateKey]_ is 160 bits.
+
+Obsolescence
+````````````
+[NIST-800-57]_ recommends a minimum of (L=2048, N=224) for usage beyond 2010.
+This may be mitigated somewhat by the "cryptoperiod", or lifespan of a given
+key.
+
+The prime number was chosen in 2003 [CHOOSING-CONSTANTS]_, and the person that
+chose the number (TheCrypto) is currently no longer an I2P developer.  As such,
+we do not know if the prime chosen is a 'strong prime'.  If a larger prime is
+chosen for future purposes, this should be a strong prime, and we will document
+the construction process.
+
+References
+``````````
+* [MEETING-51]_
+* [MEETING-52]_
+
+
+New Signature Algorithms
+========================
+
+As of release 0.9.12, the router supports additional signature algorithms that
+are more secure than 1024-bit DSA.  The first usage is for Destinations;
+support for Router Identities was added in release 0.9.16.  Support for
+migrating existing Destinations from old to new signatures will be added in a
+future release.  Signature type is encoded in the Destination and Router
+Identity, so that new signature algorithms or curves may be added at any time.
+The current supported signature types are as follows:
+
+* DSA-SHA1
+* ECDSA-SHA256-P256
+* ECDSA-SHA384-P384
+* ECDSA-SHA512-P521
+* RSA-SHA256-2048
+* RSA-SHA384-3072
+* RSA-SHA512-4096
+* EdDSA-SHA512-Ed25519 (as of release 0.9.15)
+
+ECDSA
+-----
+
+ECDSA uses the standard NIST curves and standard SHA-2 hashes.
+
+We will migrate new destinations to ECDSA-SHA256-P256 in the 0.9.16 - 0.9.19
+release time frame.  Usage for Router Identities is supported as of release
+0.9.16 and migration may occur in early 2015.
+
+RSA
+---
+
+Standard RSA PKCS#1 v1.5 (RFC 2313) with the public exponent F4 = 65537.
+
+RSA is now used for signing all out-of-band trusted content, including router
+updates, reseeding, plugins, and news.  The signatures are embedded in the
+"su3" format [UPDATES]_.  4096-bit keys are recommended and used by all known
+signers.  RSA is not used, or planned for use, in any in-network Destinations
+or Router Identities.
+
+EdDSA 25519
+-----------
+
+Standard EdDSA using curve 25519 and standard 512-bit SHA-2 hashes.
+
+Supported as of release 0.9.15.
+
+Migration for Destinations and Router Identities is scheduled for mid-2015.
+
+
+Hashes
+======
+
+SHA256
+------
+
+Hashes within I2P are plain old SHA256, as implemented in [SHA256Generator]_.
+
+Obsolescence
+````````````
+The vulnerability of the network to a SHA-256 attack and the impact of
+transitioning to a longer hash is to be studied.  It may be quite difficult to
+make any change backward-compatible.
+
+References
+``````````
+* [SHA-2]_
+
+
+Transports
+==========
+
+At the lowest protocol layer, point-to-point inter-router communication is
+protected by the transport layer security.  Both transports use 256 byte (2048
+bit) Diffie-Hellman key exchange using the same shared prime and generator as
+specified above for ElGamal_, followed by symmetric AES encryption as described
+above.  This provides perfect forward secrecy [PFS]_ on the transport links.
+
+.. _tcp:
+
+NTCP connections
+----------------
+
+NTCP connections are negotiated with a 2048 Diffie-Hellman implementation,
+using the router's identity to proceed with a station to station agreement,
+followed by some encrypted protocol specific fields, with all subsequent data
+encrypted with AES (as above).  The primary reason to do the DH negotiation
+instead of using ElGamalAES+SessionTag [ELG-AES]_ is that it provides
+'(perfect) forward secrecy' [PFS]_, while ElGamalAES+SessionTag does not.
+
+In order to migrate to a more standardized implementation (TLS/SSL or even
+SSH), the following issues must be addressed:
+
+1. Can we somehow reestablish sessions securely (ala session tags) or do we
+   need to do full negotiation each time?
+
+2. Can we simplify/avoid the x509 or other certificate formats and use our own
+   RouterInfo structure (which contains the ElGamal and DSA keys)?
+
+See the NTCP specification [NTCP]_ for details.
+
+.. _udp:
+
+UDP connections
+---------------
+
+SSU (the UDP transport) encrypts each packet with AES256/CBC with both an
+explicit IV and MAC (HMAC-MD5-128) after agreeing upon an ephemeral session key
+through a 2048 bit Diffie-Hellman exchange, station-to-station authentication
+with the other router's DSA key, plus each network message has their own hash
+for local integrity checking.
+
+See the SSU specification [SSU-KEYS]_ for details.
+
+WARNING - I2P's HMAC-MD5-128 used in SSU is apparently non-standard.
+Apparently, an early version of SSU used HMAC-SHA256, and then it was switched
+to MD5-128 for performance reasons, but left the 32-byte buffer size intact.
+See HMACGenerator.java and the 2005-07-05 status notes [STATUS-HMAC]_ for
+details.
+
+
+References
+==========
+
+.. [BENCHMARKS]
+    {{ site_url('misc/benchmarks', True) }}
+
+    Crypto++ benchmarks, originally at http://www.eskimo.com/~weidai/benchmarks.html (now dead),
+    rescued from http://www.archive.org/, dated Apr 23, 2008.
+
+.. [CHOOSING-CONSTANTS]
+    http://article.gmane.org/gmane.comp.security.invisiblenet.iip.devel/343
+
+.. [CryptixAESEngine]
+    https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/CryptixAESEngine.java
+
+.. [CryptixRijndael_Algorithm]
+    https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/CryptixRijndael_Algorithm.java
+
+.. [DSA]
+    http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
+
+.. [DSAEngine]
+    https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/DSAEngine.java
+
+.. [ELG-AES]
+    {{ site_url('docs/how/elgamal-aes') }}
+
+.. [ElGamalEngine]
+    https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/ElGamalEngine.java
+
+.. [ElGamalAESEngine]
+    https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/ElGamalAESEngine.java
+
+.. [Koshiba2004]
+    Koshiba & Kurosawa. Short Exponent Diffie-Hellman Problems. PKC 2004, LNCS 2947, pp. 173-186
+
+    http://www.springerlink.com/content/2jry7cftp5bpdghm/
+
+    Full text: http://books.google.com/books?id=cXyiNZ2_Pa0C&amp;lpg=PA173&amp;ots=PNIz3dWe4g&amp;pg=PA173#v=onepage&amp;q&amp;f=false
+
+.. [LeaseSet]
+    {{ ctags_url('LeaseSet') }}
+
+.. [MEETING-51]
+    {{ get_url('meetings_show', id=51) }}
+
+.. [MEETING-52]
+    {{ get_url('meetings_show', id=52) }}
+
+.. [NETDB-DELIVERY]
+    {{ site_url('docs/how/network-database', True) }}#delivery
+
+.. [NIST-800-57]
+    http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57-Part1-revised2_Mar08-2007.pdf
+
+.. [NTCP]
+    {{ site_url('docs/transport/ntcp', True) }}
+
+.. [PFS]
+    http://en.wikipedia.org/wiki/Perfect_forward_secrecy
+
+.. [PrivateKey]
+    {{ ctags_url('PrivateKey') }}
+
+.. [RFC-2313]
+    http://tools.ietf.org/html/rfc2313
+
+.. [RFC-3526]
+    http://tools.ietf.org/html/rfc3526
+
+.. [RFC-3526-S3]
+    http://tools.ietf.org/html/rfc3526#section-3
+
+.. [SCI.CRYPT]
+    http://groups.google.com/group/sci.crypt/browse_thread/thread/1855a5efa7416677/339fa2f945cc9ba0#339fa2f945cc9ba0
+
+.. [SHA-2]
+    https://en.wikipedia.org/wiki/SHA-2
+
+.. [SHA256Generator]
+    https://github.com/i2p/i2p.i2p/tree/master/core/java/src/net/i2p/crypto/SHA256Generator.java
+
+.. [SigningPrivateKey]
+    {{ ctags_url('SigningPrivateKey') }}
+
+.. [SigningPublicKey]
+    {{ ctags_url('SigningPublicKey') }}
+
+.. [SSU-KEYS]
+    {{ site_url('docs/transport/ssu', True) }}#keys
+
+.. [STATUS-AES]
+    Feb. 7, 2006 Status Notes
+
+    {{ get_url('blog_post', slug='2006/02/07/status') }}
+
+.. [STATUS-HMAC]
+    Jul. 5, 2005 Status Notes
+
+    {{ get_url('blog_post', slug='2005/07/05/status') }}
+
+.. [TunnelBuild]
+    {{ ctags_url('TunnelBuild') }}
+
+.. [TUNNEL-TESTING]
+    {{ site_url('docs/how/tunnel-routing', True) }}#testing
+
+.. [UPDATES]
+    {{ spec_url('updates') }}
+
+.. [vanOorschot1996]
+    van Oorschot, Weiner. On Diffie-Hellman Key Agreement with Short Exponents. EuroCrypt '96
+
+    http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.5952&rep=rep1&type=pdf
diff --git a/i2p2www/spec/datagrams.rst b/i2p2www/spec/datagrams.rst
new file mode 100644
index 0000000000000000000000000000000000000000..6cd2045d4369200c43f8946d641cb3fd09b6ad87
--- /dev/null
+++ b/i2p2www/spec/datagrams.rst
@@ -0,0 +1,130 @@
+======================
+Datagram Specification
+======================
+.. meta::
+    :lastupdated: July 2014
+    :accuratefor: 0.9.14
+
+.. contents::
+
+
+Overview
+========
+
+See [DATAGRAMS]_ for an overview of the Datagrams API.
+
+
+.. _raw:
+
+Non-Repliable Datagrams
+=======================
+
+Non-repliable datagrams have no 'from' address and are not authenticated.  They
+are also called "raw" datagrams.  Strictly speaking, they are not "datagrams"
+at all, they are just raw data.  They are not handled by the datagram API.
+However, SAM and the I2PTunnel classes support "raw datagrams".
+
+Format
+------
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----//
+  | payload...
+  +----+----+----+----+----//
+
+  length: 0 - unlimited (see notes)
+{% endhighlight %}
+
+Notes
+-----
+
+The practical length is limited by lower layers of protocols - the tunnel
+message spec [TUNMSG]_ limits messages to about 61.2 KB and the transports
+[TRANSPORT]_ currently limit messages to about 32 KB, although this may be
+raised in the future.
+
+
+.. _repliable:
+
+Repliable Datagrams
+===================
+
+Repliable datagrams contain a 'from' address and a signature. These add at
+least 427 bytes of overhead.
+
+Format
+------
+
+.. raw:: html
+
+  {% highlight lang='dataspec' -%}
++----+----+----+----+----+----+----+----+
+  | from                                  |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +                                       +
+  |                                       |
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | signature                             |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | payload...
+  +----+----+----+----//
+
+
+  from :: a `Destination`
+          length: 387+ bytes
+          The originator and signer of the datagram
+
+  signature :: a `Signature`
+               Signature type must match the signing public key type of $from
+               length: 40+ bytes, as implied by the Signature type.
+               For the default DSA_SHA1 key type:
+                  The DSA `Signature` of the SHA-256 hash of the payload.
+               For other key types:
+                  The `Signature` of the payload.
+               The signature may be verified by the signing public key of $from
+
+  payload ::  The data
+              Length: 0 to ~31.5 KB (see notes)
+
+  Total length: Payload length + 427+
+{% endhighlight %}
+
+Notes
+-----
+
+* The practical length is limited by lower layers of protocols - the transports
+  [TRANSPORT]_ currently limit messages to about 32 KB, so the data length here
+  is limited to about 31.5 KB.
+
+* See important notes about the reliability of large datagrams [DATAGRAMS]_. For
+  best results, limit the payload to about 10 KB or less.
+
+* Signatures for types other than DSA_SHA1 were redefined in release 0.9.14.
+
+
+References
+==========
+
+.. [DATAGRAMS]
+    {{ site_url('docs/api/datagrams', True) }}
+
+.. [TRANSPORT]
+    {{ site_url('docs/transport', True) }}
+
+.. [TUNMSG]
+    {{ spec_url('tunnel-message') }}#notes
diff --git a/i2p2www/spec/geoip.rst b/i2p2www/spec/geoip.rst
new file mode 100644
index 0000000000000000000000000000000000000000..0f84e680cae3403c187322126ddbaaca88d44f87
--- /dev/null
+++ b/i2p2www/spec/geoip.rst
@@ -0,0 +1,86 @@
+========================
+GeoIP File Specification
+========================
+.. meta::
+    :lastupdated: December 2013
+    :accuratefor: 0.9.9
+
+.. contents::
+
+
+Overview
+========
+
+This page specifies the format of the various GeoIP files,
+used by the router to look up a country for an IP.
+
+
+Country Name (countries.txt) Format
+===================================
+
+This format is easily generated from data files available from many public sources.
+For example:
+
+.. raw:: html
+
+  {% highlight lang='bash' %}$ wget http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip
+  $ unzip GeoIPCountryCSV.zip
+  $ cut -d, -f5,6 < GeoIPCountryWhois.csv | sed 's/"//g' | sort | uniq > countries.txt
+{% endhighlight %}
+
+* Encoding is UTF-8
+* '#' in column 1 specifies a comment line
+* Entry lines are CountryCode,CountryName
+* CountryCode is the ISO two-letter code, upper case
+* CountryName is in English
+
+
+IPv4 (geoip.txt) Format
+=======================
+
+This format is borrowed from Tor and is easily generated from data files available from many public sources.
+For example:
+
+.. raw:: html
+
+  {% highlight lang='bash' %}$ wget http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip
+  $ unzip GeoIPCountryCSV.zip
+  $ cut -d, -f3-5 < GeoIPCountryWhois.csv | sed 's/"//g' > geoip.txt
+  $ cut -d, -f5,6 < GeoIPCountryWhois.csv | sed 's/"//g' | sort | uniq > countries.txt
+{% endhighlight %}
+
+* Encoding is ASCII
+* '#' in column 1 specifies a comment line
+* Entry lines are FromIP,ToIP,CountryCode
+* FromIP and ToIP are unsigned integer representations of the 4-byte IP
+* CountryCode is the ISO two-letter code, upper case
+* Entry lines must be sorted by numeric FromIP
+
+
+IPv6 (geoipv6.dat.gz) Format
+============================
+
+This is a compressed binary format designed for I2P.
+The file is gzipped. Ungzipped format:
+
+.. raw:: html
+
+  {% highlight %}  Bytes 0-9: Magic number "I2PGeoIPv6"
+    Bytes 10-11: Version (0x0001)
+    Bytes 12-15 Options (0x00000000) (future use)
+    Bytes 16-23: Creation date (ms since 1970-01-01)
+    Bytes 24-xx: Optional comment (UTF-8) terminated by zero byte
+    Bytes xx-255: null padding
+    Bytes 256-: 18 byte records:
+        8 byte from (/64)
+        8 byte to (/64)
+        2 byte ISO country code LOWER case (ASCII)
+{% endhighlight %}
+
+NOTES:
+
+* Data must be sorted (SIGNED long twos complement), no overlap.
+  So the order is 80000000 ... FFFFFFFF 00000000 ... 7FFFFFFF.
+* The GeoIPv6.java class contains a program to generate this format from
+  public sources such as the Maxmind GeoLite data.
+* IPv6 GeoIP lookup is supported as of release 0.9.8.
diff --git a/i2p2www/spec/i2cp.rst b/i2p2www/spec/i2cp.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d01eea659294f6664c39fa28ae691268509d560c
--- /dev/null
+++ b/i2p2www/spec/i2cp.rst
@@ -0,0 +1,1385 @@
+==================
+I2CP Specification
+==================
+.. meta::
+    :lastupdated: June 2015
+    :accuratefor: 0.9.21
+
+.. contents::
+
+
+Overview
+========
+
+This page specified the I2P Control Protocol (I2CP), which is the interface
+between clients and the router.  Java clients will use the I2CP client API,
+which implements this protocol.  Non-Java clients will most likely use a
+higher-layer protocol such as SAM or BOB.
+
+The protocol is only serialized if the client and router are not in the same
+JVM; otherwise, I2CP message objects are passed via an internal JVM interface.
+
+More information is on the I2CP Overview page [I2CP]_.
+
+
+Sessions
+========
+
+The protocol was designed to handle multiple "sessions", each with a 2-byte
+session ID, over a single TCP connection, however, Multiple sessions were not
+implemented until version 0.9.21.  See the `multisession section below`_.  Do
+not attempt to use multiple sessions on a single I2CP connection with routers
+older than version 0.9.21.
+
+.. _multisession section below: _multisession
+
+It also appears that there are some provisions for a single client to talk to
+multiple routers over separate connections. This is also untested, and probably
+not useful.
+
+It does not appear that there is currently a way for a session to be maintained
+after a disconnect, or to be recovered on a different I2CP connection.
+
+
+Example Message Sequences
+=========================
+
+Note: The examples below do not show the Protocol Byte (0x2a) that must be sent
+from the client to the router when first connecting.  More information about
+connection initialization is on the I2CP Overview page [I2CP]_.
+
+Standard Session Establish
+--------------------------
+
+.. raw:: html
+
+  {% highlight %}
+    Client                                           Router
+
+                             --------------------->  Get Date Message
+          Set Date Message  <---------------------
+                             --------------------->  Create Session Message
+    Session Status Message  <---------------------
+  Request LeaseSet Message  <---------------------
+                             --------------------->  Create LeaseSet Message
+{% endhighlight %}
+
+Get Bandwidth Limits (Simple Session)
+-------------------------------------
+
+.. raw:: html
+
+  {% highlight %}
+    Client                                           Router
+
+                             --------------------->  Get Bandwidth Limits Message
+  Bandwidth Limits Message  <---------------------
+{% endhighlight %}
+
+Destination Lookup (Simple Session)
+-----------------------------------
+
+.. raw:: html
+
+  {% highlight %}
+    Client                                           Router
+
+                             --------------------->  Dest Lookup Message
+        Dest Reply Message  <---------------------
+{% endhighlight %}
+
+Outgoing Message
+----------------
+
+Existing session, with i2cp.messageReliability=none
+
+.. raw:: html
+
+  {% highlight %}
+    Client                                           Router
+
+                             --------------------->  Send Message Message
+{% endhighlight %}
+
+Existing session, with i2cp.messageReliability=none and nonzero nonce
+
+.. raw:: html
+
+  {% highlight %}
+    Client                                           Router
+
+                             --------------------->  Send Message Message
+    Message Status Message  <---------------------
+    (succeeded)
+{% endhighlight %}
+
+Existing session, with i2cp.messageReliability=BestEffort
+
+.. raw:: html
+
+  {% highlight %}
+    Client                                           Router
+
+                             --------------------->  Send Message Message
+    Message Status Message  <---------------------
+    (accepted)
+    Message Status Message  <---------------------
+    (succeeded)
+{% endhighlight %}
+
+Incoming Message
+----------------
+
+Existing session, with i2cp.fastReceive=true (as of 0.9.4)
+
+.. raw:: html
+
+  {% highlight %}
+    Client                                           Router
+
+   Message Payload Message  <---------------------
+{% endhighlight %}
+
+Existing session, with i2cp.fastReceive=false
+
+.. raw:: html
+
+  {% highlight %}
+    Client                                           Router
+
+    Message Status Message  <---------------------
+    (available)
+                             --------------------->  Receive Message Begin Message
+   Message Payload Message  <---------------------
+                             --------------------->  Receive Message End Message
+{% endhighlight %}
+
+
+.. _multisession:
+
+Multisession Notes
+------------------
+
+Multiple sessions on a single I2CP connection are supported as of router
+version 0.9.21.  The first session that is created is the "primary session".
+Additional sessions are "subsessions".  Subsessions are used to support
+multiple destinations sharing a common set of tunnels.  The initial application
+is for the primary session to use ECDSA signing keys, while the subsession uses
+DSA signing keys for communication with old eepsites.
+
+Subsessions share the same inbound and outbound tunnel pools as the primary
+session.  Subsessions must use the same encryption keys as the primary session.
+This applies both to the LeaseSet encryption keys and the (unused) Destination
+encryption keys.  Subsessions must use different signing keys in the
+destination, so the destination hash is different from the primary session.  As
+subsessions use the same encryption keys and tunnels as the primary session, it
+is apparent to all that the Destinations are running on the same router, so the
+usual anti-correlation anonymity guarantees do not apply.
+
+Subsessions are created by sending a CreateSession message and receiving a
+SessionStatus message in reply, as usual. Subsessions must be created after the
+primary session is created.  The SessionStatus response will, on success,
+contain a unique Session ID, distinct from the ID for the primary session.
+While CreateSession messages should be processed in-order, there is no sure way
+to correlate a CreateSession message with the response, so a client should not
+have multiple CreateSession messages outstanding simultaneously.  SessionConfig
+options for the subsession may not be honored where they are different from the
+primary session.  In particular, since subsessions use the same tunnel pool as
+the primary session, tunnel options may be ignored.
+
+The router will send separate RequestVariableLeaseSet messages for each
+Destination to the client, and the client must reply with a CreateLeaseSet
+message for each.  The leases for the two Destinations will not necessarily be
+identical, even though they are selected from the same tunnel pool.
+
+A subsession may be destroyed with the DestroySession message as usual.  This
+will not destroy the primary session or stop the I2CP connection.  Destroying
+the primary session will, however, destroy all subsessions and stop the I2CP
+connection.  A Disconnect message destroys all sessions.
+
+Note that most, but not all, I2CP messages contain a Session ID.  For the ones
+that do not, clients may need additional logic to properly handle router
+responses.  DestLookup and DestReply do not contain Session IDs; use the newer
+HostLookup and HostReply instead.  GetBandwidthLimts and BandwidthLimits do not
+contain session IDs, however the response is not session-specific.
+
+Support for multiple sessions is preliminary and subject to change.  Support
+may not be complete in other parts of the API and user interface, particularly
+streaming and i2ptunnel.  Current support is primarily for clients (i.e.
+Destinations that do not publish their leaseset or accept incoming
+connections), and is incomplete and untested for servers.  Future releases may
+provide additional features and options.
+
+
+.. _notes:
+
+Version Notes
+-------------
+
+The initial protocol version byte (0x2a) sent by the client is not expected to
+change.  Prior to release 0.8.7, the router's version information was not
+available to the client, thus preventing new clients from working with old
+routers.  As of release 0.8.7, the two parties' protocol version strings are
+exchanged in the Get/Set Date Messages.  Going forward, clients may use this
+information to communicate correctly with old routers.  Clients and routers
+should not send messages that are unsupported by the other side, as they
+generally disconnect the session upon reception of an unsupported message.
+
+The exchanged version information is the "core" version or I2CP protocol
+version, and is not necessarily the router version.
+
+A basic summary of the I2CP protocol versions is as follows. For details, see
+below.
+
+==============  ======================
+   Version      Required I2CP Features
+==============  ======================
+   0.9.21       Multiple sessions on a single I2CP connection supported
+
+   0.9.20       Additional SetDate messages may be sent to the client at any
+                time
+
+   0.9.16       Authentication, if enabled, is required via GetDate before all
+                other messages
+
+   0.9.15       Dest/LS key certs w/ EdDSA Ed25519 sig type supported
+
+   0.9.14       Per-message override of messageReliability=none with nonzero
+                nonce
+
+   0.9.12       Dest/LS key certs w/ ECDSA P-256, P-384, and P-521 sig types
+                supported
+
+                Note: RSA sig types also supported as of this version, but
+                currently unused
+
+   0.9.11       Host Lookup and Host Reply messages supported
+
+                Authentication mapping in Get Date message supported
+
+   0.9.7        Request Variable Lease Set message supported
+
+   0.9.5        Additional Message Status codes defined
+
+   0.9.4        Send Message nonce=0 allowed
+
+                Fast receive mode is the default
+
+   0.9.2        Send Message Expires flag tag bits supported
+
+   0.9          Supports up to 16 leases in a lease set (6 previously)
+
+   0.8.7        Get Date and Set Date version strings included.
+
+                If not present, the client or router is version 0.8.6 or older.
+
+   0.8.4        Send Message Expires flag bits supported
+
+   0.8.3        Dest Lookup and Get Bandwidth messages supported in standard
+                session
+
+                Concurrent Dest Lookup messages supported
+
+   0.8.1        i2cp.messageReliability=none supported
+
+   0.7.2        Get Bandwidth Limits and Bandwidth Limits messages supported
+
+   0.7.1        Send Message Expires message supported<br>
+                     Reconfigure Session message supported
+
+   0.7          Dest Lookup and Dest Reply messages supported
+
+0.6.5 or lower  All messages and features not listed above
+==============  ======================
+
+
+.. _structures:
+
+Common structures
+=================
+
+.. _struct-I2CPMessageHeader:
+
+I2CP message header
+-------------------
+
+Description
+```````````
+Common header to all I2CP messages, containing the message length and message
+type.
+
+Contents
+````````
+1. 4 byte [Integer]_ specifying the length of the message body
+2. 1 byte [Integer]_ specifying the message type.
+3. The I2CP message body, 0 or more bytes
+
+Notes
+`````
+Actual message length limit is about 64 KB.
+
+.. _struct-MessageId:
+
+Message ID
+----------
+
+Description
+```````````
+Uniquely identifies a message waiting on a particular router at a point in
+time.  This is always generated by the router and is NOT the same as the nonce
+generated by the client.
+
+Contents
+````````
+1. 4 byte [Integer]_
+
+Notes
+`````
+Message IDs are unique within a session only; they are not globally unique.
+
+.. _struct-Payload:
+
+Payload
+-------
+
+Description
+```````````
+This structure is the content of a message being delivered from one Destination
+to another.
+
+Contents
+````````
+1. 4 byte [Integer]_ length
+2. That many bytes
+
+Notes
+`````
+The payload is in a gzip format as specified on the I2CP Overview page
+[I2CP-FORMAT]_.
+
+.. _struct-SessionConfig:
+
+Session Config
+--------------
+
+Description
+```````````
+Defines the configuration options for a particular client session.
+
+Contents
+````````
+1. [Destination]_
+2. [Mapping]_ of options
+3. Creation [Date]_
+4. [Signature]_ of the previous 3 fields, signed by the [SigningPrivateKey]_
+
+Notes
+`````
+* The options are specified on the
+<a href="{{ site_url('docs/protocol/i2cp') }}#options">I2CP Overview page</a>.
+
+* The [Mapping]_ must be sorted by key so that the signature will be validated
+  correctly in the router.
+
+* The creation date must be within +/- 30 seconds of the current time when
+  processed by the router, or the config will be rejected.
+
+.. _struct-SessionId:
+
+Session ID
+----------
+
+Description
+```````````
+Uniquely identifies a session on a particular router at a point in
+time.
+
+Contents
+````````
+1. 2 byte [Integer]_
+
+Notes
+`````
+
+
+Messages
+========
+
+See also the I2CP Javadocs [I2CP-JAVADOCS]_.
+
+.. _types:
+
+Message Types
+-------------
+
+===============================  =========  ====  =====
+            Message              Direction  Type  Since
+===============================  =========  ====  =====
+BandwidthLimitsMessage_           R -> C     23   0.7.2
+CreateLeaseSetMessage_            C -> R      4
+CreateSessionMessage_             C -> R      1
+DestLookupMessage_                C -> R     34   0.7
+DestReplyMessage_                 R -> C     35   0.7
+DestroySessionMessage_            C -> R      3
+DisconnectMessage_                bidir.     30
+GetBandwidthLimitsMessage_        C -> R      8   0.7.2
+GetDateMessage_                   C -> R     32
+HostLookupMessage_                C -> R     38   0.9.11
+HostReplyMessage_                 R -> C     39   0.9.11
+MessagePayloadMessage_            R -> C     31
+MessageStatusMessage_             R -> C     22
+ReceiveMessageBeginMessage_       C -> R      6
+ReceiveMessageEndMessage_         C -> R      7
+ReconfigureSessionMessage_        C -> R      2   0.7.1
+ReportAbuseMessage_               bidir.     29
+RequestLeaseSetMessage_           R -> C     21
+RequestVariableLeaseSetMessage_   R -> C     37   0.9.7
+SendMessageMessage_               C -> R      5
+SendMessageExpiresMessage_        C -> R     36   0.7.1
+SessionStatusMessage_             R -> C     20
+SetDateMessage_                   R -> C     33
+===============================  =========  ====  =====
+
+.. _msg-BandwidthLimits:
+
+BandwidthLimitsMessage
+----------------------
+
+Description
+```````````
+Tell the client what the bandwidth limits are.
+
+Sent from Router to Client in response to a GetBandwidthLimitsMessage_.
+
+Contents
+````````
+1. 4 byte [Integer]_ Client inbound limit (KBps)
+2. 4 byte [Integer]_ Client outbound limit (KBps)
+3. 4 byte [Integer]_ Router inbound limit (KBps)
+4. 4 byte [Integer]_ Router inbound burst limit (KBps)
+5. 4 byte [Integer]_ Router outbound limit (KBps)
+6. 4 byte [Integer]_ Router outbound burst limit (KBps)
+7. 4 byte [Integer]_ Router burst time (seconds)
+8. Nine 4-byte [Integer]_ (undefined)
+
+Notes
+`````
+Currently, the client limits are the only values set, and are actually the
+router limits. All the values labeled as router limits are always 0.  As of
+release 0.7.2.
+
+.. _msg-CreateLeaseSet:
+
+CreateLeaseSetMessage
+---------------------
+
+Description
+```````````
+This message is sent in response to a RequestLeaseSetMessage_ or
+RequestVariableLeaseSetMessage_ and contains all of the [Lease]_ structures that
+should be published to the I2NP Network Database.
+
+Sent from Client to Router.
+
+Contents
+````````
+1. `Session ID`_
+2. [SigningPrivateKey]_
+3. [PrivateKey]_
+4. [LeaseSet]_
+
+Notes
+`````
+The SigningPrivateKey matches the [SigningPublicKey]_ from within the LeaseSet,
+as does the PrivateKey with the [PublicKey]_. The signing key is necessary to
+allow the router to revoke the LeaseSet if the client goes offline, and the
+encryption key is necessary for decrypting garlic routed messages. The LeaseSet
+granted may include Lease structures for tunnels pointing at another router if
+the client is actively connected to multiple routers with Leases granted to
+each.
+
+**XXX** Really?
+Revocation is unimplemented.
+Connection to multiple routers is untested.
+
+.. _msg-CreateSession:
+
+CreateSessionMessage
+--------------------
+
+Description
+```````````
+This message is sent from a client to initiate a session, where a session is
+defined as a single Destination's connection to the network, to which all
+messages for that Destination will be delivered and from which all messages
+that Destination sends to any other Destination will be sent through.
+
+Sent from Client to Router.  The router responds with a SessionStatusMessage_.
+
+Contents
+````````
+1. `Session Config`_
+
+Notes
+`````
+* This is the second message sent by the client. Previously the client sent a
+  GetDateMessage_ and received a SetDateMessage_ response.
+
+* If the Date in the Session Config is too far (more than +/- 30 seconds) from
+  the router's current time, the session will be rejected.
+
+* If there is already a session on the router for this Destination, the session
+  will be rejected.
+
+* The [Mapping]_ in the Session Config must be sorted by key so that the
+  signature will be validated correctly in the router.
+
+.. _msg-DestLookup:
+
+DestLookupMessage
+-----------------
+
+Description
+```````````
+Sent from Client to Router.  The router responds with a DestReplyMessage_.
+
+Contents
+````````
+1. SHA-256 [Hash]_
+
+Notes
+`````
+As of release 0.7.
+
+As of release 0.8.3, multiple outstanding lookups are supported, and lookups
+are supported in both I2PSimpleSession and in standard sessions.
+
+HostLookupMessage_ is preferred as of release 0.9.11.
+
+.. _msg-DestReply:
+
+DestReplyMessage
+----------------
+
+Description
+```````````
+Sent from Router to Client in response to a DestLookupMessage_.
+
+Contents
+````````
+1. [Destination]_ on success, or [Hash]_ on failure
+
+Notes
+`````
+As of release 0.7.
+
+As of release 0.8.3, the requested Hash is returned if the lookup failed, so
+that the client may have multiple lookups outstanding and correlate the replies
+to the lookups.  To correlate a Destination response with a request, take the
+Hash of the Destination.  Prior to release 0.8.3, the response was empty on
+failure.
+
+.. _msg-DestroySession:
+
+DestroySessionMessage
+---------------------
+
+Description
+```````````
+This message is sent from a client to destroy a session.
+
+Sent from Client to Router. The router responds with a SessionStatusMessage_.
+
+Contents
+````````
+1. `Session ID`_
+
+Notes
+`````
+The router at this point should release all resources related to the session.
+
+.. _msg-Disconnect:
+
+DisconnectMessage
+-----------------
+
+Description
+```````````
+Tell the other party that there are problems and the current connection is about to
+be destroyed. This does not necessarily end a session.
+Sent either from router to client or from client to router.
+
+Contents
+````````
+1. Reason [String]_
+
+Notes
+`````
+Only implemented in the router-to-client direction.  Disconnecting probably
+does end a session, in practice.
+
+.. _msg-GetBandwidthLimits:
+
+GetBandwidthLimitsMessage
+-------------------------
+
+Description
+```````````
+Request that the router state what its current bandwidth limits are.
+
+Sent from Client to Router.  The router responds with a
+BandwidthLimitsMessage_.
+
+Contents
+````````
+*None*
+
+Notes
+`````
+As of release 0.7.2.
+
+As of release 0.8.3, supported in both I2PSimpleSession and in standard
+sessions.
+
+.. _msg-GetDate:
+
+GetDateMessage
+--------------
+
+Description
+```````````
+Sent from Client to Router.  The router responds with a SetDateMessage_.
+
+Contents
+````````
+1. I2CP Version [String]_
+2. Authentication [Mapping]_ (optional, as of release 0.9.11)
+
+Notes
+`````
+* Generally the first message sent by the client after sending the protocol
+  version byte.
+
+* The version string is included as of release 0.8.7. This is only useful if
+  the client and router are not in the same JVM. If it is not present, the
+  client is version 0.8.6 or earlier.
+
+* As of release 0.9.11, the authentication [Mapping]_ may be included, with the
+  keys i2cp.username and i2cp.password. The Mapping need not be sorted as this
+  message is not signed. Prior to and including 0.9.10, authentication is
+  included in the `Session Config`_ Mapping, and no authentication is enforced
+  for GetDateMessage_, GetBandwidthLimitsMessage_, or DestLookupMessage_. When
+  enabled, authentication via GetDateMessage_ is required before any other
+  messages as of release 0.9.16. This is only useful outside router context.
+  This is an incompatible change, but will only affect sessions outside router
+  context with authentication, which should be rare.
+
+.. _msg-HostLookup:
+
+HostLookupMessage
+-----------------
+
+Description
+```````````
+Sent from Client to Router.  The router responds with a HostReplyMessage_.
+
+This replaces the DestLookupMessage_ and adds a request ID, a timeout, and host
+name lookup support.  As it also supports Hash lookups, it may be used for all
+lookups if the router supports it.  For host name lookups, the router will
+query its context's naming service.  This is only useful if the client is
+outside the router's context.  Inside router context, the client should query
+the naming service itself, which is much more efficient.
+
+Contents
+````````
+1. `Session ID`_
+2. 4 byte [Integer]_ request ID
+3. 4 byte [Integer]_ timeout (ms)
+4. 1 byte [Integer]_ request type
+5. SHA-256 [Hash]_ or host name [String]_
+
+Notes
+`````
+* As of release 0.9.11. Use DestLookupMessage_ for older routers.
+
+* The session ID and request ID will be returned in the HostReplyMessage_. Use
+  0xFFFF for the session ID if there is no session.
+
+* Timeout is useful for Hash lookups. Recommended minimum 10,000 (10 sec.). In
+  the future it may also be useful for remote naming service lookups. The value
+  may be not be honored for local host name lookups, which should be fast.
+
+* The request type is 0 for Hash and 1 for host name.
+
+* Base 32 host name lookup is supported but it is preferred to convert it to a
+  Hash first.
+
+.. _msg-HostReply:
+
+HostReplyMessage
+----------------
+
+Description
+```````````
+Sent from Router to Client in response to a HostLookupMessage_.
+
+Contents
+````````
+1. `Session ID`_
+2. 4 byte [Integer]_ request ID
+3. 1 byte [Integer]_ result code
+4. [Destination]_, only present if result code is zero.
+
+Notes
+`````
+* As of release 0.9.11. See HostLookupMessage_ notes.
+
+* The session ID and request ID are those from the HostLookupMessage_.
+
+* The result code is 0 for success, 1-255 for failure. Only 1 is used for
+  failure now, more specific failure codes may be defined in the future.
+
+.. _msg-MessagePayload:
+
+MessagePayloadMessage
+---------------------
+
+Description
+```````````
+Deliver the payload of a message to the client.
+
+Sent from Router to Client.  The client responds with a
+ReceiveMessageEndMessage_.
+
+Contents
+````````
+1. `Session ID`_
+2. `Message ID`_
+3. Payload_
+
+Notes
+`````
+
+.. _msg-MessageStatus:
+
+MessageStatusMessage
+--------------------
+
+Description
+```````````
+Notify the client of the delivery status of an incoming or outgoing message.
+Sent from Router to Client.  If this message indicates that an incoming message
+is available, the client responds with a ReceiveMessageBeginMessage_.  For an
+outgoing message, this is a response to a SendMessageMessage_ or
+SendMessageExpiresMessage_.
+
+Contents
+````````
+1. `Session ID`_
+2. `Message ID`_ generated by the router
+3. 1 byte [Integer]_ status
+4. 4 byte [Integer]_ size
+5. 4 byte [Integer]_ nonce previously generated by the client
+
+Notes
+`````
+Through version 0.9.4, the known status values are 0 for message is available,
+1 for accepted, 2 for best effort succeeded, 3 for best effort failed, 4 for
+guaranteed succeeded, 5 for guaranteed failed. The size Integer specifies the
+size of the available message and is only relevant for status = 0.  Even though
+guaranteed is unimplemented, (best effort is the only service), the current
+router implementation uses the guaranteed status codes, not the best effort
+codes.
+
+As of router version 0.9.5, additional status codes are defined, however they
+are not necessarily implemented.  See [MSM-JAVADOCS]_ for details.  All status
+codes:
+
+===========  =============  ======================  ==========================================================
+Status Code  As Of Release           Name           Description
+===========  =============  ======================  ==========================================================
+     0                      Available               For incoming messages only. All other status codes below
+                                                    are for outgoing messages.
+
+                                                    The included size is the size in bytes of the available
+                                                    message.
+
+                                                    This is unused in "fast receive" mode, which is the
+                                                    default as of release 0.9.4.
+
+     1                      Accepted                Outgoing message accepted by the local router for
+                                                    delivery. The included nonce matches the nonce in the
+                                                    SendMessageMessage_, and the included Message ID will be
+                                                    used for subsequent success or failure notification.
+
+     2                      Best Effort Success     Probable success (unused)
+
+     3                      Best Effort Failure     Probable failure
+
+     4                      Guaranteed Success      Probable success
+
+     5                      Guaranteed Failure      Generic failure, specific cause unknown.
+                                                    May not really be a guaranteed failure.
+
+     6           0.9.5      Local Success           Local delivery successful.
+                                                    The destination was another client on the same router.
+
+     7           0.9.5      Local Failure           Local delivery failure.
+                                                    The destination was another client on the same router.
+
+     8           0.9.5      Router Failure          The local router is not ready, has shut down, or has
+                                                    major problems.
+
+                                                    This is a guaranteed failure.
+
+     9           0.9.5      Network Failure         The local computer apparently has no network connectivity
+                                                    at all.
+
+                                                    This is a guaranteed failure.
+
+    10           0.9.5      Bad Session             The I2CP session is invalid or closed.
+
+                                                    This is a guaranteed failure.
+
+    11           0.9.5      Bad Message             The message payload is invalid or zero-length or too big.
+
+                                                    This is a guaranteed failure.
+
+    12           0.9.5      Bad Options             Something is invalid in the message options, or the
+                                                    expiration is in the past or too far in the future.
+
+                                                    This is a guaranteed failure.
+
+    13           0.9.5      Overflow Failure        Some queue or buffer in the router is full and the message
+                                                    was dropped.
+
+                                                    This is a guaranteed failure.
+
+    14           0.9.5      Message Expired         The message expired before it could be sent.
+
+                                                    This is a guaranteed failure.
+
+    15           0.9.5      Bad Local Leaseset      The client has not yet signed a [LeaseSet]_, or the local
+                                                    keys are invalid, or it has expired, or it does not have
+                                                    any tunnels in it.
+
+                                                    This is a guaranteed failure.
+
+    16           0.9.5      No Local Tunnels        Local problems. No outbound tunnel to send through, or no
+                                                    inbound tunnel if a reply is required.
+
+                                                    This is a guaranteed failure.
+
+    17           0.9.5      Unsupported Encryption  The certs or options in the [Destination]_ or its
+                                                    [LeaseSet]_ indicate that it uses an encryption format
+                                                    that we don't support, so we can't talk to it.
+
+                                                    This is a guaranteed failure.
+
+    18           0.9.5      Bad Destination         Something is wrong with the far-end [Destination]_. Bad
+                                                    format, unsupported options, certificates, etc.
+
+                                                    This is a guaranteed failure.
+
+    19           0.9.5      Bad Leaseset            We got the far-end [LeaseSet]_ but something strange is
+                                                    wrong with it. Unsupported options or certificates, no
+                                                    tunnels, etc.
+
+                                                    This is a guaranteed failure.
+
+    20           0.9.5      Expired Leaseset        We got the far-end [LeaseSet]_ but it's expired and we
+                                                    can't get a new one.
+
+                                                    This is a guaranteed failure.
+
+    21           0.9.5      No Leaseset             Could not find the far-end [LeaseSet]_. This is a common
+                                                    failure, equivalent to a DNS lookup failure.
+
+                                                    This is a guaranteed failure.
+===========  =============  ======================  ==========================================================
+
+When status = 1 (accepted), the nonce matches the nonce in the
+SendMessageMessage_, and the included Message ID will be used for subsequent
+success or failure notification.  Otherwise, the nonce may be ignored.
+
+.. _msg-ReceiveMessageBegin:
+
+ReceiveMessageBeginMessage
+--------------------------
+
+Description
+```````````
+Request the router to deliver a message that it was previously notified of.
+Sent from Client to Router.  The router responds with a MessagePayloadMessage_.
+
+Contents
+````````
+1. `Session ID`_
+2. `Message ID`
+
+Notes
+`````
+The ReceiveMessageBeginMessage_ is sent as a response to a
+MessageStatusMessage_ stating that a new message is available for pickup. If
+the message id specified in the ReceiveMessageBeginMessage_ is invalid or
+incorrect, the router may simply not reply, or it may send back a
+DisconnectMessage_.
+
+This is unused in "fast receive" mode, which is the default as of release
+0.9.4.
+
+.. _msg-ReceiveMessageEnd:
+
+ReceiveMessageEndMessage
+------------------------
+
+Description
+```````````
+Tell the router that delivery of a message was completed successfully and that
+the router can discard the message.
+
+Sent from Client to Router.
+
+Contents
+````````
+1. `Session ID`_
+2. `Message ID`
+
+Notes
+`````
+The ReceiveMessageEndMessage_ is sent after a MessagePayloadMessage_ fully
+delivers a message's payload.
+
+This is unused in "fast receive" mode, which is the default as of release
+0.9.4.
+
+.. _msg-ReconfigureSession:
+
+ReconfigureSessionMessage
+-------------------------
+
+Description
+```````````
+
+Sent from Client to Router to update the session configuration.  The router
+responds with a SessionStatusMessage_.
+
+Contents
+````````
+1. `Session ID`_
+2. `Session Config`_
+
+Notes
+`````
+* As of release 0.7.1.
+
+* If the Date in the Session Config is too far (more than +/- 30 seconds) from
+  the router's current time, the session will be rejected.
+
+* The [Mapping]_ in the Session Config must be sorted by key so that the
+  signature will be validated correctly in the router.
+
+* Some configuration options may only be set in the CreateSessionMessage_, and
+  changes here will not be recognized by the router. Changes to tunnel options
+  inbound.* and outbound.* are always recognized.
+
+.. _msg-ReportAbuse:
+
+ReportAbuseMessage
+------------------
+
+Description
+```````````
+Tell the other party (client or router) that they are under attack, potentially
+with reference to a particular MessageId. If the router is under attack, the
+client may decide to migrate to another router, and if a client is under
+attack, the router may rebuild its routers or banlist some of the peers that
+sent it messages delivering the attack.
+
+Sent either from router to client or from client to router.
+
+Contents
+````````
+1. `Session ID`_
+2. 1 byte [Integer]_ abuse severity (0 is minimally abusive, 255 being
+   extremely abusive)
+3. Reason [String]_
+4. `Message ID`_
+
+Notes
+`````
+Unused.  Not fully implemented. Both router and client can generate a
+ReportAbuseMessage_, but neither has a handler for the message when received.
+
+.. _msg-RequestLeaseSet:
+
+RequestLeaseSetMessage
+----------------------
+
+Description
+```````````
+Request that a client authorize the inclusion of a particular set of inbound
+tunnels.  Sent from Router to Client.  The client responds with a
+CreateLeaseSetMessage_.
+
+Contents
+````````
+1. `Session ID`_
+2. 1 byte [Integer]_ number of tunnels
+3. That many pairs of:
+
+   1. [RouterIdentity]_
+   2. [TunnelId]_
+
+4. End [Date]_
+
+Notes
+`````
+This requests a [LeaseSet]_ with all [Leases]_ set to expire at the same time.
+For client versions 0.9.7 or higher, RequestVariableLeaseSetMessage_ is
+preferred.
+
+.. _msg-RequestVariableLeaseSet:
+
+RequestVariableLeaseSetMessage
+------------------------------
+
+Description
+```````````
+Request that a client authorize the inclusion of a particular set of inbound
+tunnels.
+
+Sent from Router to Client.  The client responds with a CreateLeaseSetMessage_.
+
+Contents
+````````
+1. `Session ID`_
+2. 1 byte [Integer]_ number of tunnels
+3. That many [Leases]_
+
+Notes
+`````
+This requests a [LeaseSet]_ with an individual expiration time for each
+[Lease]_.
+
+As of release 0.9.7.  For clients before that release, use
+RequestLeaseSetMessage_.
+
+.. _msg-SendMessage:
+
+SendMessageMessage
+------------------
+
+Description
+```````````
+This is how a client sends a message (the payload) to the [Destination]_.  The
+router will use a default expiration.
+
+Sent from Client to Router.  The router responds with a MessageStatusMessage_.
+
+Contents
+````````
+1. `Session ID`_
+2. [Destination]_
+3. Payload_
+4. 4 byte [Integer]_ nonce
+
+Notes
+`````
+As soon as the SendMessageMessage_ arrives fully intact, the router should
+return a MessageStatusMessage_ stating that it has been accepted for delivery.
+That message will contain the same nonce sent here.  Later on, based on the
+delivery guarantees of the session configuration, the router may additionally
+send back another MessageStatusMessage_ updating the status.
+
+As of release 0.8.1, the router does not send either MessageStatusMessage_ if
+i2cp.messageReliability=none.
+
+Prior to release 0.9.4, a nonce value of 0 was not allowed.  As of release
+0.9.4, a nonce value of 0 is allowed, and tells to the router that it should
+not send either MessageStatusMessage_, i.e. it acts as if
+i2cp.messageReliability=none for this message only.
+
+Prior to release 0.9.14, a session with i2cp.messageReliability=none could not
+be overridden on a per-message basis.  As of release 0.9.14, in a session with
+i2cp.messageReliability=none, the client may request delivery of a
+MessageStatusMessage_ with the delivery success or failure by setting the nonce
+to a nonzero value.  The router will not send the "accepted"
+MessageStatusMessage_ but it will later send the client a MessageStatusMessage_
+with the same nonce, and a success or failure value.
+
+.. _msg-SendMessageExpires:
+
+SendMessageExpiresMessage
+-------------------------
+
+Description
+```````````
+Sent from Client to Router. Same as SendMessageMessage_, except includes an
+expiration and options.
+
+Contents
+````````
+1. `Session ID`_
+2. [Destination]_
+3. Payload_
+4. 4 byte [Integer]_ nonce
+5. 2 bytes of flags (options)
+6. Expiration [Date]_ truncated from 8 bytes to 6 bytes
+
+Notes
+`````
+As of release 0.7.1.
+
+In "best effort" mode, as soon as the SendMessageExpiresMessage arrives fully
+intact, the router should return a MessageStatusMessage stating that it has
+been accepted for delivery.  That message will contain the same nonce sent
+here.  Later on, based on the delivery guarantees of the session configuration,
+the router may additionally send back another MessageStatusMessage updating the
+status.
+
+As of release 0.8.1, the router does not send either Message Status Message if
+i2cp.messageReliability=none.
+
+Prior to release 0.9.4, a nonce value of 0 was not allowed.  As of release
+0.9.4, a nonce value of 0 is allowed, and tells the router that it should not
+send either Message Status Message, i.e. it acts as if
+i2cp.messageReliability=none for this message only.
+
+Prior to release 0.9.14, a session with i2cp.messageReliability=none could not
+be overridden on a per-message basis.  As of release 0.9.14, in a session with
+i2cp.messageReliability=none, the client may request delivery of a Message
+Status Message with the delivery success or failure by setting the nonce to a
+nonzero value.  The router will not send the "accepted" Message Status Message
+but it will later send the client a Message Status Message with the same nonce,
+and a success or failure value.
+
+Flags Field
+```````````
+As of release 0.8.4, the upper two bytes of the Date are redefined to contain
+flags. The flags must default to all zeros for backward compatibility.  The
+Date will not encroach on the flags field until the year 10889.  The flags may
+be used by the application to provide hints to the router as to whether a
+LeaseSet and/or ElGamal/AES Session Tags should be delivered with the message.
+The settings will significantly affect the amount of protocol overhead and the
+reliability of message delivery.  The individual flag bits are defined as
+follows, as of release 0.9.2.  Definitions are subject to change. Use the
+SendMessageOptions class to construct the flags.
+
+Bit order: 15...0
+
+Bits 15-11
+    Unused, must be zero
+
+Bits 10-9
+    Message Reliability Override (Unimplemented, to be removed).
+
+===========  ===========
+Field value  Description
+===========  ===========
+    00       Use session setting i2cp.messageReliability (default)
+
+    01       Use "best effort" message reliability for this message, overriding
+             the session setting. The router will send one or more
+             MessageStatusMessages in response.
+
+             Unused. Use a nonzero nonce value to override a session setting of
+             "none".
+
+    10       Use "guaranteed" message reliability for this message, overriding
+             the session setting. The router will send one or more
+             MessageStatusMessages in response.
+
+             Unused. Use a nonzero nonce value to override a session setting of
+             "none".
+
+    11       Unused. Use a nonce value of 0 to force "none" and override a
+             session setting of "best effort" or "guaranteed".
+===========  ===========
+
+Bit 8
+    If 1, don't bundle a lease set in the garlic with this message.  If 0, the
+    router may bundle a lease set at its discretion.
+
+Bits 7-4
+    Low tag threshold. If there are less than this many tags available, send
+    more.  This is advisory and does not force tags to be delivered.
+
+===========  =============
+Field value  Tag threshold
+===========  =============
+   0000      Use session key manager settings
+   0001             2
+   0010             3
+   0011             6
+   0100             9
+   0101            14
+   0110            20
+   0111            27
+   1000            35
+   1001            45
+   1010            57
+   1011            72
+   1100            92
+   1101           117
+   1110           147
+   1111           192
+===========  =============
+
+Bits 3-0
+    Number of tags to send if required.  This is advisory and does not force
+    tags to be delivered.
+
+===========  ============
+Field value  Tags to send
+===========  ============
+   0000      Use session key manager settings
+   0001            2
+   0010            4
+   0011            6
+   0100            8
+   0101           12
+   0110           16
+   0111           24
+   1000           32
+   1001           40
+   1010           51
+   1011           64
+   1100           80
+   1101          100
+   1110          125
+   1111          160
+===========  ============
+
+.. _msg-SessionStatus:
+
+SessionStatusMessage
+--------------------
+
+Description
+```````````
+Instruct the client as to the status of its session.
+
+Sent from Router to Client, possibly in response to a CreateSessionMessage_ or
+ReconfigureSessionMessage_.
+
+Contents
+````````
+1. `Session ID`_
+2. 1 byte [Integer]_ status
+
+======  ======  =========  =============================================================
+Status  Since     Name     Definition
+======  ======  =========  =============================================================
+   0            Destroyed  The session with the given ID is terminated.
+
+   1            Created    In response to a CreateSessionMessage_, a new session with
+                           the given ID is now active.
+
+   2            Updated    In response to a ReconfigureSessionMessage_, an existing
+                           session with the given ID has been reconfigured.
+
+   3            Invalid    In response to a CreateSessionMessage_, the configuration is
+                           invalid. The included session ID should be ignored.
+
+                           In response to a ReconfigureSessionMessage_, the new
+                           configuration is invalid for the session with the given ID.
+
+   4    0.9.12  Refused    In response to a CreateSessionMessage_, the router was unable
+                           to create the session, perhaps due to limits being exceeded.
+                           The included session ID should be ignored.
+======  ======  =========  =============================================================
+
+Notes
+`````
+Status values include 0 for destroyed, 1 for created, 2 for updated, and 3 for
+invalid session.  If created, the Session ID is the identifier to be used for
+the rest of the session.
+
+.. _msg-SetDate:
+.. _SetDateMessage:
+
+Set Date
+--------
+
+Description
+```````````
+The current date and time.  Sent from Router to Client as a part of the initial
+handshake.  As of release 0.9.20, may also be sent at any time after the
+handshake to notify the client of a clock shift.
+
+Contents
+````````
+1. [Date]_
+2. I2CP Version [String]_
+
+Notes
+`````
+This is generally the first message sent by the router.  The version string is
+included as of release 0.8.7.  This is only useful if the client and router are
+not in the same JVM.  If it is not present, the router is version 0.8.6 or
+earlier.
+
+Additional SetDate messages will not be sent to clients in the same JVM.
+
+
+References
+==========
+
+.. [Date]
+    {{ ctags_url('Date') }}
+
+.. [Destination]
+    {{ ctags_url('Destination') }}
+
+.. [Hash]
+    {{ ctags_url('Hash') }}
+
+.. [I2CP]
+    {{ site_url('docs/protocol/i2cp', True) }}
+
+.. [I2CP-FORMAT]
+    {{ site_url('docs/protocol/i2cp', True) }}#format
+
+.. [I2CP-JAVADOCS]
+    http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/i2cp/package-summary.html
+
+.. [Integer]
+    {{ ctags_url('Integer') }}
+
+.. [Leases]
+.. [Lease]
+    {{ ctags_url('Lease') }}
+
+.. [LeaseSet]
+    {{ ctags_url('LeaseSet') }}
+
+.. [Mapping]
+    {{ ctags_url('Mapping') }}
+
+.. [MSM-JAVADOCS]
+    http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/i2cp/MessageStatusMessage.html
+
+.. [PrivateKey]
+    {{ ctags_url('PrivateKey') }}
+
+.. [PublicKey]
+    {{ ctags_url('PublicKey') }}
+
+.. [RouterIdentity]
+    {{ ctags_url('RouterIdentity') }}
+
+.. [Signature]
+    {{ ctags_url('Signature') }}
+
+.. [SigningPrivateKey]
+    {{ ctags_url('SigningPrivateKey') }}
+
+.. [SigningPublicKey]
+    {{ ctags_url('SigningPublicKey') }}
+
+.. [String]
+    {{ ctags_url('String') }}
+
+.. [TunnelId]
+    {{ ctags_url('TunnelId') }}
diff --git a/i2p2www/spec/i2np.rst b/i2p2www/spec/i2np.rst
new file mode 100644
index 0000000000000000000000000000000000000000..023a5e68f7297e9f8128fd37670105b52a6925e8
--- /dev/null
+++ b/i2p2www/spec/i2np.rst
@@ -0,0 +1,1309 @@
+==================
+I2NP Specification
+==================
+.. meta::
+    :lastupdated: January 2016
+    :accuratefor: 0.9.24
+
+.. contents::
+
+
+Overview
+========
+
+The I2P Network Protocol (I2NP), which is sandwiched between I2CP and the
+various I2P transport protocols, manages the routing and mixing of messages
+between routers, as well as the selection of what transports to use when
+communicating with a peer for which there are multiple common transports
+supported.
+
+
+.. _versions:
+
+Protocol Versions
+=================
+
+All routers must publish their I2NP protocol version in the "router.version"
+field in the RouterInfo properties.  This version field indicates their level
+of support for various I2NP protocol features, and is not necessarily the
+actual router version.
+
+If alternative (non-Java) routers wish to publish any version information about
+the actual router implementation, they must do so in another property.
+Versions other than those listed below are allowed. Support will be determined
+through a numeric comparison; for example, 0.9.13 implies support for 0.9.12
+features.  Note that the "coreVersion" property is not used for determination
+of the I2NP protocol version.
+
+A basic summary of the I2NP protocol versions is as follows. For details, see
+below.
+
+==============  ================================================================
+   Version      Required I2NP Features
+==============  ================================================================
+   0.9.18       DSM type bits 7-1 ignored
+
+   0.9.16       RI key certs / ECDSA and EdDSA sig types
+
+                Note: RSA sig types also supported as of this version, but
+                currently unused
+
+                DLM lookup types (DLM flag bits 3-2)
+
+   0.9.15       Dest/LS key certs w/ EdDSA Ed25519 sig type (if floodfill)
+
+   0.9.12       Dest/LS key certs w/ ECDSA P-256, P-384, and P-521 sig types (if
+                floodfill)
+
+                Note: RSA sig types also supported as of this version, but
+                currently unused
+
+                Nonzero expiration allowed in RouterAddress
+
+   0.9.7        Encrypted DSM/DSRM replies supported (DLM flag bit 1) (if
+                floodfill)
+
+   0.9.6        Nonzero DLM flag bits 7-1 allowed
+
+   0.9.3        Requires zero expiration in RouterAddress
+
+   0.9          Supports up to 16 leases in a DSM LS store (6 previously)
+
+   0.7.12       VTBM and VTBRM message support
+
+   0.7.10       Floodfill supports encrypted DSM stores
+
+0.7.9 or lower  All messages and features not listed above
+
+   0.6.1.10     TBM and TBRM messages introduced
+
+                Minimum version compatible with current network
+==============  ================================================================
+
+Note that there are also transport-related features and compatibility issues;
+see the NTCP and SSU transport documentation for details.
+
+
+.. _structures:
+
+Common structures
+=================
+
+The following structures are elements of multiple I2NP messages.
+They are not complete messages.
+
+.. _struct-I2NPMessageHeader:
+
+I2NP message header
+-------------------
+
+Description
+```````````
+Common header to all I2NP messages, which contains important information like a checksum, expiration date, etc.
+
+Contents
+````````
+1 byte [Integer]_ specifying the type of this message, followed by a 4 byte
+[Integer]_ specifying the message-id.  After that there is an expiration
+[Date]_, followed by a 2 byte [Integer]_ specifying the length of the message
+payload, followed by a [Hash]_, which is truncated to the first byte. After
+that the actual message data follows.
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
+Standard (16 bytes):
+
+  +----+----+----+----+----+----+----+----+
+  |type|      msg_id       |  expiration
+  +----+----+----+----+----+----+----+----+
+                           |  size   |chks|
+  +----+----+----+----+----+----+----+----+
+
+  Short (SSU, 5 bytes):
+
+  +----+----+----+----+----+
+  |type| short_expiration  |
+  +----+----+----+----+----+
+
+  type :: `Integer`
+          length -> 1 byte
+          purpose -> identifies the message type (see table below)
+
+  msg_id :: `Integer`
+            length -> 4 bytes
+            purpose -> uniquely identifies this message (for some time at least)
+                       This is usually a locally-generated random number, but
+                       for outgoing tunnel build messages it may be derived from
+                       the incoming message. See below.
+
+  expiration :: `Date`
+                8 bytes
+                date this message will expire
+
+  short_expiration :: `Integer`
+                      4 bytes
+                      date this message will expire (seconds since the epoch)
+
+  size :: `Integer`
+          length -> 2 bytes
+          purpose -> length of the payload
+
+  chks :: `Integer`
+          length -> 1 byte
+          purpose -> checksum of the payload
+                     SHA256 hash truncated to the first byte
+
+  data ::
+          length -> $size bytes
+          purpose -> actual message contents
+{% endhighlight %}
+
+Notes
+`````
+* When transmitted over [SSU]_, the 16-byte standard header is not used. Only a
+  1-byte type and a 4-byte expiration in seconds is included. The message id
+  and size are incorporated into various parts of the SSU data packet format.
+  The checksum is not required since errors are caught in decryption.
+
+* The standard header is also required for I2NP messages contained in other
+  messages and structures (Data, TunnelData, TunnelGateway, and GarlicClove).
+  As of release 0.8.12, to reduce overhead, checksum verification is disabled
+  at some places in the protocol stack. However, for compatibility with older
+  versions, checksum generation is still required. It is a topic for future
+  research to determine points in the protocol stack where the far-end router's
+  version is known and checksum generation can be disabled.
+
+* The short expiration is unsigned and will wrap around on Feb. 7, 2106. As of
+  that date, an offset must be added to get the correct time.
+
+.. _struct-BuildRequestRecord:
+
+BuildRequestRecord
+------------------
+
+Description
+```````````
+One Record in a set of multiple records to request the creation of one hop in
+the tunnel. For more details see the tunnel overview [TUNNEL-IMPL]_ and the
+tunnel creation specification [TUNNEL-CREATION]_.
+
+Contents
+````````
+[TunnelId]_ to receive messages on, followed by the [Hash]_ of our
+[RouterIdentity]_. After that the [TunnelId]_ and the [Hash]_ of the next
+router's [RouterIdentity]_ follow.
+
+Definition
+``````````
+ElGamal and AES encrypted:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  | encrypted data...                     |
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+
+  encrypted_data :: ElGamal and AES encrypted data
+                    length -> 528
+
+  total length: 528
+{% endhighlight %}
+
+ElGamal encrypted:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  | toPeer                                |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | encrypted data...                     |
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+
+  toPeer :: First 16 bytes of the SHA-256 Hash of the peer's `RouterIdentity`
+            length -> 16 bytes
+
+  encrypted_data :: ElGamal-2048 encrypted data (see notes)
+                    length -> 512
+
+  total length: 528
+{% endhighlight %}
+
+Cleartext:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  | receive_tunnel    | our_ident         |
+  +----+----+----+----+                   +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                   +----+----+----+----+
+  |                   | next_tunnel       |
+  +----+----+----+----+----+----+----+----+
+  | next_ident                            |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | layer_key                             |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | iv_key                                |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | reply_key                             |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | reply_iv                              |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |flag| request_time      | send_msg_id
+  +----+----+----+----+----+----+----+----+
+       |                                  |
+  +----+                                  +
+  |         29 bytes padding              |
+  +                                       +
+  |                                       |
+  +                             +----+----+
+  |                             |
+  +----+----+----+----+----+----+
+
+  receive_tunnel :: `TunnelId`
+                    length -> 4 bytes
+
+  our_ident :: `Hash`
+               length -> 32 bytes
+
+  next_tunnel :: `TunnelId`
+                 length -> 4 bytes
+
+  next_ident :: `Hash`
+                length -> 32 bytes
+
+  layer_key :: `SessionKey`
+               length -> 32 bytes
+
+  iv_key :: `SessionKey`
+            length -> 32 bytes
+
+  reply_key :: `SessionKey`
+               length -> 32 bytes
+
+  reply_iv :: data
+              length -> 16 bytes
+
+  flag :: `Integer`
+          length -> 1 byte
+
+  request_time :: `Integer`
+                  length -> 4 bytes
+                  Hours since the epoch, i.e. current time / 3600
+
+  send_message_id :: `Integer`
+                     length -> 4 bytes
+
+  padding :: Data
+             length -> 29 bytes
+             source -> random
+
+  total length: 222
+{% endhighlight %}
+
+Notes
+`````
+* In the 512-byte encrypted record, the ElGamal data contains bytes 1-256 and
+  258-513 of the 514-byte ElGamal encrypted block [CRYPTO-ELG]_. The two
+  padding bytes from the block (the zero bytes at locations 0 and 257) are
+  removed.
+
+* See the tunnel creation specification [TUNNEL-CREATION]_ for details on field
+  contents.
+
+.. _struct-BuildResponseRecord:
+
+BuildResponseRecord
+-------------------
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
+Encrypted:
+
+  bytes 0-527 :: AES-encrypted record (note: same size as `BuildRequestRecord`)
+
+  Unencrypted:
+
+  +----+----+----+----+----+----+----+----+
+  |                                       |
+  +                                       +
+  |                                       |
+  +   SHA-256 Hash of following bytes     +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | random data...                        |
+  ~                                       ~
+  |                                       |
+  +                                  +----+
+  |                                  | ret|
+  +----+----+----+----+----+----+----+----+
+
+  bytes 0-31   :: SHA-256 Hash of bytes 32-527
+  bytes 32-526 :: random data
+  byte  527    :: reply
+
+  total length: 528
+{% endhighlight %}
+
+Notes
+`````
+* The random data field could, in the future, be used to return congestion or
+  peer connectivity information back to the requestor.
+
+* See the tunnel creation specification [TUNNEL-CREATION]_ for details on the
+  reply field.
+
+.. _struct-GarlicClove:
+.. _Garlic Cloves:
+
+GarlicClove
+-----------
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
+Unencrypted:
+
+  +----+----+----+----+----+----+----+----+
+  | Delivery Instructions                 |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | I2NP Message                          |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |    Clove ID       |     Expiration
+  +----+----+----+----+----+----+----+----+
+                      | Certificate  |
+  +----+----+----+----+----+----+----+
+
+  Delivery Instructions :: as defined below
+         Length varies but is typically 1, 33, or 37 bytes
+
+  I2NP Message :: Any I2NP Message
+
+  Clove ID :: 4 byte `Integer`
+
+  Expiration :: `Date` (8 bytes)
+
+  Certificate :: Always NULL in the current implementation (3 bytes total, all zeroes)
+{% endhighlight %}
+
+Notes
+`````
+* Cloves are never fragmented. When used in a Garlic Clove, the first bit of
+  the Delivery Instructions flag byte specifies encryption. If this bit is 0,
+  the clove is not encrypted. If 1, the clove is encrypted, and a 32 byte
+  Session Key immediately follows the flag byte. Clove encryption is not fully
+  implemented.
+
+* See also the garlic routing specification [GARLIC]_.
+
+* Maximum length is a function of the total length of all the cloves and the
+  maximum length of the GarlicMessage.
+
+* In the future, the certificate could possibly be used for a HashCash to "pay"
+  for the routing.
+
+* The message can be any I2NP message (including a GarlicMessage, although that
+  is not used in practice). The messages used in practice are DataMessage,
+  DeliveryStatusMessage, and DatabaseStoreMessage.
+
+* The Clove ID is generally set to a random number on transmit and is checked
+  for duplicates on receive (same message ID space as top-level Message IDs)
+
+.. _struct-GarlicCloveDeliveryInstructions:
+
+Garlic Clove Delivery Instructions
+----------------------------------
+
+This specification is for Delivery Instructions inside Garlic Cloves only.
+Note that "Delivery Instructions" are also used inside Tunnel Messages, where
+the format is significantly different.  See the Tunnel Message documentation
+[TMDI]_ for details.  Do NOT use the following specification for Tunnel Message
+Delivery Instructions!
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |flag|                                  |
+  +----+                                  +
+  |                                       |
+  +       Session Key (optional)          +
+  |                                       |
+  +                                       +
+  |                                       |
+  +    +----+----+----+----+--------------+
+  |    |                                  |
+  +----+                                  +
+  |                                       |
+  +         To Hash (optional)            +
+  |                                       |
+  +                                       +
+  |                                       |
+  +    +----+----+----+----+--------------+
+  |    |  Tunnel ID (opt)  |  Delay (opt)  
+  +----+----+----+----+----+----+----+----+
+       |
+  +----+
+
+  flag ::
+         1 byte
+         Bit order: 76543210
+         bit 7: encrypted? Unimplemented, always 0
+                  If 1, a 32-byte encryption session key is included
+         bits 6-5: delivery type
+                  0x0 = LOCAL, 0x01 = DESTINATION, 0x02 = ROUTER, 0x03 = TUNNEL
+         bit 4: delay included?  Not fully implemented, always 0
+                  If 1, four delay bytes are included
+         bits 3-0: reserved, set to 0 for compatibility with future uses
+
+  Session Key ::
+         32 bytes
+         Optional, present if encrypt flag bit is set.
+         Unimplemented, never set, never present.
+
+  To Hash ::
+         32 bytes
+         Optional, present if delivery type is DESTINATION, ROUTER, or TUNNEL
+            If DESTINATION, the SHA256 Hash of the destination
+            If ROUTER, the SHA256 Hash of the router
+            If TUNNEL, the SHA256 Hash of the gateway router
+
+  Tunnel ID :: `TunnelId`
+         4 bytes
+         Optional, present if delivery type is TUNNEL
+         The destination tunnel ID
+
+  Delay :: `Integer`
+         4 bytes
+         Optional, present if delay included flag is set
+         Not fully implemented. Specifies the delay in seconds.
+
+  Total length: Typical length is:
+         1 byte for LOCAL delivery;
+         33 bytes for ROUTER / DESTINATION delivery;
+         37 bytes for TUNNEL delivery
+{% endhighlight %}
+
+
+Messages
+========
+
+==================================  =======
+             Message                 Type
+==================================  =======
+DatabaseStore_                         1
+DatabaseLookup_                        2
+DatabaseSearchReply_                   3
+DeliveryStatus_                        10
+Garlic_                                11
+TunnelData_                            18
+TunnelGateway_                         19
+Data_                                  20
+TunnelBuild_                           21
+TunnelBuildReply_                      22
+VariableTunnelBuild_                   23
+VariableTunnelBuildReply_              24
+Reserved for experimental messages  224-254
+Reserved for future expansion         255
+==================================  =======
+
+.. _msg-DatabaseStore:
+
+DatabaseStore
+-------------
+
+Description
+```````````
+An unsolicited database store, or the response to a successful DatabaseLookup_ Message
+
+Contents
+````````
+An uncompressed LeaseSet or a compressed RouterInfo
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
+with reply token:
+  +----+----+----+----+----+----+----+----+
+  | SHA256 Hash as key                    |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |type| reply token       | reply_tunnelId
+  +----+----+----+----+----+----+----+----+
+       | SHA256 of the gateway RouterInfo |
+  +----+                                  +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +    +----+----+----+----+----+----+----+
+  |    | data ...
+  +----+-//
+
+  with reply token == 0:
+  +----+----+----+----+----+----+----+----+
+  | SHA256 Hash as key                    |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |type|         0         | data ...
+  +----+----+----+----+----+-//
+
+  key ::
+      32 bytes
+      SHA256 hash
+
+  type ::
+       1 byte
+       type identifier
+       bit 0:
+               0    `RouterInfo`
+               1    `LeaseSet`
+       bits 7-1:
+              Through release 0.9.17, must be 0
+              As of release 0.9.18, ignored, reserved for future options, set to 0 for compatibility
+
+  reply token ::
+              4 bytes
+              If greater than zero, a `DeliveryStatusMessage`
+              is requested with the Message ID set to the value of the Reply Token.
+              A floodfill router is also expected to flood the data to the closest floodfill peers
+              if the token is greater than zero.
+
+  reply_tunnelId ::
+                 4 byte `TunnelId`
+                 Only included if reply token &gt; 0
+                 This is the `TunnelId` of the inbound gateway of the tunnel the response should be sent to
+                 If $reply_tunnelId is zero, the reply is sent directy to the reply gateway router.
+
+  reply gateway ::
+                32 bytes
+                Hash of the `RouterInfo` entry to reach the gateway
+                Only included if reply token &gt; 0
+                If $reply_tunnelId is nonzero, this is the router hash of the inbound gateway
+                of the tunnel the response should be sent to.
+                If $reply_tunnelId is zero, this is the router hash the response should be sent to.
+
+  data ::
+       If type == 0, data is a 2-byte `Integer` specifying the number of bytes that follow,
+                     followed by a gzip-compressed `RouterInfo`.
+       If type == 1, data is an uncompressed `LeaseSet`.
+{% endhighlight %}
+
+Notes
+`````
+* For security, the reply fields are ignored if the message is received down a
+  tunnel.
+
+* The key is the "real" hash of the RouterIdentity or Destination, NOT the
+  routing key.
+
+.. _msg-DatabaseLookup:
+
+DatabaseLookup
+--------------
+
+Description
+```````````
+A request to look up an item in the network database.  The response is either a
+DatabaseStore_ or a DatabaseSearchReply_.
+
+Contents
+````````
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  | SHA256 hash as the key to look up     |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | SHA256 hash of the routerInfo         |
+  + who is asking, or the gateway to      +
+  | send the reply to                     |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |flag| reply_tunnelId    | size    |    |
+  +----+----+----+----+----+----+----+    +
+  | SHA256 of $key1 to exclude            |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                  +----+
+  |                                  |    |
+  +----+----+----+----+----+----+----+    +
+  | SHA256 of $key2 to exclude            |
+  +                                       +
+  ~                                       ~
+  +                                  +----+
+  |                                  |    |
+  +----+----+----+----+----+----+----+    +
+  |                                       |
+  +                                       +
+  |   Session key if reply encryption     |
+  +   was requested                       +
+  |                                       |
+  +                                  +----+
+  |                                  |tags|
+  +----+----+----+----+----+----+----+----+
+  |                                       |
+  +                                       +
+  |   Session tags if reply encryption    |
+  +   was requested                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+
+  key ::
+      32 bytes
+      SHA256 hash of the object to lookup
+
+  from ::
+       32 bytes
+       if deliveryFlag == 0, the SHA256 hash of the routerInfo entry this
+                             request came from (to which the reply should be
+                             sent)
+       if deliveryFlag == 1, the SHA256 hash of the reply tunnel gateway (to
+                             which the reply should be sent)
+
+  flags ::
+       1 byte
+       bit order: 76543210
+       bit 0: deliveryFlag
+               0  => send reply directly
+               1  => send reply to some tunnel
+       bit 1: encryptionFlag
+               through release 0.9.5, must be set to 0
+               as of release 0.9.6, ignored
+               as of release 0.9.7:
+               0  => send unencrypted reply
+               1  => send AES encrypted reply using enclosed key and tag
+       bits 3-2: lookup type flags
+               through release 0.9.5, must be set to 00
+               as of release 0.9.6, ignored
+               as of release 0.9.16:
+               00  => normal lookup, return `RouterInfo` or `LeaseSet` or
+                      `DatabaseSearchReplyMessage`
+               01  => LS lookup, return `LeaseSet` or
+                      `DatabaseSearchReplyMessage`
+               10  => RI lookup, return `RouterInfo` or
+                      `DatabaseSearchReplyMessage`
+               11  => exploration lookup, return `DatabaseSearchReplyMessage`
+                      containing non-floodfill routers only (replaces an
+                      excludedPeer of all zeroes)
+       bits 7-4:
+               through release 0.9.5, must be set to 0
+               as of release 0.9.6, ignored, set to 0 for compatibility with
+               future uses and with older routers
+
+  reply_tunnelId ::
+                 4 byte `TunnelID`
+                 only included if deliveryFlag == 1
+                 tunnelId of the tunnel to send the reply to
+
+  size ::
+       2 byte `Integer`
+       valid range: 0-512
+       number of peers to exclude from the `DatabaseSearchReplyMessage`
+
+  excludedPeers ::
+                $size SHA256 hashes of 32 bytes each (total $size*32 bytes)
+                if the lookup fails, these peers are requested to be excluded
+                from the list in the `DatabaseSearchReplyMessage`.
+                if excludedPeers includes a hash of all zeroes, the request is
+                exploratory, and the `DatabaseSearchReplyMessage` is requested
+                to list non-floodfill routers only.
+
+  reply_key ::
+       32 byte `SessionKey`
+       only included if encryptionFlag == 1, only as of release 0.9.7
+
+  tags ::
+       1 byte `Integer`
+       valid range: 1-32 (typically 1)
+       the number of reply tags that follow
+       only included if encryptionFlag == 1, only as of release 0.9.7
+
+  reply_tags ::
+       one or more 32 byte `SessionTag`s (typically one)
+       only included if encryptionFlag == 1, only as of release 0.9.7
+{% endhighlight %}
+
+Notes
+`````
+* Prior to 0.9.16, the key may be for a RouterInfo or LeaseSet, as they are in
+  the same key space, and there was no flag to request only a particular type
+  of data.
+
+* Encryption flag, reply key, and reply tags as of release 0.9.7.
+
+* Encrypted replies are only useful when the response is through a tunnel.
+
+* The number of included tags could be greater than one if alternative DHT
+  lookup strategies (for example, recursive lookups) are implemented.
+
+* The lookup key and exclude keys are the "real" hashes, NOT routing keys.
+
+.. _msg-DatabaseSearchReply:
+
+DatabaseSearchReply
+-------------------
+
+Description
+```````````
+The response to a failed DatabaseLookup_ Message
+
+Contents
+````````
+A list of router hashes closest to the requested key
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  | SHA256 hash as query key              |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | num| peer_hashes                      |
+  +----+                                  +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +    +----+----+----+----+----+----+----+
+  |    | from                             |
+  +----+                                  +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +    +----+----+----+----+----+----+----+
+  |    |
+  +----+
+
+  key ::
+      32 bytes
+      SHA256 of the object being searched
+
+  num ::
+      1 byte `Integer`
+      number of peer hashes that follow, 0-255
+
+  peer_hashes ::
+            $num SHA256 hashes of 32 bytes each (total $num*32 bytes)
+            SHA256 of the `RouterIdentity` that the other router thinks is close
+            to the key
+
+  from ::
+       32 bytes
+       SHA256 of the `RouterInfo` of the router this reply was sent from
+{% endhighlight %}
+
+Notes
+`````
+* The 'from' hash is unauthenticated and cannot be trusted.
+
+* The returned peer hashes are not necessarily closer to the key than the
+  router being queried.
+
+* Typical number of hashes returned: 3
+
+* The lookup key, peer hashes, and from hash are "real" hashes, NOT routing
+  keys.
+
+.. _msg-DeliveryStatus:
+
+DeliveryStatus
+--------------
+
+Description
+```````````
+A simple message acknowledgment. Generally created by the message originator,
+and wrapped in a Garlic Message with the message itself, to be returned by the
+destination.
+
+Contents
+````````
+The ID of the delivered message, and the creation or arrival time.
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+----+----+----+----+
+  | msg_id            |           time_stamp                  |
+  +----+----+----+----+----+----+----+----+----+----+----+----+
+
+  msg_id :: `Integer`
+         4 bytes
+         unique ID of the message we deliver the DeliveryStatus for (see
+         `I2NPMessageHeader` for details)
+
+  time_stamp :: `Date`
+               8 bytes
+               time the message was successfully created or delivered
+{% endhighlight %}
+
+Notes
+`````
+* It appears that the time stamp is always set by the creator to the current
+  time. However there are several uses of this in the code, and more may be
+  added in the future.
+
+* This message is also used as a session established confirmation in SSU
+  [SSU-ED]_. In this case, the message ID is set to a random number, and the
+  "arrival time" is set to the current network-wide ID, which is 2 (i.e.
+  0x0000000000000002).
+
+.. _msg-Garlic:
+
+Garlic
+------
+
+Description
+```````````
+Used to wrap multiple encrypted I2NP Messages
+
+Contents
+````````
+When decrypted, a series of `Garlic Cloves`_.
+
+Encrypted:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |      length       | data              |
+  +----+----+----+----+                   +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+
+  length ::
+         4 byte `Integer`
+         number of bytes that follow 0 - 64 KB
+
+  data ::
+       $length bytes
+       ElGamal encrypted data
+{% endhighlight %}
+Unencrypted data:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  | num|  clove 1                         |
+  +----+                                  +
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |         clove 2 ...                   |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | Certificate  |   Message_ID      |     
+  +----+----+----+----+----+----+----+----+
+            Expiration               |
+  +----+----+----+----+----+----+----+
+
+  num ::
+       1 byte `Integer` number of `GarlicClove`s to follow
+
+  clove ::  a `GarlicClove`
+
+  Certificate :: always NULL in the current implementation (3 bytes total, all zeroes)
+
+  Message_ID :: 4 byte `Integer`
+
+  Expiration :: `Date` (8 bytes)
+{% endhighlight %}
+
+Notes
+`````
+* When unencrypted, data contains one or more `Garlic Cloves`_.
+
+* The AES encrypted block is padded to a minimum of 128 bytes; with the 32-byte
+  Session Tag the minimum size of the encrypted message is 160 bytes; with the
+  4 length bytes the minimum size of the Garlic Message is 164 bytes.
+
+* Actual max length is less than 64 KB; see [I2NP]_.
+
+* See also the ElGamal/AES specification [ELG-AES]_.
+
+* See also the garlic routing specification [GARLIC]_.
+
+* The 128 byte minimum size of the AES encrypted block is not currently
+  configurable, however the minimum size of a DataMessage in a GarlicClove in a
+  GarlicMessage, with overhead, is 128 bytes anyway. A configurable option to
+  increase the minimum size may be added in the future.
+
+* The message ID is generally set to a random number on transmit and appears to
+  be ignored on receive.
+
+* In the future, the certificate could possibly be used for a HashCash to "pay"
+  for the routing.
+
+.. _msg-TunnelData:
+
+TunnelData
+----------
+
+Description
+```````````
+A message sent from a tunnel's gateway or participant to the next participant
+or endpoint.  The data is of fixed length, containing I2NP messages that are
+fragmented, batched, padded, and encrypted.
+
+Contents
+````````
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |     tunnnelID     | data              |
+  +----+----+----+----+                   |
+  |                                       |
+  ~                                       ~
+  ~                                       ~
+  |                                       |
+  +                   +----+----+----+----+
+  |                   |
+  +----+----+----+----+
+
+  tunnelId ::
+           4 byte `TunnelId`
+           identifies the tunnel this message is directed at
+
+  data ::
+       1024 bytes
+       payload data.. fixed to 1024 bytes
+{% endhighlight %}
+
+Notes
+`````
+* The I2NP message ID for this message is set to a new random number at each
+  hop.
+
+* See also the Tunnel Message Specification [TUNNEL-MSG]_
+
+.. _msg-TunnelGateway:
+
+TunnelGateway
+-------------
+
+Description
+```````````
+Wraps another I2NP message to be sent into a tunnel at the tunnel's inbound gateway.
+
+Contents
+````````
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+-//
+  | tunnelId          | length  | data...
+  +----+----+----+----+----+----+----+-//
+
+  tunnelId ::
+           4 byte `TunnelId`
+           identifies the tunnel this message is directed at
+
+  length ::
+         2 byte `Integer`
+         length of the payload
+
+  data ::
+       $length bytes
+       actual payload of this message
+{% endhighlight %}
+
+Notes
+`````
+* The payload is an I2NP message with a standard 16-byte header.
+
+.. _msg-Data:
+
+Data
+----
+
+Description
+```````````
+Used by Garlic Messages and Garlic Cloves to wrap arbitrary data.
+
+Contents
+````````
+A length Integer, followed by opaque data.
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+-//-+
+  | length            | data... |
+  +----+----+----+----+----+-//-+
+
+  length ::
+         4 bytes
+         length of the payload
+
+  data ::
+       $length bytes
+       actual payload of this message
+{% endhighlight %}
+
+Notes
+`````
+* This message contains no routing information and will never be sent
+  "unwrapped". It is only used inside `Garlic` messages.
+
+.. _msg-TunnelBuild:
+
+TunnelBuild
+-----------
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  | Record 0 ...                          |
+
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | Record 1 ...                          |
+
+  ~ .....                                 ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | Record 7 ...                          |
+
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+
+  Just 8 `BuildRequestRecord`s attached together
+  record size: 528 bytes
+  total size: 8*528 = 4224 bytes
+{% endhighlight %}
+
+Notes
+`````
+* See also the tunnel creation specification [TUNNEL-CREATION]_.
+
+* The I2NP message ID for this message must be set according to the tunnel
+  creation specification.
+
+* While this message is rarely seen in today's network, having been replaced by
+  the `VariableTunnelBuild` message, it may still be used for very long tunnels,
+  and has not been deprecated. Routers must implement.
+
+.. _msg-TunnelBuildReply:
+
+TunnelBuildReply
+----------------
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
+Same format as `TunnelBuildMessage`, with `BuildResponseRecord`s
+{% endhighlight %}
+
+Notes
+`````
+* See also the tunnel creation specification [TUNNEL-CREATION]_.
+
+* The I2NP message ID for this message must be set according to the tunnel
+  creation specification.
+
+* While this message is rarely seen in today's network, having been replaced by
+  the `VariableTunnelBuildReply` message, it may still be used for very long
+  tunnels, and has not been deprecated. Routers must implement.
+
+.. _msg-VariableTunnelBuild:
+
+VariableTunnelBuild
+-------------------
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  | num| BuildRequestRecords...
+  +----+----+----+----+----+----+----+----+
+
+  Same format as `TunnelBuildMessage`, except for the addition of a $num field
+  in front and $num number of `BuildRequestRecord`s instead of 8
+
+  num ::
+         1 byte `Integer`
+         Valid values: 1-8
+
+  record size: 528 bytes
+  total size: 1+$num*528
+{% endhighlight %}
+
+Notes
+`````
+* This message was introduced in router version 0.7.12, and may not be sent to
+  tunnel participants earlier than that version.
+
+* See also the tunnel creation specification [TUNNEL-CREATION]_.
+
+* The I2NP message ID for this message must be set according to the tunnel
+  creation specification.
+
+* Typical number of records in today's network is 5.
+
+.. _msg-VariableTunnelBuildReply:
+
+VariableTunnelBuildReply
+------------------------
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  | num| BuildResponseRecords...
+  +----+----+----+----+----+----+----+----+
+
+  Same format as `VariableTunnelBuildMessage`, with `BuildResponseRecord`s.
+{% endhighlight %}
+
+Notes
+`````
+* This message was introduced in router version 0.7.12, and may not be sent to
+  tunnel participants earlier than that version.
+
+* See also the tunnel creation specification [TUNNEL-CREATION]_.
+
+* The I2NP message ID for this message must be set according to the tunnel
+  creation specification.
+
+* Typical number of records in today's network is 5.
+
+
+References
+==========
+
+.. [CRYPTO-ELG]
+    {{ site_url('docs/how/cryptography', True) }}#elgamal
+
+.. [Date]
+    {{ ctags_url('Date') }}
+
+.. [ElG-AES]
+    {{ site_url('docs/how/elgamal-aes', True) }}
+
+.. [GARLIC]
+    {{ site_url('docs/how/garlic-routing', True) }}
+
+.. [Hash]
+    {{ ctags_url('Hash') }}
+
+.. [I2NP]
+    {{ site_url('docs/protocol/i2np', True) }}
+
+.. [Integer]
+    {{ ctags_url('Integer') }}
+
+.. [RouterIdentity]
+    {{ ctags_url('RouterIdentity') }}
+
+.. [SSU]
+    {{ site_url('docs/transport/ssu', True) }}
+
+.. [SSU-ED]
+    {{ site_url('docs/transport/ssu', True) }}#establishDirect
+
+.. [TMDI]
+    {{ ctags_url('TunnelMessageDeliveryInstructions') }}
+
+.. [TUNNEL-CREATION]
+    {{ spec_url('tunnel-creation') }}
+
+.. [TUNNEL-MSG]
+    {{ spec_url('tunnel-message') }}
+
+.. [TUNNEL-IMPL]
+    {{ site_url('docs/tunnels/implementation', True) }}
+
+.. [TunnelId]
+    {{ ctags_url('TunnelId') }}
diff --git a/i2p2www/spec/plugin.rst b/i2p2www/spec/plugin.rst
new file mode 100644
index 0000000000000000000000000000000000000000..2a0295892489939ff7cb740d19f1b32f48d49dff
--- /dev/null
+++ b/i2p2www/spec/plugin.rst
@@ -0,0 +1,691 @@
+====================
+Plugin Specification
+====================
+.. meta::
+    :lastupdated: February 2016
+    :accuratefor: 0.9.25
+
+.. contents::
+
+
+Overview
+========
+
+This document specifies a .xpi2p file format (like the Firefox .xpi), but with
+a simple plugin.config description file instead of an XML install.rdf file.
+This file format is used for both initial plugin installs and plugin updates.
+
+In addition, this document provides a brief overview of how the router installs
+plugins, and policies and guidelines for plugin developers.
+
+The basic .xpi2p file format is the same as a i2pupdate.sud file (the format
+used for router updates), but the installer will let the user install the addon
+even if it doesn't know the signer's key yet.
+
+As of release 0.9.15, the SU3 file format [UPDATES]_ is supported and is
+preferred. This format enables stronger signing keys.
+
+The standard directory structure will let users install the following types of
+addons:
+
+* console webapps
+
+* new eepsite with cgi-bin, webapps
+
+* console themes
+
+* console translations
+
+* Java programs
+
+* Java programs in a separate JVM
+
+* Any shell script or program
+
+A plugin installs all its files in ~/.i2p/plugins/name/
+(%APPDIR%\I2P\plugins\name\ on Windows). The installer will prevent
+installation anywhere else, although the plugin can access libraries elsewhere
+when running.
+
+This should be viewed only as a way to make installation, uninstallation, and
+upgrading easier, and to lessen basic inter-plugin conflicts.
+
+There is essentially no security model once the plugin is running, however. The
+plugin runs in the same JVM and with the same permissions as the router, and
+has full access to the file system, the router, executing external programs,
+etc.
+
+Details
+=======
+
+foo.xpi2p is a signed update (sud) file [UPDATES]_ containing the following:
+
+Standard .sud header prepended to the zip file, containing the following::
+
+    40-byte DSA signature [CRYPTO-DSA]_
+    16-byte plugin version in UTF-8, padded with trailing zeroes if necessary
+
+Zip file containing the following:
+
+plugin.config file
+``````````````````
+This file is required. It is a standard I2P configuration file [CONFIG]_,
+containing the following properties:
+
+The following four are required properties.  The first three must be identical
+to those in the installed plugin for an update plugin.
+
+    name
+        Will be installed in this directory name
+
+        For native plugins, you may want separate names in different packages -
+        foo-windows and foo-linux, for example
+
+    key
+        DSA public key [CRYPTO-DSA]_ as 172 B64 chars ending with '='
+
+        Omit for SU3 format.
+
+    signer
+        yourname@mail.i2p recommended)
+
+    version
+        Must be in a format VersionComparator can parse, e.g. 1.2.3-4
+
+        16 bytes max (must match sud version)
+
+        Valid number separators are '.', '-', and '_'
+
+        This must be greater than the one in the installed plugin for an update plugin.
+
+Values for the following properties are displayed on /configclients in the
+router console if present:
+
+    date
+        Java time - long int
+
+    author
+        yourname@mail.i2p recommended
+
+    websiteURL
+        http://foo.i2p/
+
+    updateURL
+        http://foo.i2p/foo.xpi2p
+
+        The update checker will check bytes 41-56 at this URL
+        to determine whether a newer version is available
+
+        (Should the checker fetch with ?currentVersion=1.2.3?...
+        No. If the dev wants to have the URL contain the current version, just
+        set it in the config file, and remember to change it every release)
+
+    updateURL.su3
+        http://foo.i2p/foo.su3
+
+        The location of the su3-format update file, as of 0.9.15
+
+    description
+        in English
+
+    description_xx
+        for language xx
+
+    license
+        The plugin license
+
+    disableStop=true
+        Default false.
+        If true, the stop button will not be shown. Use this if there are no
+        webapps and no clients with stopargs.
+
+The following properties are used to add a link on the console summary bar:
+
+    consoleLinkName
+        will be added to summary bar
+
+    consoleLinkName_xx
+        for language xx
+
+    consoleLinkURL
+        /appname/index.jsp
+
+    consoleLinkTooltip
+        supported as of 0.7.12-6
+
+    consoleLinkTooltip_xx
+        lang xx as of 0.7.12-6
+
+The following optional properties may be used to add a custom icon on the
+console:
+
+    console-icon
+        supported as of 0.9.20
+
+        Only for webapps.
+
+        A path within the webapp to a 32x32 image, e.g. /icon.png
+        Applies to all webapps in the plugin.
+
+    icon-code
+        supported as of 0.9.25
+
+        Provides a console icon for plugins without web resources.
+
+        A B64 string produced by calling `net.i2p.data.Base64 encode FILE` on a
+        32x32 png image file.
+
+The following properties are used by the plugin installer:
+
+    type
+        app/theme/locale/webapp/...
+
+        (unimplemented, probably not necessary)
+
+    min-i2p-version
+        The minimum version of I2P this plugin requires
+
+    max-i2p-version
+        The maximum version of I2P this plugin will run on
+
+    min-java-version
+        The minimum version of Java this plugin requires
+
+    min-jetty-version
+        supported as of 0.8.13, use 6 for Jetty 6 webapps
+
+    max-jetty-version
+        supported as of 0.8.13, use 5.99999 for Jetty 5 webapps
+
+    required-platform-OS
+        unimplemented - perhaps will be displayed only, not verified
+
+    other-requirements
+        unimplemented
+
+        e.g. python x.y - not verified by the installer, just displayed to the
+        user
+
+    dont-start-at-install=true
+        Default false.
+
+        Won't start the plugin when it is installed or updated. On initial
+        installation, configures the plugin so the user must manually start it.
+        An update will not change the user's preference to start it if they
+        choose to do so.
+
+    router-restart-required=true
+        Default false.
+
+        This does not restart the router or the plugin on an update, it just
+        informs the user that a restart is required. It has no effect on initial
+        plugin installation.
+
+    update-only=true
+        Default false.
+
+        If true, will fail if an installation does not exist.
+
+    install-only=true
+        Default false.
+        If true, will fail if an installation exists.
+
+    min-installed-version
+        to update over, if an installation exists
+
+    max-installed-version
+        to update over, if an installation exists
+
+    depends=plugin1,plugin2,plugin3
+        unimplemented - is this too hard? proposed by sponge
+
+    depends-version=0.3.4,,5.6.7
+        unimplemented
+
+The following property is used for translation plugins:
+
+    langs=xx,yy,Klingon,...
+        (unimplemented)
+        (yy is the country flag)
+
+Application Directories and Files
+`````````````````````````````````
+Each of the following directories or files is optional, but something must be
+there or it won't do anything:
+
+console/
+    locale/
+        Only jars containing new resource bundles (translations) for apps in the
+        base I2P installation. Bundles for this plugin should go inside
+        console/webapp/foo.war or lib/foo.jar
+
+    themes/
+        New themes for the router console
+        Place each theme in a subdirectory.
+
+    webapps/
+        (See important notes below about webapps)
+
+        .wars
+            These will be run at install time unless disabled in webapps.config
+            The war name does not have to be the same as the plugin name.
+            Do not duplicate war names in the base I2P installation.
+
+    webapps.config 
+        Same format as router's webapps.config. Also used to specify additional
+        jars in $PLUGIN/lib/ or $I2P/lib for the webapp classpath, with
+        ``webapps.warname.classpath=$PLUGIN/lib/foo.jar,$I2P/lib/bar.jar``
+
+        NOTE: Currently, the classpath line is only loaded if the warname is the
+        same as the plugin name.
+
+        NOTE: Prior to router version 0.7.12-9, the router looked for
+        ``plugin.warname.startOnLoad`` instead of
+        ``webapps.warname.startOnLoad``. For compatibility with older router
+        versions, a plugin wishing to disable a war should include both lines.
+
+eepsite/
+    (See important notes below about eepsites)
+
+    cgi-bin/
+
+    docroot/
+
+    logs/
+
+    webapps/
+
+    jetty.xml
+        The installer will have to do variable substitution in here to set the
+        path. The location and name of this file doesn't really matter, as long
+        as it is set in clients.config - it may be more convenient to be up one
+        level from here (that's what the zzzot plugin does)
+
+lib/
+    Put any jars here, and specify them in a classpath line in
+    console/webapps.config and/or clients.config
+
+clients.config file
+```````````````````
+This file is optional, and specifies clients that will be run when a plugin is
+started.  It uses the same format as the router's clients.config file.  See the
+clients.config configuration file specification [CONFIG]_ for more information
+about the format and important details about how clients are started and
+stopped.
+
+    property clientApp.0.stopargs=foo bar stop baz
+        If present, the class will be called with these args to stop the client
+        All stop tasks are called with zero delay
+        Note: The router can't tell if your unmanaged clients are running or not.
+        Each should handle stopping an app that isn't running without complaint.
+        That probably goes for starting a client that is already started too.
+
+    property clientApp.0.uninstallargs=foo bar uninstall baz
+        If present, the class will be called with these args just before
+        deleting $PLUGIN. All uninstall tasks are called with zero delay
+
+    property clientApp.0.classpath=$I2P/lib/foo.bar,$PLUGIN/lib/bar.jar
+        The plugin runner will do variable substitution in the args and stopargs
+        lines as follows:
+
+        $I2P
+            I2P base installation dir
+
+        $CONFIG
+            I2P config dir (typically ~/.i2p)
+
+        $PLUGIN
+            this plugin's installation dir (typically ~/.i2p/plugins/appname)
+
+        (See important notes below about running shell scripts or external
+        programs)
+
+
+Plugin installer tasks
+======================
+
+This lists what happens when a plugin is installed by I2P.
+
+* The .xpi2p file is downloaded.
+
+* The .sud signature is verified against stored keys. As of release 0.9.14.1,
+  if there is no matching key, the installation fails, unless an advanced
+  router property is set to allow all keys.
+
+* Verify the integrity of the zip file.
+
+* Extract the plugin.config file.
+
+* Verify the I2P version, to make sure the plugin will work.
+
+* Check that webapps don't duplicate the existing $I2P applications.
+
+* Stop the existing plugin (if present).
+
+* Verify that the install directory does not exist yet if update=false, or ask
+  to overwrite.
+
+* Verify that the install directory does exist if update=true, or ask to
+  create.
+
+* Unzip the plugin in to appDir/plugins/name/
+
+* Add the plugin to plugins.config
+
+
+Plugin starter tasks
+====================
+
+This lists what happens when plugins are started.
+First, plugins.config is checked to see which plugins need to be started.
+For each plugin:
+
+* Check clients.config, and load and start each item (add the configured jars
+  to the classpath).
+
+* Check console/webapp and console/webapp.config. Load and start required items
+  (add the configured jars to the classpath).
+
+* Add console/locale/foo.jar to the translation classpath if present.
+
+* Add console/theme to the theme search path if present.
+
+* Add the summary bar link.
+
+
+Console webapp notes
+====================
+
+Console webapps with background tasks should implement a ServletContextListener
+(see seedless or i2pbote for examples), or override destroy() in the servlet,
+so that they can be stopped.  As of router version 0.7.12-3, console webapps
+will always be stopped before they are restarted, so you do not need to worry
+about multiple instances, as long as you do this.  Also as of router version
+0.7.12-3, console webapps will be stopped at router shutdown.
+
+Don't bundle library jars in the webapp; put them in lib/ and put a classpath
+in webapps.config.  Then you can make separate install and update plugins,
+where the update plugin does not contain the library jars.
+
+Don't include .java or .jsp files; otherwise jetty will recompile them at
+installation.
+
+For now, a webapp needing to add classpath files in $PLUGIN must be the same
+name as the plugin.  For example, a webapp in plugin foo must be named foo.war.
+
+
+Eepsite notes
+=============
+
+It isn't clear how to have a plugin install to an existing eepsite.  The router
+has no hook to the eepsite, and it may or may not be running, and there may be
+more than one.  Better is to start your own Jetty instance and I2PTunnel
+instance, for a brand new eepsite.
+
+It can instantiate a new I2PTunnel (somewhat like the i2ptunnel CLI does), but
+it won't appear in the i2ptunnel gui of course, that's a different instance.
+But that's ok. Then you can start and stop i2ptunnel and jetty together.
+
+So don't count on the router to automatically merge this with some existing
+eepsite. It probably won't happen.  Start a new I2PTunnel and Jetty from
+clients.config.  The best examples of this are the zzzot and pebble plugins,
+available at zzz's plugins page [STATS-PLUGINS]_.
+
+How to get path substitution into jetty.xml?  See zzzot and pebble plugins for
+examples.
+
+
+Client start/stop notes
+=======================
+
+As of release 0.9.4, the router supports "managed" plugin clients.  Managed
+plugin clients are instantiated and started by the ``ClientAppManager``.  The
+ClientAppManager maintains a reference to the client and receives updates on
+the client's state.  Managed plugin client are preferred, as it is much easier
+to implement state tracking and to start and stop a client. It also is much
+easier to avoid static references in the client code which could lead to
+excessive memory usage after a client is stopped.  See the clients.config
+configuration file specification [CONFIG]_ for more information on writing a
+managed client.
+
+For "unmanaged" plugin clients, The router has no way to monitor the state of
+clients started via clients.config.  The plugin author should handle multiple
+start or stop calls gracefully, if at all possible, by keeping a static state
+table, or using PID files, etc.  Avoid logging or exceptions on multiple starts
+or stops.  This also goes for a stop call without a previous start.  As of
+router version 0.7.12-3, plugins will be stopped at router shutdown, which
+means that all clients with stopargs in clients.config will be called, whether
+or not they were previously started.
+
+
+Shell script and external program notes
+=======================================
+
+To run shell scripts or other external programs, see [ZZZ-141]_.
+
+To work on both Windows and Linux, write a small Java class that checks the OS
+type, then runs ShellCommand on either the .bat or a .sh file you provide.
+
+External programs won't be stopped when the router stops, and a second copy
+will fire up when the router starts. To work around this, you could write a
+wrapper class or shell script that does the usual storage of the PID in a PID
+file, and check for it on start.
+
+
+Other plugin guidelines
+=======================
+
+* See i2p.scripts branch or any of the sample plugins on zzz's page for a xpi2p
+  file generator to make it easy.
+
+* Pack200 of jars and wars is strongly recommended for plugins, it generally
+  shrinks plugins by 60-65&#37;. See any of the sample plugins on zzz's page for
+  an example. Pack200 unpacking is supported on routers 0.7.11-5 or higher,
+  which is essentially all routers that support plugins at all.
+
+* Plugins should not attempt to write anywhere in $I2P as it may be readonly,
+  and that isn't good policy anyway.
+
+* Plugins may write to $CONFIG but keeping files in $PLUGIN only is recommended.
+  All files in $PLUGIN will be deleted at uninstall. Files elsewhere will not be
+  deleted at uninstall unless the plugin does it explicitly with a client in
+  clients.config run with uninstallargs. If the user may want to save data after
+  uninstallation, the uninstallargs hook could ask.
+
+* $CWD may be anywhere; do not assume it is in a particular place, do not
+  attempt to read or write files relative to $CWD.
+
+* Java programs should find out where they are with the directory getters in
+  I2PAppContext.
+
+* Plugin directory is
+  ``I2PAppContext.getGlobalContext().getAppDir().getAbsolutePath() + "/plugins/" + appname``,
+  or put a $PLUGIN argument in the args line in clients.config. There is no
+  reliable way to find the i2p install or config or plugin directory without
+  using the context API in i2p.jar.
+
+* See [ZZZ-16]_ for info on generating signing keys and generating/verifying
+  keys and sud files.
+
+* All config files must be UTF-8.
+
+* To run in a separate JVM, use ShellCommand with
+  ``java -cp foo:bar:baz my.main.class arg1 arg2 arg3``. Of course, it will be a
+  lot harder to stop the plugin then... But with some trickery with PID files it
+  should be possible.
+
+* As an alternative to stopargs in clients.config, a Java client may register a
+  shutdown hook with I2PAppContext.addShutdownTask(). But this wouldn't shut
+  down a plugin when upgrading, so stopargs is recommended. Also, set all
+  created threads to daemon mode.
+
+* Do not include classes duplicating those in the standard installation. Extend
+  the classes if necessary.
+
+* Beware of the different classpath definitions in wrapper.config between old
+  and new installations - see classpath section below.
+
+* Clients will reject duplicate keys with different keynames, and duplicate
+  keynames with different keys, and different keys or keynames in upgrade
+  packages. Safeguard your keys. Only generate them once.
+
+* Do not modify the plugin.config file at runtime as it will be overwritten on
+  upgrade. Use a different config file in the directory for storing runtime
+  configuration.
+
+* In general, plugins should not require access to $I2P/lib/router.jar. Do not
+  access router classes, unless you are doing something special. The router may
+  in the future implement a restricted classpath for plugins that prevents
+  access to router classes.
+
+* Since each version must be higher than the one before, you could enhance your
+  build script to add a build number to the end of the version. This helps for
+  testing. Most of zzz's plugins have that feature, check build.xml for an example.
+
+* Plugins must never call ``System.exit()``.
+
+* Please respect licenses by meeting license requirements for any software you
+  bundle.
+
+* The router sets the JVM time zone to UTC. If a plugin needs to know the user's
+  actual time zone, it is stored by the router in the I2PAppContext property
+  ``i2p.systemTimeZone``.
+
+
+Classpaths
+==========
+
+The following jars in $I2P/lib can be assumed to be in the standard classpath
+for all I2P installations, no matter how old or how new the original
+installation:
+
+=====================  ============================  =====
+         Jar                     Contains            Usage
+=====================  ============================  =====
+commons-logging.jar    Apache Logging                For plugins requiring Apache logging.
+                                                     Prior to Jetty 6 (release 0.9), this
+                                                     contained Apache Commons Logging only.
+                                                     From release 0.9 to release 0.9.23, this
+                                                     contained both Commons Logging and Tomcat JULI.
+                                                     As of release 0.9.24, this contains
+                                                     Apache Tomcat JULI logging only.
+
+commons-el.jar         JSP Expressions Language      For plugins with JSPs that use EL
+i2p.jar                Core API                      Almost all plugins will need
+i2ptunnel.jar          I2PTunnel                     For plugins with HTTP or other servers
+jasper-compiler.jar    nothing                       Empty since Jetty 6 (release 0.9)
+jasper-runtime.jar     Jasper Compiler and Runtime,  Needed for plugins with JSPs
+                       and some Tomcat utils
+javax.servlet.jar      Servlet API                   Needed for plugins with JSPs
+jbigi.jar              Binaries                      No plugin should need
+mstreaming.jar         Streaming API                 Almost all plugins will need
+org.mortbay.jetty.jar  Jetty Base                    Only plugins starting their own Jetty instance
+                                                     will need. Recommended way of starting Jetty
+                                                     is with net.i2p.jetty.JettyStart in jetty-i2p.jar.
+router.jar             Router                        Only plugins using router context will need; most will not
+sam.jar                SAM API                       No plugin should need
+streaming.jar          Streaming Implementation      Almost all plugins will need
+systray.jar            URL Launcher                  Most plugins should not need
+systray4j.jar          Systray                       No plugin should need
+wrapper.jar            Router                        Most plugins should not need
+=====================  ============================  =====
+
+The following jars in $I2P/lib can be assumed to be present for all I2P
+installations, no matter how old or how new the original installation, but are
+not necessarily in the classpath:
+
+============  ===============  =====
+    Jar          Contains      Usage
+============  ===============  =====
+jstl.jar      Standard Taglib  For plugins using JSP tags
+standard.jar  Standard Taglib  For plugins using JSP tags
+============  ===============  =====
+
+Anything not listed above may not be present in everybody's classpath, even if
+you have it in the classpath in YOUR version of i2p.  If you need any jar not
+listed above, add $I2P/lib/foo.jar to the classpath specified in clients.config
+or webapps.config in your plugin.
+
+Previously, a classpath entry specified in clients.config was added to the
+classpath for the entire JVM.  However, as of 0.7.13-3, this was fixed using
+class loaders, and now, as originally intended, the specified classpath in
+clients.config is only for the particular thread.  See the section on JVM
+crashes below, and [ZZZ-633]_ for background.  Therefore, specify the full
+required classpath for each client.
+
+
+Java Version Notes
+==================
+
+I2P has required Java 7 since release 0.9.24 (January 2016).
+I2P has required Java 6 since release 0.9.12 (April 2014).
+Any I2P users on the latest release should be running a 1.7 (7.0) JVM.
+In early 2016, unless you require 1.7 language or library features, you should
+create your plugin so it works on 1.6. Later in the year, most of the network
+will be on 0.9.24 or higher with Java 7.
+
+If your plugin **does not require 1.7**:
+
+* Ensure that all java and jsp files are compiled with source="1.6"
+  target="1.6".
+
+* Ensure that all bundled library jars are also for 1.6 or lower.
+
+If your plugin **requires 1.7**:
+
+* Note that on your download page.
+
+* Add min-java-version=1.7 to your plugin.config
+
+In any case, you **must** set a bootclasspath when compiling with Java 8 to
+prevent runtime crashes.
+
+
+JVM Crashes When Updating
+=========================
+
+Note - this should all be fixed now.
+
+The JVM has a tendency to crash when updating jars in a plugin if that plugin
+was running since I2P was started (even if the plugin was later stopped).  This
+may have been fixed with the class loader implementation in 0.7.13-3, but it
+may not.  For further testing.
+
+The safest is to design your plugin with the jar inside the war (for a webapp),
+or to require a restart after update, or don't update the jars in your plugin.
+
+Due to the way class loaders work inside a webapp, it _may_ be safe to have
+external jars if you specify the classpath in webapps.config.  More testing is
+required to verify this.  Don't specify the classpath with a 'fake' client in
+clients.config if it's only needed for a webapp - use webapps.config instead.
+
+The least safe, and apparently the source of most crashes, is clients with
+plugin jars specified in the classpath in clients.config.
+
+None of this should be a problem on initial install - you should not ever have
+to require a restart for an initial install of a plugin.
+
+
+References
+==========
+
+.. [CONFIG]
+    {{ spec_url('configuration') }}
+
+.. [CRYPTO-DSA]
+    {{ site_url('docs/how/cryptography', True) }}#DSA
+
+.. [STATS-PLUGINS]
+    http://{{ i2pconv('stats.i2p') }}/i2p/plugins/
+
+.. [UPDATES]
+    {{ spec_url('updates') }}
+
+.. [ZZZ-16]
+    http://{{ i2pconv('zzz.i2p') }}/topics/16
+
+.. [ZZZ-141]
+    http://{{ i2pconv('zzz.i2p') }}/topics/141
+
+.. [ZZZ-633]
+    http://{{ i2pconv('zzz.i2p') }}/topics/633
diff --git a/i2p2www/spec/spectags b/i2p2www/spec/spectags
new file mode 100644
index 0000000000000000000000000000000000000000..f1c30bc2191f26988e82b3f54c4ba2c35f57a4fc
--- /dev/null
+++ b/i2p2www/spec/spectags
@@ -0,0 +1,76 @@
+!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;" to lines/
+!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/
+!_TAG_PROGRAM_AUTHOR	Darren Hiebert	/dhiebert@users.sourceforge.net/
+!_TAG_PROGRAM_NAME	Exuberant Ctags	//
+!_TAG_PROGRAM_URL	http://ctags.sourceforge.net	/official site/
+!_TAG_PROGRAM_VERSION	5.9~svn20110310	//
+BandwidthLimits	i2cp.rst	441;"	m
+Boolean	common-structures.rst	110;"	t
+BuildRequestRecord	i2np.rst	175;"	s
+BuildResponseRecord	i2np.rst	349;"	s
+Certificate	common-structures.rst	340;"	t
+CreateLeaseSet	i2cp.rst	469;"	m
+CreateSession	i2cp.rst	503;"	m
+Data	i2np.rst	1098;"	m
+DatabaseLookup	i2np.rst	653;"	m
+DatabaseSearchReply	i2np.rst	808;"	m
+DatabaseStore	i2np.rst	552;"	m
+Date	common-structures.rst	80;"	t
+DeliveryInstructions	common-structures.rst	1070;"	s
+DeliveryStatus	i2np.rst	882;"	m
+DestLookup	i2cp.rst	535;"	m
+DestReply	i2cp.rst	557;"	m
+Destination	common-structures.rst	680;"	s
+DestroySession	i2cp.rst	580;"	m
+Disconnect	i2cp.rst	599;"	m
+Garlic	i2np.rst	925;"	m
+GarlicClove	i2np.rst	394;"	s
+GarlicCloveDeliveryInstructions	i2np.rst	456;"	s
+GetBandwidthLimits	i2cp.rst	619;"	m
+GetDate	i2cp.rst	642;"	m
+Hash	common-structures.rst	293;"	t
+HostLookup	i2cp.rst	675;"	m
+HostReply	i2cp.rst	715;"	m
+I2CPMessageHeader	i2cp.rst	299;"	s
+I2NPMessageHeader	i2np.rst	93;"	s
+Integer	common-structures.rst	67;"	t
+KeysAndCert	common-structures.rst	580;"	s
+Lease	common-structures.rst	713;"	s
+LeaseSet	common-structures.rst	761;"	s
+Mapping	common-structures.rst	502;"	t
+MessageId	i2cp.rst	319;"	s
+MessagePayload	i2cp.rst	740;"	m
+MessageStatus	i2cp.rst	761;"	m
+Payload	i2cp.rst	338;"	s
+PrivateKey	common-structures.rst	145;"	t
+PublicKey	common-structures.rst	128;"	t
+ReceiveMessageBegin	i2cp.rst	903;"	m
+ReceiveMessageEnd	i2cp.rst	929;"	m
+ReconfigureSession	i2cp.rst	954;"	m
+ReportAbuse	i2cp.rst	984;"	m
+RequestLeaseSet	i2cp.rst	1012;"	m
+RequestVariableLeaseSet	i2cp.rst	1040;"	m
+RouterAddress	common-structures.rst	897;"	s
+RouterIdentity	common-structures.rst	651;"	s
+RouterInfo	common-structures.rst	969;"	s
+SendMessage	i2cp.rst	1066;"	m
+SendMessageExpires	i2cp.rst	1109;"	m
+SessionConfig	i2cp.rst	358;"	s
+SessionId	i2cp.rst	385;"	s
+SessionKey	common-structures.rst	162;"	t
+SessionStatus	i2cp.rst	1252;"	m
+SessionTag	common-structures.rst	308;"	t
+SetDate	i2cp.rst	1297;"	m
+Signature	common-structures.rst	254;"	t
+SigningPrivateKey	common-structures.rst	216;"	t
+SigningPublicKey	common-structures.rst	177;"	t
+String	common-structures.rst	94;"	t
+Tunnel	tunnel-message.rst	33;"	m
+TunnelBuild	i2np.rst	1127;"	m
+TunnelBuildReply	i2np.rst	1162;"	m
+TunnelData	i2np.rst	1020;"	m
+TunnelGateway	i2np.rst	1063;"	m
+TunnelId	common-structures.rst	323;"	t
+TunnelMessageDeliveryInstructions	tunnel-message.rst	161;"	s
+VariableTunnelBuild	i2np.rst	1180;"	m
+VariableTunnelBuildReply	i2np.rst	1213;"	m
diff --git a/i2p2www/spec/ssu.rst b/i2p2www/spec/ssu.rst
new file mode 100644
index 0000000000000000000000000000000000000000..83d04b91d5cdce4702119afc0604a1e6377e2370
--- /dev/null
+++ b/i2p2www/spec/ssu.rst
@@ -0,0 +1,1199 @@
+==========================
+SSU Protocol Specification
+==========================
+.. meta::
+    :lastupdated: November 2015
+    :accuratefor: 0.9.24
+
+.. contents::
+
+
+Overview
+========
+
+See [SSU]_ for an overview of the SSU transport.
+
+
+.. _dh:
+
+DH Key Exchange
+===============
+
+The initial 2048-bit DH key exchange is described on the SSU page [SSU-KEYS]_.
+This exchange uses the same shared prime as that used for I2P's ElGamal
+encryption [CRYPTO-ELG]_.
+
+
+.. _header:
+
+Message Header
+==============
+
+All UDP datagrams begin with a 16 byte MAC (Message Authentication Code) and a
+16 byte IV (Initialization Vector) followed by a variable-size payload
+encrypted with the appropriate key.  The MAC used is HMAC-MD5, truncated to 16
+bytes, while the key is a full 32 byte AES256 key.  The specific construct of
+the MAC is the first 16 bytes from::
+
+  HMAC-MD5(encryptedPayload + IV + (payloadLength ^ protocolVersion), macKey)
+
+where '+' means append and '^' means exclusive-or.
+
+The IV is generated randomly for each packet.  The encryptedPayload is the
+encrypted version of the message starting with the flag byte
+(encrypt-then-MAC).  The payloadLength used in the MAC is a 2 byte unsigned
+integer.  Note that protocolVersion is 0, so the exclusive-or is a no-op.  The
+macKey is either the introduction key or is constructed from the exchanged DH
+key (see details below), as specified for each message below.
+
+**WARNING** - the HMAC-MD5-128 used here is non-standard, see [CRYPTO-HMAC]_
+for details.
+
+The payload itself (that is, the message starting with the flag byte) is
+AES256/CBC encrypted with the IV and the sessionKey, with replay prevention
+addressed within its body, explained below.
+
+The protocolVersion is a 2 byte unsigned integer and is currently set to 0.
+Peers using a different protocol version will not be able to communicate with
+this peer, though earlier versions not using this flag are.
+
+HMAC Specification
+------------------
+
+* Inner padding: 0x36...
+* Outer padding: 0x5C...
+* Key: 32 bytes
+* Hash digest function: MD5, 16 bytes
+* Block size: 64 bytes
+* MAC size: 16 bytes
+* Example C implementations:
+
+  * hmac.h in i2pd [I2PD-SRC]_
+  * I2PHMAC.cpp in i2pcpp [I2PCPP-SRC]_.
+
+* Example Java implementation:
+
+  * I2PHMac.java in I2P [I2P-SRC]_
+
+Session Key Details
+-------------------
+
+The 32-byte session key is created as follows:
+
+1. Take the exchanged DH key, represented as a positive minimal-length
+   BigInteger byte array (two's complement big-endian)
+
+2. If the most significant bit is 1 (i.e. array[0] & 0x80 != 0), prepend a 0x00
+   byte, as in Java's BigInteger.toByteArray() representation
+
+3. If the byte array is greater than or equal to 32 bytes, use the first (most
+   significant) 32 bytes
+
+4. If the byte array is less than 32 bytes, append 0x00 bytes to extend to 32
+   bytes. *Very unlikely - See note below.*
+
+MAC Key Details
+---------------
+
+The 32-byte MAC key is created as follows:
+
+1. Take the exchanged DH key byte array, prepended with a 0x00 byte if
+   necessary, from step 2 in the Session Key Details above.
+
+2. If that byte array is greater than or equal to 64 bytes, the MAC key is
+   bytes 33-64 from that byte array.
+
+3. If that byte array is less than 64 bytes, the MAC key is the SHA-256 Hash of
+   that byte array. *As of release 0.9.8. See note below.*
+
+Important note
+``````````````
+Code before release 0.9.8 was broken and did not correctly handle DH key byte
+arrays between 32 and 63 bytes (steps 3 and 4 above) and the connection will
+fail.  As these cases didn't ever work, they were redefined as described above
+for release 0.9.8, and the 0-32 byte case was redefined as well.  Since the
+nominal exchanged DH key is 256 bytes, the chances of the mininimal
+representation being less than 64 bytes is vanishingly small.
+
+Header Format
+-------------
+
+Within the AES encrypted payload, there is a minimal common structure to the
+various messages - a one byte flag and a four byte sending timestamp (seconds
+since the unix epoch).
+
+The header format is:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
+Header: 37+ bytes
+  Encryption starts with the flag byte.
+  +----+----+----+----+----+----+----+----+
+  |                  MAC                  |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |                   IV                  |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |flag|        time       |              |
+  +----+----+----+----+----+              +
+  | keying material (optional)            |
+  +                                       +
+  |                                       |
+  ~                                       ~
+  |                                       |
+  +                        +----+----+----+
+  |                        |#opt|         |
+  +----+----+----+----+----+----+         +
+  | #opt extended option bytes (optional) |
+  ~                                       ~
+  ~                                       ~
+  +----+----+----+----+----+----+----+----+
+{% endhighlight %}
+
+The flag byte contains the following bitfields:
+
+.. raw:: html
+
+  {% highlight %}
+  Bit order: 76543210 (bit 7 is MSB)
+
+    bits 7-4: payload type
+       bit 3: If 1, rekey data is included. Always 0, unimplemented
+       bit 2: If 1, extended options are included. Always 0 before release
+              0.9.24.
+    bits 1-0: reserved, set to 0 for compatibility with future uses
+{% endhighlight %}
+
+Without rekeying and extended options, the header size is 37 bytes.
+
+.. _rekey:
+
+Rekeying
+--------
+
+If the rekey flag is set, 64 bytes of keying material follow the timestamp.
+
+When rekeying, the first 32 bytes of the keying material is fed into a SHA256
+to produce the new MAC key, and the next 32 bytes are fed into a SHA256 to
+produce the new session key, though the keys are not immediately used.  The
+other side should also reply with the rekey flag set and that same keying
+material.  Once both sides have sent and received those values, the new keys
+should be used and the previous keys discarded.  It may be useful to keep the
+old keys around briefly, to address packet loss and reordering.
+
+NOTE: Rekeying is currently unimplemented.
+
+.. _extend:
+
+Extended Options
+----------------
+
+If the extended options flag is set, a one byte option size value is appended,
+followed by that many extended option bytes. Extended options have always been
+part of the specification, but were unimplemented until release 0.9.24. When
+present, the option format is specific to the message type. See message
+documentation below on whether extended options are expected for the given
+message, and the specified format. While Java routers have always recognized the
+flag and options length, other implementations have not. Therefore, do not send
+extended options to routers older than release 0.9.24.
+
+
+Padding
+=======
+
+All messages contain 0 or more bytes of padding.  Each message must be padded
+to a 16 byte boundary, as required by the AES256 encryption layer
+[CRYPTO-AES]_.
+
+Through release 0.9.7, messages were only padded to the next 16 byte boundary,
+and messages not a multiple of 16 bytes could possibly be invalid.
+
+As of release 0.9.7, messages may be padded to any length as long as the
+current MTU is honored.  Any extra 1-15 padding bytes beyond the last block of
+16 bytes cannot be encrypted or decrypted and will be ignored.  However, the
+full length and all padding is included in the MAC calculation.
+
+As of release 0.9.8, transmitted messages are not necessarily a multiple of 16
+bytes.  The SessionConfirmed message is an exception, see below.
+
+
+Keys
+====
+
+Signatures in the SessionCreated and SessionConfirmed messages are generated
+using the [SigningPublicKey]_ from the [RouterIdentity]_ which is distributed
+out-of-band by publishing in the network database, and the associated
+[SigningPrivateKey]_.
+
+Through release 0.9.15, the signature algorithm was always DSA, with a 40 byte
+signature.
+
+As of release 0.9.16, the signature algorithm may be specified by a a
+[KeyCertificate]_ in Bob's [RouterIdentity]_.
+
+Both introduction keys and session keys are 32 bytes, and are defined by the
+Common structures specification [SESSIONKEY]_.  The key used for the MAC and
+encryption is specified for each message below.
+
+Introduction keys are delivered through an external channel (the network
+database, where they are identical to the router Hash for now).
+
+
+Notes
+=====
+
+IPv6
+----
+
+The protocol specification allows both 4-byte IPv4 and 16-byte IPv6 addresses.
+SSU-over-IPv6 is supported as of version 0.9.8.  See the documentation of
+individual messages below for details on IPv6 support.
+
+.. _time:
+
+Timestamps
+----------
+
+While most of I2P uses 8-byte [Date]_ timestamps with millisecond resolution,
+SSU uses 4-byte unsigned integer timestamps with one-second resolution. Because
+these values are unsigned, they will not roll over until February 2106.
+
+
+Messages
+========
+
+There are 10 messages (payload types) defined:
+
+====  ================  =====
+Type      Message       Notes
+====  ================  =====
+  0   SessionRequest
+  1   SessionCreated
+  2   SessionConfirmed
+  3   RelayRequest
+  4   RelayResponse
+  5   RelayIntro
+  6   Data
+  7   PeerTest
+  8   SessionDestroyed  Implemented as of 0.8.9
+ n/a  HolePunch
+====  ================  =====
+
+.. _sessionRequest:
+
+SessionRequest (type 0)
+-----------------------
+
+This is the first message sent to establish a session.
+
+====================  ======================================================
+**Peer:**             Alice to Bob
+**Data:**             * 256 byte X, to begin the DH agreement
+                      * 1 byte IP address size
+                      * that many byte representation of Bob's IP address
+                      * N bytes, currently uninterpreted
+**Crypto Key used:**  Bob's introKey, as retrieved from the network database
+**MAC Key used:**     Bob's introKey, as retrieved from the network database
+====================  ======================================================
+
+Message format:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |         X, as calculated from DH      |
+  ~                .  .  .                ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |size| that many byte IP address (4-16) |
+  +----+----+----+----+----+----+----+----+
+  | arbitrary amount of uninterpreted data|
+  ~                .  .  .                ~
+{% endhighlight %}
+
+Typical size including header, in current implementation: 304 (IPv4) or 320
+(IPv6) bytes (before non-mod-16 padding)
+
+Extended options
+````````````````
+*Note: This is a proposal, to be implemented in 0.9.24. Subject to change.*
+
+* Minimum length: 3 (option length byte + 2 bytes)
+
+* Option length: 2 minimum
+
+* 2 bytes flags:
+
+.. raw:: html
+
+  {% highlight %}
+  Bit order: 15...76543210 (bit 15 is MSB)
+
+        bit 0: 1 for Alice to request a relay tag from Bob in the
+               `SessionCreated` response, 0 if Alice does not need a relay tag.
+               Note that "1" is the default if no extended options are present
+    bits 15-1: unused, set to 0 for compatibility with future uses
+{% endhighlight %}
+
+Notes
+`````
+* IPv4 and IPv6 addresses are supported.
+
+* The uninterpreted data could possibly be used in the future for challenges.
+
+.. _sessioncreated:
+
+SessionCreated (type 1)
+-----------------------
+
+This is the response to a SessionRequest_.
+
+====================  ==========================================================
+**Peer:**             Bob to Alice
+**Data:**             * 256 byte Y, to complete the DH agreement
+	              * 1 byte IP address size
+	              * that many byte representation of Alice's IP address
+	              * 2 byte Alice's port number
+                      * 4 byte relay (introduction) tag which Alice can publish
+                        (else 0x00000000)
+                      * 4 byte timestamp (seconds from the epoch) for use in the
+                        DSA signature
+                      * Bob's [Signature]_ of the critical exchanged data (X +
+                        Y + Alice's IP + Alice's port + Bob's IP + Bob's port +
+                        Alice's new relay tag + Bob's signed on time), encrypted
+                        with another layer of encryption using the negotiated
+                        sessionKey.  The IV is reused here. See notes for length
+                        information.
+                      * 0-15 bytes of padding of the signature, using random
+                        data, to a multiple of 16 bytes, so that the signature +
+                        padding may be encrypted with an additional layer of
+                        encryption using the negotiated session key as part of
+                        the DSA block.
+                      * N bytes, currently uninterpreted
+**Crypto Key used:**  Bob's introKey, with an additional layer of encryption
+                      over the 40 byte signature and the following 8 bytes
+                      padding.
+**MAC Key used:**     Bob's introKey
+====================  ==========================================================
+
+Message format:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |         Y, as calculated from DH      |
+  ~                .  .  .                ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |size| that many byte IP address (4-16) |
+  +----+----+----+----+----+----+----+----+
+  | Port (A)| public relay tag  |  signed
+  +----+----+----+----+----+----+----+----+
+    on time |                             |
+  +----+----+                             +
+  |                                       |
+  +                                       +
+  |             signature                 |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +         +----+----+----+----+----+----+
+  |         |   (0-15 bytes of padding) 
+  +----+----+----+----+----+----+----+----+
+            |                             |
+  +----+----+                             +
+  |           arbitrary amount            |
+  ~        of uninterpreted data          ~
+  ~                .  .  .                ~
+{% endhighlight %}
+
+Typical size including header, in current implementation: 368 bytes (IPv4 or
+IPv6) (before non-mod-16 padding)
+
+Notes
+`````
+* IPv4 and IPv6 addresses are supported.
+
+* If the relay tag is nonzero, Bob is offering to act as an introducer for
+  Alice. Alice may subsequently publish Bob's address and the relay tag in the
+  network database.
+
+* For the signature, Bob must use his external port, as that what Alice will
+  use to verify. If Bob's NAT/firewall has mapped his internal port to a
+  different external port, and Bob is unaware of it, the verification by Alice
+  will fail.
+
+* See the Keys_ section above for details on signatures. Alice already has
+  Bob's public signing key, from the network database.
+
+* Through release 0.9.15, the signature was always a 40 byte DSA signature and
+  the padding was always 8 bytes. As of release 0.9.16, the signature type and
+  length are implied by the type of the [SigningPublicKey]_ in Bob's
+  [RouterIdentity]_. The padding is as necessary to a multiple of 16 bytes.
+
+* This is the only message that uses the sender's intro key. All others use the
+  receiver's intro key or the established session key.
+
+* Signed-on time appears to be unused or unverified in the current
+  implementation.
+
+* The uninterpreted data could possibly be used in the future for challenges.
+
+* Extended options in the header: Not expected, undefined.
+
+.. _sessionconfirmed:
+
+SessionConfirmed (type 2)
+-------------------------
+
+This is the response to a SessionCreated_ message and the last step in
+establishing a session.  There may be multiple SessionConfirmed messages
+required if the Router Identity must be fragmented.
+
+====================  ==========================================================
+**Peer:**             Alice to Bob
+**Data:**             * 1 byte identity fragment info::
+
+                          Bit order: 76543210 (bit 7 is MSB)
+                          bits 7-4: current identity fragment # 0-14
+                          bits 3-0: total identity fragments (F) 1-15
+
+                      * 2 byte size of the current identity fragment
+                      * that many byte fragment of Alice's [RouterIdentity]_
+                      * After the last identity fragment only:
+
+                        * 4 byte signed-on time
+
+                      * N bytes padding, currently uninterpreted
+                      * After the last identity fragment only:
+
+                        * The remaining bytes contain Alice's [Signature]_ of
+                          the critical exchanged data (X + Y + Alice's IP +
+                          Alice's port + Bob's IP + Bob's port + Alice's new
+                          relay tag + Alice's signed on time). See notes for
+                          length information.
+**Crypto Key used:**  Alice/Bob sessionKey, as generated from the DH exchange
+**MAC Key used:**     Alice/Bob MAC Key, as generated from the DH exchange
+====================  ==========================================================
+
+**Fragment 0 through F-2** (only if F > 1; currently unused, see notes below):
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |info| cursize |                        |
+  +----+----+----+                        +
+  |      fragment of Alice's full         |
+  ~            Router Identity            ~
+  ~                .  .  .                ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | arbitrary amount of uninterpreted data|
+  ~                .  .  .                ~
+{% endhighlight %}
+ 
+**Fragment F-1 (last or only fragment):**
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |info| cursize |                        |
+  +----+----+----+                        +
+  |     last fragment of Alice's full     |
+  ~            Router Identity            ~
+  ~                .  .  .                ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |  signed on time   |                   |
+  +----+----+----+----+                   +
+  |  arbitrary amount of uninterpreted    |
+  ~      data, until the signature at     ~
+  ~       end of the current packet       ~
+  |  Packet length must be mult. of 16    |
+  +----+----+----+----+----+----+----+----+
+  +                                       +
+  |                                       |
+  +                                       +
+  |             signature                 |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+{% endhighlight %}
+ 
+Typical size including header, in current implementation: 480 bytes (before
+non-mod-16 padding)
+
+Notes
+`````
+* In the current implementation, the maximum fragment size is 512 bytes. This
+  should be extended so that longer signatures will work without fragmentation.
+  The current implementation does not correctly process signatures split across
+  two fragments.
+
+* The typical [RouterIdentity]_ is 387 bytes, so no fragmentation is ever
+  necessary. If new crypto extends the size of the RouterIdentity, the
+  fragmentation scheme must be tested carefully.
+
+* There is no mechanism for requesting or redelivering missing fragments.
+
+* The total fragments field F must be set identically in all fragments.
+
+* See the Keys_ section above for details on DSA signatures.
+
+* Signed-on time appears to be unused or unverified in the current
+  implementation.
+
+* Since the signature is at the end, the padding in the last or only packet
+  must pad the total packet to a multiple of 16 bytes, or the signature will
+  not get decrypted correctly. This is different from all the other message
+  types, where the padding is at the end.
+
+* Through release 0.9.15, the signature was always a 40 byte DSA signature. As
+  of release 0.9.16, the signature type and length are implied by the type of
+  the [SigningPublicKey]_ in Alice's [RouterIdentity]_. The padding is as
+  necessary to a multiple of 16 bytes.
+
+* Extended options in the header: Not expected, undefined.
+
+.. _sessiondestroyed:
+
+SessionDestroyed (type 8)
+-------------------------
+
+The SessionDestroyed message was implemented (reception only) in release 0.8.1,
+and is sent as of release 0.8.9.
+
+====================  ============================
+**Peer:**             Alice to Bob or Bob to Alice
+**Data:**             none
+**Crypto Key used:**  Alice/Bob sessionKey
+**MAC Key used:**     Alice/Bob MAC Key
+====================  ============================
+
+This message does not contain any data.  Typical size including header, in
+current implementation: 48 bytes (before non-mod-16 padding)
+
+Notes
+`````
+* Destroy messages received with the sender's or receiver's intro key will be
+  ignored.
+
+* Extended options in the header: Not expected, undefined.
+
+
+.. _relayrequest:
+
+RelayRequest (type 3)
+---------------------
+
+This is the first message sent from Alice to Bob to request an introduction to
+Charlie.
+
+====================  ==========================================================
+**Peer:**             Alice to Bob
+**Data:**             * 4 byte relay (introduction) tag, nonzero, as received by
+                        Alice in the SessionCreated_ message from Bob
+                      * 1 byte IP address size
+                      * that many byte representation of Alice's IP address
+                      * 2 byte port number (of Alice)
+                      * 1 byte challenge size
+                      * that many bytes to be relayed to Charlie in the intro
+                      * Alice's 32-byte introduction key (so Bob can reply with
+                        Charlie's info)
+                      * 4 byte nonce of Alice's relay request
+                      * N bytes, currently uninterpreted
+**Crypto Key used:**  Bob's introKey, as retrieved from the network database (or
+                      Alice/Bob sessionKey, if established)
+**MAC Key used:**     Bob's introKey, as retrieved from the network database (or
+                      Alice/Bob MAC Key, if established)
+====================  ==========================================================
+ 
+Message format:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |      relay tag    |size| Alice IP addr
+  +----+----+----+----+----+----+----+----+
+       | Port (A)|size| challenge bytes   |
+  +----+----+----+----+                   +
+  |      to be delivered to Charlie       |
+  +----+----+----+----+----+----+----+----+
+  | Alice's intro key                     |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |       nonce       |                   |
+  +----+----+----+----+                   +
+  | arbitrary amount of uninterpreted data|
+  ~                .  .  .                ~
+{% endhighlight %}
+
+Typical size including header, in current implementation: 96 bytes (no Alice IP
+included) or 112 bytes (4-byte Alice IP included) (before non-mod-16 padding)
+
+Notes
+`````
+* The IP address is only included if it is be different than the packet's
+  source address and port. In the current implementation, the IP length is
+  always 0 and the port is always 0, and the receiver should use the packet's
+  source address and port.
+
+* This message may be sent via IPv4 or IPv6. If IPv6, Alice must include her
+  IPv4 address and port.
+
+* If Alice includes her address/port, Bob may perform additional validation
+  before continuing.
+
+  * Prior to release 0.9.24, Java I2P rejected any address or port that was
+    different from the connection.
+
+* Challenge is unimplemented, challenge size is always zero
+
+* There are no plans to implement relaying for IPv6.
+
+* Prior to release 0.9.12, Bob's intro key was always used. As of release
+  0.9.12, the session key is used if there is an established session between
+  Alice and Bob. In practice, there must be an established session, as Alice
+  will only get the nonce (introduction tag) from the session created message,
+  and Bob will mark the introduction tag invalid once the session is destroyed.
+
+* Extended options in the header: Not expected, undefined.
+
+.. _relayresponse:
+
+RelayResponse (type 4)
+----------------------
+
+This is the response to a RelayRequest_ and is sent from Bob to Alice.
+
+====================  ==========================================================
+**Peer:**             Bob to Alice
+**Data:**             * 1 byte IP address size
+                      * that many byte representation of Charlie's IP address
+                      * 2 byte Charlie's port number
+                      * 1 byte IP address size
+                      * that many byte representation of Alice's IP address
+                      * 2 byte Alice's port number
+                      * 4 byte nonce sent by Alice
+                      * N bytes, currently uninterpreted
+**Crypto Key used:**  Alice's introKey, as received in the Relay Request (or
+                      Alice/Bob sessionKey, if established)
+**MAC Key used:**     Alice's introKey, as received in the Relay Request (or
+                      Alice/Bob MAC Key, if established)
+====================  ==========================================================
+
+Message format:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |size|    Charlie IP     | Port (C)|size|
+  +----+----+----+----+----+----+----+----+
+  |    Alice IP       | Port (A)|  nonce
+  +----+----+----+----+----+----+----+----+
+            |   arbitrary amount of       |
+  +----+----+                             +
+  |          uninterpreted data           |
+  ~                .  .  .                ~
+{% endhighlight %}
+
+Typical size including header, in current implementation: 64 (Alice IPv4) or 80
+(Alice IPv6) bytes (before non-mod-16 padding)
+
+Notes
+`````
+* This message may be sent via IPv4 or IPv6.
+
+* Alice's IP address/port are the apparent IP/port that Bob received the
+  RelayRequest on (not necessarily the IP Alice included in the RelayRequest),
+  and may be IPv4 or IPv6. Alice currently ignores these on receive.
+
+* Charlie's IP address must be IPv4, as that is the address that Alice will
+  send the SessionRequest to after the Hole Punch.
+
+* There are no plans to implement relaying for IPv6.
+
+* Prior to release 0.9.12, Alice's intro key was always used. As of release
+  0.9.12, the session key is used if there is an established session between
+  Alice and Bob.
+
+* Extended options in the header: Not expected, undefined.
+
+.. _relayintro:
+
+RelayIntro (type 5)
+-------------------
+
+This is the introduction for Alice, which is sent from Bob to Charlie.
+
+====================  =====================================================
+**Peer:**             Bob to Charlie
+**Data:**             * 1 byte IP address size
+                      * that many byte representation of Alice's IP address
+                      * 2 byte port number (of Alice)
+                      * 1 byte challenge size
+                      * that many bytes relayed from Alice
+                      * N bytes, currently uninterpreted
+**Crypto Key used:**  Bob/Charlie sessionKey
+**MAC Key used:**     Bob/Charlie MAC Key
+====================  =====================================================
+
+Message format:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |size|     Alice IP      | Port (A)|size|
+  +----+----+----+----+----+----+----+----+
+  |      that many bytes of challenge     |
+  +                                       +
+  |        data relayed from Alice        |
+  +----+----+----+----+----+----+----+----+
+  | arbitrary amount of uninterpreted data|
+  ~                .  .  .                ~
+{% endhighlight %}
+
+Typical size including header, in current implementation: 48 bytes (before
+non-mod-16 padding)
+
+Notes
+`````
+* Alice's IP address is always 4 bytes in the current implementation, because
+  Alice is trying to connect to Charlie via IPv4.
+
+* This message must be sent via an established IPv4 connection, as that's the
+  only way that Bob knows Charlie's IPv4 address to return to Alice in the
+  RelayResponse_.
+
+* Challenge is unimplemented, challenge size is always zero
+
+* Extended options in the header: Not expected, undefined.
+
+.. _data:
+
+Data (type 6)
+-------------
+
+This message is used for data transport and acknowledgment.
+
+====================  ==========================================================
+**Peer:**             Any
+**Data:**             * 1 byte flags::
+
+                          Bit order: 76543210 (bit 7 is MSB)
+                          bit 7: explicit ACKs included
+                          bit 6: ACK bitfields included
+                          bit 5: reserved
+                          bit 4: explicit congestion notification (ECN)
+                          bit 3: request previous ACKs
+                          bit 2: want reply
+                          bit 1: extended data included (unused, never set)
+                          bit 0: reserved
+
+                      * if explicit ACKs are included:
+
+	                * a 1 byte number of ACKs
+                        * that many 4 byte MessageIds being fully ACKed
+
+                      * if ACK bitfields are included:
+
+                        * a 1 byte number of ACK bitfields
+                        * that many 4 byte MessageIds + a 1 or more byte ACK
+                          bitfield. The bitfield uses the 7 low bits of each
+                          byte, with the high bit specifying whether an
+                          additional bitfield byte follows it (1 = true, 0 = the
+                          current bitfield byte is the last).  These sequence of
+                          7 bit arrays represent whether a fragment has been
+                          received - if a bit is 1, the fragment has been
+                          received.  To clarify, assuming fragments 0, 2, 5, and
+                          9 have been received, the bitfield bytes would be as
+                          follows::
+
+                              byte 0:
+                                 Bit order: 76543210 (bit 7 is MSB)
+                                 bit 7: 1 (further bitfield bytes follow)
+                                 bit 6: 0 (fragment 6 not received)
+                                 bit 5: 1 (fragment 5 received)
+                                 bit 4: 0 (fragment 4 not received)
+                                 bit 3: 0 (fragment 3 not received)
+                                 bit 2: 1 (fragment 2 received)
+                                 bit 1: 0 (fragment 1 not received)
+                                 bit 0: 1 (fragment 0 received)
+                              byte 1:
+                                 Bit order: 76543210 (bit 7 is MSB)
+                                 bit 7: 0 (no further bitfield bytes)
+                                 bit 6: 0 (fragment 13 not received)
+                                 bit 5: 0 (fragment 12 not received)
+                                 bit 4: 0 (fragment 11 not received)
+                                 bit 3: 0 (fragment 10 not received)
+                                 bit 2: 1 (fragment 9 received)
+                                 bit 1: 0 (fragment 8 not received)
+                                 bit 0: 0 (fragment 7 not received)
+
+                      * If extended data included:
+
+                        * 1 byte data size
+                        * that many bytes of extended data (currently
+                          uninterpreted)
+
+                      * 1 byte number of fragments (can be zero)
+                      * If nonzero, that many message fragments. Each fragment
+                        contains:
+
+                        * 4 byte messageId
+                        * 3 byte fragment info::
+
+                            Bit order: 76543210 (bit 7 is MSB)
+                            bits 23-17: fragment # 0 - 127
+                            bit 16: isLast (1 = true)
+                            bits 15-14: unused, set to 0 for compatibility with
+                                        future uses
+                            bits 13-0: fragment size 0 - 16383
+
+                        * that many bytes
+
+                      * N bytes padding, uninterpreted
+**Crypto Key used:**  Alice/Bob sessionKey
+**MAC Key used:**     Alice/Bob MAC Key
+====================  ==========================================================
+
+Message format:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |flag| (additional headers, determined  |
+  +----+                                  +
+  ~ by the flags, such as ACKs or         ~
+  | bitfields                             |
+  +----+----+----+----+----+----+----+----+
+  |#frg|     messageId     |   frag info  |
+  +----+----+----+----+----+----+----+----+
+  | that many bytes of fragment data      |
+  ~                .  .  .                ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |     messageId     |   frag info  |    |
+  +----+----+----+----+----+----+----+    +
+  | that many bytes of fragment data      |
+  ~                .  .  .                ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |     messageId     |   frag info  |    |
+  +----+----+----+----+----+----+----+    +
+  | that many bytes of fragment data      |
+  ~                .  .  .                ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  | arbitrary amount of uninterpreted data|
+  ~                .  .  .                ~
+{% endhighlight %}
+
+Notes
+`````
+* The current implementation adds a limited number of duplicate acks for
+  messages previously acked, if space is available.
+
+* If the number of fragments is zero, this is an ack-only or keepalive message.
+
+* The ECN feature is unimplemented, and the bit is never set.
+
+* In the current implementation, the want reply bit is set when the number of
+  fragments is greater then zero, and not set when there are no fragments.
+
+* Extended data is unimplemented and never present.
+
+* Reception of multiple fragments is supported in all releases. Transmission of
+  multiple fragments is implemented in release 0.9.16.
+
+* As currently implemented, maximum fragments is 64 (maximum fragment number =
+  63).
+
+* As currently implemented, maximum fragment size is of course less than the
+  MTU.
+
+* Take care not to exceed the maximum MTU even if there is a large number of
+  ACKs to send.
+
+* The protocol allows zero-length fragments but there's no reason to send them.
+
+* In SSU, the data uses a short 5-byte I2NP header followed by the payload of
+  the I2NP message instead of the standard 16-byte I2NP header. The short I2NP
+  header consists only of the one-byte I2NP type and 4-byte expiration in
+  seconds. The I2NP message ID is used as the message ID for the fragment. The
+  I2NP size is assembled from the fragment sizes. The I2NP checksum is not
+  required as UDP message integrity is ensured by decryption.
+
+* Message IDs are not sequence numbers and are not consecutive. SSU does not
+  guarantee in-order delivery. While we use the I2NP message ID as the SSU
+  message ID, from the SSU protocol view, they are random numbers. In fact,
+  since the router uses a single Bloom filter for all peers, the message ID
+  must be an actual random number.
+
+* Because there are no sequence numbers, there is no way to be sure an ACK was
+  received. The current implementation routinely sends a large amount of
+  duplicate ACKs. Duplicate ACKs should not be taken as an indication of
+  congestion.
+
+* ACK Bitfield notes: The receiver of a data packet does not know how many
+  fragments are in the message unless it has received the last fragment.
+  Therefore, the number of bitfield bytes sent in response may be less or more
+  than the number of fragments divided by 7. For example, if the highest
+  fragment the receiver has seen is number 4, only one byte is required to be
+  sent, even if there may be 13 fragments total. Up to 10 bytes (i.e. (64 / 7)
+  + 1) may be included for each message ID acked.
+
+* Extended options in the header: Not expected, undefined.
+
+.. _peertest:
+
+PeerTest (type 7)
+-----------------
+
+See [SSU-PEERTEST]_ for details.
+
+====================  ==========================================================
+**Peer:**             Any
+**Data:**             * 4 byte nonce
+                      * 1 byte IP address size (may be zero)
+                      * that many byte representation of Alice's IP address, if
+                        size > 0
+                      * 2 byte Alice's port number
+                      * Alice's or Charlie's 32-byte introduction key
+                      * N bytes, currently uninterpreted
+
+**Crypto Key used:**  Listed in order of occurrence:
+
+                      1. When sent from Alice to Bob: Alice/Bob sessionKey
+
+                         (The protocol also permits Bob's introKey if Alice and
+                         Bob do not have an established session, but in the
+                         current implementation Alice always selects a Bob that
+                         is established.  As of release 0.9.15, Bob will reject
+                         PeerTests from peers without an established session.)
+
+                      2. When sent from Bob to Charlie: Bob/Charlie sessionKey
+
+                      3. When sent from Charlie to Bob: Bob/Charlie sessionKey
+
+                      4. When sent from Bob to Alice: Alice's introKey, as
+                         received in the PeerTest message from Alice
+
+                      5. When sent from Charlie to Alice: Alice's introKey, as
+                         received in the PeerTest message from Bob
+
+                      6. When sent from Alice to Charlie: Charlie's introKey, as
+                         received in the PeerTest message from Charlie
+
+**MAC Key used:**     Listed in order of occurrence:
+
+                      1. When sent from Alice to Bob: Alice/Bob MAC Key
+
+                         (The protocol also permits Bob's introKey if Alice and
+                         Bob do not have an established session, but in the
+                         current implementation Alice always selects a Bob that
+                         is established. As of release 0.9.15, Bob will reject
+                         PeerTests from peers without an established session.)
+
+                      2. When sent from Bob to Charlie: Bob/Charlie MAC Key
+
+                      3. When sent from Charlie to Bob: Bob/Charlie MAC Key
+
+                      4. When sent from Bob to Alice: Alice's introKey, as
+                         received in the PeerTest message from Alice
+
+                      5. When sent from Charlie to Alice: Alice's introKey, as
+                         received in the PeerTest message from Bob
+
+                      6. When sent from Alice to Charlie: Charlie's introKey, as
+                         received in the PeerTest message from Charlie
+====================  ==========================================================
+
+Message format:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |    test nonce     |size| Alice IP addr
+  +----+----+----+----+----+----+----+----+
+       | Port (A)|                        |
+  +----+----+----+                        +
+  | Alice or Charlie's                    |
+  + introduction key (Alice's is sent to  +
+  | Bob and Charlie, while Charlie's is   |
+  + sent to Alice)                        +
+  |                                       |
+  +              +----+----+----+----+----+
+  |              | arbitrary amount of    |
+  +----+----+----+                        |
+  | uninterpreted data                    |
+  ~                .  .  .                ~
+{% endhighlight %}
+
+Typical size including header, in current implementation: 80 bytes (before
+non-mod-16 padding)
+
+Notes
+`````
+* When sent by Alice, IP address size is 0, IP address is not present, and port
+  is 0, as Bob and Charlie do not use the data; the point is to determine
+  Alice's true IP address/port and tell Alice; Bob and Charlie don't care what
+  Alice thinks her address is.
+
+* When sent by Bob or Charlie, IP and port are present, and IP address is
+  always 4 bytes in the current implementation. IPv6 testing is not currently
+  supported.
+
+* IPv6 Notes: Only testing of IPv4 addresses is supported. Therefore, all
+  Alice-Bob and Alice-Charlie communication must be via IPv4. Bob-Charlie
+  communication, however, may be via IPv4 or IPv6. Alice's address, when
+  specified in the PeerTest message, must be 4 bytes.
+
+* A peer must maintain a table of active test states (nonces). On reception of
+  a PeerTest message, look up the nonce in the table. If found, it's an
+  existing test and you know your role (Alice, Bob, or Charlie). Otherwise, if
+  the IP is not present and the port is 0, this is a new test and you are Bob.
+  Otherwise, this is a new test and you are Charlie.
+
+* As of release 0.9.15, Alice must have an established session with Bob and use
+  the session key.
+
+* Extended options in the header: Not expected, undefined.
+
+HolePunch
+---------
+
+A HolePunch is simply a UDP packet with no data.  It is unauthenticated and
+unencrypted.  It does not contain a SSU header, so it does not have a message
+type number.  It is sent from Charlie to Alice as a part of the Introduction
+sequence.
+
+
+.. _sampledatagrams:
+
+Sample datagrams
+================
+
+Minimal data message
+--------------------
+
+* no fragments, no ACKs, no NACKs, etc
+* Size: 39 bytes
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |                  MAC                  |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |                   IV                  |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |flag|        time       |flag|#frg|    |
+  +----+----+----+----+----+----+----+    +
+  |  padding to fit a full AES256 block   |
+  +----+----+----+----+----+----+----+----+
+{% endhighlight %}
+
+Minimal data message with payload
+---------------------------------
+
+* Size: 46+fragmentSize bytes
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |                  MAC                  |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |                   IV                  |
+  +                                       +
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |flag|        time       |flag|#frg|
+  +----+----+----+----+----+----+----+----+
+    messageId    |   frag info  |         |
+  ----+----+----+----+----+----+         +
+  | that many bytes of fragment data      |
+  ~                .  .  .                ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+{% endhighlight %}
+
+
+References
+==========
+
+.. [CRYPTO-AES]
+    {{ site_url('docs/how/cryptography', True) }}#AES
+
+.. [CRYPTO-ELG]
+    {{ site_url('docs/how/cryptography', True) }}#elgamal
+
+.. [CRYPTO-HMAC]
+    {{ site_url('docs/how/cryptography', True) }}#udp
+
+.. [Date]
+    {{ ctags_url('Date') }}
+
+.. [I2P-SRC]
+    https://github.com/i2p/i2p.i2p
+
+.. [I2PCPP-SRC]
+    http://{{ i2pconv('git.repo.i2p') }}/w/i2pcpp.git
+
+.. [I2PD-SRC]
+    https://github.com/PurpleI2P/i2pd
+
+.. [KeyCertificate]
+    {{ spec_url('common-structures') }}#key-certificates
+
+.. [RouterIdentity]
+    {{ ctags_url('RouterIdentity') }}
+
+.. [SESSIONKEY]
+    {{ ctags_url('SessionKey') }}
+
+.. [Signature]
+    {{ ctags_url('Signature') }}
+
+.. [SigningPrivateKey]
+    {{ ctags_url('SigningPrivateKey') }}
+
+.. [SigningPublicKey]
+    {{ ctags_url('SigningPublicKey') }}
+
+.. [SSU]
+    {{ site_url('docs/transport/ssu', True) }}
+
+.. [SSU-KEYS]
+    {{ site_url('docs/transport/ssu', True) }}#keys
+
+.. [SSU-PEERTEST]
+    {{ site_url('docs/transport/ssu', True) }}#peerTesting
diff --git a/i2p2www/spec/streaming.rst b/i2p2www/spec/streaming.rst
new file mode 100644
index 0000000000000000000000000000000000000000..77b82ab5d9ebcd7a22cf4cba6f6e9d4cec6f708b
--- /dev/null
+++ b/i2p2www/spec/streaming.rst
@@ -0,0 +1,193 @@
+===============================
+Streaming Library Specification
+===============================
+.. meta::
+    :lastupdated: June 2015
+    :accuratefor: 0.9.20
+
+.. contents::
+
+
+Overview
+========
+
+See [STREAMING]_ for an overview of the Streaming Library.
+
+
+Protocol Specification
+======================
+
+Packet Format
+-------------
+
+The format of a single packet in the streaming protocol is:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  | send Stream ID    | rcv Stream ID     |
+  +----+----+----+----+----+----+----+----+
+  | sequence  Num     | ack Through       |
+  +----+----+----+----+----+----+----+----+
+  | nc |   NACKs ...
+  +----+----+----+----+----+----+----+----+
+       | rd |  flags  | opt size| opt data
+  +----+----+----+----+----+----+----+----+
+     ...                                  |
+  +----+----+----+----+----+----+----+----+
+  |   payload ...
+  +----+----+----+-//
+
+  sendStreamId :: 4 byte `Integer`
+                  Random number selected by the packet recipient before sending
+                  the first SYN reply packet and constant for the life of the
+                  connection. 0 in the SYN message sent by the connection
+                  originator, and in subsequent messages, until a SYN reply is
+                  received, containing the peer's stream ID.
+
+  receiveStreamId :: 4 byte `Integer`
+                     Random number selected by the packet originator before
+                     sending the first SYN packet and constant for the life of
+                     the connection. May be 0 if unknown, for example in a RESET
+                     packet.
+
+  sequenceNum :: 4 byte `Integer`
+                 The sequence for this message, starting at 0 in the SYN
+                 message, and incremented by 1 in each message except for plain
+                 ACKs and retransmissions. If the sequenceNum is 0 and the SYN
+                 flag is not set, this is a plain ACK packet that should not be
+                 ACKed.
+
+  ackThrough :: 4 byte `Integer`
+                The highest packet sequence number that was received on the
+                $receiveStreamId. This field is ignored on the initial
+                connection packet (where $receiveStreamId is the unknown id) or
+                if the NO_ACK flag set. All packets up to and including this
+                sequence number are ACKed, EXCEPT for those listed in NACKs
+                below.
+
+  NACK count :: 1 byte `Integer`
+                The number of 4-byte NACKs in the next field
+
+  NACKs :: $nc * 4 byte `Integer`s
+           Sequence numbers less than ackThrough that are not yet received. Two
+           NACKs of a packet is a request for a 'fast retransmit' of that packet.
+
+  resendDelay :: 1 byte `Integer`
+                 How long is the creator of this packet going to wait before
+                 resending this packet (if it hasn't yet been ACKed).  The value
+                 is seconds since the packet was created. Currently ignored on
+                 receive.
+
+  flags :: 2 byte value
+           See below.
+
+  option size :: 2 byte `Integer`
+                 The number of bytes in the next field
+
+  option data :: 0 or more bytes
+                 As specified by the flags. See below.
+
+  payload :: remaining packet size
+{% endhighlight %}
+
+Flags and Option Data Fields
+----------------------------
+
+The flags field above specifies some metadata about the packet, and in turn may
+require certain additional data to be included.  The flags are as follows. Any
+data structures specified must be added to the options area in the given order.
+
+Bit order: 15....0 (15 is MSB)
+
+=====  ========================  ============  ===============  ===============================================================
+ Bit             Flag            Option Order    Option Data    Function
+=====  ========================  ============  ===============  ===============================================================
+  0    SYNCHRONIZE                    --             --         Similar to TCP SYN. Set in the initial packet and in the first
+                                                                response. FROM_INCLUDED and SIGNATURE_INCLUDED must also be
+                                                                set.
+
+  1    CLOSE                          --             --         Similar to TCP FIN. If the response to a SYNCHRONIZE fits in a
+                                                                single message, the response will contain both SYNCHRONIZE and
+                                                                CLOSE. SIGNATURE_INCLUDED must also be set.
+
+  2    RESET                          --             --         Abnormal close. SIGNATURE_INCLUDED must also be set. Prior to
+                                                                release 0.9.20, due to a bug, FROM_INCLUDED must also be set.
+
+  3    SIGNATURE_INCLUDED              4       variable length  Currently sent only with SYNCHRONIZE, CLOSE, and RESET, where
+                                               [Signature]_     it is required, and with ECHO, where it is required for a
+                                                                ping. The signature uses the Destination's [SigningPrivateKey]_
+                                                                to sign the entire header and payload with the space in the
+                                                                option data field for the signature being set to all zeroes.
+
+                                                                Prior to release 0.9.11, the signature was always 40 bytes. As
+                                                                of release 0.9.11, the signature may be variable-length, see
+                                                                below for details.
+
+  4    SIGNATURE_REQUESTED            --             --         Unused. Requests every packet in the other direction to have
+                                                                SIGNATURE_INCLUDED
+
+  5    FROM_INCLUDED                   2       387+ byte        Currently sent only with SYNCHRONIZE, where it is required, and
+                                               [Destination]_   with ECHO, where it is required for a ping. Prior to release
+                                                                0.9.20, due to a bug, must also be sent with RESET.
+
+  6    DELAY_REQUESTED                 1       2 byte           Optional delay. How many milliseconds the sender of this packet
+                                               [Integer]_       wants the recipient to wait before sending any more data. A
+                                                                value greater than 60000 indicates choking.
+
+  7    MAX_PACKET_SIZE_INCLUDED        3       2 byte           Currently sent with SYNCHRONIZE only. Was also sent in
+                                               [Integer]_       retransmitted packets until release 0.9.1.
+
+  8    PROFILE_INTERACTIVE            --             --         Unused or ignored; the interactive profile is unimplemented.
+
+  9    ECHO                           --             --         Unused except by ping programs. If set, most other options are
+                                                                ignored. See the streaming docs [STREAMING]_.
+
+ 10    NO_ACK                         --             --         This flag simply tells the recipient to ignore the ackThrough
+                                                                field in the header. Currently set in the inital SYN packet,
+                                                                otherwise the ackThrough field is always valid. Note that this
+                                                                does not save any space, the ackThrough field is before the
+                                                                flags and is always present.
+
+11-15  unused                                                   Set to zero for compatibility with future uses.
+=====  ========================  ============  ===============  ===============================================================
+
+Variable Length Signature Notes
+```````````````````````````````
+Prior to release 0.9.11, the signature in the option field was always 40 bytes.
+As of release 0.9.11, the signature is variable length.  The Signature type and
+length are inferred from the type of key used in the FROM_INCLUDED option and
+the [Signature]_ documentation.
+
+* When a packet contains both FROM_INCLUDED and SIGNATURE_INCLUDED (as in
+  SYNCHRONIZE), the inference may be made directly.
+
+* When a packet does not contain FROM_INCLUDED, the inference must be made from
+  a previous SYNCHRONIZE packet.
+
+* When a packet does not contain FROM_INCLUDED, and there was no previous
+  SYNCHRONIZE packet (for example a stray CLOSE or RESET packet), the inference
+  can be made from the length of the remaining options (since
+  SIGNATURE_INCLUDED is the last option), but the packet will probably be
+  discarded anyway, since there is no FROM available to validate the signature.
+  If more option fields are defined in the future, they must be accounted for.
+
+
+References
+==========
+
+.. [Destination]
+    {{ ctags_url('Destination') }}
+
+.. [Integer]
+    {{ ctags_url('Integer') }}
+
+.. [Signature]
+    {{ ctags_url('Signature') }}
+
+.. [SigningPrivateKey]
+    {{ ctags_url('SigningPrivateKey') }}
+
+.. [STREAMING]
+    {{ site_url('docs/api/streaming', True) }}
diff --git a/i2p2www/spec/tunnel-creation.rst b/i2p2www/spec/tunnel-creation.rst
new file mode 100644
index 0000000000000000000000000000000000000000..7ba79c1ae5fb5e3a256d976799694835e125a65b
--- /dev/null
+++ b/i2p2www/spec/tunnel-creation.rst
@@ -0,0 +1,365 @@
+=============================
+Tunnel Creation Specification
+=============================
+.. meta::
+    :lastupdated: January 2016
+    :accuratefor: 0.9.24
+
+.. contents::
+
+
+.. _tunnelCreate.overview:
+
+Overview
+========
+
+This document specifies the details of the encrypted tunnel build messages used
+to create tunnels using a "non-interactive telescoping" method.  See the tunnel
+build document [TUNNEL-IMPL]_ for an overview of the process, including peer
+selection and ordering methods.
+
+The tunnel creation is accomplished by a single message passed along the path
+of peers in the tunnel, rewritten in place, and transmitted back to the tunnel
+creator.  This single tunnel message is made up of a variable number of records
+(up to 8) - one for each potential peer in the tunnel.   Individual records are
+asymmetrically (ElGamal [CRYPTO-ELG]_) encrypted to be read only by a specific
+peer along the path, while an additional symmetric layer of encryption (AES
+[CRYPTO-AES]_) is added at each hop so as to expose the asymmetrically
+encrypted record only at the appropriate time.
+
+.. _number:
+
+Number of Records
+-----------------
+
+Not all records must contain valid data.  The build message for a 3-hop tunnel,
+for example, may contain more records to hide the actual length of the tunnel
+from the participants.  There are two build message types. The original Tunnel
+Build Message ([TBM]_) contains 8 records, which is more than enough for any
+practical tunnel length.  The newer Variable Tunnel Build Message ([VTBM]_)
+contains 1 to 8 records. The originator may trade off the size of the message
+with the desired amount of tunnel length obfuscation.
+
+In the current network, most tunnels are 2 or 3 hops long.  The current
+implementation uses a 5-record VTBM to build tunnels of 4 hops or less, and the
+8-record TBM for longer tunnels.  The 5-record VTBM (which, when fragmented,
+fits in three 1KB tunnel messaages) reduces network traffic and increases
+build sucess rate, because smaller messages are less likely to be dropped.
+
+The reply message must be the same type and length as the build message.
+
+.. _tunnelCreate.requestRecord:
+
+Request Record Specification
+----------------------------
+
+Also specified in the I2NP Specification [BRR]_.
+
+Cleartext of the record, visible only to the hop being asked::
+
+  bytes     0-3: tunnel ID to receive messages as
+  bytes    4-35: local router identity hash
+  bytes   36-39: next tunnel ID
+  bytes   40-71: next router identity hash
+  bytes  72-103: AES-256 tunnel layer key
+  bytes 104-135: AES-256 tunnel IV key
+  bytes 136-167: AES-256 reply key
+  bytes 168-183: AES-256 reply IV
+  byte      184: flags
+  bytes 185-188: request time (in hours since the epoch, rounded down)
+  bytes 189-192: next message ID
+  bytes 193-221: uninterpreted / random padding
+
+The next tunnel ID and next router identity hash fields are used to specify the
+next hop in the tunnel, though for an outbound tunnel endpoint, they specify
+where the rewritten tunnel creation reply message should be sent.  In addition,
+the next message ID specifies the message ID that the message (or reply) should
+use.
+
+The tunnel layer key, tunnel IV key, reply key, and reply IV are each random
+32-byte values generated by the creator, for use in this build request record
+only.
+
+The flags field contains the following::
+
+ Bit order: 76543210 (bit 7 is MSB)
+ bit 7: if set, allow messages from anyone
+ bit 6: if set, allow messages to anyone, and send the reply to the
+        specified next hop in a Tunnel Build Reply Message
+ bits 5-0: Undefined, must set to 0 for compatibility with future options
+
+Bit 7 indicates that the hop will be an inbound gateway (IBGW).  Bit 6
+indicates that the hop will be an outbound endpoint (OBEP).  If neither bit is
+set, the hop will be an intermediate participant.  Both cannot be set at once.
+
+Request Record Creation
+```````````````````````
+Every hop gets a random Tunnel ID.
+The current and next-hop Tunnel IDs are filled in.
+Every record gets a random tunnel IV key, reply IV, layer key, and reply key.
+
+.. _encryption:
+
+Request Record Encryption
+`````````````````````````
+That cleartext record is ElGamal 2048 encrypted [CRYPTO-ELG]_ with the hop's
+public encryption key and formatted into a 528 byte record::
+
+  bytes   0-15: First 16 bytes of the SHA-256 of the current hop's router identity
+  bytes 16-527: ElGamal-2048 encrypted request record
+
+In the 512-byte encrypted record, the ElGamal data contains bytes 1-256 and
+258-513 of the 514-byte ElGamal encrypted block [CRYPTO-ELG]_.  The two padding
+bytes from the block (the zero bytes at locations 0 and 257) are removed.
+
+Since the cleartext uses the full field, there is no need for additional
+padding beyond ``SHA256(cleartext) + cleartext``.
+
+Each 528-byte record is then iteratively encrypted (using AES decryption, with
+the reply key and reply IV for each hop) so that the router identity will only
+be in cleartext for the hop in question.
+
+.. _tunnelCreate.hopProcessing:
+
+Hop Processing and Encryption
+-----------------------------
+
+When a hop receives a TunnelBuildMessage, it looks through the records
+contained within it for one starting with their own identity hash (trimmed to
+16 bytes).  It then decrypts the ElGamal block from that record and retrieves
+the protected cleartext.  At that point, they make sure the tunnel request is
+not a duplicate by feeding the AES-256 reply key into a Bloom filter.
+Duplicates or invalid requests are dropped. Records that are not stamped with
+the current hour, or the previous hour if shortly after the top of the hour,
+must be dropped. For example, take the hour in the timestamp, convert to a full
+time, then if it's more than 65 minutes behind or 5 minutes ahead of the current
+time, it is invalid. The Bloom filter must have a duration of at least one hour
+(plus a few minutes, to allow for clock skew), so that duplicate records in the
+current hour that are not rejected by checking the hour timestamp in the record,
+will be rejected by the filter.
+
+After deciding whether they will agree to participate in the tunnel or not,
+they replace the record that had contained the request with an encrypted reply
+block.  All other records are AES-256 encrypted [CRYPTO-AES]_ with the included
+reply key and IV. Each is AES/CBC encrypted separately with the same reply key
+and reply IV.  The CBC mode is not continued (chained) across records.
+
+Each hop knows only its own response.  If it agrees, it will maintain the
+tunnel until expiration, even if it will not be used, as it cannot know whether
+all other hops agreed.
+
+.. _tunnelCreate.replyRecord:
+
+Reply Record Specification
+``````````````````````````
+After the current hop reads their record, they replace it with a reply record
+stating whether or not they agree to participate in the tunnel, and if they do
+not, they classify their reason for rejection.  This is simply a 1 byte value,
+with 0x0 meaning they agree to participate in the tunnel, and higher values
+meaning higher levels of rejection.
+
+The following rejection codes are defined:
+
+* TUNNEL_REJECT_PROBABALISTIC_REJECT = 10
+* TUNNEL_REJECT_TRANSIENT_OVERLOAD = 20
+* TUNNEL_REJECT_BANDWIDTH = 30
+* TUNNEL_REJECT_CRIT = 50
+
+To hide other causes, such as router shutdown, from peers, the current
+implementation uses TUNNEL_REJECT_BANDWIDTH for almost all rejections.
+
+The reply is encrypted with the AES session key delivered to it in the
+encrypted block, padded with 495 bytes of random data to reach the full record
+size.  The padding is placed before the status byte::
+
+  AES-256-CBC(SHA-256(padding+status) + padding + status, key, IV)
+
+  bytes   0-31 : SHA-256 of bytes 32-527
+  bytes 32-526 : Random padding
+  byte 527     : Reply value
+
+This is also described in the I2NP spec [BRR]_.
+
+.. _tunnelCreate.requestPreparation:
+
+Tunnel Build Message Preparation
+--------------------------------
+
+When building a new Tunnel Build Message, all of the Build Request Records must
+first be built and asymmetrically encrypted using ElGamal [CRYPTO-ELG]_.  Each
+record is then premptively decrypted with the reply keys and IVs of the hops
+earlier in the path, using AES [CRYPTO-AES]_.  That decryption should be run in
+reverse order so that the asymmetrically encrypted data will show up in the
+clear at the right hop after their predecessor encrypts it.
+
+The excess records not needed for individual requests are simply filled with
+random data by the creator.
+
+.. _tunnelCreate.requestDelivery:
+
+Tunnel Build Message Delivery
+-----------------------------
+
+For outbound tunnels, the delivery is done directly from the tunnel creator to
+the first hop, packaging up the TunnelBuildMessage as if the creator was just
+another hop in the tunnel.  For inbound tunnels, the delivery is done through
+an existing outbound tunnel.  The outbound tunnel is generally from the same
+pool as the new tunnel being built.  If no outbound tunnel is available in that
+pool, an outbound exploratory tunnel is used.  At startup, when no outbound
+exploratory tunnel exists yet, a fake 0-hop outbound tunnel is used.
+
+.. _tunnelCreate.endpointHandling:
+
+Tunnel Build Message Endpoint Handling
+--------------------------------------
+
+For creation of an outbound tunnel, when the request reaches an outbound
+endpoint (as determined by the 'allow messages to anyone' flag), the hop is
+processed as usual, encrypting a reply in place of the record and encrypting
+all of the other records, but since there is no 'next hop' to forward the
+TunnelBuildMessage on to, it instead places the encrypted reply records into a
+TunnelBuildReplyMessage ([TBRM]_) or VariableTunnelBuildReplyMessage ([VTBRM]_)
+(the type of message and number of records must match that of the request) and
+delivers it to the reply tunnel specified within the request record.  That
+reply tunnel forwards the Tunnel Build Reply Message back to the tunnel
+creator, just as for any other message [TUNNEL-OP]_.  The tunnel creator then
+processes it, as described below.
+
+The reply tunnel was selected by the creator as follows: Generally it is an
+inbound tunnel from the same pool as the new outbound tunnel being built.  If
+no inbound tunnel is available in that pool, an inbound exploratory tunnel is
+used.  At startup, when no inbound exploratory tunnel exists yet, a fake 0-hop
+inbound tunnel is used.
+
+For creation of an inbound tunnel, when the request reaches the inbound
+endpoint (also known as the tunnel creator), there is no need to generate an
+explicit Tunnel Build Reply Message, and the router processes each of the
+replies, as below.
+
+.. _tunnelCreate.replyProcessing:
+
+Tunnel Build Reply Message Processing
+-------------------------------------
+
+To process the reply records, the creator simply has to AES decrypt each record
+individually, using the reply key and IV of each hop in the tunnel after the
+peer (in reverse order).  This then exposes the reply specifying whether they
+agree to participate in the tunnel or why they refuse.  If they all agree, the
+tunnel is considered created and may be used immediately, but if anyone
+refuses, the tunnel is discarded.
+
+The agreements and rejections are noted in each peer's profile
+[PEER-SELECTION]_, to be used in future assessments of peer tunnel capacity.
+
+
+.. _tunnelCreate.notes:
+
+History and Notes
+=================
+
+This strategy came about during a discussion on the I2P mailing list between
+Michael Rogers, Matthew Toseland (toad), and jrandom regarding the predecessor
+attack.  See [TUNBUILD-SUMMARY]_, [TUNBUILD-REASONING]_.  It was introduced in
+release 0.6.1.10 on 2006-02-16, which was the last time a
+non-backward-compatible change was made in I2P.
+
+Notes:
+
+* This design does not prevent two hostile peers within a tunnel from
+  tagging one or more request or reply records to detect that they are within
+  the same tunnel, but doing so can be detected by the tunnel creator when
+  reading the reply, causing the tunnel to be marked as invalid.
+
+* This design does not include a proof of work on the asymmetrically
+  encrypted section, though the 16 byte identity hash could be cut in half with
+  the latter replaced by a hashcash function of up to 2^64 cost.
+
+* This design alone does not prevent two hostile peers within a tunnel from
+  using timing information to determine whether they are in the same tunnel.
+  The use of batched and synchronized request delivery could help (batching up
+  requests and sending them off on the (ntp-synchronized) minute).  However,
+  doing so lets peers 'tag' the requests by delaying them and detecting the
+  delay later in the tunnel, though perhaps dropping requests not delivered in
+  a small window would work (though doing that would require a high degree of
+  clock synchronization).  Alternately, perhaps individual hops could inject a
+  random delay before forwarding on the request?
+
+* Are there any nonfatal methods of tagging the request?
+
+* The timestamp with a one-hour resolution is used for replay prevention. The
+  constraint was not enforced until release 0.9.16.
+
+
+.. _future:
+
+Future Work
+===========
+
+* In the current implementation, the originator leaves one record empty
+  for itself. Thus a message of n records can only build a tunnel of n-1 hops.
+  This appears to be necessary for inbound tunnels (where the next-to-last hop
+  can see the hash prefix for the next hop), but not for outbound tunnels.
+  This is to be researched and verified.  If it is possible to use the
+  remaining record without compromising anonymity, we should do so.
+
+* Further analysis of possible tagging and timing attacks described in the
+  above notes.
+
+* Use only VTBM; do not select old peers that don't support it.
+
+* The Build Request Record does not specify a tunnel lifetime or expiration;
+  each hop expires the tunnel after 10 minutes, which is a network-wide
+  hardcoded constant.  We could use a bit in the flag field and take 4 (or 8)
+  bytes out of the padding to specify a lifetime or expiration.  The requestor
+  would only specify this option if all participants supported it.
+
+
+.. _ref:
+
+References
+==========
+
+.. [BRR]
+    {{ ctags_url('BuildRequestRecord') }}
+
+.. [CRYPTO-AES]
+    {{ site_url('docs/how/cryptography', True) }}#AES
+
+.. [CRYPTO-ELG]
+    {{ site_url('docs/how/cryptography', True) }}#elgamal
+
+.. [HASHING-IT-OUT]
+    http://www-users.cs.umn.edu/~hopper/hashing_it_out.pdf
+
+.. [PEER-SELECTION]
+    {{ site_url('docs/how/peer-selection', True) }}
+
+.. [PREDECESSOR]
+    http://forensics.umass.edu/pubs/wright-tissec.pdf
+
+.. [PREDECESSOR-2008]
+    http://forensics.umass.edu/pubs/wright.tissec.2008.pdf
+
+.. [TBM]
+    {{ ctags_url('TunnelBuild') }}
+
+.. [TBRM]
+    {{ ctags_url('TunnelBuildReply') }}
+
+.. [TUNBUILD-REASONING]
+    http://osdir.com/ml/network.i2p/2005-10/msg00129.html
+
+.. [TUNBUILD-SUMMARY]
+    http://osdir.com/ml/network.i2p/2005-10/msg00138.html
+
+.. [TUNNEL-IMPL]
+    {{ site_url('docs/tunnels/implementation', True) }}
+
+.. [TUNNEL-OP]
+    {{ site_url('docs/tunnels/implementation', True) }}#tunnel.operation
+
+.. [VTBM]
+    {{ ctags_url('VariableTunnelBuild') }}
+
+.. [VTBRM]
+    {{ ctags_url('VariableTunnelBuildReply') }}
diff --git a/i2p2www/spec/tunnel-message.rst b/i2p2www/spec/tunnel-message.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c53d3345ce450b661dfb5d779af855583cad5ed3
--- /dev/null
+++ b/i2p2www/spec/tunnel-message.rst
@@ -0,0 +1,353 @@
+============================
+Tunnel Message Specification
+============================
+.. meta::
+    :lastupdated: February 2014
+    :accuratefor: 0.9.11
+
+.. contents::
+
+
+Overview
+========
+
+This document specifies the format of tunnel messages.  For general information
+about tunnels see the tunnel documentation [TUNNEL-IMPL]_.
+
+
+Message preprocessing
+=====================
+
+A *tunnel gateway* is the entrance, or first hop, of a tunnel.  For an outbound
+tunnel, the gateway is the creator of the tunnel.  For an inbound tunnel, the
+gateway is at the opposite end from the creator of the tunnel.
+
+A gateway *preprocesses* [I2NP]_ messages by fragmenting and combining them
+into tunnel messages.
+
+While I2NP messages are variable size from 0 to almost 64 KB, tunnel messages
+are fixed-size, approximately 1 KB.  Fixed message size restricts several types
+of attacks that are possible from observing message size.
+
+After the tunnel messages are created, they are encrypted as described in the
+tunnel documentation [TUNNEL-IMPL]_.
+
+.. _msg-Tunnel:
+
+Tunnel Message (Encrypted)
+--------------------------
+
+These are the contents of a tunnel data message after encryption.
+
+.. raw:: html
+
+  {% highlight lang='dataspec' -%}
++----+----+----+----+----+----+----+----+
+  |    Tunnel ID      |       IV          |
+  +----+----+----+----+                   +
+  |                                       |
+  +                   +----+----+----+----+
+  |                   |                   |
+  +----+----+----+----+                   +
+  |                                       |
+  +           Encrypted Data              +
+  ~                                       ~
+  |                                       |
+  +                   +-------------------+
+  |                   |
+  +----+----+----+----+
+
+  Tunnel ID :: `TunnelId`
+         4 bytes
+         the ID of the next hop
+
+  IV ::
+         16 bytes
+         the initialization vector
+
+  Encrypted Data ::
+         1008 bytes
+         the encrypted tunnel message
+
+  total size: 1028 Bytes
+{% endhighlight %}
+
+Tunnel Message (Decrypted)
+--------------------------
+
+These are the contents of a tunnel data message when decrypted.
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |    Tunnel ID      |       IV          |
+  +----+----+----+----+                   +
+  |                                       |
+  +                   +----+----+----+----+
+  |                   |     Checksum      |
+  +----+----+----+----+----+----+----+----+
+  |          nonzero padding...           |
+  ~                                       ~
+  |                                       |
+  +                                  +----+
+  |                                  |zero|
+  +----+----+----+----+----+----+----+----+
+  |                                       |
+  |       Delivery Instructions  1        |
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |                                       |
+  +       I2NP Message Fragment 1         +
+  |                                       |
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |                                       |
+  |       Delivery Instructions 2...      |
+  ~                                       ~
+  |                                       |
+  +----+----+----+----+----+----+----+----+
+  |                                       |
+  +       I2NP Message Fragment 2...      +
+  |                                       |
+  ~                                       ~
+  |                                       |
+  +                   +-------------------+
+  |                   |
+  +----+----+----+----+
+
+  Tunnel ID :: `TunnelId`
+         4 bytes
+         the ID of the next hop
+
+  IV ::
+         16 bytes
+         the initialization vector
+
+  Checksum ::
+         4 bytes
+         the first 4 bytes of the SHA256 hash of (the contents of the message
+         (after the zero byte) + IV)
+
+  Nonzero padding ::
+         0 or more bytes
+         random nonzero data for padding
+
+  Zero ::
+         1 byte
+         the value 0x00
+
+  Delivery Instructions :: `TunnelMessageDeliveryInstructions`
+         length varies but is typically 7, 39, 43, or 47 bytes
+         Indicates the fragment and the routing for the fragment
+
+  Message Fragment ::
+         1 to 996 bytes, actual maximum depends on delivery instruction size
+         A partial or full I2NP Message
+
+  total size: 1028 Bytes
+{% endhighlight %}
+
+Notes
+`````
+* The padding, if any, must be before the instruction/message pairs.
+  There is no provision for padding at the end.
+
+* The checksum does NOT cover the padding or the zero byte.
+  Take the message starting at the first delivery instructions, concatenate the
+  IV, and take the Hash of that.
+
+
+.. _struct-TunnelMessageDeliveryInstructions:
+
+Tunnel Message Delivery Instructions
+====================================
+
+The instructions are encoded with a single control byte, followed by any
+necessary additional information.  The first bit (MSB) in that control byte
+determines how the remainder of the header is interpreted - if it is not set,
+the message is either not fragmented or this is the first fragment in the
+message.  If it is set, this is a follow on fragment.
+
+This specification is for Delivery Instructions inside Tunnel Messages only.
+Note that "Delivery Instructions" are also used inside Garlic Cloves
+[I2NP-GC]_, where the format is significantly different.  See the I2NP
+documentation [I2NP-GCDI]_ for details.  Do NOT use the following specification
+for Garlic Clove Delivery Instructions!
+
+First Fragment Delivery Instructions
+------------------------------------
+
+If the MSB of the first byte is 0, this is an initial I2NP message fragment,
+or a complete (unfragmented) I2NP message, and the instructions are:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+----+
+  |flag|  Tunnel ID (opt)  |              |
+  +----+----+----+----+----+              +
+  |                                       |
+  +                                       +
+  |         To Hash (optional)            |
+  +                                       +
+  |                                       |
+  +                        +--------------+
+  |                        |dly | Message  
+  +----+----+----+----+----+----+----+----+
+   ID (opt) |extended opts (opt)|  size   |
+  +----+----+----+----+----+----+----+----+
+
+  flag ::
+         1 byte
+         Bit order: 76543210
+         bit 7: 0 to specify an initial fragment or an unfragmented message
+         bits 6-5: delivery type
+                   0x0 = LOCAL
+                   0x01 = TUNNEL
+                   0x02 = ROUTER
+                   0x03 = unused, invalid
+                   Note: LOCAL is used for inbound tunnels only, unimplemented
+                   for outbound tunnels
+         bit 4: delay included?  Unimplemented, always 0
+                                 If 1, a delay byte is included
+         bit 3: fragmented?  If 0, the message is not fragmented, what follows
+                             is the entire message
+                             If 1, the message is fragmented, and the
+                             instructions contain a Message ID
+         bit 2: extended options?  Unimplemented, always 0
+                                   If 1, extended options are included
+         bits 1-0: reserved, set to 0 for compatibility with future uses
+
+  Tunnel ID :: `TunnelId`
+         4 bytes
+         Optional, present if delivery type is TUNNEL
+         The destination tunnel ID
+
+  To Hash ::
+         32 bytes
+         Optional, present if delivery type is DESTINATION, ROUTER, or TUNNEL
+            If DESTINATION, the SHA256 Hash of the destination
+            If ROUTER, the SHA256 Hash of the router
+            If TUNNEL, the SHA256 Hash of the gateway router
+
+  Delay ::
+         1 byte
+         Optional, present if delay included flag is set
+         In tunnel messages: Unimplemented, never present; original
+         specification:
+            bit 7: type (0 = strict, 1 = randomized)
+            bits 6-0: delay exponent (2^value minutes)
+
+  Message ID ::
+         4 bytes
+         Optional, present if this message is the first of 2 or more fragments
+            (i.e. if the fragmented bit is 1)
+         An ID that uniquely identifies all fragments as belonging to a single
+         message (the current implementation uses `I2NPMessageHeader.msg_id`)
+
+  Extended Options ::
+         2 or more bytes
+         Optional, present if extend options flag is set
+         Unimplemented, never present; original specification:
+         One byte length and then that many bytes
+
+  size ::
+         2 bytes
+         The length of the fragment that follows
+         Valid values: 1 to approx. 960 in a tunnel message
+
+  Total length: Typical length is:
+         3 bytes for LOCAL delivery (tunnel message);
+         35 bytes for ROUTER / DESTINATION delivery or 39 bytes for TUNNEL
+         delivery (unfragmented tunnel message);
+         39 bytes for ROUTER delivery or 43 bytes for TUNNEL delivery (first
+         fragment)
+{% endhighlight %}
+
+Follow-on Fragment Delivery Instructions
+----------------------------------------
+
+If the MSB of the first byte is 1, this is a follow-on fragment, and the
+instructions are:
+
+.. raw:: html
+
+  {% highlight lang='dataspec' %}
++----+----+----+----+----+----+----+
+  |frag|     Message ID    |  size   |
+  +----+----+----+----+----+----+----+
+
+  frag ::
+         1 byte
+         Bit order: 76543210
+         binary 1nnnnnnd
+                bit 7: 1 to indicate this is a follow-on fragment
+                bits 6-1: nnnnnn is the 6 bit fragment number from 1 to 63
+                bit 0: d is 1 to indicate the last fragment, 0 otherwise
+
+  Message ID ::
+         4 bytes
+         Identifies the fragment sequence that this fragment belongs to.
+         This will match the message ID of an initial fragment (a fragment
+         with flag bit 7 set to 0 and flag bit 3 set to 1).
+
+  size ::
+         2 bytes
+         the length of the fragment that follows
+         valid values: 1 to 996
+
+  total length: 7 bytes
+{% endhighlight %}
+
+JavaDoc: http://{{ i2pconv('i2p-javadocs.i2p') }}/net/i2p/data/i2np/DeliveryInstructions.html
+
+
+Notes
+=====
+
+I2NP Message Maximum Size
+-------------------------
+
+While the maximum I2NP message size is nominally 64 KB, the size is further
+constrained by the method of fragmenting I2NP messages into multiple 1 KB
+tunnel messages.  The maximum number of fragments is 64, and the initial
+fragment may not be perfectly aligned at the start of a tunnel message.  So the
+message must nominally fit in 63 fragments.
+
+The maximum size of an initial fragment is 956 bytes (assuming TUNNEL delivery
+mode); the maximum size of a follow-on fragment is 996 bytes.  Therefore the
+maximum size is approximately 956 + (62 * 996) = 62708 bytes, or 61.2 KB.
+
+Ordering, Batching, Packing
+---------------------------
+
+Tunnel messages may be dropped or reordered.  The tunnel gateway, who creates
+tunnel messages, is free to implement any batching, mixing, or reordering
+strategy to fragment I2NP messages and efficiently pack fragments into tunnel
+messages.  In general, an optimal packing is not possible (the "packing
+problem").  The gateways may implement various delay and reordering strategies.
+
+Cover Traffic
+-------------
+
+Tunnel messages may contain only padding (i.e. no delivery instructions or
+message fragments at all) for cover traffic. This is unimplemented.
+
+
+References
+==========
+
+.. [I2NP]
+    {{ site_url('docs/protocol/i2np', True) }}
+
+.. [I2NP-GC]
+    {{ ctags_url('GarlicClove') }}
+
+.. [I2NP-GCDI]
+    {{ ctags_url('GarlicCloveDeliveryInstructions') }}
+
+.. [TUNNEL-IMPL]
+    {{ site_url('docs/tunnels/implementation', True) }}
diff --git a/i2p2www/spec/updates.rst b/i2p2www/spec/updates.rst
new file mode 100644
index 0000000000000000000000000000000000000000..5fb3c4527646c15528dc8ce0c3c3e134c1845ea0
--- /dev/null
+++ b/i2p2www/spec/updates.rst
@@ -0,0 +1,563 @@
+=============================
+Software Update Specification
+=============================
+.. meta::
+    :lastupdated: May 2015
+    :accuratefor: 0.9.20
+
+.. contents::
+
+
+Overview
+========
+
+I2P uses a simple, yet secure, system for automated software update.  The
+router console periodically pulls a news file from a configurable I2P URL.
+There is a hardcoded backup URL pointing to the project website, in case the
+default project news host goes down.
+
+The contents of the news file are displayed on the home page of the router
+console.  In addition, the news file contains the most recent version number of
+the software.  If the version is higher than the router's version number, it
+will display an indication to the user that an update is available.
+
+The router may optionally download, or download and install, the new version if
+configured to do so.
+
+Old News File Specification
+---------------------------
+
+This format is replaced by the su3 news format as of release 0.9.17.
+
+The news.xml file may contain the following elements::
+
+    <i2p.news date="$Date: 2010-01-22 00:00:00 $" />
+    <i2p.release version="0.7.14" date="2010/01/22" minVersion="0.6" />
+
+Parameters in the i2p.release entry are as follows.  All keys are
+case-insensitive. All values must be enclosed in double quotes.
+
+    date
+        The release date of the router version.
+
+        Unused. Format not specified.
+
+    minJavaVersion
+        The minimum version of Java required to run the current version.
+
+        As of release 0.9.9.
+
+    minVersion
+        The minimum version of the router required to update to the current
+        version. If a router is older than this, the user must (manually?)
+        update to an intermediate version first.
+
+        As of release 0.9.9.
+
+    su3Clearnet
+        One or more HTTP URLs where the .su3 update file may be found on the
+        clearnet (non-I2P). Multiple URLs must be separated by a space or comma.
+
+        As of release 0.9.9.
+
+    su3SSL
+        One or more HTTPS URLs where the .su3 update file may be found on the
+        clearnet (non-I2P). Multiple URLs must be separated by a space or comma.
+
+        As of release 0.9.9.
+
+    sudTorrent
+        The magnet link for the .sud (non-pack200) torrent of the update.
+
+        As of release 0.9.4.
+
+    su2Torrent
+        The magnet link for the .su2 (pack200) torrent of the update.
+
+        As of release 0.9.4.
+
+    su3Torrent
+        The magnet link for the .su3 (new format) torrent of the update.
+
+        As of release 0.9.9.
+
+    version
+        Required.
+
+        The latest current router version available.
+
+The elements may be included inside XML comments to prevent interpretation by
+browsers.  The i2p.release element and version are required. All others are
+optional.  NOTE: Due to parser limitations an entire element must be on a
+single line.
+
+Update File Specification
+-------------------------
+
+As of release 0.9.9, the signed update file, named i2pupdate.su3, will use the
+"su3" file format specified below.  Approved release signers will use 4096-bit
+RSA keys.  The X.509 public key certificates for these signers are distributed
+in the router installation packages.  The updates may contain certificates for
+new, approved signers, and/or contain a list of certificates to delete for
+revocation.
+
+
+Old Update File Specification
+-----------------------------
+
+This format is obsolete as of release 0.9.9.
+
+The signed update file, traditionally named i2pupdate.sud, is simply a zip file
+with a prepended 56 byte header.  The header contains:
+
+* A 40-byte DSA [Signature]_
+* A 16-byte I2P version in UTF-8, padded with trailing zeroes if necessary
+
+The signature covers only the zip archive - not the prepended version.  The
+signature must match one of the DSA [SigningPublicKey]_ configured into the
+router, which has a hardcoded default list of keys of the current project
+release managers.
+
+For version comparison purposes, version fields contain [0-9]*, field
+separators are '-', '_', and '.', and all other characters are ignored.
+
+As of version 0.8.8, the version must also be specified as a zip file comment
+in UTF-8, without the trailing zeroes.  The updating router verifes that the
+version in the header (not covered by the signature) matches the version in the
+zip file comment, which is covered by the signature.  This prevents spoofing of
+the version number in the header.
+
+Download and Installation
+-------------------------
+
+The router first downloads the header of the update file from one in a
+configurable list of I2P URLs, using the built-in HTTP client and proxy, and
+checks that the version is newer.  This prevents the problem of update hosts
+that do not have the latest file.  The router then downloads the full update
+file.  The router verifies that the update file version is newer before
+installation.  It also, of course, verifies the signature, and verifes that the
+zip file comment matches the header version, as explained above.
+
+The zip file is extracted and copied to "i2pupdate.zip" in the I2P
+configuration directory (~/.i2p on Linux).
+
+As of release 0.7.12, the router supports Pack200 decompression.  Files inside
+the zip archive with a .jar.pack or .war.pack suffix are transparently
+decompressed to a .jar or .war file.  Update files containing .pack files are
+traditionally named with a '.su2' suffix.  Pack200 shrinks the update files by
+about 60%.
+
+As of release 0.8.7, the router will delete the libjbigi.so and libjcpuid.so
+files if the zip archive contains a lib/jbigi.jar file, so that the new files
+will be extracted from jbigi.jar.
+
+As of release 0.8.12, if the zip archive contains a file deletelist.txt, the
+router will delete the files listed there. The format is:
+
+* One file name per line
+
+* All file names are relative to the installation directory; no absolute file
+  names allowed, no files starting with ".."
+
+* Comments start with '#'
+
+The router will then delete the deletelist.txt file.
+
+.. _su3:
+
+SU3 File Specification
+----------------------
+
+This specification is used for router updates as of release 0.9.9, reseed data
+as of release 0.9.14, plugins as of release 0.9.15, and the news file as of
+release 0.9.17.
+
+Issues with the previous .sud/.su2 format
+`````````````````````````````````````````
+* No magic number or flags
+
+* No way to specify compression, pack200 or not, or signing algo
+
+* Version is not covered by signature, so it is enforced by requiring it to be
+  in the zip file comment (for router files) or in the plugin.config file (for
+  plugins)
+
+* Signer not specified so verifier must try all known keys
+
+* Signature-before-data format requires two passes to generate file
+
+Goals
+`````
+* Fix above problems
+
+* Migrate to more secure signature algorithm
+
+* Keep version info in same format and offset for compatibility with existing
+  version checkers
+
+* One-pass signature verification and file extraction
+
+Specification
+`````````````
+
+======  ========================================================================
+Bytes   Contents
+======  ========================================================================
+ 0-5    Magic number "I2Psu3"
+  6     unused = 0
+  7     su3 file format version = 0
+
+ 8-9    Signature type
+
+        * 0x0000 = DSA-SHA1
+        * 0x0001 = ECDSA-SHA256-P256
+        * 0x0002 = ECDSA-SHA384-P384
+        * 0x0003 = ECDSA-SHA512-P521
+        * 0x0004 = RSA-SHA256-2048
+        * 0x0005 = RSA-SHA384-3072
+        * 0x0006 = RSA-SHA512-4096
+
+10-11   Signature length, e.g. 40 (0x0028) for DSA-SHA1. Must match that
+        specified for the [Signature]_ type.
+ 12     unused = 0
+
+ 13     Version length (in bytes not chars, including padding)
+
+        must be at least 16 (0x10) for compatibility
+
+ 14     unused = 0
+ 15     Signer ID length (in bytes not chars)
+16-23   Content length (not including header or sig)
+ 24     unused = 0
+
+ 25     File type
+
+        * 0x00 = zip file
+        * 0x01 = xml file (as of 0.9.15)
+        * 0x02 = html file (as of 0.9.17)
+        * 0x03 = xml.gz file (as of 0.9.17)
+
+ 26     unused = 0
+
+ 27     Content type
+
+        * 0x00 = unknown
+        * 0x01 = router update
+        * 0x02 = plugin or plugin update
+        * 0x03 = reseed data
+        * 0x04 = news feed (as of 0.9.15)
+
+28-39   unused = 0
+
+40-55+  Version, UTF-8 padded with trailing 0x00, 16 bytes minimum, length
+        specified at byte 13. Do not append 0x00 bytes if the length is 16 or
+        more.
+
+ xx+    ID of signer, (e.g. "zzz@mail.i2p") UTF-8, not padded, length specified
+        at byte 15
+
+ xx+    Content:
+
+        * Length specified in header at bytes 16-23
+        * Format specified in header at byte 25
+        * Content specified in header at byte 27
+
+ xx+    Signature: Length is specified in header at bytes 10-11, covers
+        everything starting at byte 0
+======  ========================================================================
+
+All unused fields must be set to 0 for compatibility with future versions.
+
+Signature Details
+`````````````````
+The signature covers the entire header starting at byte 0, through the end of
+the content.  We use raw signatures. Take the hash of the data (using the hash
+type implied by the signature type at bytes 8-9) and pass that to a "raw" sign
+or verify function (e.g. "NONEwithRSA" in Java).
+
+While signature verification and content extraction may be implemented in one
+pass, an implementation must read and buffer the first 10 bytes to determine
+the hash type before starting to verify.
+
+Signature lengths for the various signature types are given in the [Signature]_
+specification.  Pad the signature with leading zeros if necessary.  See the
+cryptography details page [CRYPTO-SIG]_ for parameters of the various signature
+types.
+
+Notes
+`````
+The content type specifies the trust domain.  For each content type, clients
+maintain a set of X.509 public key certificates for parties trusted to sign
+that content.  Only certificates for the specified content type may be used.
+The certificate is looked up by the ID of the signer.  Clients must verify that
+the content type is that expected for the application.
+
+All values are in network byte order (big endian).
+
+SU3 Router Update File Specification
+------------------------------------
+
+SU3 Details
+```````````
+* SU3 Content Type: 1 (ROUTER UPDATE)
+
+* SU3 File Type: 0 (ZIP)
+
+* SU3 Version: The router version
+
+* Jar and war files in the zip are compressed with pack200 as documented above
+  for "su2" files. If the client does not support pack200, it must download the
+  update in a "sud" format.
+
+Notes
+`````
+* For releases, the SU3 version is the "base" router version, e.g. "0.9.20".
+
+* For development builds, which are supported as of release 0.9.20, the SU3
+  version is the "full" router version, e.g. "0.9.20-5" or "0.9.20-5-rc". See
+  RouterVersion.java [I2P-SRC]_.
+
+SU3 Reseed File Specification
+-----------------------------
+
+As of 0.9.14, reseed data is delivered in an "su3" file format.
+
+Goals
+`````
+* Signed files with strong signatures and trusted certificates to prevent
+  man-in-the-middle attacks that could boot victims into a separate, untrusted
+  network.
+
+* Use su3 file format already used for updates, reseeding, and plugins
+
+* Single compressed file to speed up reseeding, which was slow to fetch 200 files
+
+Specification
+`````````````
+1. The file must be named "i2pseeds.su3".
+
+2. The file must be in the same directory as the router infos on the web server.
+
+3. A router will first try to fetch (index URL)/i2pseeds.su3; if that fails it
+   will fetch the index URL and then fetch the individual router info files
+   found in the links.
+
+SU3 Details
+```````````
+* SU3 Content Type: 3 (RESEED)
+
+* SU3 File Type: 0 (ZIP)
+
+* SU3 Version: Seconds since the epoch, in ASCII (date +%s)
+
+* Router info files in the zip file must be at the "top level". No directories
+  are in the zip file.
+
+* Router info files must be named "routerInfo-(44 character base 64 router
+  hash).dat", as in the old reseed mechanism. The I2P base 64 alphabet must be
+  used.
+
+SU3 Plugin File Specification
+-----------------------------
+
+As of 0.9.15, plugins may be packaged in an "su3" file format.
+
+SU3 Details
+```````````
+* SU3 Content Type: 2 (PLUGIN)
+
+* SU3 File Type: 0 (ZIP)
+
+  * See the plugin specification [PLUGIN]_ for details.
+
+* SU3 Version: The plugin version, must match that in plugin.config.
+
+* Jar and war files in the zip are compressed with pack200 as documented above
+  for "su2" files.
+
+SU3 News File Specification
+---------------------------
+
+As of 0.9.17, the news is delivered in an "su3" file format.
+
+Goals
+`````
+* Signed news with strong signatures and trusted certificates
+
+* Use su3 file format already used for updates, reseeding, and plugins
+
+* Standard XML format for use with standard parsers
+
+* Standard Atom format for use with standard feed readers and generators
+
+* Sanitization and verification of HTML before displaying on console
+
+* Suitable for easy implementation on Android and other platforms without an
+  HTML console
+
+SU3 Details
+```````````
+* SU3 Content Type: 4 (NEWS)
+
+* SU3 File Type: 1 (XML) or 3 (XML.GZ)
+
+* SU3 Version: Seconds since the epoch, in ASCII (date +%s)
+
+* File Format: XML or gzipped XML, containing an [RFC-4287]_ (Atom) XML Feed.
+  Charset must be UTF-8.
+
+Specification
+`````````````
+**Atom <feed> Details:**
+
+The following <feed> elements are used:
+
+    <entry>
+        A news item. See below.
+
+    <i2p:release>
+        I2P update metadata. See below.
+
+    <updated>
+        Required
+
+        Timestamp for the feed (conforming to [RFC-4287]_ (Atom) section 3.3 and
+        [RFC-3339]_.
+
+**Atom <entry> Details:**
+
+Each Atom <entry> in the news feed may be parsed and displayed in the router console.
+The following elements are used:
+
+    <author>
+        Optional
+
+        Containing:
+
+        <name>
+            The name of the entry author
+
+    <content>
+        Required
+
+        Content, must be type="xhtml".
+
+        The XHTML will be sanitized with a whitelist of allowed elements and a
+        blacklist of disallowed attributes. Clients may ignore an element, or
+        the enclosing entry, or the entire feed when a non-whitelisted element
+        is encountered.
+
+    <link>
+        Optional
+
+        Link for further information
+
+    <summary>
+        Optional
+
+        Short summary, suitable for a tooltip
+
+    <title>
+        Required
+
+        Title of the news entry
+
+    <updated>
+        Required
+
+        Timestamp for this entry (conforming to [RFC-4287]_ (Atom) section 3.3
+        and [RFC-3339]_).
+
+**Atom <i2p:release> Details:**
+
+There must be at least one <i2p:release> entity in the feed. Each contains the
+following attributes and entities:
+
+    date (attribute)
+        Required
+
+        Timestamp for this entry (conforming to [RFC-4287]_ (Atom) section 3.3
+        and [RFC-3339]_.
+
+        The date also may be in truncated format yyyy-mm-dd (without the 'T');
+        this is the "full-date" format in [RFC-3339]_. In this format the time
+        is assumed to be 00:00:00 UTC for any processing.
+
+    minJavaVersion (attribute)
+        If present, the minimum version of Java required to run the current
+        version.
+
+    minVersion (attribute)
+        If present, the minimum version of the router required to update to the
+        current version. If a router is older than this, the user must
+        (manually?) update to an intermediate version first.
+
+    <i2p:version>
+        Required
+
+        The latest current router version available.
+
+    <i2p:update>
+        An update file (one or more). It must contain at least one child.
+
+        type (attribute)
+            "sud", "su2", or "su3".
+
+            Must be unique across all <i2p:update> elements.
+
+        <i2p:clearnet>
+            Out-of-network direct download links (zero or more)
+
+            href (attribute)
+                A standard clearnet http link
+
+        <i2p:clearnetssl>
+            Out-of-network direct download links (zero or more)
+
+            href (attribute)
+                A standard clearnet https link
+
+        <i2p:torrent>
+            In-network magnet link
+
+            href (attribute)
+                A magnet link
+
+        <i2p:url>
+            In-network direct download links (zero or more)
+
+            href (attribute)
+                An in-network http .i2p link
+
+
+Future Work
+===========
+
+* The router update mechanism is part of the web router console. There is
+  currently no provision for updates of an embedded router lacking the router
+  console.
+
+
+References
+==========
+
+.. [CRYPTO-SIG]
+    {{ site_url('docs/how/cryptography', True) }}#sig
+
+.. [I2P-SRC]
+    https://github.com/i2p/i2p.i2p
+
+.. [PLUGIN]
+    {{ spec_url('plugin') }}
+
+.. [RFC-3339]
+    http://tools.ietf.org/html/rfc3339
+
+.. [RFC-4287]
+    http://tools.ietf.org/html/rfc4287
+
+.. [Signature]
+    {{ ctags_url('Signature') }}
+
+.. [SigningPublicKey]
+    {{ ctags_url('SigningPublicKey') }}
diff --git a/i2p2www/spec/views.py b/i2p2www/spec/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..3d4393db8d063e2da3b1625267dff8c5495380fa
--- /dev/null
+++ b/i2p2www/spec/views.py
@@ -0,0 +1,109 @@
+import codecs
+from docutils.core import (
+    publish_doctree,
+    publish_from_doctree,
+    publish_parts,
+)
+from flask import (
+    abort,
+    g,
+    make_response,
+    redirect,
+    render_template,
+    render_template_string,
+    request,
+    safe_join,
+    url_for,
+)
+import os.path
+
+from i2p2www import PROPOSAL_DIR, SPEC_DIR
+from i2p2www import helpers
+
+
+SPEC_METATAGS = {
+    'accuratefor': None,
+    'lastupdated': None,
+    }
+
+SPEC_LIST_METATAGS = [
+    ]
+
+
+def spec_index():
+    specs = []
+    for f in os.listdir(SPEC_DIR):
+        if f.endswith('.rst'):
+            path = safe_join(SPEC_DIR, f)
+            # read file header
+            header = ''
+            with codecs.open(path, encoding='utf-8') as fd:
+                for line in fd:
+                    header += line
+                    if not line.strip():
+                        break
+            parts = publish_parts(source=header, source_path=SPEC_DIR, writer_name="html")
+            meta = get_metadata_from_meta(parts['meta'])
+
+            spec = {
+                'name': f[:-4],
+                'title': parts['title'],
+            }
+            spec.update(meta)
+            specs.append(spec)
+
+    specs.sort(key=lambda s: s['name'])
+    return render_template('spec/index.html', specs=specs)
+
+def spec_show(name, txt=False):
+    # check if that file actually exists
+    path = safe_join(SPEC_DIR, name + '.rst')
+    if not os.path.exists(path):
+        abort(404)
+
+    # read file
+    with codecs.open(path, encoding='utf-8') as fd:
+        content = fd.read()
+
+    if txt:
+        # Strip out RST
+        content = content.replace('.. meta::\n', '')
+        content = content.replace('.. contents::\n\n', '')
+        content = content.replace('.. raw:: html\n\n', '')
+        content = content.replace('\n.. [', '\n[')
+        content = content.replace(']_.', '].')
+        content = content.replace(']_', '] ')
+        # Change highlight formatter
+        content = content.replace('{% highlight', "{% highlight formatter='textspec'")
+
+    # render the post with Jinja2 to handle URLs etc.
+    rendered_content = render_template_string(content)
+    rendered_content = rendered_content.replace('</pre></div>', '  </pre></div>')
+
+    if txt:
+        # Send response
+        r = make_response(rendered_content)
+        r.mimetype = 'text/plain'
+        return r
+
+    # Render the ToC
+    doctree = publish_doctree(source=rendered_content)
+    bullet_list = doctree[1][1]
+    doctree.clear()
+    doctree.append(bullet_list)
+    toc = publish_from_doctree(doctree, writer_name='html')
+
+    # Remove the ToC from the main document
+    rendered_content = rendered_content.replace('.. contents::\n', '')
+
+    # publish the spec with docutils
+    parts = publish_parts(source=rendered_content, source_path=SPEC_DIR, writer_name="html")
+    meta = get_metadata_from_meta(parts['meta'])
+
+    return render_template('spec/show.html', title=parts['title'], toc=toc, body=parts['fragment'], name=name, meta=meta)
+
+def spec_show_txt(name):
+    return spec_show(name, True)
+
+def get_metadata_from_meta(meta):
+    return helpers.get_metadata_from_meta(meta, SPEC_METATAGS, SPEC_LIST_METATAGS)
diff --git a/i2p2www/static/styles/duck/default.css b/i2p2www/static/styles/duck/default.css
index 49d8052bf8f7fd0d14aaa81cc4d83da158eb1db6..4d6592fbbb06639d19a3401c5b71f36ec84eebbd 100644
--- a/i2p2www/static/styles/duck/default.css
+++ b/i2p2www/static/styles/duck/default.css
@@ -18,6 +18,29 @@ a:hover {
     color: #f00e0e
 }
 
+dl p.first {
+    margin-top: 0 !important;
+}
+
+dt {
+    float: left;
+    clear: left;
+    min-width: 100px;
+    margin-right: 10px;
+    text-align: right;
+    font-weight: bold;
+    color: green;
+}
+
+dt:after {
+    content: ":";
+}
+
+dd {
+    margin: 0 0 0 110px;
+    padding: 0 0 0.5em 0;
+}
+
 div.hide {
     display: none
 }
diff --git a/i2p2www/templatevars.py b/i2p2www/templatevars.py
index 975e4a9e2180f70b0c37b4a5b49ceac8683dc172..13a2693061bd47c912a56ada42d6b27cabc3654a 100644
--- a/i2p2www/templatevars.py
+++ b/i2p2www/templatevars.py
@@ -1,7 +1,8 @@
+import ctags
 from flask import g, request, safe_join, url_for
 import os.path
 
-from i2p2www import CANONICAL_DOMAIN, CURRENT_I2P_VERSION, RTL_LANGS, SUPPORTED_LANGS, SUPPORTED_LANG_NAMES, STATIC_DIR, app
+from i2p2www import CANONICAL_DOMAIN, CURRENT_I2P_VERSION, RTL_LANGS, SUPPORTED_LANGS, SUPPORTED_LANG_NAMES, SPEC_DIR, STATIC_DIR, app
 
 INPROXY = '.xyz' # http://zzz.i2p/topics/1771-i2p-xyz-inproxy
 
@@ -21,15 +22,50 @@ I2P_TO_CLEAR = {
 
 @app.context_processor
 def utility_processor():
+    _ctags = ctags.CTags(os.path.join(SPEC_DIR, 'spectags'))
+    kinds = {
+        't': 'type',
+        's': 'struct',
+        'm': 'msg',
+        }
+
     # Shorthand for getting a site url
-    def get_site_url(path=None):
+    def get_site_url(path=None, external=False):
         lang = 'en'
         if hasattr(g, 'lang') and g.lang:
             lang = g.lang
         if path:
-            return url_for('site_show', lang=lang, page=path)
+            return url_for('site_show', lang=lang, page=path, _external=external)
+        else:
+            return url_for('site_show', lang=lang, _external=external)
+
+    def get_spec_url(name):
+        url = url_for('spec_show', name=name, _external=True)
+        # Remove ?lang=xx
+        if '?' in url:
+            url = url[:url.index('?')]
+        return url
+
+    def get_ctags_url(value):
+        filename, kind = _lookup_ctag(value)
+        # Handle message types
+        if not kind and value.endswith('Message'):
+            value = value[:-7]
+            filename, kind = _lookup_ctag(value)
+        if kind:
+            specname, _ = os.path.splitext(filename)
+            url = get_spec_url(specname)
+            return '%s#%s-%s' % \
+                (url, kinds[kind], value.lower())
+        else:
+            return ''
+
+    def _lookup_ctag(token):
+        entry = ctags.TagEntry()
+        if _ctags.find(entry, token, 0):
+            return entry['file'], entry['kind']
         else:
-            return url_for('site_show', lang=lang)
+            return None, None
 
     # Shorthand for getting a language-specific url
     def get_url_with_lang(endpoint, **args):
@@ -112,6 +148,8 @@ def utility_processor():
                 change_theme=change_theme,
                 logo_url=get_logo_for_theme,
                 site_url=get_site_url,
+                spec_url=get_spec_url,
+                ctags_url=get_ctags_url,
                 get_url=get_url_with_lang,
                 is_rtl=is_rtl_lang,
                 get_flag=get_flag,
diff --git a/i2p2www/urls.py b/i2p2www/urls.py
index 33fa42184117cd16f5aeaf780de41babb3a1924d..63faa463b6ed7b08fe240f65ba2b4ad00b791f91 100644
--- a/i2p2www/urls.py
+++ b/i2p2www/urls.py
@@ -44,6 +44,10 @@ url('/', 'views.main_index')
 url('/<lang:lang>/', 'views.site_show', defaults={'page': 'index'})
 url('/<lang:lang>/<path:page>', 'views.site_show')
 
+url('/spec', 'spec.views.spec_index')
+url('/spec/<string:name>', 'spec.views.spec_show')
+url('/spec/<string:name>.txt', 'spec.views.spec_show_txt')
+
 url('/<lang:lang>/papers/', 'anonbib.views.papers_list')
 url('/<lang:lang>/papers/bibtex', 'anonbib.views.papers_bibtex')
 url('/<lang:lang>/papers/by-<string:choice>', 'anonbib.views.papers_list')
diff --git a/i2p2www/views.py b/i2p2www/views.py
index 2efecd78475254cdb5dff0d92aa811e20534e2b9..53335dc8f96fda7b7a5d925ab2e3aa7c2a3bcf9d 100644
--- a/i2p2www/views.py
+++ b/i2p2www/views.py
@@ -12,11 +12,22 @@ from i2p2www.blog.helpers import get_blog_posts
 def main_index():
     return redirect(url_for('site_show', lang='en'))
 
+SPEC_REDIRECTS = {
+    'docs/how/cryptography': 'cryptography',
+}
+
 # Site pages
 @cache.cached()
 def site_show(page):
     if page.endswith('.html'):
         return redirect(url_for('site_show', page=page[:-5]))
+
+    # Redirect for old spec pages
+    if page.startswith('docs/spec/'):
+        return redirect(url_for('spec_show', name=page[10:]))
+    if page in SPEC_REDIRECTS:
+        return redirect(url_for('spec_show', name=SPEC_REDIRECTS[page]))
+
     name = 'site/%s.html' % page
     page_file = safe_join(TEMPLATE_DIR, name)