diff --git a/account/tbacct.in b/account/tbacct.in
index 5f15dcae6c57995e06857f1ba4bc4a1ca7382ec9..26253e84d1588573800a598e1b135609c535e02c 100644
--- a/account/tbacct.in
+++ b/account/tbacct.in
@@ -2,7 +2,7 @@
 
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2004 University of Utah and the Flux Group.
+# Copyright (c) 2000-2005 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -40,6 +40,7 @@ my $TBLOGS	= "@TBLOGSEMAIL@";
 my $CONTROL	= "@USERNODE@";
 my $BOSSNODE	= "@BOSSNODE@";
 my $WITHSFS	= @SFSSUPPORT@;
+my $WIKISUPPORT = @WIKISUPPORT@;
 
 my $SAMBANODE	= "fs";  # DNS makes this do the right thing in E-in-E.
 my $SMBPASSWD	= "/usr/local/bin/smbpasswd";
@@ -58,6 +59,8 @@ my $MKUSERCERT	= "$TB/sbin/mkusercert";
 my $SFSUPDATE	= "$TB/sbin/sfskey_update";
 my $PBAG	= "$TB/sbin/paperbag";
 my $EXPORTSSETUP= "$TB/sbin/exports_setup";
+my $ADDWIKIUSER = "$TB/sbin/addwikiuser";
+my $DELWIKIUSER = "$TB/sbin/delwikiuser";
 my $NOLOGIN	= "/sbin/nologin";
 my $SSH		= "$TB/bin/sshtb";
 my $SAVEUID	= $UID;
@@ -319,6 +322,10 @@ sub AddUser()
     system("$GENELISTS -u $user")
 	if (! $batch);
 
+    # And to the wiki if enabled.
+    system("$ADDWIKIUSER $user")
+	if ($WIKISUPPORT && !$batch);
+
     # Generate the SSL cert for the user.
     system("$MKUSERCERT $user");
 
@@ -394,6 +401,11 @@ sub DelUser()
 
     # Remove from elists.
     system("$GENELISTS -u $user");
+
+    # And to the wiki if enabled.
+    system("$DELWIKIUSER $user")
+	if ($WIKISUPPORT);
+    
     $EUID = 0;
 
     $sfsupdate = 1;
@@ -426,6 +438,13 @@ sub UpdatePassword()
 	}
     }
     $UID = $SAVEUID;
+
+    $EUID = $UID;
+    # And to the wiki if enabled.
+    system("$ADDWIKIUSER -u $user")
+	if ($WIKISUPPORT);
+    $EUID = 0;
+    
     return 0;
 }
 
diff --git a/configure b/configure
index da0e25db6af1c49a6b9645689b9160add78055b1..65c2dea627c3872eccdc93345c55d7cc70273441 100755
--- a/configure
+++ b/configure
@@ -1305,6 +1305,7 @@ fi
 
 
 
+
 
 
 #
@@ -1353,6 +1354,7 @@ OUTERBOSS_SSLCERTNAME="/etc/outer_emulab.pem"
 PLABSUPPORT=0
 PLAB_ROOTBALL="change.me"
 PLAB_SLICEPREFIX="utah_elab"
+WIKISUPPORT=0
 TBLOGFACIL="local5"
 LINKTEST_NSPATH="/share/linktest-ns"
 BOSSEVENTPORT=2927
@@ -1852,17 +1854,17 @@ for ac_hdr in ulxmlrpcpp/ulxr_config.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:1856: checking for $ac_hdr" >&5
+echo "configure:1858: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1861 "configure"
+#line 1863 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1866: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1868: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -1901,17 +1903,17 @@ for ac_hdr in linux/videodev.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:1905: checking for $ac_hdr" >&5
+echo "configure:1907: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1910 "configure"
+#line 1912 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1915: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1917: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -1944,7 +1946,7 @@ done
 # Extract the first word of "gtk-config", so it can be a program name with args.
 set dummy gtk-config; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:1948: checking for $ac_word" >&5
+echo "configure:1950: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_GTK_CONFIG'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2023,7 +2025,7 @@ fi
 # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
 # ./install, which can be erroneously created by make from ./install.sh.
 echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
-echo "configure:2027: checking for a BSD compatible install" >&5
+echo "configure:2029: checking for a BSD compatible install" >&5
 if test -z "$INSTALL"; then
 if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2084,7 +2086,7 @@ esac
 # Extract the first word of "rsync", so it can be a program name with args.
 set dummy rsync; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:2088: checking for $ac_word" >&5
+echo "configure:2090: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_RSYNC'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2265,7 +2267,10 @@ outfiles="$outfiles Makeconf GNUmakefile \
 	robots/mezzanine/libmezz/GNUmakefile \
 	robots/mezzanine/mezzanine/GNUmakefile \
 	robots/tracker/GNUmakefile \
-	robots/mezzanine/mezzcal/GNUmakefile robots/robomonitord "
+	robots/mezzanine/mezzcal/GNUmakefile robots/robomonitord \
+        wiki/GNUmakefile wiki/addwikiuser wiki/wikiproxy \
+	wiki/usertemplate wiki/webhometemplate \
+        wiki/delwikiuser wiki/setwikigroups wiki/addwikiproj "
 
 #
 # Do this for easy distclean.
@@ -2476,6 +2481,7 @@ s%@OUTERBOSS_NODENAME@%$OUTERBOSS_NODENAME%g
 s%@OUTERBOSS_XMLRPCPORT@%$OUTERBOSS_XMLRPCPORT%g
 s%@OUTERBOSS_SSLCERTNAME@%$OUTERBOSS_SSLCERTNAME%g
 s%@PLABSUPPORT@%$PLABSUPPORT%g
+s%@WIKISUPPORT@%$WIKISUPPORT%g
 s%@TBLOGFACIL@%$TBLOGFACIL%g
 s%@PLAB_ROOTBALL@%$PLAB_ROOTBALL%g
 s%@PLAB_SLICEPREFIX@%$PLAB_SLICEPREFIX%g
diff --git a/configure.in b/configure.in
index 53fe4ba3850e53a5b58e61d1f0707ab07c364a1b..90640b23476d688151784c7d750c9d271607463f 100755
--- a/configure.in
+++ b/configure.in
@@ -109,6 +109,7 @@ AC_SUBST(OUTERBOSS_NODENAME)
 AC_SUBST(OUTERBOSS_XMLRPCPORT)
 AC_SUBST(OUTERBOSS_SSLCERTNAME)
 AC_SUBST(PLABSUPPORT)
+AC_SUBST(WIKISUPPORT)
 AC_SUBST(TBLOGFACIL)
 AC_SUBST(PLAB_ROOTBALL)
 AC_SUBST(PLAB_SLICEPREFIX)
@@ -188,6 +189,7 @@ OUTERBOSS_SSLCERTNAME="/etc/outer_emulab.pem"
 PLABSUPPORT=0
 PLAB_ROOTBALL="change.me"
 PLAB_SLICEPREFIX="utah_elab"
+WIKISUPPORT=0
 TBLOGFACIL="local5"
 LINKTEST_NSPATH="/share/linktest-ns"
 BOSSEVENTPORT=2927
@@ -760,7 +762,10 @@ outfiles="$outfiles Makeconf GNUmakefile \
 	robots/mezzanine/libmezz/GNUmakefile \
 	robots/mezzanine/mezzanine/GNUmakefile \
 	robots/tracker/GNUmakefile \
-	robots/mezzanine/mezzcal/GNUmakefile robots/robomonitord "
+	robots/mezzanine/mezzcal/GNUmakefile robots/robomonitord \
+        wiki/GNUmakefile wiki/addwikiuser wiki/wikiproxy \
+	wiki/usertemplate wiki/webhometemplate \
+        wiki/delwikiuser wiki/setwikigroups wiki/addwikiproj "
 
 #
 # Do this for easy distclean.
diff --git a/defs-default b/defs-default
index 2fd53a936bfb06ac8233fe4fcffab89de63378ef..f3789b388b5d849c47eb1bee25c3c56cd58af9e3 100644
--- a/defs-default
+++ b/defs-default
@@ -34,6 +34,7 @@ TBMAINSITE=1
 THISHOMEBASE=Emulab.Net
 PLABSUPPORT=1
 PLAB_ROOTBALL="plabroot-10.tar.bz2"
+WIKISUPPORT=1
 #
 # SSL Certificate stuff. Used to customize config files in ssl directory.
 # Note that OrganizationalUnit is set in the cnf file.
diff --git a/tbsetup/mkproj.in b/tbsetup/mkproj.in
index 19ecdf03f10af122d6785d035c8e663843cea3e6..c60f1270cf8bcbb297d35af182fbfa10bd969848 100755
--- a/tbsetup/mkproj.in
+++ b/tbsetup/mkproj.in
@@ -25,6 +25,8 @@ my $TBOPS    = "@TBOPSEMAIL@";
 my $MKGROUP  = "$TB/sbin/mkgroup";
 my $MKACCT   = "$TB/sbin/tbacct add";
 my $GRANTTYPE= "$TB/sbin/grantnodetype -d";
+my $WIKISUPPORT = @WIKISUPPORT@;
+my $ADDWIKIPROJ = "$TB/sbin/addwikiproj";
 
 my $PROJROOT = "/proj";
 my $GRPROOT  = "/groups";
@@ -125,6 +127,11 @@ $EUID = $UID;
 system("$MKGROUP $pid $pid") == 0 or
     fatal("$MKGROUP $pid failed!");
 
+if ($WIKISUPPORT) {
+    system("$ADDWIKIPROJ $pid") == 0 or
+	fatal("$ADDWIKIPROJ $pid failed!");
+}
+
 system("$MKACCT $projhead") == 0 or
     fatal("$MKACCT $projhead failed!");
 
diff --git a/tbsetup/setgroups.in b/tbsetup/setgroups.in
index 0120acd976d776ab9df6a1b6c7532f35701fa2af..802526f1206eb3601cd5149b2f3b96929e6eb5d4 100755
--- a/tbsetup/setgroups.in
+++ b/tbsetup/setgroups.in
@@ -2,7 +2,7 @@
 
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2004 University of Utah and the Flux Group.
+# Copyright (c) 2000-2005 University of Utah and the Flux Group.
 # All rights reserved.
 #
 
@@ -39,10 +39,13 @@ my $TBLOGS  = "@TBLOGSEMAIL@";
 my $CONTROL = "@USERNODE@";
 my $BOSSNODE= "@BOSSNODE@";
 my $ADMINGRP= "@TBADMINGROUP@";
+my $WIKISUPPORT   = @WIKISUPPORT@;
+my $SETWIKIGROUPS = "$TB/sbin/setwikigroups";
 
 my $SSH     = "$TB/bin/sshtb";
 my $GENELISTS="$TB/sbin/genelists";
 my $USERMOD = "/usr/sbin/pw usermod";
+my $SAVEUID = $UID;
 
 my $dbuid;
 my @userlist;
@@ -202,7 +205,7 @@ foreach my $uid (@userlist) {
     #
     # Form a list of project (group) membership names. We do this in two
     # steps to ensure that we get the default group membership since we
-    # want that to be the user's primary group. Not sure this really matters
+    # want that to be the users primary group. Not sure this really matters
     # all that much, but might as well.
     #
     $query_result =
@@ -363,6 +366,17 @@ else {
     }
 }
 
+# and the twiki.
+if ($WIKISUPPORT) {
+    $UID  = $SAVEUID;
+    $EUID = $UID;
+
+    foreach $user (@userlist) {
+	system("$SETWIKIGROUPS $user") == 0 or
+	    fatal("$SETWIKIGROUPS $user failed!");
+    }
+}
+
 print "Group Update Completed!\n";
 exit(0);
 
diff --git a/www/defs.php3.in b/www/defs.php3.in
index 758831a382d35d4a57cb852e2244f9393e60e729..f82c7baec92edb90aa752c902685d802e643bd3f 100644
--- a/www/defs.php3.in
+++ b/www/defs.php3.in
@@ -15,6 +15,8 @@ $TBDOCBASE	= "@TBDOCBASE@";
 $TBWWW		= "@TBWWW@";
 $THISHOMEBASE	= "@THISHOMEBASE@";
 $ELABINELAB     = @ELABINELAB@;
+$WIKISUPPORT    = @WIKISUPPORT@;
+$WIKIURL        = "https://${USERNODE}/twiki/bin/logon";
 
 $TBMAILADDR_OPS		= "@TBOPSEMAIL_NOSLASH@";
 $TBMAILADDR_WWW		= "@TBWWWEMAIL_NOSLASH@";
diff --git a/www/menu.php3 b/www/menu.php3
index 06ff138bb356256e083359d55c0c05758ede7422..eb14e98004aa2cd13d23bc731890595f4d562e05 100644
--- a/www/menu.php3
+++ b/www/menu.php3
@@ -1,7 +1,7 @@
 <?php
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2004 University of Utah and the Flux Group.
+# Copyright (c) 2000-2005 University of Utah and the Flux Group.
 # All rights reserved.
 #
 
@@ -221,7 +221,8 @@ function WRITEPLABBOTTOMBAR() {
 #
 function WRITESIDEBAR() {
     global $login_status, $login_uid;
-    global $TBBASE, $TBDOCBASE, $BASEPATH;
+    global $TBBASE, $TBDOCBASE, $BASEPATH, $WIKISUPPORT, $WIKIURL;
+    global $CHECKLOGIN_WIKINAME;
     global $THISHOMEBASE;
 
     #
@@ -377,6 +378,14 @@ function WRITESIDEBAR() {
 		WRITESIDEBARBUTTON("My Emulab",
 				   $TBBASE,
 				   "showuser.php3?target_uid=$login_uid");
+
+		if ($WIKISUPPORT && $CHECKLOGIN_WIKINAME != "") {
+		    $wikiname = $CHECKLOGIN_WIKINAME;
+		
+		    WRITESIDEBARBUTTON_ABSCOOL("My Wikis",
+					       "${WIKIURL}/Main/$wikiname",
+					       "${WIKIURL}/Main/$wikiname");
+		}
 	    
                 # Since a user can be a member of more than one project,
                 # display this option, and let the form decide if the 
diff --git a/www/showstuff.php3 b/www/showstuff.php3
index c6cbca01e5f2a4207045f1660adef3071c3fab18..9cab86341d0220733bcd93db6c3135b2e8c4b8c7 100644
--- a/www/showstuff.php3
+++ b/www/showstuff.php3
@@ -14,10 +14,12 @@
 # A project
 #
 function SHOWPROJECT($pid, $thisuid) {
-    global $TBDBNAME;
+    global $WIKISUPPORT, $WIKIURL;
 
     $query_result =
-	DBQueryFatal("SELECT * FROM projects WHERE pid='$pid'");
+	DBQueryFatal("select p.*,g.wikiname from projects as p ".
+		     "left join groups as g on g.pid=p.pid and g.gid=g.pid ".
+		     "where p.pid='$pid'");
     $row = mysql_fetch_array($query_result);
 
     echo "<center>
@@ -42,6 +44,7 @@ function SHOWPROJECT($pid, $thisuid) {
     $approved           = $row[approved];
     $expt_count         = $row[expt_count];
     $expt_last          = $row[expt_last];
+    $wikiname           = $row[wikiname];
 
     if ($proj_public) {
 	$proj_public = "Yes";
@@ -87,6 +90,16 @@ function SHOWPROJECT($pid, $thisuid) {
               <td class=\"left\">
                   <A href='$proj_URL'>$proj_URL</A></td>
           </tr>\n";
+
+    if ($WIKISUPPORT && isset($wikiname)) {
+	$wikiurl = "${WIKIURL}/$wikiname/WebHome";
+	
+	echo "<tr>
+                  <td>Project Wiki:</td>
+                  <td class=\"left\">
+                      <A href='$wikiurl'>$wikiname</A></td>
+              </tr>\n";
+    }
     
     echo "<tr>
               <td>Publicly Visible: </td>
@@ -370,7 +383,7 @@ function SHOWGROUPMEMBERSHIP($uid) {
 # A User
 #
 function SHOWUSER($uid) {
-    global $TBDBNAME;
+    global $WIKISUPPORT, $WIKIURL;
 
     $userinfo_result =
 	DBQueryFatal("SELECT * from users where uid='$uid'");
@@ -397,6 +410,7 @@ function SHOWUSER($uid) {
     $frozen      = $row['weblogin_frozen'];
     $failcount   = $row['weblogin_failcount'];
     $failstamp   = $row['weblogin_failstamp'];
+    $wikiname    = $row['wikiname'];
 
     if (!strcmp($usr_addr2, ""))
 	$usr_addr2 = "&nbsp";
@@ -452,6 +466,16 @@ function SHOWUSER($uid) {
               <td>Home Page URL:</td>
               <td><A href='$usr_URL'>$usr_URL</A></td>
           </tr>\n";
+
+    if ($WIKISUPPORT && isset($wikiname)) {
+	$wikiurl = "${WIKIURL}/Main/$wikiname";
+	
+	echo "<tr>
+                  <td>Emulab Wiki Page:</td>
+                  <td class=\"left\">
+                      <A href='$wikiurl'>$wikiname</A></td>
+              </tr>\n";
+    }
     
     #echo "<tr>
     #          <td>Expiration date:</td>
diff --git a/www/tbauth.php3 b/www/tbauth.php3
index a6eb0ef3b915e13f0b344a53b9a5bd1a3b854ce4..cfa8ece57344e57c675a5ea0a3284af43ac83bee 100644
--- a/www/tbauth.php3
+++ b/www/tbauth.php3
@@ -1,7 +1,7 @@
 <?php
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2004 University of Utah and the Flux Group.
+# Copyright (c) 2000-2005 University of Utah and the Flux Group.
 # All rights reserved.
 #
 #
@@ -13,6 +13,7 @@
 $CHECKLOGIN_STATUS		= -1;
 $CHECKLOGIN_UID			= 0;
 $CHECKLOGIN_NOLOGINS		= -1;
+$CHECKLOGIN_WIKINAME            = "";
 
 #
 # New Mapping. 
@@ -125,6 +126,7 @@ function GETUID() {
 function CHECKLOGIN($uid) {
     global $TBAUTHCOOKIE, $TBLOGINCOOKIE, $HTTP_COOKIE_VARS, $TBAUTHTIMEOUT;
     global $CHECKLOGIN_STATUS, $CHECKLOGIN_UID, $CHECKLOGIN_NODETYPES;
+    global $CHECKLOGIN_WIKINAME;
     global $nocookieauth;
     #
     # If we already figured this out, do not duplicate work!
@@ -149,7 +151,7 @@ function CHECKLOGIN($uid) {
     $query_result =
 	DBQueryFatal("select NOW()>=u.pswd_expires,l.hashkey,l.timeout, ".
 		     "       status,admin,cvsweb,g.trust,adminoff,webonly, " .
-		     "       user_interface,n.type,u.stud " .
+		     "       user_interface,n.type,u.stud,u.wikiname " .
 		     " from users as u ".
 		     "left join login as l on l.uid=u.uid ".
 		     "left join group_membership as g on g.uid=u.uid ".
@@ -186,6 +188,7 @@ function CHECKLOGIN($uid) {
 
 	$type     = $row[10];
 	$stud     = $row[11];
+	$wikiname = $row[12];
 
 	$CHECKLOGIN_NODETYPES[$type] = 1;
     }
@@ -309,6 +312,8 @@ function CHECKLOGIN($uid) {
 	$CHECKLOGIN_STATUS |= CHECKLOGIN_UNVERIFIED;
     if (strcmp($status, TBDB_USERSTATUS_ACTIVE) == 0)
 	$CHECKLOGIN_STATUS |= CHECKLOGIN_ACTIVE;
+    if (isset($wikiname) && $wikiname != "")
+	$CHECKLOGIN_WIKINAME = $wikiname;
 
     #
     # Set the magic enviroment variable, if appropriate, for the sake of