diff --git a/deploy/datasets/index.js b/deploy/datasets/index.js
index cb69686463aa6cd2240a183f3c09340a5d0e5b02..c5bb0a0d1e9a485f54960ee3e3ad4bf95191fff3 100644
--- a/deploy/datasets/index.js
+++ b/deploy/datasets/index.js
@@ -2,11 +2,12 @@ const express = require('express')
 const path = require('path')
 const fs = require('fs')
 const datasetsRouter = express.Router()
-const { init, getDatasets, getPreview, getDatasetFromId, getDatasetFileAsZip } = require('./query')
+const { init, getDatasets, getPreview, getDatasetFromId, getDatasetFileAsZip, getTos } = require('./query')
 const url = require('url')
 const qs = require('querystring')
 
 const bodyParser = require('body-parser')
+
 datasetsRouter.use(bodyParser.urlencoded({ extended: false }))
 datasetsRouter.use(bodyParser.json())
 
@@ -34,6 +35,17 @@ const getVary = (headers) => (_req, res, next) => {
   next()
 }
 
+
+datasetsRouter.get('/tos', cacheMaxAge24Hr, async (req, res) => {
+  const tos = getTos()
+  if (tos) {
+    res.setHeader('Content-Type', 'text/markdown; charset=utf-8')
+    res.status(200).send(tos)
+  } else {
+    res.status(404).end()
+  }
+})
+
 datasetsRouter.use('/spatialSearch', noCacheMiddleWare, require('./spatialRouter'))
 
 datasetsRouter.get('/templateName/:templateName', noCacheMiddleWare, (req, res, next) => {
@@ -145,48 +157,8 @@ datasetsRouter.get('/downloadKgFiles', checkKgQuery, async (req, res) => {
     stream.pipe(res)
   } catch (e) {
     console.warn('datasets/index#downloadKgFiles', e)
-    res.status(400).send(e)
+    res.status(400).send(e.toString())
   }
 })
 
-/**
- * TODO
- * deprecate jszip in favour of archiver
- */
-
-var JSZip = require("jszip");
-
-datasetsRouter.post("/downloadParcellationThemself", (req,res, next) => {
-
-
-  //ToDo We can add termsOfUse Text file somewhere - will be better
-  const termsOfUse = 'Access to the data and metadata provided through HBP Knowledge Graph Data Platform ' +
-      '("KG") requires that you cite and acknowledge said data and metadata according to the Terms and' +
-      ' Conditions of the Platform.\r\n## Citation requirements are outlined - https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#citations' +
-      '\r\n## Acknowledgement requirements are outlined - https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#acknowledgements' +
-      '\r\n## These outlines are based on the authoritative Terms and Conditions are found - https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use' +
-      '\r\n## If you do not accept the Terms & Conditions you are not permitted to access or use the KG to search for, to submit, to post, or to download any materials found there-in.'
-
-
-  var zip = new JSZip();
-
-  zip.file("credits.txt", req.body['publicationsText'])
-  zip.file("Terms of use.txt", termsOfUse)
-
-
-
-
-  //ToDo: Need to download files dynamically. Nii folder should be removed
-  if (req.body['niiFiles']) {
-    var nii = zip.folder("nifti")
-    const filepath = process.env.STORAGE_PATH || path.join(__dirname, 'nii')
-    req.body['niiFiles'].forEach(file => {
-      nii.file(file['file'], fs.readFileSync(path.join(filepath, file['file'])))
-    })
-  }
-
-  res.setHeader('Content-Type', 'application/zip')
-  zip.generateNodeStream().pipe(res)
-});
-
 module.exports = datasetsRouter
\ No newline at end of file
diff --git a/deploy/datasets/kgtos.md b/deploy/datasets/kgtos.md
new file mode 100644
index 0000000000000000000000000000000000000000..44bc5966c7674b3bfbd5d40ff535d9464bd4ba4a
--- /dev/null
+++ b/deploy/datasets/kgtos.md
@@ -0,0 +1,16 @@
+The interactive viewer queries HBP Knowledge Graph Data Platform ("KG") for published datasets.
+
+
+Access to the data and metadata provided through KG requires that you cite and acknowledge said data and metadata according to the Terms and Conditions of the Platform.
+
+
+Citation requirements are outlined <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#citations> .
+
+
+Acknowledgement requirements are outlined <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#acknowledgements>
+
+
+These outlines are based on the authoritative Terms and Conditions are found <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use>
+
+
+If you do not accept the Terms & Conditions you are not permitted to access or use the KG to search for, to submit, to post, or to download any materials found there-in.
diff --git a/deploy/datasets/query.js b/deploy/datasets/query.js
index 1516c3e4559d579e2360e9c9b7374af30c8eb093..d243a34c8794a207aeba72e334234120fd81c27a 100644
--- a/deploy/datasets/query.js
+++ b/deploy/datasets/query.js
@@ -5,8 +5,7 @@ const path = require('path')
 const archiver = require('archiver')
 const { commonSenseDsFilter } = require('./supplements/commonSense')
 const { getPreviewFile, hasPreview } = require('./supplements/previewFile')
-
-const kgQueryUtil = require('./../auth/util')
+const { init: kgQueryUtilInit, getUserKGRequestParam } = require('./util')
 
 let cachedData = null
 let otherQueryResult = null
@@ -29,11 +28,6 @@ const getKgQuerySingleDatasetUrl = ({ kgId }) => {
   return _newUrl
 }
 
-const timeout = process.env.TIMEOUT || 5000
-const STORAGE_PATH = process.env.STORAGE_PATH || path.join(__dirname, 'data')
-
-let getPublicAccessToken
-
 const fetchDatasetFromKg = async ({ user } = {}) => {
 
   const { releasedOnly, option } = await getUserKGRequestParam({ user })
@@ -220,197 +214,87 @@ const filter = (datasets = [], { templateName, parcellationName }) => datasets
 /**
  * on init, populate the cached data
  */
-exports.init = async () => {
-  const { getPublicAccessToken: getPublic } = await kgQueryUtil()
-  getPublicAccessToken = getPublic
+const init = async () => {
+  await kgQueryUtilInit()
   return await getPublicDs()
 }
 
-exports.getDatasets = ({ templateName, parcellationName, user }) => getDs({ user })
+const getDatasets = ({ templateName, parcellationName, user }) => getDs({ user })
   .then(json => filter(json, { templateName, parcellationName }))
 
-exports.getPreview = ({ datasetName, templateSelected }) => getPreviewFile({ datasetName, templateSelected })
-
-/**
- * TODO
- * change to real spatial query
- */
-const cachedMap = new Map()
+const getPreview = ({ datasetName, templateSelected }) => getPreviewFile({ datasetName, templateSelected })
 
-/**
- * TODO change to URL constructor to improve readability
- */
-const spatialQuery = 'https://kg.humanbrainproject.eu/query/neuroglancer/seeg/coordinate/v1.0.0/spatialWithCoordinatesNG/instances?vocab=https%3A%2F%2Fschema.hbp.eu%2FmyQuery%2F'
-
-const getXformFn = (templateSpace) => {
-  const _ = {}
-  switch(templateSpace){
-    case 'Waxholm Space rat brain atlas v.2.0': 
-      _['nmToVoxel'] = transformWaxholmV2NmToVoxel
-      _['voxelToNm'] = transformWaxholmV2VoxelToNm
-      break;
-    default: {
-      _['nmToVoxel'] = defaultXform
-      _['voxelToNm'] = defaultXform
-    }
-  }
-  return _
-}
-
-const getSpatialSearcParam = ({ templateName, queryArg }) => {
-  let kgSpaceName
-  const { nmToVoxel } = getXformFn(templateName)
-
-  const coordsString = queryArg.split('__');
-  const boundingBoxCorners = coordsString.map(coordString => coordString.split('_'))
-  const bbInVoxelSpace = boundingBoxCorners.map(nmToVoxel)
-
-  switch (templateName){
-    case 'Waxholm Space rat brain atlas v.2.0':
-      kgSpaceName = 'waxholmV2'
-      break;
-    default: 
-      kgSpaceName = templateName
-  }
-  return {
-    boundingBox: `${kgSpaceName}:${bbInVoxelSpace.map(v => v.join(',')).join(',')}`
-  }
+const getDatasetFromId = async ({ user, kgId, returnAsStream = false }) => {
+  const { option, releasedOnly } = await getUserKGRequestParam({ user })
+  const _url = getKgQuerySingleDatasetUrl({ kgId })
+  if (releasedOnly) _url.searchParams.set('databaseScope', 'RELEASED')
+  if (returnAsStream) return request(_url, option)
+  else return new Promise((resolve, reject) => {
+    request(_url, option, (err, resp, body) => {
+      if (err) return reject(err)
+      if (resp.statusCode >= 400) return reject(resp.statusCode)
+      return resolve(JSON.parse(body))
+    })
+  })
 }
 
-const fetchSpatialDataFromKg = async ({ templateName, queryGeometry, queryArg, user }) => {
+const renderPublication = ({ name, cite, doi }) => `${name}
+  ${cite}
+  https://doi.org/${doi}
+`
 
-  const { releasedOnly, option } = await getUserKGRequestParam({ user })
+const prepareDatasetMetadata = ({ name, kgReference, contributors, publications }) => {
 
-  const _ = getSpatialSearcParam({ templateName, queryGeometry, queryArg })
-  const url = require('url')
-  const search = new url.URLSearchParams()
-  
-  for (let key in _) {
-    search.append(key, _[key])  
-  }
-  if (releasedOnly) search.append('databaseScope', 'RELEASED')
-  
-  const _url = `${spatialQuery}&${search.toString()}`
-  return await new Promise((resolve, reject) => {
-    request(_url, option, (err, resp, body) => {
-      if (err)
-        return reject(err)
-      if (resp.statusCode >= 400) {
-        return reject(resp.statusCode)
-      }
-      const json = JSON.parse(body)
+  return `${name}
 
-      const { voxelToNm } = getXformFn(templateName)
-
-      const _ = json.results.map(({ name, coordinates, dataset}) => {
-        return {
-          name,
-          templateSpace: templateName,
-          dataset: dataset.map(ds => ds = {name: ds.name, externalLink: 'https://kg.humanbrainproject.eu/instances/Dataset/' + ds.identifier}),
-          geometry: {
-            type: 'point',
-            space: 'real',
-            position: voxelToNm([
-              coordinates[0].x,
-              coordinates[0].y,
-              coordinates[0].z
-            ])
-          }
-        }
-      })
+Contributors: ${contributors.join(', ')}
 
-      return resolve(_)
-    })
-  })
+Publications:
+${publications.map(renderPublication).join('\n')}
+`
 }
 
-exports.getSpatialDatasets = async ({ templateName, queryGeometry, queryArg, user }) => {
-  /**
-   * Local data can be injected here
-   */
-  return await fetchSpatialDataFromKg({ templateName, queryGeometry, queryArg, user })
-}
+let kgtos
 
-let publicAccessToken
+fs.readFile(path.join(__dirname, 'kgtos.md') , 'utf-8', (err, data) => {
+  if (err) console.warn(`reading kgtos Error`, err)
+  kgtos = data
+})
 
-async function getUserKGRequestParam({ user }) {
-    /**
-     * n.b. ACCESS_TOKEN env var is usually only set during dev
-     */
-    const accessToken = (user && user.tokenset && user.tokenset.access_token) || process.env.ACCESS_TOKEN
-    const releasedOnly = !accessToken
-    if (!accessToken && !publicAccessToken && getPublicAccessToken) {
-        publicAccessToken = await getPublicAccessToken()
-    }
-    const option = accessToken || publicAccessToken
-        ? {
-            auth: {
-                'bearer': accessToken || publicAccessToken || process.env.ACCESS_TOKEN
-            }
-        }
-        : {}
+const getTos = () => kgtos
 
-    return {
-      option,
-      releasedOnly,
-      token: accessToken || publicAccessToken
-    }
-}
+const getDatasetFileAsZip = async ({ user, kgId } = {}) => {
+  if (!kgId) {
+    throw new Error('kgId must be defined')
+  }
 
-/**
- * perhaps export the xform fns into a different module
- * ideally, in the future, KG can handle xform of voxel to nm
- */
-const transformWaxholmV2NmToVoxel = (coord) => {
-  /**
-   * as waxholm is already in RAS, does not need to swap axis
-   */
+  const dataset = await getDatasetFromId({ user, kgId })
+  const { files, name: datasetName } = dataset
+  const zip = archiver('zip')
 
   /**
-   * atlas viewer applies translation (below in nm) in order to center the brain
-   * query already translates nm to mm, so the unit of transl should be [mm, mm, mm]
+   * append citation information
    */
-  const transl = [-9550781, -24355468, -9707031].map(v => v / 1e6)
+  zip.append(prepareDatasetMetadata(dataset), {
+    name: `${datasetName}.txt`
+  })
 
   /**
-   * mm/voxel
+   * append kg citation policy
    */
-  const voxelDim = [0.0390625, 0.0390625, 0.0390625]
-  return coord.map((v, idx) => (v - transl[idx]) / voxelDim[idx])
-}
-
-const transformWaxholmV2VoxelToNm = (coord) => {
-  const transl = [-9550781, -24355468, -9707031].map(v => v / 1e6)
-  const voxelDim = [0.0390625, 0.0390625, 0.0390625]
-  return coord.map((v, idx) => (v * voxelDim[idx]) + transl[idx])
-}
 
-const defaultXform = (coord) => coord
-const getDatasetFromId = async ({ user, kgId, returnAsStream = false }) => {
-  const { option, releasedOnly } = await getUserKGRequestParam({ user })
-  const _url = getKgQuerySingleDatasetUrl({ kgId })
-  if (releasedOnly) _url.searchParams.set('databaseScope', 'RELEASED')
-  if (returnAsStream) return request(_url, option)
-  else return new Promise((resolve, reject) => {
-    request(_url, option, (err, resp, body) => {
-      if (err) return reject(err)
-      if (resp.statusCode >= 400) return reject(resp.statusCode)
-      return resolve(JSON.parse(body))
-    })
+  zip.append(getTos(), {
+    name: `Terms Of Use.md`
   })
-}
 
-const getDatasetFileAsZip = async ({ user, kgId } = {}) => {
-  if (!kgId) {
-    throw new Error('kgId must be defined')
-  }
-
-  const result = await getDatasetFromId({ user, kgId })
-  const { files } = result
-  const zip = archiver('zip')
+  /**
+   * append all of the files
+   */
   for (let file of files) {
     const { name, absolutePath } = file
-    zip.append(request(absolutePath), { name })
+    zip.append(request(absolutePath), { 
+      name: path.join(datasetName, name)
+    })
   }
 
   zip.finalize()
@@ -418,5 +302,12 @@ const getDatasetFileAsZip = async ({ user, kgId } = {}) => {
   return zip
 }
 
-exports.getDatasetFromId = getDatasetFromId
-exports.getDatasetFileAsZip = getDatasetFileAsZip
+module.exports = {
+  getDatasetFromId,
+  getDatasetFileAsZip,
+  init,
+  getDatasets,
+  getPreview,
+  getTos
+}
+
diff --git a/deploy/datasets/spatialQuery.js b/deploy/datasets/spatialQuery.js
new file mode 100644
index 0000000000000000000000000000000000000000..51409c90f272c0e0f7ee8677a531f2af4995448f
--- /dev/null
+++ b/deploy/datasets/spatialQuery.js
@@ -0,0 +1,105 @@
+const url = require('url')
+const request = require('request')
+const { getUserKGRequestParam } = require('./util')
+const { transformWaxholmV2NmToVoxel, transformWaxholmV2VoxelToNm } = require('./spatialXform/waxholmRat')
+
+/**
+ * TODO change to URL constructor to improve readability
+ */
+const spatialQuery = 'https://kg.humanbrainproject.eu/query/neuroglancer/seeg/coordinate/v1.0.0/spatialWithCoordinatesNG/instances?vocab=https%3A%2F%2Fschema.hbp.eu%2FmyQuery%2F'
+
+const defaultXform = (coord) => coord
+
+const getXformFn = (templateSpace) => {
+  const _ = {}
+  switch(templateSpace){
+    case 'Waxholm Space rat brain atlas v.2.0': 
+      _['nmToVoxel'] = transformWaxholmV2NmToVoxel
+      _['voxelToNm'] = transformWaxholmV2VoxelToNm
+      break;
+    default: {
+      _['nmToVoxel'] = defaultXform
+      _['voxelToNm'] = defaultXform
+    }
+  }
+  return _
+}
+
+const getSpatialSearcParam = ({ templateName, queryArg }) => {
+  let kgSpaceName
+  const { nmToVoxel } = getXformFn(templateName)
+
+  const coordsString = queryArg.split('__');
+  const boundingBoxCorners = coordsString.map(coordString => coordString.split('_'))
+  const bbInVoxelSpace = boundingBoxCorners.map(nmToVoxel)
+
+  switch (templateName){
+    case 'Waxholm Space rat brain atlas v.2.0':
+      kgSpaceName = 'waxholmV2'
+      break;
+    default: 
+      kgSpaceName = templateName
+  }
+  return {
+    boundingBox: `${kgSpaceName}:${bbInVoxelSpace.map(v => v.join(',')).join(',')}`
+  }
+}
+
+
+const fetchSpatialDataFromKg = async ({ templateName, queryGeometry, queryArg, user }) => {
+
+  const { releasedOnly, option } = await getUserKGRequestParam({ user })
+
+  const _ = getSpatialSearcParam({ templateName, queryGeometry, queryArg })
+  const search = new url.URLSearchParams()
+  
+  for (let key in _) {
+    search.append(key, _[key])  
+  }
+  if (releasedOnly) search.append('databaseScope', 'RELEASED')
+  
+  const _url = `${spatialQuery}&${search.toString()}`
+  return await new Promise((resolve, reject) => {
+    request(_url, option, (err, resp, body) => {
+      if (err)
+        return reject(err)
+      if (resp.statusCode >= 400) {
+        return reject(resp.statusCode)
+      }
+      const json = JSON.parse(body)
+
+      const { voxelToNm } = getXformFn(templateName)
+
+      const _ = json.results.map(({ name, coordinates, dataset}) => {
+        return {
+          name,
+          templateSpace: templateName,
+          dataset: dataset.map(ds => ds = {name: ds.name, externalLink: 'https://kg.humanbrainproject.eu/instances/Dataset/' + ds.identifier}),
+          geometry: {
+            type: 'point',
+            space: 'real',
+            position: voxelToNm([
+              coordinates[0].x,
+              coordinates[0].y,
+              coordinates[0].z
+            ])
+          }
+        }
+      })
+
+      return resolve(_)
+    })
+  })
+}
+
+
+const getSpatialDatasets = async ({ templateName, queryGeometry, queryArg, user }) => {
+  /**
+   * Local data can be injected here
+   */
+  return await fetchSpatialDataFromKg({ templateName, queryGeometry, queryArg, user })
+}
+
+module.exports = {
+  getSpatialDatasets
+}
\ No newline at end of file
diff --git a/deploy/datasets/spatialRouter.js b/deploy/datasets/spatialRouter.js
index ef6574fc6ce86a78ddcc541eb475a8391032198b..5bfb78a2c241c4d134628a11dce2914842d9b430 100644
--- a/deploy/datasets/spatialRouter.js
+++ b/deploy/datasets/spatialRouter.js
@@ -1,5 +1,5 @@
 const router = require('express').Router()
-const { getSpatialDatasets } = require('./query')
+const { getSpatialDatasets } = require('./spatialQuery')
 
 const badRequestString = `spatialSearch endpoint uses param as follows:
 
diff --git a/deploy/datasets/spatialXform/waxholmRat.js b/deploy/datasets/spatialXform/waxholmRat.js
new file mode 100644
index 0000000000000000000000000000000000000000..480013b5b98310c8d77c6f117d550d4126737db0
--- /dev/null
+++ b/deploy/datasets/spatialXform/waxholmRat.js
@@ -0,0 +1,33 @@
+
+/**
+ * perhaps export the xform fns into a different module
+ * ideally, in the future, KG can handle xform of voxel to nm
+ */
+const transformWaxholmV2NmToVoxel = (coord) => {
+  /**
+   * as waxholm is already in RAS, does not need to swap axis
+   */
+
+  /**
+   * atlas viewer applies translation (below in nm) in order to center the brain
+   * query already translates nm to mm, so the unit of transl should be [mm, mm, mm]
+   */
+  const transl = [-9550781, -24355468, -9707031].map(v => v / 1e6)
+
+  /**
+   * mm/voxel
+   */
+  const voxelDim = [0.0390625, 0.0390625, 0.0390625]
+  return coord.map((v, idx) => (v - transl[idx]) / voxelDim[idx])
+}
+
+const transformWaxholmV2VoxelToNm = (coord) => {
+  const transl = [-9550781, -24355468, -9707031].map(v => v / 1e6)
+  const voxelDim = [0.0390625, 0.0390625, 0.0390625]
+  return coord.map((v, idx) => (v * voxelDim[idx]) + transl[idx])
+}
+
+module.exports = {
+  transformWaxholmV2NmToVoxel,
+  transformWaxholmV2VoxelToNm
+}
\ No newline at end of file
diff --git a/deploy/datasets/util.js b/deploy/datasets/util.js
new file mode 100644
index 0000000000000000000000000000000000000000..93b8c8a4cee91e1eb960aecccd71723d749798c4
--- /dev/null
+++ b/deploy/datasets/util.js
@@ -0,0 +1,36 @@
+const kgQueryUtil = require('./../auth/util')
+
+let getPublicAccessToken, publicAccessToken
+
+const getUserKGRequestParam = async ({ user }) => {
+  /**
+   * n.b. ACCESS_TOKEN env var is usually only set during dev
+   */
+  const accessToken = (user && user.tokenset && user.tokenset.access_token) || process.env.ACCESS_TOKEN
+  const releasedOnly = !accessToken
+  if (!accessToken && !publicAccessToken && getPublicAccessToken) {
+    publicAccessToken = await getPublicAccessToken()
+  }
+  const option = accessToken || publicAccessToken
+    ? {
+        auth: { bearer: accessToken || publicAccessToken }
+      }
+    : {}
+
+  return {
+    option,
+    releasedOnly,
+    token: accessToken || publicAccessToken
+  }
+}
+
+const init = async () => {
+  if (getPublicAccessToken) return
+  const { getPublicAccessToken: getPublic } = await kgQueryUtil()
+  getPublicAccessToken = getPublic
+}
+
+module.exports = {
+  init,
+  getUserKGRequestParam
+}
\ No newline at end of file
diff --git a/deploy/package.json b/deploy/package.json
index 2d33823ef6a544b335a6a6edfc2096456b09419c..f65ac9c5183114edabe9ca040ab05f087de9dcfc 100644
--- a/deploy/package.json
+++ b/deploy/package.json
@@ -18,7 +18,6 @@
     "express": "^4.16.4",
     "express-session": "^1.15.6",
     "helmet-csp": "^2.8.0",
-    "jszip": "^3.2.1",
     "jwt-decode": "^2.2.0",
     "memorystore": "^1.6.1",
     "openid-client": "^2.4.5",
diff --git a/src/atlasViewer/atlasViewer.constantService.service.ts b/src/atlasViewer/atlasViewer.constantService.service.ts
index bb1d4183e203ac97f3c9d54c52a49d18e5e660e1..7b9026aec1fed0984ebc59361fdc0c8be26423d1 100644
--- a/src/atlasViewer/atlasViewer.constantService.service.ts
+++ b/src/atlasViewer/atlasViewer.constantService.service.ts
@@ -4,6 +4,7 @@ import { ViewerStateInterface } from "../services/stateStore.service";
 import { Subject, Observable } from "rxjs";
 import { ACTION_TYPES, ViewerConfiguration } from 'src/services/state/viewerConfig.store'
 import { map, shareReplay, filter } from "rxjs/operators";
+import { MatSnackBar } from "@angular/material";
 
 export const CM_THRESHOLD = `0.05`
 export const CM_MATLAB_JET = `float r;if( x < 0.7 ){r = 4.0 * x - 1.5;} else {r = -4.0 * x + 4.5;}float g;if (x < 0.5) {g = 4.0 * x - 0.5;} else {g = -4.0 * x + 3.5;}float b;if (x < 0.3) {b = 4.0 * x + 0.5;} else {b = -4.0 * x + 2.5;}float a = 1.0;`
@@ -239,7 +240,10 @@ Interactive atlas viewer requires **webgl2.0**, and the \`EXT_color_buffer_float
   private supportEmailAddress = `x.gui@fz-juelich.de`
   private repoUrl = `https://github.com/HumanBrainProject/interactive-viewer`
 
-  constructor(private store : Store<ViewerStateInterface>){
+  constructor(
+    private store : Store<ViewerStateInterface>,
+    private snackbar: MatSnackBar  
+  ){
 
     const ua = window && window.navigator && window.navigator.userAgent
       ? window.navigator.userAgent
@@ -270,11 +274,9 @@ Interactive atlas viewer requires **webgl2.0**, and the \`EXT_color_buffer_float
   }
 
   catchError(e: Error | string){
-    /**
-     * DO NOT REMOVE
-     * general catch all & reflect in UI
-     */
-    console.warn(e)
+    this.snackbar.open(e.toString(), 'Dismiss', {
+      duration: 3000
+    })
   }
 }
 
diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html
index d22eae5adfd3f8b045326761fc9d7bc2033e233b..412c75ef2043698334961d251208789f4e2f2277 100644
--- a/src/atlasViewer/atlasViewer.template.html
+++ b/src/atlasViewer/atlasViewer.template.html
@@ -169,6 +169,7 @@
   
       </div>
       
+      <!-- TODO deprecate -->
       <!-- TODO move to nehuba overlay container -->
       <panel-component class="shadow" fixedMouseContextualContainerDirective #rClContextMenu>
         <div heading>
diff --git a/src/ui/databrowserModule/databrowser.service.ts b/src/ui/databrowserModule/databrowser.service.ts
index 65f9a241b4e1640b4e6cdffc8938a83e84bf38c8..2d6a0edf442ada66de691a7ba8061067f426a04d 100644
--- a/src/ui/databrowserModule/databrowser.service.ts
+++ b/src/ui/databrowserModule/databrowser.service.ts
@@ -14,6 +14,7 @@ import { WidgetUnit } from "src/atlasViewer/widgetUnit/widgetUnit.component";
 import { SHOW_KG_TOS } from "src/services/state/uiState.store";
 import { regionFlattener } from "src/util/regionFlattener";
 import { DATASETS_ACTIONS_TYPES } from "src/services/state/dataStore.store";
+import { HttpClient } from "@angular/common/http";
 
 const noMethodDisplayName = 'No methods described'
 
@@ -46,8 +47,8 @@ function generateToken() {
 })
 export class DatabrowserService implements OnDestroy{
 
+  public kgTos$: Observable<any>
   public favedDataentries$: Observable<DataEntry[]>
-
   public darktheme: boolean = false
 
   public instantiatedWidgetUnits: WidgetUnit[] = []
@@ -80,8 +81,18 @@ export class DatabrowserService implements OnDestroy{
   constructor(
     private workerService: AtlasWorkerService,
     private constantService: AtlasViewerConstantsServices,
-    private store: Store<ViewerConfiguration>
+    private store: Store<ViewerConfiguration>,
+    private http: HttpClient
   ){
+    this.kgTos$ = this.http.get(`${this.constantService.backendUrl}datasets/tos`, {
+      responseType: 'text'
+    }).pipe(
+      catchError((err,obs) => {
+        console.warn(`fetching kgTos error`, err)
+        return of(null)
+      }),
+      shareReplay(1)
+    )
 
     this.favedDataentries$ = this.store.pipe(
       select('dataStore'),
diff --git a/src/ui/databrowserModule/databrowser/databrowser.template.html b/src/ui/databrowserModule/databrowser/databrowser.template.html
index 060a49ad118bbaea29a100e86db11bc0b128d144..9df68602e046068f4758d8445076d6b10ac55040 100644
--- a/src/ui/databrowserModule/databrowser/databrowser.template.html
+++ b/src/ui/databrowserModule/databrowser/databrowser.template.html
@@ -27,6 +27,7 @@
       </readmore-component>
       
       <!-- modality picker -->
+      <!-- TODO use material for popover, then remove popover module and ngx/bootstrap -->
       <div>
         <span
           placement="right"
diff --git a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.template.html b/src/ui/databrowserModule/fileviewer/dedicated/dedicated.template.html
index 3517468edf64042507f0768dead716797805adfe..2f05732a5ca04d28adda8776a13abafcd009aa89 100644
--- a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.template.html
+++ b/src/ui/databrowserModule/fileviewer/dedicated/dedicated.template.html
@@ -4,7 +4,7 @@
 
 <a 
   href="#"
-  [tooltip]="tooltipText"
+  [matTooltip]="tooltipText"
   container="body"
   class="btn btn-sm rounded-circle"
   [ngClass]="isShowing ? 'btn-primary' : 'btn-outline-secondary'"
diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.component.ts b/src/ui/databrowserModule/singleDataset/singleDataset.component.ts
index df2e0e71bf37141fa63e464f9b71b012c58486f2..a62636ede17fed686626debeea225bfa54790bdd 100644
--- a/src/ui/databrowserModule/singleDataset/singleDataset.component.ts
+++ b/src/ui/databrowserModule/singleDataset/singleDataset.component.ts
@@ -1,6 +1,7 @@
 import { Component, Input, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from "@angular/core";
 import { KgSingleDatasetService } from "../kgSingleDatasetService.service";
 import { Publication, File } from 'src/services/state/dataStore.store'
+import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service";
 
 @Component({
   selector: 'single-dataset-view',
@@ -41,7 +42,8 @@ export class SingleDatasetView implements OnInit {
 
   constructor(
     private singleDatasetService: KgSingleDatasetService,
-    private cdr: ChangeDetectorRef
+    private cdr: ChangeDetectorRef,
+    private constantService: AtlasViewerConstantsServices
   ){}
 
   ngOnInit() {
@@ -105,8 +107,8 @@ export class SingleDatasetView implements OnInit {
     this.singleDatasetService.downloadZipFromKg({
       kgId,
       kgSchema
-    })
-      .catch(console.error)
+    }, this.name)
+      .catch(err => this.constantService.catchError(err))
       .finally(() => this.downloadInProgress = false)
   }
 }
diff --git a/src/ui/kgtos/kgtos.component.ts b/src/ui/kgtos/kgtos.component.ts
index d191860a02a4e316abc662944cc02be068c948ff..53edea2a8ec08f93674f856bebe07b4c23c48c0c 100644
--- a/src/ui/kgtos/kgtos.component.ts
+++ b/src/ui/kgtos/kgtos.component.ts
@@ -1,4 +1,6 @@
 import { Component } from "@angular/core";
+import { DatabrowserService } from "../databrowserModule/databrowser.service";
+import { Observable } from "rxjs";
 
 @Component({
   selector: 'kgtos-component',
@@ -10,4 +12,11 @@ import { Component } from "@angular/core";
 
 export class KGToS{
 
+  public kgTos$: Observable<string>
+
+  constructor(
+    private dbService: DatabrowserService
+  ){
+    this.kgTos$ = this.dbService.kgTos$
+  }
 }
\ No newline at end of file
diff --git a/src/ui/kgtos/kgtos.template.html b/src/ui/kgtos/kgtos.template.html
index 5f8fe4eb6240d7227cec618be5efa43e478cceaf..f0eee2bb6cba0905078ea3331eeb3f3faac9ff83 100644
--- a/src/ui/kgtos/kgtos.template.html
+++ b/src/ui/kgtos/kgtos.template.html
@@ -1,20 +1,27 @@
-<div>
-  <p>
-    The interactive viewer queries HBP Knowledge Graph Data Platform ("KG") for published datasets.
-  </p>
-  <p>
-    Access to the data and metadata provided through KG requires that you cite and acknowledge said data and metadata according to the Terms and Conditions of the Platform.
-  </p>
-  <p>
-    Citation requirements are outlined <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#citations">here</a>.
-  </p>
-  <p>
-    Acknowledgement requirements are outlined <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#acknowledgements">here</a>.
-  </p>
-  <p>
-    These outlines are based on the authoritative Terms and Conditions are found <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use">here</a>.
-  </p>
-  <p>
-    If you do not accept the Terms & Conditions you are not permitted to access or use the KG to search for, to submit, to post, or to download any materials found there-in.
-  </p>
-</div>
\ No newline at end of file
+<ng-container *ngIf="kgTos$ | async as kgTos; else backup">
+  <markdown-dom [markdown]="kgTos">
+  </markdown-dom>
+</ng-container>
+
+<ng-template #backup>
+  <div>
+    <p>
+      The interactive viewer queries HBP Knowledge Graph Data Platform ("KG") for published datasets.
+    </p>
+    <p>
+      Access to the data and metadata provided through KG requires that you cite and acknowledge said data and metadata according to the Terms and Conditions of the Platform.
+    </p>
+    <p>
+      Citation requirements are outlined <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#citations">here</a>.
+    </p>
+    <p>
+      Acknowledgement requirements are outlined <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#acknowledgements">here</a>.
+    </p>
+    <p>
+      These outlines are based on the authoritative Terms and Conditions are found <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use">here</a>.
+    </p>
+    <p>
+      If you do not accept the Terms & Conditions you are not permitted to access or use the KG to search for, to submit, to post, or to download any materials found there-in.
+    </p>
+  </div>
+</ng-template>
\ No newline at end of file
diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts
index 3112fa91a944a0766c871af90034af33c989a715..049cf8b121318723b2718d938ab3899280e292a7 100644
--- a/src/ui/nehubaContainer/nehubaContainer.component.ts
+++ b/src/ui/nehubaContainer/nehubaContainer.component.ts
@@ -1253,7 +1253,9 @@ export class NehubaContainer implements OnInit, OnDestroy{
   downloadDs(event: MouseEvent, ds: DataEntry, downloadBtn: MatButton){
     downloadBtn.disabled = true
     const id = getIdFromDataEntry(ds)
-    this.kgSingleDataset.downloadZipFromKg({kgId: id})
+    const { name } = ds
+    this.kgSingleDataset.downloadZipFromKg({kgId: id}, name)
+      .catch(err => this.constantService.catchError(err))
       .finally(() => downloadBtn.disabled = false)
   }
 }
diff --git a/src/ui/pluginBanner/pluginBanner.template.html b/src/ui/pluginBanner/pluginBanner.template.html
index d88e991af9c9dfbe02335b789c60c398ba98d392..b79ca60cccb56c912f2fb0bb00ea5bbbd52f0c02 100644
--- a/src/ui/pluginBanner/pluginBanner.template.html
+++ b/src/ui/pluginBanner/pluginBanner.template.html
@@ -1,6 +1,6 @@
 <div 
   placement = "bottom"
-  [tooltip] = "pluginEnabledFlag ? null : 'coming soon'"
+  [matTooltip] = "pluginEnabledFlag ? null : 'coming soon'"
   *ngFor = "let plugin of pluginServices.fetchedPluginManifests"
   (click) = "clickPlugin(plugin)"
   [ngClass] = "{'btn-disabled' : !pluginEnabledFlag}"
diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts
index ae129d6220403450c0ebc00cff530c85e1cf842c..78463d9efc5eac6af2a03001284b9f455397409a 100644
--- a/src/ui/ui.module.ts
+++ b/src/ui/ui.module.ts
@@ -18,7 +18,6 @@ import { SafeStylePipe } from "../util/pipes/safeStyle.pipe";
 import { PluginBannerUI } from "./pluginBanner/pluginBanner.component";
 import { CitationsContainer } from "./citation/citations.component";
 import { LayerBrowser, LockedLayerBtnClsPipe } from "./layerbrowser/layerbrowser.component";
-import { TooltipModule } from "ngx-bootstrap/tooltip";
 import { KgEntryViewer } from "./kgEntryViewer/kgentry.component";
 import { SubjectViewer } from "./kgEntryViewer/subjectViewer/subjectViewer.component";
 import { GetLayerNameFromDatasets } from "../util/pipes/getLayerNamePipe.pipe";
@@ -34,7 +33,6 @@ import { ShowToastDirective } from "../util/directives/showToast.directive";
 import { HelpComponent } from "./help/help.component";
 import { ConfigComponent } from './config/config.component'
 import { FlatmapArrayPipe } from "src/util/pipes/flatMapArray.pipe";
-import { PopoverModule } from 'ngx-bootstrap/popover'
 import { DatabrowserModule } from "./databrowserModule/databrowser.module";
 import { SigninBanner } from "./signinBanner/signinBanner.components";
 import { SigninModal } from "./signinModal/signinModal.component";
@@ -75,9 +73,6 @@ import { KgSearchBtnColorPipe } from "src/util/pipes/kgSearchBtnColor.pipe";
     UtilModule,
     ScrollingModule,
     AngularMaterialModule,
-
-    PopoverModule.forRoot(),
-    TooltipModule.forRoot()
   ],
   declarations : [
     NehubaContainer,