Adds Syncbase tutorial using Go Client API to website

Added tests, made the tutorial easy to follow (should work by just
copying and pasting the fragments instead of having to manually copy and
paste environment variables and endpoints).

Change-Id: I4856775b6a3c4c08604490d567d18aed22611df5
diff --git a/Makefile b/Makefile
index c42823f..ec0581f 100644
--- a/Makefile
+++ b/Makefile
@@ -89,6 +89,8 @@
 tutWipeSlate   = tutorials/wipe-slate
 tutHello       = tutorials/hello-world
 tutBasics      = tutorials/basics
+tutSyncbaseLocalPersist    = tutorials/syncbase/localPersist
+tutSyncbaseSync  = tutorials/syncbase/sync
 tutPrincipals  = tutorials/security/principals-and-blessings
 tutPermsAuth   = tutorials/security/permissions-authorizer
 tutCaveats1st  = tutorials/security/first-party-caveats
@@ -118,6 +120,8 @@
 completerScripts = \
 	$(completer)-hello-world.sh \
 	$(completer)-basics.sh \
+	$(completer)-syncbase-local-persist.sh \
+	$(completer)-syncbase-sync.sh \
 	$(completer)-permissions-authorizer.sh \
 	$(completer)-custom-authorizer.sh \
 	$(completer)-suffix-part1.sh \
@@ -133,7 +137,8 @@
 	$(scenario)-b-setup.sh \
 	$(scenario)-c-setup.sh \
 	$(scenario)-d-setup.sh \
-	$(scenario)-e-setup.sh
+	$(scenario)-e-setup.sh \
+	$(scenario)-f-setup.sh
 
 depsCommon = \
 	content/$(tutSetup).md \
@@ -268,6 +273,24 @@
 $(scenario)-b-setup.sh: $(completer)-basics.sh
 	cp $^ $@
 
+depsSyncbaseLocalPersist = $(depsBasics) content/$(tutSyncbaseLocalPersist).md
+.PHONY: test-syncbase-local-persist
+test-syncbase-local-persist: $(depsSyncbaseLocalPersist) | $(MDRIP)
+	$(MDRIP) --subshell test $^
+$(completer)-syncbase-local-persist.sh: $(depsSyncbaseLocalPersist) | $(MDRIP)
+	mkdir -p $(@D)
+	$(MDRIP) --preambled 0 completer $^ > $@
+$(scenario)-f-setup.sh: $(completer)-syncbase-local-persist.sh
+	cp $^ $@
+
+depsSyncbaseSync = $(depsSyncbaseLocalPersist) content/$(tutSyncbaseSync).md
+.PHONY: test-syncbase-sync
+test-syncbase-sync: $(depsSyncbaseSync) | $(MDRIP)
+	$(MDRIP) --subshell test $^
+$(completer)-syncbase-sync.sh: $(depsSyncbaseSync) | $(MDRIP)
+	mkdir -p $(@D)
+	$(MDRIP) --preambled 0 completer $^ > $@
+
 depsPrincipals = $(depsBasics) content/$(tutPrincipals).md
 .PHONY: test-principals
 test-principals: $(depsPrincipals) | $(MDRIP)
@@ -378,7 +401,12 @@
 	content/$(tutPrincipals).md \
 	content/$(tutPermsAuth).md \
 	content/$(tutSuffixPart1).md \
-	content/$(tutSuffixPart2).md
+	content/$(tutSuffixPart2).md \
+	content/$(tutWipeSlate).md \
+	content/$(tutBasics).md \
+	content/$(tutSyncbaseLocalPersist).md \
+	content/$(tutSyncbaseSync).md \
+
 
 # An ordering that lets us test all the Java tutorials faster than running the
 # individual tests in sequence.
@@ -470,4 +498,4 @@
 # Change this to the desired version before running the target.
 SYNCBASE_ANDROID_VERSION=0.1.7
 upgrade-syncbase-android:
-	find content/syncbase -type f -exec sed -i "s/\(compile 'io.v:syncbase:\)\(.*\)'/\1$(SYNCBASE_ANDROID_VERSION)'/g" {} \;
\ No newline at end of file
+	find content/syncbase -type f -exec sed -i "s/\(compile 'io.v:syncbase:\)\(.*\)'/\1$(SYNCBASE_ANDROID_VERSION)'/g" {} \;
diff --git a/content/tutorials/index.md b/content/tutorials/index.md
index b5adbdb..d7fe0e1 100644
--- a/content/tutorials/index.md
+++ b/content/tutorials/index.md
@@ -12,6 +12,10 @@
   {{# content.tutorials.naming }}
   * [{{ title }}]({{ url }})
   {{/ content.tutorials.naming }}
+* [Syncbase Basics](/tutorials/syncbase/)
+  {{# content.tutorials.syncbase }}
+  * [{{ title }}]({{ url }})
+  {{/ content.tutorials.syncbase }}
 * [Java](/tutorials/java/)
   {{# content.tutorials.java }}
   * [{{ title }}]({{ url }})
diff --git a/content/tutorials/syncbase/index.md b/content/tutorials/syncbase/index.md
new file mode 100644
index 0000000..5424935
--- /dev/null
+++ b/content/tutorials/syncbase/index.md
@@ -0,0 +1,29 @@
+= yaml =
+title: Overview
+layout: tutorial
+sort: 30
+toc: false
+= yaml =
+
+Syncbase provides a database that supports peer-to-peer synchronization built on
+top of Vanadium. It works even when devices are not connected to the Internet.
+
+In the following tutorials, we will modify the code from the [basics tutorial]
+so it runs over Syncbase. This will allow us to synchronize our set of fortunes
+across multiple devices.
+
+* [Persisting to Local Storage]<br> _Wherein_ fortunes are stored in a local
+  Syncbase.
+
+* [Exchanging Data]<br> _Wherein_ devices exchange their local data with each
+  other.
+
+These tutorials use a low level Syncbase API. The [Syncbase tutorial]
+complements this tutorial and uses a higher level API for mobile development
+which facilitates many of the patterns illustrated here.
+
+[basics tutorial]: /tutorials/basics.html
+[Syncbase tutorial]: /syncbase/tutorial/introduction.html
+[Persisting to Local Storage]: /tutorials/syncbase/localPersist.html
+[Exchanging Data]: /tutorials/syncbase/sync.html
+
diff --git a/content/tutorials/syncbase/localPersist.md b/content/tutorials/syncbase/localPersist.md
new file mode 100644
index 0000000..28fc473
--- /dev/null
+++ b/content/tutorials/syncbase/localPersist.md
@@ -0,0 +1,377 @@
+= yaml =
+title: Local Persistence
+layout: tutorial
+wherein: we persist the fortune teller service state in Syncbase.
+prerequisites: {completer: syncbase-local-persist, scenario: b}
+sort: 14
+toc: true
+= yaml =
+
+# Introduction
+
+This tutorial focuses on modifying the fortune application from the [basics
+tutorial] to persist data in Syncbase.
+
+Our Syncbase program will use the same architecture as our basic fortune
+program. A __client__ will communicate with a __server__ using RPC, which will
+call into a __service__.
+
+Now however, instead of the service keeping a local database of fortunes in
+memory, it will store the fortunes in Syncbase. Syncbase provides a __key-value
+store__ API; our service can `Put` a __key__ associated with some __value__, and
+`Get` the value back using the key.
+
+Syncbase stores data in __databases__, which themselves hold __collections__. In
+this tutorial we will create a new database and a new collection, and modify the
+`Add` and `Get` RPC calls to use Syncbase instead of an in-memory
+array.
+
+# Modifying the Service
+
+We will first modify the service to make calls into Syncbase instead of keeping
+a local array of fortunes in memory. The implementation below connects to a
+Syncbase instance, and modifies the `Add` and `Get` RPC calls to interact with
+Syncbase.
+
+<!-- @defineService @test @completer -->
+```
+mkdir -p $V_TUT/src/fortune/service
+ cat - <<EOF >$V_TUT/src/fortune/service/service.go
+{{# helpers.codedim}}
+package service
+
+import (
+  "fortune/ifc"
+  "math/rand"
+  "strconv"
+  "sync"
+
+  "v.io/v23/context"
+  "v.io/v23/rpc"
+{{/ helpers.codedim}}
+  "v.io/v23/syncbase"
+{{# helpers.codedim}}
+)
+{{/ helpers.codedim}}
+
+// Constant names of different Syncbase entities.
+const (
+  fortuneDatabaseName   = "fortuneDb"
+  fortuneCollectionName = "fortuneCollection"
+
+  // A special key that specifies the number of fortunes.
+  numFortunesKey = "numFortunes"
+)
+
+type impl struct {
+  random        *rand.Rand   // To pick a random fortune
+  mu            sync.RWMutex // To safely enable concurrent use.
+
+  syncbaseName       string  // The Syncbase endpoint
+
+  sbs syncbase.Service    // Handle to the Syncbase service
+  d   syncbase.Database   // Handle to the fortunes database
+  c   syncbase.Collection // Handle to the fortunes collection
+}
+
+// Makes an implementation.
+func Make(ctx *context.T, syncbaseName string) ifc.FortuneServerMethods {
+{{# helpers.codedim}}
+  impl := &impl{
+    random:             rand.New(rand.NewSource(99)),
+{{/ helpers.codedim}}
+    syncbaseName:       syncbaseName,
+  }
+  if err := impl.initSyncbase(ctx); err != nil {
+    panic(err)
+  }
+{{# helpers.codedim}}
+  return impl
+}
+{{/ helpers.codedim}}
+
+// Initialize Syncbase by creating a new service, database and collection.
+func (f *impl) initSyncbase(ctx *context.T) error {
+  // Create a new service handle and a database to store the fortunes.
+  sbs := syncbase.NewService(f.syncbaseName)
+  d := sbs.Database(ctx, fortuneDatabaseName, nil)
+  if err := d.Create(ctx, nil); err != nil {
+      return err
+  }
+
+  // Create the collection where we store fortunes.
+  c := d.Collection(ctx, fortuneCollectionName)
+  if err := c.Create(ctx, nil); err != nil {
+      return err
+  }
+
+{{# helpers.codedim}}
+  f.sbs = sbs
+  f.d = d
+  f.c = c
+  return nil
+{{/ helpers.codedim}}
+}
+
+// Get RPC implementation. Returns a fortune retrieved from Syncbase.
+func (f *impl) Get(ctx *context.T, _ rpc.ServerCall) (string, error) {
+  f.mu.RLock()
+  defer f.mu.RUnlock()
+
+  var numKeys int
+  if err := f.c.Get(ctx, numFortunesKey, &numKeys); err != nil || numKeys == 0 {
+    return "[empty]", nil
+  }
+
+  // Get a random number in the range [0, numKeys) and convert it to a string;
+  // this acts as the key in the sycnbase collection.
+  key := strconv.Itoa(f.random.Intn(numKeys))
+  var value string
+
+  if err := f.c.Get(ctx, key, &value); err == nil {
+    return value, nil
+  } else {
+    return "[error]", err
+  }
+}
+
+// Add RPC implementation. Adds a new fortune by persisting it to Syncbase.
+func (f *impl) Add(ctx *context.T, _ rpc.ServerCall, fortune string) error {
+  f.mu.Lock()
+  defer f.mu.Unlock()
+
+  var numKeys int
+  if err := f.c.Get(ctx, numFortunesKey, &numKeys); err != nil {
+    numKeys = 0
+  }
+
+  // Put the fortune into Syncbase.
+  key := strconv.Itoa(numKeys)
+  if err := f.c.Put(ctx, key, &fortune); err != nil {
+    return err
+  }
+
+  // Update the number of keys.
+  return f.c.Put(ctx, numFortunesKey, numKeys+1)
+}
+
+EOF
+```
+
+That's a lot of code! We will go through it function by function below.
+
+## Make
+
+Our `Make` function looks the same as it did before, but with an additional
+field `syncbaseName`. Each Syncbase instance has a _name_; think of this as an
+address for finding where the Syncbase is.
+
+## Initializing Syncbase
+
+Syncbase provides a storage service that can be shared between different apps.
+Apps thus use RPC calls to the Syncbase service to create and access their own
+databases.
+
+Syncbase initialization occurs in `initSyncbase`. The high level steps are as
+follows:
+
+1. Create a new database.
+
+2. Create a new collection. The collection
+   stores the keys and values (in this case, our fortunes).
+
+## Get and Add
+
+Finally, we have our `Get` and `Add` functions. Let's break these down.
+
+The first notable change is that we store the number of fortunes we have put
+into Syncbase using a special key `numFortunesKey`. After getting the number of
+fortunes we have in Syncbase, we must decide which fortune to return. We want a
+random fortune based on the random number generator, but our keys have to be
+strings; The `strconv.Itoa` function converts a random number to a string, which
+we can use a key in Syncbase.
+
+Next, we call `Get` on our collection; this call fetches the value into the
+variable `value`. We check for errors and return the fortune if
+everything looks alright.
+
+The `Add` function works similarly, except we also increment the counter which
+holds how many fortunes we have in our Syncbase.
+
+# Server
+
+We need to make a small change to our server. Namely, we need to pass in the
+name of our Syncbase instance, so we can pass this to our service, which in turn
+will use the name to connect to Syncbase. The core server logic remains
+unchanged.
+
+<!-- @defineServer @test @completer -->
+```
+mkdir -p $V_TUT/src/fortune/server
+ cat - <<EOF >$V_TUT/src/fortune/server/main.go
+package main
+
+{{# helpers.codedim }}
+import (
+  "fmt"
+  "flag"
+  "fortune/ifc"
+  "fortune/server/util"
+  "fortune/service"
+  "log"
+
+  "v.io/v23"
+  "v.io/v23/rpc"
+  "v.io/x/ref/lib/signals"
+  _ "v.io/x/ref/runtime/factories/generic"
+)
+
+var (
+  serviceName = flag.String(
+    "service-name", "",
+    "Name for service in default mount table.")
+{{/ helpers.codedim }}
+  syncbaseName = flag.String(
+    "sb-name", "",
+    "Name of Syncbase service")
+{{# helpers.codedim }}
+)
+
+func main() {
+  ctx, shutdown := v23.Init()
+  defer shutdown()
+
+{{/ helpers.codedim }}
+  fortune := ifc.FortuneServer(service.Make(ctx, *syncbaseName))
+{{# helpers.codedim }}
+
+  // If the dispatcher isn't nil, it's presumed to have
+  // obtained its authorizer from util.MakeAuthorizer().
+  dispatcher := util.MakeDispatcher()
+
+  // Start serving.
+  var err error
+  var server rpc.Server
+  if dispatcher == nil {
+    // Use the default dispatcher.
+    _, server, err = v23.WithNewServer(
+      ctx, *serviceName, fortune, util.MakeAuthorizer())
+  } else {
+    _, server, err = v23.WithNewDispatchingServer(
+      ctx, *serviceName, dispatcher)
+  }
+  if err != nil {
+    log.Panic("Error serving service: ", err)
+  }
+  endpoint := server.Status().Endpoints[0]
+  util.SaveEndpointToFile(endpoint)
+  fmt.Printf("Listening at: %v\n", endpoint)
+
+  // Wait forever.
+  <-signals.ShutdownOnSignals(ctx)
+}
+{{/ helpers.codedim }}
+
+EOF
+```
+
+
+{{# helpers.hidden}}
+<!-- @removeCodeDimMarkup @test @completer -->
+```
+sed -i 's/{{.*}}//' $V_TUT/src/fortune/server/main.go
+sed -i 's/{{.*}}//' $V_TUT/src/fortune/service/service.go
+```
+{{/ helpers.hidden}}
+
+Finally, install the client and server:
+
+<!-- @installClientServer @test @completer -->
+```
+go install fortune/server
+go install fortune/client
+```
+
+# Credentials
+
+We will create a Syncbase instance tied to the fortune application and Alice's
+devices. To authorize this, we make a new blessing `idp:o:fortune:alice`.
+
+Syncbase requires this naming scheme for its blessings.
+
+<!-- @makeCreds @test @completer -->
+```
+$V_BIN/principal create \
+  --with-passphrase=false \
+  --overwrite $V_TUT/cred/alice idp:o:fortune:alice
+```
+
+# Run Your Code
+
+First, start a Syncbase instance. Like our server, Syncbase spits out an endpoint
+which we write to a file. We then sleep until this endpoint appears, since we
+need it to start our server.
+
+<!-- @startSb1 @test @completer -->
+```
+$V_BIN/syncbased \
+  --v23.tcp.address=127.0.0.1:0 \
+  --v23.credentials=$V_TUT/cred/alice > $V_TUT/endpoint 2> /dev/null &
+TUT_PID_SB1=$!
+while [ ! -s $V_TUT/endpoint ]; do sleep 1; done
+```
+
+Then, start the server:
+
+<!-- @startServer1 @test @completer @sleep -->
+```
+rm -f $V_TUT/server.txt
+$V_TUT/bin/server \
+  --v23.credentials=$V_TUT/cred/alice \
+  --v23.tcp.address=127.0.0.1:0 \
+  --endpoint-file-name=$V_TUT/server.txt \
+  --sb-name=`cat $V_TUT/endpoint | grep 'ENDPOINT=' | cut -d'=' -f2` &> /dev/null &
+TUT_PID_SERVER1=$!
+```
+
+We can now make RPC calls:
+
+<!-- @initialClientCall @test @completer -->
+```
+$V_TUT/bin/client \
+  --v23.credentials=$V_TUT/cred/alice \
+  --server=`cat $V_TUT/server.txt` \
+  --add='The greatest risk is not taking one.'
+```
+
+<!-- @secondClientCall @completer @test -->
+```
+$V_TUT/bin/client \
+  --v23.credentials=$V_TUT/cred/alice \
+  --server=`cat $V_TUT/server.txt`
+```
+
+The second call should return the fortune we just added. The fortune is
+persisted in Syncbase.
+
+# Cleanup
+
+To clean up, kill the servers, Syncbase instances, and remove any temporary
+files.
+
+<!-- @cleanup @test @completer -->
+```
+kill_tut_process TUT_PID_SERVER1
+kill_tut_process TUT_PID_SB1
+```
+
+# Summary
+
+* You wrote a service which connects with Syncbase, creates a fortunes
+  collection, and persists data in that collection.
+
+There is a lot more you can do with Syncbase. To dive deeper, see the [Syncbase
+tutorial].
+
+[basics tutorial]: /tutorials/basics.html
+[Syncbase tutorial]: /syncbase/tutorial/introduction.html
diff --git a/content/tutorials/syncbase/sync.md b/content/tutorials/syncbase/sync.md
new file mode 100644
index 0000000..a92dc92
--- /dev/null
+++ b/content/tutorials/syncbase/sync.md
@@ -0,0 +1,442 @@
+= yaml =
+title: Syncing
+layout: tutorial
+wherein: we distribute the fortune teller service state across multiple devices using Syncbase.
+prerequisites: {completer: syncbase-sync, scenario: f}
+sort: 14
+toc: true
+= yaml =
+
+# Introduction
+
+In this tutorial, we will extend the Syncbase [local persistence tutorial] so
+that multiple fortune teller services can _exchange_ data.
+
+Syncbase uses collections as the unit of synchronization. Devices join groups
+called _syncgroups_, and each syncgroup manages one or more collections and
+ensures that each device in the syncgroup receives updates to its collections.
+In this tutorial we will add the fortune collection from the [local persistence
+tutorial] to a new syncgroup, and then add multiple fortune services to the same
+syncgroup in order to exchange data between them.
+
+# Modifying the Service
+
+First, we need to modify the service to join a syncgroup, and make small changes to the
+`Add` and `Get` RPCs.
+
+<!-- @defineService @test @completer -->
+```
+mkdir -p $V_TUT/src/fortune/service
+ cat - <<EOF >$V_TUT/src/fortune/service/service.go
+{{# helpers.codedim}}
+package service
+
+import (
+  "fortune/ifc"
+  "math/rand"
+  "strconv"
+  "strings"
+  "sync"
+
+  "v.io/v23/context"
+  "v.io/v23/rpc"
+  "v.io/v23/security"
+  "v.io/v23/security/access"
+  wire "v.io/v23/services/syncbase"
+  "v.io/v23/syncbase"
+)
+
+// Constant names of different Syncbase entities.
+const (
+  fortuneDatabaseName   = "fortuneDb"
+  fortuneCollectionName = "fortuneCollection"
+{{/ helpers.codedim}}
+  fortuneSyncgroupName  = "fortuneSyncgroup"
+{{# helpers.codedim}}
+
+  // A special key that specifies the number of fortunes.
+  numFortunesKey = "numFortunes"
+)
+
+type impl struct {
+  random *rand.Rand   // To pick a random fortune
+  mu     sync.RWMutex // To safely enable concurrent use.
+
+  syncbaseName       string // The Syncbase endpoint
+  remoteSyncbaseName string // A remote endpoint to connect to
+
+  sbs syncbase.Service    // Handle to the Syncbase service
+  d   syncbase.Database   // Handle to the fortunes database
+  c   syncbase.Collection // Handle to the fortunes collection
+}
+
+// Makes an implementation.
+func Make(ctx *context.T, syncbaseName, remoteSyncbaseName string) ifc.FortuneServerMethods {
+  impl := &impl{
+    random:             rand.New(rand.NewSource(99)),
+    syncbaseName:       syncbaseName,
+{{/ helpers.codedim}}
+    remoteSyncbaseName: remoteSyncbaseName,
+{{# helpers.codedim}}
+  }
+
+  if err := impl.initSyncbase(ctx); err != nil {
+    panic(err)
+  }
+  return impl
+}
+
+// Initialize Syncbase by establishing a new service and creating a new database
+// and a new collection.
+func (f *impl) initSyncbase(ctx *context.T) error {
+
+  // Create a new service and get its database.
+  sbs := syncbase.NewService(f.syncbaseName)
+  d := sbs.Database(ctx, fortuneDatabaseName, nil)
+  if err := d.Create(ctx, nil); err != nil {
+    return err
+  }
+
+  // Create the collection where we will store fortunes.
+  c := d.Collection(ctx, fortuneCollectionName)
+  if err := c.Create(ctx, nil); err != nil {
+    return err
+  }
+
+{{/ helpers.codedim}}
+  // Join a syncgroup if there is someone to connect to, otherwise create one.
+  sg := d.Syncgroup(ctx, fortuneSyncgroupName)
+  sgPerms := access.Permissions{}
+  sgPerms.Add(security.BlessingPattern(sg.Id().Blessing),
+    access.TagStrings(wire.AllSyncgroupTags...)...)
+
+  sgSpec := wire.SyncgroupSpec{
+    Description: "FortuneSyncgroup",
+    Perms:       sgPerms,
+    Collections: []wire.Id{c.Id()},
+  }
+  sgInfo := wire.SyncgroupMemberInfo{SyncPriority: 10}
+
+  if f.remoteSyncbaseName != "" {
+    if _, err := sg.Join(ctx, f.remoteSyncbaseName, nil, sgInfo); err != nil {
+      return err
+    }
+  } else {
+    if err := sg.Create(ctx, sgSpec, sgInfo); err != nil {
+      return err
+    }
+  }
+{{# helpers.codedim}}
+
+  f.sbs = sbs
+  f.d = d
+  f.c = c
+  return nil
+}
+
+// Get RPC implementation. Returns a fortune retrieved from Syncbase.
+func (f *impl) Get(ctx *context.T, _ rpc.ServerCall) (string, error) {
+  f.mu.RLock()
+  defer f.mu.RUnlock()
+
+{{/ helpers.codedim}}
+  counts := make([]int, 0)
+  devices := make([]string, 0)
+  it := f.c.Scan(ctx, syncbase.Prefix(numFortunesKey))
+  for it.Advance() {
+    var numFortunes int
+    dev := strings.TrimPrefix(it.Key(), numFortunesKey)
+    if err := it.Value(&numFortunes); err != nil {
+      return "[error]", err
+    }
+
+    if numFortunes > 0 {
+      counts = append(counts, numFortunes)
+      devices = append(devices, dev)
+    }
+  }
+
+  if len(counts) == 0 {
+    return "[empty]", nil
+  }
+
+  index := f.random.Intn(len(counts))
+  count := counts[index]
+  dev := devices[index]
+
+  // Get a random number in the range [0, numKeys) and convert it to a string;
+  // this acts as the key in the fortunes collection.
+  key := strconv.Itoa(f.random.Intn(count)) + dev
+{{# helpers.codedim}}
+
+  var value string
+  if err := f.c.Get(ctx, key, &value); err == nil {
+    return value, nil
+  } else {
+    return "[error]", err
+  }
+}
+
+// Add RPC implementation. Adds a new fortune by persisting it to Syncbase.
+func (f *impl) Add(ctx *context.T, _ rpc.ServerCall, fortune string) error {
+  f.mu.Lock()
+  defer f.mu.Unlock()
+{{/ helpers.codedim}}
+
+  var numKeys int
+  if err := f.c.Get(ctx, numFortunesKey+f.syncbaseName, &numKeys); err != nil {
+    numKeys = 0
+  }
+
+  // Put the fortune into Syncbase.
+  key := strconv.Itoa(numKeys)
+  if err := f.c.Put(ctx, key+f.syncbaseName, &fortune); err != nil {
+    return err
+  }
+
+  // Update the number of keys.
+  return f.c.Put(ctx, numFortunesKey+f.syncbaseName, numKeys+1)
+
+{{# helpers.codedim}}
+}
+{{/ helpers.codedim}}
+
+
+EOF
+```
+
+__Code Walk__<br>
+
+Our `Make` function now takes one new argument: a `remoteSyncbaseName`. This
+name points to another existing Syncbase instance that this service will use to
+join the syncgroup. If the name is an empty string, then the service is
+responsible for creating the syncgroup (and other services will use the service
+as their remote).
+
+The new code in the `initSyncbase` function sets up permissions on the new
+syncgroup, adds the fortune collection to it, and then tries to join a syncgroup
+or creates one if no remote exists.
+
+We have changed all our `Get` and `Put` calls to Syncbase by appending the
+(unique) Syncbase name to each key. This is because by default, Syncbase uses a
+_last write wins_ policy to resolve conflicts to the same key. A conflict arises
+when two Syncbases attempt to update a value for the same key.
+
+As an example of a potential conflict, consider updating the `numFortunesKey`
+value concurrently on two services. If both services update the value from 1 to
+2, it means two fortunes were added (one by each service), and the true value
+should be 3. Instead, Syncbase will simply pick the latest value
+written (2 in this scenario), and our data will be inconsistent. Adding a unique
+identifier for each key prevents these conflicts since concurrent updates to the
+same key never occur.
+
+This makes the `Get` RPC slightly more complex. We use the `Scan` method with a
+prefix to read the number of fortunes _each_ Syncbase has added (each number is
+stored in a unique key, but Syncbase can perform scan on _prefixes_). We pick
+one of the Syncbases at random, and then randomly pick one of the fortunes from
+that Syncbase.
+
+# Server
+
+The server requires a small change: we need to pass a remote Syncbase name to
+the service's `Make` function.
+
+<!-- @defineServer @test @completer -->
+```
+mkdir -p $V_TUT/src/fortune/server
+ cat - <<EOF >$V_TUT/src/fortune/server/main.go
+package main
+
+{{# helpers.codedim }}
+import (
+  "fmt"
+  "flag"
+  "fortune/ifc"
+  "fortune/server/util"
+  "fortune/service"
+  "log"
+
+  "v.io/v23"
+  "v.io/v23/rpc"
+  "v.io/x/ref/lib/signals"
+  _ "v.io/x/ref/runtime/factories/generic"
+)
+
+var (
+  serviceName = flag.String(
+    "service-name", "",
+    "Name for service in default mount table.")
+  syncbaseName = flag.String(
+    "sb-name", "",
+    "Name of Syncbase service")
+{{/ helpers.codedim }}
+  remoteSyncbaseName = flag.String(
+    "remote-sb-name", "",
+    "Name of remote Syncbase service")
+{{# helpers.codedim }}
+)
+
+func main() {
+  ctx, shutdown := v23.Init()
+  defer shutdown()
+{{/ helpers.codedim }}
+  fortune := ifc.FortuneServer(service.Make(ctx, *syncbaseName,  *remoteSyncbaseName))
+{{# helpers.codedim }}
+
+  // If the dispatcher isn't nil, it's presumed to have
+  // obtained its authorizer from util.MakeAuthorizer().
+  dispatcher := util.MakeDispatcher()
+
+  // Start serving.
+  var err error
+  var server rpc.Server
+  if dispatcher == nil {
+    // Use the default dispatcher.
+    _, server, err = v23.WithNewServer(
+      ctx, *serviceName, fortune, util.MakeAuthorizer())
+  } else {
+    _, server, err = v23.WithNewDispatchingServer(
+      ctx, *serviceName, dispatcher)
+  }
+  if err != nil {
+    log.Panic("Error serving service: ", err)
+  }
+  endpoint := server.Status().Endpoints[0]
+  util.SaveEndpointToFile(endpoint)
+  fmt.Printf("Listening at: %v\n", endpoint)
+
+  // Wait forever.
+  <-signals.ShutdownOnSignals(ctx)
+}
+{{/ helpers.codedim }}
+
+EOF
+```
+
+
+{{# helpers.hidden}}
+<!-- @removeCodeDimMarkup @test @completer -->
+```
+sed -i 's/{{.*}}//' $V_TUT/src/fortune/server/main.go
+sed -i 's/{{.*}}//' $V_TUT/src/fortune/service/service.go
+```
+{{/ helpers.hidden}}
+
+Install the client and server:
+
+<!-- @installClientServer @test @completer -->
+```
+go install fortune/server
+go install fortune/client
+```
+
+# Run Your Code
+
+We will start a Syncbase instance like before:
+
+<!-- @startSb1 @test @completer @sleep -->
+```
+syncbased \
+  --v23.credentials=$V_TUT/cred/alice \
+  --v23.tcp.address=127.0.0.1:0 > $V_TUT/endpoint1  2> /dev/null &
+TUT_PID_SB1=$!
+while [ ! -s $V_TUT/endpoint1 ]; do sleep 1; done
+```
+
+Then we will start a server:
+
+<!-- @startServer1 @test @completer @sleep -->
+```
+rm -f $V_TUT/server1.txt $V_TUT/server2.txt
+$V_TUT/bin/server \
+  --v23.credentials=$V_TUT/cred/alice \
+  --v23.tcp.address=127.0.0.1:0 \
+  --endpoint-file-name=$V_TUT/server1.txt \
+  --sb-name=`cat $V_TUT/endpoint1 | grep 'ENDPOINT=' | cut -d'=' -f2` &> /dev/null &
+TUT_PID_SERVER1=$!
+```
+
+Now, we will start a _second_ Syncbase. We will store the
+endpoint in a different file `$V_TUT/endpoint2`:
+
+<!-- @startSb2 @test @completer @sleep -->
+```
+syncbased \
+  --v23.tcp.address=127.0.0.1:0 \
+  --v23.credentials=$V_TUT/cred/alice > $V_TUT/endpoint2 2> /dev/null &
+TUT_PID_SB2=$!
+while [ ! -s $V_TUT/endpoint2 ]; do sleep 1; done
+```
+
+Now start another server, and pass the first Syncbase endpoint as
+the remote Syncbase name:
+
+<!-- @startServer2 @test @completer @sleep -->
+```
+$V_TUT/bin/server \
+  --v23.credentials=$V_TUT/cred/alice \
+  --v23.tcp.address=127.0.0.1:0 \
+  --endpoint-file-name=$V_TUT/server2.txt \
+  --sb-name=`cat $V_TUT/endpoint2 | grep 'ENDPOINT=' | cut -d'=' -f2` \
+  --remote-sb-name=`cat $V_TUT/endpoint1 | grep 'ENDPOINT=' | cut -d'=' -f2` &> /dev/null &
+TUT_PID_SERVER2=$!
+```
+
+We can now make RPC calls:
+
+<!-- @initialClientCall @test @completer -->
+```
+$V_TUT/bin/client \
+  --v23.credentials=$V_TUT/cred/alice \
+  --server=`cat $V_TUT/server1.txt` \
+  --add "The greatest risk is not taking one."
+
+$V_TUT/bin/client \
+  --v23.credentials=$V_TUT/cred/alice \
+  --server=`cat $V_TUT/server2.txt` \
+  --add "A new challenge is near."
+```
+
+Now get the fortunes back:
+
+<!-- @getClientCall @test @completer -->
+```
+$V_TUT/bin/client \
+  --v23.credentials=$V_TUT/cred/alice \
+  --server=`cat $V_TUT/server1.txt`
+```
+
+Calling the `Get` RPC above enough times should return both the first and second
+fortune eventually, even though the two fortunes were added to _different_
+services. Syncbases exchange data in the fortune collection using the syncgroup
+we set up.
+
+# Cleanup
+
+To clean up, kill the servers, Syncbase instances, and remove any temporary
+files.
+
+<!-- @cleanup @test @completer -->
+```
+kill_tut_process TUT_PID_SERVER1
+kill_tut_process TUT_PID_SERVER2
+kill_tut_process TUT_PID_SB1
+kill_tut_process TUT_PID_SB2
+rm -f $V_TUT/server1.txt
+rm -f $V_TUT/server2.txt
+```
+
+# Summary
+
+* You wrote a service which connects with Syncbase, creates a Syncbase
+  collection, joins a syncgroup, and issues `Put` and `Get` calls to the
+  collection.
+
+* You launched multiple servers and Syncbases instances, issued client RPC calls
+  to both, and watched the data between the two instances sync.
+
+There is a lot more you can do with Syncbase. To dive deeper, see the [Syncbase
+tutorial].
+
+[local persistence tutorial]: /tutorials/syncbase/localPersist.html
+[Syncbase tutorial]: /syncbase/tutorial/introduction.html
diff --git a/templates/partials/core_sidebar.mustache b/templates/partials/core_sidebar.mustache
index 949b2bc..a03dad8 100644
--- a/templates/partials/core_sidebar.mustache
+++ b/templates/partials/core_sidebar.mustache
@@ -29,6 +29,14 @@
       {{/ content.tutorials.naming }}
     </nav>
 
+    <a href="#" class="nav">Syncbase</a>
+    <nav>
+      <a href="/tutorials/syncbase/">Overview</a>
+      {{# content.tutorials.syncbase }}
+      <a href="{{ url }}">{{ title }}</a>
+      {{/ content.tutorials.syncbase }}
+    </nav>
+
     <a href="#" class="nav">Java</a>
     <nav>
       <a href="/tutorials/java/">Overview</a>