diff --git a/angular.json b/angular.json
index dcbadb4911f1203a6204541cc20e00b2ec00947b..46d2ba12824d187f38aab94428da75566e9fccee 100644
--- a/angular.json
+++ b/angular.json
@@ -34,7 +34,13 @@
             "styles": [
               "src/theme.scss",
               "src/overwrite.scss",
-              "src/extra_styles.css"
+              "src/extra_styles.css",
+
+              {
+                "input": "export-nehuba/dist/min/main.css",
+                "inject": false,
+                "bundleName": "vanillaMain"
+              }
             ],
             "scripts": [{
               "input": "worker/worker.js",
diff --git a/common/constants.js b/common/constants.js
index d3fe2b2b308520de3e5e9142d58b681c9dbc2a5a..d3f6f24546090062459459f82f8652db30691244 100644
--- a/common/constants.js
+++ b/common/constants.js
@@ -148,6 +148,9 @@ If you do not accept the Terms & Conditions you are not permitted to access or u
     REMOVE_FRONTAL_OCTANT_HELPER_TEXT: `Hide the octant facing the user, and overlaying the slice views.`,
 
     AUXMESH_DESC: `Some templates contain auxiliary meshes, which compliment the appearance of the template in the perspective view.`,
+
+    OVERWRITE_SAPI_ENDPOINT_ATTR: `x-sapi-base-url`,
+    DATA_ERROR_ATTR: `data-error`
   }
 
   exports.QUICKTOUR_DESC ={
diff --git a/deploy/app.js b/deploy/app.js
index f4b3dcd07ead1a7cad8f3c987db8619c1d16e219..bc3444df3848e83ef16d9e9a8e9e2d3391cbbb58 100644
--- a/deploy/app.js
+++ b/deploy/app.js
@@ -1,4 +1,3 @@
-const fs = require('fs')
 const path = require('path')
 const express = require('express')
 const app = express.Router()
@@ -6,8 +5,7 @@ const session = require('express-session')
 const crypto = require('crypto')
 const cookieParser = require('cookie-parser')
 const bkwdMdl = require('./bkwdCompat')()
-
-const LOCAL_CDN_FLAG = !!process.env.LOCAL_CDN
+const { CONST } = require("../common/constants")
 
 if (process.env.NODE_ENV !== 'production') {
   app.use(require('cors')())
@@ -123,36 +121,6 @@ const PUBLIC_PATH = process.env.NODE_ENV === 'production'
  */
 app.use('/.well-known', express.static(path.join(__dirname, 'well-known')))
 
-if (LOCAL_CDN_FLAG) {
-  /*
-  * TODO setup local cdn for supported libraries map
-  */
-  const LOCAL_CDN = process.env.LOCAL_CDN
-  const CDN_ARRAY = [
-    'https://stackpath.bootstrapcdn.com',
-    'https://use.fontawesome.com',
-    'https://unpkg.com'
-  ]
-
-  let indexFile
-  fs.readFile(path.join(PUBLIC_PATH, 'index.html'), 'utf-8', (err, data) => {
-    if (err) throw err
-    if (!LOCAL_CDN) {
-      indexFile = data
-      return
-    }
-    const regexString = CDN_ARRAY.join('|').replace(/\/|\./g, s => `\\${s}`)
-    const regex = new RegExp(regexString, 'gm')
-    indexFile = data.replace(regex, LOCAL_CDN)
-  })
-  
-  app.get('/', bkwdMdl, (_req, res) => {
-    if (!indexFile) return res.status(404).end()
-    res.setHeader('Content-Type', 'text/html; charset=utf-8')
-    return res.status(200).send(indexFile)
-  })
-}
-
 app.use((_req, res, next) => {
   res.setHeader('Referrer-Policy', 'origin-when-cross-origin')
   next()
@@ -182,23 +150,42 @@ app.get('/', (req, res, next) => {
     middelware(req, res, next)
   }
 
-}, bkwdMdl, cookieParser(), (req, res) => {
+}, bkwdMdl, cookieParser(), async (req, res) => {
+  res.setHeader('Content-Type', 'text/html')
+
+  let returnIndex = indexTemplate
+
+  if (!!process.env.LOCAL_CDN) {
+    const CDN_ARRAY = [
+      'https://stackpath.bootstrapcdn.com',
+      'https://use.fontawesome.com',
+      'https://unpkg.com'
+    ]
+  
+    const regexString = CDN_ARRAY.join('|').replace(/\/|\./g, s => `\\${s}`)
+    const regex = new RegExp(regexString, 'gm')
+    returnIndex = returnIndex.replace(regex, process.env.LOCAL_CDN)
+  }
   const iavError = req.cookies && req.cookies['iav-error']
   
-  res.setHeader('Content-Type', 'text/html')
+  const attributeToAppend = {}
 
   if (iavError) {
     res.clearCookie('iav-error', { httpOnly: true, sameSite: 'strict' })
+    attributeToAppend[CONST.DATA_ERROR_ATTR] = iavError
+  }
 
-    const returnTemplate = indexTemplate
-      .replace(/\$\$NONCE\$\$/g, res.locals.nonce)
-      .replace('<atlas-viewer>', `<atlas-viewer data-error="${iavError.replace(/"/g, '&quot;')}">`)
-    res.status(200).send(returnTemplate)
-  } else {
-    const returnTemplate = indexTemplate
-      .replace(/\$\$NONCE\$\$/g, res.locals.nonce)
-    res.status(200).send(returnTemplate)
+  if (!!process.env.OVERWRITE_API_ENDPOING) {
+    attributeToAppend[CONST.OVERWRITE_SAPI_ENDPOINT_ATTR] = process.env.OVERWRITE_API_ENDPOING
   }
+  
+  const attr = Object.entries(attributeToAppend).map(([key, value]) => `${key}="${value.replace(/"/g, '&quot;')}"`).join(" ")
+
+  const returnTemplate = returnIndex
+    .replace(/\$\$NONCE\$\$/g, res.locals.nonce)
+    .replace('<atlas-viewer>', `<atlas-viewer ${attr}>`)
+
+  res.status(200).send(returnTemplate)
 })
 
 app.get('/ready', async (req, res) => {
diff --git a/docs/releases/v2.12.0.md b/docs/releases/v2.12.0.md
index ba23e2fa1c0a5e1dc3cdb00fdfef918654e02bf0..99ad15e06c399f56481ee50d135ecb889ae3e1ec 100644
--- a/docs/releases/v2.12.0.md
+++ b/docs/releases/v2.12.0.md
@@ -13,4 +13,5 @@
 ## Behind the scene
 
 - update spotlight mechanics from in-house to angular CDK
-- Updated neuroglancer/nehuba dependency. This allows volumes with non-rigid affine to be displayed properly.
+- updated neuroglancer/nehuba dependency. This allows volumes with non-rigid affine to be displayed properly.
+- allow siibra-api endpoint to be configured at runtime
diff --git a/src/atlasComponents/sapi/sapi.service.ts b/src/atlasComponents/sapi/sapi.service.ts
index b5aaf5bc4a03501f57dda3e3312240f67b800343..0750c09315d429dee2d64b41680eecdd6288b35a 100644
--- a/src/atlasComponents/sapi/sapi.service.ts
+++ b/src/atlasComponents/sapi/sapi.service.ts
@@ -13,6 +13,7 @@ import {
 import { FeatureType, PathReturn, RouteParam, SapiRoute } from "./typeV3";
 import { BoundingBox, SxplrAtlas, SxplrParcellation, SxplrRegion, SxplrTemplate, VoiFeature, Feature } from "./sxplrTypes";
 import { parcBanList, speciesOrder } from "src/util/constants";
+import { CONST } from "common/constants"
 
 export const useViewer = {
   THREESURFER: "THREESURFER",
@@ -94,7 +95,12 @@ export class SAPI{
    */
   static get BsEndpoint$(): Observable<string> {
     if (!!BS_ENDPOINT_CACHED_VALUE) return BS_ENDPOINT_CACHED_VALUE
-    const endpoints = environment.SIIBRA_API_ENDPOINTS.split(',')
+    const rootEl = document.querySelector('atlas-viewer')
+    const overwriteSapiUrl = rootEl?.getAttribute(CONST.OVERWRITE_SAPI_ENDPOINT_ATTR)
+    
+    const endpoints = overwriteSapiUrl
+      ? [ overwriteSapiUrl ]
+      : environment.SIIBRA_API_ENDPOINTS.split(',')
     if (endpoints.length === 0) {
       SAPI.ErrorMessage = `No siibra-api endpoint defined!`
       return NEVER
diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts
index c28d15f9a80133259294b4de407a543014adcf78..b80a7c4de7fa4836e9916ccff47e5aa228385fdf 100644
--- a/src/atlasViewer/atlasViewer.component.ts
+++ b/src/atlasViewer/atlasViewer.component.ts
@@ -76,11 +76,11 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
     @Inject(DARKTHEME) private darktheme$: Observable<boolean>
   ) {
 
-    const error = this.el.nativeElement.getAttribute('data-error')
+    const error = this.el.nativeElement.getAttribute(CONST.DATA_ERROR_ATTR)
 
     if (error) {
       this.snackbar.open(error, 'Dismiss', { duration: 5000 })
-      this.el.nativeElement.removeAttribute('data-error')
+      this.el.nativeElement.removeAttribute(CONST.DATA_ERROR_ATTR)
     }
   }
 
diff --git a/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts b/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts
index 7db5392a46b74554df8c9e2db2ab8d5e221ab44b..f5bea95d183a58649bb52caedbd27430cfc2b47c 100644
--- a/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts
+++ b/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts
@@ -107,6 +107,10 @@ describe('> mesh.service.ts', () => {
             [labelIndex2]: fits2
           }
         })
+        const mockStore = TestBed.inject(MockStore)
+        
+        mockStore.overrideSelector(atlasSelection.selectors.selectedTemplate, {} as any)
+        mockStore.overrideSelector(atlasSelection.selectors.selectedParcellation, {} as any)
       })
 
       describe("> auxMesh defined", () => {
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
index ed281a389241e4a8fbaec5c004557601f2af1aa2..89b3a42f99d93c8222d314784bfa9b6057de5aa1 100644
--- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
@@ -307,8 +307,10 @@ describe('> nehubaViewer.component.ts', () => {
     describe('> # setColorMap', () => {
       let nehubaViewerSpy: any
       let ngViewerStatechildrenGetSpy = jasmine.createSpy('get')
-      let toJsonSpy = jasmine.createSpy('toJsonSpy')
-      let restoreStateSpy = jasmine.createSpy('restoreStateSpy')
+      let layersMngerToJsonSpy = jasmine.createSpy('layersMngerToJsonSpy')
+      let posToJsonSpy = jasmine.createSpy('posToJsonSpy')
+      let layerMgerRestoreStateSpy = jasmine.createSpy('layerMgerRestoreStateSpy')
+      let posRestoreStateSpy = jasmine.createSpy("posRestoreStateSpy")
 
       const ngId1 = 'foo-bar'
       const ngId2 = 'hello-world'
@@ -326,11 +328,23 @@ describe('> nehubaViewer.component.ts', () => {
           }
         }
 
-        ngViewerStatechildrenGetSpy.and.returnValue({
-          toJSON: toJsonSpy,
-          restoreState: restoreStateSpy,
+        ngViewerStatechildrenGetSpy.and.callFake(prop => {
+          if (prop === "position") {
+            return {
+              toJSON: posToJsonSpy,
+              restoreState: posRestoreStateSpy
+            }
+          }
+          if (prop === "layers") {
+            return {
+              toJSON: layersMngerToJsonSpy,
+              restoreState: layerMgerRestoreStateSpy,
+            }
+          }
+          throw new Error(`prop ${prop} is not anticipated`)
         })
-        toJsonSpy.and.returnValue([{
+        posToJsonSpy.and.returnValue([1.1, 2.2, 3.3])
+        layersMngerToJsonSpy.and.returnValue([{
           name: ngId1
         }, {
           name: ngId2
@@ -338,8 +352,8 @@ describe('> nehubaViewer.component.ts', () => {
       })
       afterEach(() => {
         ngViewerStatechildrenGetSpy.calls.reset()
-        toJsonSpy.calls.reset()
-        restoreStateSpy.calls.reset()
+        layersMngerToJsonSpy.calls.reset()
+        layerMgerRestoreStateSpy.calls.reset()
       })
       it('> calls nehubaViewer.restoreState', () => {
         const fixture = TestBed.createComponent(NehubaViewerUnit)
@@ -359,7 +373,7 @@ describe('> nehubaViewer.component.ts', () => {
 
         fixture.componentInstance['setColorMap'](mainMap)
 
-        expect(restoreStateSpy).toHaveBeenCalledOnceWith([{
+        expect(layerMgerRestoreStateSpy).toHaveBeenCalledOnceWith([{
           name: ngId1,
           segmentColors: {
             1: rgbToHex([100, 100, 100]),
@@ -372,6 +386,10 @@ describe('> nehubaViewer.component.ts', () => {
             2: rgbToHex([20, 20, 20]),
           }
         }])
+
+        expect(posRestoreStateSpy).toHaveBeenCalledOnceWith(
+          [ 1.1, 2.2, 3.3 ]
+        )
       })
     })
 
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
index 017187edb71c29fc59bc0dd589790a6dc3bb4e02..0bee1941f311aee0df808e66678299649bcc6128 100644
--- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
@@ -505,7 +505,6 @@ export class NehubaViewerUnit implements OnDestroy {
           ...rest,
           ...(transform ? { transform } : {})
         }
-        console.log(combined)
         viewer.layerManager.addManagedLayer(
           viewer.layerSpecification.getLayer(key, combined))
 
@@ -820,7 +819,14 @@ export class NehubaViewerUnit implements OnDestroy {
        */
     }
 
+    /**
+     * n.b. 2
+     * updating layer colormap seems to also mess up the position ()
+     */
+
     const layersManager = this.nehubaViewer.ngviewer.state.children.get("layers")
+    const position = this.nehubaViewer.ngviewer.state.children.get("position")
+    const prevPos = position.toJSON()
     const layerJson = layersManager.toJSON()
     for (const layer of layerJson) {
       if (layer.name in mainDict) {
@@ -828,6 +834,7 @@ export class NehubaViewerUnit implements OnDestroy {
       }
     }
     layersManager.restoreState(layerJson)
+    position.restoreState(prevPos)
     this.#triggerMeshLoad$.next(null)
   }
 }
diff --git a/third_party/vanilla.html b/third_party/vanilla.html
index be223e1e38ab861e649e4c4666e894d0a48c6f8c..5513962d41c1a6cb07e54a164e06a6bae15784f5 100644
--- a/third_party/vanilla.html
+++ b/third_party/vanilla.html
@@ -9,6 +9,7 @@
   <script src="main.bundle.js"></script>
   <link rel="stylesheet" href="vanilla_styles.css">
   <link rel="stylesheet" href="main.css">
+  <link rel="stylesheet" href="vanillaMain.css">
 </head>
 <body>
   <div id="neuroglancer-container"></div>