Index: src/main/java/de/mpiwg/itgroup/annotations/Actor.java
===================================================================
--- src/main/java/de/mpiwg/itgroup/annotations/Actor.java	(revision 15)
+++ src/main/java/de/mpiwg/itgroup/annotations/Actor.java	(revision 16)
@@ -32,7 +32,6 @@
     public boolean isEquivalentWith(Person person, AnnotationStore store) {
         if (person == null) return false;
-        if (person.equals(getIdString())) {
-            return true;
-        }
+        if (person.equals(this)) return true;
+        if (person.getIdString().equals(this.getIdString())) return true;
         if (isGroup() && store != null) {
             // check if person in group
Index: src/main/java/de/mpiwg/itgroup/annotations/Annotation.java
===================================================================
--- src/main/java/de/mpiwg/itgroup/annotations/Annotation.java	(revision 15)
+++ src/main/java/de/mpiwg/itgroup/annotations/Annotation.java	(revision 16)
@@ -3,4 +3,7 @@
  */
 package de.mpiwg.itgroup.annotations;
+
+import java.util.List;
+import java.util.Set;
 
 import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore;
@@ -82,5 +85,9 @@
      */
     protected Actor readPermission;
-    
+       
+    /**
+     * List of tags on this Annotation.
+     */
+    protected Set<String> tags;
     
     /**
@@ -318,4 +325,18 @@
         this.readPermission = readPermission;
     }
+
+    /**
+     * @return the tags
+     */
+    public Set<String> getTags() {
+        return tags;
+    }
+
+    /**
+     * @param tags the tags to set
+     */
+    public void setTags(Set<String> tags) {
+        this.tags = tags;
+    }
     
     
Index: src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java
===================================================================
--- src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java	(revision 15)
+++ src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java	(revision 16)
@@ -6,5 +6,7 @@
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.log4j.Logger;
@@ -35,5 +37,5 @@
 
     public static enum NodeTypes {
-        ANNOTATION, PERSON, TARGET, GROUP 
+        ANNOTATION, PERSON, TARGET, GROUP, TAG
     }
 
@@ -41,5 +43,5 @@
 
     public static enum RelationTypes implements RelationshipType {
-        ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ, MEMBER_OF
+        ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ, MEMBER_OF, HAS_TAG
     }
 
@@ -49,5 +51,5 @@
         super();
         this.graphDb = graphDb;
-        nodeIndexes = new ArrayList<Index<Node>>(3);
+        nodeIndexes = new ArrayList<Index<Node>>(5);
         // List.set(enum.ordinal(), val) seems not to work.
         nodeIndexes.add(NodeTypes.ANNOTATION.ordinal(), graphDb.index().forNodes("annotations"));
@@ -55,4 +57,5 @@
         nodeIndexes.add(NodeTypes.TARGET.ordinal(), graphDb.index().forNodes("targets"));
         nodeIndexes.add(NodeTypes.GROUP.ordinal(), graphDb.index().forNodes("groups"));
+        nodeIndexes.add(NodeTypes.TAG.ordinal(), graphDb.index().forNodes("tags"));
     }
 
@@ -71,5 +74,4 @@
     }
 
-    
     /**
      * Returns List of Groups the person is member of.
@@ -86,5 +88,5 @@
             // make sure we're getting a group
             if (!(group instanceof Group)) {
-                logger.error("target of MEMBER_OF is not GROUP! rel="+rel);
+                logger.error("target of MEMBER_OF is not GROUP! rel=" + rel);
                 continue;
             }
@@ -93,5 +95,5 @@
         return groups;
     }
-    
+
     /**
      * Returns if person with uri is in Group group.
@@ -104,5 +106,5 @@
         Node pn = getPersonNodeByUri(person.getUriString());
         if (pn == null) return false;
-        // optimised version of getGroupsForPersonNode
+        // optimized version of getGroupsForPersonNode
         Iterable<Relationship> rels = pn.getRelationships(RelationTypes.MEMBER_OF);
         for (Relationship rel : rels) {
@@ -114,5 +116,5 @@
         return false;
     }
-    
+
     /**
      * Returns the Annotation with the given id.
@@ -138,5 +140,7 @@
         annot.setBodyText((String) annotNode.getProperty("bodyText", null));
         annot.setBodyUri((String) annotNode.getProperty("bodyUri", null));
-        // get annotation target from relation
+        /*
+         * get annotation target from relation
+         */
         Relationship targetRel = getRelation(annotNode, RelationTypes.ANNOTATES, null);
         if (targetRel != null) {
@@ -151,5 +155,7 @@
             annot.setFragmentType(FragmentTypes.valueOf(ft));
         }
-        // get creator from relation
+        /*
+         * get creator from relation
+         */
         Relationship creatorRel = getRelation(annotNode, RelationTypes.CREATED, null);
         if (creatorRel != null) {
@@ -160,7 +166,11 @@
             logger.error("annotation " + annotNode + " has no creator node!");
         }
-        // get creation date
+        /*
+         * get creation date
+         */
         annot.setCreated((String) annotNode.getProperty("created", null));
-        // get permissions
+        /*
+         * get permissions
+         */
         Relationship adminRel = getRelation(annotNode, RelationTypes.PERMITS_ADMIN, null);
         if (adminRel != null) {
@@ -187,4 +197,15 @@
             annot.setReadPermission(read);
         }
+        /*
+         * get tags
+         */
+        Set<String> tags = new HashSet<String>();
+        for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
+            String tag = (String) rel.getEndNode().getProperty("name", null);
+            if (tag != null) {
+                tags.add(tag);
+            }
+        }
+        annot.setTags(tags);
 
         return annot;
@@ -286,4 +307,49 @@
             setPermissionRelation(annotNode, RelationTypes.PERMITS_READ, annot.getReadPermission());
 
+            /*
+             * Tags on this annotation.
+             */
+            Set<String> newTags = annot.getTags();
+            // we ignore existing tags if tags == null
+            if (newTags != null) {
+                List<Relationship> oldHasTags = new ArrayList<Relationship>();
+                for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
+                    oldHasTags.add(rel);
+                }
+                // adjust to new tags
+                if (newTags.isEmpty()) {
+                    // remove old tags
+                    if (!oldHasTags.isEmpty()) {
+                        for (Relationship rel : oldHasTags) {
+                            rel.delete();
+                            // TODO: should we delete orphan nodes too?
+                        }
+                    }
+                } else {
+                    if (!oldHasTags.isEmpty()) {
+                        // adjust old tags
+                        for (Relationship rel : oldHasTags) {
+                            String oldTag = (String) rel.getEndNode().getProperty("name", null);
+                            if (newTags.contains(oldTag)) {
+                                // tag exists
+                                newTags.remove(oldTag);
+                            } else {
+                                // tag exists no longer
+                                rel.delete();
+                                // TODO: should we delete orphan nodes too?
+                            }
+                        }
+                    }
+                    if (!newTags.isEmpty()) {
+                        // still tags to add
+                        for (String tag : newTags) {
+                            // create new tag
+                            Node tagNode = getOrCreateTagNode(tag);
+                            getOrCreateRelation(annotNode, RelationTypes.HAS_TAG, tagNode);
+                        }
+                    }
+
+                }
+            }
             tx.success();
         } finally {
@@ -371,7 +437,17 @@
             }
         }
+        // TODO: if both uri and user are given we should intersect
         return annotations;
     }
 
+    /**
+     * Returns Relationship of type from Node start to Node end. Creates one if
+     * it doesn't exist.
+     * 
+     * @param start
+     * @param type
+     * @param end
+     * @return
+     */
     protected Relationship getOrCreateRelation(Node start, RelationshipType type, Node end) {
         if (start.hasRelationship()) {
@@ -458,5 +534,5 @@
                     person.setProperty("TYPE", NodeTypes.GROUP.name());
                 } else {
-                    person.setProperty("TYPE", NodeTypes.PERSON.name());                    
+                    person.setProperty("TYPE", NodeTypes.PERSON.name());
                 }
                 person.setProperty("uri", uri);
@@ -474,4 +550,24 @@
         }
         return person;
+    }
+
+    protected Node getOrCreateTagNode(String tagname) {
+        Index<Node> idx = getNodeIndex(NodeTypes.TAG);
+        IndexHits<Node> tags = idx.get("name", tagname);
+        Node tag = tags.getSingle();
+        if (tag == null) {
+            // does not exist yet
+            Transaction tx = graphDb.beginTx();
+            try {
+                tag = graphDb.createNode();
+                tag.setProperty("TYPE", NodeTypes.TAG.name());
+                tag.setProperty("name", tagname);
+                idx.add(tag, "name", tagname);
+                tx.success();
+            } finally {
+                tx.finish();
+            }
+        }
+        return tag;
     }
 
@@ -533,6 +629,7 @@
     }
 
-    /** returns the (first) Relationship of RelationTypes type from Node start.
-     *  
+    /**
+     * returns the (first) Relationship of RelationTypes type from Node start.
+     * 
      * @param start
      * @param type
@@ -549,4 +646,5 @@
         }
         for (Relationship rel : rels) {
+            // just the first one
             return rel;
         }
Index: src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorAnnotations.java
===================================================================
--- src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorAnnotations.java	(revision 15)
+++ src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorAnnotations.java	(revision 16)
@@ -55,7 +55,8 @@
         logger.debug("request authenticated=" + authUser);
 
-        Annotation annot = getAnnotationStore().getAnnotationById(id);
+        AnnotationStore store = getAnnotationStore();
+        Annotation annot = store.getAnnotationById(id);
         if (annot != null) {
-            if (! annot.isActionAllowed("read", authUser, null)) {
+            if (! annot.isActionAllowed("read", authUser, store)) {
                 setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!");
                 return null;
@@ -160,5 +161,5 @@
                 return null;
             }
-            if (! storedAnnot.isActionAllowed("update", authUser, null)) {
+            if (! storedAnnot.isActionAllowed("update", authUser, store)) {
                 setStatus(Status.CLIENT_ERROR_FORBIDDEN);
                 return null;
@@ -207,7 +208,8 @@
         Person authUser = Person.createPersonWithId(this.checkAuthToken(entity));
         logger.debug("request authenticated=" + authUser);
-        Annotation annot = getAnnotationStore().getAnnotationById(id);
+        AnnotationStore store = getAnnotationStore();
+        Annotation annot = store.getAnnotationById(id);
         if (annot != null) {
-            if (! annot.isActionAllowed("delete", authUser, null)) {
+            if (! annot.isActionAllowed("delete", authUser, store)) {
                 setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!");
                 return null;
@@ -216,5 +218,5 @@
         
         // delete annotation
-        getAnnotationStore().deleteById(id);
+        store.deleteById(id);
         setStatus(Status.SUCCESS_NO_CONTENT);
         return null;
Index: src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorResourceImpl.java
===================================================================
--- src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorResourceImpl.java	(revision 15)
+++ src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorResourceImpl.java	(revision 16)
@@ -10,5 +10,7 @@
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -190,4 +192,7 @@
             jo.put("uri", annot.getTargetBaseUri());
 
+            /*
+             * user
+             */
             if (makeUserObject) {
                 // create user object
@@ -214,4 +219,7 @@
             }
 
+            /*
+             * ranges
+             */
             if (annot.getTargetFragment() != null) {
                 // we only look at the first xpointer
@@ -226,5 +234,7 @@
             }
             
-            // permissions
+            /*
+             * permissions
+             */
             JSONObject perms = new JSONObject();
             jo.put("permissions", perms);
@@ -267,4 +277,19 @@
             }
             
+            /*
+             * tags
+             */
+            Set<String> tagset = annot.getTags(); 
+            if (tagset != null) {
+                JSONArray tags = new JSONArray();
+                jo.put("tags", tags);
+                for (String tag : tagset) {
+                    tags.put(tag);
+                }
+            }
+            
+            /*
+             * id
+             */
             // encode Annotation URL (=id) in base64
             String annotUrl = annot.getUri();
@@ -413,13 +438,19 @@
     public Annotation updateAnnotation(Annotation annot, JSONObject jo, Representation entity) throws JSONException,
             UnsupportedEncodingException {
-        // target uri
+        /*
+         * target uri
+         */
         if (jo.has("uri")) {
             annot.setTargetBaseUri(jo.getString("uri"));
         }
-        // annotation text
+        /*
+         * annotation text
+         */
         if (jo.has("text")) {
             annot.setBodyText(jo.getString("text"));
         }
-        // check authentication
+        /*
+         * check authentication
+         */
         String authUser = checkAuthToken(entity);
         if (authUser == null) {
@@ -434,5 +465,7 @@
              */
         }
-        // get or create creator object
+        /*
+         * get or create creator object
+         */
         Actor creator = annot.getCreator();
         if (creator == null) {
@@ -483,5 +516,7 @@
             creator.setUri(userUri);
         }
-
+        /*
+         * creation date
+         */
         if (annot.getCreated() == null) {
             // set creation date
@@ -491,5 +526,7 @@
         }
 
-        // create xpointer from the first range/area
+        /*
+         * create xpointer from the first range/area
+         */
         if (jo.has("ranges")) {
             JSONObject ranges = jo.getJSONArray("ranges").getJSONObject(0);
@@ -505,5 +542,7 @@
         }
 
-        // permissions
+        /*
+         * permissions
+         */
         if (jo.has("permissions")) {
             JSONObject permissions = jo.getJSONObject("permissions");
@@ -530,4 +569,17 @@
         }
 
+        /*
+         * tags
+         */
+        if (jo.has("tags")) {
+            HashSet<String> tagset = new HashSet<String>();
+            JSONArray tags = jo.getJSONArray("tags");
+            for (int i = 0; i < tags.length(); ++i) {
+                tagset.add(tags.getString(i));
+            }
+            annot.setTags(tagset);
+        }
+
+        
         return annot;
     }
