diff --git a/xtrn/bullshit/999-bullshit.xjs b/xtrn/bullshit/999-bullshit.xjs
new file mode 100644
index 0000000000000000000000000000000000000000..5fc008472c9065aee6ea6ffe3e9514f3bf496ea1
--- /dev/null
+++ b/xtrn/bullshit/999-bullshit.xjs
@@ -0,0 +1,13 @@
+<!--Bulletins-->
+
+<?xjs
+	load('../xtrn/bullshit/bullshit.ssjs');
+?>
+
+<div class="well well-sm">
+	<h4>Bulletins</h4>
+</div>
+
+<?xjs 
+	bullshit('bullshit', 5);
+?>
diff --git a/xtrn/bullshit/bullshit.ssjs b/xtrn/bullshit/bullshit.ssjs
new file mode 100644
index 0000000000000000000000000000000000000000..2ac146d10716cf1520ef7d99c2d7ce36e42f6ad8
--- /dev/null
+++ b/xtrn/bullshit/bullshit.ssjs
@@ -0,0 +1,50 @@
+// Bullshit lister function for use with web-v4
+
+load("sbbsdefs.js");
+
+// Deuce's URL-ifier
+function linkify(body) {
+	urlRE=/(?:https?|ftp|telnet|ssh|gopher|rlogin|news):\/\/[^\s'"'<>()]*|[-\w.+]+@(?:[-\w]+\.)+[\w]{2,6}/gi;
+	body=body.replace(
+		urlRE, 
+		function (str) {
+			var ret = ''
+			var p = 0;
+			var link = str.replace(/\.*$/, '');
+			var linktext = link;
+			if (link.indexOf('://') == -1) link = 'mailto:' + link;
+			return (
+				'<a class="ulLink" href="' + link + '">' + linktext + '</a>' +
+				str.substr(linktext.length)
+			);
+		}
+	);
+	return body;
+}
+
+function bullshit(sub, maxMessages) {
+
+	writeln('<div><ul class="list-group">');
+	var msgBase = new MsgBase(sub);
+	if (!msgBase.open()) return;
+	var shown = 0;
+	for (	var m = msgBase.last_msg;
+			m > msgBase.first_msg, shown < maxMessages;
+			m = m - 1
+	) {
+		var header = msgBase.get_msg_header(m);
+		if (header === null || header.attr&MSG_DELETE) continue;
+		writeln(
+			'<li class="list-group-item striped">' +
+			'<strong>' + header.subject + '</strong><br>' +
+			'<em>' + system.timestr(header.when_written_time) + '</em>' +
+			'<div class="message">' +
+			linkify(msgBase.get_msg_body(m)).replace(/\r?\n/g, "<br>") +
+			'</div>' +
+			'</li>'
+		);
+		shown++;
+	}
+	msgBase.close();
+	writeln('</ul></div>');
+}
\ No newline at end of file