diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c
index 3d29f4bc7bd5f1e2fa94998586d5dc0f010150af..d4c161655d108aade2ff0cc7536007305263cbab 100644
--- a/src/sbbs3/services.c
+++ b/src/sbbs3/services.c
@@ -327,9 +327,11 @@ js_login(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 	if(argc>2)
 		inc_logons=JSVAL_TO_BOOLEAN(argv[2]);
 
-	SAFECOPY(user.note,client->client->addr);
-	SAFECOPY(user.comp,client->client->host);
-	SAFECOPY(user.modem,client->service->protocol);
+	if(client->client!=NULL) {
+		SAFECOPY(user.note,client->client->addr);
+		SAFECOPY(user.comp,client->client->host);
+		SAFECOPY(user.modem,client->service->protocol);
+	}
 
 	if(inc_logons) {
 		user.logons++;
@@ -353,8 +355,10 @@ js_login(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 		lprintf("%04d %s !JavaScript ERROR creating msg_area object"
 			,client->socket,client->service->protocol);
 
-	client->client->user=user.alias;
-	client_on(client->socket,client->client,TRUE /* update */);
+	if(client->client!=NULL) {
+		client->client->user=user.alias;
+		client_on(client->socket,client->client,TRUE /* update */);
+	}
 
 	memcpy(&client->user,&user,sizeof(user));
 
@@ -480,8 +484,9 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client,
 			break;
 
 		/* Client Object */
-		if(js_CreateClientObject(js_cx, js_glob, "client", service_client->client, sock)==NULL)
-			break;
+		if(service_client->client!=NULL)
+			if(js_CreateClientObject(js_cx, js_glob, "client", service_client->client, sock)==NULL)
+				break;
 
 		/* User Class */
 		if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL) 
@@ -507,6 +512,10 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client,
 			,NULL,0))==NULL)
 			break;
 
+		if(service_client->client==NULL)	/* standalone service */
+			if(js_CreateSocketObject(js_cx, server, "socket", service_client->socket)==NULL)
+				break;
+
 		sprintf(ver,"Synchronet Services %s",revision);
 		val = STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, ver));
 		if(!JS_SetProperty(js_cx, server, "version", &val))
@@ -564,10 +573,49 @@ js_BranchCallback(JSContext *cx, JSScript *script)
     return(JS_TRUE);
 }
 
+static void js_init_cmdline(JSContext* js_cx, JSObject* js_obj, char* spath)
+{
+	char*					p;
+	char*					args=NULL;
+	int						argc=0;
+	JSString*				arg_str;
+	JSObject*				argv;
+	jsval					val;
+
+	p=spath;
+	while(*p && *p>' ') p++;
+	if(*p) {
+		*p=0;	/* truncate arguments */
+		args=p+1;
+	}
+	argv=JS_NewArrayObject(js_cx, 0, NULL);
+
+	if(args!=NULL && argv!=NULL) {
+		while(*args) {
+			p=strchr(args,' ');
+			if(p!=NULL)
+				*p=0;
+			while(*args && *args<=' ') args++; /* Skip spaces */
+			arg_str = JS_NewStringCopyZ(js_cx, args);
+			if(arg_str==NULL)
+				break;
+			val=STRING_TO_JSVAL(arg_str);
+			if(!JS_SetElement(js_cx, argv, argc, &val))
+				break;
+			argc++;
+			if(p==NULL)	/* last arg */
+				break;
+			args+=(strlen(args)+1);
+		}
+	}
+	JS_DefineProperty(js_cx, js_obj, "argv", OBJECT_TO_JSVAL(argv)
+		,NULL,NULL,JSPROP_READONLY);
+	JS_DefineProperty(js_cx, js_obj, "argc", INT_TO_JSVAL(argc)
+		,NULL,NULL,JSPROP_READONLY);
+}
 
 static void js_service_thread(void* arg)
 {
-	char*					p;
 	char*					host_name;
 	HOSTENT*				host;
 	SOCKET					socket;
@@ -575,11 +623,7 @@ static void js_service_thread(void* arg)
 	service_t*				service;
 	service_client_t		service_client;
 	/* JavaScript-specific */
-	char					spath[MAX_PATH];
-	char*					args=NULL;
-	int						argc=0;
-	JSString*				arg_str;
-	JSObject*				argv;
+	char					spath[MAX_PATH+1];
 	JSString*				datagram;
 	JSObject*				js_glob;
 	JSScript*				js_script;
@@ -674,37 +718,8 @@ static void js_service_thread(void* arg)
 
 	/* RUN SCRIPT */
 	sprintf(spath,"%s%s",scfg.exec_dir,service->cmd);
-	p=spath;
-	while(*p && *p>' ') p++;
-	if(*p) {
-		*p=0;	/* truncate arguments */
-		args=p+1;
-	}
 
-	argv=JS_NewArrayObject(js_cx, 0, NULL);
-
-	if(args!=NULL && argv!=NULL) {
-		while(*args) {
-			p=strchr(args,' ');
-			if(p!=NULL)
-				*p=0;
-			while(*args && *args<=' ') args++; /* Skip spaces */
-			arg_str = JS_NewStringCopyZ(js_cx, args);
-			if(arg_str==NULL)
-				break;
-			val=STRING_TO_JSVAL(arg_str);
-			if(!JS_SetElement(js_cx, argv, argc, &val))
-				break;
-			argc++;
-			if(p==NULL)	/* last arg */
-				break;
-			args+=(strlen(args)+1);
-		}
-	}
-	JS_DefineProperty(js_cx, js_glob, "argv", OBJECT_TO_JSVAL(argv)
-		,NULL,NULL,JSPROP_READONLY);
-	JS_DefineProperty(js_cx, js_glob, "argc", INT_TO_JSVAL(argc)
-		,NULL,NULL,JSPROP_READONLY);
+	js_init_cmdline(js_cx, js_glob, spath);
 
 	val = BOOLEAN_TO_JSVAL(JS_FALSE);
 	JS_SetProperty(js_cx, js_glob, "logged_in", &val);
@@ -757,6 +772,117 @@ static void js_service_thread(void* arg)
 	close_socket(socket);
 }
 
+static void js_standalone_service_thread(void* arg)
+{
+	char					spath[MAX_PATH+1];
+	service_t*				service;
+	service_client_t		service_client;
+	/* JavaScript-specific */
+	JSObject*				js_glob;
+	JSScript*				js_script;
+	JSRuntime*				js_runtime;
+	JSContext*				js_cx;
+	jsval					val;
+	jsval					rval;
+
+	// Copy service_client arg
+	service=(service_t*)arg;
+
+	lprintf("%04d %s JavaScript standalone service thread started", service->socket, service->protocol);
+
+	thread_up(TRUE /* setuid */);
+
+	memset(&service_client,0,sizeof(service_client));
+	service_client.socket = service->socket;
+	service_client.service = service;
+
+	if((js_runtime=JS_NewRuntime(startup->js_max_bytes))==NULL
+		|| (js_cx=js_initcx(js_runtime,service->socket,&service_client,&js_glob))==NULL) {
+		lprintf("%04d !%s ERROR initializing JavaScript context"
+			,service->socket,service->protocol);
+		close_socket(service->socket);
+		service->socket=INVALID_SOCKET;
+		thread_down();
+		return;
+	}
+
+	sprintf(spath,"%s%s",scfg.exec_dir,service->cmd);
+	js_init_cmdline(js_cx, js_glob, spath);
+
+	val = BOOLEAN_TO_JSVAL(JS_FALSE);
+	JS_SetProperty(js_cx, js_glob, "logged_in", &val);
+
+	js_script=JS_CompileFile(js_cx, js_glob, spath);
+
+	if(js_script==NULL) 
+		lprintf("%04d !JavaScript FAILED to compile script (%s)",service->socket,spath);
+	else  {
+		JS_SetBranchCallback(js_cx, js_BranchCallback);
+		JS_ExecuteScript(js_cx, js_glob, js_script, &rval);
+		JS_DestroyScript(js_cx, js_script);
+	}
+	JS_DestroyContext(js_cx);	/* Free Context */
+
+	JS_DestroyRuntime(js_runtime);
+
+	thread_down();
+	lprintf("%04d %s JavaScript standalone service thread terminated"
+		,service->socket, service->protocol);
+
+	close_socket(service->socket);
+	service->socket=INVALID_SOCKET;
+}
+
+static void native_standalone_service_thread(void* arg)
+{
+	char					cmd[MAX_PATH];
+	char					fullcmd[MAX_PATH*2];
+	SOCKET					socket_dup;
+	service_t*				service;
+
+	service = (service_t*)arg;
+
+	lprintf("%04d %s standalone service thread started", service->socket, service->protocol);
+
+	thread_up(TRUE /* setuid */);
+
+#ifdef _WIN32
+	if(!DuplicateHandle(GetCurrentProcess(),
+		(HANDLE)socket,
+		GetCurrentProcess(),
+		(HANDLE*)&socket_dup,
+		0,
+		TRUE, // Inheritable
+		DUPLICATE_SAME_ACCESS)) {
+		lprintf("%04d !%s ERROR %d duplicating socket descriptor"
+			,service->socket,service->protocol,GetLastError());
+		close_socket(service->socket);
+		service->socket=INVALID_SOCKET;
+		thread_down();
+		return;
+	}
+#else
+	socket_dup = dup(service->socket);
+#endif
+
+	/* RUN SCRIPT */
+	if(strpbrk(service->cmd,"/\\")==NULL)
+		sprintf(cmd,"%s%s",scfg.exec_dir,service->cmd);
+	else
+		strcpy(cmd,service->cmd);
+	sprintf(fullcmd,cmd,socket_dup);
+
+	system(fullcmd);
+
+	thread_down();
+	lprintf("%04d %s standalone service thread terminated"
+		,socket, service->protocol);
+
+	close_socket(service->socket);
+	service->socket=INVALID_SOCKET;
+	closesocket(socket_dup);	/* close duplicate handle */
+}
+
 static void native_service_thread(void* arg)
 {
 	char					cmd[MAX_PATH];
@@ -852,7 +978,7 @@ static void native_service_thread(void* arg)
 	client_on(socket,&client,FALSE /* update */);
 
 	/* RUN SCRIPT */
-	if(!strchr(service->cmd,BACKSLASH))
+	if(strpbrk(service->cmd,"/\\")==NULL)
 		sprintf(cmd,"%s%s",scfg.exec_dir,service->cmd);
 	else
 		strcpy(cmd,service->cmd);
@@ -1179,6 +1305,20 @@ void DLLCALL services_thread(void* arg)
 			return;
 		}
 
+		/* Setup standalone service threads */
+		for(i=0;i<(int)services;i++) {
+			if(!(service[i].options&SERVICE_OPT_STANDALONE))
+				continue;
+
+			/* start thread here */
+			SAFECOPY(cmd,service[i].cmd);
+			strlwr(cmd);
+			if(strstr(cmd,".js"))	/* JavaScript */
+				_beginthread(js_standalone_service_thread, 0, &service[i]);
+			else					/* Native */
+				_beginthread(native_standalone_service_thread, 0, &service[i]);
+		}
+
 		/* signal caller that we've started up successfully */
 		if(startup->started!=NULL)
     		startup->started();
@@ -1221,6 +1361,8 @@ void DLLCALL services_thread(void* arg)
 			FD_ZERO(&socket_set);	
 			high_socket=0;
 			for(i=0;i<(int)services;i++) {
+				if(service[i].options&SERVICE_OPT_STANDALONE)
+					continue;
 				if(service[i].socket==INVALID_SOCKET)
 					continue;
 				FD_SET(service[i].socket,&socket_set);
diff --git a/src/sbbs3/services.h b/src/sbbs3/services.h
index 4d8e09cc09c28bd12d3f9f8587fbcb8b5b904d13..04dda807fc5c7e5d9a22f326c51984edc0c29e9e 100644
--- a/src/sbbs3/services.h
+++ b/src/sbbs3/services.h
@@ -82,7 +82,8 @@ typedef struct {
 } services_startup_t;
 
 /* Option bit definitions	*/
-#define SERVICE_OPT_UDP		(1<<0)	/* UDP Socket */
+#define SERVICE_OPT_UDP			(1<<0)	/* UDP Socket */
+#define SERVICE_OPT_STANDALONE	(1<<1)	/* Stand alone server */
 
 #ifdef __cplusplus
 extern "C" {