Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java	(revision 0)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java	(revision 0)
@@ -0,0 +1,305 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index.reference;
+
+import static com.google.common.collect.Maps.newHashMap;
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.jackrabbit.oak.api.Type.REFERENCE;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+import static org.apache.jackrabbit.oak.api.Type.WEAKREFERENCE;
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
+import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.INTEGRITY;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.core.ImmutableRoot;
+import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * Index editor for keeping a references to a node up to date.
+ * 
+ */
+class ReferenceEditor implements Editor {
+
+    // TODO
+    // - what happens when you move a node? who updates the backlinks?
+    // - what to do with version storage nodes?
+    // - error messages
+
+    private final static String REF_NAME = ":references";
+    private final static String WEAK_REF_NAME = ":weakreferences";
+
+    /** Parent editor, or {@code null} if this is the root editor. */
+    private final ReferenceEditor parent;
+
+    /** Name of this node, or {@code null} for the root node. */
+    private final String name;
+
+    /** Path of this editor, built lazily in {@link #getPath()}. */
+    private String path;
+
+    /** The Id Manager, built lazily in {@link #getIdManager()}. */
+    private IdentifierManager idManager;
+
+    private final NodeBuilder builder;
+
+    /**
+     * <UUID, Set<paths-pointing-to-the-uuid>>
+     */
+    private final Map<String, Set<String>> newRefs;
+
+    /**
+     * <UUID, Set<paths-pointing-to-the-uuid>>
+     */
+    private final Map<String, Set<String>> rmRefs;
+
+    /**
+     * <UUID, Set<paths-pointing-to-the-uuid>>
+     */
+    private final Map<String, Set<String>> newWeakRefs;
+
+    /**
+     * <UUID, Set<paths-pointing-to-the-uuid>>
+     */
+    private final Map<String, Set<String>> rmWeakRefs;
+
+    public ReferenceEditor(NodeBuilder builder) {
+        this.parent = null;
+        this.name = null;
+        this.path = "/";
+        this.builder = builder;
+        this.newRefs = newHashMap();
+        this.rmRefs = newHashMap();
+        this.newWeakRefs = newHashMap();
+        this.rmWeakRefs = newHashMap();
+    }
+
+    private ReferenceEditor(ReferenceEditor parent, String name) {
+        this.parent = parent;
+        this.name = name;
+        this.path = null;
+        this.builder = parent.builder;
+        this.newRefs = parent.newRefs;
+        this.rmRefs = parent.rmRefs;
+        this.newWeakRefs = parent.newWeakRefs;
+        this.rmWeakRefs = parent.rmWeakRefs;
+    }
+
+    /**
+     * Returns the path of this node, building it lazily when first requested.
+     */
+    private String getPath() {
+        if (path == null) {
+            path = concat(parent.getPath(), name);
+        }
+        return path;
+    }
+
+    /**
+     * Returns the id manager, building it lazily when first requested.
+     */
+    private IdentifierManager getIdManager() {
+        if (idManager == null) {
+            if (parent != null) {
+                return parent.getIdManager();
+            }
+            this.idManager = new IdentifierManager(new ImmutableRoot(
+                    this.builder.getNodeState()));
+        }
+        return idManager;
+    }
+
+    @Override
+    public void enter(NodeState before, NodeState after)
+            throws CommitFailedException {
+    }
+
+    @Override
+    public void leave(NodeState before, NodeState after)
+            throws CommitFailedException {
+        if (parent == null) {
+            // local uuid-> nodebuilder cache
+            Map<String, NodeBuilder> builders = newHashMap();
+            for (Entry<String, Set<String>> ref : rmRefs.entrySet()) {
+                String uuid = ref.getKey();
+                NodeBuilder child = resolveUUID(uuid, builders);
+                if (child == null) {
+                    throw new CommitFailedException(INTEGRITY, 2,
+                            "Unable to resolve UUID " + uuid);
+                }
+                Set<String> rm = ref.getValue();
+                Set<String> add = newHashSet();
+                if (newRefs.containsKey(uuid)) {
+                    add = newRefs.remove(uuid);
+                }
+                set(child, REF_NAME, add, rm);
+            }
+            for (Entry<String, Set<String>> ref : newRefs.entrySet()) {
+                String uuid = ref.getKey();
+                NodeBuilder child = resolveUUID(uuid, builders);
+                if (child == null) {
+                    throw new CommitFailedException(INTEGRITY, 3,
+                            "Unable to resolve UUID " + uuid);
+                }
+                Set<String> add = ref.getValue();
+                Set<String> rm = newHashSet();
+                set(child, REF_NAME, add, rm);
+            }
+            for (Entry<String, Set<String>> ref : rmWeakRefs.entrySet()) {
+                String uuid = ref.getKey();
+                NodeBuilder child = resolveUUID(uuid, builders);
+                if (child == null) {
+                    throw new CommitFailedException(INTEGRITY, 4,
+                            "Unable to resolve UUID " + uuid);
+                }
+                Set<String> rm = ref.getValue();
+                Set<String> add = newHashSet();
+                if (newWeakRefs.containsKey(uuid)) {
+                    add = newWeakRefs.remove(uuid);
+                }
+                set(child, WEAK_REF_NAME, add, rm);
+            }
+            for (Entry<String, Set<String>> ref : newWeakRefs.entrySet()) {
+                String uuid = ref.getKey();
+                NodeBuilder child = resolveUUID(uuid, builders);
+                if (child == null) {
+                    throw new CommitFailedException(INTEGRITY, 5,
+                            "Unable to resolve UUID " + uuid);
+                }
+                Set<String> add = ref.getValue();
+                Set<String> rm = newHashSet();
+                set(child, WEAK_REF_NAME, add, rm);
+            }
+        }
+    }
+
+    private NodeBuilder resolveUUID(String uuid,
+            Map<String, NodeBuilder> builders) {
+        if (builders.containsKey(uuid)) {
+            return builders.get(uuid);
+        }
+        String path = getIdManager().resolveUUID(uuid);
+
+        if (path == null) {
+            return null;
+        }
+        NodeBuilder child = getChild(builder, path);
+        if (child != null) {
+            builders.put(uuid, child);
+        }
+        return child;
+    }
+
+    private static NodeBuilder getChild(NodeBuilder root, String path) {
+        NodeBuilder child = root;
+        for (String p : elements(path)) {
+            child = child.child(p);
+        }
+        return child;
+    }
+
+    private static void set(NodeBuilder child, String name, Set<String> add,
+            Set<String> rm) {
+        // TODO should we optimize for the remove/add case? intersect the
+        // sets, work on the diffs?
+
+        Set<String> vals;
+        PropertyState ref = child.getProperty(name);
+        if (ref != null) {
+            vals = newHashSet(ref.getValue(STRINGS));
+        } else {
+            vals = newHashSet();
+        }
+        vals.addAll(add);
+        vals.removeAll(rm);
+        if (!vals.isEmpty()) {
+            child.setProperty(name, vals, STRINGS);
+        } else {
+            child.removeProperty(name);
+        }
+    }
+
+    @Override
+    public void propertyAdded(PropertyState after) {
+        propertyChanged(null, after);
+    }
+
+    @Override
+    public void propertyChanged(PropertyState before, PropertyState after) {
+        if (before != null) {
+            if (before.getType() == REFERENCE) {
+                put(rmRefs, before.getValue(STRING), getPath());
+            }
+            if (before.getType() == WEAKREFERENCE) {
+                put(rmWeakRefs, before.getValue(STRING), getPath());
+            }
+        }
+        if (after != null) {
+            if (after.getType() == REFERENCE) {
+                put(newRefs, after.getValue(STRING), getPath());
+            }
+            if (after.getType() == WEAKREFERENCE) {
+                put(newWeakRefs, after.getValue(STRING), getPath());
+            }
+        }
+    }
+
+    private static void put(Map<String, Set<String>> map, String key,
+            String value) {
+        Set<String> values = map.get(key);
+        if (values == null) {
+            values = newHashSet();
+        }
+        values.add(value);
+        map.put(key, values);
+    }
+
+    @Override
+    public void propertyDeleted(PropertyState before) {
+        propertyChanged(before, null);
+    }
+
+    @Override
+    public Editor childNodeAdded(String name, NodeState after) {
+        return new ReferenceEditor(this, name);
+    }
+
+    @Override
+    public Editor childNodeChanged(String name, NodeState before,
+            NodeState after) {
+        return new ReferenceEditor(this, name);
+    }
+
+    @Override
+    public Editor childNodeDeleted(String name, NodeState before)
+            throws CommitFailedException {
+        if (before.hasProperty(REF_NAME)) {
+            throw new CommitFailedException(INTEGRITY, 1,
+                    "Unable to delete referenced node");
+        }
+        return new ReferenceEditor(this, name);
+    }
+}
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditorProvider.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditorProvider.java	(revision 0)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditorProvider.java	(revision 0)
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index.reference;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
+import org.apache.jackrabbit.oak.spi.commit.VisibleEditor;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+@Component
+@Service(EditorProvider.class)
+public class ReferenceEditorProvider implements EditorProvider {
+
+    @Override
+    public Editor getRootEditor(NodeState before, NodeState after,
+            NodeBuilder builder) throws CommitFailedException {
+        return new VisibleEditor(new ReferenceEditor(builder));
+    }
+
+}
Index: oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
===================================================================
--- oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java	(revision 1539334)
+++ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java	(working copy)
@@ -32,6 +32,7 @@
 import org.apache.jackrabbit.oak.plugins.index.nodetype.NodeTypeIndexProvider;
 import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
 import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexProvider;
+import org.apache.jackrabbit.oak.plugins.index.reference.ReferenceEditorProvider;
 import org.apache.jackrabbit.oak.plugins.name.NameValidatorProvider;
 import org.apache.jackrabbit.oak.plugins.name.NamespaceValidatorProvider;
 import org.apache.jackrabbit.oak.plugins.nodetype.RegistrationEditorProvider;
@@ -70,6 +71,7 @@
         with(new TypeEditorProvider());
         with(new RegistrationEditorProvider());
         with(new ConflictValidatorProvider());
+        with(new ReferenceEditorProvider());
 
         with(new PropertyIndexEditorProvider());
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java	(revision 1539334)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java	(working copy)
@@ -255,7 +255,7 @@
     }
 
     @CheckForNull
-    private String resolveUUID(String uuid) {
+    public String resolveUUID(String uuid) {
         return resolveUUID(StringPropertyState.stringProperty("", uuid));
     }
 
Index: oak-jcr/pom.xml
===================================================================
--- oak-jcr/pom.xml	(revision 1539334)
+++ oak-jcr/pom.xml	(working copy)
@@ -37,9 +37,7 @@
       org.apache.jackrabbit.test.api.SessionTest#testMoveConstraintViolationExceptionSrc               <!-- OAK-132 -->
       org.apache.jackrabbit.test.api.SessionTest#testMoveConstraintViolationExceptionDest              <!-- OAK-132 -->
       org.apache.jackrabbit.test.api.SessionTest#testMoveLockException
-      org.apache.jackrabbit.test.api.SessionUUIDTest#testSaveReferentialIntegrityException             <!-- OAK-66 -->
       org.apache.jackrabbit.test.api.NodeTest#testRemoveNodeParentLocked
-      org.apache.jackrabbit.test.api.NodeUUIDTest#testSaveReferentialIntegrityException                <!-- OAK-66 -->
       org.apache.jackrabbit.test.api.NodeSetPrimaryTypeTest#testLocked
       org.apache.jackrabbit.test.api.WorkspaceCopyVersionableTest#testCopyNodesVersionableAndCheckedIn <!-- OAK-118 -->
       org.apache.jackrabbit.test.api.WorkspaceMoveVersionableTest#testMoveNodesVersionableAndCheckedIn <!-- OAK-118 -->
