veyron/services/store/stored: Add viewer to stored itself (enabled by default), and update mdb example accordingly, with a proper Makefile and full usage instructions in a README.md file.

Change-Id: I1d99b3e5990479bf82a6e16ef14f87dba36229c5
diff --git a/examples/mdb/Makefile b/examples/mdb/Makefile
new file mode 100644
index 0000000..aaa45dc
--- /dev/null
+++ b/examples/mdb/Makefile
@@ -0,0 +1,7 @@
+build:
+	${VEYRON_ROOT}/veyron/scripts/build/go install veyron/... veyron2/...
+
+run: build
+	./run.sh
+
+.PHONY: build run
diff --git a/examples/mdb/README.md b/examples/mdb/README.md
new file mode 100644
index 0000000..49cfc8d
--- /dev/null
+++ b/examples/mdb/README.md
@@ -0,0 +1,12 @@
+# mdb example
+
+A simple "movie database" example of store usage.
+
+## How to run
+
+Simply run `make run`. Under the hood, this generates a self-signed identity,
+starts a mounttable daemon, starts a store daemon, and initializes the store
+with mdb data and templates.
+
+Once everything's up and running, visit the store daemon's viewer in your
+browser (http://localhost:5000 by default) to explore the mdb data.
diff --git a/examples/storage/mdb/mdb_init/main.go b/examples/mdb/mdb_init/main.go
similarity index 95%
rename from examples/storage/mdb/mdb_init/main.go
rename to examples/mdb/mdb_init/main.go
index 28f8166..022a362 100644
--- a/examples/storage/mdb/mdb_init/main.go
+++ b/examples/mdb/mdb_init/main.go
@@ -22,7 +22,7 @@
 	"strings"
 	"time"
 
-	"veyron/examples/storage/mdb/schema"
+	"veyron/examples/mdb/schema"
 	"veyron2/naming"
 	"veyron2/rt"
 	"veyron2/storage"
@@ -353,7 +353,7 @@
 	return nil
 }
 
-// processFile stores the contens of the file to the store.
+// processFile stores the contents of the file in the store.
 func (st *state) processFile(path, name string) error {
 	switch filepath.Ext(path) {
 	case ".json":
@@ -375,11 +375,6 @@
 // main reads all the files in the templates directory and adds them to the
 // store.
 func main() {
-	// The client's identity needs to match the Admin ACLs at the empty
-	// store (since only the admin can put data).  The identity here
-	// matches with that used for server.ServerConfig.Admin in
-	// mdb_stored/main.go.  An alternative would be to relax the ACLs on
-	// the store.
 	rt.Init()
 
 	vlog.Infof("Binding to store on %s", storeName)
@@ -389,11 +384,13 @@
 	}
 	state := newState(st)
 
-	// Store all templates.
+	// Store all data and templates.
 	filepath.Walk(*templatesDir, func(path string, _ os.FileInfo, _ error) error {
 		err := state.processFile(path, strings.TrimPrefix(path, *templatesDir))
 		if err != nil {
-			vlog.Infof("%s: %s", path, err)
+			vlog.Fatalf("Error processing %s: %s", path, err)
+		} else {
+			vlog.Infof("Processed %s", path)
 		}
 		return err
 	})
diff --git a/examples/mdb/run.sh b/examples/mdb/run.sh
new file mode 100755
index 0000000..aa0d811
--- /dev/null
+++ b/examples/mdb/run.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+set -e
+set -u
+
+VEYRON_BIN=${VEYRON_ROOT}/veyron/go/bin
+ID_FILE=/var/tmp/id
+
+# Generate a self-signed identity.
+${VEYRON_BIN}/identity generate > ${ID_FILE}
+
+# Start the mounttable daemon.
+${VEYRON_BIN}/mounttabled --address=':8100' &
+
+export VEYRON_IDENTITY=${ID_FILE}
+export NAMESPACE_ROOT='/127.0.0.1:8100'
+
+sleep 1  # Wait for mounttabled to start up.
+
+# Start the store daemon.
+rm -rf /var/tmp/veyron_store.db
+${VEYRON_BIN}/stored &
+
+sleep 1  # Wait for stored to start up.
+
+# Initialize the store with mdb data and templates.
+${VEYRON_BIN}/mdb_init --load-all
+
+echo
+echo 'Visit http://localhost:5000 to browse the mdb data.'
+echo 'Hit Ctrl-C to kill all running services.'
+
+trap 'kill $(jobs -pr)' INT TERM EXIT
+wait
diff --git a/examples/storage/mdb/schema/init.go b/examples/mdb/schema/init.go
similarity index 100%
rename from examples/storage/mdb/schema/init.go
rename to examples/mdb/schema/init.go
diff --git a/examples/storage/mdb/schema/schema.vdl b/examples/mdb/schema/schema.vdl
similarity index 100%
rename from examples/storage/mdb/schema/schema.vdl
rename to examples/mdb/schema/schema.vdl
diff --git a/examples/storage/mdb/schema/schema.vdl.go b/examples/mdb/schema/schema.vdl.go
similarity index 100%
rename from examples/storage/mdb/schema/schema.vdl.go
rename to examples/mdb/schema/schema.vdl.go
diff --git a/examples/storage/mdb/templates/contents.json b/examples/mdb/templates/contents.json
similarity index 100%
rename from examples/storage/mdb/templates/contents.json
rename to examples/mdb/templates/contents.json
diff --git a/examples/storage/mdb/templates/css/movie.css b/examples/mdb/templates/css/movie.css
similarity index 100%
rename from examples/storage/mdb/templates/css/movie.css
rename to examples/mdb/templates/css/movie.css
diff --git a/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Dir.tmpl b/examples/mdb/templates/veyron/examples/mdb/schema/Dir.tmpl
similarity index 100%
rename from examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Dir.tmpl
rename to examples/mdb/templates/veyron/examples/mdb/schema/Dir.tmpl
diff --git a/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Movie.tmpl b/examples/mdb/templates/veyron/examples/mdb/schema/Movie.tmpl
similarity index 100%
rename from examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Movie.tmpl
rename to examples/mdb/templates/veyron/examples/mdb/schema/Movie.tmpl
diff --git a/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Person.tmpl b/examples/mdb/templates/veyron/examples/mdb/schema/Person.tmpl
similarity index 100%
rename from examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Person.tmpl
rename to examples/mdb/templates/veyron/examples/mdb/schema/Person.tmpl
diff --git a/examples/storage/mdb/mdbd/main.go b/examples/storage/mdb/mdbd/main.go
deleted file mode 100644
index 1cd2735..0000000
--- a/examples/storage/mdb/mdbd/main.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// mdbd provides a UI for the mdb/schema movie database.
-//
-// The main purpose of this server is to register the types for the mdb/schema
-// so that the html/templates work correctly.
-package main
-
-import (
-	"flag"
-	"fmt"
-	"log"
-	"os"
-	"os/user"
-
-	// Register the mdb/schema types.
-	_ "veyron/examples/storage/mdb/schema"
-	"veyron/examples/storage/viewer"
-	"veyron2/rt"
-	"veyron2/storage/vstore"
-)
-
-var (
-	storeName string
-	port      = flag.Int("port", 10000, "IPV4 port number to serve")
-)
-
-func init() {
-	username := "unknown"
-	if u, err := user.Current(); err == nil {
-		username = u.Username
-	}
-	hostname := "unknown"
-	if h, err := os.Hostname(); err == nil {
-		hostname = h
-	}
-	dir := "global/vstore/" + hostname + "/" + username
-	flag.StringVar(&storeName, "store", dir, "Name of the Veyron store")
-}
-
-func main() {
-	rt.Init()
-
-	log.Printf("Binding to store on %s", storeName)
-	st, err := vstore.New(storeName)
-	if err != nil {
-		log.Fatalf("Can't connect to store: %s: %s", storeName, err)
-	}
-
-	viewer.ListenAndServe(fmt.Sprintf(":%d", *port), st)
-}
diff --git a/examples/storage/viewer/reflect.go b/examples/storage/viewer/reflect.go
deleted file mode 100644
index c7b4359..0000000
--- a/examples/storage/viewer/reflect.go
+++ /dev/null
@@ -1,150 +0,0 @@
-package viewer
-
-import (
-	"bytes"
-	"fmt"
-	"reflect"
-
-	"veyron2/storage"
-)
-
-// printer defines a pretty printer for Go values, using reflection to traverse
-// the values.
-type printer struct {
-	buf bytes.Buffer
-}
-
-var (
-	tyID = reflect.TypeOf(storage.ID{})
-)
-
-// stringTypePointers removes the outer Ptr and Interface types.
-func stripTypePointers(ty reflect.Type) reflect.Type {
-	kind := ty.Kind()
-	for kind == reflect.Ptr || kind == reflect.Interface {
-		ty = ty.Elem()
-		kind = ty.Kind()
-	}
-	return ty
-}
-
-// templatePath returns the path to the templates value, defined as
-// /templates/<pkgPath>/<typeName>.
-func templatePath(v interface{}) string {
-	ty := stripTypePointers(reflect.TypeOf(v))
-	pkgPath := ty.PkgPath()
-	tyName := ty.Name()
-	if pkgPath == "" || tyName == "" {
-		return ""
-	}
-	return fmt.Sprintf("templates/%s/%s", pkgPath, tyName)
-}
-
-// print formats the argument.
-func (p *printer) print(v interface{}) {
-	p.printValue(0, reflect.ValueOf(v))
-}
-
-// printType formats the type.
-func (p *printer) printType(indent int, v reflect.Type) {
-	p.printString(v.Name())
-}
-
-// printValue is the main pretty-printer method.  It formats the value at the
-// indentation level specified by the argument.
-func (p *printer) printValue(indent int, v reflect.Value) {
-	if !v.IsValid() {
-		p.printString("<invalid>")
-		return
-	}
-	ty := v.Type()
-	if ty == tyID {
-		p.printString(v.Interface().(storage.ID).String())
-		return
-	}
-	switch ty.Kind() {
-	case reflect.Ptr:
-		p.printString("&")
-		fallthrough
-	case reflect.Interface:
-		p.printValue(indent, v.Elem())
-	case reflect.Array, reflect.Slice:
-		p.printSliceValue(indent, v)
-	case reflect.Map:
-		p.printMapValue(indent, v)
-	case reflect.Struct:
-		p.printStructValue(indent, v)
-	case reflect.String:
-		fmt.Fprintf(&p.buf, "%q", v.Interface())
-	default:
-		fmt.Fprintf(&p.buf, "%+v", v.Interface())
-	}
-}
-
-// printSliceValue formats a slice or array.
-func (p *printer) printSliceValue(indent int, v reflect.Value) {
-	p.printType(indent, v.Type())
-	p.printString("{")
-	l := v.Len()
-	for i := 0; i < l; i++ {
-		p.printIndent(indent + 1)
-		p.printValue(indent+1, v.Index(i))
-		p.printString(",")
-	}
-	p.printIndent(indent)
-	p.printString("}")
-}
-
-// printMapValue formats a map.
-func (p *printer) printMapValue(indent int, v reflect.Value) {
-	p.printType(indent, v.Type())
-	p.printString("{")
-	for _, key := range v.MapKeys() {
-		p.printIndent(indent + 1)
-		p.printValue(indent+1, key)
-		p.printString(": ")
-		p.printValue(indent+2, v.MapIndex(key))
-		p.printString(",")
-	}
-	p.printIndent(indent)
-	p.printString("}")
-}
-
-// printStructValue formats a struct.
-func (p *printer) printStructValue(indent int, v reflect.Value) {
-	ty := v.Type()
-	p.printType(indent, ty)
-	p.printString("{")
-	l := ty.NumField()
-	for i := 0; i < l; i++ {
-		field := ty.Field(i)
-		if field.PkgPath != "" {
-			continue
-		}
-		p.printIndent(indent + 1)
-		p.printString(field.Name)
-		p.printString(": ")
-		p.printValue(indent+1, v.Field(i))
-		p.printString(",")
-	}
-	p.printIndent(indent)
-	p.printString("}")
-}
-
-// printString adds a string to the output.
-func (p *printer) printString(s string) {
-	p.buf.WriteString(s)
-}
-
-// printIndent prints a newline and then indents to the specified indentation.
-func (p *printer) printIndent(indent int) {
-	p.buf.WriteRune('\n')
-	for i := 0; i < indent; i++ {
-		p.buf.WriteString("  ")
-	}
-}
-
-// String returns the formatted text.
-func (p *printer) String() string {
-	return p.buf.String()
-}
diff --git a/examples/storage/viewer/value.go b/examples/storage/viewer/value.go
deleted file mode 100644
index b82f09e..0000000
--- a/examples/storage/viewer/value.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package viewer
-
-import (
-	"path/filepath"
-	"sort"
-	"time"
-
-	"veyron2/rt"
-	"veyron2/storage"
-)
-
-// Value is the type used to pass store values to the template.  The Value
-// contains the name of the value, the actual value, and a list of
-// subdirectories.
-type Value struct {
-	store   storage.Store
-	Name    string
-	Value   interface{}
-	Subdirs []string
-}
-
-// glob performs a glob expansion of the pattern.  The results are sorted.
-func glob(st storage.Store, path, pattern string) ([]string, error) {
-	results := st.BindObject(path).Glob(rt.R().TODOContext(), pattern)
-	names := []string{}
-	rStream := results.RecvStream()
-	for rStream.Advance() {
-		names = append(names, "/"+rStream.Value())
-	}
-	if err := rStream.Err(); err != nil {
-		return nil, err
-	}
-	sort.Strings(names)
-	return names, nil
-}
-
-// fullpath returns the absolute path from a relative path.
-func (v *Value) fullpath(path string) string {
-	if len(path) == 0 || path[0] != '/' {
-		return v.Name + "/" + path
-	}
-	return path
-}
-
-// Date performs a Time conversion, given an integer argument that represents a
-// time in nanoseconds since the Unix epoch.
-func (v *Value) Date(ns int64) time.Time {
-	return time.Unix(0, ns)
-}
-
-// Join joins the path elements.
-func (v *Value) Join(elem ...string) string {
-	return filepath.ToSlash(filepath.Join(elem...))
-}
-
-// Base returns the last element of the path.
-func (v *Value) Base(path string) string {
-	return filepath.Base(path)
-}
-
-// Glob performs a glob expansion of a pattern.
-func (v *Value) Glob(pattern string) ([]string, error) {
-	return glob(v.store, v.Name, pattern)
-}
-
-// Get fetches a value from the store.  The result is nil if the value does not
-// exist.
-func (v *Value) Get(path string) interface{} {
-	path = v.fullpath(path)
-	e, err := v.store.BindObject(path).Get(rt.R().TODOContext())
-	if err != nil {
-		return nil
-	}
-	return e.Value
-}
diff --git a/examples/storage/viewer/viewer.go b/examples/storage/viewer/viewer.go
deleted file mode 100644
index f7cee5b..0000000
--- a/examples/storage/viewer/viewer.go
+++ /dev/null
@@ -1,157 +0,0 @@
-// package viewer exports a store through an HTTP server, with the following features.
-//
-// URL paths correspond to store paths.  For example, if the URL is
-// http://myhost/a/b/c, the store value that is fetched is /a/b/c.
-//
-// Values can be formatted using html templates (documented in the
-// text/templates package).  Templates are named according to the type of the
-// value that they format, using a path /templates/<pkgPath>/<typeName>.  For
-// example, suppose we are viewing the page for /movies/Inception, and it
-// contains a value of type *examples/store/mdb/schema.Movie.  We fetch the
-// template /templates/examples/store/mdb/schema/Movie, which must be a string in
-// html/template format.  If it exists, the template is compiled and used to
-// print the value.  If the template does not exist, the value is formatted in
-// raw form.
-//
-// String values that have a path ending with suffix .css are printed in raw form.
-package viewer
-
-import (
-	"fmt"
-	"html"
-	"html/template"
-	"io"
-	"net/http"
-	"path/filepath"
-
-	"veyron2/rt"
-	"veyron2/storage"
-	"veyron2/vlog"
-)
-
-// server is the HTTP server handler.
-type server struct {
-	store storage.Store
-}
-
-var _ http.Handler = (*server)(nil)
-
-const (
-	// rawTemplateText is used to format the output in a raw textual form.
-	rawTemplateText = `<html>
-{{$value := .}}
-<head>
-<title>{{.Name}}</title>
-</head>
-<body>
-<h1>{{.Name}}</h1>
-<pre>{{.Value}}</pre>
-{{with .Subdirs}}
-<h3>Subdirectories</h3>
-{{range .}}
-<p><a href="{{.}}">{{$value.Base .}}</a></p>
-{{end}}
-{{end}}
-</body>
-</html>`
-)
-
-var (
-	rawTemplate = mustParse("raw", rawTemplateText)
-)
-
-// mustParse parses the template text.  It panics on error.
-func mustParse(name, text string) *template.Template {
-	tmpl, err := template.New(name).Parse(text)
-	if err != nil {
-		panic(fmt.Sprintf("Error parsing template %q: %s", text, err))
-	}
-	return tmpl
-}
-
-// loadTemplate fetches the template for the value from the store.  The template
-// is based on the type of the value, under /template/<pkgPath>/<typeName>.
-func (s *server) loadTemplate(v interface{}) *template.Template {
-	path := templatePath(v)
-	e, err := s.store.BindObject(path).Get(rt.R().TODOContext())
-	if err != nil {
-		return nil
-	}
-	str, ok := e.Value.(string)
-	if !ok {
-		return nil
-	}
-	tmpl, err := template.New(path).Parse(str)
-	if err != nil {
-		vlog.Infof("Template error: %s: %s", path, err)
-		return nil
-	}
-	return tmpl
-}
-
-// printRawValuePage prints the value in raw format.
-func (s *server) printRawValuePage(w http.ResponseWriter, path string, v interface{}) {
-	var p printer
-	p.print(v)
-	subdirs, _ := glob(s.store, path, "*")
-	x := &Value{Name: path, Value: p.String(), Subdirs: subdirs}
-	if err := rawTemplate.Execute(w, x); err != nil {
-		w.Write([]byte(html.EscapeString(err.Error())))
-	}
-}
-
-// printValuePage prints the value using a template if possible.  If a template
-// is not found, the value is printed in raw format instead.
-func (s *server) printValuePage(w http.ResponseWriter, path string, v interface{}) {
-	if tmpl := s.loadTemplate(v); tmpl != nil {
-		if err := tmpl.Execute(w, &Value{store: s.store, Name: path, Value: v}); err != nil {
-			w.Write([]byte(html.EscapeString(err.Error())))
-		}
-		return
-	}
-	s.printRawValuePage(w, path, v)
-}
-
-// printRawPage prints a string value directly, without processing.
-func (s *server) printRawPage(w http.ResponseWriter, v interface{}) {
-	str, ok := v.(string)
-	if !ok {
-		fmt.Fprintf(w, "%s", v)
-	} else {
-		io.WriteString(w, str)
-	}
-}
-
-// ServeHTTP is the main HTTP handler.
-func (s *server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
-	path := req.URL.Path
-	e, err := s.store.BindObject(path).Get(rt.R().TODOContext())
-	if err != nil {
-		msg := fmt.Sprintf("<html><body><h1>%s</h1><h2>Error: %s</h2></body></html>",
-			html.EscapeString(path),
-			html.EscapeString(err.Error()))
-		w.WriteHeader(http.StatusNotFound)
-		w.Write([]byte(msg))
-		return
-	}
-
-	q := req.URL.Query()
-	switch filepath.Ext(path) {
-	case ".css":
-		s.printRawPage(w, e.Value)
-	default:
-		if q["raw"] != nil {
-			s.printRawValuePage(w, path, e.Value)
-		} else {
-			s.printValuePage(w, path, e.Value)
-		}
-	}
-}
-
-// ListenAndServe is the main entry point.  It serves store at the specified
-// network address.
-func ListenAndServe(addr string, st storage.Store) error {
-	s := &server{store: st}
-	vlog.Infof("Viewer running at http://localhost%s", addr)
-	return http.ListenAndServe(addr, s)
-}
diff --git a/examples/todos/todos_appd/main.go b/examples/todos/todos_appd/main.go
index ebc1192..3c6c2b7 100644
--- a/examples/todos/todos_appd/main.go
+++ b/examples/todos/todos_appd/main.go
@@ -1,6 +1,6 @@
 // todos_appd is a web application backed by a Veyron store.
 //
-// For now, it simply displays the raw contents of the store.
+// It doesn't work yet, but it will soon. :)
 
 // TODO(sadovsky): Implement actual app, using Veyron {store,query,watch,sync}
 // over veyron.js.
@@ -11,22 +11,18 @@
 	"fmt"
 	"html/template"
 	"io"
-	"log"
 	"net/http"
 	"os"
 	"os/user"
 	"path"
 
-	"veyron/examples/storage/viewer"
 	_ "veyron/examples/todos/schema" // Register the todos/schema types.
 	"veyron2/rt"
-	"veyron2/storage/vstore"
 )
 
 var (
 	storeName string
-	port      = flag.Int("port", 10000, "IPV4 port number to serve")
-	useViewer = flag.Bool("useViewer", false, "If true, serve viewer instead")
+	port      = flag.Int("port", 10000, "IPV4 port to serve")
 )
 
 var rootDir = path.Join(
@@ -47,16 +43,6 @@
 	flag.StringVar(&storeName, "store", dir, "Name of the Veyron store")
 }
 
-func serveViewer() {
-	log.Printf("Binding to store on %s", storeName)
-	st, err := vstore.New(storeName)
-	if err != nil {
-		log.Fatalf("Can't connect to store: %s: %s", storeName, err)
-	}
-
-	viewer.ListenAndServe(fmt.Sprintf(":%d", *port), st)
-}
-
 func renderTemplate(w io.Writer, basename string, data interface{}) {
 	filename := path.Join(rootDir, "templates", basename)
 	t, err := template.ParseFiles(filename)
@@ -84,13 +70,10 @@
 func main() {
 	rt.Init()
 
-	if *useViewer {
-		serveViewer()
-	} else {
-		http.HandleFunc("/", wrap(handleHome))
-		http.Handle("/css/", http.FileServer(http.Dir(rootDir)))
-		http.Handle("/js/", http.FileServer(http.Dir(rootDir)))
-		fmt.Printf("Server running at http://localhost:%d\n", *port)
-		http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
-	}
+	http.HandleFunc("/", wrap(handleHome))
+	http.Handle("/css/", http.FileServer(http.Dir(rootDir)))
+	http.Handle("/js/", http.FileServer(http.Dir(rootDir)))
+
+	fmt.Printf("Server running at http://localhost:%d\n", *port)
+	http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
 }
diff --git a/examples/todos/todos_init/main.go b/examples/todos/todos_init/main.go
index 7013a0c..e163461 100644
--- a/examples/todos/todos_init/main.go
+++ b/examples/todos/todos_init/main.go
@@ -1,12 +1,4 @@
-// todos_init is a tool to initialize the store with an initial database. This
-// is really for demo purposes; in a real database, the contents would be
-// persistent.
-//
-// The data is loaded from a JSON file, todos_init/data.json.
-//
-// Since JSON doesn't support all of the store types, there is a translation
-// phase, where the contents are loaded into a string form, then converted to
-// the todos/schema schema.
+// todos_init reads data.json and populates the store with initial data.
 package main
 
 import (
@@ -42,7 +34,6 @@
 	if h, err := os.Hostname(); err == nil {
 		hostname = h
 	}
-	// TODO(sadovsky): Change this to be the correct veyron2 path.
 	dir := "global/vstore/" + hostname + "/" + username
 	flag.StringVar(&storeName, "store", dir, "Name of the Veyron store")
 }
@@ -61,7 +52,7 @@
 // state is the initial store state.
 type state struct {
 	store storage.Store
-	tname string // Current transaction name; nil if there's no transaction.
+	tname string // Current transaction name; empty if there's no transaction.
 }
 
 // newState returns a fresh state.
@@ -87,9 +78,11 @@
 	for i, _ := range l {
 		prefix := filepath.Join(l[:i]...)
 		o := st.store.BindObject(naming.Join(st.tname, prefix))
-		if _, err := o.Get(rt.R().TODOContext()); err != nil {
+		if exist, err := o.Exists(rt.R().TODOContext()); err != nil {
+			vlog.Infof("Error checking existence at %q: %s", prefix, err)
+		} else if !exist {
 			if _, err := o.Put(rt.R().TODOContext(), &schema.Dir{}); err != nil {
-				vlog.Errorf("Error creating parent %q: %s", prefix, err)
+				vlog.Infof("Error creating parent %q: %s", prefix, err)
 			}
 		}
 	}
@@ -171,9 +164,8 @@
 // main reads the data JSON file and populates the store.
 func main() {
 	// The client's identity needs to match the Admin ACLs at the empty store
-	// (since only the admin can put data). The identity here matches with that
-	// used for server.ServerConfig.Admin in todos_stored/main.go. An alternative
-	// would be to relax the ACLs on the store.
+	// (since only the admin can put data).
+	// TODO(sadovsky): What identity should we pass here?
 	rt.Init(veyron2.RuntimeID(security.FakePrivateID("anonymous")))
 
 	vlog.Infof("Binding to store on %s", storeName)
diff --git a/services/store/stored/main.go b/services/store/stored/main.go
index 0988933..55012ab 100644
--- a/services/store/stored/main.go
+++ b/services/store/stored/main.go
@@ -14,6 +14,7 @@
 
 import (
 	"flag"
+	"fmt"
 	"log"
 	"os"
 	"os/user"
@@ -22,6 +23,8 @@
 	"veyron/services/store/server"
 
 	"veyron2/rt"
+	"veyron2/storage/viewer"
+	"veyron2/storage/vstore"
 
 	_ "veyron/services/store/typeregistryhack"
 )
@@ -29,8 +32,11 @@
 var (
 	mountName string
 	dbName    = flag.String("db", "/var/tmp/veyron_store.db", "Metadata database")
-	// TODO(rthellend): Remove the address flag when the config manager is working.
-	address = flag.String("address", ":0", "Address to listen on.")
+	// TODO(rthellend): Remove the address flag when the config manager is
+	// working.
+	address    = flag.String("address", ":0", "Address to listen on")
+	viewerPort = flag.Int("viewerPort", 5000,
+		"IPV4 port to serve viewer from, or 0 to disable viewer")
 )
 
 func init() {
@@ -46,8 +52,7 @@
 	flag.StringVar(&mountName, "name", dir, "Mount point for media")
 }
 
-// Main starts the content service, taking arguments from the command line
-// flags.
+// main starts the store service, taking args from command line flags.
 func main() {
 	r := rt.Init()
 
@@ -58,7 +63,8 @@
 	}
 
 	// Create a new StoreService.
-	storeService, err := server.New(server.ServerConfig{Admin: r.Identity().PublicID(), DBName: *dbName})
+	storeService, err := server.New(
+		server.ServerConfig{Admin: r.Identity().PublicID(), DBName: *dbName})
 	if err != nil {
 		log.Fatal("server.New() failed: ", err)
 	}
@@ -80,6 +86,15 @@
 		log.Fatal("s.Serve() failed: ", err)
 	}
 
+	// Run viewer if requested.
+	if *viewerPort > 0 {
+		vst, err := vstore.New(mountName)
+		if err != nil {
+			log.Fatalf("Failed to start viewer: %s", err)
+		}
+		go viewer.ListenAndServe(fmt.Sprintf(":%d", *viewerPort), vst)
+	}
+
 	// Wait forever.
 	done := make(chan struct{})
 	<-done
diff --git a/services/store/typeregistryhack/init.go b/services/store/typeregistryhack/init.go
index 72b5216..bea5d02 100644
--- a/services/store/typeregistryhack/init.go
+++ b/services/store/typeregistryhack/init.go
@@ -8,7 +8,7 @@
 	// Register boxes types.
 	"veyron/examples/boxes"
 	// Register mdb types.
-	_ "veyron/examples/storage/mdb/schema"
+	_ "veyron/examples/mdb/schema"
 	// Register todos types.
 	_ "veyron/examples/todos/schema"
 	// Register bank types.