diff --git a/src/sbbs3/js_bbs.cpp b/src/sbbs3/js_bbs.cpp
index 4585cc593f1f6f4af2cb7140203cb27b1c1b4ab0..8b156030a8aebc30e0a6acfdcb7032a8e41ad026 100644
--- a/src/sbbs3/js_bbs.cpp
+++ b/src/sbbs3/js_bbs.cpp
@@ -8,7 +8,7 @@
  * @format.tab-size 4		(Plain Text/Source Code File Header)			*
  * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
  *																			*
- * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2012 Rob Swindell - http://www.synchro.net/copyright.html		*
  *																			*
  * This program is free software; you can redistribute it and/or			*
  * modify it under the terms of the GNU General Public License				*
@@ -129,6 +129,7 @@ enum {
 	,BBS_PROP_MSG_NUMBER
 	,BBS_PROP_MSG_EXPIRATION
 	,BBS_PROP_MSG_FORWARDED
+	,BBS_PROP_MSG_THREAD_ID
 	,BBS_PROP_MSG_THREAD_BACK
 	,BBS_PROP_MSG_THREAD_NEXT
 	,BBS_PROP_MSG_THREAD_FIRST
@@ -227,12 +228,13 @@ enum {
 	,"message auxillary attributes"
 	,"message network attributes"
 	,"message header offset"
-	,"message number"
+	,"message number (unique, monotonically incrementing)"
 	,"message expiration"
 	,"message forwarded"
-	,"message thread, back message number (AKA msg_thread_orig)"
+	,"message thread identifier (0 if unknown)"
+	,"message thread, back message number"
 	,"message thread, next message number"
-	,"message thread, first reply to this message"
+	,"message thread, message number of first reply to this message"
 	,"message identifier"
 	,"message replied-to identifier"
 	,"message delivery attempt counter"
@@ -587,6 +589,10 @@ static JSBool js_bbs_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 			if(sbbs->current_msg!=NULL)
 				val=sbbs->current_msg->forwarded;
 			break;
+		case BBS_PROP_MSG_THREAD_ID:
+			if(sbbs->current_msg!=NULL)
+				val=sbbs->current_msg->hdr.thread_id;
+			break;
 		case BBS_PROP_MSG_THREAD_BACK:
 			if(sbbs->current_msg!=NULL)
 				val=sbbs->current_msg->hdr.thread_back;
@@ -911,6 +917,7 @@ static jsSyncPropertySpec js_bbs_properties[] = {
 	{	"msg_number"		,BBS_PROP_MSG_NUMBER		,PROP_READONLY	,310},
 	{	"msg_expiration"	,BBS_PROP_MSG_EXPIRATION	,PROP_READONLY	,310},
 	{	"msg_forwarded"		,BBS_PROP_MSG_FORWARDED		,PROP_READONLY	,310},
+	{	"msg_thread_id"		,BBS_PROP_MSG_THREAD_BACK	,PROP_READONLY	,316},
 	{	"msg_thread_back"	,BBS_PROP_MSG_THREAD_BACK	,PROP_READONLY	,312},
 	{	"msg_thread_orig"	,BBS_PROP_MSG_THREAD_BACK	,JSPROP_READONLY,310},	/* alias */
 	{	"msg_thread_next"	,BBS_PROP_MSG_THREAD_NEXT	,PROP_READONLY	,310},
diff --git a/src/sbbs3/js_msgbase.c b/src/sbbs3/js_msgbase.c
index 8ac08b9abf606aa661b99f2712310451bb97f41e..5f5e3774a2075f0ea92ccbd1896824f1113bedac 100644
--- a/src/sbbs3/js_msgbase.c
+++ b/src/sbbs3/js_msgbase.c
@@ -8,7 +8,7 @@
  * @format.tab-size 4		(Plain Text/Source Code File Header)			*
  * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
  *																			*
- * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2012 Rob Swindell - http://www.synchro.net/copyright.html		*
  *																			*
  * This program is free software; you can redistribute it and/or			*
  * modify it under the terms of the GNU General Public License				*
@@ -704,6 +704,11 @@ static BOOL parse_header_object(JSContext* cx, private_t* p, JSObject* hdr, smbm
 		msg->hdr.when_imported.zone=(short)i32;
 	}
 
+	if(JS_GetProperty(cx, hdr, "thread_id", &val) && !JSVAL_NULL_OR_VOID(val)) {
+		if(!JS_ValueToInt32(cx,val,&i32))
+			return FALSE;
+		msg->hdr.thread_id=i32;
+	}
 	if((JS_GetProperty(cx, hdr, "thread_orig", &val) 
 			|| JS_GetProperty(cx, hdr, "thread_back", &val)) && !JSVAL_NULL_OR_VOID(val)) {
 		if(!JS_ValueToInt32(cx,val,&i32))
@@ -1045,8 +1050,9 @@ static JSBool js_get_msg_header_resolve(JSContext *cx, JSObject *obj, jsid id)
 	LAZY_UINTEGER("when_imported_time", p->msg.hdr.when_imported.time, JSPROP_ENUMERATE);
 	LAZY_INTEGER("when_imported_zone", p->msg.hdr.when_imported.zone, JSPROP_ENUMERATE);
 	LAZY_INTEGER("when_imported_zone_offset", smb_tzutc(p->msg.hdr.when_imported.zone), JSPROP_ENUMERATE|JSPROP_READONLY);
+	LAZY_UINTEGER("thread_id", p->msg.hdr.thread_id, JSPROP_ENUMERATE);
 	LAZY_UINTEGER("thread_back", p->msg.hdr.thread_back, JSPROP_ENUMERATE);
-	LAZY_UINTEGER("thread_orig", p->msg.hdr.thread_back, JSPROP_ENUMERATE);
+	LAZY_UINTEGER("thread_orig", p->msg.hdr.thread_back, 0);
 	LAZY_UINTEGER("thread_next", p->msg.hdr.thread_next, JSPROP_ENUMERATE);
 	LAZY_UINTEGER("thread_first", p->msg.hdr.thread_first, JSPROP_ENUMERATE);
 	LAZY_UINTEGER("delivery_attempts", p->msg.hdr.delivery_attempts, JSPROP_ENUMERATE);
@@ -2251,6 +2257,7 @@ static jsSyncMethodSpec js_msgbase_functions[] = {
 	"<tr><td align=top><tt>when_imported_time</tt><td>Date/time message was imported"
 	"<tr><td align=top><tt>when_imported_zone</tt><td>Time zone (in SMB format)"
 	"<tr><td align=top><tt>when_imported_zone_offset</tt><td>Time zone in minutes east of UTC"
+	"<tr><td align=top><tt>thread_id</tt><td>Thread identifier (originating message number)"
 	"<tr><td align=top><tt>thread_back</tt><td>Message number that this message is a reply to"
 	"<tr><td align=top><tt>thread_next</tt><td>Message number of the next reply to the original message in this thread"
 	"<tr><td align=top><tt>thread_first</tt><td>Message number of the first reply to this message"
diff --git a/src/sbbs3/postmsg.cpp b/src/sbbs3/postmsg.cpp
index 2d91b891c67ab75d39dbb2b3d5e395c7fe098eff..fc21c5863f6773aae4a624aa27686a97c0f9e0e1 100644
--- a/src/sbbs3/postmsg.cpp
+++ b/src/sbbs3/postmsg.cpp
@@ -8,7 +8,7 @@
  * @format.tab-size 4		(Plain Text/Source Code File Header)			*
  * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
  *																			*
- * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2012 Rob Swindell - http://www.synchro.net/copyright.html		*
  *																			*
  * This program is free software; you can redistribute it and/or			*
  * modify it under the terms of the GNU General Public License				*
@@ -323,6 +323,9 @@ bool sbbs_t::postmsg(uint subnum, smbmsg_t *remsg, long wm_mode)
 
 		msg.hdr.thread_back=remsg->hdr.number;	/* needed for threading backward */
 
+		if((msg.hdr.thread_id=remsg->hdr.thread_id)) == 0)
+			msg.hdr.thread_id=remsg->hdr.number;
+
 		/* Add RFC-822 Reply-ID (generate if necessary) */
 		if(remsg->id!=NULL)
 			smb_hfield_str(&msg,RFC822REPLYID,remsg->id);
diff --git a/src/sbbs3/readmsgs.cpp b/src/sbbs3/readmsgs.cpp
index 743f6a447b7d596897cdcb35e9386d5183d15950..544ba92b51403546d22a5b1dc8051bcb0cf4812e 100644
--- a/src/sbbs3/readmsgs.cpp
+++ b/src/sbbs3/readmsgs.cpp
@@ -8,7 +8,7 @@
  * @format.tab-size 4		(Plain Text/Source Code File Header)			*
  * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
  *																			*
- * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2012 Rob Swindell - http://www.synchro.net/copyright.html		*
  *																			*
  * This program is free software; you can redistribute it and/or			*
  * modify it under the terms of the GNU General Public License				*
@@ -140,6 +140,8 @@ void sbbs_t::msghdr(smbmsg_t* msg)
 	bprintf("%-16.16s %u\r\n"	 ,"header length"	,msg->hdr.length);
 
 	/* optional fixed fields */
+	if(msg->hdr.thread_id)
+		bprintf("%-16.16s %ld\r\n"	,"thread_id"		,msg->hdr.thread_id);
 	if(msg->hdr.thread_back)
 		bprintf("%-16.16s %ld\r\n"	,"thread_back"		,msg->hdr.thread_back);
 	if(msg->hdr.thread_next)