Java: first draft of the syncbase API

Change-Id: I75a1ae76ea6f754b20242d13a726ac65bb9bbfd0
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 0000000..6e35645
--- /dev/null
+++ b/lib/.gitignore
@@ -0,0 +1,16 @@
+# Vanadium
+/.v23
+
+# Gradle
+.gradle/
+/build/
+.idea/
+/generated-src/
+
+# Eclipse
+.classpath
+.project
+.settings
+
+# IntelliJ
+lib.iml
diff --git a/lib/build.gradle b/lib/build.gradle
new file mode 100644
index 0000000..ccb6d77
--- /dev/null
+++ b/lib/build.gradle
@@ -0,0 +1,71 @@
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.2.3'
+        classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0'
+    }
+}
+
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+
+    maven {
+        url "http://srdjan.mtv:8081/nexus/content/repositories/test_repo/"
+    }
+}
+
+dependencies {
+    compile 'io.v:vanadium:0.1-SNAPSHOT'
+    compile group: 'com.google.guava', name: 'guava', version: '18.0'
+    testCompile group: 'junit', name: 'junit', version: '4.12'
+    testCompile group: 'com.google.truth', name: 'truth', version: '0.25'
+}
+
+class VanadiumEnvironment {
+    File v23Root;
+    File v23Bin;
+
+    public static getVanadiumEnvironment() {
+        VanadiumEnvironment result = new VanadiumEnvironment()
+
+        if (!System.getenv().containsKey('V23_ROOT')) {
+            throw new InvalidUserDataException("V23_ROOT is not set. "
+                    + "Please follow the Vanadium installation instructions at "
+                    + "https://v.io/installation/index.html")
+        }
+
+        result.v23Root = new File(System.getenv()['V23_ROOT'])
+        result.v23Bin = new File(result.v23Root, ['devtools', 'bin', 'v23'].join(File.separator))
+        if (!result.v23Bin.exists() || !result.v23Bin.isFile() || !result.v23Bin.canExecute()) {
+            throw new InvalidUserDataException(
+                    result.v23Bin.toString() + " does not exist or is not an executable file. "
+                    + "Please follow the Vanadium installation instructions at "
+                    + "https://v.io/installation/index.html")
+        }
+        return result
+    }
+}
+
+def v23Root = VanadiumEnvironment.getVanadiumEnvironment().v23Root.getAbsolutePath()
+def v23Bin = VanadiumEnvironment.getVanadiumEnvironment().v23Bin.getAbsolutePath()
+def vdlBin = [v23Root, 'release', 'go', 'bin', 'vdl'].join(File.separator)
+def vdlPath = [ [v23Root, 'roadmap', 'go', 'src'].join(File.separator),
+                [v23Root, 'release', 'go', 'src'].join(File.separator) ].join(":")
+
+sourceSets.main.java.srcDirs += 'generated-src/vdl'
+
+task buildVdlTool(type: Exec) {
+    commandLine v23Bin, 'go', 'install', 'v.io/x/ref/cmd/vdl'
+}
+
+task generateVdl(type: Exec, dependsOn: buildVdlTool) {
+    environment VDLPATH: vdlPath
+    commandLine vdlBin, 'generate', '--lang=java', '--java-out-dir=generated-src/vdl', 'all'
+}
+
+tasks.'compileJava'.dependsOn(generateVdl)
\ No newline at end of file
diff --git a/lib/gradle/wrapper/gradle-wrapper.jar b/lib/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..085a1cd
--- /dev/null
+++ b/lib/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/lib/gradle/wrapper/gradle-wrapper.properties b/lib/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..5787069
--- /dev/null
+++ b/lib/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Jun 18 09:35:48 PDT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-bin.zip
diff --git a/lib/gradlew b/lib/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/lib/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/SyncbaseApp.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/SyncbaseApp.java
new file mode 100644
index 0000000..4d0be37
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/SyncbaseApp.java
@@ -0,0 +1,77 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.v23.services.syncbase;
+
+import io.v.syncbase.v23.services.syncbase.nosql.Database;
+import io.v.syncbase.v23.services.syncbase.nosql.Schema;
+import io.v.syncbase.v23.services.syncbase.util.AccessController;
+import io.v.v23.context.VContext;
+import io.v.v23.security.access.Permissions;
+import io.v.v23.verror.VException;
+
+/**
+ * A handle for an app running as part of a {@link SyncbaseService}.
+ */
+public interface SyncbaseApp extends AccessController {
+    /**
+     * Returns the relative name of this app.
+     */
+    String name();
+
+    /**
+     * Returns the full (i.e., object) name of this app.
+     */
+    String fullName();
+
+    /**
+     * Returns {@code true} iff this app exists and the user has sufficient
+     * permissions to access it.
+     *
+     * @param  ctx        Vanadium context
+     * @return            {@code true} iff this app exists and the user has sufficient
+     *                    permissions to access it
+     * @throws VException if the app's existence couldn't be determined
+     */
+    boolean exists(VContext ctx) throws VException;
+
+    /**
+     * Returns a handle for a NoSQL database with the provided name.
+     * <p>
+     * Note that this database may not yet exist and can be created using the
+     * {@link Database#create} method.
+     *
+     * @param  relativeName name of the NoSQL database; must not contain slashes
+     * @param  schema       database schema;  it can be {@code null} only if schema was never
+     *                      set for the database in the first place
+     * @return              a handle for a NoSQL database with the provided name
+     */
+    Database getNoSqlDatabase(String relativeName, Schema schema);
+
+    /**
+     * Returns a list of all database names.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the list of database names couldn't be retrieved
+     */
+    String[] listDatabases(VContext ctx) throws VException;
+
+    /**
+     * Creates the app.
+     *
+     * @param  ctx        Vanadium context
+     * @param  perms      app permissions; if {@code null}, {@link SyncbaseService}'s
+     *                    permissions are used
+     * @throws VException if the app couldn't be created
+     */
+    void create(VContext ctx, Permissions perms) throws VException;
+
+    /**
+     * Deletes the app.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the app couldn't be deleted
+     */
+    void delete(VContext ctx) throws VException;
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/SyncbaseService.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/SyncbaseService.java
new file mode 100644
index 0000000..62d2bb5
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/SyncbaseService.java
@@ -0,0 +1,37 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package io.v.syncbase.v23.services.syncbase;
+
+import io.v.v23.context.VContext;
+import io.v.syncbase.v23.services.syncbase.util.AccessController;
+import io.v.v23.verror.VException;
+
+/**
+ * The interface for a Vanadium Syncbase service.
+ */
+public interface SyncbaseService extends AccessController {
+    /**
+     * Returns the full (i.e., object) name of this service.
+     */
+    String fullName();
+
+    /**
+     * Returns the handle to an app with the given name.
+     * <p>
+     * Note that this app may not yet exist and can be created using the
+     * {@link SyncbaseApp#create} call.
+     *
+     * @param  relativeName name of the given app.  May not contain slashes
+     * @return              the handle to an app with the given name
+     */
+    SyncbaseApp getApp(String relativeName);
+
+    /**
+     * Returns a list of all app names.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the list of app names couldn't be retrieved
+     */
+    String[] listApps(VContext ctx) throws VException;
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/BatchDatabase.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/BatchDatabase.java
new file mode 100644
index 0000000..c3cedbb
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/BatchDatabase.java
@@ -0,0 +1,33 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import io.v.v23.context.VContext;
+import io.v.v23.verror.VException;
+
+/**
+ * A handle to a set of reads and writes to the database that should be considered an atomic unit.
+ * <p>
+ * See {@link Database#beginBatch Database.beginBatch()} for concurrency semantics.
+ */
+public interface BatchDatabase extends DatabaseCore {
+    /**
+     * Persists the pending changes to the database.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if there was an error committing the changes
+     */
+    void commit(VContext ctx) throws VException;
+
+    /**
+     * Notifies the server that any pending changes can be discarded.
+     * <p>
+     * This method is not strictly required, but it may allow the server to release locks
+     * or other resources sooner than if it was not called.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if there was an error discarding the changes
+     */
+    void abort(VContext ctx) throws VException;
+}
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Database.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Database.java
new file mode 100644
index 0000000..d1f2e2f
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Database.java
@@ -0,0 +1,128 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import io.v.v23.context.VContext;
+import io.v.v23.security.access.Permissions;
+import io.v.syncbase.v23.services.syncbase.util.AccessController;
+import io.v.v23.verror.VException;
+
+/**
+ * A database interface, which is logically a collection of {@link Table}s.
+ */
+public interface Database extends DatabaseCore, AccessController {
+    /**
+     * Returns {@code true} iff this database exists and the user has sufficient
+     * permissions to access it.
+     *
+     * @param  ctx        Vanadium context
+     * @return            {@code true} iff this database exists and the user has sufficient
+     *                    permissions to access it
+     * @throws VException if the database's existence couldn't be determined
+     */
+    boolean exists(VContext ctx) throws VException;
+
+    /**
+     * Creates this database.
+     *
+     * @param  ctx        Vanadium context
+     * @param  perms      database permissions; if {@code null},
+     *                    {@link io.v.syncbase.v23.services.syncbase.SyncbaseApp}'s
+     *                    permissions are used
+     * @throws VException if the database couldn't be created
+     */
+    void create(VContext ctx, Permissions perms) throws VException;
+
+    /**
+     * Deletes this database.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the database couldn't be deleted
+     */
+    void delete(VContext ctx) throws VException;
+
+    /**
+     * Creates the table with the given name.
+     *
+     * @param  ctx           Vanadium context
+     * @param  relativeName  relative name of the table; must not contain {@code /}
+     * @param  perms         table permissions; if {@code null}, {@link Database}'s
+     *                       permissions are used
+     * @throws VException    if the table couldn't be created
+     */
+    void createTable(VContext ctx, String relativeName, Permissions perms) throws VException;
+
+    /**
+     * Deletes the table with the given name.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the table couldn't be deleted
+     */
+    void deleteTable(VContext ctx) throws VException;
+
+    /**
+     * Creates a new "batch", i.e., a handle to a set of reads and writes to the database that
+     * should be considered an atomic unit.  Instead of calling this function directly, clients are
+     * encouraged to use the {@link NoSql#runInBatch NoSql.runInBatch()} helper function, which
+     * detects "concurrent batch" errors and handles retries internally.
+     * <p>
+     * Default concurrency semantics are as follows:
+     * <ul>
+     *   <li> Reads (e.g. {@code get}s, {@code scan}s) inside a batch operate over a consistent
+     *   snapshot taken during {@link #beginBatch beginBatch()}, and will see the effects of prior
+     *   writes performed inside the batch.</li>
+     *   <li> {@link BatchDatabase#commit commit()} may fail with {@link Errors#CONCURRENT_BATCH},
+     *   indicating that after {@link #beginBatch beginBatch()} but before
+     *   {@link BatchDatabase#commit commit()}, some concurrent routine wrote to a key that matches
+     *   a key or row-range read inside this batch.</li>
+     *   <li> Other methods will never fail with error {@link Errors#CONCURRENT_BATCH}, even if it
+     *   is known that {@link BatchDatabase#commit commit()} will fail with this
+     *   error.</li>
+     * </ul>
+     * <p>
+     * Once a batch has been committed or aborted, subsequent method calls will
+     * fail with no effect.
+     * <p>
+     * Concurrency semantics can be configured using BatchOptions.
+     *
+     * @param  ctx        Vanadium context
+     * @param  opts       batch options
+     * @return            a handle to a set of reads and writes to the database that should be
+     *                    considered an atomic unit
+     * @throws VException if the batch couldn't be created
+     */
+    BatchDatabase beginBatch(VContext ctx, BatchOptions opts) throws VException;
+
+    /**
+     * Returns a handle to a database {@link SyncGroup} with the given name.
+     *
+     * @param  name name of the synchronization group
+     */
+    SyncGroup getSyncGroup(String name);
+
+    /**
+     * Returns the global names of all {@link SyncGroup}s attached to this database.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the sync group names couldn't be retrieved
+     */
+    String[] listSyncGroupNames(VContext ctx) throws VException;
+
+    /**
+     * Compares the current schema version of the database with the schema version provided while
+     * creating this database handle.  If the current database schema version is lower, then the
+     * {@link SchemaUpgrader} associated with the schema is called. If {@link SchemaUpgrader} is
+     * successful, this method stores the new schema metadata in database.
+     * <p>
+     * Note: this database handle may have been created with a {@code null} schema, in which case
+     * this method skips schema check and the caller is responsible for maintaining schema sanity.
+     *
+     * @param  ctx        Vanadium context
+     * @return            {@code true} iff the database schema had to be upgraded, i.e., if the
+     *                    current database schema version was lower than the schema version with
+     *                    which the database was created
+     * @throws VException if there was an error upgrading the schema
+     */
+    boolean upgradeIfOutdated(VContext ctx) throws VException;
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/DatabaseCore.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/DatabaseCore.java
new file mode 100644
index 0000000..bf11ea6
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/DatabaseCore.java
@@ -0,0 +1,58 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import io.v.v23.context.VContext;
+import io.v.v23.verror.VException;
+
+/**
+ * Base interface for {@link Database} and {@link BatchDatabase}, allowing clients to pass the
+ * handle to helper methods that are batch-agnostic.
+ */
+public interface DatabaseCore {
+    /**
+     * Returns the relative name of the database.
+     */
+    String name();
+
+    /**
+     * Returns the full (i.e., object) name of the database.
+     */
+    String fullName();
+
+    /**
+     * Returns the table with the given name.
+     * <p>
+     * Note that this table may not yet exist and can be created using the {@link Table#create}
+     * method.)
+     *
+     * @param  relativeName name of the table; must not contain slashes
+     */
+    Table getTable(String relativeName);
+
+    /**
+     * Returns a list of all table names.
+     *
+     * @param  ctx        Vanadium context
+     * @return            a list of all table names
+     * @throws VException if the list of table names couldn't be retrieved
+     */
+    String[] listTables(VContext ctx) throws VException;
+
+    /**
+     * Executes a SyncQL query, returning a {@link ResultStream} object that allows the caller to
+     * iterate over arrays of values for each row that matches the query.
+     * <p>
+     * It is legal to perform writes concurrently with {@link #exec exec()}. The returned stream reads
+     * from a consistent snapshot taken at the time of the method and will not reflect subsequent
+     * writes to keys not yet reached by the stream.
+     *
+     * @param  ctx        Vanadium context
+     * @param  query      a SyncQL query
+     * @return            a {@link ResultStream} object that allows the caller to iterate over
+     *                    arrays of values for each row that matches the query
+     * @throws VException if there was an error executing the query
+     */
+    ResultStream exec(VContext ctx, String query) throws VException;
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/NoSql.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/NoSql.java
new file mode 100644
index 0000000..5fd7c62
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/NoSql.java
@@ -0,0 +1,82 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import io.v.v23.context.VContext;
+import io.v.v23.verror.VException;
+
+/**
+ * Various utility methods for the NoSql database.
+ */
+public class NoSql {
+    /**
+     * Creates a {@link RowRange} representing a single row.
+     */
+    public static RowRange singleRowRange(String row) {
+        return new RowRangeImpl(row);
+    }
+
+    /**
+     * Creates a {@link RowRange} represented by the provided {@code [start, limit)} parameters.
+     */
+    public static RowRange range(String start, String limit) {
+        return new RowRangeImpl(start, limit);
+    }
+
+    /**
+     * Creates {@link PrefixRange} with the provided prefix.
+     */
+    public static PrefixRange prefix(String prefix) {
+        return new PrefixRangeImpl(prefix);
+    }
+
+    /**
+     * Interface for a batch operation that is executed as part of {@link #runInBatch runInBatch()}.
+     */
+    public static interface BatchOperation {
+        /**
+         * Performs the batch operation.
+         *
+         * @param  db         batch database on which the operation is performed
+         * @throws VException if there was an error performing the operation;  if thrown, the
+         *                    batch operation is aborted
+         */
+        void run(BatchDatabase db) throws VException;
+    }
+
+    /**
+     * Runs the given batch operation, managing retries and
+     * {@link BatchDatabase#commit commit()}/{@link BatchDatabase#abort abort()}s.
+     *
+     * @param  ctx        Vanadium context
+     * @param  db         database on which the batch operation is to be performed
+     * @param  opts       batch configuration
+     * @param  op         batch operation
+     * @throws VException if there was an error executing the given batch operation
+     */
+    public static void runInBatch(VContext ctx, Database db, BatchOptions opts, BatchOperation op)
+            throws VException {
+        for (int i = 0; i < 3; ++i) {
+            BatchDatabase batch = db.beginBatch(ctx, opts);
+            try {
+                op.run(batch);
+            } catch (VException e) {
+                batch.abort(ctx);
+                throw e;
+            }
+            try {
+                batch.commit(ctx);
+                return;
+            } catch (VException e) {
+                if (!e.getID().equals(Errors.CONCURRENT_BATCH)) {
+                    throw e;
+                }
+            }
+        }
+        throw Errors.newConcurrentBatch(ctx);
+    }
+
+    private NoSql() {}
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/PrefixRange.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/PrefixRange.java
new file mode 100644
index 0000000..d550ab4
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/PrefixRange.java
@@ -0,0 +1,15 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+/**
+ * Represents all rows with keys that have some prefix.
+ */
+public interface PrefixRange extends RowRange {
+    /**
+     * Returns the prefix shared by all the keys in the range.
+     */
+    String prefix();
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/PrefixRangeImpl.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/PrefixRangeImpl.java
new file mode 100644
index 0000000..00e5170
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/PrefixRangeImpl.java
@@ -0,0 +1,30 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import io.v.syncbase.v23.services.syncbase.util.Util;
+
+class PrefixRangeImpl implements PrefixRange {
+    private final String prefix;
+
+    PrefixRangeImpl(String prefix) {
+        this.prefix = prefix;
+    }
+
+    @Override
+    public String start() {
+        return Util.prefixRangeStart(prefix);
+    }
+
+    @Override
+    public String limit() {
+        return Util.prefixRangeLimit(prefix);
+    }
+
+    @Override
+    public String prefix() {
+        return this.prefix;
+    }
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/ResultStream.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/ResultStream.java
new file mode 100644
index 0000000..8ff2771
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/ResultStream.java
@@ -0,0 +1,29 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import io.v.v23.vdl.VdlValue;
+import io.v.v23.verror.VException;
+
+/**
+ * An interface for iterating through rows resulting from a
+ * {@link DatabaseCore#exec DatabaseCore.exec()}.
+ */
+public interface ResultStream extends Stream {
+    /**
+     * Returns an array of column names that matched the query.  The number of values returned
+     * by each call to {@link #result} will match the size of this array.
+     */
+    String[] columnNames();
+
+    /**
+     * Returns the result that was staged by {@link #advance}.
+     * <p>
+     * This method does not block.
+     *
+     * @throws VException if the value could not be decoded or if {@link #advance} returned
+     *                    {@code false} or was not called at all
+     */
+    VdlValue[] result() throws VException;
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Row.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Row.java
new file mode 100644
index 0000000..ba953bd
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Row.java
@@ -0,0 +1,56 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import io.v.v23.context.VContext;
+import io.v.v23.verror.VException;
+
+/**
+ * A handle for a single row in a {@link Table}.
+ */
+public interface Row {
+    /**
+     * Returns the primary key for this row.
+     */
+    String key();
+
+    /**
+     * Returns the full (i.e., object) name of this row.
+     */
+    String fullName();
+
+    /**
+     * Returns {@code true} iff this row exists and the caller has permissions
+     * to access it.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the row's existence couldn't be determined
+     */
+    boolean exists(VContext ctx) throws VException;
+
+    /**
+     * Deletes this row.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the row couldn't be deleted
+     */
+    void delete(VContext ctx) throws VException;
+
+    /**
+     * Returns the value for this row.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the value couldn't be retrieved
+     */
+    Object get(VContext ctx) throws VException;
+
+    /**
+     * Writes the given value for this row.
+     *
+     * @param  ctx        Vanadium context
+     * @param  value      value to write
+     * @throws VException if the value couldn't be written
+     */
+    void put(VContext ctx, Object value) throws VException;
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/RowRange.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/RowRange.java
new file mode 100644
index 0000000..e1f9503
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/RowRange.java
@@ -0,0 +1,21 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+/**
+ * Represents all rows with keys in {@code [start, limit)}.  If limit is {@code ""}, all rows with
+ * keys &ge; {@code start} are included.
+ */
+public interface RowRange {
+    /**
+     * Returns the key that marks the start of the row range.
+     */
+    public String start();
+
+    /**
+     * Returns the key that marks the limit of the row range.
+     */
+    public String limit();
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/RowRangeImpl.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/RowRangeImpl.java
new file mode 100644
index 0000000..7363fc1
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/RowRangeImpl.java
@@ -0,0 +1,24 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+class RowRangeImpl implements RowRange {
+    private final String start, limit;
+
+    RowRangeImpl(String start, String limit) {
+        this.start = start;
+        this.limit = limit;
+    }
+
+    RowRangeImpl(String row) {
+        this.start = this.limit = row;
+    }
+
+    @Override
+    public String start() { return this.start; }
+
+    @Override
+    public String limit() { return this.limit; }
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/ScanStream.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/ScanStream.java
new file mode 100644
index 0000000..54bcee8
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/ScanStream.java
@@ -0,0 +1,31 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import io.v.v23.verror.VException;
+
+/**
+ * An interface for iterating through a collection of key/value pairs (obtained via
+ * {@link Table#scan Table.scan()}).
+ */
+public interface ScanStream extends Stream {
+    /**
+     * Returns the key of the element that was staged by {@link #advance}.
+     * <p>
+     * This method does not block.
+     *
+     * @throws VException  if {@link #advance} returned {@code false} or was not called at all
+     */
+    String key() throws VException;
+
+    /**
+     * Returns the value of the element that was staged by {@link #advance}.
+     * <p>
+     * This method does not block.
+     *
+     * @throws VException if the value could not be decoded or if {@link #advance} returned
+     *                    {@code false} or was not called at all
+     */
+    Object value() throws VException;
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Schema.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Schema.java
new file mode 100644
index 0000000..9c036c7
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Schema.java
@@ -0,0 +1,41 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+/**
+ * A database schema.
+ * <p>
+ * Each database has a schema associated with it which defines the current version of the
+ * database.  When a new version of an app wishes to change its data in a way that it is not
+ * compatible with the old app's data, this app must change the schema version and provide relevant
+ * upgrade logic in the specified {@link SchemaUpgrader}. The conflict resolution rules are also
+ * associated with the schema version. Hence if the conflict resolution rules change then the schema
+ * version also must be bumped.
+ */
+public class Schema {
+    private final SchemaMetadata metadata;
+    private final SchemaUpgrader upgrader;
+
+    /**
+     * Creates a new database schema with the specified metadata and schema upgrader.
+     * <p>
+     * Note: {@link SchemaUpgrader} is purely local and is not persisted.
+     */
+    public Schema(SchemaMetadata metadata, SchemaUpgrader upgrader) {
+        this.metadata = metadata;
+        this.upgrader = upgrader;
+    }
+
+    /**
+     * Returns the metadata related to this schema.
+     */
+    public SchemaMetadata metadata() { return this.metadata; }
+
+    /**
+     * Returns the upgrade logic used for upgrading the schema when an app's schema version differs
+     * from the database's schema version.
+     */
+    public SchemaUpgrader upgrader() { return this.upgrader; }
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/SchemaUpgrader.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/SchemaUpgrader.java
new file mode 100644
index 0000000..72e45af
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/SchemaUpgrader.java
@@ -0,0 +1,25 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import io.v.v23.verror.VException;
+
+/**
+ * An interface that must be implemented by an app in order to upgrade the database schema from
+ * a lower version to a higher version.
+ */
+public interface SchemaUpgrader {
+    /**
+     * Updgrades database from an old to the new schema version.
+     * <p>
+     * This method must be idempotent.
+     *
+     * @param  db         database to be upgraded
+     * @param  oldVersion old schema version
+     * @param  newVersion new schema version
+     * @throws VException if the database couldn't be upgraded
+     */
+    void run(Database db, int oldVersion, int newVersion) throws VException;
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Stream.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Stream.java
new file mode 100644
index 0000000..15aa392
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Stream.java
@@ -0,0 +1,42 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import io.v.v23.verror.VException;
+
+/**
+ * An interface for iterating through a collection of elements.
+ */
+public interface Stream {
+    /**
+     * Stages an element so that the client can retrieve it.
+     * <p>
+     * Returns {@code true} iff there is an element to retrieve.
+     * <p>
+     * The client must call {@link #advance} before retrieving the element.
+     * <p>
+     * The client must call {@link #cancel} if it does not iterate through all elements
+     * (i.e. until {@link #advance} returns {@code false}).
+     * <p>
+     * This method may block if an element is not immediately available.
+     *
+     * @return             {@code true} iff there is an element to retrieve
+     * @throws VException  if there was an error advancing the stream
+     */
+    boolean advance() throws VException;
+
+    /**
+     * Notifies the stream provider that it can stop producing elements.  The client must call
+     * {@link #cancel} if it does not iterate through all elements (i.e. until {@link #advance})
+     * returns {@code false}).
+     * <p>
+     * This method is idempotent and can be called concurrently with a thread that is
+     * iterating via {@link #advance}.
+     * <p>
+     * This method causes {@link #advance} to subsequently return {@code false}.
+     * <p>
+     * This method does not block.
+     */
+    void cancel();
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/SyncGroup.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/SyncGroup.java
new file mode 100644
index 0000000..7c3071a
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/SyncGroup.java
@@ -0,0 +1,144 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import java.util.Map;
+
+import io.v.v23.context.VContext;
+import io.v.v23.verror.VException;
+
+/**
+ * A handle for a {@link Database} synchronization group.
+ */
+public interface SyncGroup {
+    /**
+     * Returns {@code true} iff this sync group exists and the caller has at least {@code read}
+     * permissions on it.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the sync group's existence couldn't be determined
+     */
+    boolean exists(VContext ctx) throws VException;
+
+    /**
+     * Creates a new sync group with the given spec.  Requires:
+     * <p>
+     * <ul>
+     *     <li> client must have at least {@code read} access on the database,</li>
+     *     <li> prefix ACL must exist at each sync group prefix,</li>
+     *     <li> client must have at least {@code read} access on each of these prefix ACLs.</li>
+     * </ul>
+     *
+     * @param  ctx        Vanadium context
+     * @param  spec       sync group specification
+     * @param  info       creator's membership information
+     * @throws VException if the sync group couldn't be created
+     */
+    void create(VContext ctx, SyncGroupSpec spec, SyncGroupMemberInfo info) throws VException;
+
+    /**
+     * Joins a sync group.  Requires:
+     * <p>
+     * <ul>
+     *     <li> client must have at least {@code read} access on the database and on the sync group
+     *          ACLs.</li>
+     * </ul>
+     *
+     * @param  ctx        Vanadium context
+     * @param  info       joiner's membership information
+     * @return            sync group specification
+     * @throws VException if the sync group couldn't be joined
+     */
+    SyncGroupSpec join(VContext ctx, SyncGroupMemberInfo info) throws VException;
+
+    /**
+     * Leaves the sync group.  Previously synced data will continue to be available.  Requires:
+     * <p>
+     * <ul>
+     *     <li> client must have at least {@code read} access on the database.</li>
+     * </ul>
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the sync group couldn't be left
+     */
+    void leave(VContext ctx) throws VException;
+
+    /**
+     * Destroys the sync group.  Previously synced data will continue to be available to all
+     * members.  Requires:
+     * <p>
+     * <ul>
+     *     <li> client must have at least {@code read} access on the database and an {@code admin}
+     *          access on the sync group ACLs.</li>
+     * </ul>
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the sync group couldn't be destroyed
+     */
+    void destroy(VContext ctx) throws VException;
+
+    /**
+     * Ejects a member from the sync group.  The ejected member will not be able to sync further,
+     * but will retain any data it has already synced.  Requires:
+     * <p>
+     * <ul>
+     *     <li> client must have at least {@code read} access on the database and an {@code admin}
+     *          access on the sync group ACLs.</li>
+     * </ul>
+     *
+     * @param  ctx        Vanadium context
+     * @param  member     member to be ejected
+     * @throws VException if the member couldn't be ejected
+     */
+    void eject(VContext ctx, String member) throws VException;
+
+    /**
+     * Returns the sync group specification, along with its version string.  The version number
+     * allows for atomic read-modify-write of the specification (see {@link #setSpec setSpec()}).
+     * Requires:
+     * <p>
+     * <ul>
+     *     <li> client must have at least {@code read} access on the database and on the sync group
+     *          ACLs.</li>
+     * </ul>
+     *
+     * @param  ctx        Vanadium context
+     * @return            sync group specification along with its version number.  The returned
+     *                    map is guaranteed to be non-{@code null} and contain exactly one member
+     * @throws VException if the sync group specification couldn't be retrieved
+     */
+    Map<String, SyncGroupSpec> getSpec(VContext ctx) throws VException;
+
+    /**
+     * Sets the sync group specification.  The version string may be either empty or the value of
+     * the previous {@link #getSpec getSpec()}, in which case {@link #setSpec setSpec()} will only
+     * succeed iff the current version matches the specified one.
+     * <p>
+     * <ul>
+     *     <li> client must have at least {@code read} access on the database and an {@code admin}
+     *          access on the sync group ACLs.</li>
+     * </ul>
+     *
+     * @param  ctx        Vanadium context
+     * @param  spec       the new sync group specification
+     * @param  version    expected version string of the sync group;  if non-empty, this
+     *                    method will only succeed if the current sync group's version matches
+     *                    this value
+     * @throws VException if the sync group specification couldn't be set
+     */
+    void setSpec(VContext ctx, SyncGroupSpec spec, String version) throws VException;
+
+    /**
+     * Returns the information on membership of the sync group.  Requires:
+     * <p>
+     * <ul>
+     *     <li> client must have at least {@code read} access on the database and on the sync group
+     *          ACLs.</li>
+     * </ul>
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException [description]
+     */
+    Map<String, SyncGroupMemberInfo> getMembers(VContext ctx) throws VException;
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Table.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Table.java
new file mode 100644
index 0000000..ee89215
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/nosql/Table.java
@@ -0,0 +1,131 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.v23.services.syncbase.nosql;
+
+import io.v.v23.context.VContext;
+import io.v.v23.security.access.Permissions;
+import io.v.v23.verror.VException;
+
+/**
+ * Interface for a database table, i.e., a collection of {@link Row}s.
+ */
+public interface Table {
+    /**
+     * Returns the relative name of this table.
+     */
+    String name();
+
+    /**
+     * Returns the full (i.e., object) name of this table.
+     */
+    String fullName();
+
+    /**
+     * Returns {@code true} iff this table exists and the caller has sufficient permissions
+     * to access it.
+     *
+     * @param  ctx        Vanadium context
+     * @throws VException if the table's existence couldn't be determined
+     */
+    boolean exists(VContext ctx) throws VException;
+
+    /**
+     * Returns the row with the given primary key.
+     *
+     * @param  key primary key of the row
+     */
+    Row getRow(String key);
+
+    /**
+     * Returns the value for the given primary key.
+     *
+     * @param  ctx        Vanadium context
+     * @param  key        the primary key for a row
+     * @throws VException if the value couldn't be retrieved or if its class doesn't match the
+     *                    provided class
+     */
+    Object get(VContext ctx, String key) throws VException;
+
+    /**
+     * Writes the value to the table under the provided primary key.
+     *
+     * @param  ctx        Vanadium context
+     * @param  key        primary key under which the value is to be written
+     * @param  value      value to be written
+     * @throws VException if the value couldn't be written
+     */
+    void put(VContext ctx, String key, Object value) throws VException;
+
+    /**
+     * Deletes all rows in the given half-open range {@code [start, limit)}. If {@code limit} is
+     * {@code ""}, all rows with keys &ge; {@code start} are deleted.
+     *
+     * @param  ctx        Vanadium context
+     * @param  range      range of rows to be deleted
+     * @throws VException if the rows couldn't be deleted
+     */
+    void delete(VContext ctx, RowRange range) throws VException;
+
+    /**
+     * Returns all rows in the given half-open range {@code [start, limit)}. If {@code limit}
+     * is {@code ""}, all rows with keys &ge; {@code start} are included.
+     * <p>
+     * It is legal to perform writes concurrently with {@link #scan scan()}. The returned stream
+     * reads from a consistent snapshot taken at the time of the method and will not reflect
+     * subsequent writes to keys not yet reached by the stream.
+     *
+     * See helpers {@link NoSql#prefix NoSql.prefix()}, {@link NoSql#range NoSql.range()},
+     * and {@link NoSql#singleRowRange NoSql.singleRowRange()}.
+     *
+     * @param  ctx   Vanadium context
+     * @param  range range of rows to be read
+     * @return       a {@link ScanStream} used for iterating over the snapshot of the provided rows
+     */
+    ScanStream scan(VContext ctx, RowRange range);
+
+    /**
+     * Returns an array of {@link PrefixPermissions} (i.e., {@code (prefix, perms)} pairs) for
+     * the row with the given key.
+     * <p>
+     * The array is sorted from longest prefix to shortest, so element zero is the one that
+     * applies to the row with the given key. The last element is always the prefix {@code ""}
+     * which represents the table's permissions -- the array will always have at least one element.
+     *
+     * @param  ctx        Vanadium context
+     * @param  key        key of the row whose permission prefixes are to be retrieved
+     * @return            an array of prefix permissions for the given row
+     * @throws VException if the prefix permissions couldn't be retrieved
+     */
+    PrefixPermissions[] getPermissions(VContext ctx, String key) throws VException;
+
+    /**
+     * Sets the permissions for all current and future rows with the given prefix. If the prefix
+     * overlaps with an existing prefix, the longest prefix that matches a row applies.
+     * For example:
+     * <p><blockquote><pre>
+     *     setPermissions(ctx, NoSql.prefix("a/b"), perms1);
+     *     setPermissions(ctx, NoSql.prefix("a/b/c"), perms2);
+     * </pre></blockquote><p>
+     * The permissions for row {@code "a/b/1"} are {@code perms1}, and the permissions for row
+     * {@code "a/b/c/1"} are {@code perms2}.
+     *
+     * @param  ctx        Vanadium context
+     * @param  prefix     prefix to which to apply the new permissions
+     * @param  perms      permissions to apply
+     * @throws VException if the permissions couldn't be applied
+     */
+    void setPermissions(VContext ctx, PrefixRange prefix, Permissions perms) throws VException;
+
+    /**
+     * Deletes permissions for the specified prefix.  Any rows covered by this prefix will use the
+     * next longest prefix's permissions.  (See the array returned by
+     * {@link #getPermissions getPermissions()}).
+     *
+     * @param  ctx        Vanadium context
+     * @param  prefix     prefix for which the permissions are to be deleted
+     * @throws VException if the permissions couldn't be deleted
+     */
+    void deletePermissions(VContext ctx, PrefixRange prefix) throws VException;
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/util/AccessController.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/util/AccessController.java
new file mode 100644
index 0000000..2bb241b
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/util/AccessController.java
@@ -0,0 +1,44 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package io.v.syncbase.v23.services.syncbase.util;
+
+import java.util.Map;
+
+import io.v.v23.context.VContext;
+import io.v.v23.security.access.Permissions;
+import io.v.v23.verror.VException;
+
+/**
+ * Provides access control for various syncbase objects.
+ */
+public interface AccessController {
+    /**
+     * Replaces the current permissions for an object.
+     * <p>
+     * For a detailed documentation, see
+     * {@link io.v.v23.services.permissions.ObjectClient#setPermissions}.
+     *
+     * @param  ctx        Vanadium context
+     * @param  perms      new permissions for the object
+     * @param  version    object's permissions version, which allows for optional, optimistic
+     *                    concurrency control.  If non-empty, this value must come from
+     *                    {@link #getPermissions getPermissions()}.  If empty,
+     *                    {@link #setPermissions setPermissions()} performs an unconditional update.
+     * @throws VException if the object permissions couldn't be updated
+     */
+    void setPermissions(VContext ctx, Permissions perms, String version) throws VException;
+
+    /**
+     * Returns the current permissions for an object.
+     * <p>
+     * For detailed documentation, see
+     * {@link io.v.v23.services.permissions.ObjectClient#getPermissions}.
+     *
+     * @param  ctx        Vanadium context
+     * @return            object permissions along with its version number.  The returned map
+     *                    is guaranteed to be non-{@code null} and contain exactly one member.
+     * @throws VException if the object permissions couldn't be retrieved
+     */
+    Map<String, Permissions> getPermissions(VContext ctx) throws VException;
+}
\ No newline at end of file
diff --git a/lib/src/main/java/io/v/syncbase/v23/services/syncbase/util/Util.java b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/util/Util.java
new file mode 100644
index 0000000..2d5cf07
--- /dev/null
+++ b/lib/src/main/java/io/v/syncbase/v23/services/syncbase/util/Util.java
@@ -0,0 +1,43 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.v23.services.syncbase.util;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Various syncbase utility methods.
+ */
+public class Util {
+    /**
+     * Returns the start of the row range for the given prefix.
+     */
+    public static String prefixRangeStart(String prefix) {
+        return prefix;
+    }
+
+    /**
+     * Returns the limit of the row range for the given prefix.
+     */
+    public static String prefixRangeLimit(String prefix) {
+        // We convert a string to a byte[] array, which can be thought of as a base-256
+        // number.  The code below effectively adds 1 to this number, then chops off any
+        // trailing 0x00 bytes. If the input string consists entirely of 0xFF, an empty string
+        // will be returned.
+        try {
+            byte[] bytes = prefix.getBytes("ISO8859-1");
+            int last = bytes.length - 1;
+            for (; last >= 0 && bytes[last] == (byte) 0xFF; --last);
+            if (last < 0) {
+                return "";
+            }
+            bytes[last] += 1;
+            return new String(bytes, 0, last + 1, "ISO8859-1");
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("JVM must support ISO8859-1 char encoding", e);
+        }
+    }
+
+    private Util() {}
+}
diff --git a/lib/src/test/java/io/v/syncbase/v23/services/syncbase/util/UtilTest.java b/lib/src/test/java/io/v/syncbase/v23/services/syncbase/util/UtilTest.java
new file mode 100644
index 0000000..c5a2aeb
--- /dev/null
+++ b/lib/src/test/java/io/v/syncbase/v23/services/syncbase/util/UtilTest.java
@@ -0,0 +1,46 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.v23.services.syncbase.util;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assert_;
+
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the various {@link Util} methods.
+ */
+public class UtilTest extends TestCase {
+    public void testPrefixRange() {
+        List<String[]> tests = Arrays.asList(new String[][] {
+                {"", "", ""},
+                {"a", "a", "b"},
+                {"aa", "aa", "ab"},
+                {"\u00fe", "\u00fe", "\u00ff"},
+                {"a\u00fe", "a\u00fe", "a\u00ff"},
+                {"aa\u00fe", "aa\u00fe", "aa\u00ff"},
+                {"a\u00ff", "a\u00ff", "b"},
+                {"aa\u00ff", "aa\u00ff", "ab"},
+                {"a\u00ff\u00ff", "a\u00ff\u00ff", "b"},
+                {"aa\u00ff\u00ff", "aa\u00ff\u00ff", "ab"},
+                {"\u00ff", "\u00ff", ""},
+                {"\u00ff\u00ff", "\u00ff\u00ff", ""}
+        });
+        for (String[] test : tests) {
+            String prefix = test[0];
+            String start = test[1];
+            String limit = test[2];
+            String actualStart = Util.prefixRangeStart(prefix);
+            String actualLimit = Util.prefixRangeLimit(prefix);
+            assert_().withFailureMessage("Failed for prefix: " + prefix)
+                    .that(actualStart).isEqualTo(start);
+            assert_().withFailureMessage("Failed for prefix: " + prefix)
+                    .that(actualLimit).isEqualTo(limit);
+        }
+    }
+}