diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java
index 9c903ac211554704d9fd48fc0ecae374e3e86080..a39da7785f68002baf92d953e9216290a7dcfcdf 100644
--- a/core/java/src/net/i2p/I2PAppContext.java
+++ b/core/java/src/net/i2p/I2PAppContext.java
@@ -34,6 +34,7 @@ import net.i2p.util.I2PProperties;
 import net.i2p.util.KeyRing;
 import net.i2p.util.LogManager;
 //import net.i2p.util.PooledRandomSource;
+import net.i2p.util.PortMapper;
 import net.i2p.util.RandomSource;
 import net.i2p.util.SecureDirectory;
 import net.i2p.util.I2PProperties.I2PPropertyCallback;
@@ -85,6 +86,7 @@ public class I2PAppContext {
     private RandomSource _random;
     private KeyGenerator _keyGenerator;
     protected KeyRing _keyRing; // overridden in RouterContext
+    private final PortMapper _portMapper;
     private volatile boolean _statManagerInitialized;
     private volatile boolean _sessionKeyManagerInitialized;
     private volatile boolean _namingServiceInitialized;
@@ -200,6 +202,7 @@ public class I2PAppContext {
             _overrideProps.putAll(envProps);
         _shutdownTasks = new ConcurrentHashSet(32);
         initializeDirs();
+        _portMapper = new PortMapper(this);
     }
     
    /**
@@ -910,4 +913,12 @@ public class I2PAppContext {
     public boolean hasWrapper() {
         return System.getProperty("wrapper.version") != null;
     }
+
+    /**
+     *  Basic mapping from service names to ports
+     *  @since 0.8.12
+     */
+    public PortMapper portMapper() {
+        return _portMapper;
+    }
 }
diff --git a/core/java/src/net/i2p/util/PortMapper.java b/core/java/src/net/i2p/util/PortMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..efb49ad28366323c125dd777da79dca95edd481e
--- /dev/null
+++ b/core/java/src/net/i2p/util/PortMapper.java
@@ -0,0 +1,75 @@
+package net.i2p.util;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import net.i2p.I2PAppContext;
+
+/**
+ * Map services to internal or external application ports
+ * for this context. Not intended for the router's NTCP or SSU ports.
+ *
+ * @since 0.8.12
+ */
+public class PortMapper {
+    private final ConcurrentHashMap<String, Integer> _dir;
+
+    public static final String SVC_CONSOLE = "console";
+    public static final String SVC_HTTPS_CONSOLE = "https_console";
+    public static final String SVC_HTTP_PROXY = "HTTP";
+    public static final String SVC_HTTPS_PROXY = "HTTPS";
+    public static final String SVC_EEPSITE = "eepsite";
+    public static final String SVC_IRC = "irc";
+    public static final String SVC_SOCKS = "socks";
+    public static final String SVC_TAHOE = "tahoe-lafs";
+    public static final String SVC_SMTP = "SMTP";
+    public static final String SVC_POP = "POP3";
+    public static final String SVC_SAM = "SAM";
+    public static final String SVC_BOB = "BOB";
+    /** not necessary, already in config? */
+    public static final String SVC_I2CP = "I2CP";
+
+    /**
+     *  @param context unused for now
+     */
+    public PortMapper(I2PAppContext context) {
+        _dir = new ConcurrentHashMap(8);
+    }
+
+    /**
+     *  Add the service
+     *  @param port > 0
+     *  @return success, false if already registered
+     */
+    public boolean register(String service, int port) {
+        if (port <= 0)
+            return false;
+        return _dir.putIfAbsent(service, Integer.valueOf(port)) == null;
+    }
+
+    /**
+     *  Remove the service
+     */
+    public void unregister(String service) {
+        _dir.remove(service);
+    }
+
+    /**
+     *  Get the registered port for a service
+     *  @return -1 if not registered
+     */
+    public int getPort(String service) {
+        return getPort(service, -1);
+    }
+
+    /**
+     *  Get the registered port for a service
+     *  @param def default
+     *  @return def if not registered
+     */
+    public int getPort(String service, int def) {
+        Integer port = _dir.get(service);
+        if (port == null)
+            return def;
+        return port.intValue();
+    }
+}