{% extends "_layout.html" %} {% block title %}I2P Plugin Specification{% endblock %} {% block content %}
Page last updated January 2012, current as of router version 0.8.12
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.
The standard directory structure will let users install the following types of addons:
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.
foo.xpi2p is a sud file containing the following:
Standard .sud header prepended to the zip file, containing the following: 40-byte DSA signature 16-byte plugin version in UTF-8, padded with trailing zeroes if necessary Zip file containing the following: (REQUIRED) plugin.config file: (standard I2P config file, UTF-8 containing key=value lines, comments start with #) Containing the following properties: (* = required) 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 as 172 B64 chars ending with '=') *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. The following items are displayed on configclients.jsp 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) 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. The following items 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 items are used by the plugin installer: 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, sets the plugin as 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 item is used for translation plugins: langs=xx,yy,Klingon,... (unimplemented) (yy is the country flag) 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 (same format as router's clients.config) These will be run when a plugin is started Start at client #0, number consecutively New 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 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. New 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 New 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)
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.
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 (kinda 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.
How to get path substitution into jetty.xml? See zzzot and pebble plugins for examples.
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.
To run shell scripts or other external programs, see zzz.i2p
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.
i2p.jar, router.jar, jbigi.jar, sam.jar, mstreaming.jar, streaming.jar, i2ptunnel.jar, org.mortbay.jetty.jar, javax.servlet.jar, jasper-compiler.jar, jasper-runtime.jar, commons-logging.jar, commons-el.jar, wrapper.jar, systray.jar, systray4j.jar
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 this thread on zzz.i2p for background. Therefore, specify the full required classpath for each client.
If your plugin does not require 1.6:
If your plugin requires 1.6:
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. {% endblock %}