diff --git a/contrib/fuse/impl_c/blackmatch.py b/contrib/fuse/impl_c/blackmatch.py
index 322406969..3f6558243 100644
--- a/contrib/fuse/impl_c/blackmatch.py
+++ b/contrib/fuse/impl_c/blackmatch.py
@@ -5,25 +5,25 @@ from allmydata.uri import CHKFileURI, NewDirectoryURI, LiteralFileURI
 from allmydata.scripts.common_http import do_http as do_http_req
 from allmydata.util.hashutil import tagged_hash
 from allmydata.util.assertutil import precondition
-from allmydata.util import base32
+from allmydata.util import base32, fileutil
 from allmydata.scripts.common import get_aliases
 
 from twisted.python import usage
+from twisted.internet.protocol import Factory, Protocol
+from twisted.internet import reactor, defer
 
 import base64
+import errno
 import sha
+import socket
+import stat
+import subprocess
 import sys
 import os
 #import pprint
-import errno
-import stat
-# pull in some spaghetti to make this stuff work without fuse-py being installed
-try:
-    import _find_fuse_parts
-    junk = _find_fuse_parts
-    del junk
-except ImportError:
-    pass
+
+# one needs either python-fuse to have been installed in sys.path, or
+# suitable affordances to be made in the build or runtime environment
 import fuse
 
 import time
@@ -31,7 +31,7 @@ import traceback
 import simplejson
 import urllib
 
-VERSIONSTR="0.6"
+VERSIONSTR="0.7"
 
 USAGE = 'usage: tahoe fuse [dir_cap_name] [fuse_options] mountpoint'
 DEFAULT_DIRECTORY_VALIDITY=26
@@ -62,6 +62,14 @@ class TahoeFuseOptions(usage.Options):
         ["cache-timeout", None, 20,
          "Time, in seconds, to cache directory data."],
         ]
+    optFlags = [
+        ['no-split', None,
+         'run stand-alone; no splitting into client and server'],
+        ['server', None,
+         'server mode (should not be used by end users)'],
+        ['server-shutdown', None,
+         'shutdown server (should not be used by end users)'],
+         ]
 
     def __init__(self):
         usage.Options.__init__(self)
@@ -84,6 +92,12 @@ class TahoeFuseOptions(usage.Options):
 
 logfile = file('tfuse.log', 'ab')
 
+def reopen_logfile(fname):
+    global logfile
+    log('switching to %s' % (fname,))
+    logfile.close()
+    logfile = file(fname, 'ab')
+
 def log(msg):
     logfile.write("%s: %s\n" % (time.asctime(), msg))
     #time.sleep(0.1)
@@ -273,31 +287,22 @@ class TahoeFuseFile(object):
         self.log("fgetattr()")
         s = os.fstat(self.fd)
         self.log("fgetattr() -> %r" % (s,))
-        return s
+        return stat_to_dict(s)
 
     @logexc
     def ftruncate(self, len):
         self.log("ftruncate(%r)" % (len,))
         self.file.truncate(len)
 
-class TahoeFuse(fuse.Fuse):
-
-    def __init__(self, tfs, *args, **kw):
-        log("TF: __init__(%r, %r)" % (args, kw))
+class TahoeFuseBase(object):
 
+    def __init__(self, tfs):
+        log("TFB: __init__()")
         self.tfs = tfs
-        #_tfs_ = tfs
-        #class MyFuseFile(TahoeFuseFile):
-            #tfs = _tfs_
-        #self.file_class = MyFuseFile
-        #log("TF: file_class: %r" % (self.file_class,))
-
         self.files = {}
 
-        fuse.Fuse.__init__(self, *args, **kw)
-
     def log(self, msg):
-        log("<TF> %s" % (msg, ))
+        log("<TFB> %s" % (msg, ))
 
     @logexc
     def readlink(self, path):
@@ -379,7 +384,8 @@ class TahoeFuse(fuse.Fuse):
         fs_size = 8*2**40 # 8Tb
         fs_free = 2*2**40 # 2Tb
 
-        s = fuse.StatVfs(f_bsize = preferred_block_size,
+        #s = fuse.StatVfs(f_bsize = preferred_block_size,
+        s = dict(f_bsize = preferred_block_size,
                          f_frsize = block_size,
                          f_blocks = fs_size / block_size,
                          f_bfree = fs_free / block_size,
@@ -389,16 +395,12 @@ class TahoeFuse(fuse.Fuse):
                          f_favail = 2**20, # available files (root)
                          f_flag = 2, # no suid
                          f_namemax = 255) # max name length
+        #self.log('statfs(): %r' % (s,))
         return s
 
     def fsinit(self):
         self.log("fsinit()")
 
-    def main(self, *a, **kw):
-        self.log("main(%r, %r)" % (a, kw))
-
-        return fuse.Fuse.main(self, *a, **kw)
-
     ##################################################################
 
     @logexc
@@ -409,7 +411,8 @@ class TahoeFuse(fuse.Fuse):
             return -errno.ENOENT
         dirlist = ['.', '..'] + node.children.keys()
         self.log('dirlist = %r' % (dirlist,))
-        return [fuse.Direntry(d) for d in dirlist]
+        #return [fuse.Direntry(d) for d in dirlist]
+        return dirlist
 
     @logexc
     def getattr(self, path):
@@ -421,12 +424,13 @@ class TahoeFuse(fuse.Fuse):
             mtime = self.tfs.root.mtime
             s = TStat({}, st_mode=mode, st_nlink=1, st_mtime=mtime)
             self.log('getattr(%r) -> %r' % (path, s))
-            return s
+            #return s
+            return stat_to_dict(s)
             
         parent, name, child = self.tfs.get_parent_name_and_child(path)
         if not child: # implicitly 'or not parent'
             raise ENOENT('No such file or directory')
-        return parent.get_stat(name)
+        return stat_to_dict(parent.get_stat(name))
 
     @logexc
     def access(self, path, mode):
@@ -510,14 +514,197 @@ class TahoeFuse(fuse.Fuse):
         self.log("ftruncate(%r, %r)" % (path, len,))
         return self._get_file(path).ftruncate(len)
 
+class TahoeFuseLocal(TahoeFuseBase, fuse.Fuse):
+    def __init__(self, tfs, *args, **kw):
+        log("TFL: __init__(%r, %r)" % (args, kw))
+        TahoeFuseBase.__init__(self, tfs)
+        fuse.Fuse.__init__(self, *args, **kw)
 
-def launch_tahoe_fuse(tfs, argv):
+    def log(self, msg):
+        log("<TFL> %s" % (msg, ))
+
+    def main(self, *a, **kw):
+        self.log("main(%r, %r)" % (a, kw))
+        return fuse.Fuse.main(self, *a, **kw)
+
+    # overrides for those methods which return objects not marshalled
+    def fgetattr(self, path):
+        return TStat({}, **(TahoeFuseBase.fgetattr(self, path)))
+
+    def getattr(self, path):
+        return TStat({}, **(TahoeFuseBase.getattr(self, path)))
+
+    def statfs(self):
+        return fuse.StatVfs(**(TahoeFuseBase.statfs(self)))
+        #self.log('statfs()')
+        #ret = fuse.StatVfs(**(TahoeFuseBase.statfs(self)))
+        #self.log('statfs(): %r' % (ret,))
+        #return ret
+
+    @logexc
+    def readdir(self, path, offset):
+        return [ fuse.Direntry(d) for d in TahoeFuseBase.readdir(self, path, offset) ]
+
+class TahoeFuseShim(fuse.Fuse):
+    def __init__(self, trpc, *args, **kw):
+        log("TF: __init__(%r, %r)" % (args, kw))
+        self.trpc = trpc
+        fuse.Fuse.__init__(self, *args, **kw)
+
+    def log(self, msg):
+        log("<TFs> %s" % (msg, ))
+
+    @logexc
+    def readlink(self, path):
+        self.log("readlink(%r)" % (path,))
+        return self.trpc.call('readlink', path)
+
+    @logexc
+    def unlink(self, path):
+        self.log("unlink(%r)" % (path,))
+        return self.trpc.call('unlink', path)
+
+    @logexc
+    def rmdir(self, path):
+        self.log("rmdir(%r)" % (path,))
+        return self.trpc.call('unlink', path)
+
+    @logexc
+    def symlink(self, path, path1):
+        self.log("symlink(%r, %r)" % (path, path1))
+        return self.trpc.call('link', path, path1)
+
+    @logexc
+    def rename(self, path, path1):
+        self.log("rename(%r, %r)" % (path, path1))
+        return self.trpc.call('rename', path, path1)
+
+    @logexc
+    def link(self, path, path1):
+        self.log("link(%r, %r)" % (path, path1))
+        return self.trpc.call('link', path, path1)
+
+    @logexc
+    def chmod(self, path, mode):
+        self.log("XX chmod(%r, %r)" % (path, mode))
+        return self.trpc.call('chmod', path, mode)
+
+    @logexc
+    def chown(self, path, user, group):
+        self.log("XX chown(%r, %r, %r)" % (path, user, group))
+        return self.trpc.call('chown', path, user, group)
+
+    @logexc
+    def truncate(self, path, len):
+        self.log("XX truncate(%r, %r)" % (path, len))
+        return self.trpc.call('truncate', path, len)
+
+    @logexc
+    def utime(self, path, times):
+        self.log("XX utime(%r, %r)" % (path, times))
+        return self.trpc.call('utime', path, times)
+
+    @logexc
+    def statfs(self):
+        self.log("statfs()")
+        response = self.trpc.call('statfs')
+        #self.log("statfs(): %r" % (response,))
+        kwargs = dict([ (str(k),v) for k,v in response.items() ])
+        return fuse.StatVfs(**kwargs)
+
+    def fsinit(self):
+        self.log("fsinit()")
+
+    def main(self, *a, **kw):
+        self.log("main(%r, %r)" % (a, kw))
+
+        return fuse.Fuse.main(self, *a, **kw)
+
+    ##################################################################
+
+    @logexc
+    def readdir(self, path, offset):
+        self.log('readdir(%r, %r)' % (path, offset))
+        return [ fuse.Direntry(d) for d in self.trpc.call('readdir', path, offset) ]
+
+    @logexc
+    def getattr(self, path):
+        self.log('getattr(%r)' % (path,))
+        response = self.trpc.call('getattr', path)
+        kwargs = dict([ (str(k),v) for k,v in response.items() ])
+        s = TStat({}, **kwargs)
+        self.log('getattr(%r) -> %r' % (path, s))
+        return s
+
+    @logexc
+    def access(self, path, mode):
+        self.log("access(%r, %r)" % (path, mode))
+        return self.trpc.call('access', path, mode)
+
+    @logexc
+    def mkdir(self, path, mode):
+        self.log("mkdir(%r, %r)" % (path, mode))
+        return self.trpc.call('mkdir', path, mode)
+
+    ##################################################################
+    # file methods
+
+    def open(self, path, flags):
+        self.log('open(%r, %r)' % (path, flags, ))
+        return self.trpc.call('open', path, flags)
+
+    def create(self, path, flags, mode):
+        self.log('create(%r, %r, %r)' % (path, flags, mode))
+        return self.trpc.call('create', path, flags, mode)
+
+    ##
+
+    def read(self, path, size, offset):
+        self.log('read(%r, %r, %r)' % (path, size, offset, ))
+        return self.trpc.call('read', path, size, offset)
+
+    @logexc
+    def write(self, path, buf, offset):
+        self.log("write(%r, -%s-, %r)" % (path, len(buf), offset))
+        return self.trpc.call('write', path, buf, offset)
+
+    @logexc
+    def release(self, path, flags):
+        self.log("release(%r, %r)" % (path, flags,))
+        return self.trpc.call('release', path, flags)
+
+    @logexc
+    def fsync(self, path, isfsyncfile):
+        self.log("fsync(%r, %r)" % (path, isfsyncfile,))
+        return self.trpc.call('fsync', path, isfsyncfile)
+
+    @logexc
+    def flush(self, path):
+        self.log("flush(%r)" % (path,))
+        return self.trpc.call('flush', path)
+
+    @logexc
+    def fgetattr(self, path):
+        self.log("fgetattr(%r)" % (path,))
+        #return self.trpc.call('fgetattr', path)
+        response = self.trpc.call('fgetattr', path)
+        kwargs = dict([ (str(k),v) for k,v in response.items() ])
+        s = TStat({}, **kwargs)
+        self.log('getattr(%r) -> %r' % (path, s))
+        return s
+
+    @logexc
+    def ftruncate(self, path, len):
+        self.log("ftruncate(%r, %r)" % (path, len,))
+        return self.trpc.call('ftruncate', path, len)
+
+
+def launch_tahoe_fuse(tf_class, tobj, argv):
     sys.argv = ['tahoe fuse'] + list(argv)
     log('setting sys.argv=%r' % (sys.argv,))
     config = TahoeFuseOptions()
-    server = TahoeFuse(tfs, version="%prog " +VERSIONSTR+", fuse "+ fuse.__version__,
-                       usage=config.getSynopsis(),
-                       dash_s_do='setsingle')
+    version = "%prog " +VERSIONSTR+", fuse "+ fuse.__version__
+    server = tf_class(tobj, version=version, usage=config.getSynopsis(), dash_s_do='setsingle')
     server.parse(errex=1)
     server.main()
 
@@ -534,6 +721,16 @@ def fingerprint(uri):
         return None
     return base64.b32encode(sha.new(uri).digest()).lower()[:6]
 
+stat_fields = [ 'st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid', 'st_gid', 'st_size',
+                'st_atime', 'st_mtime', 'st_ctime', ]
+def stat_to_dict(statobj, fields=None):
+    if fields is None:
+        fields = stat_fields
+    d = {}
+    for f in fields:
+        d[f] = getattr(statobj, f, None)
+    return d
+
 class TStat(fuse.Stat):
     # in fuse 0.2, these are set by fuse.Stat.__init__
     # in fuse 0.2-pre3 (hardy) they are not. badness unsues if they're missing
@@ -563,10 +760,7 @@ class TStat(fuse.Stat):
         fuse.Stat.__init__(self, **kwargs)
 
     def __repr__(self):
-        d = {}
-        for f in self.fields:
-            d[f] = getattr(self, f, None)
-        return "<Stat%r>" % (d,)
+        return "<Stat%r>" % (stat_to_dict(self),)
 
 class Directory(object):
     def __init__(self, tfs, ro_uri, rw_uri):
@@ -594,7 +788,6 @@ class Directory(object):
 
     def load(self, name=None):
         now = time.time()
-        print 'loading', name or self
         log('%s.loading(%s)' % (self, name))
         url = self.tfs.compose_url("uri/%s?t=json", self.get_uri())
         data = urllib.urlopen(url).read()
@@ -916,18 +1109,259 @@ _tfs = None # to appease pyflakes; is set in main()
 def print_tree():
     log('tree:\n' + _tfs.pprint())
 
-def main(argv):
-    log("\n\nmain(%s)" % (argv,))
 
+def unmarshal(obj):
+    if obj is None or isinstance(obj, int) or isinstance(obj, long) or isinstance(obj, float):
+        return obj
+    elif isinstance(obj, unicode):
+        #log('unmarshal(%r)' % (obj,))
+        return base64.b64decode(obj)
+    elif isinstance(obj, list):
+        return map(unmarshal, obj)
+    elif isinstance(obj, dict):
+        return dict([ (k,unmarshal(v)) for k,v in obj.items() ])
+    else:
+        raise ValueError('object type not int,str,list,dict,none (%s) (%r)' % (type(obj), obj))
+
+def marshal(obj):
+    if obj is None or isinstance(obj, int) or isinstance(obj, long) or isinstance(obj, float):
+        return obj
+    elif isinstance(obj, str):
+        return base64.b64encode(obj)
+    elif isinstance(obj, list) or isinstance(obj, tuple):
+        return map(marshal, obj)
+    elif isinstance(obj, dict):
+        return dict([ (k,marshal(v)) for k,v in obj.items() ])
+    else:
+        raise ValueError('object type not int,str,list,dict,none (%s)' % type(obj))
+
+
+class TRPCProtocol(Protocol):
+    compute_response_sha1 = True
+    log_all_requests = False
+
+    def connectionMade(self):
+        self.buf = []
+
+    def dataReceived(self, data):
+        if data == 'keepalive\n':
+            log('keepalive connection on %r' % (self.transport,))
+            self.keepalive = True
+            return
+
+        if not data.endswith('\n'):
+            self.buf.append(data)
+            return
+        if self.buf:
+            self.buf.append(data)
+            reqstr = ''.join(self.buf)
+            self.buf = []
+            self.dispatch_request(reqstr)
+        else:
+            self.dispatch_request(data)
+
+    def dispatch_request(self, reqstr):
+        try:
+            req = simplejson.loads(reqstr)
+        except ValueError, ve:
+            log(ve)
+            return
+
+        d = defer.maybeDeferred(self.handle_request, req)
+        d.addCallback(self.send_response)
+        d.addErrback(self.send_error)
+
+    def send_error(self, failure):
+        log('failure: %s' % (failure,))
+        if failure.check(TFSIOError):
+            e = failure.value
+            self.send_response(['error', 'errno', e.args[0], e.args[1]])
+        else:
+            self.send_response(['error', 'failure', str(failure)])
+
+    def send_response(self, result):
+        response = simplejson.dumps(result)
+        header = { 'len': len(response), }
+        if self.compute_response_sha1:
+            header['sha1'] = base64.b64encode(sha.new(response).digest())
+        hdr = simplejson.dumps(header)
+        self.transport.write(hdr)
+        self.transport.write('\n')
+        self.transport.write(response)
+        self.transport.loseConnection()
+
+    def connectionLost(self, reason):
+        if hasattr(self, 'keepalive'):
+            log('keepalive connection %r lost, shutting down' % (self.transport,))
+            reactor.callLater(0, reactor.stop)
+
+    def handle_request(self, req):
+        if type(req) is not list or not req or len(req) < 1:
+            return ['error', 'malformed request']
+        if req[0] == 'call':
+            if len(req) < 3:
+                return ['error', 'malformed request']
+            methname = req[1]
+            try:
+                args = unmarshal(req[2])
+            except ValueError, ve:
+                return ['error', 'malformed arguments', str(ve)]
+
+            try:
+                meth = getattr(self.factory.server, methname)
+            except AttributeError, ae:
+                return ['error', 'no such method', str(ae)]
+
+            if self.log_all_requests:
+                log('call %s(%s)' % (methname, ', '.join(map(repr, args))))
+            try:
+                result = meth(*args)
+            except TFSIOError, e:
+                log('errno: %s; %s' % e.args)
+                return ['error', 'errno', e.args[0], e.args[1]]
+            except Exception, e:
+                log('exception: ' + traceback.format_exc())
+                return ['error', 'exception', str(e)]
+            d = defer.succeed(None)
+            d.addCallback(lambda junk: result) # result may be Deferred
+            d.addCallback(lambda res: ['result', marshal(res)]) # only applies if not errback
+            return d
+
+class TFSServer(object):
+    def __init__(self, socket_path, server=None):
+        self.socket_path = socket_path
+        log('TFSServer init socket: %s' % (socket_path,))
+
+        self.factory = Factory()
+        self.factory.protocol = TRPCProtocol
+        if server:
+            self.factory.server = server
+        else:
+            self.factory.server = self
+
+    def get_service(self):
+        if not hasattr(self, 'svc'):
+            from twisted.application import strports
+            self.svc = strports.service('unix:'+self.socket_path, self.factory)
+        return self.svc
+
+    def run(self):
+        svc = self.get_service()
+        def ss():
+            try:
+                svc.startService()
+            except:
+                reactor.callLater(0, reactor.stop)
+                raise
+        reactor.callLater(0, ss)
+        reactor.run()
+
+    def hello(self):
+        return 'pleased to meet you'
+
+    def echo(self, arg):
+        return arg
+
+    def failex(self):
+        raise ValueError('expected')
+
+    def fail(self):
+        return defer.maybeDeferred(self.failex)
+
+class RPCError(RuntimeError):
+    pass
+
+class TRPC(object):
+    def __init__(self, socket_fname):
+        self.socket_fname = socket_fname
+        self.keepalive = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        self.keepalive.connect(self.socket_fname)
+        self.keepalive.send('keepalive\n')
+        log('requested keepalive on %s' % (self.keepalive,))
+
+    def req(self, req):
+        # open conenction to trpc server
+        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        s.connect(self.socket_fname)
+        # send request
+        s.send(simplejson.dumps(req))
+        s.send('\n')
+        # read response header
+        hdr_data = s.recv(8192)
+        first_newline = hdr_data.index('\n')
+        header = hdr_data[:first_newline]
+        data = hdr_data[first_newline+1:]
+        hdr = simplejson.loads(header)
+        hdr_len = hdr['len']
+        if hdr.has_key('sha1'):
+            hdr_sha1 = base64.b64decode(hdr['sha1'])
+            spool = [data]
+            spool_sha = sha.new(data)
+            # spool response
+            while True:
+                data = s.recv(8192)
+                if data:
+                    spool.append(data)
+                    spool_sha.update(data)
+                else:
+                    break
+        else:
+            spool = [data]
+            # spool response
+            while True:
+                data = s.recv(8192)
+                if data:
+                    spool.append(data)
+                else:
+                    break
+        s.close()
+        # decode response
+        resp = ''.join(spool)
+        spool = None
+        assert hdr_len == len(resp), str((hdr_len, len(resp), repr(resp)))
+        if hdr.has_key('sha1'):
+            data_sha1 = spool_sha.digest()
+            spool = spool_sha = None
+            assert hdr_sha1 == data_sha1, str((base32.b2a(hdr_sha1), base32.b2a(data_sha1)))
+        #else:
+            #print 'warning, server provided no sha1 to check'
+        return resp
+
+    def call(self, methodname, *args):
+        res = self.req(['call', methodname, marshal(args)])
+
+        result = simplejson.loads(res)
+        if not result or len(result) < 2:
+            raise TypeError('malformed response %r' % (result,))
+        if result[0] == 'error':
+            if result[1] == 'errno':
+                raise TFSIOError(result[2], result[3])
+            else:
+                raise RPCError(*(result[1:])) # error, exception / error, failure
+        elif result[0] == 'result':
+            return unmarshal(result[1])
+        else:
+            raise TypeError('unknown response type %r' % (result[0],))
+
+    def shutdown(self):
+        log('shutdown() closing keepalive %s' % (self.keepalive,))
+        self.keepalive.close()
+
+def main(argv):
+    log("main(%s)" % (argv,))
+
+    # check for version or help options (no args == help)
     if not argv:
         argv = ['--help']
-    if len(argv) == 1 and argv[0] in ['-h', '--help', '--version']:
+    if len(argv) == 1 and argv[0] in ['-h', '--help']:
         config = TahoeFuseOptions()
         print >> sys.stderr, config
         print >> sys.stderr, 'fuse usage follows:'
-        launch_tahoe_fuse(None, argv)
+    if len(argv) == 1 and argv[0] in ['-h', '--help', '--version']:
+        launch_tahoe_fuse(TahoeFuseLocal, None, argv)
         return -2
 
+    # parse command line options
     config = TahoeFuseOptions()
     try:
         #print 'parsing', argv
@@ -937,6 +1371,7 @@ def main(argv):
         print e
         return -1
 
+    # check for which alias or uri is specified
     if config['alias']:
         alias = config['alias']
         #print 'looking for aliases in', config['node-directory']
@@ -944,9 +1379,10 @@ def main(argv):
         if alias not in aliases:
             raise usage.error('Alias %r not found' % (alias,))
         root_uri = aliases[alias]
+        root_name = alias
     elif config['root-uri']:
         root_uri = config['root-uri']
-        alias = 'root-uri'
+        root_name = 'uri_' + base32.b2a(tagged_hash('root_name', root_uri))[:12]
         # test the uri for structural validity:
         try:
             NewDirectoryURI.init_from_string(root_uri)
@@ -955,33 +1391,109 @@ def main(argv):
     else:
         raise usage.error('At least one of --alias or --root-uri must be specified')
 
-
     nodedir = config['node-directory']
     nodeurl = config['node-url']
     if not nodeurl:
         nodeurl = getnodeurl(nodedir)
 
-    # switch to named log file.
-    global logfile
-    fname = 'tfuse.%s.log' % (alias,)
-    log('switching to %s' % (fname,))
-    logfile.close()
-    logfile = file(fname, 'ab')
-    log('\n'+(24*'_')+'init'+(24*'_')+'\n')
+    # allocate socket
+    socket_dir = os.path.join(os.path.expanduser(nodedir), "tfuse.sockets")
+    socket_path = os.path.join(socket_dir, root_name)
+    if len(socket_path) > 103:
+        # try googling AF_UNIX and sun_len for some taste of why this oddity exists.
+        raise OSError(errno.ENAMETOOLONG, 'socket path too long (%s)' % (socket_path,))
+
+    fileutil.make_dirs(socket_dir, 0700)
+    if os.path.exists(socket_path):
+        log('socket exists')
+        if config['server-shutdown']:
+            log('calling shutdown')
+            trpc = TRPC(socket_path)
+            result = trpc.shutdown()
+            log('result: %r' % (result,))
+            log('called shutdown')
+            return
+        else:
+            raise OSError(errno.EEXIST, 'fuse already running (%r exists)' % (socket_path,))
+    elif config['server-shutdown']:
+        raise OSError(errno.ENOTCONN, '--server-shutdown specified, but server not running')
 
     if not os.path.exists(config.mountpoint):
-        raise OSError(2, 'No such file or directory: "%s"' % (config.mountpoint,))
+        raise OSError(errno.ENOENT, 'No such file or directory: "%s"' % (config.mountpoint,))
 
-    cache_timeout = float(config['cache-timeout'])
-    tfs = TFS(nodedir, nodeurl, root_uri, cache_timeout)
-    print tfs.pprint()
-
-    # make tfs instance accesible to print_tree() for dbg
     global _tfs
-    _tfs = tfs
+    #
+    # Standalone ("no-split")
+    #
+    if config['no-split']:
+        reopen_logfile('tfuse.%s.unsplit.log' % (root_name,))
+        log('\n'+(24*'_')+'init (unsplit)'+(24*'_')+'\n')
 
-    args = [ '-o'+opt for opt in config.fuse_options ] + [config.mountpoint]
-    launch_tahoe_fuse(tfs, args)
+        cache_timeout = float(config['cache-timeout'])
+        tfs = TFS(nodedir, nodeurl, root_uri, cache_timeout)
+        #print tfs.pprint()
 
+        # make tfs instance accesible to print_tree() for dbg
+        _tfs = tfs
+
+        args = [ '-o'+opt for opt in config.fuse_options ] + [config.mountpoint]
+        launch_tahoe_fuse(TahoeFuseLocal, tfs, args)
+
+    #
+    # Server
+    #
+    elif config['server']:
+        reopen_logfile('tfuse.%s.server.log' % (root_name,))
+        log('\n'+(24*'_')+'init (server)'+(24*'_')+'\n')
+
+        log('daemonizing')
+        from twisted.scripts._twistd_unix import daemonize
+        daemonize()
+
+        cache_timeout = float(config['cache-timeout'])
+        tfs = TFS(nodedir, nodeurl, root_uri, cache_timeout)
+        #print tfs.pprint()
+
+        # make tfs instance accesible to print_tree() for dbg
+        _tfs = tfs
+
+        log('launching tfs server')
+        tfuse = TahoeFuseBase(tfs)
+        tfs_server = TFSServer(socket_path, tfuse)
+        tfs_server.run()
+        log('tfs server ran, exiting')
+
+    #
+    # Client
+    #
+    else:
+        reopen_logfile('tfuse.%s.client.log' % (root_name,))
+        log('\n'+(24*'_')+'init (client)'+(24*'_')+'\n')
+
+        server_args = [sys.executable, sys.argv[0], '--server'] + argv
+        if 'Allmydata.app/Contents/MacOS' in sys.executable:
+            # in this case blackmatch is the 'fuse' subcommand of the 'tahoe' executable
+            # otherwise we assume blackmatch is being run from source
+            server_args.insert(2, 'fuse')
+        #print 'launching server:', server_args
+        server = subprocess.Popen(server_args)
+        waiting_since = time.time()
+        wait_at_most = 8
+        while not os.path.exists(socket_path):
+            log('waiting for appearance of %r' % (socket_path,))
+            print 'waiting...'
+            time.sleep(1)
+            if time.time() - waiting_since > wait_at_most:
+                log('%r did not appear within %ss' % (socket_path, wait_at_most))
+                raise IOError(2, 'no socket %s' % (socket_path,))
+            print 'running...'
+        #print 'launched server'
+        trpc = TRPC(socket_path)
+
+
+        args = [ '-o'+opt for opt in config.fuse_options ] + [config.mountpoint]
+        launch_tahoe_fuse(TahoeFuseShim, trpc, args)
+
+        
 if __name__ == '__main__':
     sys.exit(main(sys.argv[1:]))
diff --git a/contrib/fuse/runtests.py b/contrib/fuse/runtests.py
index b08d52be0..23de5cfc7 100644
--- a/contrib/fuse/runtests.py
+++ b/contrib/fuse/runtests.py
@@ -68,6 +68,12 @@ implementations = {
                                '--node-directory', '%(nodedir)s', '%(mountpath)s', ],
                    mount_wait=True,
                    suites=['read', 'write', ]),
+    'impl_c_no_split': dict(module=impl_c,
+                   mount_args=['--cache-timeout', '0', '--root-uri', '%(root-uri)s',
+                               '--no-split',
+                               '--node-directory', '%(nodedir)s', '%(mountpath)s', ],
+                   mount_wait=True,
+                   suites=['read', 'write', ]),
     }
 
 if sys.platform == 'darwin':