diff --git a/deploy/csp/index.js b/deploy/csp/index.js
index 616a0ab03fe626d20af4c007eca18a0347e749a7..87a6fe19e8e0652322a121858aef0a7394b46721 100644
--- a/deploy/csp/index.js
+++ b/deploy/csp/index.js
@@ -124,6 +124,7 @@ module.exports = {
       } else {
         console.warn(`CSP Violation: no data received!`)
       }
+      res.status(204).end()
     })
   }
 }
diff --git a/docs/releases/v2.4.4.md b/docs/releases/v2.4.4.md
index 184c33ef099225d7e22b99a0f69f7b94c0dc9a33..cadb5d9df79de2f9c768008c4a35d1b705a0bb81 100644
--- a/docs/releases/v2.4.4.md
+++ b/docs/releases/v2.4.4.md
@@ -1,5 +1,13 @@
 # v2.4.4
 
+# Features
+
+- Allow name and description of annotations to be exported
+
 ## Bugfixes
 
 - Fix version of connectivity web component
+
+## Under the hood stuff
+
+- Respond in csp violation reports
diff --git a/src/atlasComponents/userAnnotations/annotationList/annotationList.component.ts b/src/atlasComponents/userAnnotations/annotationList/annotationList.component.ts
index 525f5eaa5dad6a2ca706664963bda383d8bef9ac..f3b0e82f73509655e5135fb13041645a4a83cb63 100644
--- a/src/atlasComponents/userAnnotations/annotationList/annotationList.component.ts
+++ b/src/atlasComponents/userAnnotations/annotationList/annotationList.component.ts
@@ -3,7 +3,7 @@ import {ARIA_LABELS} from "common/constants";
 import { ModularUserAnnotationToolService } from "../tools/service";
 import { IAnnotationGeometry, TExportFormats } from "../tools/type";
 import { ComponentStore } from "src/viewerModule/componentStore";
-import { map, startWith, tap } from "rxjs/operators";
+import { map, shareReplay, startWith } from "rxjs/operators";
 import { Observable } from "rxjs";
 import { TZipFileConfig } from "src/zipFilesOutput/type";
 import { TFileInputEvent } from "src/getFileInput/type";
@@ -11,7 +11,7 @@ import { FileInputDirective } from "src/getFileInput/getFileInput.directive";
 import { MatSnackBar } from "@angular/material/snack-bar";
 import { unzip } from "src/zipFilesOutput/zipFilesOutput.directive";
 
-const README = 'EXAMPLE OF READ ME TEXT'
+const README = `{id}.sands.json file contains the data of annotations. {id}.desc.json contains the metadata of annotations.`
 
 @Component({
   selector: 'annotation-list',
@@ -39,6 +39,7 @@ export class AnnotationList {
 
   public filesExport$: Observable<TZipFileConfig[]> = this.managedAnnotations$.pipe(
     startWith([] as IAnnotationGeometry[]),
+    shareReplay(1),
     map(manAnns => {
       const readme = {
         filename: 'README.md',
@@ -50,7 +51,13 @@ export class AnnotationList {
           filecontent: JSON.stringify(ann.toSands(), null, 2),
         }
       })
-      return [ readme, ...annotationSands ]
+      const annotationDesc = manAnns.map(ann => {
+        return {
+          filename: `${ann.id}.desc.json`,
+          filecontent: JSON.stringify(this.annotSvc.exportAnnotationMetadata(ann), null, 2)
+        }
+      })
+      return [ readme, ...annotationSands, ...annotationDesc ]
     })
   )
   constructor(
@@ -71,7 +78,7 @@ export class AnnotationList {
   private parseAndAddAnnotation(input: string) {
     const json = JSON.parse(input)
     const annotation = this.annotSvc.parseAnnotationObject(json)
-    this.annotSvc.importAnnotation(annotation)
+    if (annotation) this.annotSvc.importAnnotation(annotation)
   }
 
   async handleImportEvent(ev: TFileInputEvent<'text' | 'file'>){
diff --git a/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html b/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html
index 17ac890eafc6f4049b039ad7c36147a2d481642d..2bfd568c082e19312a3a29748ed2b6ad02abe042 100644
--- a/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html
+++ b/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html
@@ -26,7 +26,7 @@
 
             <!-- export -->
             <button mat-icon-button
-                [zip-files-output]="filesExport$ | async"
+                [zip-files-output]="filesExport$"
                 zip-files-output-zip-filename="exported_annotations.zip"
                 [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_EXPORT"
                 [matTooltip]="ARIA_LABELS.USER_ANNOTATION_EXPORT"
diff --git a/src/atlasComponents/userAnnotations/tools/delete.ts b/src/atlasComponents/userAnnotations/tools/delete.ts
index dc817a65012bd3dbe5409fb8656d5b9d5bcb9990..4c47b673b553b04f3d0e9ab25716caf715ea70a7 100644
--- a/src/atlasComponents/userAnnotations/tools/delete.ts
+++ b/src/atlasComponents/userAnnotations/tools/delete.ts
@@ -7,6 +7,7 @@ import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools,
 export class ToolDelete extends AbsToolClass<Point> implements IAnnotationTools, OnDestroy {
 
   public subs: Subscription[] = []
+  protected managedAnnotations = []
   toolType: TToolType = 'deletion'
   iconClass = 'fas fa-trash'
   name = 'Delete'
diff --git a/src/atlasComponents/userAnnotations/tools/line.ts b/src/atlasComponents/userAnnotations/tools/line.ts
index 1303612f991d1cadc53a1ce73ddf9f043babca52..a24dd9267fd9b993b2cc2cb8cfa84f3ed5350077 100644
--- a/src/atlasComponents/userAnnotations/tools/line.ts
+++ b/src/atlasComponents/userAnnotations/tools/line.ts
@@ -192,7 +192,7 @@ export class ToolLine extends AbsToolClass<Line> implements IAnnotationTools, On
   
   subs: Subscription[] = []
 
-  private managedAnnotations: Line[] = []
+  protected managedAnnotations: Line[] = []
   public managedAnnotations$ = new Subject<Line[]>()
 
   onMouseMoveRenderPreview(pos: [number, number, number]) {
@@ -311,14 +311,6 @@ export class ToolLine extends AbsToolClass<Line> implements IAnnotationTools, On
     this.subs.forEach(s => s.unsubscribe())
   }
 
-  addAnnotation(line: Line) {
-    const idx = this.managedAnnotations.findIndex(ann => ann.id === line.id)
-    if (idx >= 0) throw new Error(`Line annotation has already been added`)
-    line.remove = () => this.removeAnnotation(line.id)
-    this.managedAnnotations.push(line)
-    this.managedAnnotations$.next(this.managedAnnotations)
-  }
-
   removeAnnotation(id: string){
     const idx = this.managedAnnotations.findIndex(ann => ann.id === id)
     if (idx < 0) {
diff --git a/src/atlasComponents/userAnnotations/tools/point.ts b/src/atlasComponents/userAnnotations/tools/point.ts
index 2dcafc2490a82b2dacb3bf5180b7457947de2770..43f13978df340405932be5c6fb39678a3596d83c 100644
--- a/src/atlasComponents/userAnnotations/tools/point.ts
+++ b/src/atlasComponents/userAnnotations/tools/point.ts
@@ -1,5 +1,5 @@
 import { AbsToolClass, getCoord, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, INgAnnotationTypes, TAnnotationEvent, TBaseAnnotationGeomtrySpec, TCallbackFunction, TNgAnnotationEv, TSandsPoint, TToolType } from "./type";
-import { merge, Observable, Subject, Subscription } from "rxjs";
+import { Observable, Subject, Subscription } from "rxjs";
 import { OnDestroy } from "@angular/core";
 import { filter, switchMapTo, takeUntil } from "rxjs/operators";
 
@@ -108,7 +108,7 @@ export class ToolPoint extends AbsToolClass<Point> implements IAnnotationTools,
   public iconClass = POINT_ICON_CLASS
   
   public subs: Subscription[] = []
-  private managedAnnotations: Point[] = []
+  protected managedAnnotations: Point[] = []
   public managedAnnotations$ = new Subject<Point[]>()
 
   constructor(
@@ -176,14 +176,6 @@ export class ToolPoint extends AbsToolClass<Point> implements IAnnotationTools,
     )
   }
 
-  addAnnotation(point: Point){
-    const found = this.managedAnnotations.find(p => p.id === point.id)
-    if (found) throw new Error(`Point annotation already added`)
-    point.remove = () => this.removeAnnotation(point.id)
-    this.managedAnnotations.push(point)
-    this.managedAnnotations$.next(this.managedAnnotations)
-  }
-
   /**
    * @description remove managed annotation via id
    * @param id id of annotation
diff --git a/src/atlasComponents/userAnnotations/tools/poly.ts b/src/atlasComponents/userAnnotations/tools/poly.ts
index c33662c2afc022ae8240884c629946587057b662..304f3854ee3a99aa1806f3a738267a6f0653b0d2 100644
--- a/src/atlasComponents/userAnnotations/tools/poly.ts
+++ b/src/atlasComponents/userAnnotations/tools/poly.ts
@@ -89,7 +89,7 @@ export class Polygon extends IAnnotationGeometry{
   }
 
   toString() {
-    return `Points: ${JSON.stringify(this.points.map(p => p.toString()))}, edges: ${JSON.stringify(this.edges)}.`
+    return `Name: ${this.name}, Desc: ${this.desc}, Points: ${JSON.stringify(this.points.map(p => p.toString()))}, edges: ${JSON.stringify(this.edges)}.`
   }
 
   toSands(): TSandsPolyLine{
@@ -233,7 +233,7 @@ export class ToolPolygon extends AbsToolClass<Polygon> implements IAnnotationToo
   private selectedPoly: Polygon
   private lastAddedPoint: Point
 
-  private managedAnnotations: Polygon[] = []
+  protected managedAnnotations: Polygon[] = []
   public managedAnnotations$ = new Subject<Polygon[]>()
 
   public subs: Subscription[] = []
@@ -403,14 +403,6 @@ export class ToolPolygon extends AbsToolClass<Polygon> implements IAnnotationToo
     )
   }
 
-  addAnnotation(poly: Polygon){
-    const idx = this.managedAnnotations.findIndex(ann => ann.id === poly.id)
-    if (idx >= 0) throw new Error(`Polygon already added.`)
-    poly.remove = () => this.removeAnnotation(poly.id)
-    this.managedAnnotations.push(poly)
-    this.managedAnnotations$.next(this.managedAnnotations)
-  }
-
   removeAnnotation(id: string) {
     const idx = this.managedAnnotations.findIndex(ann => ann.id === id)
     if (idx < 0) {
diff --git a/src/atlasComponents/userAnnotations/tools/select.ts b/src/atlasComponents/userAnnotations/tools/select.ts
index 38a0b11ba39ec9df2ddbb20b875f01cd48575257..a06d36e988723f95db5ad30e926966642ce8a78c 100644
--- a/src/atlasComponents/userAnnotations/tools/select.ts
+++ b/src/atlasComponents/userAnnotations/tools/select.ts
@@ -10,6 +10,7 @@ export class ToolSelect extends AbsToolClass<Point> implements IAnnotationTools,
   toolType: TToolType = 'selecting'
   iconClass = 'fas fa-mouse-pointer'
   name = 'Select'
+  protected managedAnnotations = []
 
   onMouseMoveRenderPreview(){
     return []
diff --git a/src/atlasComponents/userAnnotations/tools/service.ts b/src/atlasComponents/userAnnotations/tools/service.ts
index fde183e33b5d8fd56059d857f4c5dfb600502350..24c26c5a2d74ccb8eb96146e88642c11962eef3c 100644
--- a/src/atlasComponents/userAnnotations/tools/service.ts
+++ b/src/atlasComponents/userAnnotations/tools/service.ts
@@ -26,6 +26,17 @@ const IAV_VOXEL_SIZES_NM = {
   'minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2': [1000000, 1000000, 1000000]
 }
 
+type TAnnotationMetadata = {
+  id: string
+  name: string
+  desc: string
+}
+
+const descType: 'siibra-ex/meta/desc' = 'siibra-ex/meta/desc'
+type TTypedAnnMetadata = {
+  '@type': 'siibra-ex/meta/desc'
+} & TAnnotationMetadata
+
 function scanCollapse<T>(){
   return (src: Observable<{
     tool: string
@@ -551,7 +562,7 @@ export class ModularUserAnnotationToolService implements OnDestroy{
     const anns: IAnnotationGeometry[] = []
     for (const obj of arr) {
       const geometry = this.parseAnnotationObject(obj)
-      anns.push(geometry)
+      if (geometry) anns.push(geometry)
     }
     
     for (const ann of anns) {
@@ -559,6 +570,21 @@ export class ModularUserAnnotationToolService implements OnDestroy{
     }
   }
 
+  public exportAnnotationMetadata(ann: IAnnotationGeometry): TAnnotationMetadata & { '@type': 'siibra-ex/meta/desc' } {
+    return {
+      '@type': descType,
+      id: ann.id,
+      name: ann.name,
+      desc: ann.desc,
+    }
+  }
+
+  /**
+   * stop gap measure when exporting/import annotations in sands format
+   * metadata (name/desc) will be saved in a separate metadata file
+   */
+  private metadataMap = new Map<string, TAnnotationMetadata>()
+
   private storeAnnotation(anns: IAnnotationGeometry[]){
     const arr = []
     for (const ann of anns) {
@@ -626,25 +652,49 @@ export class ModularUserAnnotationToolService implements OnDestroy{
     })
   }
 
-  parseAnnotationObject(json: TSands | TGeometryJson): IAnnotationGeometry{
+  parseAnnotationObject(json: TSands | TGeometryJson | TTypedAnnMetadata): IAnnotationGeometry | null{
+    let returnObj: IAnnotationGeometry
     if (json['@type'] === 'tmp/poly') {
-      return Polygon.fromSANDS(json)
+      returnObj = Polygon.fromSANDS(json)
     }
     if (json['@type'] === 'tmp/line') {
-      return Line.fromSANDS(json)
+      returnObj = Line.fromSANDS(json)
     }
     if (json['@type'] === 'https://openminds.ebrains.eu/sands/CoordinatePoint') {
-      return Point.fromSANDS(json)
+      returnObj = Point.fromSANDS(json)
     }
     if (json['@type'] === 'siibra-ex/annotation/point') {
-      return Point.fromJSON(json)
+      returnObj = Point.fromJSON(json)
     }
     if (json['@type'] === 'siibra-ex/annotation/line') {
-      return Line.fromJSON(json)
+      returnObj = Line.fromJSON(json)
     }
     if (json['@type'] === 'siibra-ex/annotation/polyline') {
-      return Polygon.fromJSON(json)
+      returnObj = Polygon.fromJSON(json)
+    }
+    if (json['@type'] === descType) {
+      const existingAnn = this.managedAnnotations.find(ann => json.id === ann.id)
+      if (existingAnn) {
+
+        // potentially overwriting existing name and desc...
+        // maybe should show warning?
+        existingAnn.setName(json.name)
+        existingAnn.setDesc(json.desc)
+        return existingAnn
+      } else {
+        const { id, name, desc } = json
+        this.metadataMap.set(id, { id, name, desc })
+        return
+      }
+    } else {
+      const metadata = this.metadataMap.get(returnObj.id)
+      if (returnObj && metadata) {
+        returnObj.setName(metadata?.name || null)
+        returnObj.setDesc(metadata?.desc || null)
+        this.metadataMap.delete(returnObj.id)
+      }
     }
+    if (returnObj) return returnObj
     throw new Error(`cannot parse annotation object`)
   }
 
diff --git a/src/atlasComponents/userAnnotations/tools/type.ts b/src/atlasComponents/userAnnotations/tools/type.ts
index 1716457a166eea87cc6b9943b4c3ebed3538c03a..439d48c3b7ea338d1889890b9d8fbeb78fb49396 100644
--- a/src/atlasComponents/userAnnotations/tools/type.ts
+++ b/src/atlasComponents/userAnnotations/tools/type.ts
@@ -16,9 +16,9 @@ export abstract class AbsToolClass<T extends IAnnotationGeometry> {
   public abstract name: string
   public abstract iconClass: string
 
-  public abstract addAnnotation(annotation: T): void
   public abstract removeAnnotation(id: string): void
-  public abstract managedAnnotations$: Observable<T[]>
+  public abstract managedAnnotations$: Subject<T[]>
+  protected abstract managedAnnotations: T[] = []
 
   abstract subs: Subscription[]
   protected space: TBaseAnnotationGeomtrySpec['space']
@@ -152,6 +152,14 @@ export abstract class AbsToolClass<T extends IAnnotationGeometry> {
       }),
     ))
   )
+
+  public addAnnotation(geom: T) {
+    const found = this.managedAnnotations.find(ann => ann.id === geom.id)
+    if (found) found.remove()
+    geom.remove = () => this.removeAnnotation(geom.id)
+    this.managedAnnotations.push(geom)
+    this.managedAnnotations$.next(this.managedAnnotations)
+  }
 }
 
 export type TToolType = 'selecting' | 'drawing' | 'deletion'
diff --git a/src/zipFilesOutput/zipFilesOutput.directive.ts b/src/zipFilesOutput/zipFilesOutput.directive.ts
index bcf70bddfaf8e3e84ab97f887ae93ae8e95fcfc6..dc980728cfd8675ecfaaf57eb1c4317f74556ba0 100644
--- a/src/zipFilesOutput/zipFilesOutput.directive.ts
+++ b/src/zipFilesOutput/zipFilesOutput.directive.ts
@@ -2,6 +2,8 @@ import { Directive, HostListener, Inject, Input } from "@angular/core";
 import { TZipFileConfig } from "./type";
 import * as JSZip from "jszip";
 import { DOCUMENT } from "@angular/common";
+import { isObservable, Observable } from "rxjs";
+import { take } from "rxjs/operators";
 
 @Directive({
   selector: '[zip-files-output]',
@@ -10,15 +12,15 @@ import { DOCUMENT } from "@angular/common";
 
 export class ZipFilesOutput {
   @Input('zip-files-output')
-  zipFiles: TZipFileConfig[] = []
+  zipFiles: Observable<TZipFileConfig[]> | TZipFileConfig[] = []
 
   @Input('zip-files-output-zip-filename')
   zipFilename = 'archive.zip'
 
-  @HostListener('click')
-  async onClick(){
+  private async zipArray(arrZipConfig: TZipFileConfig[]){
+
     const zip = new JSZip()
-    for (const zipFile of this.zipFiles) {
+    for (const zipFile of arrZipConfig) {
       const { filecontent, filename, base64 } = zipFile
       zip.file(filename, filecontent, { base64 })
     }
@@ -32,6 +34,21 @@ export class ZipFilesOutput {
     this.doc.body.removeChild(anchor)
     URL.revokeObjectURL(anchor.href)
   }
+
+  @HostListener('click')
+  async onClick(){
+    if (Array.isArray(this.zipFiles)) {
+      await this.zipArray(this.zipFiles)
+      return
+    }
+    if (isObservable(this.zipFiles)) {
+      const zipFiles = await this.zipFiles.pipe(
+        take(1)
+      ).toPromise()
+      await this.zipArray(zipFiles)
+      return
+    }
+  }
   constructor(
     @Inject(DOCUMENT) private doc: Document
   ){