diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..e892ee722357afacacda556c7fdb951d057d636b
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,90 @@
+"""A setuptools based setup module.
+
+See:
+https://packaging.python.org/en/latest/distributing.html
+https://github.com/pypa/sampleproject
+"""
+
+# Always prefer setuptools over distutils
+from setuptools import setup, find_packages
+# To use a consistent encoding
+from codecs import open
+from os import path
+
+here = path.abspath(path.dirname(__file__))
+
+long_description = 'Transparent WebSocket Proxy'
+
+setup(
+    name='strudelv2_tws',
+
+    version='0.0.1',
+
+    description=long_description,
+    long_description=long_description,
+
+    # The project's main homepage.
+    url='backend',
+
+    # Author details
+    author='Chris Hines',
+    author_email='help@massive.org.au',
+
+    # Choose your license
+    license='MIT',
+
+    # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
+    classifiers=[
+        # How mature is this project? Common values are
+        #   3 - Alpha
+        #   4 - Beta
+        #   5 - Production/Stable
+        'Development Status :: 3 - Alpha',
+
+        # Indicate who your project is intended for
+        'Intended Audience :: Office workers',
+        'Topic :: Software Development :: Build Tools',
+
+        # Pick your license as you wish (should match "license" above)
+        'License :: OSI Approved :: MIT License',
+
+        # Specify the Python versions you support here. In particular, ensure
+        # that you indicate whether you support Python 2, Python 3 or both.
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 2.6',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.3',
+        'Programming Language :: Python :: 3.4',
+        'Programming Language :: Python :: 3.5',
+    ],
+
+    # What does your project relate to?
+    keywords='',
+
+    # You can just specify the packages manually here if your project is
+    # simple. Or you can use find_packages().
+    packages=find_packages(exclude=['contrib', 'docs', 'tests']),
+
+    # Alternatively, if you want to distribute just a my_module.py, uncomment
+    # this:
+    #   py_modules=["my_module"],
+
+    # List run-time dependencies here.  These will be installed by pip when
+    # your project is installed. For an analysis of "install_requires" vs pip's
+    # requirements files see:
+    # https://packaging.python.org/en/latest/requirements.html
+    install_requires=[
+            ],
+
+
+    data_files = [('',[])],
+
+    # To provide executable scripts, use entry points in preference to the
+    # "scripts" keyword. Entry points provide cross-platform support and allow
+    # pip to create the appropriate form of executable for the target platform.
+    entry_points={
+        'console_scripts': [ 'twsproxy=twsproxy:main'],
+        'gui_scripts': [  ]
+    },
+)
diff --git a/twsproxy/__init__.py b/twsproxy/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..26181f58de7eac67a281dbee37d4feefa5f1933b
--- /dev/null
+++ b/twsproxy/__init__.py
@@ -0,0 +1,253 @@
+import threading
+import socket, array
+import select
+import logging
+TES =  'http://localhost:8080/'
+failthresh = 10
+class TWSProxy(threading.Thread):
+
+    TIMEOUT = 10
+    AUTHTIMEOUT = 60
+    MAXBUFF = 8192
+    MAXHEADERS = 8192
+
+    def __init__(self,socket):
+        super().__init__()
+        self.csock = socket
+        self.ssock = None
+        self.authtok = None
+        self._timeout = 10
+
+
+
+    def inittws(self,closed):
+        """ Read from the client socket until one of
+            1) We can verify the request based on headers
+            2) We hit a timeout (i.e. no more headers will be sent and the client is waiting for a response)
+            If we can verify 
+            open a new socket to the server and return this
+        """
+
+        logger = logging.getLogger()
+        bytessofar = 0
+        header=bytearray(TWSProxy.MAXHEADERS)
+        keepreading = True
+        initcount=0
+        while keepreading:
+            initcount=initcount+1
+            r,w,e = select.select([self.csock],[],[],5)
+            if len(r) > 0:
+                partial = self.csock.recv(TWSProxy.MAXBUFF)
+                header[bytessofar:bytessofar+len(partial)] = partial
+                bytessofar = bytessofar + len(partial)
+                logger.debug('inittws, checking headers')
+                port = TWSProxy.verifyauth(header[0:bytessofar])
+                if port is not None:
+                    logger.debug('inittws, found auth token and got port {}'.format(port))
+                    keepreading = False
+                if port is None:
+                    logger.debug('inittws, no authtok found in the first {} bytes'.format(bytessofar))
+            else:
+                logger.debug('inittws, select returned with no more info, verifying headers for the last time')
+                port = TWSProxy.verifyauth(header[0:bytessofar])
+                keepreading = False
+            if initcount > failthresh:
+                logger.debug('inittws, checked headers enough times, got {} bytes with no success'.format(bytessofar))
+                keepreading = False
+
+        if port is not None:
+            self.ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            self.ssock.setblocking(True)
+            try:
+                self.ssock.connect(('127.0.0.1',port))
+                if bytessofar > 0:
+                    logger.debug('inittws: returning the initial {} bytes'.format(bytessofar))
+                    return (header,bytessofar)
+                    #TWSProxy.reliablesend(self.ssock,header[0:bytessofar],bytessofar)
+            except ConnectionRefusedError as e:
+                logger.error('inittws, got my tokens, got my port attempted to send data and it all went pear shaped')
+                self.ssock.close()
+                self.csock.close()
+                closed.set()
+        else:
+            logger.debug('inittws, unable to determine correct port, closing connection')
+            self.csock.close()
+            closed.set()
+
+        
+
+    def run(self):
+        import logging
+        logger = logging.getLogger()
+        logger.debug('starting new thread listening on {}'.format(self.csock))
+        initshutdown = threading.Event()
+        initshutdown.clear()
+        (header, bytessofar) = self.inittws(initshutdown)
+        logger.debug('connecting {} to {}'.format(self.csock,self.ssock))
+        if initshutdown.isSet():
+            logger.debug('NOT connecting {} inittws did not connect us'.format(self.csock))
+
+        t1 = None
+        t2 = None
+        logger.debug('testing initshutdown')
+        if not initshutdown.isSet():
+            logger.debug('creating threads')
+#            TWSProxy.twosocks(self.csock,self.ssock,initshutdown)
+            t1 = threading.Thread(target=TWSProxy.sockcopy, args=(self.ssock, self.csock, initshutdown),name='s2c')
+            t2 = threading.Thread(target=TWSProxy.sockcopy, args=(self.csock, self.ssock, initshutdown),name='c2s')
+            t1.start()
+            t2.start()
+            logger.debug('connections made, threads listening, passing along the initial headers')
+            TWSProxy.reliablesend(self.ssock,header[0:bytessofar],bytessofar)
+            t1.join()
+            t2.join()
+        else:
+            logger.debug('not creating threads, shutdown is set')
+        if self.ssock is not None:
+            self.ssock.close()
+        if self.csock is not None:
+            self.csock.close()
+
+    @staticmethod
+    def verifyauth(header):
+        import re
+        import requests
+        logger = logging.getLogger()
+        token = b'twsproxyauth=(?P<authtok>\w+)[\W|$]'
+        m = re.search(token,header)
+        if m:
+            authtok = m.groupdict()['authtok']
+            logger.debug('authtok found {}'.format(authtok))
+            s = requests.Session()
+            url = TES+'tunnelstat/'+authtok.decode()
+            try:
+                logger.debug('verify auth querying url {}'.format(url))
+                r = s.get(url)
+                port = r.json()
+                if port is None:
+                    logger.debug('authtok found but no tunnel to connect to {}'.format(authtok))
+                else:
+                    logger.debug('authtok found port found {}'.format(port))
+            except:
+                logger.error('authtok found port found {}'.format(port))
+                raise Exception('unable to get a port number for the authtok {}'.format(r.text))
+            return port
+        return None
+#        if m:
+#            print('match verify!',m.group(0))
+#            return 8888 
+#        else:
+#            return None
+
+
+    @staticmethod
+    def reliablesend(socket,buff,msglength):
+        totalsent = 0
+        while totalsent < msglength:
+            sent = socket.send(buff[totalsent:])
+            if sent == 0:
+                raise RuntimeError("socket connection broken")
+            totalsent = totalsent + sent
+
+    @staticmethod
+    def twosocks(client,server,initshutdown):
+        import threading
+        logger=logging.getLogger()
+        closed = False
+        clientopen = True
+        serveropen = True
+        shuttype = socket.SHUT_RD
+        while serveropen or clientopen:
+            r,w,e = select.select([client,server],[],[],TWSProxy.TIMEOUT)
+            if client in r:
+                try:
+                    buff = client.recv(TWSProxy.MAXBUFF)
+                    msglength = len(buff)
+                    if msglength > 0:
+                        TWSProxy.reliablesend(server,buff,msglength)
+                    else:
+                        clientopen = False
+                        server.shutdown(shuttype)
+                except:
+                        clientopen = False
+            if server in r:
+                try:
+                    buff = server.recv(TWSProxy.MAXBUFF)
+                    msglength = len(buff)
+                    if msglength > 0:
+                        TWSProxy.reliablesend(client,buff,msglength)
+                    else:
+                        client.shutdown(shuttype)
+#                        print("server closed socket for reading")
+                        serveropen = False
+                except:
+                        serveropen = False
+        # If the client has finished sending, and we've finished transmitting to the server
+        # The server may still have some data to transmit to the client
+        closed = False
+        while not closed:
+            r,w,e = select.select([server],[],[],TWSProxy.TIMEOUT)
+            buff = server.recv(TWSProxy.MAXBUFF)
+            msglength = len(buff)
+            if msglength > 0:
+                try:
+                    TWSProxy.reliablesend(client,buff,msglength)
+                except BrokenPipeError as e:
+                    pass
+            else:
+                closed = True
+
+
+
+    @staticmethod
+    def sockcopy(src,dest,initshutdown):
+        shuttype = socket.SHUT_RD
+        import threading
+        logger = logging.getLogger()
+        logger.debug('sock copy started')
+        closed = False
+        name = threading.current_thread().name
+        failcount=0
+        failthresh = 10
+        while not closed and failcount < failthresh:
+            r,w,e = select.select([src],[],[],TWSProxy.TIMEOUT)
+            if len(r) > 0:
+                failcount=0
+                buff = None
+                msglength = -1
+                try:
+                    buff = src.recv(TWSProxy.MAXBUFF)
+                    if buff is None:
+                        continue
+                except ConnectionResetError as e:
+                    close = True
+                    continue
+                except Exception as e:
+                    import traceback
+                    logger.error(traceback.format_exc())
+#                    closed = True
+                    continue
+                msglength = len(buff)
+                if msglength > 0:
+                    TWSProxy.reliablesend(dest,buff,msglength)
+                if msglength == 0:
+                    dest.shutdown(shuttype)
+                    initshutdown.set()
+                    closed = True
+            else: 
+                failcount=failcount+1
+
+        if failcount > failthresh:
+            dest.shutdown(shuttype)
+            initshutdown.set()
+
+def main():
+    from . import server
+    import logging
+    logging.basicConfig(filename="/var/log/tws.log",format="%(asctime)s %(levelname)s:%(process)s: %(message)s")
+    logger = logging.getLogger()
+    logger.setLevel(logging.DEBUG)
+    logger.debug("starting TWS proxy")
+    server = server.TWSServer()
+    logger.debug("initialised server object")
+    server.run()
diff --git a/twsproxy/__init__.pyc b/twsproxy/__init__.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e090b12163034609d876d5519901ac3a9a07174b
Binary files /dev/null and b/twsproxy/__init__.pyc differ
diff --git a/twsproxy/__main__.py b/twsproxy/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..aee234e7afd81450fa13ea10d0706a7cc332edf7
--- /dev/null
+++ b/twsproxy/__main__.py
@@ -0,0 +1,10 @@
+from . import server
+import logging
+logging.basicConfig(filename="/var/log/tws.log",format="%(asctime)s %(levelname)s:%(process)s: %(message)s")
+logger = logging.getLogger()
+logger.setLevel(logging.DEBUG)
+
+logger.debug("starting TWS proxy")
+print("starting TWS proxy")
+server = server.TWSServer()
+server.run()
diff --git a/twsproxy/server/__init__.py b/twsproxy/server/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..99ca5fb78823a33dfc1ba0fab5897c69ba420a32
--- /dev/null
+++ b/twsproxy/server/__init__.py
@@ -0,0 +1,41 @@
+import socket
+from .. import TWSProxy
+import logging
+class TWSServer:
+    import socket
+
+    LISTENPORT = 4000
+    MAXCONN = 5
+
+    def run(self):
+        logger = logging.getLogger()
+        logger.debug("starting up server")
+        serversocket = socket.socket(
+                    socket.AF_INET, socket.SOCK_STREAM)
+        #bind the socket to a public host,
+        # and a well-known port
+        for port in range(self.LISTENPORT,self.LISTENPORT+1):
+            try:
+                serversocket.bind(('127.0.0.1', port))
+        #become a server socket
+                serversocket.listen(self.MAXCONN)
+                logger.debug("Server listening on port {}".format(port))
+                break
+            except Exception as e:
+                print(e)
+                pass
+        openconnections = []
+        logger.debug("waiting for a connection")
+        while 1:
+            (clientsocket, address) = serversocket.accept()
+            logger.debug("accepted connection on {}".format(clientsocket))
+            clientsocket.setblocking(True)
+            tunnel = TWSProxy(clientsocket)
+            tunnel.daemon = True
+            tunnel.start()
+            openconnections.append(tunnel)
+            for c in openconnections:
+                if not c.is_alive():
+                    c.join()
+                    openconnections.remove(c)
+
diff --git a/twsproxy/server/__init__.pyc b/twsproxy/server/__init__.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..45968f93cdd9c9776ea3fbd704daed6312114aa6
Binary files /dev/null and b/twsproxy/server/__init__.pyc differ