diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 98ff32775c62db929d9bcd1087e89d60eaee393c..f084ac2010fe12bb85d640c634fdda21594c2c04 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -37,10 +37,10 @@ jobs:
         node-version: 16.x
     - run: npm i
     - run: |
-        if [[ "$GITHUB_REF" = *hotfix* ]]
+        if [[ "$GITHUB_REF" = *hotfix* ]] || [[ "$GITHUB_REF" = refs/heads/staging ]]
         then
-          export BS_REST_URL=https://siibra-api-rc.apps.hbp.eu/v2_0
-          echo 'export const environment = { "BS_REST_URL": "https://siibra-api-rc.apps.hbp.eu/v2_0" }' > src/environments/environment.common.ts
+          export SIIBRA_API_ENDPOINTS=https://siibra-api-rc.apps.hbp.eu/v2_0
+          node src/environments/parseEnv.js ./environment.ts
         fi
         npm run test-ci
 
diff --git a/.github/workflows/docker_img.yml b/.github/workflows/docker_img.yml
index 47734fc27ba6540ee111646142553af53a365798..a25d30c92fe0636dc593c859e224eead06025d52 100644
--- a/.github/workflows/docker_img.yml
+++ b/.github/workflows/docker_img.yml
@@ -18,7 +18,7 @@ jobs:
       PRODUCTION: 'true'
       DOCKER_REGISTRY: 'docker-registry.ebrains.eu/siibra/'
 
-      SIIBRA_API_STABLE: 'https://siibra-api-stable.apps.hbp.eu/v2_0'
+      SIIBRA_API_STABLE: 'https://siibra-api-stable.apps.hbp.eu/v2_0,https://siibra-api-stable-ns.apps.hbp.eu/v2_0,https://siibra-api-stable.apps.jsc.hbp.eu/v2_0'
       SIIBRA_API_RC: 'https://siibra-api-rc.apps.hbp.eu/v2_0'
       SIIBRA_API_LATEST: 'https://siibra-api-latest.apps-dev.hbp.eu/v2_0'
 
@@ -37,19 +37,19 @@ jobs:
         if [[ "$GITHUB_REF" == 'refs/heads/master' ]]
         then
           echo "Either master, using prod env..."
-          echo "BS_REST_URL=${{ env.SIIBRA_API_STABLE }}" >> $GITHUB_ENV
+          echo "SIIBRA_API_ENDPOINTS=${{ env.SIIBRA_API_STABLE }}" >> $GITHUB_ENV
         elif [[ "$GITHUB_REF" == 'refs/heads/staging' ]]
         then
           echo "Either staging, using staging env..."
-          echo "BS_REST_URL=${{ env.SIIBRA_API_RC }}" >> $GITHUB_ENV
+          echo "SIIBRA_API_ENDPOINTS=${{ env.SIIBRA_API_RC }}" >> $GITHUB_ENV
         else
           if [[ "$GITHUB_REF" == *hotfix* ]]
           then
             echo "Hotfix branch, using prod env..."
-            echo "BS_REST_URL=${{ env.SIIBRA_API_RC }}" >> $GITHUB_ENV
+            echo "SIIBRA_API_ENDPOINTS=${{ env.SIIBRA_API_RC }}" >> $GITHUB_ENV
           else
             echo "Using dev env..."
-            echo "BS_REST_URL=${{ env.SIIBRA_API_LATEST }}" >> $GITHUB_ENV
+            echo "SIIBRA_API_ENDPOINTS=${{ env.SIIBRA_API_LATEST }}" >> $GITHUB_ENV
           fi
         fi
 
@@ -78,7 +78,7 @@ jobs:
           --build-arg VERSION=$VERSION \
           --build-arg MATOMO_URL=$MATOMO_URL \
           --build-arg MATOMO_ID=$MATOMO_ID \
-          --build-arg BS_REST_URL=$BS_REST_URL \
+          --build-arg SIIBRA_API_ENDPOINTS=$SIIBRA_API_ENDPOINTS \
           --build-arg EXPERIMENTAL_FEATURE_FLAG=$EXPERIMENTAL_FEATURE_FLAG \
           -t $DOCKER_BUILT_TAG \
           .
diff --git a/Dockerfile b/Dockerfile
index 17e735232f80890da6b5b2b65db1b33c152c9765..8ad7624403cf18aa021e133e2926d1fc1f616b04 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,8 +3,8 @@ FROM node:14 as builder
 ARG BACKEND_URL
 ENV BACKEND_URL=${BACKEND_URL}
 
-ARG BS_REST_URL
-ENV BS_REST_URL=${BS_REST_URL:-https://siibra-api-stable.apps.hbp.eu/v1_0}
+ARG SIIBRA_API_ENDPOINTS
+ENV SIIBRA_API_ENDPOINTS=${SIIBRA_API_ENDPOINTS:-https://siibra-api-stable.apps.hbp.eu/v2_0,https://siibra-api-stable-ns.apps.hbp.eu/v2_0,https://siibra-api-stable.apps.jsc.hbp.eu/v2_0}
 
 ARG STRICT_LOCAL
 ENV STRICT_LOCAL=${STRICT_LOCAL:-false}
@@ -39,6 +39,7 @@ RUN rm -rf ./node_modules
 
 RUN npm i
 RUN npm run build
+RUN node third_party/matomo/processMatomo.js
 RUN npm run build-storybook
 
 # gzipping container
diff --git a/build_env.md b/build_env.md
index 1c29ea98614c3a40dbe91d7c620f9e6a5356b4e8..fa8ed9e3951cdabd2cfa2f26bd46c5904e74af14 100644
--- a/build_env.md
+++ b/build_env.md
@@ -7,7 +7,8 @@ As interactive atlas viewer uses [webpack define plugin](https://webpack.js.org/
 | `VERSION` | printed in console on viewer startup | `GIT_HASH` \|\| unspecificed hash | v2.2.2 |
 | `PRODUCTION` | if the build is for production, toggles optimisations such as minification | `undefined` | true |
 | `BACKEND_URL` | backend that the viewer calls to fetch available template spaces, parcellations, plugins, datasets | `null` | https://interactive-viewer.apps.hbp.eu/ |
-| `BS_REST_URL` | [siibra-api](https://github.com/FZJ-INM1-BDA/siibra-api) used to fetch different resources | https://siibra-api-stable.apps.hbp.eu/v1_0 |
+| ~~`BS_REST_URL`~~ _deprecated. use `SIIBRA_API_ENDPOINTS` instead_ | [siibra-api](https://github.com/FZJ-INM1-BDA/siibra-api) used to fetch different resources | `https://siibra-api-stable.apps.hbp.eu/v1_0` |
+| `SIIBRA_API_ENDPOINTS` | Comma separated endpoints of [siibra-api](https://github.com/FZJ-INM1-BDA/siibra-api) used to fetch different resources | `https://siibra-api-stable.apps.hbp.eu/v2_0,https://siibra-api-stable-ns.apps.hbp.eu/v2_0,https://siibra-api-stable.apps.jsc.hbp.eu/v2_0` |
 | `MATOMO_URL` | base url for matomo analytics | `null` | https://example.com/matomo/ |
 | `MATOMO_ID` | application id for matomo analytics | `null` | 6 |
 | `STRICT_LOCAL` | hides **Explore** and **Download** buttons. Useful for offline demo's | `false` | `true` |
diff --git a/docs/releases/v2.7.3.md b/docs/releases/v2.7.3.md
new file mode 100644
index 0000000000000000000000000000000000000000..ec24dc460b6f4bc81b0faab9a78fa5eaf29ff948
--- /dev/null
+++ b/docs/releases/v2.7.3.md
@@ -0,0 +1,13 @@
+# v2.7.3
+
+## Bugfix
+
+- fixed matomo visitor counting (broke since 2.7.0 release)
+- fixed sane url generation
+- fixed reset navigation buttons in navigation card
+
+## Under the hood
+
+- minor refactor of unused code
+- added mirrors to siibra-api
+- experimental support for drag and drop swc
diff --git a/e2e/checklist.md b/e2e/checklist.md
index eef89d804006fd65b2cfadb59cc3d687cb78f714..d03d59b1f8424872c3c88efefa15b01a3b600ecd 100644
--- a/e2e/checklist.md
+++ b/e2e/checklist.md
@@ -65,6 +65,9 @@
   - [ ] on hover, show correct region name(s)
   - [ ] whole mesh loads
 ## saneURL
+- [ ] saneurl generation functions properly
+  - [ ] try existing key (human), and get unavailable error
+  - [ ] try non existing key, and get available
 - [ ] [saneUrl](https://atlases.ebrains.eu/viewer-staging/saneUrl/bigbrainGreyWhite) redirects to big brain
 - [ ] [saneUrl](https://atlases.ebrains.eu/viewer-staging/saneUrl/julichbrain) redirects to julich brain (colin 27)
 - [ ] [saneUrl](https://atlases.ebrains.eu/viewer-staging/saneUrl/whs4) redirects to waxholm v4
diff --git a/mkdocs.yml b/mkdocs.yml
index 4d705ac0aabaec7503bcfab2794f46ea6a27d83b..645c2483972a50115c13db5bfeecb40e4489fc6d 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -33,6 +33,7 @@ nav:
     - Fetching datasets: 'advanced/datasets.md'
     - Display non-atlas volumes: 'advanced/otherVolumes.md'
   - Release notes:
+    - v2.7.3: 'releases/v2.7.3.md'
     - v2.7.2: 'releases/v2.7.2.md'
     - v2.7.1: 'releases/v2.7.1.md'
     - v2.7.0: 'releases/v2.7.0.md'
diff --git a/package.json b/package.json
index 82db76567fac6c5f0b472486c523ce38fa0b3a82..dd637a495824c81008750313065fd1866e01dadf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "interactive-viewer",
-  "version": "2.7.2",
+  "version": "2.7.3",
   "description": "siibra-explorer - explore brain atlases. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular",
   "scripts": {
     "lint": "eslint src --ext .ts",
diff --git a/src/atlasComponents/sapi/features/sapiFeature.ts b/src/atlasComponents/sapi/features/sapiFeature.ts
index 5abaa0040f6cb71046c70bf57e7a77a57f2794a8..8290da0cad9b4c8e057845258980ff5091579272 100644
--- a/src/atlasComponents/sapi/features/sapiFeature.ts
+++ b/src/atlasComponents/sapi/features/sapiFeature.ts
@@ -7,7 +7,7 @@ export class SAPIFeature {
   }
 
   public detail$ = this.sapi.httpGet<SapiFeatureModel>(
-    `${SAPI.bsEndpoint}/features/${this.id}`,
+    `${SAPI.BsEndpoint}/features/${this.id}`,
     this.opts
   )
 }
diff --git a/src/atlasComponents/sapi/module.ts b/src/atlasComponents/sapi/module.ts
index a64cc8bc817f05c801cde40d58a95d93c1d198a1..6d0757bb5e771b5bb18e9009bc9032f399fa0c38 100644
--- a/src/atlasComponents/sapi/module.ts
+++ b/src/atlasComponents/sapi/module.ts
@@ -1,4 +1,4 @@
-import { NgModule } from "@angular/core";
+import { APP_INITIALIZER, NgModule } from "@angular/core";
 import { SAPI } from "./sapi.service";
 import { CommonModule } from "@angular/common";
 import { HttpClientModule, HTTP_INTERCEPTORS } from "@angular/common/http";
@@ -21,6 +21,11 @@ import { MatSnackBarModule } from "@angular/material/snack-bar";
       provide: HTTP_INTERCEPTORS,
       useClass: PriorityHttpInterceptor,
       multi: true
+    },
+    {
+      provide: APP_INITIALIZER,
+      useValue: () => SAPI.SetBsEndPoint(),
+      multi: true
     }
   ]
 })
diff --git a/src/atlasComponents/sapi/sapi.service.spec.ts b/src/atlasComponents/sapi/sapi.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0448de0ccf6c7c2fcb3d5cd587e996db861a7bf2
--- /dev/null
+++ b/src/atlasComponents/sapi/sapi.service.spec.ts
@@ -0,0 +1,97 @@
+import { NEVER } from "rxjs"
+import * as env from "src/environments/environment"
+import { SAPI } from "./sapi.service"
+
+describe("> sapi.service.ts", () => {
+  describe("> SAPI", () => {
+    describe("#SetBsEndPoint", () => {
+      let fetchSpy: jasmine.Spy
+      let environmentSpy: jasmine.Spy
+
+      const endpt1 = 'http://foo-bar'
+      const endpt2 = 'http://buzz-bizz'
+
+      const atlas1 = 'foo'
+      const atlas2 = 'bar'
+
+      let originalBsEndpoint: string
+      beforeAll(() => {
+        originalBsEndpoint = SAPI.BsEndpoint
+      })
+
+      afterAll(() => {
+        SAPI.BsEndpoint = originalBsEndpoint
+      })
+      
+      beforeEach(() => {
+        fetchSpy = spyOn(window, 'fetch')
+        fetchSpy.and.callThrough()
+
+        environmentSpy = spyOnProperty(env, 'environment')
+        environmentSpy.and.returnValue({
+          SIIBRA_API_ENDPOINTS: `${endpt1},${endpt2}`
+        })
+      })
+
+      afterEach(() => {
+        fetchSpy.calls.reset()
+        environmentSpy.calls.reset()
+      })
+
+      describe("> first passes", () => {
+        beforeEach(() => {
+          const resp = new Response(JSON.stringify([atlas1]), { headers: { 'content-type': 'application/json' }, status: 200 })
+          fetchSpy.and.resolveTo(resp)
+        })
+        it("> should call fetch once", async () => {
+          await SAPI.SetBsEndPoint()
+          expect(fetchSpy).toHaveBeenCalledTimes(1)
+          expect(fetchSpy).toHaveBeenCalledOnceWith(`${endpt1}/atlases`)
+        })
+
+        it("> endpoint should be set", async () => {
+          await SAPI.SetBsEndPoint()
+          expect(SAPI.BsEndpoint).toBe(endpt1)
+        })
+      })
+
+      describe("> first fails", () => {
+        beforeEach(() => {
+          let counter = 0
+          fetchSpy.and.callFake(async () => {
+            if (counter === 0) {
+              counter ++
+              throw new Error(`bla`)
+            }
+            const resp = new Response(JSON.stringify([atlas1]), { headers: { 'content-type': 'application/json' }, status: 200 })
+            return resp
+          })
+        })
+
+        it("> should call twice", async () => {
+          await SAPI.SetBsEndPoint()
+          expect(fetchSpy).toHaveBeenCalledTimes(2)
+          expect(fetchSpy.calls.allArgs()).toEqual([
+            [`${endpt1}/atlases`],
+            [`${endpt2}/atlases`],
+          ])
+        })
+
+        it('> should set endpt2', async () => {
+          await SAPI.SetBsEndPoint()
+          expect(SAPI.BsEndpoint).toBe(endpt2)
+        })
+
+        it("> instances bsendpoint should be the updated version", async () => {
+          await SAPI.SetBsEndPoint()
+          const mockHttpClient = {
+            get: jasmine.createSpy()
+          }
+          mockHttpClient.get.and.returnValue(NEVER)
+          const sapi = new SAPI(mockHttpClient as any, null, null)
+          expect(sapi.bsEndpoint).toBe(endpt2)
+        })
+      })
+    })
+  })
+})
diff --git a/src/atlasComponents/sapi/sapi.service.ts b/src/atlasComponents/sapi/sapi.service.ts
index 0600ef2978744509aacbd555e1ae9c5472df89ec..394acc033c64d0b3567024c4f65f1ddc53dfc3c1 100644
--- a/src/atlasComponents/sapi/sapi.service.ts
+++ b/src/atlasComponents/sapi/sapi.service.ts
@@ -3,7 +3,8 @@ import { HttpClient } from '@angular/common/http';
 import { map, shareReplay } from "rxjs/operators";
 import { SAPIAtlas, SAPISpace } from './core'
 import {
-  SapiAtlasModel, SapiModalityModel,
+  SapiAtlasModel,
+  SapiModalityModel,
   SapiParcellationModel,
   SapiQueryPriorityArg,
   SapiRegionalFeatureModel,
@@ -24,15 +25,38 @@ import { SAPIFeature } from "./features";
 import { environment } from "src/environments/environment"
 
 export const SIIBRA_API_VERSION_HEADER_KEY='x-siibra-api-version'
-export const SIIBRA_API_VERSION = '0.2.1'
+export const SIIBRA_API_VERSION = '0.2.2'
 
 type RegistryType = SAPIAtlas | SAPISpace | SAPIParcellation
 
 @Injectable()
 export class SAPI{
-  static bsEndpoint = environment.BS_REST_URL || `https://siibra-api-latest.apps-dev.hbp.eu/v2_0`
 
-  public bsEndpoint = SAPI.bsEndpoint
+  static async SetBsEndPoint() {
+    let idx = 0
+    const siibraApiEndpts = environment.SIIBRA_API_ENDPOINTS.split(',')
+    while (idx < siibraApiEndpts.length) {
+      const url = siibraApiEndpts[idx]
+      try {
+        const resp = await fetch(`${url}/atlases`)
+        const atlases = await resp.json()
+        if (atlases.length > 0) {
+          SAPI.BsEndpoint = url
+          return
+        }
+      } catch (e) {
+        idx ++
+      }
+    }
+    SAPI.ErrorMessage = `It appears all of our mirrors are not working. The viewer may not be working properly...`
+  }
+
+  static ErrorMessage = null
+  static BsEndpoint = `https://siibra-api-rc.apps.hbp.eu/v2_0`
+
+  get bsEndpoint() {
+    return SAPI.BsEndpoint
+  }
   
   registry = {
     _map: {} as Record<string, {
@@ -92,7 +116,7 @@ export class SAPI{
   }
 
   getModalities(): Observable<SapiModalityModel[]> {
-    return this.http.get<SapiModalityModel[]>(`${SAPI.bsEndpoint}/modalities`)
+    return this.http.get<SapiModalityModel[]>(`${SAPI.BsEndpoint}/modalities`)
   }
 
   httpGet<T>(url: string, params?: Record<string, string>, sapiParam?: SapiQueryPriorityArg){
@@ -133,6 +157,9 @@ export class SAPI{
     private snackbar: MatSnackBar,
     private workerSvc: AtlasWorkerService,
   ){
+    if (SAPI.ErrorMessage) {
+      this.snackbar.open(SAPI.ErrorMessage, 'Dismiss', { duration: 5000 })
+    }
     this.atlases$.subscribe(atlases => {
       for (const atlas of atlases) {
         for (const space of atlas.spaces) {
diff --git a/src/atlasComponents/sapi/stories.base.ts b/src/atlasComponents/sapi/stories.base.ts
index 51fde4f0cd5695f6055398e8bd9ec39ed469c541..1fa58bef89b562d5be3ad8ef5383c302bb335649 100644
--- a/src/atlasComponents/sapi/stories.base.ts
+++ b/src/atlasComponents/sapi/stories.base.ts
@@ -67,22 +67,22 @@ export const parcId = {
 }
 
 export async function getAtlases(): Promise<SapiAtlasModel[]> {
-  return await (await fetch(`${SAPI.bsEndpoint}/atlases`)).json() as SapiAtlasModel[]
+  return await (await fetch(`${SAPI.BsEndpoint}/atlases`)).json() as SapiAtlasModel[]
 }
 
 export async function getAtlas(id: string): Promise<SapiAtlasModel>{
-  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${id}`)).json()
+  return await (await fetch(`${SAPI.BsEndpoint}/atlases/${id}`)).json()
 }
 
 export async function getParc(atlasId: string, id: string): Promise<SapiParcellationModel>{
-  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId}/parcellations/${id}`)).json()
+  return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId}/parcellations/${id}`)).json()
 }
 export async function getParcRegions(atlasId: string, id: string, spaceId: string): Promise<SapiRegionModel[]>{
-  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId}/parcellations/${id}/regions?space_id=${encodeURIComponent(spaceId)}`)).json()
+  return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId}/parcellations/${id}/regions?space_id=${encodeURIComponent(spaceId)}`)).json()
 }
 
 export async function getSpace(atlasId: string, id: string): Promise<SapiSpaceModel> {
-  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId}/spaces/${id}`)).json()
+  return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId}/spaces/${id}`)).json()
 }
 
 export async function getHumanAtlas(): Promise<SapiAtlasModel> {
@@ -90,7 +90,7 @@ export async function getHumanAtlas(): Promise<SapiAtlasModel> {
 }
 
 export async function getMni152(): Promise<SapiSpaceModel> {
-  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}`)).json()
+  return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}`)).json()
 }
 
 export async function getJba29(): Promise<SapiParcellationModel> {
@@ -103,33 +103,33 @@ export async function getJba29Regions(): Promise<SapiRegionModel[]> {
 
 export async function getHoc1Right(spaceId=null): Promise<SapiRegionModel> {
   if (!spaceId) {
-    return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right`)).json()
+    return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right`)).json()
   }
-  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right?space_id=${encodeURIComponent(spaceId)}`)).json()
+  return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right?space_id=${encodeURIComponent(spaceId)}`)).json()
 }
 
 export async function get44Left(spaceId=null): Promise<SapiRegionModel> {
   if (!spaceId) {
-    return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/area%2044%20left`)).json()
+    return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/area%2044%20left`)).json()
   }
-  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/area%2044%20left?space_id=${encodeURIComponent(spaceId)}`)).json()
+  return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/area%2044%20left?space_id=${encodeURIComponent(spaceId)}`)).json()
 }
 
 export async function getHoc1RightSpatialFeatures(): Promise<SxplrCleanedFeatureModel[]> {
-  const json: SapiSpatialFeatureModel[] = await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}/features?parcellation_id=2.9&region=hoc1%20right`)).json()
+  const json: SapiSpatialFeatureModel[] = await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}/features?parcellation_id=2.9&region=hoc1%20right`)).json()
   return cleanIeegSessionDatasets(json.filter(it => it['@type'] === "siibra/features/ieegSession"))
 }
 
 export async function getHoc1RightFeatures(): Promise<SapiRegionalFeatureModel[]> {
-  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right/features`)).json()
+  return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right/features`)).json()
 }
 
 export async function getHoc1RightFeatureDetail(featId: string): Promise<SapiRegionalFeatureModel>{
-  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right/features/${encodeURIComponent(featId)}`)).json()
+  return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right/features/${encodeURIComponent(featId)}`)).json()
 }
 
 export async function getJba29Features(): Promise<SapiParcellationFeatureModel[]> {
-  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/features`)).json()
+  return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/features`)).json()
 }
 
 export async function getBigbrainSpatialFeatures(): Promise<SapiSpatialFeatureModel[]>{
@@ -137,14 +137,14 @@ export async function getBigbrainSpatialFeatures(): Promise<SapiSpatialFeatureMo
     [-1000, -1000, -1000],
     [1000, 1000, 1000]
   ]
-  const url = new URL(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.bigbrain}/features`)
+  const url = new URL(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.bigbrain}/features`)
   url.searchParams.set(`bbox`, JSON.stringify(bbox))
   return await (await fetch(url.toString())).json()
 }
 
 export async function getMni152SpatialFeatureHoc1Right(): Promise<SapiSpatialFeatureModel[]>{
   
-  const url = new URL(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}/features`)
+  const url = new URL(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}/features`)
   url.searchParams.set(`parcellation_id`, parcId.human.jba29)
   url.searchParams.set("region", 'hoc1 right')
   return await (await fetch(url.toString())).json()
diff --git a/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.spec.ts b/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.spec.ts
index 77df043bb37ed7ffb87cad72d422dda777db33d0..972e411795a3ac9e3a862a3cb61bc7d403f1a105 100644
--- a/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.spec.ts
+++ b/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.spec.ts
@@ -3,11 +3,11 @@ import { SAPI } from "src/atlasComponents/sapi/sapi.service"
 import { SapiParcellationModel } from "src/atlasComponents/sapi/type"
 import { getTraverseFunctions } from "./parcellationVersion.pipe"
 
-describe(`parcellationVersion.pipe.ts (endpoint at ${SAPI.bsEndpoint})`, () => {
+describe(`parcellationVersion.pipe.ts (endpoint at ${SAPI.BsEndpoint})`, () => {
   describe("getTraverseFunctions", () => {
     let julichBrainParcellations: SapiParcellationModel[] = []
     beforeAll(async () => {
-      const res = await fetch(`${SAPI.bsEndpoint}/atlases/${encodeURIComponent(IDS.ATLAES.HUMAN)}/parcellations`)
+      const res = await fetch(`${SAPI.BsEndpoint}/atlases/${encodeURIComponent(IDS.ATLAES.HUMAN)}/parcellations`)
       const arr: SapiParcellationModel[] = await res.json()
       julichBrainParcellations = arr.filter(it => /Julich-Brain Cytoarchitectonic Maps/.test(it.name))
     })
diff --git a/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts
index 3889c4c3f20621280a66f92f3e37f2a4521414d0..b1efcd694501bd33796a961449fbb3fec244b28c 100644
--- a/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts
+++ b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts
@@ -6,7 +6,6 @@ import {CUSTOM_ELEMENTS_SCHEMA, Directive, Input} from "@angular/core";
 import {provideMockActions} from "@ngrx/effects/testing";
 import {MockStore, provideMockStore} from "@ngrx/store/testing";
 import {Observable, of} from "rxjs";
-import {BS_ENDPOINT} from "src/util/constants";
 import {SAPI} from "src/atlasComponents/sapi";
 import {AngularMaterialModule} from "src/sharedModules";
 
@@ -66,10 +65,6 @@ describe('ConnectivityComponent', () => {
             providers: [
                 provideMockActions(() => actions$),
                 provideMockStore(),
-                {
-                    provide: BS_ENDPOINT,
-                    useValue: MOCK_BS_ENDPOINT
-                },
                 {
                     provide: SAPI,
                     useValue: {
diff --git a/src/atlasViewer/atlasViewer.workerService.service.ts b/src/atlasViewer/atlasViewer.workerService.service.ts
index 9a17115534cae61a899a29c7d32bc4602599fd19..67a422714822d25e47fdb55b345116cda9db8eba 100644
--- a/src/atlasViewer/atlasViewer.workerService.service.ts
+++ b/src/atlasViewer/atlasViewer.workerService.service.ts
@@ -3,9 +3,6 @@ import { fromEvent } from "rxjs";
 import { filter, take } from "rxjs/operators";
 import { getUuid } from "src/util/fn";
 
-// worker is now exported in angular.json file
-export const worker = new Worker('worker.js')
-
 interface IWorkerMessage {
   method: string
   param: any
@@ -17,7 +14,11 @@ interface IWorkerMessage {
 })
 
 export class AtlasWorkerService {
-  public worker = worker
+  private worker: Worker
+
+  constructor(){
+    this.worker = new Worker('worker.js')
+  }
 
   async sendMessage(_data: IWorkerMessage){
 
diff --git a/src/environments/environment.common.ts b/src/environments/environment.common.ts
index f4d5c96275db63b6e84e76a7223c597139fba508..4f410904c371a0239abecd0e602153b1680fdba3 100644
--- a/src/environments/environment.common.ts
+++ b/src/environments/environment.common.ts
@@ -4,7 +4,7 @@ export const environment = {
   VERSION: 'unknown version',
   PRODUCTION: true,
   BACKEND_URL: null,
-  BS_REST_URL: 'https://siibra-api-latest.apps-dev.hbp.eu/v2_0',
+  SIIBRA_API_ENDPOINTS: 'https://siibra-api-stable.apps.hbp.eu/v2_0,https://siibra-api-stable.apps.jsc.hbp.eu/v2_0,https://siibra-api-stable-ns.apps.hbp.eu/v2_0',
   SPATIAL_TRANSFORM_BACKEND: 'https://hbp-spatial-backend.apps.hbp.eu',
   MATOMO_URL: null,
   MATOMO_ID: null,
diff --git a/src/environments/parseEnv.js b/src/environments/parseEnv.js
index b05909d596c85cc1875650602cafb8a5fa2fba3a..9533d341158797f5da8a604929d9327e8980545f 100644
--- a/src/environments/parseEnv.js
+++ b/src/environments/parseEnv.js
@@ -2,15 +2,17 @@ const fs = require('fs')
 const path = require('path')
 const { promisify } = require('util')
 const asyncWrite = promisify(fs.writeFile)
+const process = require("process")
 
 const main = async () => {
-  const pathToEnvFile = path.join(__dirname, './environment.prod.ts')
+  const target = process.argv[2] || './environment.prod.ts'
+  const pathToEnvFile = path.join(__dirname, target)
   const {
     BACKEND_URL,
     STRICT_LOCAL,
     MATOMO_URL,
     MATOMO_ID,
-    BS_REST_URL,
+    SIIBRA_API_ENDPOINTS,
     VERSION,
     GIT_HASH = 'unknown hash',
     EXPERIMENTAL_FEATURE_FLAG
@@ -21,7 +23,7 @@ const main = async () => {
     STRICT_LOCAL,
     MATOMO_URL,
     MATOMO_ID,
-    BS_REST_URL,
+    SIIBRA_API_ENDPOINTS,
     VERSION,
     GIT_HASH,
     EXPERIMENTAL_FEATURE_FLAG,
@@ -39,7 +41,7 @@ export const environment = {
   ...commonEnv,
   GIT_HASH: ${gitHash},
   VERSION: ${version},
-  BS_REST_URL: ${JSON.stringify(BS_REST_URL)},
+  SIIBRA_API_ENDPOINTS: ${JSON.stringify(SIIBRA_API_ENDPOINTS)},
   BACKEND_URL: ${JSON.stringify(BACKEND_URL)},
   STRICT_LOCAL: ${JSON.stringify(STRICT_LOCAL)},
   MATOMO_URL: ${JSON.stringify(MATOMO_URL)},
diff --git a/src/main.module.ts b/src/main.module.ts
index 7d26a8ee52400fa2acd8e9cbd0a670ddd06c5670..da570799012cb86e814b3760f110174c943f8f36 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -33,7 +33,6 @@ import { CookieModule } from './ui/cookieAgreement/module';
 import { KgTosModule } from './ui/kgtos/module';
 import { AtlasViewerRouterModule } from './routerModule';
 import { MessagingGlue } from './messagingGlue';
-import { BS_ENDPOINT } from './util/constants';
 import { QuickTourModule } from './ui/quickTour';
 import { of } from 'rxjs';
 import { CANCELLABLE_DIALOG, CANCELLABLE_DIALOG_OPTS } from './util/interfaces';
@@ -163,10 +162,6 @@ import { CONST } from "common/constants"
       provide: WINDOW_MESSAGING_HANDLER_TOKEN,
       useClass: MessagingGlue
     },
-    {
-      provide: BS_ENDPOINT,
-      useValue: (environment.BS_REST_URL || `https://siibra-api-stable.apps.hbp.eu/v1_0`).replace(/\/$/, '')
-    },
     {
       provide: DARKTHEME,
       useFactory: (store: Store) => store.pipe(
diff --git a/src/share/saneUrl/saneUrl.component.spec.ts b/src/share/saneUrl/saneUrl.component.spec.ts
index 5ab173950c92148144b7b3025f63cddf95205a30..982ecb76fb47ca954a5da6c9f5c792c69f3118ba 100644
--- a/src/share/saneUrl/saneUrl.component.spec.ts
+++ b/src/share/saneUrl/saneUrl.component.spec.ts
@@ -1,14 +1,13 @@
-import { TestBed, fakeAsync, tick, flush } from '@angular/core/testing'
-import { ShareModule } from '../share.module'
+import { TestBed, fakeAsync, tick, flush, ComponentFixture } from '@angular/core/testing'
 import { SaneUrl } from './saneUrl.component'
-import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'
 import { By } from '@angular/platform-browser'
 import { BACKENDURL } from 'src/util/constants'
 import { NoopAnimationsModule } from '@angular/platform-browser/animations'
 import { SaneUrlSvc } from './saneUrl.service'
 import { AngularMaterialModule } from 'src/sharedModules'
 import { CUSTOM_ELEMENTS_SCHEMA, Directive } from '@angular/core'
-import { of } from 'rxjs'
+import { of, throwError } from 'rxjs'
+import { NotFoundError } from '../type'
 
 const inputCss = `input[aria-label="Custom link"]`
 const submitCss = `button[aria-label="Create custom link"]`
@@ -25,15 +24,22 @@ class AuthStateDummy {
 
 describe('> saneUrl.component.ts', () => {
   describe('> SaneUrl', () => {
+    const mockSaneUrlSvc = {
+      saneUrlroot: 'saneUrlroot',
+      getKeyVal: jasmine.createSpy('getKeyVal'),
+      setKeyVal: jasmine.createSpy('setKeyVal'),
+    }
     beforeEach(async () => {
       await TestBed.configureTestingModule({
         imports: [
-          HttpClientTestingModule,
           NoopAnimationsModule,
           AngularMaterialModule,
         ],
         providers: [
-          SaneUrlSvc,
+          {
+            provide: SaneUrlSvc,
+            useValue: mockSaneUrlSvc
+          }
         ],
         declarations: [
           SaneUrl,
@@ -43,11 +49,18 @@ describe('> saneUrl.component.ts', () => {
           CUSTOM_ELEMENTS_SCHEMA
         ]
       }).compileComponents()
+
+      mockSaneUrlSvc.getKeyVal.and.returnValue(
+        of('foo-bar')
+      )
+      mockSaneUrlSvc.setKeyVal.and.returnValue(
+        of('OK')
+      )
     })
 
     afterEach(() => {
-      const ctrl = TestBed.inject(HttpTestingController)
-      ctrl.verify()
+      mockSaneUrlSvc.getKeyVal.calls.reset()
+      mockSaneUrlSvc.setKeyVal.calls.reset()
     })
 
     it('> can be created', () => {
@@ -112,215 +125,188 @@ describe('> saneUrl.component.ts', () => {
       const input = fixture.debugElement.query( By.css( inputCss ) )
     })
 
-    it('> on entering string in input, makes debounced GET request', fakeAsync(() => {
-
-      const value = 'test_1'
-
-      const httpTestingController = TestBed.inject(HttpTestingController)
-
-      // Necessary to detectChanges, or formControl will not initialise properly
-      // See https://stackoverflow.com/a/56600762/6059235
-      const fixture = TestBed.createComponent(SaneUrl)
-      fixture.detectChanges()
-
-      // Set value
-      fixture.componentInstance.customUrl.setValue(value)
-
-      tick(500)
-
-      const req = httpTestingController.expectOne(`${BACKENDURL}saneUrl/${value}`)
-      req.flush(200)
-    }))
-
-    it('> on 200 response, show error', fakeAsync(() => {
-      
-      const value = 'test_1'
-
-      const httpTestingController = TestBed.inject(HttpTestingController)
-
-      // Necessary to detectChanges, or formControl will not initialise properly
-      // See https://stackoverflow.com/a/56600762/6059235
-      const fixture = TestBed.createComponent(SaneUrl)
-      fixture.detectChanges()
-
-      // Set value
-      fixture.componentInstance.customUrl.setValue(value)
-
-      tick(500)
-
-      const req = httpTestingController.expectOne(`${BACKENDURL}saneUrl/${value}`)
-      req.flush('OK')
-
-      // Expect validator to fail catch it
-      expect(fixture.componentInstance.customUrl.invalid).toEqual(true)
-
-      // on change detection, UI should catch it
-      fixture.detectChanges()
-
-      const input = fixture.debugElement.query( By.css( inputCss ) )
-
-      const submit = fixture.debugElement.query( By.css( submitCss ) )
-      const disabled = !!submit.attributes['disabled']
-      expect(disabled.toString()).toEqual('true')
-    }))
-
-    it('> on 404 response, show available', fakeAsync(() => {
-
-      const value = 'test_1'
-
-      const httpTestingController = TestBed.inject(HttpTestingController)
-
-      // Necessary to detectChanges, or formControl will not initialise properly
-      // See https://stackoverflow.com/a/56600762/6059235
-      const fixture = TestBed.createComponent(SaneUrl)
-      fixture.detectChanges()
-
-      // Set value
-      fixture.componentInstance.customUrl.setValue(value)
-
-      tick(500)
-
-      const req = httpTestingController.expectOne(`${BACKENDURL}saneUrl/${value}`)
-      req.flush('some reason', { status: 404, statusText: 'Not Found.' })
-
-      // Expect validator to fail catch it
-      expect(fixture.componentInstance.customUrl.invalid).toEqual(false)
-
-      // on change detection, UI should catch it
-      fixture.detectChanges()
-
-      const input = fixture.debugElement.query( By.css( inputCss ) )
-
-      const submit = fixture.debugElement.query( By.css( submitCss ) )
-      const disabled = !!submit.attributes['disabled']
-      expect(disabled.toString()).toEqual('false')
-    }))
-
-    it('> on other error codes, show invalid', fakeAsync(() => {
-
-      const value = 'test_1'
-
-      const httpTestingController = TestBed.inject(HttpTestingController)
-
-      // Necessary to detectChanges, or formControl will not initialise properly
-      // See https://stackoverflow.com/a/56600762/6059235
-      const fixture = TestBed.createComponent(SaneUrl)
-      fixture.detectChanges()
-
-      // Set value
-      fixture.componentInstance.customUrl.setValue(value)
-
-      tick(500)
-
-      const req = httpTestingController.expectOne(`${BACKENDURL}saneUrl/${value}`)
-      req.flush('some reason', { status: 401, statusText: 'Unauthorised.' })
-
-      // Expect validator to fail catch it
-      expect(fixture.componentInstance.customUrl.invalid).toEqual(true)
-
-      // on change detection, UI should catch it
-      fixture.detectChanges()
-
-      const input = fixture.debugElement.query( By.css( inputCss ) )
-
-      const submit = fixture.debugElement.query( By.css( submitCss ) )
-      const disabled = !!submit.attributes['disabled']
-      expect(disabled.toString()).toEqual('true')
-    }))
-
-    it('> on click create link btn calls correct API', fakeAsync(() => {
-
-      const value = 'test_1'
-
-      const httpTestingController = TestBed.inject(HttpTestingController)
-
-      // Necessary to detectChanges, or formControl will not initialise properly
-      // See https://stackoverflow.com/a/56600762/6059235
-      const fixture = TestBed.createComponent(SaneUrl)
-      fixture.detectChanges()
-
-      // Set value
-      fixture.componentInstance.customUrl.setValue(value)
-
-      tick(500)
-
-      const req = httpTestingController.expectOne(`${BACKENDURL}saneUrl/${value}`)
-      req.flush('some reason', { status: 404, statusText: 'Not Found.' })
-
-      fixture.detectChanges()
-      flush()
-
-      const submit = fixture.debugElement.query( By.css( submitCss ) )
-      const disabled = !!submit.attributes['disabled']
-      expect(disabled.toString()).toEqual('false')
-
-      submit.triggerEventHandler('click', {})
-
-      fixture.detectChanges()
-
-      const disabledInProgress = !!submit.attributes['disabled']
-      expect(disabledInProgress.toString()).toEqual('true')
-
-      const req2 = httpTestingController.expectOne({
-        method: 'POST',
-        url: `${BACKENDURL}saneUrl/${value}`
+    describe("> on valid input", () => {
+      let saneUrlCmp: SaneUrl
+      let fixture: ComponentFixture<SaneUrl>
+      const stateTobeSaved = 'foo-bar'
+      beforeEach(() => {
+        // Necessary to detectChanges, or formControl will not initialise properly
+        // See https://stackoverflow.com/a/56600762/6059235
+        fixture = TestBed.createComponent(SaneUrl)
+        saneUrlCmp = fixture.componentInstance
+        saneUrlCmp.stateTobeSaved = stateTobeSaved
+        fixture.detectChanges()
       })
-      
-      req2.flush({})
-
-      fixture.detectChanges()
-
-      const disabledAfterComplete = !!submit.attributes['disabled']
-      expect(disabledAfterComplete.toString()).toEqual('true')
-
-      const cpyBtn = fixture.debugElement.query( By.css( copyBtnCss ) )
-      expect(cpyBtn).toBeTruthy()
-    }))
-
-    it('> on click create link btn fails show result', fakeAsync(() => {
-
-      const value = 'test_1'
-
-      const httpTestingController = TestBed.inject(HttpTestingController)
-
-      // Necessary to detectChanges, or formControl will not initialise properly
-      // See https://stackoverflow.com/a/56600762/6059235
-      const fixture = TestBed.createComponent(SaneUrl)
-      fixture.detectChanges()
-
-      // Set value
-      fixture.componentInstance.customUrl.setValue(value)
-
-      tick(500)
-
-      const req = httpTestingController.expectOne(`${BACKENDURL}saneUrl/${value}`)
-      req.flush('some reason', { status: 404, statusText: 'Not Found.' })
-
-      fixture.detectChanges()
-      flush()
-
-      const submit = fixture.debugElement.query( By.css( submitCss ) )
-      const disabled = !!submit.attributes['disabled']
-      expect(disabled.toString()).toEqual('false')
-
-      submit.triggerEventHandler('click', {})
-
-      fixture.detectChanges()
-
-      const disabledInProgress = !!submit.attributes['disabled']
-      expect(disabledInProgress.toString()).toEqual('true')
-
-      const req2 = httpTestingController.expectOne({
-        method: 'POST',
-        url: `${BACKENDURL}saneUrl/${value}`
+      it('> on entering string in input, makes debounced GET request', fakeAsync(() => {
+
+        const value = 'test_1'
+  
+        // Set value
+        fixture.componentInstance.customUrl.setValue(value)
+  
+        tick(500)
+  
+        expect(mockSaneUrlSvc.getKeyVal).toHaveBeenCalledOnceWith(value)
+      }))
+  
+      describe("> on 200", () => {
+        it("> show error", fakeAsync(() => {
+  
+          const value = 'test_1'
+    
+          // Set value
+          fixture.componentInstance.customUrl.setValue(value)
+    
+          tick(500)
+    
+          // Expect validator to fail catch it
+          expect(fixture.componentInstance.customUrl.invalid).toEqual(true)
+    
+          // on change detection, UI should catch it
+          fixture.detectChanges()
+    
+          const input = fixture.debugElement.query( By.css( inputCss ) )
+    
+          const submit = fixture.debugElement.query( By.css( submitCss ) )
+          const disabled = !!submit.attributes['disabled']
+          expect(disabled.toString()).toEqual('true')
+        }))
+      })
+  
+      describe('> on 404', () => {
+        beforeEach(() => {
+          mockSaneUrlSvc.getKeyVal.and.returnValue(
+            throwError(new NotFoundError('not found'))
+          )
+        })
+        it("> should available", fakeAsync(() => {
+  
+          const value = 'test_1'
+    
+          // Set value
+          fixture.componentInstance.customUrl.setValue(value)
+    
+          tick(500)
+    
+          // Expect validator to fail catch it
+          expect(fixture.componentInstance.customUrl.invalid).toEqual(false)
+    
+          // on change detection, UI should catch it
+          fixture.detectChanges()
+    
+          const input = fixture.debugElement.query( By.css( inputCss ) )
+    
+          const submit = fixture.debugElement.query( By.css( submitCss ) )
+          const disabled = !!submit.attributes['disabled']
+          expect(disabled.toString()).toEqual('false')
+        }))
       })
+  
+      describe("> on other error", () => {
+        beforeEach(() => {
+  
+          mockSaneUrlSvc.getKeyVal.and.returnValue(
+            throwError(new Error('other errors'))
+          )
+        })
+        it("> show invalid", fakeAsync(() => {
+          const value = 'test_1'
+    
+          // Set value
+          fixture.componentInstance.customUrl.setValue(value)
+    
+          tick(500)
+    
+          // Expect validator to fail catch it
+          expect(fixture.componentInstance.customUrl.invalid).toEqual(true)
+    
+          // on change detection, UI should catch it
+          fixture.detectChanges()
+    
+          const input = fixture.debugElement.query( By.css( inputCss ) )
+    
+          const submit = fixture.debugElement.query( By.css( submitCss ) )
+          const disabled = !!submit.attributes['disabled']
+          expect(disabled.toString()).toEqual('true')
+        }))
+      })
+  
+      describe("> on click create link", () => {
+        beforeEach(() => {
+          mockSaneUrlSvc.getKeyVal.and.returnValue(
+            throwError(new NotFoundError('not found'))
+          )
+        })
+        it("> calls correct service function", fakeAsync(() => {
+  
+          const value = 'test_1'
+    
+          // Set value
+          fixture.componentInstance.customUrl.setValue(value)
+    
+          tick(500)
+    
+          fixture.detectChanges()
+          flush()
+    
+          const submit = fixture.debugElement.query( By.css( submitCss ) )
+          const disabled = !!submit.attributes['disabled']
+          expect(disabled.toString()).toEqual('false')
+    
+          submit.triggerEventHandler('click', {})
+    
+          fixture.detectChanges()
+    
+          const disabledInProgress = !!submit.attributes['disabled']
+          expect(disabledInProgress.toString()).toEqual('true')
+    
+          fixture.detectChanges()
+    
+          const disabledAfterComplete = !!submit.attributes['disabled']
+          expect(disabledAfterComplete.toString()).toEqual('true')
+    
+          const cpyBtn = fixture.debugElement.query( By.css( copyBtnCss ) )
+          expect(cpyBtn).toBeTruthy()
+        }))
+  
+        describe("> on fail", () => {
+          beforeEach(() => {
+            mockSaneUrlSvc.setKeyVal.and.returnValue(
+              throwError(new Error(`some error`))
+            )
+          })
+          it("> show result", fakeAsync(() => {
+  
+            const value = 'test_1'
       
-      req2.flush('Something went wrong', { statusText: 'Wrong status text', status: 500 })
-
-      fixture.detectChanges()
-
-      const input = fixture.debugElement.query( By.css( inputCss ) )
-
-    }))
+            // Set value
+            fixture.componentInstance.customUrl.setValue(value)
+      
+            tick(500)
+      
+            fixture.detectChanges()
+      
+            const submit = fixture.debugElement.query( By.css( submitCss ) )
+            const disabled = !!submit.attributes['disabled']
+            expect(disabled.toString()).toEqual('false')
+      
+            submit.triggerEventHandler('click', {})
+      
+            fixture.detectChanges()
+      
+            const disabledInProgress = !!submit.attributes['disabled']
+            expect(disabledInProgress.toString()).toEqual('true')
+      
+            expect(mockSaneUrlSvc.setKeyVal).toHaveBeenCalledOnceWith(value, stateTobeSaved)
+            
+            fixture.detectChanges()
+      
+            const input = fixture.debugElement.query( By.css( inputCss ) )
+      
+          }))
+        })
+      })
+  
+    })
   })
 })
diff --git a/src/share/saneUrl/saneUrl.service.ts b/src/share/saneUrl/saneUrl.service.ts
index 8f3af1f3d55fcb9baec57d44ddbbda76ac24c2df..c5d9849f760ed90b7197ef8206b53db55ab1025d 100644
--- a/src/share/saneUrl/saneUrl.service.ts
+++ b/src/share/saneUrl/saneUrl.service.ts
@@ -4,23 +4,27 @@ import { throwError } from "rxjs";
 import { catchError, mapTo } from "rxjs/operators";
 import { BACKENDURL } from 'src/util/constants'
 import { IKeyValStore, NotFoundError } from '../type'
+import { DISABLE_PRIORITY_HEADER } from "src/util/priority"
 
 @Injectable({
   providedIn: 'root'
 })
 
 export class SaneUrlSvc implements IKeyValStore{
-  public saneUrlRoot = `${BACKENDURL}saneUrl/`
+  public saneUrlRoot = `${BACKENDURL}go/`
   constructor(
     private http: HttpClient
   ){
-
+    if (!BACKENDURL) {
+      const loc = window.location
+      this.saneUrlRoot = `${loc.protocol}//${loc.hostname}${!!loc.port ? (':' + loc.port) : ''}${loc.pathname}go/`
+    }
   }
 
   getKeyVal(key: string) {
     return this.http.get<Record<string, any>>(
       `${this.saneUrlRoot}${key}`,
-      { responseType: 'json' }
+      { responseType: 'json', headers: { [DISABLE_PRIORITY_HEADER]: '1' } }
     ).pipe(
       catchError((err, obs) => {
         const { status } = err
@@ -35,7 +39,8 @@ export class SaneUrlSvc implements IKeyValStore{
   setKeyVal(key: string, value: any) {
     return this.http.post(
       `${this.saneUrlRoot}${key}`,
-      value
+      value,
+      { headers: { [DISABLE_PRIORITY_HEADER]: '1' } }
     ).pipe(
       mapTo(`${this.saneUrlRoot}${key}`)
     )
diff --git a/src/util/constants.ts b/src/util/constants.ts
index 5c4e7185743a1dca35e0523f076385b797fc21b5..9b1700442a02984d4b5a15c73b122d5bcc0cf8ed 100644
--- a/src/util/constants.ts
+++ b/src/util/constants.ts
@@ -115,7 +115,6 @@ export const compareLandmarksChanged: (prevLandmarks: any[], newLandmarks: any[]
 }
 
 export const CYCLE_PANEL_MESSAGE = `[spacebar] to cycle through views`
-export const BS_ENDPOINT = new InjectionToken<string>('BS_ENDPOINT')
 
 export const UNSUPPORTED_PREVIEW = [{
   text: 'Preview of Colin 27 and JuBrain Cytoarchitectonic',
diff --git a/src/util/priority.ts b/src/util/priority.ts
index ab703f76b14e1a299fa2ae017d81038a43c1c858..79ef1b043e4d5bb13a99e157995315e05e94b8d5 100644
--- a/src/util/priority.ts
+++ b/src/util/priority.ts
@@ -24,6 +24,8 @@ type Queue = {
   next: HttpHandler
 }
 
+export const DISABLE_PRIORITY_HEADER = 'x-sxplr-disable-priority'
+
 @Injectable({
   providedIn: 'root'
 })
@@ -137,8 +139,11 @@ export class PriorityHttpInterceptor implements HttpInterceptor{
      * Since the way in which serialization occurs is via path and query param...
      * body is not used.
      */
-    if (this.disablePriority || req.method !== 'GET') {
-      return next.handle(req)
+    if (this.disablePriority || req.method !== 'GET' || !!req.headers.get(DISABLE_PRIORITY_HEADER)) {
+      const newReq = req.clone({
+        headers: req.headers.delete(DISABLE_PRIORITY_HEADER)
+      })
+      return next.handle(newReq)
     }
 
     const { urlWithParams } = req
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
index c6437fc7c93844af930c70ce2c5ae019227f76cf..6c5bb5aeb0c02634389135fe0056c5b88644d89b 100644
--- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
@@ -1,7 +1,6 @@
 import { TestBed, fakeAsync, tick, ComponentFixture } from "@angular/core/testing"
 import { CommonModule } from "@angular/common"
 import { NehubaViewerUnit, IMPORT_NEHUBA_INJECT_TOKEN, scanFn } from "./nehubaViewer.component"
-import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"
 import { LoggingModule, LoggingService } from "src/logging"
 import { IMeshesToLoad, SET_MESHES_TO_LOAD } from "../constants"
 import { Subject } from "rxjs"
@@ -106,7 +105,6 @@ describe('> nehubaViewer.component.ts', () => {
             provide: SET_COLORMAP_OBS,
             useValue: setcolorMap$
           },
-          AtlasWorkerService,
           LoggingService,
         ]
       }).compileComponents()
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
index b1f70202ac24bfbe8375be62bcee549eb5132694..4d38f0487b48b24a678e8a34b454d521d102e94e 100644
--- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
@@ -1,7 +1,6 @@
 import { Component, ElementRef, EventEmitter, OnDestroy, Output, Inject, Optional } from "@angular/core";
-import { fromEvent, Subscription, BehaviorSubject, Observable, Subject, of, interval } from 'rxjs'
-import { debounceTime, filter, map, scan, switchMap, take, distinctUntilChanged, debounce } from "rxjs/operators";
-import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
+import { Subscription, BehaviorSubject, Observable, Subject, of, interval } from 'rxjs'
+import { debounceTime, filter, scan, switchMap, take, distinctUntilChanged, debounce } from "rxjs/operators";
 import { LoggingService } from "src/logging";
 import { bufferUntil, getExportNehuba, getViewer, setNehubaViewer, switchMapWaitFor } from "src/util/fn";
 import { deserializeSegment, NEHUBA_INSTANCE_INJTKN } from "../util";
@@ -135,7 +134,6 @@ export class NehubaViewerUnit implements OnDestroy {
 
   constructor(
     public elementRef: ElementRef,
-    private workerService: AtlasWorkerService,
     private log: LoggingService,
     @Inject(IMPORT_NEHUBA_INJECT_TOKEN) getImportNehubaPr: () => Promise<any>,
     @Optional() @Inject(NEHUBA_INSTANCE_INJTKN) private nehubaViewer$: Subject<NehubaViewerUnit>,
@@ -179,67 +177,6 @@ export class NehubaViewerUnit implements OnDestroy {
       })
       .catch(e => this.errorEmitter.emit(e))
 
-
-    /**
-     * TODO move to layerCtrl.service
-     */
-    this.ondestroySubscriptions.push(
-      fromEvent(this.workerService.worker, 'message').pipe(
-        filter((message: any) => {
-
-          if (!message) {
-            // this.log.error('worker response message is undefined', message)
-            return false
-          }
-          if (!message.data) {
-            // this.log.error('worker response message.data is undefined', message.data)
-            return false
-          }
-          if (message.data.type !== 'ASSEMBLED_USERLANDMARKS_VTK') {
-            /* worker responded with not assembled landmark, no need to act */
-            return false
-          }
-          /**
-           * nb url may be undefined
-           * if undefined, user have removed all user landmarks, and all that needs to be done
-           * is remove the user landmark layer
-           *
-           * message.data.url
-           */
-
-          return true
-        }),
-        debounceTime(100),
-        map(e => e.data.url),
-      ).subscribe(url => {
-        this.landmarksLoaded = !!url
-        this.removeuserLandmarks()
-
-        /**
-         * url may be null if user removes all landmarks
-         */
-        if (!url) {
-          /**
-           * remove transparency from meshes in current layer(s)
-           */
-          this.setMeshTransparency(false)
-          return
-        }
-        const _ = {}
-        _[NG_USER_LANDMARK_LAYER_NAME] = {
-          type: 'mesh',
-          source: `vtk://${url}`,
-          shader: this.userLandmarkShader,
-        }
-        this.loadLayer(_)
-
-        /**
-         * adding transparency to meshes in current layer(s)
-         */
-        this.setMeshTransparency(true)
-      }),
-    )
-  
     if (this.setColormap$) {
       this.ondestroySubscriptions.push(
         this.setColormap$.pipe(
@@ -546,37 +483,6 @@ export class NehubaViewerUnit implements OnDestroy {
   }
 
   private userLandmarkShader: string = FRAGMENT_MAIN_WHITE
-  
-  // TODO single landmark for user landmark
-  public updateUserLandmarks(landmarks: any[]) {
-    if (!this.nehubaViewer) {
-      return
-    }
-    
-    this.workerService.worker.postMessage({
-      type : 'GET_USERLANDMARKS_VTK',
-      scale: Math.min(...this.dim.map(v => v * NG_LANDMARK_CONSTANT)),
-      landmarks : landmarks.map(lm => lm.position.map(coord => coord * 1e6)),
-    })
-
-    const parseLmColor = lm => {
-      if (!lm) return null
-      const { color } = lm
-      if (!color) return null
-      if (!Array.isArray(color)) return null
-      if (color.length !== 3) return null
-      const parseNum = num => (num >= 0 && num <= 255 ? num / 255 : 1).toFixed(3)
-      return `emitRGB(vec3(${color.map(parseNum).join(',')}));`
-    }
-  
-    const appendConditional = (frag, idx) => frag && `if (label > ${idx - 0.01} && label < ${idx + 0.01}) { ${frag} }`
-
-    if (landmarks.some(parseLmColor)) {
-      this.userLandmarkShader = `void main(){ ${landmarks.map(parseLmColor).map(appendConditional).filter(v => !!v).join('else ')} else {${FRAGMENT_EMIT_WHITE}} }`
-    } else {
-      this.userLandmarkShader = FRAGMENT_MAIN_WHITE  
-    }
-  }
 
   public removeSpatialSearch3DLandmarks() {
     this.removeLayer({
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
index 0c7b084eb8dab41764d88d913578e8eeb843478b..fea1911266af6a410bdd339c805c4aeb3129f20c 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
@@ -284,8 +284,33 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnDestroy, AfterViewIni
     /**
      * TODO check extension?
      */
-     
     this.dismissAllAddedLayers()
+
+    if (/\.swc$/i.test(file.name)) {
+      const url = URL.createObjectURL(file)
+      this.droppedLayerNames.push({
+        layerName: randomUuid,
+        resourceUrl: url
+      })
+      this.store$.dispatch(
+        atlasAppearance.actions.addCustomLayer({
+          customLayer: {
+            id: randomUuid,
+            source: `swc://${url}`,
+            segments: ["1"],
+            transform: [
+              [1e3, 0, 0, 0],
+              [0, 1e3, 0, 0],
+              [0, 0, 1e3, 0],
+              [0, 0, 0, 1],
+            ],
+            clType: 'customlayer/nglayer' as const
+          }
+        })
+      )
+      return
+    }
+     
     
     // Get file, try to inflate, if files, use original array buffer
     const buf = await file.arrayBuffer()
diff --git a/src/viewerModule/nehuba/statusCard/statusCard.component.spec.ts b/src/viewerModule/nehuba/statusCard/statusCard.component.spec.ts
index ce0afa8fa8632b433b5998eed1107654fdc4dfa2..ab6ac511153553a1bc50365bd2b85c4a239c94f4 100644
--- a/src/viewerModule/nehuba/statusCard/statusCard.component.spec.ts
+++ b/src/viewerModule/nehuba/statusCard/statusCard.component.spec.ts
@@ -146,8 +146,8 @@ describe('> statusCard.component.ts', () => {
           initialNgState: {
             navigation: {
               pose: {
-                orientation: [0,0,0,1],
-                position: [10, 20, 30]
+                orientation: [0, 0, 0, 1],
+                position: [0, 0, 0]
               },
               zoomFactor: 1e6
             }
diff --git a/src/viewerModule/nehuba/statusCard/statusCard.component.ts b/src/viewerModule/nehuba/statusCard/statusCard.component.ts
index 26d6786e8b7f7ea3723eb47d7a32bc299c46b720..58bbdf911ed843cd5e68123f65869fe5843b3ace 100644
--- a/src/viewerModule/nehuba/statusCard/statusCard.component.ts
+++ b/src/viewerModule/nehuba/statusCard/statusCard.component.ts
@@ -177,10 +177,7 @@ export class StatusCardComponent implements OnInit, OnChanges{
    */
   public resetNavigation({rotation: rotationFlag = false, position: positionFlag = false, zoom : zoomFlag = false}: {rotation?: boolean, position?: boolean, zoom?: boolean}): void {
     const config = getNehubaConfig(this.selectedTemplate)
-    const {
-      orientation,
-      position
-    } = config.dataset.initialNgState.navigation.pose
+
     const {
       zoomFactor: zoom
     } = config.dataset.initialNgState.navigation
@@ -189,8 +186,8 @@ export class StatusCardComponent implements OnInit, OnChanges{
       actions.navigateTo({
         navigation: {
           ...this.currentNavigation,
-          ...(rotationFlag ? { orientation: orientation } : {}),
-          ...(positionFlag ? { position: position } : {}),
+          ...(rotationFlag ? { orientation: [0, 0, 0, 1] } : {}),
+          ...(positionFlag ? { position: [0, 0, 0] } : {}),
           ...(zoomFlag ? { zoom: zoom } : {}),
         },
         physical: true,
diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts
index c5042ecfc22e3134ab6af2c967bdad86bf5d4933..0ec1d5125ba83f89dc91ea8e20e33954673030a7 100644
--- a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts
+++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts
@@ -22,7 +22,6 @@ describe('> viewerCtrlCmp.component.ts', () => {
     let mockStore: MockStore
 
     let mockNehubaViewer = {
-      updateUserLandmarks: jasmine.createSpy(),
       nehubaViewer: {
         ngviewer: {
           layerManager: {
@@ -42,7 +41,6 @@ describe('> viewerCtrlCmp.component.ts', () => {
     }
 
     afterEach(() => {
-      mockNehubaViewer.updateUserLandmarks.calls.reset()
       mockNehubaViewer.nehubaViewer.ngviewer.layerManager.getLayerByName.calls.reset()
       mockNehubaViewer.nehubaViewer.ngviewer.display.scheduleRedraw.calls.reset()
     })