diff --git a/apt/APT_Profile.pm.in b/apt/APT_Profile.pm.in
index e6ebdf61f45cc3d8dad64c0e23136cfa1452c529..123c5d6999a68a638d4e57f2583b54c0a022445f 100644
--- a/apt/APT_Profile.pm.in
+++ b/apt/APT_Profile.pm.in
@@ -209,6 +209,8 @@ sub Create($$$$$)
     $query .= ",rspec=$rspec";
     $query .= ",public=1"
 	if (exists($argref->{'public'}) && $argref->{'public'});
+    $query .= ",listed=1"
+	if (exists($argref->{'listed'}) && $argref->{'listed'});
 
     if (! DBQueryWarn($query)) {
 	DBQueryWarn("unlock tables");
@@ -272,5 +274,25 @@ sub Delete($)
     return 0;
 }
 
+#
+# Mark the update time.
+#
+sub MarkModified()
+{
+    my ($self) = @_;
+
+    # Must be a real reference. 
+    return -1
+	if (! ref($self));
+
+    my $idx = $self->idx();
+
+    DBQueryWarn("update apt_profiles set modified=now() ".
+		"where idx='$idx'")
+	or return -1;
+
+    return 0;
+}
+
 # _Always_ make sure that this 1 is at the end of the file...
 1;
diff --git a/apt/manage_profile.in b/apt/manage_profile.in
index 980ec680c643fef43829c254c8440e72681da71b..3b613092b911308e997b06ce28b6e2a6d5a96001 100644
--- a/apt/manage_profile.in
+++ b/apt/manage_profile.in
@@ -129,6 +129,7 @@ my %xmlfields =
      "profile_pid"	   => ["pid",		$SLOT_REQUIRED],
      "profile_creator"	   => ["creator",	$SLOT_OPTIONAL],
      "profile_description" => ["description",	$SLOT_REQUIRED|$SLOT_UPDATE],
+     "profile_listed"      => ["listed",	$SLOT_OPTIONAL|$SLOT_UPDATE],
      "profile_public"      => ["public",	$SLOT_OPTIONAL|$SLOT_UPDATE],
      "rspec"		   => ["rspec",		$SLOT_REQUIRED|$SLOT_UPDATE],
 );
@@ -241,6 +242,8 @@ if ($update) {
     }
     $profile->Update(\%update_args) == 0 or
 	fatal("Could not update profile record");
+    # Bump the modtime.
+    $profile->MarkModified();
 }
 else {
     my $usererror;
diff --git a/protogeni/scripts/aptssh-setup.in b/protogeni/scripts/aptssh-setup.in
index 0dea7cadcc2c7f30947256e8245df605faf79f37..e66865063efcffa04cfb17c02598e0edbc1576d8 100644
--- a/protogeni/scripts/aptssh-setup.in
+++ b/protogeni/scripts/aptssh-setup.in
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -wT
 #
-# Copyright (c) 2000-2013 University of Utah and the Flux Group.
+# Copyright (c) 2000-2014 University of Utah and the Flux Group.
 # 
 # {{{EMULAB-LICENSE
 # 
@@ -62,6 +62,7 @@ my $user;
 use lib "@prefix@/lib";
 use GeniDB;
 use GeniUser;
+use emutil;
 # Connect to the SA DB.
 GeniDB::DBConnect(GeniDB::GENISA_DBNAME());
 
@@ -136,18 +137,14 @@ system("$SSH -host $CONTROL '/usr/sbin/chown -R nobody:nobody $dir'")
     == 0 or fatal("Could not mkdir $dir: $!");
 system("$SSH -host $CONTROL '/bin/chmod -R 700 $dir'")
     == 0 or fatal("Could not mkdir $dir: $!");
-
-system("/usr/bin/fsync $udir");
-system("/usr/bin/fsync $udir/id_rsa.pub");
-sleep(5);
-$UID = $SAVEUID;
-
 # Grab a copy for the DB.
-my $pubkey = `cat $udir/id_rsa.pub`;
+my $pubkey =
+    emutil::ExecQuiet("$SSH -host $CONTROL '/bin/cat $dir/id_dsa.pub'");
 if ($?) {
     fatal("Could not read new key from file");
 }
 chomp($pubkey);
+$UID = $SAVEUID;
 
 # Only one.
 $geniuser->DeleteInternalKeys();
diff --git a/protogeni/scripts/quickvm.in b/protogeni/scripts/quickvm.in
index 1148b34f4da2734cc59f263639fdab6037e01e60..943ad0f7b3d9a5f5601d141ba6e60a05e3a6f21b 100755
--- a/protogeni/scripts/quickvm.in
+++ b/protogeni/scripts/quickvm.in
@@ -503,6 +503,7 @@ my $response =
 
 if (!defined($response) || $response->code() != GENIRESPONSE_SUCCESS) {
     $slice->Delete();
+    SetQuickVMStatus($quickvm_uuid, "failed");
     fatal("CreateSliver failed: ".
 	  (defined($response) ? $response->output() : "") . "\n");
 }
@@ -605,6 +606,7 @@ sub Terminate($)
 	fatal("No such quick VM: $uuid");
     }
     my $row = $query_result->fetchrow_hashref();
+    my $status = $row->{"status"};
 
     my $geniuser = GeniUser->Lookup($row->{'creator_uuid'}, 1);
     if (!defined($geniuser)) {
@@ -612,6 +614,9 @@ sub Terminate($)
     }
     my $slice = GeniSlice->Lookup($row->{'slice_uuid'});
     if (!defined($slice)) {
+	if ($status eq "failed") {
+	    goto done;
+	}
 	fatal("No slice for quick VM: $uuid");
     }
     # Create a slice credential
@@ -660,6 +665,7 @@ sub Terminate($)
 	      (defined($response) ? $response->output() : "") . "\n");
     }
     $slice->Delete();
+  done:
     DBQueryWarn("delete from quickvms where uuid=$safe_uuid");
     exit(0);
 }
diff --git a/sql/database-create.sql b/sql/database-create.sql
index a8f036653dc1e581b779b32fcaaa0342ad9a2b4b..1c9ce8081253bbde9f9d5e786d98c5501f3fe3ab 100644
--- a/sql/database-create.sql
+++ b/sql/database-create.sql
@@ -67,8 +67,12 @@ CREATE TABLE `apt_profiles` (
   `pid` varchar(48) NOT NULL default '',
   `pid_idx` mediumint(8) unsigned NOT NULL default '0',
   `created` datetime default NULL,
+  `modified` datetime default NULL,
   `uuid` varchar(40) NOT NULL,
   `public` tinyint(1) NOT NULL default '0',
+  `shared` tinyint(1) NOT NULL default '0',
+  `listed` tinyint(1) NOT NULL default '0',
+  `weburi` tinytext,
   `description` mediumtext,
   `rspec` mediumtext,
   PRIMARY KEY (`idx`),
diff --git a/sql/database-fill.sql b/sql/database-fill.sql
index 0b532cb1ca09878e20ac7abe0a3ca1a7800f8af7..9f420664040f67fe32c583317d3ea5039f5f1fdb 100644
--- a/sql/database-fill.sql
+++ b/sql/database-fill.sql
@@ -1273,6 +1273,8 @@ REPLACE INTO table_regex VALUES ('apt_profiles','pid','text','redirect','project
 REPLACE INTO table_regex VALUES ('apt_profiles','creator','text','redirect','users:uid',0,0,NULL);
 REPLACE INTO table_regex VALUES ('apt_profiles','name','text','redirect','images:imagename',0,64,NULL);
 REPLACE INTO table_regex VALUES ('apt_profiles','public','int','redirect','default:boolean',0,0,NULL);
+REPLACE INTO table_regex VALUES ('apt_profiles','listed','int','redirect','default:boolean',0,0,NULL);
+REPLACE INTO table_regex VALUES ('apt_profiles','shared','int','redirect','default:boolean',0,0,NULL);
 REPLACE INTO table_regex VALUES ('apt_profiles','description','text','redirect','default:html_fulltext',0,512,NULL);
 REPLACE INTO table_regex VALUES ('apt_profiles','rspec','text','redirect','default:html_fulltext',0,8192,NULL);
 
diff --git a/sql/updates/4/380 b/sql/updates/4/380
new file mode 100644
index 0000000000000000000000000000000000000000..297fbb35907c8c7fd428f7c894b63233a1e0d841
--- /dev/null
+++ b/sql/updates/4/380
@@ -0,0 +1,43 @@
+#
+# Modify APT profiles table.
+#
+use strict;
+use libdb;
+
+sub DoUpdate($$$)
+{
+    my ($dbhandle, $dbname, $version) = @_;
+
+    if (!DBSlotExists("apt_profiles", "modified")) {
+	DBQueryFatal("alter table apt_profiles add ".
+		     " `modified` datetime default NULL ".
+		     "  after created");
+    }
+    if (!DBSlotExists("apt_profiles", "shared")) {
+	DBQueryFatal("alter table apt_profiles add ".
+		     " `shared` tinyint(1) NOT NULL default '0' ".
+		     "  after public");
+    }
+    if (!DBSlotExists("apt_profiles", "listed")) {
+	DBQueryFatal("alter table apt_profiles add ".
+		     " `listed` tinyint(1) NOT NULL default '0' ".
+		     "  after shared");
+	DBQueryFatal("update apt_profiles set listed=public");
+    }
+    if (!DBSlotExists("apt_profiles", "weburi")) {
+	DBQueryFatal("alter table apt_profiles add ".
+		     " `weburi` tinytext ".
+		     " after listed");
+    }
+    DBQueryFatal("REPLACE INTO table_regex VALUES ".
+		 "('apt_profiles','listed','int','redirect',".
+		 "'default:boolean',0,0,NULL)");
+    DBQueryFatal("REPLACE INTO table_regex VALUES ".
+		 "('apt_profiles','shared','int','redirect',".
+		 "'default:boolean',0,0,NULL)");
+    return 0;
+}
+
+# Local Variables:
+# mode:perl
+# End:
diff --git a/tbsetup/gentopofile.in b/tbsetup/gentopofile.in
index cd118162c67cf72382e17650fb970a06d74d7216..84d2171d079f574de37dc4aebdb9a6967b238c9c 100644
--- a/tbsetup/gentopofile.in
+++ b/tbsetup/gentopofile.in
@@ -601,12 +601,16 @@ while (my ($sim) = $query_result->fetchrow_array) {
 # we need to convert port:ip above to lan:ip.
 # 
 $query_result =
-    DBQueryFatal("select v.vname,member,mask,cost,vlink from virt_lans as v ".
+    DBQueryFatal("select v.vname,member,mask,cost,vlink,s.capkey ".
+		 "  from virt_lans as v ".
+		 "left join virt_lan_settings as s on ".
+		 "     s.exptidx=v.exptidx and s.vname=v.vname and ".
+		 "     s.capkey='portvlan' ".
 		 "left join virt_bridges as b on b.vname=v.bridge_vname and ".
 		 "     b.pid=v.pid and b.eid=v.eid ".
 		 "where v.pid='$pid' and v.eid='$eid' ");
 
-while (my ($vname,$member,$mask,$cost,$bridgelink) =
+while (my ($vname,$member,$mask,$cost,$bridgelink,$lantype) =
        $query_result->fetchrow_array()) {
     my ($node,$port) = split(":", $member);
     
@@ -620,7 +624,12 @@ while (my ($vname,$member,$mask,$cost,$bridgelink) =
 	$lans{$vname} = {};
 	$lans{$vname}->{"mask"}    = $mask;
 	$lans{$vname}->{"cost"}    = $cost;
+	$lans{$vname}->{"noroute"} = 0;
 	$lans{$vname}->{"members"} = {};
+
+	if (defined($lantype)) {
+	    $lans{$vname}->{"noroute"} = 1;
+	}
     }
 
     # Store lan:ip into the portlist for the node.
@@ -664,8 +673,9 @@ if ($experiment->geniflags()) {
 		    last;
 		}
 		$lans{$vname} = {};
-		$lans{$vname}->{"mask"}  = $mask;
-		$lans{$vname}->{"cost"}  = 1;
+		$lans{$vname}->{"mask"}    = $mask;
+		$lans{$vname}->{"cost"}    = 1;
+		$lans{$vname}->{"noroute"} = 1;
 	    }
 	    if ($member->GetAttribute("tunnel_ip", \$ip)) {
 		print STDERR "Could not get ip for $member\n";
@@ -730,12 +740,14 @@ foreach my $node (sort keys(%nodes)) {
 #
 # Then spit out the lans. As above, ALWAYS print the header.
 #
-print $OUT "# lans: vname,mask,cost\n";
+print $OUT "# lans: vname,mask,cost,noroute\n";
 
 foreach my $lan (sort keys(%lans)) {
-    my $cost = $lans{$lan}->{"cost"};
-    my $mask = $lans{$lan}->{"mask"};
-    print $OUT "$lan,$mask,$cost\n";
+    my $cost    = $lans{$lan}->{"cost"};
+    my $mask    = $lans{$lan}->{"mask"};
+    my $noroute = $lans{$lan}->{"noroute"};
+    
+    print $OUT "$lan,$mask,$cost,$noroute\n";
 }
 
 #
diff --git a/tbsetup/libaudit.pm.in b/tbsetup/libaudit.pm.in
index c9df676b47bb7391b8fd8089ce89585d67df5184..b8e3caac859cd77acb3a1ec355f575228c27c05c 100644
--- a/tbsetup/libaudit.pm.in
+++ b/tbsetup/libaudit.pm.in
@@ -30,7 +30,7 @@ use Exporter;
 @EXPORT =
     qw ( AuditStart AuditEnd AuditAbort AuditFork AuditSetARGV AuditGetARGV
 	 AddAuditInfo
-	 LogStart LogEnd LogAbort
+	 LogStart LogEnd LogAbort AuditDisconnect
 	 LIBAUDIT_NODAEMON LIBAUDIT_DAEMON LIBAUDIT_LOGONLY
 	 LIBAUDIT_NODELETE LIBAUDIT_FANCY LIBAUDIT_LOGTBOPS LIBAUDIT_LOGTBLOGS
        );
@@ -285,6 +285,20 @@ sub AuditGetARGV()
     return @SAVEARGV;
 }
 
+sub AuditDisconnect()
+{
+    if ($auditing) {
+	if (!$daemon && $PERL_VERSION >= 5.008 && $libaudit::SAVE_STDOUT) {
+	    close($libaudit::SAVE_STDOUT);
+	    close($libaudit::SAVE_STDERR);
+
+	    open(FOO, "> /dev/null");
+	    $libaudit::SAVE_STDOUT = *FOO;
+	    $libaudit::SAVE_STDERR = *FOO;
+	}
+    }
+}
+
 #
 # Abort an Audit. Dump the log file and do not send email.
 #
@@ -293,7 +307,7 @@ sub AuditAbort()
     if ($auditing) {
 	$auditing = 0;
 
-	if (!$daemon && $PERL_VERSION >= 5.008) {
+	if (!$daemon && $PERL_VERSION >= 5.008 && $libaudit::SAVE_STDOUT) {
 	    eval("open(STDOUT, \">&\", \$libaudit::SAVE_STDOUT); ".
 		 "open(STDERR, \">&\", \$libaudit::SAVE_STDERR);");
 	}
diff --git a/utils/grantnodetype.in b/utils/grantnodetype.in
index efa53f2623e92db5b5b76abf03d7a48ca9f22c53..e61f9dbf4354c8c041206dfbfe845336741214c3 100644
--- a/utils/grantnodetype.in
+++ b/utils/grantnodetype.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 
 #
-# Copyright (c) 2003-2012 University of Utah and the Flux Group.
+# Copyright (c) 2003-2012, 2014 University of Utah and the Flux Group.
 # 
 # {{{EMULAB-LICENSE
 # 
@@ -35,16 +35,19 @@ use Getopt::Std;
 sub usage()
 {
     print STDERR "Usage: grantnodetype [-h] [-r] [-m] -p <pid> <type> [count]\n";
+    print STDERR "       grantnodetype [-h] -R <type>\n";
     print STDERR "	-h   This message\n";
     print STDERR "	-r   Revoke access instead of grant\n";
     print STDERR "	-m   Force insert of missing minus policy\n";
+    print STDERR "      -R   Remove all projects for type, and minus policy\n";
     print STDERR "   count   Overdide default count of 999999\n";
     exit(-1);
 }
-my $optlist  = "hp:dnrm";
+my $optlist  = "hp:dnrmR";
 my $impotent = 0;
 my $debug    = 0;
 my $revoke   = 0;
+my $revokeall= 0;
 my $addminus = 0;
 my %newtypes = ();
 my $pid;
@@ -106,13 +109,17 @@ if (defined($options{n})) {
 if (defined($options{r})) {
     $revoke = 1;
 }
+if (defined($options{R})) {
+    $revokeall = 1;
+}
 if (defined($options{d})) {
     $debug = 1;
 }
 if (defined($options{p})) {
     $pid = $options{p};
 }
-if (scalar(@ARGV) < 1 || scalar(@ARGV) > 2 || !defined($pid)) {
+if (!$revokeall &&
+    (scalar(@ARGV) < 1 || scalar(@ARGV) > 2 || !defined($pid))) {
     usage();
 }
 my $type = $ARGV[0];
@@ -128,6 +135,19 @@ else {
     die("Tainted type name: $type");
 }
 
+#
+# Clear the type from the group_policies table, including the minus policy,
+# which allows the type to be used by everyone.
+#
+if ($revokeall) {
+    DBQueryFatal("delete from group_policies ".
+		 "where policy='type' and auxdata='$type'")
+	if (!$impotent);
+
+    system($update_perms);
+    exit($? >> 8);
+}
+
 if ($pid =~ /^([-\w]+)$/) {
     $pid = $1;
 }
diff --git a/www/aptui/js/manage_profile.js b/www/aptui/js/manage_profile.js
index e1b2ca5169ddfd1a15afae03178733b825ecad9c..eae2826d59eba5d1728938eccc7a6ebf4c8e7572 100644
--- a/www/aptui/js/manage_profile.js
+++ b/www/aptui/js/manage_profile.js
@@ -43,9 +43,10 @@ function ($, sup)
 	console.info(topo);
 
 	sup.ShowModal("#quickvm_topomodal");
-    
-	sup.maketopmap("#showtopo_div",
-		   ($("#showtopo_dialog").outerWidth()) - 90,
+
+	// Subtract -2 cause of the border. 
+	sup.maketopmap("#showtopo_nopicker",
+ 		   ($("#showtopo_nopicker").outerWidth() - 2),
 		   300, topo);
     }
 
diff --git a/www/aptui/js/myprofiles.js b/www/aptui/js/myprofiles.js
index 3cb631c203147f24e0ad26aabfc390d05bc6ad94..c42565d7bbdd50abc54b0ffcaba0a77c0198989c 100644
--- a/www/aptui/js/myprofiles.js
+++ b/www/aptui/js/myprofiles.js
@@ -16,7 +16,6 @@ function ($, sup)
         $('#quickvm_topomodal').on('hidden.bs.modal', function() {
 	    sup.ShowProfileList($('.current'))
         });
-
 	$('button#profile').click(function (event) {
 	    event.preventDefault();
 	    sup.ShowModal('#quickvm_topomodal');
@@ -30,6 +29,12 @@ function ($, sup)
 	    sup.UpdateProfileSelection($('.selected'));
 	    sup.HideModal('#quickvm_topomodal');
 	});
+	// We have to get the selected profile from the hidden form variable.
+	$('a#instantiate').click(function (event) {
+	    event.preventDefault();
+	    var profile = $('#selected_profile').attr('value');	    
+	    window.location.replace("quickvm.php?profile=" + profile);
+	});
     }
 
     $(document).ready(initialize);
diff --git a/www/aptui/js/quickvm_sup.js b/www/aptui/js/quickvm_sup.js
index 04c0a7226ee08e17ee9450aa0c72b221ff7c84ca..8972e29dd7b6600f68f16dc84e69bd20504bce9e 100755
--- a/www/aptui/js/quickvm_sup.js
+++ b/www/aptui/js/quickvm_sup.js
@@ -130,8 +130,9 @@ function ShowTopo(uuid)
 	console.info(topo);
 
 	$("#showtopo_container").removeClass("invisible");
+	// Subtract -2 cause of the border. 
 	maketopmap("#showtopo_container",
-		   $("#showtopo_div").width() - 30,
+		   $("#showtopo_statuspage").outerWidth() - 2,
 		   300, topo);
 
     }
@@ -568,6 +569,10 @@ function StartCountdownClock(when)
 	// Clock reset
 	if (StartCountdownClock.reset != when) {
 	    when = StartCountdownClock.reset;
+	    if (when === "n/a") {
+		StartCountdownClock.stop = 1;
+		return;
+	    }
 
 	    // Reformat in local time and show the user.
 	    var local_date = new Date(when);
@@ -818,11 +823,22 @@ function ConvertManifestToJSON(name, xml)
 	"nodes": [],
 	"links": [],
     };
+    var interfaces = new Array();
+    var count = 0;
 
     $(xml).find("node").each(function(){
 	var client_id = $(this).attr("client_id");
 	var jobj      = {"name" : client_id};
 	
+	$(this).find("interface").each(function() {
+	    var interface_id = $(this).attr("client_id");
+	    var interface    = new Object();
+	    interface.client_id  = interface_id;
+	    interface.node_id    = client_id;
+	    interface.node_index = count;
+	    interfaces.push(interface);
+	});
+	
 	var login  = $(this).find("login");
 	if (login) {
 	    var user   = login.attr("username");
@@ -834,18 +850,22 @@ function ConvertManifestToJSON(name, xml)
 	    jobj.hostport  = host + ":" + port;
 	    jobj.sshurl    = sshurl;
 	}
-	json.nodes.push(jobj);
+	json.nodes[count] = jobj;
+	count++;
     });
 
     $(xml).find("link").each(function(){
 	var client_id = $(this).attr("client_id");
 	var link_type = $(this).find("link_type");
+	var ifacerefs = $(this).find("interface_ref");
 
-	if (link_type && $(link_type).attr("name") == "lan") {
-	    console.info("Oops, a lan");
+	if (ifacerefs.length < 2) {
+	    console.info("Oops, not enough interfaces in " + client_id);
+	}
+	else if (ifacerefs.length > 2) {
+	    console.info("Oops, too many interfaces in " + client_id);
 	}
 	else {
-	    var ifacerefs = $(this).find("interface_ref");
 	    var source    = ifacerefs[0];
 	    var target    = ifacerefs[1];
 
@@ -854,20 +874,26 @@ function ConvertManifestToJSON(name, xml)
 	    
 	    var source_ifname = source.attr("client_id");
 	    var target_ifname = target.attr("client_id");
-	    var source_ifpair = source_ifname.split(":");
-	    var target_ifpair = target_ifname.split(":");
-	    var source_name   = source_ifpair[0];
-	    var target_name   = target_ifpair[0];
+	    var source_name   = null;
+	    var target_name   = null;
 	    var source_index  = null;
 	    var target_index  = null;
-	
-	    // Javascript does not do dictionaries. Too bad.
-	    for (i = 0; i < json.nodes.length; i++) {
-		if (json.nodes[i].name == source_name) {
-		    source_index = i;
+
+	    /*
+	     * First we have map the client_ids to the node by
+	     * searching all of the interfaces we put into the
+	     * list above.
+	     *
+	     * Javascript does not do dictionaries. Too bad.
+	     */
+	    for (i = 0; i < interfaces.length; i++) {
+		if (interfaces[i].client_id == source_ifname) {
+		    source_name  = interfaces[i].node_id;
+		    source_index = interfaces[i].node_index;
 		}
-		if (json.nodes[i].name == target_name) {
-		    target_index = i;
+		if (interfaces[i].client_id == target_ifname) {
+		    target_name  = interfaces[i].node_id;
+		    target_index = interfaces[i].node_index;
 		}
 	    }
 	    json.links.push({"name"         : client_id,
@@ -916,11 +942,16 @@ function LoginByModal()
 	else {
 	    // Clear previous error.
 	    $("#quickvm_login_form_error").html("");
-	    HideModal("#quickvm_login_modal");
 	    $("#loginstatus").html("<a>" + uid + " logged in</a>");
 	    $("#loginstatus").removeClass("hidden");
 	    $("#quickvm_actions_menu").removeClass("hidden");
 	    $("#loginbutton").addClass("hidden");
+	    $("#quickvm_login_form_error").html(
+		"<center>" + "Login successful</center><br>");
+	    setTimeout(function() {
+		HideModal("#quickvm_login_modal");
+		$("#quickvm_login_form_error").html("");
+	    }, 2000);
 	}
     }
     var xmlthing = $.ajax({
diff --git a/www/aptui/login.php b/www/aptui/login.php
index a6b52bebd87fefa5e281a0e37fb606fd013a38ae..153f5c9e25ba237f63c3f0f9e948115f3a520bba 100644
--- a/www/aptui/login.php
+++ b/www/aptui/login.php
@@ -27,6 +27,11 @@ chdir("apt");
 include("quickvm_sup.php");
 $page_title = "Login";
 
+#
+# Get current user in case we need an error message.
+#
+$this_user = CheckLogin($check_status);
+
 #
 # Verify page arguments.
 #
@@ -71,6 +76,11 @@ function SPITFORM($uid, $referrer, $error)
 {
     global $TBDB_UIDLEN, $TBBASE, $refer;
     
+    header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
+    header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+    header("Cache-Control: no-cache, max-age=0, must-revalidate, no-store");
+    header("Pragma: no-cache");
+
     SPITHEADER();
  
     echo "<div class='row'>
@@ -98,6 +108,9 @@ function SPITFORM($uid, $referrer, $error)
         case "timedout":
 	    echo "Your login has timed out!";
 	    break;
+        case "alreadyloggedin":
+	    echo "You are already logged in. Logout first?";
+	    break;
 	default:
 	    echo "Unknown Error ($error)!";
         }
@@ -140,7 +153,8 @@ function SPITFORM($uid, $referrer, $error)
 # If not clicked, then put up a form.
 #
 if (!$ajax_request && !isset($login)) {
-    SPITFORM(REMEMBERED_ID(), $referrer, null);
+    SPITFORM(REMEMBERED_ID(), $referrer,
+	     ($this_user ? "alreadyloggedin" : null));
     return;
 }
 
@@ -182,6 +196,11 @@ else {
     }
 }
 
+header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
+header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+header("Cache-Control: no-cache, max-age=0, must-revalidate, no-store");
+header("Pragma: no-cache");
+
 #
 # Failed, then try again with an error message.
 # 
diff --git a/www/aptui/logout.php b/www/aptui/logout.php
index e2830dabb37f6ac1c4574de58d1f064ed84939f3..e0d5f5609676c56d2ff5698d10cf53696820507d 100644
--- a/www/aptui/logout.php
+++ b/www/aptui/logout.php
@@ -53,5 +53,5 @@ if ($ajax_request) {
     SPITAJAX_RESPONSE("");
     exit();
 }
-header("Location: quickvm.php");
+header("Location: login.php");
 ?>
diff --git a/www/aptui/manage_profile.php b/www/aptui/manage_profile.php
index f85e46c55d7bb5282c3f8708e11d170a235ab8a5..5a99bd501c3a1d39d95f207a55f3e8c4e92da216 100644
--- a/www/aptui/manage_profile.php
+++ b/www/aptui/manage_profile.php
@@ -25,7 +25,9 @@ chdir("..");
 include("defs.php3");
 chdir("apt");
 include("quickvm_sup.php");
+include("profile_defs.php");
 $page_title = "Manage Profile";
+$notifyupdate = 0;
 
 #
 # Get current user.
@@ -38,6 +40,7 @@ $this_user = CheckLogin($check_status);
 $optargs = OptionalPageArguments("create",      PAGEARG_STRING,
 				 "action",      PAGEARG_STRING,
 				 "idx",         PAGEARG_INTEGER,
+				 "finished",    PAGEARG_BOOLEAN,
 				 "formfields",  PAGEARG_ARRAY);
 
 #
@@ -45,7 +48,7 @@ $optargs = OptionalPageArguments("create",      PAGEARG_STRING,
 #
 function SPITFORM($formfields, $errors)
 {
-    global $this_user, $projlist, $action, $idx;
+    global $this_user, $projlist, $action, $idx, $notifyupdate;
     $editing = 0;
 
     if ($action == "edit") {
@@ -118,7 +121,11 @@ function SPITFORM($formfields, $errors)
     # Look for non-specific error.
     #
     if ($errors && array_key_exists("error", $errors)) {
-	echo "<font color=red>" . $errors["error"] . "</font>";
+	echo "<font color=red><center>" . $errors["error"] . "</center></font>";
+    }
+    # Did we just complete an update.
+    if ($notifyupdate) {
+	echo "<font color=green><center>Update Successful!</center></font>";
     }
     # Mark as editing mode on post.
     if ($editing) {     
@@ -215,11 +222,11 @@ function SPITFORM($formfields, $errors)
     }
     $formatter("profile_rspec", "Your rspec", $rspec_html);
 
-    $formatter("profile_public", "Public?",
+    $formatter("profile_listed", "Listed?",
 	       "<div class='checkbox'>
-                <label><input name=\"formfields[profile_public]\" ".
-	               $formfields["profile_public"] .
-	       "       id='profile_public' value=checked
+                <label><input name=\"formfields[profile_listed]\" ".
+	               $formfields["profile_listed"] .
+	       "       id='profile_listed' value=checked
                        type=checkbox> ".
 	       "List on the public page for anyone to use?</label></div>");
 
@@ -227,15 +234,15 @@ function SPITFORM($formfields, $errors)
 
     echo "<div class='form-group'>
             <div class='col-sm-offset-2 col-sm-10'>
-               <button class='btn btn-primary btm-xs pull-right'
+               <button class='btn btn-primary btn-sm pull-right'
                    id='profile_submit_button'
                    type='submit' name='create'>$button_label</button>\n";
-    echo "     <a class='btn btn-primary btm-xs pull-right'
+    if ($editing) {
+	echo " <a class='btn btn-primary btn-sm pull-right'
                    style='margin-right: 10px;'
                    href='quickvm.php?profile=$idx'
                    type='submit' name='create'>Instantiate</a>\n";
-    if ($editing) {
-	echo " <a class='btn btn-danger btm-xs pull-left'
+	echo " <a class='btn btn-danger btn-sm pull-left'
                    style='margin-right: 10px;'
                    href='manage_profile.php?action=delete&idx=$idx'
                    type='button' name='delete'>Delete</a>\n";
@@ -292,7 +299,7 @@ function SPITFORM($formfields, $errors)
                  <div class='panel panel-default'
                             id='showtopo_container'>
                     <div class='panel-body'>
-                     <div id='showtopo_div'></div>
+                     <div id='showtopo_nopicker'></div>
                     </div>
                  </div>
                </div>
@@ -336,11 +343,9 @@ if (! isset($create)) {
 	    $errors["error"] = "No profile specified for edit/delete!";
 	}
 	else {
-	    $query_result =
-		DBQueryFatal("select * from apt_profiles ".
-			     "where idx='$idx' and creator_idx='$this_idx'");
-
-	    if (!$query_result || !mysql_num_rows($query_result)) {
+	    $profile = Profile::Lookup($idx);
+	    
+	    if (!$profile || $this_idx != $profile->creator_idx()) {
 		$errors["error"] = "No such profile!";
 	    }
 	    else if ($action == "delete") {
@@ -349,13 +354,30 @@ if (! isset($create)) {
 		return;
 	    }
 	    else {
-		$row = mysql_fetch_array($query_result);
-		$defaults["profile_pid"]         = $row["pid"];
-		$defaults["profile_description"] = $row["description"];
-		$defaults["profile_name"]        = $row["name"];
-		$defaults["profile_rspec"]       = $row["rspec"];
-		$defaults["profile_public"]      =
-		    ($row["public"] ? "checked" : "");
+		$defaults["profile_pid"]         = $profile->pid();
+		$defaults["profile_description"] = $profile->description();
+		$defaults["profile_name"]        = $profile->name();
+		$defaults["profile_rspec"]       = $profile->rspec();
+		$defaults["profile_listed"]      =
+		    ($profile->listed() ? "checked" : "");
+
+		#
+		# If we are displaying after a successful edit, and it
+		# just happened (by looking at the modify time), show
+		# a message that the update was successful. This is pretty
+		# crappy, but I do not want to go for a fancy thing (popover)
+		# just yet, maybe later.
+		#
+		if (isset($finished) && $profile->modified()) {
+		    $mod = new DateTime($profile->modified());
+		    if ($mod) {
+			$now  = new DateTime("now");
+			$diff = $now->getTimestamp() - $mod->getTimestamp();
+			if ($diff < 2) {
+			    $notifyupdate = 1;
+			}
+		    }
+		}
 	    }
 	}
     }
@@ -470,12 +492,15 @@ else {
     fwrite($fp, "<attribute name='rspec'>");
     fwrite($fp, "  <value>" . htmlspecialchars($rspec) . "</value>");
     fwrite($fp, "</attribute>\n");
-    if (isset($formfields["profile_public"]) &&
-	$formfields["profile_public"] == "checked") {
-	fwrite($fp, "<attribute name='profile_public'>");
-	fwrite($fp, "  <value>1</value>");
-	fwrite($fp, "</attribute>\n");
+    fwrite($fp, "<attribute name='profile_listed'><value>");
+    if (isset($formfields["profile_listed"]) &&
+	$formfields["profile_listed"] == "checked") {
+	fwrite($fp, "1");
+    }
+    else {
+	fwrite($fp, "0");
     }
+    fwrite($fp, "</value></attribute>\n");
     fwrite($fp, "</profile>\n");
     fclose($fp);
     chmod($xmlname, 0666);
@@ -533,7 +558,8 @@ if (!$query_result || !mysql_num_rows($query_result)) {
 else {
     $row = mysql_fetch_array($query_result);
     $idx = $row["idx"];
-    header("Location: $APTBASE/manage_profile.php?action=edit&idx=$idx");
+    header("Location: $APTBASE/manage_profile.php?action=edit&idx=$idx".
+	   "&finished=1");
 }
 
 ?>
diff --git a/www/aptui/myprofiles.php b/www/aptui/myprofiles.php
index 639254226a98b47c0385c9be0d94fd3b8e7698ae..6f37f1c1111aa5a0bf12a4621e0fb59287d7277f 100644
--- a/www/aptui/myprofiles.php
+++ b/www/aptui/myprofiles.php
@@ -99,6 +99,8 @@ $query_result =
 if (mysql_num_rows($query_result) == 0) {
     echo "<b>No profiles to show you. Maybe you want to ".
 	"<a href='manage_profile.php'>create one?</a></b>\n";
+    echo "<script src='js/lib/require.js' data-main='js/null'>
+          </script>\n";
     SPITFOOTER();
     exit();
 }
@@ -135,8 +137,12 @@ echo "    <div class='panel panel-default'>
                        type='button' name='profile_button'>
                   Select a Profile</button>\n";
 echo "        </div>
-              <button class='btn btn-primary btn-xs pull-right'
-                       type='submit' name='submit'>Go</button>
+              <button class='btn btn-primary btn-sm pull-right'
+                       type='submit' name='submit'>Modify</button>
+	      <a id='instantiate'
+                   class='btn btn-primary btn-sm pull-right'
+                   style='margin-right: 10px;'
+                   type='button'>Instantiate</a>
             </form>
            </div>
           </div>
diff --git a/www/aptui/profile_defs.php b/www/aptui/profile_defs.php
new file mode 100644
index 0000000000000000000000000000000000000000..8e587987abc3793ddafd0400c14ccb912226ba42
--- /dev/null
+++ b/www/aptui/profile_defs.php
@@ -0,0 +1,111 @@
+<?php
+#
+# Copyright (c) 2006-2014 University of Utah and the Flux Group.
+# 
+# {{{EMULAB-LICENSE
+# 
+# This file is part of the Emulab network testbed software.
+# 
+# This file is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or (at
+# your option) any later version.
+# 
+# This file is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
+# License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with this file.  If not, see <http://www.gnu.org/licenses/>.
+# 
+# }}}
+#
+#
+class Profile
+{
+    var	$profile;
+    var $project;
+
+    #
+    # Constructor by lookup on unique index.
+    #
+    function Profile($idx) {
+	$safe_idx = addslashes($idx);
+
+	$query_result =
+	    DBQueryWarn("select * from apt_profiles ".
+			"where idx='$safe_idx'");
+
+	if (!$query_result || !mysql_num_rows($query_result)) {
+	    $this->profile = null;
+	    return;
+	}
+	$this->profile = mysql_fetch_array($query_result);
+
+	# Load lazily;
+	$this->project    = null;
+    }
+    # accessors
+    function field($name) {
+	return (is_null($this->profile) ? -1 : $this->profile[$name]);
+    }
+    function name()	    { return $this->field('name'); }
+    function idx()	    { return $this->field('idx'); }
+    function creator()	    { return $this->field('creator'); }
+    function creator_idx()  { return $this->field('creator_idx'); }
+    function pid()	    { return $this->field('pid'); }
+    function pid_idx()	    { return $this->field('pid_idx'); }
+    function created()	    { return $this->field('created'); }
+    function modified()	    { return $this->field('modified'); }
+    function uuid()	    { return $this->field('uuid'); }
+    function ispublic()	    { return $this->field('public'); }
+    function shared()	    { return $this->field('shared'); }
+    function listed()	    { return $this->field('listed'); }
+    function weburi()	    { return $this->field('weburi'); }
+    function description()  { return $this->field('description'); }
+    function rspec()	    { return $this->field('rspec'); }
+    
+    # Hmm, how does one cause an error in a php constructor?
+    function IsValid() {
+	return !is_null($this->profile);
+    }
+
+    # Lookup up a single profile by idx. 
+    function Lookup($idx) {
+	$foo = new Profile($idx);
+
+	if ($foo->IsValid()) {
+            # Insert into cache.
+	    return $foo;
+	}	
+	return null;
+    }
+
+    # Lookup by name/version. If no version, then return highest
+    # numbered version.
+    function LookupByName() {}
+
+    #
+    # Refresh an instance by reloading from the DB.
+    #
+    function Refresh() {
+	if (! $this->IsValid())
+	    return -1;
+
+	$idx = $this->idx();
+
+	$query_result =
+	    DBQueryWarn("select * from apt_profiles where idx='$idx'");
+    
+	if (!$query_result || !mysql_num_rows($query_result)) {
+	    $this->profile    = NULL;
+	    $this->project    = null;
+	    return -1;
+	}
+	$this->profile    = mysql_fetch_array($query_result);
+	$this->project    = null;
+	return 0;
+    }
+}
+?>
diff --git a/www/aptui/quickvm.css b/www/aptui/quickvm.css
index 42f54696020909d13cf3afdc6c6163d5a5b8ecda..7e00c073f5106ed1c9468f3bcfa0cbe01fba21bf 100644
--- a/www/aptui/quickvm.css
+++ b/www/aptui/quickvm.css
@@ -119,14 +119,22 @@ body {
   padding: 5px;
 }
 
+#showtopo_nopicker {
+  border: 1px solid #000;
+}
+
 @media (min-width: 970px) {
   .modal-dialog {
   width: 950px;
 }
 
+#showtopo_description,
 #showtopo_div {
   width: 643px;
 }
+#showtopo_nopicker {
+  width: 850px;
+}
 }
 
 @media (max-width: 969px) {
@@ -136,38 +144,62 @@ body {
 }
 
 @media (min-width: 910px) and (max-width: 969px) {
+  #showtopo_description,
   #showtopo_div {
   width: 610px;
+}
+  #showtopo_nopicker {
+  width: 800px;
 }
 }
 
 @media (min-width: 850px) and (max-width: 909px) {
+  #showtopo_description,
   #showtopo_div {
   width: 560px;
+}
+  #showtopo_nopicker {
+  width: 750px;
 }
 }
 
 @media (min-width: 790px) and (max-width: 849px) {
+  #showtopo_description,
   #showtopo_div {
   width: 510px;
+}
+  #showtopo_nopicker {
+  width: 710px;
 }
 }
 
 @media (min-width: 768px) and (max-width: 789px) {
+  #showtopo_description,
   #showtopo_div {
   width: 500px;
+}
+  #showtopo_nopicker {
+  width: 700px;
 }
 }
 
 @media (min-width: 740px) and (max-width: 767px) {
+  #showtopo_description,
   #showtopo_div {
   width: 470px;
+}
+  #showtopo_nopicker {
+  width: 670px;
 }
 }
 
 @media (min-width: 690px) and (max-width: 739px) {
+  #showtopo_description,
   #showtopo_div {
   width: 430px;
+}
+  #showtopo_nopicker {
+  width: 630px;
 }
 }
 
diff --git a/www/aptui/quickvm.php b/www/aptui/quickvm.php
index 073ac8056f33a1556ea678583ce5dc6d71bf6dfc..3257c01cd2f314ddb880e78680adf9f0b3062b3a 100755
--- a/www/aptui/quickvm.php
+++ b/www/aptui/quickvm.php
@@ -46,7 +46,6 @@ $optargs = OptionalPageArguments("create",      PAGEARG_STRING,
 				 "stuffing",    PAGEARG_STRING,
 				 "verify",      PAGEARG_STRING,
 				 "sshkey",	PAGEARG_STRING,
-				 "profile",     PAGEARG_INTEGER,
 				 "ajax_request",  PAGEARG_BOOLEAN,
 				 "ajax_method",   PAGEARG_STRING,
 				 "ajax_argument", PAGEARG_STRING);
@@ -92,10 +91,6 @@ while ($row = mysql_fetch_array($query_result)) {
 	$profile_default = $row["idx"];
     }
 }
-# URL specified profile to use.
-if (isset($profile) && array_key_exists($profile, $profile_array)) {
-    $profile_default = $profile;
-}
 
 function SPITFORM($username, $email, $sshkey, $profile, $newuser, $errors)
 {
@@ -275,7 +270,7 @@ if (!isset($create)) {
 	$username = $this_user->uid();
 	$email    = $this_user->email();
     }
-    SPITFORM($username, $email, $sshkey, null, false, null);
+    SPITFORM($username, $email, $sshkey, $profile, false, null);
     SPITFOOTER();
     return;
 }
diff --git a/www/aptui/quickvm_status.php b/www/aptui/quickvm_status.php
index 5d18226c99e501a3cd11224f9587575ffa85fc74..5efd98c4971c0f075ff91c24f79b9902950475af 100644
--- a/www/aptui/quickvm_status.php
+++ b/www/aptui/quickvm_status.php
@@ -98,21 +98,6 @@ if (!$creator) {
     return;
 }
 $slice = GeniSlice::Lookup("sa", $quickvm->slice_uuid());
-if (!$slice) {
-    if ($ajax_request) {
-	SPITAJAX_ERROR(1, "no slice for quickvm ");
-	exit();
-    }
-    SPITHEADER(1);
-    echo "<div class='align-center'>
-            <p class='lead text-center'>
-               Hmm, there seems to be a problem.
-            </p>
-          </div>\n";
-    SPITFOOTER();
-    TBERROR("No slice for quickvm $uuid", 0);
-    return;
-}
 
 #
 # Deal with ajax requests.
@@ -133,6 +118,10 @@ if (isset($ajax_request)) {
 	SPITAJAX_RESPONSE(SSHAuthObject($creator->uid(), $ajax_argument));
     }
     elseif ($ajax_method == "request_extension") {
+	if (!isset($slice)) {
+	    SPITAJAX_ERROR(1, "Nothing to extend!");
+	    return;
+	}
         # Only extend for 24 hours. More later.
 	$expires_time = strtotime($slice->expires());
 	if ($expires_time > time() + (3600 * 36)) {
@@ -171,8 +160,12 @@ if (isset($ajax_request)) {
 SPITHEADER(1);
 
 $style = "style='border: none;'";
-$slice_urn       = $slice->urn();
-$slice_expires   = gmdate("Y-m-d H:i:s", strtotime($slice->expires()));
+$slice_urn       = "n/a";
+$slice_expires   = "n/a";
+if (isset($slice)) {
+    $slice_urn       = $slice->urn();
+    $slice_expires   = gmdate("Y-m-d H:i:s", strtotime($slice->expires()));
+}
 $quickvm_status  = $quickvm->status();
 $creator_uid     = $creator->uid();
 $creator_email   = $creator->email();
@@ -271,7 +264,7 @@ echo "<div class='row'>
 echo "<div class='panel panel-default invisible' id='showtopo_container'>\n";
 echo "<div class='panel-body'>\n";
 echo "<div id='quicktabs_div'>\n";
-echo "<div id='showtopo_div'></div>\n";
+echo "<div id='showtopo_statuspage'></div>\n";
 SpitToolTip("Click on a node to SSH to that node.\n".
 	    "Click and drag on a node to move things around.");
 echo "</div>\n"; # showtopo