diff --git a/apps/sam/java/src/net/i2p/sam/MasterSession.java b/apps/sam/java/src/net/i2p/sam/MasterSession.java
index 7d5efd439f081297006515f918d8003ec67649c8..ba64e9255a6f97d88818251a6568e51208b4818f 100644
--- a/apps/sam/java/src/net/i2p/sam/MasterSession.java
+++ b/apps/sam/java/src/net/i2p/sam/MasterSession.java
@@ -18,6 +18,7 @@ import net.i2p.I2PException;
 import net.i2p.client.I2PSession;
 import net.i2p.client.I2PSessionException;
 import net.i2p.client.I2PSessionMuxedListener;
+import net.i2p.client.streaming.I2PServerSocket;
 import net.i2p.client.streaming.I2PSocket;
 import net.i2p.data.DataFormatException;
 import net.i2p.data.DataHelper;
@@ -327,13 +328,15 @@ class MasterSession extends SAMv3StreamSession implements SAMDatagramReceiver, S
 		}
 
 		public void run() {
-			while (!stop && getSocketServer() != null) {
-				
+			if (_log.shouldWarn())
+				_log.warn("Stream acceptor started");
+			final I2PServerSocket i2pss = socketMgr.getServerSocket();
+			while (!stop) {
 				// wait and accept a connection from I2P side
 				I2PSocket i2ps;
 				try {
-					i2ps = getSocketServer().accept();
-					if (i2ps == null)
+					i2ps = i2pss.accept();
+					if (i2ps == null)  // never null as of 0.9.17
 						continue;
 				} catch (SocketTimeoutException ste) {
 					continue;
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java
index 8f16c1c160f333dc5420067c7fd4b9ac4bf6d136..2a7f80fd1f693435706d9a9a0a2bcb7dd9e9a41c 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java
@@ -51,6 +51,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
     protected final long _id;
     private static final AtomicLong __id = new AtomicLong();
     private static final int FIRST_READ_TIMEOUT = 60*1000;
+    protected static final String SESSION_ERROR = "SESSION STATUS RESULT=I2P_ERROR";
     
     /**
      * Create a new SAM version 1 handler.  This constructor expects
@@ -132,7 +133,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
                     ReadLine.readLine(sock, buf, gotFirstLine ? 0 : FIRST_READ_TIMEOUT);
                     sock.setSoTimeout(0);
                 } catch (SocketTimeoutException ste) {
-                    writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"command timeout, bye\"\n");
+                    writeString(SESSION_ERROR, "command timeout, bye");
                     break;
                 }
                 msg = buf.toString();
@@ -222,19 +223,19 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
                     || (streamSession != null)) {
                     if (_log.shouldLog(Log.DEBUG))
                         _log.debug("Trying to create a session, but one still exists");
-                    return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Session already exists\"\n");
+                    return writeString(SESSION_ERROR, "Session already exists");
                 }
                 if (props.isEmpty()) {
                     if (_log.shouldLog(Log.DEBUG))
                         _log.debug("No parameters specified in SESSION CREATE message");
-                    return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No parameters for SESSION CREATE\"\n");
+                    return writeString(SESSION_ERROR, "No parameters for SESSION CREATE");
                 }
                 
                 dest = (String) props.remove("DESTINATION");
                 if (dest == null) {
                     if (_log.shouldLog(Log.DEBUG))
                         _log.debug("SESSION DESTINATION parameter not specified");
-                    return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"DESTINATION not specified\"\n");
+                    return writeString(SESSION_ERROR, "DESTINATION not specified");
                 }
                 
                 String destKeystream = null;
@@ -264,7 +265,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
                 if (style == null) {
                     if (_log.shouldLog(Log.DEBUG))
                         _log.debug("SESSION STYLE parameter not specified");
-                    return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No SESSION STYLE specified\"\n");
+                    return writeString(SESSION_ERROR, "No SESSION STYLE specified");
                 }
                 
 		// Unconditionally override what the client may have set
@@ -288,7 +289,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
                                && !dir.equals("BOTH")) {
                         if (_log.shouldLog(Log.DEBUG))
                             _log.debug("Unknown DIRECTION parameter value: [" + dir + "]");
-                        return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unknown DIRECTION parameter\"\n");
+                        return writeString(SESSION_ERROR, "Unknown DIRECTION parameter");
                     }
                 
                     streamSession = newSAMStreamSession(destKeystream, dir,props);
@@ -296,7 +297,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
                 } else {
                     if (_log.shouldLog(Log.DEBUG))
                         _log.debug("Unrecognized SESSION STYLE: \"" + style +"\"");
-                    return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized SESSION STYLE\"\n");
+                    return writeString(SESSION_ERROR, "Unrecognized SESSION STYLE");
                 }
                 return writeString("SESSION STATUS RESULT=OK DESTINATION="
                                    + dest + "\n");
@@ -304,22 +305,22 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
                 if (_log.shouldLog(Log.DEBUG))
                     _log.debug("Unrecognized SESSION message opcode: \""
                            + opcode + "\"");
-                return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized opcode\"\n");
+                return writeString(SESSION_ERROR, "Unrecognized opcode");
             }
         } catch (DataFormatException e) {
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("Invalid destination specified");
-            return writeString("SESSION STATUS RESULT=INVALID_KEY DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
+            return writeString("SESSION STATUS RESULT=INVALID_KEY", e.getMessage());
         } catch (I2PSessionException e) {
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("I2P error when instantiating session", e);
-            return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
+            return writeString(SESSION_ERROR, e.getMessage());
         } catch (SAMException e) {
             _log.error("Unexpected SAM error", e);
-            return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
+            return writeString(SESSION_ERROR, e.getMessage());
         } catch (IOException e) {
             _log.error("Unexpected IOException", e);
-            return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
+            return writeString(SESSION_ERROR, e.getMessage());
         }
     }
 
@@ -1012,6 +1013,18 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
         }
         return rv;
     }
+
+    /**
+     * Write a string and message, escaping the message.
+     * Writes s + createMessageString(msg) + \n
+     *
+     * @param s The string, non-null
+     * @param s The message may be null
+     * @since 0.9.25
+     */
+    protected boolean writeString(String s, String msg) {
+        return writeString(s + createMessageString(msg) + '\n');
+    }
   
     public void receiveStreamBytes(int id, ByteBuffer data) throws IOException {
         if (streamSession == null) {
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java
index 0033b3ee236a1df50f7981992b72ba9d400fea4d..e8284964ff3231ad15ad7d0a3c70c88d32cf89ca 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java
@@ -56,6 +56,7 @@ class SAMv3Handler extends SAMv1Handler
 	private long _lastPing;
 	private static final int FIRST_READ_TIMEOUT = 60*1000;
 	private static final int READ_TIMEOUT = 3*60*1000;
+	private static final String AUTH_ERROR = "AUTH STATUS RESULT=I2P_ERROR";
 	
 	/**
 	 * Create a new SAM version 3 handler.  This constructor expects
@@ -196,7 +197,7 @@ class SAMv3Handler extends SAMv1Handler
 								if (now - _lastPing >= READ_TIMEOUT) {
 									if (_log.shouldWarn())
 										_log.warn("Failed to respond to PING");
-									writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"PONG timeout\"\n");
+									writeString(SESSION_ERROR, "PONG timeout");
 									break;
 								}
 							} else {
@@ -211,13 +212,13 @@ class SAMv3Handler extends SAMv1Handler
 								if (now - _lastPing >= 2*READ_TIMEOUT) {
 									if (_log.shouldWarn())
 										_log.warn("Failed to respond to PING");
-									writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"PONG timeout\"\n");
+									writeString(SESSION_ERROR, "PONG timeout");
 									break;
 								}
 							} else if (_lastPing < 0) {
 								if (_log.shouldWarn())
 									_log.warn("2nd timeout");
-								writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"command timeout, bye\"\n");
+								writeString(SESSION_ERROR, "command timeout, bye");
 								break;
 							} else {
 								// don't clear buffer, don't send ping,
@@ -238,7 +239,7 @@ class SAMv3Handler extends SAMv1Handler
 						ReadLine.readLine(socket, buf, gotFirstLine ? 0 : FIRST_READ_TIMEOUT);
 						socket.setSoTimeout(0);
 					} catch (SocketTimeoutException ste) {
-						writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"command timeout, bye\"\n");
+						writeString(SESSION_ERROR, "command timeout, bye");
 						break;
 					}
 					line = buf.toString();
@@ -275,7 +276,7 @@ class SAMv3Handler extends SAMv1Handler
 
 				if (opcode == null) {
 					// This is not a correct message, for sure
-					if (writeString(domain + " STATUS RESULT=I2P_ERROR MESSAGE=\"command not specified\"\n"))
+					if (writeString(domain + " STATUS RESULT=I2P_ERROR", "command not specified"))
 						continue;
 					else
 						break;
@@ -398,11 +399,11 @@ class SAMv3Handler extends SAMv1Handler
 
 		String nick = (String) props.remove("ID");
 		if (nick == null)
-			return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"ID not specified\"\n");
+			return writeString(SESSION_ERROR, "ID not specified");
 
 		String style = (String) props.remove("STYLE");
 		if (style == null && !opcode.equals("REMOVE"))
-			return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No SESSION STYLE specified\"\n");
+			return writeString(SESSION_ERROR, "No SESSION STYLE specified");
 
 		try{
 			if (opcode.equals("CREATE")) {
@@ -410,19 +411,19 @@ class SAMv3Handler extends SAMv1Handler
 						|| (this.getStreamSession() != null)) {
 					if (_log.shouldLog(Log.DEBUG))
 						_log.debug("Trying to create a session, but one still exists");
-					return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Session already exists\"\n");
+					return writeString(SESSION_ERROR, "Session already exists");
 				}
 				if (props.isEmpty()) {
 					if (_log.shouldLog(Log.DEBUG))
 						_log.debug("No parameters specified in SESSION CREATE message");
-					return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No parameters for SESSION CREATE\"\n");
+					return writeString(SESSION_ERROR, "No parameters for SESSION CREATE");
 				}
 
 				dest = (String) props.remove("DESTINATION");
 				if (dest == null) {
 					if (_log.shouldLog(Log.DEBUG))
 						_log.debug("SESSION DESTINATION parameter not specified");
-					return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"DESTINATION not specified\"\n");
+					return writeString(SESSION_ERROR, "DESTINATION not specified");
 				}
 
 				if (dest.equals("TRANSIENT")) {
@@ -433,8 +434,8 @@ class SAMv3Handler extends SAMv1Handler
 					if (sigTypeStr != null) {
 						sigType = SigType.parseSigType(sigTypeStr);
 						if (sigType == null) {
-							return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"SIGNATURE_TYPE "
-							                   + sigTypeStr + " unsupported\"\n");
+							return writeString(SESSION_ERROR, "SIGNATURE_TYPE "
+							                   + sigTypeStr + " unsupported");
 						}
 					} else {
 						sigType = SigType.DSA_SHA1;
@@ -511,7 +512,7 @@ class SAMv3Handler extends SAMv1Handler
 				} else {
 					if (_log.shouldLog(Log.DEBUG))
 						_log.debug("Unrecognized SESSION STYLE: \"" + style +"\"");
-					return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized SESSION STYLE\"\n");
+					return writeString(SESSION_ERROR, "Unrecognized SESSION STYLE");
 				}
 				ok = true ;
 				return writeString("SESSION STATUS RESULT=OK DESTINATION="
@@ -520,7 +521,7 @@ class SAMv3Handler extends SAMv1Handler
                                 // prevent trouble in finally block
 				ok = true;
 				if (streamSession == null || datagramSession == null || rawSession == null)
-					return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Not a MASTER session\"\n");
+					return writeString(SESSION_ERROR, "Not a MASTER session");
 				MasterSession msess = (MasterSession) session;
 				String msg;
 				if (opcode.equals("ADD")) {
@@ -529,30 +530,30 @@ class SAMv3Handler extends SAMv1Handler
 					msg = msess.remove(nick, props);
 				}
 				if (msg == null)
-					return writeString("SESSION STATUS RESULT=OK MESSAGE=\"" + opcode + ' ' + nick + "\"\n");
+					return writeString("SESSION STATUS RESULT=OK", opcode + ' ' + nick);
 				else
-					return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"" + msg + "\"\n");
+					return writeString(SESSION_ERROR, msg);
 			} else {
 				if (_log.shouldLog(Log.DEBUG))
 					_log.debug("Unrecognized SESSION message opcode: \""
 						+ opcode + "\"");
-				return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized opcode\"\n");
+				return writeString(SESSION_ERROR, "Unrecognized opcode");
 			}
 		} catch (DataFormatException e) {
 			if (_log.shouldLog(Log.DEBUG))
 				_log.debug("Invalid destination specified");
-			return writeString("SESSION STATUS RESULT=INVALID_KEY DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
+			return writeString("SESSION STATUS RESULT=INVALID_KEY", e.getMessage());
 		} catch (I2PSessionException e) {
 			if (_log.shouldLog(Log.DEBUG))
 				_log.debug("I2P error when instantiating session", e);
-			return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"" + e.getMessage() + "\"\n");
+			return writeString(SESSION_ERROR, e.getMessage());
 		} catch (SAMException e) {
 			if (_log.shouldLog(Log.INFO))
 				_log.info("Funny SAM error", e);
-			return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"" + e.getMessage() + "\"\n");
+			return writeString(SESSION_ERROR, e.getMessage());
 		} catch (IOException e) {
 			_log.error("Unexpected IOException", e);
-			return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"" + e.getMessage() + "\"\n");
+			return writeString(SESSION_ERROR, e.getMessage());
 		} finally {
 			// unregister the session if it has not been created
 			if ( !ok && nick!=null ) {
@@ -796,29 +797,29 @@ class SAMv3Handler extends SAMv1Handler
 			String user = props.getProperty("USER");
 			String pw = props.getProperty("PASSWORD");
 			if (user == null || pw == null)
-				return writeString("AUTH STATUS RESULT=I2P_ERROR MESSAGE=\"USER and PASSWORD required\"\n");
+				return writeString(AUTH_ERROR, "USER and PASSWORD required");
 			String prop = SAMBridge.PROP_PW_PREFIX + user + SAMBridge.PROP_PW_SUFFIX;
 			if (i2cpProps.containsKey(prop))
-				return writeString("AUTH STATUS RESULT=I2P_ERROR MESSAGE=\"user " + user + " already exists\"\n");
+				return writeString(AUTH_ERROR, "user " + user + " already exists");
 			PasswordManager pm = new PasswordManager(I2PAppContext.getGlobalContext());
 			String shash = pm.createHash(pw);
 			i2cpProps.setProperty(prop, shash);
 		} else if (opcode.equals("REMOVE")) {
 			String user = props.getProperty("USER");
 			if (user == null)
-				return writeString("AUTH STATUS RESULT=I2P_ERROR MESSAGE=\"USER required\"\n");
+				return writeString(AUTH_ERROR, "USER required");
 			String prop = SAMBridge.PROP_PW_PREFIX + user + SAMBridge.PROP_PW_SUFFIX;
 			if (!i2cpProps.containsKey(prop))
-				return writeString("AUTH STATUS RESULT=I2P_ERROR MESSAGE=\"user " + user + " not found\"\n");
+				return writeString(AUTH_ERROR, "user " + user + " not found");
 			i2cpProps.remove(prop);
 		} else {
-			return writeString("AUTH STATUS RESULT=I2P_ERROR MESSAGE=\"Unknown AUTH command\"\n");
+			return writeString(AUTH_ERROR, "Unknown AUTH command");
 		}
 		try {
 			bridge.saveConfig();
 			return writeString("AUTH STATUS RESULT=OK\n");
 		} catch (IOException ioe) {
-			return writeString("AUTH STATUS RESULT=I2P_ERROR MESSAGE=\"Config save failed: " + ioe + "\"\n");
+			return writeString(AUTH_ERROR, "Config save failed: " + ioe);
 		}
 	}
 
diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java
index 1eb867ce435c49238f7f707c7df7735b0599e6e2..df2156d5de55a76103a75248e2973956716ae1c0 100644
--- a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java
+++ b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java
@@ -296,7 +296,7 @@ public class SAMStreamSend {
                     style = "RAW";
 
                 if (masterMode) {
-                    String req = "SESSION CREATE DESTINATION=TRANSIENT STYLE=MASTER ID=master " + opts + '\n';
+                    String req = "SESSION CREATE DESTINATION=TRANSIENT STYLE=MASTER ID=masterSend " + opts + '\n';
                     samOut.write(req.getBytes("UTF-8"));
                     samOut.flush();
                     if (_log.shouldLog(Log.DEBUG))
diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java
index d59ebb0a37e303cf43d78ed6ecf71a21fd85c430..ebeabe326a93794b5c04737f98bbb9e1bee3d33e 100644
--- a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java
+++ b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java
@@ -58,19 +58,22 @@ public class SAMStreamSink {
     private static I2PSSLSocketFactory _sslSocketFactory;
     
     private static final int STREAM=0, DG=1, V1DG=2, RAW=3, V1RAW=4, RAWHDR = 5, FORWARD = 6, FORWARDSSL=7;
+    private static final int MASTER=8;
     private static final String USAGE = "Usage: SAMStreamSink [-s] [-m mode] [-v version] [-b samHost] [-p samPort]\n" +
                                         "                     [-o opt=val] [-u user] [-w password] myDestFile sinkDir\n" +
                                         "       modes: stream: 0; datagram: 1; v1datagram: 2;\n" +
                                         "              raw: 3; v1raw: 4; raw-with-headers: 5;\n" +
                                         "              stream-forward: 6; stream-forward-ssl: 7\n" +
                                         "       -s: use SSL to connect to bridge\n" +
+                                        "       -x: use master session (forces -v 3.3)\n" +
                                         "       multiple -o session options are allowed";
     private static final int V3FORWARDPORT=9998;
     private static final int V3DGPORT=9999;
 
     public static void main(String args[]) {
-        Getopt g = new Getopt("SAM", args, "sb:m:p:u:v:w:");
+        Getopt g = new Getopt("SAM", args, "sxb:m:p:u:v:w:");
         boolean isSSL = false;
+        boolean isMaster = false;
         int mode = STREAM;
         String version = "1.0";
         String host = "127.0.0.1";
@@ -85,6 +88,10 @@ public class SAMStreamSink {
                 isSSL = true;
                 break;
 
+            case 'x':
+                isMaster = true;
+                break;
+
             case 'm':
                 mode = Integer.parseInt(g.getOptarg());
                 if (mode < 0 || mode > FORWARDSSL) {
@@ -131,6 +138,10 @@ public class SAMStreamSink {
             System.err.println(USAGE);
             return;
         }
+        if (isMaster) {
+            mode += MASTER;
+            version = "3.3";
+        }
         if ((user == null && password != null) ||
             (user != null && password == null)) {
             System.err.println("both user and password or neither");
@@ -169,6 +180,8 @@ public class SAMStreamSink {
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("Reader created");
             String ourDest = handshake(out, version, true, eventHandler, mode, user, password, sessionOpts);
+            if (mode >= MASTER)
+                mode -= MASTER;
             if (ourDest == null)
                 throw new IOException("handshake failed");
             if (_log.shouldLog(Log.DEBUG))
@@ -560,7 +573,10 @@ public class SAMStreamSink {
         return sock;
     }
     
-    /** @return our b64 dest or null */
+    /**
+     * @param isMaster is this the control socket
+     * @return our b64 dest or null
+     */
     private String handshake(OutputStream samOut, String version, boolean isMaster,
                              SAMEventHandler eventHandler, int mode, String user, String password,
                              String sopts) {
@@ -641,6 +657,16 @@ public class SAMStreamSink {
                     // and give it to the SAM server
                     dest = _destFile;
                 }
+                boolean masterMode;  // are we using v3.3 master session
+                String command;
+                if (mode >= MASTER) {
+                    masterMode = true;
+                    command = "ADD";
+                    mode -= MASTER;
+                } else {
+                    masterMode = false;
+                    command = "CREATE DESTINATION=" + dest;
+                }
                 String style;
                 if (mode == STREAM || mode == FORWARD || mode == FORWARDSSL)
                     style = "STREAM";
@@ -654,17 +680,36 @@ public class SAMStreamSink {
                     style = "RAW PORT=" + V3DGPORT;
                 else
                     style = "RAW HEADER=true PORT=" + V3DGPORT;
-                String req = "SESSION CREATE STYLE=" + style + " DESTINATION=" + dest + ' ' + _conOptions + ' ' + sopts + '\n';
+
+                if (masterMode) {
+                    String req = "SESSION CREATE DESTINATION=" + dest + " STYLE=MASTER ID=masterSink " + sopts + '\n';
+                    samOut.write(req.getBytes("UTF-8"));
+                    samOut.flush();
+                    if (_log.shouldLog(Log.DEBUG))
+                        _log.debug("SESSION CREATE STYLE=MASTER sent");
+                    boolean ok = eventHandler.waitForSessionCreateReply();
+                    if (!ok) 
+                        throw new IOException("SESSION CREATE STYLE=MASTER failed");
+                    if (_log.shouldLog(Log.DEBUG))
+                        _log.debug("SESSION CREATE STYLE=MASTER reply found: " + ok);
+                }
+
+                String req = "SESSION " + command + " STYLE=" + style + ' ' + _conOptions + ' ' + sopts + '\n';
                 samOut.write(req.getBytes("UTF-8"));
                 samOut.flush();
                 if (_log.shouldLog(Log.DEBUG))
-                    _log.debug("Session create sent");
+                    _log.debug("SESSION " + command + " sent");
                 if (mode == STREAM) {
-                    boolean ok = eventHandler.waitForSessionCreateReply();
+                    // why only waiting in stream mode?
+                    boolean ok;
+                    if (masterMode)
+                        ok = eventHandler.waitForSessionAddReply();
+                    else
+                        ok = eventHandler.waitForSessionCreateReply();
                     if (!ok) 
-                        throw new IOException("Session create failed");
+                        throw new IOException("SESSION " + command + " failed");
                     if (_log.shouldLog(Log.DEBUG))
-                        _log.debug("Session create reply found: " + ok);
+                        _log.debug("SESSION " + command + " reply found: " + ok);
                 }
                 req = "NAMING LOOKUP NAME=ME\n";
                 samOut.write(req.getBytes("UTF-8"));