diff --git a/deploy/app.js b/deploy/app.js
new file mode 100644
index 0000000000000000000000000000000000000000..f2fb025c607568088c25a7de6a01339bdeaf45a4
--- /dev/null
+++ b/deploy/app.js
@@ -0,0 +1,64 @@
+const path = require('path')
+const express = require('express')
+const app = express()
+const session = require('express-session')
+const MemoryStore = require('memorystore')(session)
+
+app.disable('x-powered-by')
+
+if (process.env.NODE_ENV !== 'production') {
+  app.use(require('cors')())
+}
+
+/**
+ * load env first, then load other modules
+ */
+
+const configureAuth = require('./auth')
+
+const store = new MemoryStore({
+  checkPeriod: 86400000
+})
+
+const SESSIONSECRET = process.env.SESSIONSECRET || 'this is not really a random session secret'
+
+/**
+ * passport application of oidc requires session
+ */
+app.use(session({
+  secret: SESSIONSECRET,
+  resave: true,
+  saveUninitialized: false,
+  store
+}))
+
+/**
+ * configure Auth
+ * async function, but can start server without
+ */
+configureAuth(app)
+
+const catchError = require('./catchError')
+app.use(catchError)
+
+const publicPath = process.env.NODE_ENV === 'production'
+  ? path.join(__dirname, 'public')
+  : path.join(__dirname, '..', 'dist', 'aot')
+app.use(express.static(publicPath))
+
+app.use((req, res, next) => {
+  res.set('Content-Type', 'application/json')
+  next()
+})
+
+const templateRouter = require('./templates')
+const nehubaConfigRouter = require('./nehubaConfig')
+const datasetRouter = require('./datasets')
+const pluginRouter = require('./plugins')
+
+app.use('/templates', templateRouter)
+app.use('/nehubaConfig', nehubaConfigRouter)
+app.use('/datasets', datasetRouter)
+app.use('/plugins', pluginRouter)
+
+module.exports = app
\ No newline at end of file
diff --git a/deploy/plugins/index.js b/deploy/plugins/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..9f15b4687a85be1f07b76284694132fd0fb799eb
--- /dev/null
+++ b/deploy/plugins/index.js
@@ -0,0 +1,19 @@
+/**
+ * TODO
+ * how to discover plugins?
+ */
+
+const express = require('express')
+const router = express.Router()
+const PLUGIN_URLS = process.env.PLUGIN_URLS && JSON.stringify(process.env.PLUGIN_URLS.split(';'))
+
+router.get('', (_req, res) => {
+
+  if (PLUGIN_URLS) {
+    return res.status(200).send(PLUGIN_URLS)
+  } else {
+    return res.status(200).send('[]')
+  }
+})
+
+module.exports = router
\ No newline at end of file
diff --git a/deploy/server.js b/deploy/server.js
index cd6a1da4c2e72ac70be1157b88de454e2e993d7f..85596178cccb7433a77195c447e3efa665cd2583 100644
--- a/deploy/server.js
+++ b/deploy/server.js
@@ -1,73 +1,11 @@
-const path = require('path')
-const express = require('express')
-const app = express()
-const session = require('express-session')
-const MemoryStore = require('memorystore')(session)
-
-app.disable('x-powered-by')
-
 if (process.env.NODE_ENV !== 'production') {
   require('dotenv').config()
-  app.use(require('cors')())
   process.on('unhandledRejection', (err, p) => {
     console.log({err, p})
   })
 }
 
-/**
- * load env first, then load other modules
- */
-
-const configureAuth = require('./auth')
-
-const store = new MemoryStore({
-  checkPeriod: 86400000
-})
-
-const SESSIONSECRET = process.env.SESSIONSECRET || 'this is not really a random session secret'
-
-/**
- * passport application of oidc requires session
- */
-app.use(session({
-  secret: SESSIONSECRET,
-  resave: true,
-  saveUninitialized: false,
-  store
-}))
-
-const startServer = async (app) => {
-  try{
-    await configureAuth(app)
-  }catch (e) {
-    console.log('error during configureAuth', e)
-  }
-
-  const catchError = require('./catchError')
-  app.use(catchError)
-
-  const publicPath = process.env.NODE_ENV === 'production'
-    ? path.join(__dirname, 'public')
-    : path.join(__dirname, '..', 'dist', 'aot')
-  app.use(express.static(publicPath))
-
-  app.use((req, res, next) => {
-    res.set('Content-Type', 'application/json')
-    next()
-  })
-  
-  const templateRouter = require('./templates')
-  const nehubaConfigRouter = require('./nehubaConfig')
-  const datasetRouter = require('./datasets')
-  
-  
-  app.use('/templates', templateRouter)
-  app.use('/nehubaConfig', nehubaConfigRouter)
-  app.use('/datasets', datasetRouter)
-  
-  const PORT = process.env.PORT || 3000
-  
-  app.listen(PORT, () => console.log(`listening on port ${PORT}`))
-}
+const app = require('./app')
+const PORT = process.env.PORT || 3000
 
-startServer(app)
\ No newline at end of file
+app.listen(PORT, () => console.log(`listening on port ${PORT}`))
\ No newline at end of file
diff --git a/src/atlasViewer/atlasViewer.dataService.service.ts b/src/atlasViewer/atlasViewer.dataService.service.ts
index 182bd824b534c62d1744263c4f25d9a97d9f695b..ee42e02befd2533f36d80be12359691001f95490 100644
--- a/src/atlasViewer/atlasViewer.dataService.service.ts
+++ b/src/atlasViewer/atlasViewer.dataService.service.ts
@@ -20,6 +20,17 @@ export class AtlasViewerDataService implements OnDestroy{
       PLUGINDEV
         ? fetch(PLUGINDEV).then(res => res.json())
         : Promise.resolve([]),
+      new Promise(resolve => {
+        fetch(`${this.constantService.backendUrl}plugins`)
+          .then(res => res.json())
+          .then(arr => Promise.all(
+            arr.map(url => fetch(url).then(res => res.json()))
+          ))
+          .then(resolve)
+          .catch(e => {
+            resolve([])
+          })
+      }),
       Promise.all(
         BUNDLEDPLUGINS
           .filter(v => typeof v === 'string')
diff --git a/src/atlasViewer/atlasViewer.pluginService.service.ts b/src/atlasViewer/atlasViewer.pluginService.service.ts
index 8bf27a5f3a7dee3cbcda9be7ba4a62b1acee0ee2..6a3d590f639535b9f3fb80e1298ccc599307ac54 100644
--- a/src/atlasViewer/atlasViewer.pluginService.service.ts
+++ b/src/atlasViewer/atlasViewer.pluginService.service.ts
@@ -53,7 +53,7 @@ export class PluginServices{
             fetch(plugin.scriptURL)
               .then(res=>res.text())
               .then(script=>plugin.script = script) :
-            Promise.reject('both template and templateURL are not defined') 
+            Promise.reject('both script and scriptURL are not defined') 
       ])
   }
 
@@ -108,6 +108,7 @@ export class PluginServices{
         const widgetCompRef = this.widgetService.addNewWidget(pluginUnit,{
           state : 'floating',
           exitable : true,
+          persistency: plugin.persistency,
           title : plugin.displayName || plugin.name
         })
 
@@ -179,4 +180,5 @@ export interface PluginManifest{
   script? : string 
   initState? : any
   initStateUrl? : string
+  persistency? : boolean
 }
\ No newline at end of file
diff --git a/src/components/toast/toast.component.ts b/src/components/toast/toast.component.ts
index 93b9233ba7b4048b8923cb282b4647530591650a..c0ca259a5e4bbf2da6d61f4027a81a15ad44bd93 100644
--- a/src/components/toast/toast.component.ts
+++ b/src/components/toast/toast.component.ts
@@ -12,6 +12,7 @@ import { toastAnimation } from "./toast.animation";
 
 export class ToastComponent{
   @Input() message : string 
+  @Input() htmlMessage: string
   @Input() timeout : number = 0
   @Input() dismissable : boolean = true
 
diff --git a/src/components/toast/toast.template.html b/src/components/toast/toast.template.html
index 7cd477e5aee8a9afc1793982f94cf3b51037725d..6d1450269366772b1c96930e0178436bbad5d81e 100644
--- a/src/components/toast/toast.template.html
+++ b/src/components/toast/toast.template.html
@@ -4,12 +4,25 @@
 
     </ng-template>
   </div>
-  <div message *ngIf = "message">
+  <div message
+    [innerHTML]="htmlMessage"
+    *ngIf = "htmlMessage">
+  </div>
+  <div
+    message
+    *ngIf="message && !htmlMessage">
     {{ message }}
   </div>
-  <div (click) = "dismiss($event)" *ngIf = "dismissable" close>
-    <i class = "fas fa-remove"></i>
+  <div
+    (click)="dismiss($event)"
+    *ngIf="dismissable" close>
+    <i class="fas fa-remove"></i>
   </div>
-  <timer-component (timerEnd)="dismissed.emit(false)" [pause] = "hover" [timeout] = "timeout" timer>
+  <timer-component
+    *ngIf="timeout > 0"
+    (timerEnd)="dismissed.emit(false)"
+    [pause]="hover"
+    [timeout]="timeout"
+    timer>
   </timer-component>
 </div>
\ No newline at end of file
diff --git a/src/ui/databrowserModule/databrowser.service.ts b/src/ui/databrowserModule/databrowser.service.ts
index 24b811e80dc8983d10183d068677123e2a8c3473..2ebc06a8ecbb7797d53e5bb9315876b45d5371a4 100644
--- a/src/ui/databrowserModule/databrowser.service.ts
+++ b/src/ui/databrowserModule/databrowser.service.ts
@@ -93,7 +93,6 @@ export class DatabrowserService implements OnDestroy{
     this.subscriptions.push(
       this.selectedRegions$.subscribe(r => {
         this.selectedRegions = r
-        console.log(r)
         this.workerService.worker.postMessage({
           type: 'BUILD_REGION_SELECTION_TREE',
           selectedRegions: r,
diff --git a/src/ui/menuicons/menuicons.component.ts b/src/ui/menuicons/menuicons.component.ts
index 45a8a671c96b7fae1508c0dba3a38881503418f2..f6e80ca7c12885050621d1fdc3ad696c28486eb9 100644
--- a/src/ui/menuicons/menuicons.component.ts
+++ b/src/ui/menuicons/menuicons.component.ts
@@ -4,6 +4,7 @@ import { WidgetServices } from "src/atlasViewer/widgetUnit/widgetService.service
 import { WidgetUnit } from "src/atlasViewer/widgetUnit/widgetUnit.component";
 import { LayerBrowser } from "src/ui/layerbrowser/layerbrowser.component";
 import { DataBrowser } from "src/ui/databrowserModule/databrowser/databrowser.component";
+import { PluginBannerUI } from "../pluginBanner/pluginBanner.component";
 
 @Component({
   selector: 'menu-icons',
@@ -22,7 +23,6 @@ export class MenuIconsBar{
   dataBrowser: ComponentRef<DataBrowser> = null
   dbWidget: ComponentRef<WidgetUnit> = null
 
-
   /**
    * layerBrowser
    */
@@ -30,6 +30,13 @@ export class MenuIconsBar{
   layerBrowser: ComponentRef<LayerBrowser> = null
   lbWidget: ComponentRef<WidgetUnit> = null
 
+  /**
+   * pluginBrowser
+   */
+  pbcf: ComponentFactory<PluginBannerUI>
+  pluginBanner: ComponentRef<PluginBannerUI> = null
+  pbWidget: ComponentRef<WidgetUnit> = null
+
   constructor(
     private widgetServices:WidgetServices,
     private injector:Injector,
@@ -37,6 +44,7 @@ export class MenuIconsBar{
   ){
     this.dbcf = cfr.resolveComponentFactory(DataBrowser)
     this.lbcf = cfr.resolveComponentFactory(LayerBrowser)
+    this.pbcf = cfr.resolveComponentFactory(PluginBannerUI)
   }
   public clickSearch(event: MouseEvent){
     if (this.dbWidget) {
@@ -92,6 +100,32 @@ export class MenuIconsBar{
     this.lbWidget.instance.position = [left, top]
   }
 
+  public clickPlugins(event: MouseEvent){
+    if(this.pbWidget) {
+      this.pbWidget.destroy()
+      this.pbWidget = null
+      return
+    }
+    this.pluginBanner = this.pbcf.create(this.injector)
+    this.pbWidget = this.widgetServices.addNewWidget(this.pluginBanner, {
+      exitable: true,
+      persistency: true,
+      state: 'floating',
+      title: 'Plugin Browser',
+      titleHTML: '<i class="fas fa-tools"></i> Plugin Browser'
+    })
+
+    this.pbWidget.onDestroy(() => {
+      this.pbWidget = null
+      this.pluginBanner = null
+    })
+
+    const el = event.currentTarget as HTMLElement
+    const top = el.offsetTop
+    const left = el.offsetLeft + 50
+    this.pbWidget.instance.position = [left, top]
+  }
+
   get databrowserIsShowing() {
     return this.dataBrowser !== null
   }
@@ -100,6 +134,10 @@ export class MenuIconsBar{
     return this.layerBrowser !== null
   }
 
+  get pluginbrowserIsShowing() {
+    return this.pluginBanner !== null
+  }
+
   get dataBrowserTitle() {
     return `Browse`
   }
diff --git a/src/ui/menuicons/menuicons.template.html b/src/ui/menuicons/menuicons.template.html
index 8f40b4e913de40da340d571b4e0e587afa09f110..1b6ac141f3180b05a63a433693bf2be048be5fd1 100644
--- a/src/ui/menuicons/menuicons.template.html
+++ b/src/ui/menuicons/menuicons.template.html
@@ -27,8 +27,10 @@
 </div>
 <div
   tooltip="Plugins"
+  (click)="clickPlugins($event)"
   placement="right"
-  class="btn btn-sm btn-secondary rounded-circle">
+  [ngClass]="pluginbrowserIsShowing ? 'btn-primary' : 'btn-secondary'"
+  class="btn btn-sm rounded-circle">
   <i class="fas fa-tools">
     
   </i>
diff --git a/src/ui/pluginBanner/pluginBanner.component.ts b/src/ui/pluginBanner/pluginBanner.component.ts
index 11c044191da1e0374d728b27e6b6ed3af2143b3e..0b4fe404a110a3d82cfdade361e894e0a2aeb92e 100644
--- a/src/ui/pluginBanner/pluginBanner.component.ts
+++ b/src/ui/pluginBanner/pluginBanner.component.ts
@@ -24,7 +24,7 @@ export class PluginBannerUI{
   }
 
   get pluginEnabledFlag(){
-    return PLUGINDEV || BUNDLEDPLUGINS.length > 0
+    return true || PLUGINDEV || BUNDLEDPLUGINS.length > 0
       ? true
       : false
   }
diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts
index e24e6ac310d315cadea1dc1234b3bfab5b45e826..407d38c7562406c11487bd2aa6f0eb0638967338 100644
--- a/src/ui/ui.module.ts
+++ b/src/ui/ui.module.ts
@@ -95,6 +95,7 @@ import { FilterNgLayer } from "src/util/pipes/filterNgLayer.pipe";
     /* dynamically created components needs to be declared here */
     NehubaViewerUnit,
     LayerBrowser,
+    PluginBannerUI
   ],
   exports : [
     SubjectViewer,
diff --git a/src/util/directives/toastContainer.directive.ts b/src/util/directives/toastContainer.directive.ts
index befacad355e8a3e83e8e5cd44621dfdeec67a83b..162e3c6d776fa64dea6dff58ab6e3cf9e7e0c4fc 100644
--- a/src/util/directives/toastContainer.directive.ts
+++ b/src/util/directives/toastContainer.directive.ts
@@ -51,6 +51,7 @@ export class ToastContainerDirective{
 
         toastComponent.instance.dismissable = handler.dismissable
         toastComponent.instance.message = handler.message
+        toastComponent.instance.htmlMessage = handler.htmlMessage
         toastComponent.instance.timeout = handler.timeout
 
         const _subscription = toastComponent.instance.dismissed.subscribe(userInitiated => {
diff --git a/src/util/pluginHandlerClasses/toastHandler.ts b/src/util/pluginHandlerClasses/toastHandler.ts
index deae6df7ca4eb39412cee1e5d0398a7ea95d4198..80d696980b907641d384600bcfe3643163037fe8 100644
--- a/src/util/pluginHandlerClasses/toastHandler.ts
+++ b/src/util/pluginHandlerClasses/toastHandler.ts
@@ -4,4 +4,5 @@ export class ToastHandler{
   dismissable : boolean = true
   show : () => void
   hide : () => void
+  htmlMessage: string
 }
\ No newline at end of file