diff --git a/apps/i2psnark/java/src/org/klomp/snark/ConnectionAcceptor.java b/apps/i2psnark/java/src/org/klomp/snark/ConnectionAcceptor.java
index 8ed410170a17933afda1454daf717f17318205a3..5f5e02abc10774ab9ccf234876b9c73b8eeb93d9 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/ConnectionAcceptor.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/ConnectionAcceptor.java
@@ -36,17 +36,16 @@ import net.i2p.util.Log;
  */
 public class ConnectionAcceptor implements Runnable
 {
-  private static final ConnectionAcceptor _instance = new ConnectionAcceptor();
-  public static final ConnectionAcceptor instance() { return _instance; }
   private Log _log = new Log(ConnectionAcceptor.class);
   private I2PServerSocket serverSocket;
   private PeerAcceptor peeracceptor;
   private Thread thread;
+  private I2PSnarkUtil _util;
 
   private boolean stop;
   private boolean socketChanged;
 
-  private ConnectionAcceptor() {}
+  public ConnectionAcceptor(I2PSnarkUtil util) { _util = util; }
   
   public synchronized void startAccepting(PeerCoordinatorSet set, I2PServerSocket socket) {
     if (serverSocket != socket) {
@@ -63,11 +62,12 @@ public class ConnectionAcceptor implements Runnable
     }
   }
   
-  public ConnectionAcceptor(I2PServerSocket serverSocket,
+  public ConnectionAcceptor(I2PSnarkUtil util, I2PServerSocket serverSocket,
                             PeerAcceptor peeracceptor)
   {
     this.serverSocket = serverSocket;
     this.peeracceptor = peeracceptor;
+    _util = util;
     
     socketChanged = false;
     stop = false;
@@ -78,7 +78,7 @@ public class ConnectionAcceptor implements Runnable
 
   public void halt()
   {
-    if (true) throw new RuntimeException("wtf");
+    if (stop) return;
     stop = true;
 
     I2PServerSocket ss = serverSocket;
@@ -95,7 +95,7 @@ public class ConnectionAcceptor implements Runnable
   }
   
   public void restart() {
-      serverSocket = I2PSnarkUtil.instance().getServerSocket();
+      serverSocket = _util.getServerSocket();
       socketChanged = true;
       Thread t = thread;
       if (t != null)
@@ -116,7 +116,7 @@ public class ConnectionAcceptor implements Runnable
             socketChanged = false;
         }
         while ( (serverSocket == null) && (!stop)) {
-            serverSocket = I2PSnarkUtil.instance().getServerSocket();
+            serverSocket = _util.getServerSocket();
             if (serverSocket == null)
                 try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
         }
@@ -129,7 +129,7 @@ public class ConnectionAcceptor implements Runnable
                 if (socketChanged) {
                     continue;
                 } else {
-                    I2PServerSocket ss = I2PSnarkUtil.instance().getServerSocket();
+                    I2PServerSocket ss = _util.getServerSocket();
                     if (ss != serverSocket) {
                         serverSocket = ss;
                         socketChanged = true;
@@ -143,13 +143,13 @@ public class ConnectionAcceptor implements Runnable
         catch (I2PException ioe)
           {
             if (!socketChanged) {
-                Snark.debug("Error while accepting: " + ioe, Snark.ERROR);
+                _util.debug("Error while accepting: " + ioe, Snark.ERROR);
                 stop = true;
             }
           }
         catch (IOException ioe)
           {
-            Snark.debug("Error while accepting: " + ioe, Snark.ERROR);
+            _util.debug("Error while accepting: " + ioe, Snark.ERROR);
             stop = true;
           }
       }
@@ -161,7 +161,6 @@ public class ConnectionAcceptor implements Runnable
       }
     catch (I2PException ignored) { }
     
-    throw new RuntimeException("wtf");
   }
   
   private class Handler implements Runnable {
diff --git a/apps/i2psnark/java/src/org/klomp/snark/CoordinatorListener.java b/apps/i2psnark/java/src/org/klomp/snark/CoordinatorListener.java
index fc2b9b73efb39fecbf4a87e40f96d528efee3b17..a86afa781b6c32d7cd64bbd4d1d6920153c8aaa0 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/CoordinatorListener.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/CoordinatorListener.java
@@ -30,4 +30,8 @@ public interface CoordinatorListener
    * Called when the PeerCoordinator notices a change in the state of a peer.
    */
   void peerChange(PeerCoordinator coordinator, Peer peer);
+
+  public boolean overUploadLimit(int uploaders);
+  public boolean overUpBWLimit();
+  public boolean overUpBWLimit(long total);
 }
diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
index 9497258cb243e4b290ecd7cb97ce2dfc8076fa4d..bdfc056efd9aecf9ede56a08e6e614ca91099b50 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
@@ -28,12 +28,13 @@ import net.i2p.util.SimpleTimer;
 
 /**
  * I2P specific helpers for I2PSnark
+ * We use this class as a sort of context for i2psnark
+ * so we can run multiple instances of single Snarks
+ * (but not multiple SnarkManagers, it is still static)
  */
 public class I2PSnarkUtil {
     private I2PAppContext _context;
     private Log _log;
-    private static I2PSnarkUtil _instance = new I2PSnarkUtil();
-    public static I2PSnarkUtil instance() { return _instance; }
     
     private boolean _shouldProxy;
     private String _proxyHost;
@@ -53,8 +54,8 @@ public class I2PSnarkUtil {
     public static final String DEFAULT_OPENTRACKERS = "http://tracker.welterde.i2p/a";
     public static final int DEFAULT_MAX_UP_BW = 8;  //KBps
 
-    private I2PSnarkUtil() {
-        _context = I2PAppContext.getGlobalContext();
+    public I2PSnarkUtil(I2PAppContext ctx) {
+        _context = ctx;
         _log = _context.logManager().getLog(Snark.class);
         _opts = new HashMap();
         setProxy("127.0.0.1", 4444);
@@ -233,6 +234,28 @@ public class I2PSnarkUtil {
         }
         return "unknown";
     }
+
+    /** Base64 only - static (no naming service) */
+    static Destination getDestinationFromBase64(String ip) {
+        if (ip == null) return null;
+        if (ip.endsWith(".i2p")) {
+            if (ip.length() < 520)
+                    return null;
+            try {
+                return new Destination(ip.substring(0, ip.length()-4)); // sans .i2p
+            } catch (DataFormatException dfe) {
+                return null;
+            }
+        } else {
+            try {
+                return new Destination(ip);
+            } catch (DataFormatException dfe) {
+                return null;
+            }
+        }
+    }
+
+    /** Base64 Hash or Hash.i2p or name.i2p using naming service */
     Destination getDestination(String ip) {
         if (ip == null) return null;
         if (ip.endsWith(".i2p")) {
@@ -308,6 +331,9 @@ public class I2PSnarkUtil {
     }
 
     /** hook between snark's logger and an i2p log */
+    void debug(String msg, int snarkDebugLevel) {
+        debug(msg, snarkDebugLevel, null);
+    }
     void debug(String msg, int snarkDebugLevel, Throwable t) {
         if (t instanceof OutOfMemoryError) {
             try { Thread.sleep(100); } catch (InterruptedException ie) {}
diff --git a/apps/i2psnark/java/src/org/klomp/snark/Peer.java b/apps/i2psnark/java/src/org/klomp/snark/Peer.java
index e18be2da99feb19724ca0ac04aae3ded9fb6bdb0..6e76965e85e90916ec223e83166d17e4d40674cb 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/Peer.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/Peer.java
@@ -173,7 +173,7 @@ public class Peer implements Comparable
    * If the given BitField is non-null it is send to the peer as first
    * message.
    */
-  public void runConnection(PeerListener listener, BitField bitfield)
+  public void runConnection(I2PSnarkUtil util, PeerListener listener, BitField bitfield)
   {
     if (state != null)
       throw new IllegalStateException("Peer already started");
@@ -184,7 +184,7 @@ public class Peer implements Comparable
         // Do we need to handshake?
         if (din == null)
           {
-            sock = I2PSnarkUtil.instance().connect(peerID);
+            sock = util.connect(peerID);
             _log.debug("Connected to " + peerID + ": " + sock);
             if ((sock == null) || (sock.isClosed())) {
                 throw new IOException("Unable to reach " + peerID);
diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java
index 7ff569aca5227fb4afa5eb9201f55ed0de6a39f2..d96b0b84a95c41669c3d1b4c140c877b04f06114 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java
@@ -35,9 +35,11 @@ class PeerCheckerTask extends TimerTask
   private static final long KILOPERSECOND = 1024*(PeerCoordinator.CHECK_PERIOD/1000);
 
   private final PeerCoordinator coordinator;
+  public I2PSnarkUtil _util;
 
-  PeerCheckerTask(PeerCoordinator coordinator)
+  PeerCheckerTask(I2PSnarkUtil util, PeerCoordinator coordinator)
   {
+    _util = util;
     this.coordinator = coordinator;
   }
 
@@ -102,8 +104,8 @@ class PeerCheckerTask extends TimerTask
 	    peer.setRateHistory(upload, download);
             peer.resetCounters();
 
-            Snark.debug(peer + ":", Snark.DEBUG);
-            Snark.debug(" ul: " + upload/KILOPERSECOND
+            _util.debug(peer + ":", Snark.DEBUG);
+            _util.debug(" ul: " + upload/KILOPERSECOND
                         + " dl: " + download/KILOPERSECOND
                         + " i: " + peer.isInterested()
                         + " I: " + peer.isInteresting()
@@ -129,7 +131,7 @@ class PeerCheckerTask extends TimerTask
                 // Check if it still wants pieces from us.
                 if (!peer.isInterested())
                   {
-                    Snark.debug("Choke uninterested peer: " + peer,
+                    _util.debug("Choke uninterested peer: " + peer,
                                 Snark.INFO);
                     peer.setChoking(true);
                     uploaders--;
@@ -141,7 +143,7 @@ class PeerCheckerTask extends TimerTask
                   }
                 else if (overBWLimitChoke)
                   {
-                    Snark.debug("BW limit (" + upload + "/" + uploaded + "), choke peer: " + peer,
+                    _util.debug("BW limit (" + upload + "/" + uploaded + "), choke peer: " + peer,
                                 Snark.INFO);
                     peer.setChoking(true);
                     uploaders--;
@@ -155,7 +157,7 @@ class PeerCheckerTask extends TimerTask
                 else if (peer.isInteresting() && peer.isChoked())
                   {
                     // If they are choking us make someone else a downloader
-                    Snark.debug("Choke choking peer: " + peer, Snark.DEBUG);
+                    _util.debug("Choke choking peer: " + peer, Snark.DEBUG);
                     peer.setChoking(true);
                     uploaders--;
                     coordinator.uploaders--;
@@ -168,7 +170,7 @@ class PeerCheckerTask extends TimerTask
                 else if (!peer.isInteresting() && !coordinator.completed())
                   {
                     // If they aren't interesting make someone else a downloader
-                    Snark.debug("Choke uninteresting peer: " + peer, Snark.DEBUG);
+                    _util.debug("Choke uninteresting peer: " + peer, Snark.DEBUG);
                     peer.setChoking(true);
                     uploaders--;
                     coordinator.uploaders--;
@@ -183,7 +185,7 @@ class PeerCheckerTask extends TimerTask
                          && download == 0)
                   {
                     // We are downloading but didn't receive anything...
-                    Snark.debug("Choke downloader that doesn't deliver:"
+                    _util.debug("Choke downloader that doesn't deliver:"
                                 + peer, Snark.DEBUG);
                     peer.setChoking(true);
                     uploaders--;
@@ -222,7 +224,7 @@ class PeerCheckerTask extends TimerTask
             || uploaders > uploadLimit)
             && worstDownloader != null)
           {
-            Snark.debug("Choke worst downloader: " + worstDownloader,
+            _util.debug("Choke worst downloader: " + worstDownloader,
                         Snark.DEBUG);
 
             worstDownloader.setChoking(true);
diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
index c85db3f127a569f387ef29590022d059114e9c1d..0423401603527c09d67b4423539e9f474353ed31 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
@@ -77,13 +77,15 @@ public class PeerCoordinator implements PeerListener
   private boolean halted = false;
 
   private final CoordinatorListener listener;
+  public I2PSnarkUtil _util;
   
   public String trackerProblems = null;
   public int trackerSeenPeers = 0;
 
-  public PeerCoordinator(byte[] id, MetaInfo metainfo, Storage storage,
+  public PeerCoordinator(I2PSnarkUtil util, byte[] id, MetaInfo metainfo, Storage storage,
                          CoordinatorListener listener, Snark torrent)
   {
+    _util = util;
     this.id = id;
     this.metainfo = metainfo;
     this.storage = storage;
@@ -96,7 +98,7 @@ public class PeerCoordinator implements PeerListener
     // Randomize the first start time so multiple tasks are spread out,
     // this will help the behavior with global limits
     Random r = new Random();
-    timer.schedule(new PeerCheckerTask(this), (CHECK_PERIOD / 2) + r.nextInt((int) CHECK_PERIOD), CHECK_PERIOD);
+    timer.schedule(new PeerCheckerTask(_util, this), (CHECK_PERIOD / 2) + r.nextInt((int) CHECK_PERIOD), CHECK_PERIOD);
   }
   
   // only called externally from Storage after the double-check fails
@@ -366,7 +368,7 @@ public class PeerCoordinator implements PeerListener
           {
             public void run()
             {
-              peer.runConnection(listener, bitfield);
+              peer.runConnection(_util, listener, bitfield);
             }
           };
         String threadName = peer.toString();
@@ -846,7 +848,7 @@ public class PeerCoordinator implements PeerListener
    */
   public int allowedUploaders()
   {
-    if (Snark.overUploadLimit(uploaders)) {
+    if (listener != null && listener.overUploadLimit(uploaders)) {
         // if (_log.shouldLog(Log.DEBUG))
         //   _log.debug("Over limit, uploaders was: " + uploaders);
         return uploaders - 1;
@@ -858,12 +860,16 @@ public class PeerCoordinator implements PeerListener
 
   public boolean overUpBWLimit()
   {
-    return Snark.overUpBWLimit();
+    if (listener != null)
+        return listener.overUpBWLimit();
+    return false;
   }
 
   public boolean overUpBWLimit(long total)
   {
-    return Snark.overUpBWLimit(total * 1000 / CHECK_PERIOD);
+    if (listener != null)
+        return listener.overUpBWLimit(total * 1000 / CHECK_PERIOD);
+    return false;
   }
 }
 
diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinatorSet.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinatorSet.java
index cf36c93782abc376e3a1c585a5ef84443427c4d3..23fe0980380bcce2048f6ee704ecfc4f842db0d9 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinatorSet.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinatorSet.java
@@ -12,11 +12,9 @@ import java.util.Set;
  * from it there too)
  */
 public class PeerCoordinatorSet {
-    private static final PeerCoordinatorSet _instance = new PeerCoordinatorSet();
-    public static final PeerCoordinatorSet instance() { return _instance; }
     private Set _coordinators;
     
-    private PeerCoordinatorSet() {
+    public PeerCoordinatorSet() {
         _coordinators = new HashSet();
     }
     
diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerID.java b/apps/i2psnark/java/src/org/klomp/snark/PeerID.java
index e9b03b39100d4a18415588684f818506bfcb9ab6..67ff4c8580154d1e4cbbfddcab83c1f6d77602b6 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerID.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerID.java
@@ -72,7 +72,7 @@ public class PeerID implements Comparable
     bevalue = (BEValue)m.get("ip");
     if (bevalue == null)
       throw new InvalidBEncodingException("ip missing");
-    address = I2PSnarkUtil.instance().getDestination(bevalue.getString());
+    address = I2PSnarkUtil.getDestinationFromBase64(bevalue.getString());
     if (address == null)
         throw new InvalidBEncodingException("Invalid destination [" + bevalue.getString() + "]");
 
diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java
index c1eedba324b37ef6f1e08867f31e255b93137136..fdb334d3d3727ae0649d6a25ca111947a81dea8a 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java
@@ -34,6 +34,7 @@ import java.util.StringTokenizer;
 import java.util.Timer;
 import java.util.TimerTask;
 
+import net.i2p.I2PAppContext;
 import net.i2p.client.streaming.I2PServerSocket;
 import net.i2p.data.Destination;
 import net.i2p.util.I2PThread;
@@ -102,7 +103,7 @@ public class Snark
       public void outOfMemory(OutOfMemoryError err) {
           try {
               err.printStackTrace();
-              I2PSnarkUtil.instance().debug("OOM in the snark", Snark.ERROR, err);
+              System.out.println("OOM in the snark" + err);
           } catch (Throwable t) {
               System.out.println("OOM in the OOM");
           }
@@ -225,7 +226,7 @@ public class Snark
           }
         catch(IOException ioe)
           {
-            debug("ERROR while reading stdin: " + ioe, ERROR);
+            System.out.println("ERROR while reading stdin: " + ioe);
           }
         
         // Explicit shutdown.
@@ -244,22 +245,34 @@ public class Snark
   public CompleteListener completeListener;
   public boolean stopped;
   byte[] id;
+  public I2PSnarkUtil _util;
+  private PeerCoordinatorSet _peerCoordinatorSet;
 
-  Snark(String torrent, String ip, int user_port,
+  /** from main() via parseArguments() single torrent */
+  Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
         StorageListener slistener, CoordinatorListener clistener) { 
-    this(torrent, ip, user_port, slistener, clistener, null, true, "."); 
+    this(util, torrent, ip, user_port, slistener, clistener, null, null, null, true, "."); 
   }
-  public Snark(String torrent, String ip, int user_port,
+
+  /** single torrent */
+  Snark(I2PAppContext ctx, String torrent, String ip, int user_port,
+        StorageListener slistener, CoordinatorListener clistener) { 
+    this(new I2PSnarkUtil(ctx), torrent, ip, user_port, slistener, clistener, null, null, null, true, "."); 
+  }
+
+  /** multitorrent */
+  public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
         StorageListener slistener, CoordinatorListener clistener,
-        CompleteListener complistener, boolean start, String rootDir)
+        CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet,
+        ConnectionAcceptor connectionAcceptor, boolean start, String rootDir)
   {
     if (slistener == null)
       slistener = this;
 
-    //if (clistener == null)
-    //  clistener = this;
-
     completeListener = complistener;
+    _util = util;
+    _peerCoordinatorSet = peerCoordinatorSet;
+    acceptor = connectionAcceptor;
 
     this.torrent = torrent;
     this.rootDataDir = rootDir;
@@ -292,7 +305,7 @@ public class Snark
     while (i < 20)
       id[i++] = (byte)random.nextInt(256);
 
-    Snark.debug("My peer id: " + PeerID.idencode(id), Snark.INFO);
+    debug("My peer id: " + PeerID.idencode(id), Snark.INFO);
 
     int port;
     IOException lastException = null;
@@ -301,9 +314,9 @@ public class Snark
  * If we are starting,
  * startTorrent() will force a connect.
  *
-    boolean ok = I2PSnarkUtil.instance().connect();
+    boolean ok = util.connect();
     if (!ok) fatal("Unable to connect to I2P");
-    I2PServerSocket serversocket = I2PSnarkUtil.instance().getServerSocket();
+    I2PServerSocket serversocket = util.getServerSocket();
     if (serversocket == null)
         fatal("Unable to listen for I2P connections");
     else {
@@ -324,7 +337,7 @@ public class Snark
         else
           {
             activity = "Getting torrent";
-            File torrentFile = I2PSnarkUtil.instance().get(torrent, 3);
+            File torrentFile = _util.get(torrent, 3);
             if (torrentFile == null) {
                 fatal("Unable to fetch " + torrent);
                 if (false) return; // never reached - fatal(..) throws
@@ -348,7 +361,7 @@ public class Snark
         /*
             {
               // Try to create a new metainfo file
-             Snark.debug
+             debug
                ("Trying to create metainfo torrent for '" + torrent + "'",
                 NOTICE);
              try
@@ -381,7 +394,7 @@ public class Snark
         try
           {
             activity = "Checking storage";
-            storage = new Storage(meta, slistener);
+            storage = new Storage(_util, meta, slistener);
             if (completeListener != null) {
                 storage.check(rootDataDir,
                               completeListener.getSavedTorrentTime(this),
@@ -422,10 +435,10 @@ public class Snark
    * Start up contacting peers and querying the tracker
    */
   public void startTorrent() {
-    boolean ok = I2PSnarkUtil.instance().connect();
+    boolean ok = _util.connect();
     if (!ok) fatal("Unable to connect to I2P");
     if (coordinator == null) {
-        I2PServerSocket serversocket = I2PSnarkUtil.instance().getServerSocket();
+        I2PServerSocket serversocket = _util.getServerSocket();
         if (serversocket == null)
             fatal("Unable to listen for I2P connections");
         else {
@@ -434,12 +447,20 @@ public class Snark
         }
         debug("Starting PeerCoordinator, ConnectionAcceptor, and TrackerClient", NOTICE);
         activity = "Collecting pieces";
-        coordinator = new PeerCoordinator(id, meta, storage, this, this);
-        PeerCoordinatorSet set = PeerCoordinatorSet.instance();
-        set.add(coordinator);
-        ConnectionAcceptor acceptor = ConnectionAcceptor.instance();
-        acceptor.startAccepting(set, serversocket);
-        trackerclient = new TrackerClient(meta, coordinator);
+        coordinator = new PeerCoordinator(_util, id, meta, storage, this, this);
+        if (_peerCoordinatorSet != null) {
+            // multitorrent
+            _peerCoordinatorSet.add(coordinator);
+            if (acceptor != null) {
+                acceptor.startAccepting(_peerCoordinatorSet, serversocket);
+            } else {
+              // error
+            }
+        } else {
+            // single torrent
+            acceptor = new ConnectionAcceptor(_util, serversocket, new PeerAcceptor(coordinator));
+        }
+        trackerclient = new TrackerClient(_util, meta, coordinator);
     }
 
     stopped = false;
@@ -447,11 +468,12 @@ public class Snark
     if (coordinator.halted()) {
         // ok, we have already started and stopped, but the coordinator seems a bit annoying to
         // restart safely, so lets build a new one to replace the old
-        PeerCoordinatorSet set = PeerCoordinatorSet.instance();
-        set.remove(coordinator);
-        PeerCoordinator newCoord = new PeerCoordinator(coordinator.getID(), coordinator.getMetaInfo(), 
+        if (_peerCoordinatorSet != null)
+            _peerCoordinatorSet.remove(coordinator);
+        PeerCoordinator newCoord = new PeerCoordinator(_util, coordinator.getID(), coordinator.getMetaInfo(), 
                                                        coordinator.getStorage(), coordinator.getListener(), this);
-        set.add(newCoord);
+        if (_peerCoordinatorSet != null)
+            _peerCoordinatorSet.add(newCoord);
         coordinator = newCoord;
         coordinatorChanged = true;
     }
@@ -469,7 +491,7 @@ public class Snark
             }
             fatal("Could not reopen storage", ioe);
           }
-        TrackerClient newClient = new TrackerClient(coordinator.getMetaInfo(), coordinator);
+        TrackerClient newClient = new TrackerClient(_util, coordinator.getMetaInfo(), coordinator);
         if (!trackerclient.halted())
             trackerclient.halt();
         trackerclient = newClient;
@@ -499,8 +521,8 @@ public class Snark
         if (changed && completeListener != null)
             completeListener.updateStatus(this);
     }
-    if (pc != null)
-        PeerCoordinatorSet.instance().remove(pc);
+    if (pc != null && _peerCoordinatorSet != null)
+        _peerCoordinatorSet.remove(pc);
   }
 
   static Snark parseArguments(String[] args)
@@ -522,7 +544,8 @@ public class Snark
     String ip = null;
     String torrent = null;
 
-    boolean configured = I2PSnarkUtil.instance().configured();
+    I2PSnarkUtil util = new I2PSnarkUtil(I2PAppContext.getGlobalContext());
+    boolean configured = util.configured();
     
     int i = 0;
     while (i < args.length)
@@ -572,7 +595,7 @@ public class Snark
             String proxyHost = args[i+1];
             String proxyPort = args[i+2];
             if (!configured)
-                I2PSnarkUtil.instance().setProxy(proxyHost, Integer.parseInt(proxyPort));
+                util.setProxy(proxyHost, Integer.parseInt(proxyPort));
             i += 3;
           }
         else if (args[i].equals("--i2cp"))
@@ -594,7 +617,7 @@ public class Snark
                 }
             }
             if (!configured)
-                I2PSnarkUtil.instance().setI2CPConfig(i2cpHost, Integer.parseInt(i2cpPort), opts);
+                util.setI2CPConfig(i2cpHost, Integer.parseInt(i2cpPort), opts);
             i += 3 + (opts != null ? 1 : 0);
           }
         else
@@ -611,7 +634,7 @@ public class Snark
       else
         usage("Need exactly one <url>, <file> or <dir>.");
 
-    return new Snark(torrent, ip, user_port, slistener, clistener);
+    return new Snark(util, torrent, ip, user_port, slistener, clistener);
   }
   
   private static void usage(String s)
@@ -678,7 +701,7 @@ public class Snark
    */
   public void fatal(String s, Throwable t)
   {
-    I2PSnarkUtil.instance().debug(s, ERROR, t);
+    _util.debug(s, ERROR, t);
     //System.err.println("snark: " + s + ((t == null) ? "" : (": " + t)));
     //if (debug >= INFO && t != null)
     //  t.printStackTrace();
@@ -689,13 +712,12 @@ public class Snark
   /**
    * Show debug info if debug is true.
    */
-  public static void debug(String s, int level)
+  private void debug(String s, int level)
   {
-    I2PSnarkUtil.instance().debug(s, level, null);
-    //if (debug >= level)
-    //  System.out.println(s);
+    _util.debug(s, level, null);
   }
 
+  /** coordinatorListener */
   public void peerChange(PeerCoordinator coordinator, Peer peer)
   {
     // System.out.println(peer.toString());
@@ -742,7 +764,7 @@ public class Snark
         checking = true;
       }
     if (!checking)
-      Snark.debug("Got " + (checked ? "" : "BAD ") + "piece: " + num,
+      debug("Got " + (checked ? "" : "BAD ") + "piece: " + num,
                   Snark.INFO);
   }
 
@@ -759,7 +781,7 @@ public class Snark
   
   public void storageCompleted(Storage storage)
   {
-    Snark.debug("Completely received " + torrent, Snark.INFO);
+    debug("Completely received " + torrent, Snark.INFO);
     //storage.close();
     //System.out.println("Completely received: " + torrent);
     if (completeListener != null)
@@ -787,41 +809,40 @@ public class Snark
   }
 
   /** Maintain a configurable total uploader cap
+   * coordinatorListener
    */
   final static int MIN_TOTAL_UPLOADERS = 4;
   final static int MAX_TOTAL_UPLOADERS = 10;
-  public static boolean overUploadLimit(int uploaders) {
-    PeerCoordinatorSet coordinators = PeerCoordinatorSet.instance();
-    if (coordinators == null || uploaders <= 0)
+  public boolean overUploadLimit(int uploaders) {
+    if (_peerCoordinatorSet == null || uploaders <= 0)
       return false;
     int totalUploaders = 0;
-    for (Iterator iter = coordinators.iterator(); iter.hasNext(); ) {
+    for (Iterator iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
       PeerCoordinator c = (PeerCoordinator)iter.next();
       if (!c.halted())
         totalUploaders += c.uploaders;
     }
-    int limit = I2PSnarkUtil.instance().getMaxUploaders();
-    // Snark.debug("Total uploaders: " + totalUploaders + " Limit: " + limit, Snark.DEBUG);
+    int limit = _util.getMaxUploaders();
+    // debug("Total uploaders: " + totalUploaders + " Limit: " + limit, Snark.DEBUG);
     return totalUploaders > limit;
   }
 
-  public static boolean overUpBWLimit() {
-    PeerCoordinatorSet coordinators = PeerCoordinatorSet.instance();
-    if (coordinators == null)
+  public boolean overUpBWLimit() {
+    if (_peerCoordinatorSet == null)
       return false;
     long total = 0;
-    for (Iterator iter = coordinators.iterator(); iter.hasNext(); ) {
+    for (Iterator iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
       PeerCoordinator c = (PeerCoordinator)iter.next();
       if (!c.halted())
         total += c.getCurrentUploadRate();
     }
-    long limit = 1024l * I2PSnarkUtil.instance().getMaxUpBW();
-    Snark.debug("Total up bw: " + total + " Limit: " + limit, Snark.WARNING);
+    long limit = 1024l * _util.getMaxUpBW();
+    debug("Total up bw: " + total + " Limit: " + limit, Snark.WARNING);
     return total > limit;
   }
 
-  public static boolean overUpBWLimit(long total) {
-    long limit = 1024l * I2PSnarkUtil.instance().getMaxUpBW();
+  public boolean overUpBWLimit(long total) {
+    long limit = 1024l * _util.getMaxUpBW();
     return total > limit;
   }
 }
diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
index b3d3393972c8af9b61c98eff987122f098d60dfe..4df69d080f771a69b602b4d22d134ffa697e5d74 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
@@ -37,6 +37,9 @@ public class SnarkManager implements Snark.CompleteListener {
     private I2PAppContext _context;
     private Log _log;
     private List _messages;
+    private I2PSnarkUtil _util;
+    private PeerCoordinatorSet _peerCoordinatorSet;
+    private ConnectionAcceptor _connectionAcceptor;
     
     public static final String PROP_I2CP_HOST = "i2psnark.i2cpHost";
     public static final String PROP_I2CP_PORT = "i2psnark.i2cpPort";
@@ -63,6 +66,9 @@ public class SnarkManager implements Snark.CompleteListener {
         _context = I2PAppContext.getGlobalContext();
         _log = _context.logManager().getLog(SnarkManager.class);
         _messages = new ArrayList(16);
+        _util = new I2PSnarkUtil(_context);
+        _peerCoordinatorSet = new PeerCoordinatorSet();
+        _connectionAcceptor = new ConnectionAcceptor(_util);
         loadConfig("i2psnark.config");
         int minutes = getStartupDelayMinutes();
         _messages.add("Adding torrents in " + minutes + (minutes == 1 ? " minute" : " minutes"));
@@ -73,6 +79,9 @@ public class SnarkManager implements Snark.CompleteListener {
             ((RouterContext)_context).router().addShutdownTask(new SnarkManagerShutdown());
     }
     
+    /** hook to I2PSnarkUtil for the servlet */
+    public I2PSnarkUtil util() { return _util; }
+
     private static final int MAX_MESSAGES = 5;
     public void addMessage(String message) {
         synchronized (_messages) {
@@ -162,16 +171,16 @@ public class SnarkManager implements Snark.CompleteListener {
             }
         }
         if (i2cpHost != null) {
-            I2PSnarkUtil.instance().setI2CPConfig(i2cpHost, i2cpPort, i2cpOpts);
+            _util.setI2CPConfig(i2cpHost, i2cpPort, i2cpOpts);
             _log.debug("Configuring with I2CP options " + i2cpOpts);
         }
         //I2PSnarkUtil.instance().setI2CPConfig("66.111.51.110", 7654, new Properties());
         String eepHost = _config.getProperty(PROP_EEP_HOST);
         int eepPort = getInt(PROP_EEP_PORT, 4444);
         if (eepHost != null)
-            I2PSnarkUtil.instance().setProxy(eepHost, eepPort);
-        I2PSnarkUtil.instance().setMaxUploaders(getInt(PROP_UPLOADERS_TOTAL, Snark.MAX_TOTAL_UPLOADERS));
-        I2PSnarkUtil.instance().setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW));
+            _util.setProxy(eepHost, eepPort);
+        _util.setMaxUploaders(getInt(PROP_UPLOADERS_TOTAL, Snark.MAX_TOTAL_UPLOADERS));
+        _util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW));
         getDataDir().mkdirs();
     }
     
@@ -191,12 +200,12 @@ public class SnarkManager implements Snark.CompleteListener {
                              String upLimit, String upBW, boolean useOpenTrackers, String openTrackers) {
         boolean changed = false;
         if (eepHost != null) {
-            int port = I2PSnarkUtil.instance().getEepProxyPort();
+            int port = _util.getEepProxyPort();
             try { port = Integer.parseInt(eepPort); } catch (NumberFormatException nfe) {}
-            String host = I2PSnarkUtil.instance().getEepProxyHost();
+            String host = _util.getEepProxyHost();
             if ( (eepHost.trim().length() > 0) && (port > 0) &&
-                 ((!host.equals(eepHost) || (port != I2PSnarkUtil.instance().getEepProxyPort()) )) ) {
-                I2PSnarkUtil.instance().setProxy(eepHost, port);
+                 ((!host.equals(eepHost) || (port != _util.getEepProxyPort()) )) ) {
+                _util.setProxy(eepHost, port);
                 changed = true;
                 _config.setProperty(PROP_EEP_HOST, eepHost);
                 _config.setProperty(PROP_EEP_PORT, eepPort+"");
@@ -204,11 +213,11 @@ public class SnarkManager implements Snark.CompleteListener {
             }
         }
         if (upLimit != null) {
-            int limit = I2PSnarkUtil.instance().getMaxUploaders();
+            int limit = _util.getMaxUploaders();
             try { limit = Integer.parseInt(upLimit); } catch (NumberFormatException nfe) {}
-            if ( limit != I2PSnarkUtil.instance().getMaxUploaders()) {
+            if ( limit != _util.getMaxUploaders()) {
                 if ( limit >= Snark.MIN_TOTAL_UPLOADERS ) {
-                    I2PSnarkUtil.instance().setMaxUploaders(limit);
+                    _util.setMaxUploaders(limit);
                     changed = true;
                     _config.setProperty(PROP_UPLOADERS_TOTAL, "" + limit);
                     addMessage("Total uploaders limit changed to " + limit);
@@ -218,11 +227,11 @@ public class SnarkManager implements Snark.CompleteListener {
             }
         }
         if (upBW != null) {
-            int limit = I2PSnarkUtil.instance().getMaxUpBW();
+            int limit = _util.getMaxUpBW();
             try { limit = Integer.parseInt(upBW); } catch (NumberFormatException nfe) {}
-            if ( limit != I2PSnarkUtil.instance().getMaxUpBW()) {
+            if ( limit != _util.getMaxUpBW()) {
                 if ( limit >= MIN_UP_BW ) {
-                    I2PSnarkUtil.instance().setMaxUpBW(limit);
+                    _util.setMaxUpBW(limit);
                     changed = true;
                     _config.setProperty(PROP_UPBW_MAX, "" + limit);
                     addMessage("Up BW limit changed to " + limit + "KBps");
@@ -232,8 +241,8 @@ public class SnarkManager implements Snark.CompleteListener {
             }
         }
         if (i2cpHost != null) {
-            int oldI2CPPort = I2PSnarkUtil.instance().getI2CPPort();
-            String oldI2CPHost = I2PSnarkUtil.instance().getI2CPHost();
+            int oldI2CPPort = _util.getI2CPPort();
+            String oldI2CPHost = _util.getI2CPHost();
             int port = oldI2CPPort;
             try { port = Integer.parseInt(i2cpPort); } catch (NumberFormatException nfe) {}
             String host = oldI2CPHost;
@@ -259,7 +268,7 @@ public class SnarkManager implements Snark.CompleteListener {
             
             if ( (i2cpHost.trim().length() > 0) && (port > 0) &&
                  ((!host.equals(i2cpHost) || 
-                  (port != I2PSnarkUtil.instance().getI2CPPort()) ||
+                  (port != _util.getI2CPPort()) ||
                   (!oldOpts.equals(opts)))) ) {
                 boolean snarksActive = false;
                 Set names = listTorrentFiles();
@@ -275,19 +284,19 @@ public class SnarkManager implements Snark.CompleteListener {
                     _log.debug("i2cp host [" + i2cpHost + "] i2cp port " + port + " opts [" + opts 
                                + "] oldOpts [" + oldOpts + "]");
                 } else {
-                    if (I2PSnarkUtil.instance().connected()) {
-                        I2PSnarkUtil.instance().disconnect();
+                    if (_util.connected()) {
+                        _util.disconnect();
                         addMessage("Disconnecting old I2CP destination");
                     }
                     Properties p = new Properties();
                     p.putAll(opts);
                     addMessage("I2CP settings changed to " + i2cpHost + ":" + port + " (" + i2cpOpts.trim() + ")");
-                    I2PSnarkUtil.instance().setI2CPConfig(i2cpHost, port, p);
-                    boolean ok = I2PSnarkUtil.instance().connect();
+                    _util.setI2CPConfig(i2cpHost, port, p);
+                    boolean ok = _util.connect();
                     if (!ok) {
                         addMessage("Unable to connect with the new settings, reverting to the old I2CP settings");
-                        I2PSnarkUtil.instance().setI2CPConfig(oldI2CPHost, oldI2CPPort, oldOpts);
-                        ok = I2PSnarkUtil.instance().connect();
+                        _util.setI2CPConfig(oldI2CPHost, oldI2CPPort, oldOpts);
+                        ok = _util.connect();
                         if (!ok)
                             addMessage("Unable to reconnect with the old settings!");
                     } else {
@@ -315,13 +324,13 @@ public class SnarkManager implements Snark.CompleteListener {
             addMessage("Adjusted autostart to " + autoStart);
             changed = true;
         }
-        if (I2PSnarkUtil.instance().shouldUseOpenTrackers() != useOpenTrackers) {
+        if (_util.shouldUseOpenTrackers() != useOpenTrackers) {
             _config.setProperty(I2PSnarkUtil.PROP_USE_OPENTRACKERS, useOpenTrackers + "");
             addMessage((useOpenTrackers ? "En" : "Dis") + "abled open trackers - torrent restart required to take effect");
             changed = true;
         }
         if (openTrackers != null) {
-            if (openTrackers.trim().length() > 0 && !openTrackers.trim().equals(I2PSnarkUtil.instance().getOpenTrackerString())) {
+            if (openTrackers.trim().length() > 0 && !openTrackers.trim().equals(_util.getOpenTrackerString())) {
                 _config.setProperty(I2PSnarkUtil.PROP_OPENTRACKERS, openTrackers.trim());
                 addMessage("Open Tracker list changed - torrent restart required to take effect");
                 changed = true;
@@ -357,9 +366,9 @@ public class SnarkManager implements Snark.CompleteListener {
     public Snark getTorrent(String filename) { synchronized (_snarks) { return (Snark)_snarks.get(filename); } }
     public void addTorrent(String filename) { addTorrent(filename, false); }
     public void addTorrent(String filename, boolean dontAutoStart) {
-        if ((!dontAutoStart) && !I2PSnarkUtil.instance().connected()) {
+        if ((!dontAutoStart) && !_util.connected()) {
             addMessage("Connecting to I2P");
-            boolean ok = I2PSnarkUtil.instance().connect();
+            boolean ok = _util.connect();
             if (!ok) {
                 addMessage("Error connecting to I2P - check your I2CP settings");
                 return;
@@ -400,7 +409,9 @@ public class SnarkManager implements Snark.CompleteListener {
                         addMessage(rejectMessage);
                         return;
                     } else {
-                        torrent = new Snark(filename, null, -1, null, null, this, false, dataDir.getPath());
+                        torrent = new Snark(_util, filename, null, -1, null, null, this,
+                                            _peerCoordinatorSet, _connectionAcceptor,
+                                            false, dataDir.getPath());
                         torrent.completeListener = this;
                         synchronized (_snarks) {
                             _snarks.put(filename, torrent);
@@ -569,7 +580,7 @@ public class SnarkManager implements Snark.CompleteListener {
             if (remaining == 0) {
                 // should we disconnect/reconnect here (taking care to deal with the other thread's
                 // I2PServerSocket.accept() call properly?)
-                ////I2PSnarkUtil.instance().
+                ////_util.
             }
             if (!wasStopped)
                 addMessage("Torrent stopped: '" + sfile.getName() + "'");
@@ -645,7 +656,7 @@ public class SnarkManager implements Snark.CompleteListener {
             if (existingNames.contains(foundNames.get(i))) {
                 // already known.  noop
             } else {
-                if (shouldAutoStart() && !I2PSnarkUtil.instance().connect())
+                if (shouldAutoStart() && !_util.connect())
                     addMessage("Unable to connect to I2P");
                 addTorrent((String)foundNames.get(i), !shouldAutoStart());
             }
diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkShutdown.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkShutdown.java
index 0caefb42cb7a96f0e16d6c4764e56506843978fc..44832a2126d9a00bc0209ff3926f0f1cf4cc9cb1 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/SnarkShutdown.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkShutdown.java
@@ -51,21 +51,21 @@ public class SnarkShutdown extends I2PThread
 
   public void run()
   {
-    Snark.debug("Shutting down...", Snark.NOTICE);
+    //Snark.debug("Shutting down...", Snark.NOTICE);
 
-    Snark.debug("Halting ConnectionAcceptor...", Snark.INFO);
+    //Snark.debug("Halting ConnectionAcceptor...", Snark.INFO);
     if (acceptor != null)
       acceptor.halt();
 
-    Snark.debug("Halting TrackerClient...", Snark.INFO);
+    //Snark.debug("Halting TrackerClient...", Snark.INFO);
     if (trackerclient != null)
       trackerclient.halt();
 
-    Snark.debug("Halting PeerCoordinator...", Snark.INFO);
+    //Snark.debug("Halting PeerCoordinator...", Snark.INFO);
     if (coordinator != null)
       coordinator.halt();
 
-    Snark.debug("Closing Storage...", Snark.INFO);
+    //Snark.debug("Closing Storage...", Snark.INFO);
     if (storage != null)
       {
         try
@@ -74,7 +74,7 @@ public class SnarkShutdown extends I2PThread
           }
         catch(IOException ioe)
           {
-            I2PSnarkUtil.instance().debug("Couldn't properly close storage", Snark.ERROR, ioe);
+            //I2PSnarkUtil.instance().debug("Couldn't properly close storage", Snark.ERROR, ioe);
             throw new RuntimeException("b0rking");
           }
       }
@@ -82,7 +82,7 @@ public class SnarkShutdown extends I2PThread
     // XXX - Should actually wait till done...
     try
       {
-        Snark.debug("Waiting 5 seconds...", Snark.INFO);
+        //Snark.debug("Waiting 5 seconds...", Snark.INFO);
         Thread.sleep(5*1000);
       }
     catch (InterruptedException ie) { /* ignored */ }
diff --git a/apps/i2psnark/java/src/org/klomp/snark/Storage.java b/apps/i2psnark/java/src/org/klomp/snark/Storage.java
index ba68cb8381d0a43281becdc368a526f911915463..a8f023295f48e6db518793cf6a3a3af12f4dcd34 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/Storage.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/Storage.java
@@ -44,6 +44,7 @@ public class Storage
   private File[] RAFfile;    // File to make it easier to reopen
 
   private final StorageListener listener;
+  private I2PSnarkUtil _util;
 
   private BitField bitfield; // BitField to represent the pieces
   private int needed; // Number of pieces needed
@@ -66,9 +67,10 @@ public class Storage
    *
    * @exception IOException when creating and/or checking files fails.
    */
-  public Storage(MetaInfo metainfo, StorageListener listener)
+  public Storage(I2PSnarkUtil util, MetaInfo metainfo, StorageListener listener)
     throws IOException
   {
+    _util = util;
     this.metainfo = metainfo;
     this.listener = listener;
     needed = metainfo.getPieces();
@@ -81,9 +83,10 @@ public class Storage
    * with an appropriate MetaInfo file as can be announced on the
    * given announce String location.
    */
-  public Storage(File baseFile, String announce, StorageListener listener)
+  public Storage(I2PSnarkUtil util, File baseFile, String announce, StorageListener listener)
     throws IOException
   {
+    _util = util;
     this.listener = listener;
     // Create names, rafs and lengths arrays.
     getFiles(baseFile);
@@ -236,7 +239,7 @@ public class Storage
       }
   }
 
-  private static void addFiles(List l, File f)
+  private void addFiles(List l, File f)
   {
     if (!f.isDirectory())
       l.add(f);
@@ -245,7 +248,7 @@ public class Storage
         File[] files = f.listFiles();
         if (files == null)
           {
-            Snark.debug("WARNING: Skipping '" + f 
+            _util.debug("WARNING: Skipping '" + f 
                         + "' not a normal file.", Snark.WARNING);
             return;
           }
@@ -305,7 +308,7 @@ public class Storage
     if (files == null)
       {
         // Create base as file.
-        Snark.debug("Creating/Checking file: " + base, Snark.NOTICE);
+        _util.debug("Creating/Checking file: " + base, Snark.NOTICE);
         if (!base.createNewFile() && !base.exists())
           throw new IOException("Could not create file " + base);
 
@@ -328,7 +331,7 @@ public class Storage
     else
       {
         // Create base as dir.
-        Snark.debug("Creating/Checking directory: " + base, Snark.NOTICE);
+        _util.debug("Creating/Checking directory: " + base, Snark.NOTICE);
         if (!base.mkdir() && !base.isDirectory())
           throw new IOException("Could not create directory " + base);
 
@@ -366,16 +369,16 @@ public class Storage
       bitfield = savedBitField;
       needed = metainfo.getPieces() - bitfield.count();
       _probablyComplete = complete();
-      Snark.debug("Found saved state and files unchanged, skipping check", Snark.NOTICE);
+      _util.debug("Found saved state and files unchanged, skipping check", Snark.NOTICE);
     } else {
       // the following sets the needed variable
       changed = true;
       checkCreateFiles();
     }
     if (complete())
-        Snark.debug("Torrent is complete", Snark.NOTICE);
+        _util.debug("Torrent is complete", Snark.NOTICE);
     else
-        Snark.debug("Still need " + needed + " out of " + metainfo.getPieces() + " pieces", Snark.NOTICE);
+        _util.debug("Still need " + needed + " out of " + metainfo.getPieces() + " pieces", Snark.NOTICE);
   }
 
   /**
@@ -390,14 +393,14 @@ public class Storage
     if (files == null)
       {
         // Reopen base as file.
-        Snark.debug("Reopening file: " + base, Snark.NOTICE);
+        _util.debug("Reopening file: " + base, Snark.NOTICE);
         if (!base.exists())
           throw new IOException("Could not reopen file " + base);
       }
     else
       {
         // Reopen base as dir.
-        Snark.debug("Reopening directory: " + base, Snark.NOTICE);
+        _util.debug("Reopening directory: " + base, Snark.NOTICE);
         if (!base.isDirectory())
           throw new IOException("Could not reopen directory " + base);
 
@@ -487,7 +490,7 @@ public class Storage
               allocateFile(i);
           }
         } else {
-          Snark.debug("File '" + names[i] + "' exists, but has wrong length - repairing corruption", Snark.ERROR);
+          _util.debug("File '" + names[i] + "' exists, but has wrong length - repairing corruption", Snark.ERROR);
           changed = true;
           _probablyComplete = false; // to force RW
           synchronized(RAFlock[i]) {
@@ -574,7 +577,7 @@ public class Storage
             closeRAF(i);
           }
         } catch (IOException ioe) {
-            I2PSnarkUtil.instance().debug("Error closing " + RAFfile[i], Snark.ERROR, ioe);
+            _util.debug("Error closing " + RAFfile[i], Snark.ERROR, ioe);
             // gobble gobble
         }
       }
@@ -595,7 +598,7 @@ public class Storage
     try {
       bs = new byte[len];
     } catch (OutOfMemoryError oom) {
-      I2PSnarkUtil.instance().debug("Out of memory, can't honor request for piece " + piece, Snark.WARNING, oom);
+      _util.debug("Out of memory, can't honor request for piece " + piece, Snark.WARNING, oom);
       return null;
     }
     getUncheckedPiece(piece, bs, off, len);
@@ -691,7 +694,7 @@ public class Storage
       if (needed > 0) {
         if (listener != null)
             listener.setWantedPieces(this);
-        Snark.debug("WARNING: Not really done, missing " + needed
+        _util.debug("WARNING: Not really done, missing " + needed
                     + " pieces", Snark.WARNING);
       } else {
         if (listener != null)
diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java
index c312c22d8ab4a39a466f38ec46036701f16b8383..2b33db94d5f7af5f891f4ccb3f046c5a3e384a82 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java
@@ -57,6 +57,7 @@ public class TrackerClient extends I2PThread
   private final static int MAX_CONSEC_FAILS = 5;    // slow down after this
   private final static int LONG_SLEEP = 30*60*1000; // sleep a while after lots of fails
 
+  private I2PSnarkUtil _util;
   private final MetaInfo meta;
   private final PeerCoordinator coordinator;
   private final int port;
@@ -66,10 +67,11 @@ public class TrackerClient extends I2PThread
 
   private List trackers;
 
-  public TrackerClient(MetaInfo meta, PeerCoordinator coordinator)
+  public TrackerClient(I2PSnarkUtil util, MetaInfo meta, PeerCoordinator coordinator)
   {
     // Set unique name.
     super("TrackerClient-" + urlencode(coordinator.getID()));
+    _util = util;
     this.meta = meta;
     this.coordinator = coordinator;
 
@@ -98,13 +100,13 @@ public class TrackerClient extends I2PThread
   }
 
   private boolean verifyConnected() {
-    while (!stop && !I2PSnarkUtil.instance().connected()) {
-        boolean ok = I2PSnarkUtil.instance().connect();
+    while (!stop && !_util.connected()) {
+        boolean ok = _util.connect();
         if (!ok) {
             try { Thread.sleep(30*1000); } catch (InterruptedException ie) {}
         }
     }
-    return !stop && I2PSnarkUtil.instance().connected();
+    return !stop && _util.connected();
   }
   
   public void run()
@@ -121,7 +123,7 @@ public class TrackerClient extends I2PThread
     // the primary tracker, that we don't add it twice.
     trackers = new ArrayList(2);
     trackers.add(new Tracker(meta.getAnnounce(), true));
-    List tlist = I2PSnarkUtil.instance().getOpenTrackers();
+    List tlist = _util.getOpenTrackers();
     if (tlist != null) {
         for (int i = 0; i < tlist.size(); i++) {
              String url = (String)tlist.get(i);
@@ -136,7 +138,7 @@ public class TrackerClient extends I2PThread
              }
              if (meta.getAnnounce().startsWith(url.substring(0, slash)))
                 continue;
-             String dest = I2PSnarkUtil.instance().lookup(url.substring(7, slash));
+             String dest = _util.lookup(url.substring(7, slash));
              if (dest == null) {
                 _log.error("Announce host unknown: [" + url + "]");
                 continue;
@@ -264,7 +266,7 @@ public class TrackerClient extends I2PThread
                 catch (IOException ioe)
                   {
                     // Probably not fatal (if it doesn't last to long...)
-                    Snark.debug
+                    _util.debug
                       ("WARNING: Could not contact tracker at '"
                        + tr.announce + "': " + ioe, Snark.WARNING);
                     tr.trackerProblems = ioe.getMessage();
@@ -295,12 +297,12 @@ public class TrackerClient extends I2PThread
             // we could try and total the unique peers but that's too hard for now
             coordinator.trackerSeenPeers = maxSeenPeers;
             if (!started)
-                Snark.debug("         Retrying in one minute...", Snark.DEBUG);
+                _util.debug("         Retrying in one minute...", Snark.DEBUG);
           } // *** end of while loop
       } // try
     catch (Throwable t)
       {
-        I2PSnarkUtil.instance().debug("TrackerClient: " + t, Snark.ERROR, t);
+        _util.debug("TrackerClient: " + t, Snark.ERROR, t);
         if (t instanceof OutOfMemoryError)
             throw (OutOfMemoryError)t;
       }
@@ -332,15 +334,15 @@ public class TrackerClient extends I2PThread
       + "?info_hash=" + infoHash
       + "&peer_id=" + peerID
       + "&port=" + port
-      + "&ip=" + I2PSnarkUtil.instance().getOurIPString() + ".i2p"
+      + "&ip=" + _util.getOurIPString() + ".i2p"
       + "&uploaded=" + uploaded
       + "&downloaded=" + downloaded
       + "&left=" + left
       + ((event != NO_EVENT) ? ("&event=" + event) : "");
-    Snark.debug("Sending TrackerClient request: " + s, Snark.INFO);
+    _util.debug("Sending TrackerClient request: " + s, Snark.INFO);
       
     tr.lastRequestTime = System.currentTimeMillis();
-    File fetched = I2PSnarkUtil.instance().get(s);
+    File fetched = _util.get(s);
     if (fetched == null) {
         throw new IOException("Error fetching " + s);
     }
@@ -352,7 +354,7 @@ public class TrackerClient extends I2PThread
 
         TrackerInfo info = new TrackerInfo(in, coordinator.getID(),
                                            coordinator.getMetaInfo());
-        Snark.debug("TrackerClient response: " + info, Snark.INFO);
+        _util.debug("TrackerClient response: " + info, Snark.INFO);
 
         String failure = info.getFailureReason();
         if (failure != null)
diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java
index 4319a63103c7142fc38bf5c7b1508525ce2a30db..51472f6dd1b3ffe8bfbdb4f76873343857e3ee06 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java
@@ -101,7 +101,7 @@ public class TrackerInfo
             peerID = new PeerID(((BEValue)it.next()).getMap());
         } catch (InvalidBEncodingException ibe) {
             // don't let one bad entry spoil the whole list
-            Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR);
+            //Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR);
             continue;
         }
         peers.add(new Peer(peerID, my_id, metainfo));
diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
index 9745b275f704c95bc32c6f9aa214607a161545a4..b5491891a276e12d37f7d36c9f902ba7d13e64aa 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
@@ -116,7 +116,7 @@ public class I2PSnarkServlet extends HttpServlet {
         List snarks = getSortedSnarks(req);
         String uri = req.getRequestURI();
         out.write(TABLE_HEADER);
-        if (I2PSnarkUtil.instance().connected() && snarks.size() > 0) {
+        if (_manager.util().connected() && snarks.size() > 0) {
             if (peerParam != null)
                 out.write("(<a href=\"" + req.getRequestURI() + "\">Hide Peers</a>)<br />\n");
             else
@@ -124,7 +124,7 @@ public class I2PSnarkServlet extends HttpServlet {
         }
         out.write(TABLE_HEADER2);
         out.write("<th align=\"left\" valign=\"top\">");
-        if (I2PSnarkUtil.instance().connected())
+        if (_manager.util().connected())
             out.write("<a href=\"" + uri + "?action=StopAll&nonce=" + _nonce +
                       "\" title=\"Stop all torrents and the i2p tunnel\">Stop All</a>");
         else if (snarks.size() > 0)
@@ -330,7 +330,7 @@ public class I2PSnarkServlet extends HttpServlet {
                     _manager.addMessage("Error creating torrent - you must select a tracker");
                 else if (baseFile.exists()) {
                     try {
-                        Storage s = new Storage(baseFile, announceURL, null);
+                        Storage s = new Storage(_manager.util(), baseFile, announceURL, null);
                         s.create();
                         s.close(); // close the files... maybe need a way to pass this Storage to addTorrent rather than starting over
                         MetaInfo info = s.getMetaInfo();
@@ -361,8 +361,8 @@ public class I2PSnarkServlet extends HttpServlet {
                 if (!snark.stopped)
                     _manager.stopTorrent(snark.torrent, false);
             }
-            if (I2PSnarkUtil.instance().connected()) {
-                I2PSnarkUtil.instance().disconnect();
+            if (_manager.util().connected()) {
+                _manager.util().disconnect();
                 _manager.addMessage("I2P tunnel closed");
             }
         } else if ("StartAll".equals(action)) {
@@ -690,8 +690,8 @@ public class I2PSnarkServlet extends HttpServlet {
         String uri = req.getRequestURI();
         String dataDir = _manager.getDataDir().getAbsolutePath();
         boolean autoStart = _manager.shouldAutoStart();
-        boolean useOpenTrackers = I2PSnarkUtil.instance().shouldUseOpenTrackers();
-        String openTrackers = I2PSnarkUtil.instance().getOpenTrackerString();
+        boolean useOpenTrackers = _manager.util().shouldUseOpenTrackers();
+        String openTrackers = _manager.util().getOpenTrackerString();
         //int seedPct = 0;
        
         out.write("<form action=\"" + uri + "\" method=\"POST\">\n");
@@ -723,9 +723,9 @@ public class I2PSnarkServlet extends HttpServlet {
         out.write("</select><br />\n");
 */
         out.write("Total uploader limit: <input type=\"text\" name=\"upLimit\" value=\""
-                  + I2PSnarkUtil.instance().getMaxUploaders() + "\" size=\"3\" maxlength=\"3\" /> peers<br />\n");
+                  + _manager.util().getMaxUploaders() + "\" size=\"3\" maxlength=\"3\" /> peers<br />\n");
         out.write("Up bandwidth limit: <input type=\"text\" name=\"upBW\" value=\""
-                  + I2PSnarkUtil.instance().getMaxUpBW() + "\" size=\"3\" maxlength=\"3\" /> KBps <i>(Router Up BW / 2 recommended)</i><br />\n");
+                  + _manager.util().getMaxUpBW() + "\" size=\"3\" maxlength=\"3\" /> KBps <i>(Router Up BW / 2 recommended)</i><br />\n");
         
         out.write("Use open trackers also: <input type=\"checkbox\" name=\"useOpenTrackers\" value=\"true\" " 
                   + (useOpenTrackers ? "checked " : "") 
@@ -735,15 +735,15 @@ public class I2PSnarkServlet extends HttpServlet {
 
         //out.write("<hr />\n");
         out.write("EepProxy host: <input type=\"text\" name=\"eepHost\" value=\""
-                  + I2PSnarkUtil.instance().getEepProxyHost() + "\" size=\"15\" /> ");
+                  + _manager.util().getEepProxyHost() + "\" size=\"15\" /> ");
         out.write("port: <input type=\"text\" name=\"eepPort\" value=\""
-                  + I2PSnarkUtil.instance().getEepProxyPort() + "\" size=\"5\" maxlength=\"5\" /><br />\n");
+                  + _manager.util().getEepProxyPort() + "\" size=\"5\" maxlength=\"5\" /><br />\n");
         out.write("I2CP host: <input type=\"text\" name=\"i2cpHost\" value=\"" 
-                  + I2PSnarkUtil.instance().getI2CPHost() + "\" size=\"15\" /> ");
+                  + _manager.util().getI2CPHost() + "\" size=\"15\" /> ");
         out.write("port: <input type=\"text\" name=\"i2cpPort\" value=\"" +
-                  + I2PSnarkUtil.instance().getI2CPPort() + "\" size=\"5\" maxlength=\"5\" /> <br />\n");
+                  + _manager.util().getI2CPPort() + "\" size=\"5\" maxlength=\"5\" /> <br />\n");
         StringBuffer opts = new StringBuffer(64);
-        Map options = new TreeMap(I2PSnarkUtil.instance().getI2CPOptions());
+        Map options = new TreeMap(_manager.util().getI2CPOptions());
         for (Iterator iter = options.entrySet().iterator(); iter.hasNext(); ) {
             Map.Entry entry = (Map.Entry)iter.next();
             String key = (String)entry.getKey();
@@ -877,7 +877,7 @@ class FetchAndAdd implements Runnable {
     public void run() {
         _url = _url.trim();
         // 3 retries
-        File file = I2PSnarkUtil.instance().get(_url, false, 3);
+        File file = _manager.util().get(_url, false, 3);
         try {
             if ( (file != null) && (file.exists()) && (file.length() > 0) ) {
                 _manager.addMessage("Torrent fetched from " + _url);