Working on model & description annotations.
diff --git a/core/api/src/main/java/org/onlab/onos/net/AbstractAnnotated.java b/core/api/src/main/java/org/onlab/onos/net/AbstractAnnotated.java
index f3c4b86..f29ad43 100644
--- a/core/api/src/main/java/org/onlab/onos/net/AbstractAnnotated.java
+++ b/core/api/src/main/java/org/onlab/onos/net/AbstractAnnotated.java
@@ -1,11 +1,5 @@
 package org.onlab.onos.net;
 
-import com.google.common.collect.ImmutableSet;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
 import static com.google.common.base.Preconditions.checkArgument;
 
 /**
@@ -13,13 +7,13 @@
  */
 public class AbstractAnnotated implements Annotated {
 
-    private static final Map<String, String> EMPTY = new HashMap<>();
+    private static final Annotations EMPTY = DefaultAnnotations.builder().build();
 
-    private final Map<String, String> annotations;
+    private final Annotations annotations;
 
     // For serialization
     protected AbstractAnnotated() {
-        this.annotations = EMPTY;
+        this.annotations = null;
     }
 
     /**
@@ -27,19 +21,14 @@
      *
      * @param annotations optional key/value annotations map
      */
-    protected AbstractAnnotated(Map<String, String>[] annotations) {
+    protected AbstractAnnotated(Annotations... annotations) {
         checkArgument(annotations.length <= 1, "Only one set of annotations is expected");
         this.annotations = annotations.length == 1 ? annotations[0] : EMPTY;
     }
 
     @Override
-    public Set<String> annotationKeys() {
-        return ImmutableSet.copyOf(annotations.keySet());
-    }
-
-    @Override
-    public String annotation(String key) {
-        return annotations.get(key);
+    public Annotations annotations() {
+        return annotations;
     }
 
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/AbstractDescription.java b/core/api/src/main/java/org/onlab/onos/net/AbstractDescription.java
new file mode 100644
index 0000000..5e5b117
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/AbstractDescription.java
@@ -0,0 +1,34 @@
+package org.onlab.onos.net;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Base implementation of an annotated model description.
+ */
+public class AbstractDescription implements Annotated {
+
+    private static final SparseAnnotations EMPTY = DefaultAnnotations.builder().build();
+
+    private final SparseAnnotations annotations;
+
+    // For serialization
+    protected AbstractDescription() {
+        this.annotations = null;
+    }
+
+    /**
+     * Creates a new entity, annotated with the specified annotations.
+     *
+     * @param annotations optional key/value annotations map
+     */
+    protected AbstractDescription(SparseAnnotations... annotations) {
+        checkArgument(annotations.length <= 1, "Only one set of annotations is expected");
+        this.annotations = annotations.length == 1 ? annotations[0] : EMPTY;
+    }
+
+    @Override
+    public SparseAnnotations annotations() {
+        return annotations;
+    }
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/AbstractElement.java b/core/api/src/main/java/org/onlab/onos/net/AbstractElement.java
index e810930..251d68a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/AbstractElement.java
+++ b/core/api/src/main/java/org/onlab/onos/net/AbstractElement.java
@@ -17,11 +17,13 @@
     /**
      * Creates a network element attributed to the specified provider.
      *
-     * @param providerId identity of the provider
-     * @param id         element identifier
+     * @param providerId  identity of the provider
+     * @param id          element identifier
+     * @param annotations optional key/value annotations
      */
-    protected AbstractElement(ProviderId providerId, ElementId id) {
-        super(providerId);
+    protected AbstractElement(ProviderId providerId, ElementId id,
+                              Annotations... annotations) {
+        super(providerId, annotations);
         this.id = id;
     }
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java b/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java
index 6bdda72..482ec68 100644
--- a/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java
+++ b/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java
@@ -2,8 +2,6 @@
 
 import org.onlab.onos.net.provider.ProviderId;
 
-import java.util.Map;
-
 /**
  * Base implementation of a network model entity.
  */
@@ -23,9 +21,7 @@
      * @param providerId  identity of the provider
      * @param annotations optional key/value annotations
      */
-    @SafeVarargs
-    protected AbstractModel(ProviderId providerId,
-                            Map<String, String>... annotations) {
+    protected AbstractModel(ProviderId providerId, Annotations... annotations) {
         super(annotations);
         this.providerId = providerId;
     }
diff --git a/core/api/src/main/java/org/onlab/onos/net/Annotated.java b/core/api/src/main/java/org/onlab/onos/net/Annotated.java
index f68cd46..5482c66 100644
--- a/core/api/src/main/java/org/onlab/onos/net/Annotated.java
+++ b/core/api/src/main/java/org/onlab/onos/net/Annotated.java
@@ -1,25 +1,15 @@
 package org.onlab.onos.net;
 
-import java.util.Set;
-
 /**
  * Represents an entity that carries arbitrary annotations.
  */
 public interface Annotated {
 
     /**
-     * Returns the set of annotation keys currently available.
+     * Returns the key/value annotations.
      *
-     * @return set of annotation keys
+     * @return key/value annotations
      */
-    Set<String> annotationKeys();
-
-    /**
-     * Returns the annotation value for the specified key.
-     *
-     * @param key annotation key
-     * @return annotation value; null if there is no annotation
-     */
-    String annotation(String key);
+    Annotations annotations();
 
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/Annotations.java b/core/api/src/main/java/org/onlab/onos/net/Annotations.java
index 6076172..cf78b98 100644
--- a/core/api/src/main/java/org/onlab/onos/net/Annotations.java
+++ b/core/api/src/main/java/org/onlab/onos/net/Annotations.java
@@ -1,46 +1,18 @@
 package org.onlab.onos.net;
 
-import com.google.common.collect.ImmutableMap;
-
-import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 
 /**
- * Represents a set of simple annotations that can be used to add arbitrary
- * attributes to various parts of the data model.
+ * Represents an set of simply key/value string annotations.
  */
-public final class Annotations {
-
-    private final Map<String, String> map;
+public interface Annotations {
 
     /**
-     * Creates a new set of annotations using the specified immutable map.
-     *
-     * @param map immutable map of key/value pairs
-     */
-    private Annotations(ImmutableMap<String, String> map) {
-        this.map = map;
-    }
-
-    /**
-     * Creates a new annotations builder.
-     *
-     * @return new annotations builder
-     */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    /**
-     * Returns the set of keys for available annotations. Note that this set
-     * includes keys for any attributes tagged for removal.
+     * Returns the set of keys for available annotations.
      *
      * @return annotation keys
      */
-    public Set<String> keys() {
-        return map.keySet();
-    }
+    public Set<String> keys();
 
     /**
      * Returns the value of the specified annotation.
@@ -48,66 +20,6 @@
      * @param key annotation key
      * @return annotation value
      */
-    public String value(String key) {
-        String value = map.get(key);
-        return Objects.equals(Builder.REMOVED, value) ? null : value;
-    }
+    public String value(String key);
 
-    /**
-     * Indicates whether the specified key has been tagged as removed. This is
-     * used to for merging sparse annotation sets.
-     *
-     * @param key annotation key
-     * @return true if the previous annotation has been tagged for removal
-     */
-    public boolean isRemoved(String key) {
-        return Objects.equals(Builder.REMOVED, map.get(key));
-    }
-
-    /**
-     * Facility for gradually building model annotations.
-     */
-    public static final class Builder {
-
-        private static final String REMOVED = "~rEmOvEd~";
-        private final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
-
-        // Private construction is forbidden.
-        private Builder() {
-        }
-
-        /**
-         * Adds the specified annotation. Any previous value associated with
-         * the given annotation key will be overwritten.
-         *
-         * @param key   annotation key
-         * @param value annotation value
-         * @return self
-         */
-        public Builder set(String key, String value) {
-            builder.put(key, value);
-            return this;
-        }
-
-        /**
-         * Adds the specified annotation. Any previous value associated with
-         * the given annotation key will be tagged for removal.
-         *
-         * @param key annotation key
-         * @return self
-         */
-        public Builder remove(String key) {
-            builder.put(key, REMOVED);
-            return this;
-        }
-
-        /**
-         * Returns immutable annotations built from the accrued key/values pairs.
-         *
-         * @return annotations
-         */
-        public Annotations build() {
-            return new Annotations(builder.build());
-        }
-    }
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultAnnotations.java b/core/api/src/main/java/org/onlab/onos/net/DefaultAnnotations.java
new file mode 100644
index 0000000..88eafb2
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultAnnotations.java
@@ -0,0 +1,106 @@
+package org.onlab.onos.net;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Represents a set of simple annotations that can be used to add arbitrary
+ * attributes to various parts of the data model.
+ */
+public final class DefaultAnnotations implements SparseAnnotations {
+
+    private final Map<String, String> map;
+
+    // For serialization
+    private DefaultAnnotations() {
+        this.map = null;
+    }
+
+    /**
+     * Creates a new set of annotations using the specified immutable map.
+     *
+     * @param map immutable map of key/value pairs
+     */
+    private DefaultAnnotations(Map<String, String> map) {
+        this.map = map;
+    }
+
+    /**
+     * Creates a new annotations builder.
+     *
+     * @return new annotations builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+
+    @Override
+    public Set<String> keys() {
+        return map.keySet();
+    }
+
+    @Override
+    public String value(String key) {
+        String value = map.get(key);
+        return Objects.equals(Builder.REMOVED, value) ? null : value;
+    }
+
+    @Override
+    public boolean isRemoved(String key) {
+        return Objects.equals(Builder.REMOVED, map.get(key));
+    }
+
+    /**
+     * Facility for gradually building model annotations.
+     */
+    public static final class Builder {
+
+        private static final String REMOVED = "~rEmOvEd~";
+
+        // FIXME: Figure out whether and how to make this immutable and serializable
+//        private final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
+        private final Map<String, String> builder = new HashMap<>();
+
+        // Private construction is forbidden.
+        private Builder() {
+        }
+
+        /**
+         * Adds the specified annotation. Any previous value associated with
+         * the given annotation key will be overwritten.
+         *
+         * @param key   annotation key
+         * @param value annotation value
+         * @return self
+         */
+        public Builder set(String key, String value) {
+            builder.put(key, value);
+            return this;
+        }
+
+        /**
+         * Adds the specified annotation. Any previous value associated with
+         * the given annotation key will be tagged for removal.
+         *
+         * @param key annotation key
+         * @return self
+         */
+        public Builder remove(String key) {
+            builder.put(key, REMOVED);
+            return this;
+        }
+
+        /**
+         * Returns immutable annotations built from the accrued key/values pairs.
+         *
+         * @return annotations
+         */
+        public DefaultAnnotations build() {
+//            return new DefaultAnnotations(builder.build());
+            return new DefaultAnnotations(builder);
+        }
+    }
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java b/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java
index 29a8d17..2c6c4ff 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java
@@ -36,11 +36,12 @@
      * @param hwVersion    device HW version
      * @param swVersion    device SW version
      * @param serialNumber device serial number
+     * @param annotations optional key/value annotations
      */
     public DefaultDevice(ProviderId providerId, DeviceId id, Type type,
                          String manufacturer, String hwVersion, String swVersion,
-                         String serialNumber) {
-        super(providerId, id);
+                         String serialNumber, Annotations... annotations) {
+        super(providerId, id, annotations);
         this.type = type;
         this.manufacturer = manufacturer;
         this.hwVersion = hwVersion;
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java b/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java
index 07a57e9e..6074c67 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java
@@ -20,11 +20,13 @@
      * @param hostLocation location where host attaches to the network
      * @param isIngress    true to indicated host-to-network direction; false
      *                     for network-to-host direction
+     * @param annotations optional key/value annotations
      */
     public DefaultEdgeLink(ProviderId providerId, ConnectPoint hostPoint,
-                           HostLocation hostLocation, boolean isIngress) {
+                           HostLocation hostLocation, boolean isIngress,
+                           Annotations... annotations) {
         super(providerId, isIngress ? hostPoint : hostLocation,
-              isIngress ? hostLocation : hostPoint, Type.EDGE);
+              isIngress ? hostLocation : hostPoint, Type.EDGE, annotations);
         checkArgument(hostPoint.elementId() instanceof HostId,
                       "Host point does not refer to a host ID");
         this.hostId = (HostId) hostPoint.elementId();
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java b/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java
index 8a2d6e6..efa876b 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java
@@ -1,16 +1,16 @@
 package org.onlab.onos.net;
 
-import static com.google.common.base.MoreObjects.toStringHelper;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
 
-import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
+import static com.google.common.base.MoreObjects.toStringHelper;
 
 /**
  * A basic implementation of a Host.
@@ -22,12 +22,24 @@
     private final HostLocation location;
     private final Set<IpPrefix> ips;
 
+    /**
+     * Creates an end-station host using the supplied information.
+     *
+     * @param providerId provider identity
+     * @param id         host identifier
+     * @param mac        host MAC address
+     * @param vlan       host VLAN identifier
+     * @param location   host location
+     * @param ips        host IP addresses
+     * @param annotations optional key/value annotations
+     */
     public DefaultHost(ProviderId providerId, HostId id, MacAddress mac,
-            VlanId vlan, HostLocation loc, Set<IpPrefix> ips) {
-        super(providerId, id);
+                       VlanId vlan, HostLocation location, Set<IpPrefix> ips,
+                       Annotations... annotations) {
+        super(providerId, id, annotations);
         this.mac = mac;
         this.vlan = vlan;
-        this.location = loc;
+        this.location = location;
         this.ips = new HashSet<IpPrefix>(ips);
     }
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java b/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java
index 8771faa..9b9625a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java
@@ -22,10 +22,11 @@
      * @param src        link source
      * @param dst        link destination
      * @param type       link type
+     * @param annotations optional key/value annotations
      */
     public DefaultLink(ProviderId providerId, ConnectPoint src, ConnectPoint dst,
-                       Type type) {
-        super(providerId);
+                       Type type, Annotations... annotations) {
+        super(providerId, annotations);
         this.src = src;
         this.dst = dst;
         this.type = type;
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultPath.java b/core/api/src/main/java/org/onlab/onos/net/DefaultPath.java
index cab41b8..fb0b281 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultPath.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultPath.java
@@ -24,9 +24,11 @@
      * @param providerId provider identity
      * @param links      contiguous links that comprise the path
      * @param cost       unit-less path cost
+     * @param annotations optional key/value annotations
      */
-    public DefaultPath(ProviderId providerId, List<Link> links, double cost) {
-        super(providerId, source(links), destination(links), Type.INDIRECT);
+    public DefaultPath(ProviderId providerId, List<Link> links, double cost,
+                       Annotations... annotations) {
+        super(providerId, source(links), destination(links), Type.INDIRECT, annotations);
         this.links = ImmutableList.copyOf(links);
         this.cost = cost;
     }
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultPort.java b/core/api/src/main/java/org/onlab/onos/net/DefaultPort.java
index ac57498..573532f 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultPort.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultPort.java
@@ -1,10 +1,9 @@
 package org.onlab.onos.net;
 
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-import java.util.Map;
 import java.util.Objects;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Default port implementation.
  */
@@ -22,10 +21,8 @@
      * @param isEnabled   indicator whether the port is up and active
      * @param annotations optional key/value annotations
      */
-    @SafeVarargs
     public DefaultPort(Element element, PortNumber number,
-                       boolean isEnabled,
-                       Map<String, String>... annotations) {
+                       boolean isEnabled, Annotations... annotations) {
         super(annotations);
         this.element = element;
         this.number = number;
diff --git a/core/api/src/main/java/org/onlab/onos/net/Description.java b/core/api/src/main/java/org/onlab/onos/net/Description.java
index 8b5464b..2736040 100644
--- a/core/api/src/main/java/org/onlab/onos/net/Description.java
+++ b/core/api/src/main/java/org/onlab/onos/net/Description.java
@@ -4,4 +4,8 @@
  * Base abstraction of a piece of information about network elements.
  */
 public interface Description extends Annotated {
+
+    @Override
+    SparseAnnotations annotations();
+
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/HostLocation.java b/core/api/src/main/java/org/onlab/onos/net/HostLocation.java
index 626b027..76e2312 100644
--- a/core/api/src/main/java/org/onlab/onos/net/HostLocation.java
+++ b/core/api/src/main/java/org/onlab/onos/net/HostLocation.java
@@ -9,16 +9,23 @@
     // Note that time is explicitly excluded from the notion of equality.
     private final long time;
 
+    /**
+     * Creates a new host location using the supplied device &amp; port.
+     *
+     * @param deviceId   device identity
+     * @param portNumber device port number
+     * @param time       time when detected, in millis since start of epoch
+     */
     public HostLocation(DeviceId deviceId, PortNumber portNumber, long time) {
         super(deviceId, portNumber);
         this.time = time;
     }
 
     /**
-     * Returns the timestamp when the location was established, given in
+     * Returns the time when the location was established, given in
      * milliseconds since start of epoch.
      *
-     * @return timestamp in milliseconds since start of epoch
+     * @return time in milliseconds since start of epoch
      */
     public long time() {
         return time;
diff --git a/core/api/src/main/java/org/onlab/onos/net/SparseAnnotations.java b/core/api/src/main/java/org/onlab/onos/net/SparseAnnotations.java
new file mode 100644
index 0000000..9382119
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/SparseAnnotations.java
@@ -0,0 +1,27 @@
+package org.onlab.onos.net;
+
+import java.util.Set;
+
+/**
+ * Represents an set of simply key/value string annotations.
+ */
+public interface SparseAnnotations extends Annotations {
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Note that this set includes keys for any attributes tagged for removal.
+     */
+    @Override
+    public Set<String> keys();
+
+    /**
+     * Indicates whether the specified key has been tagged as removed. This is
+     * used to for merging sparse annotation sets.
+     *
+     * @param key annotation key
+     * @return true if the previous annotation has been tagged for removal
+     */
+    public boolean isRemoved(String key);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java b/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
index f5bc0d6..fd04e5c 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
@@ -1,9 +1,9 @@
 package org.onlab.onos.net.device;
 
-import org.onlab.onos.net.AbstractAnnotated;
+import org.onlab.onos.net.AbstractDescription;
+import org.onlab.onos.net.SparseAnnotations;
 
 import java.net.URI;
-import java.util.Map;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -12,7 +12,7 @@
 /**
  * Default implementation of immutable device description entity.
  */
-public class DefaultDeviceDescription extends AbstractAnnotated
+public class DefaultDeviceDescription extends AbstractDescription
         implements DeviceDescription {
     private final URI uri;
     private final Type type;
@@ -32,11 +32,10 @@
      * @param serialNumber device serial number
      * @param annotations  optional key/value annotations map
      */
-    @SafeVarargs
     public DefaultDeviceDescription(URI uri, Type type, String manufacturer,
                                     String hwVersion, String swVersion,
                                     String serialNumber,
-                                    Map<String, String>... annotations) {
+                                    SparseAnnotations... annotations) {
         super(annotations);
         this.uri = checkNotNull(uri, "Device URI cannot be null");
         this.type = checkNotNull(type, "Device type cannot be null");
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java b/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java
index 6190183..bc6e3e5 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java
@@ -1,14 +1,14 @@
 package org.onlab.onos.net.host;
 
 import com.google.common.collect.ImmutableSet;
-import org.onlab.onos.net.AbstractAnnotated;
+import org.onlab.onos.net.AbstractDescription;
 import org.onlab.onos.net.HostLocation;
+import org.onlab.onos.net.SparseAnnotations;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 
 import java.util.HashSet;
-import java.util.Map;
 import java.util.Set;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
@@ -16,7 +16,7 @@
 /**
  * Default implementation of an immutable host description.
  */
-public class DefaultHostDescription extends AbstractAnnotated
+public class DefaultHostDescription extends AbstractDescription
         implements HostDescription {
 
     private final MacAddress mac;
@@ -32,10 +32,9 @@
      * @param location    host location
      * @param annotations optional key/value annotations map
      */
-    @SafeVarargs
     public DefaultHostDescription(MacAddress mac, VlanId vlan,
                                   HostLocation location,
-                                  Map<String, String>... annotations) {
+                                  SparseAnnotations... annotations) {
         this(mac, vlan, location, new HashSet<IpPrefix>(), annotations);
     }
 
@@ -48,10 +47,9 @@
      * @param ips         of host IP addresses
      * @param annotations optional key/value annotations map
      */
-    @SafeVarargs
     public DefaultHostDescription(MacAddress mac, VlanId vlan,
                                   HostLocation location, Set<IpPrefix> ips,
-                                  Map<String, String>... annotations) {
+                                  SparseAnnotations... annotations) {
         super(annotations);
         this.mac = mac;
         this.vlan = vlan;
diff --git a/core/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java b/core/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java
index ffdc2fa..65d8d6d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java
@@ -1,12 +1,15 @@
 package org.onlab.onos.net.link;
 
+import org.onlab.onos.net.AbstractDescription;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.Link;
+import org.onlab.onos.net.SparseAnnotations;
 
 /**
  * Default implementation of immutable link description entity.
  */
-public class DefaultLinkDescription implements LinkDescription {
+public class DefaultLinkDescription extends AbstractDescription
+        implements LinkDescription {
 
     private final ConnectPoint src;
     private final ConnectPoint dst;
@@ -18,8 +21,11 @@
      * @param src  link source
      * @param dst  link destination
      * @param type link type
+     * @param annotations optional key/value annotations
      */
-    public DefaultLinkDescription(ConnectPoint src, ConnectPoint dst, Link.Type type) {
+    public DefaultLinkDescription(ConnectPoint src, ConnectPoint dst,
+                                  Link.Type type, SparseAnnotations... annotations) {
+        super(annotations);
         this.src = src;
         this.dst = dst;
         this.type = type;
diff --git a/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java b/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java
index 36c8aed..db51374 100644
--- a/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java
@@ -6,6 +6,7 @@
 /**
  * Service for processing arp requests on behalf of applications.
  */
+// TODO: move to the peer host package
 public interface ProxyArpService {
 
     /**
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/DefaultGraphDescription.java b/core/api/src/main/java/org/onlab/onos/net/topology/DefaultGraphDescription.java
index 1369fda..dfc3ad6 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/DefaultGraphDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/DefaultGraphDescription.java
@@ -2,18 +2,19 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
-import org.onlab.onos.net.AbstractAnnotated;
+import org.onlab.onos.net.AbstractDescription;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.Device;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Link;
+import org.onlab.onos.net.SparseAnnotations;
 
 import java.util.Map;
 
 /**
  * Default implementation of an immutable topology graph data carrier.
  */
-public class DefaultGraphDescription extends AbstractAnnotated
+public class DefaultGraphDescription extends AbstractDescription
         implements GraphDescription {
 
     private final long nanos;
@@ -32,10 +33,9 @@
      * @param links       collection of infrastructure links
      * @param annotations optional key/value annotations map
      */
-    @SafeVarargs
     public DefaultGraphDescription(long nanos, Iterable<Device> devices,
                                    Iterable<Link> links,
-                                   Map<String, String>... annotations) {
+                                   SparseAnnotations... annotations) {
         super(annotations);
         this.nanos = nanos;
         this.vertexes = buildVertexes(devices);
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java
index 4b8410d..04d1a88 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java
@@ -1,10 +1,6 @@
 package org.onlab.onos.store.serializers;
 
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-
+import de.javakaffee.kryoserializers.URISerializer;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -13,6 +9,7 @@
 import org.onlab.onos.cluster.DefaultControllerNode;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultAnnotations;
 import org.onlab.onos.net.DefaultDevice;
 import org.onlab.onos.net.DefaultLink;
 import org.onlab.onos.net.DefaultPort;
@@ -30,7 +27,10 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import de.javakaffee.kryoserializers.URISerializer;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
 
 /**
  * Serialization service using Kryo.
@@ -66,6 +66,7 @@
                           ControllerNode.State.class,
                           Device.Type.class,
 
+                          DefaultAnnotations.class,
                           DefaultControllerNode.class,
                           DefaultDevice.class,
                           MastershipRole.class,