diff --git a/inst/bluebream.zcml b/inst/bluebream.zcml
index aec67c3..210409f 100644
--- a/inst/bluebream.zcml
+++ b/inst/bluebream.zcml
@@ -40,6 +40,7 @@
   
   
   
+  
   
   
   
diff --git a/inst/bluebream/main.py b/inst/bluebream/main.py
index 93fc76e..a6f445b 100644
--- a/inst/bluebream/main.py
+++ b/inst/bluebream/main.py
@@ -5,8 +5,8 @@ from zope.app.wsgi import config, getWSGIApplication
 
 def run(app, config):
     port = int(config.server_port)
+    #print(f'Serving on port {port}.')
     waitress.serve(app, port=port)
-    print(f'Serving on port {port}.')
 
 
 if __name__ == '__main__':
diff --git a/inst/loops/.env b/inst/loops/.env
new file mode 100644
index 0000000..9defda0
--- /dev/null
+++ b/inst/loops/.env
@@ -0,0 +1,8 @@
+# loops/inst/bluebream/.env
+
+SERVER_PORT=8800
+
+DBNAME=ccotest
+DBUSER=ccotest
+DBPASSWORD=cco
+DBSCHEMA=testing
diff --git a/inst/loops/application.zcml b/inst/loops/application.zcml
index c366a69..76de5b3 100644
--- a/inst/loops/application.zcml
+++ b/inst/loops/application.zcml
@@ -3,48 +3,32 @@
    xmlns:browser="http://namespaces.zope.org/browser"
    i18n_domain="main">
 
-  
-  
+  
 
   
-  
   
   
   
   
-  
-  
   
 
-  
   
 
-
-
   
 
-  
-  
-
   
   
   
   
   
   
-  
+  
+
   
-  
-  
+
+  
 
   
   
diff --git a/inst/loops/config.py b/inst/loops/config.py
new file mode 100644
index 0000000..d0d58ed
--- /dev/null
+++ b/inst/loops/config.py
@@ -0,0 +1,16 @@
+# loops/inst/loops/config.py
+
+from dotenv import load_dotenv
+from os import getenv
+
+load_dotenv()
+
+server_port = getenv('SERVER_PORT', '8099')
+
+# storage settings
+from scopes.storage.db.postgres import StorageFactory
+dbengine = 'postgresql+psycopg'
+dbname = getenv('DBNAME', 'demo')
+dbuser = getenv('DBUSER', 'demo')
+dbpassword = getenv('DBPASSWORD', 'secret')
+dbschema = getenv('DBSCHEMA', 'demo')
diff --git a/inst/loops/main.py b/inst/loops/main.py
new file mode 100644
index 0000000..efe01b1
--- /dev/null
+++ b/inst/loops/main.py
@@ -0,0 +1,15 @@
+# loops/inst/loops/main.py
+
+import waitress
+from zope.app.wsgi import config, getWSGIApplication
+
+def run(app, config):
+    port = int(config.server_port)
+    #print(f'Serving on port {port}.')
+    waitress.serve(app, port=port)
+
+
+if __name__ == '__main__':
+    import config
+    app = getWSGIApplication('zope.conf')
+    run(app, config)
diff --git a/inst/loops/notfound.pt b/inst/loops/notfound.pt
new file mode 100644
index 0000000..f3d4483
--- /dev/null
+++ b/inst/loops/notfound.pt
@@ -0,0 +1,18 @@
+
+
+
+
+  The page that you are trying to access is not available
+
+
 
+Please note the following:
+
+
+   -  You might have misspelled the url 
 
+   - 
+     You might be trying to access a non-existing page
+   
 
+
+
+
\ No newline at end of file
diff --git a/inst/loops/overrides.zcml b/inst/loops/overrides.zcml
index e1d9b9c..397495e 100644
--- a/inst/loops/overrides.zcml
+++ b/inst/loops/overrides.zcml
@@ -43,6 +43,6 @@
       class="zope.app.exception.browser.notfound.NotFound"
       />
 
-  
+  
 
 
diff --git a/inst/loops/zope.conf b/inst/loops/zope.conf
new file mode 100644
index 0000000..ab4a5e2
--- /dev/null
+++ b/inst/loops/zope.conf
@@ -0,0 +1,39 @@
+# loops/inst/bluebream/zope.conf
+# main zope configuration file for deployment
+
+# Identify the component configuration used to define the site:
+site-definition application.zcml
+
+
+
+  
+    path var/filestorage/Data.fs
+    blob-dir var/blob
+  
+
+# Uncomment this if you want to connect to a ZEO server instead:
+#  
+#    server localhost:8100
+#    storage 1
+#    # ZEO client cache, in bytes
+#    cache-size 20MB
+#    # Uncomment to have a persistent disk cache
+#    #client zeo1
+#  
+
+
+
+  # This sets up logging to both a file and to standard output (STDOUT).
+  # The "path" setting can be a relative or absolute filesystem path or
+  # the tokens STDOUT or STDERR.
+
+  
+    path var/log/z3-deploy.log
+    formatter zope.exceptions.log.Formatter
+  
+
+  
+    path STDOUT
+    formatter zope.exceptions.log.Formatter
+  
+
diff --git a/loops/browser/mobile/default.py b/loops/browser/mobile/default.py
index a252fd6..391894b 100644
--- a/loops/browser/mobile/default.py
+++ b/loops/browser/mobile/default.py
@@ -1,31 +1,11 @@
-#
-#  Copyright (c) 2011 Helmut Merz helmutm@cy55.de
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#
+# loops.browser.mobile.default
 
-"""
-Default layouts for the loops mobile skin.
-
-$Id$
+""" Default layouts for the loops mobile skin.
 """
 
 from zope.app.pagetemplate import ViewPageTemplateFile
 from zope.cachedescriptors.property import Lazy
 from zope import component
-from zope.interface import implements
 
 from cybertools.browser.renderer import RendererFactory
 from cybertools.composer.layout.base import Layout
diff --git a/loops/config/configure.zcml b/loops/config/configure.zcml
index cec2980..26a85f5 100644
--- a/loops/config/configure.zcml
+++ b/loops/config/configure.zcml
@@ -5,35 +5,42 @@
    xmlns:browser="http://namespaces.zope.org/browser"
    i18n_domain="zope">
 
-  
+  
 
-  
+  
   
     
   
 
-  
+  
   
     
   
 
-  
+  
   
     
   
 
-  
+  
   
     
   
 
-  
+  
   
     
   
 
   
   
 
   
@@ -46,5 +53,4 @@
         factory="loops.config.browser.ConfiguratorView"
         permission="zope.View" />
 
-
 
diff --git a/loops/configure.zcml b/loops/configure.zcml
index 9a795ad..8d9240d 100644
--- a/loops/configure.zcml
+++ b/loops/configure.zcml
@@ -524,6 +524,5 @@
   
   
   
-  
 
 
diff --git a/loops/expert/standard.py b/loops/expert/standard.py
index 4814019..bc18bc8 100644
--- a/loops/expert/standard.py
+++ b/loops/expert/standard.py
@@ -1,23 +1,6 @@
-#
-#  Copyright (c) 2013 Helmut Merz helmutm@cy55.de
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#
+# loops.expert.standard
 
-"""
-loops standard reports.
+""" loops standard reports.
 """
 
 from zope.cachedescriptors.property import Lazy
@@ -49,6 +32,6 @@ class TypeInstances(ReportInstance):
         for t in self.targets:
             for c in t.getChildren([self.view.typePredicate]):
                 result.append(c)
-        print '***', self.targets, result
+        print('***', self.targets, result)
         return result
 
diff --git a/loops/layout/browser/traversal.py b/loops/layout/browser/traversal.py
index 536f093..29dd820 100644
--- a/loops/layout/browser/traversal.py
+++ b/loops/layout/browser/traversal.py
@@ -1,25 +1,6 @@
-#
-#  Copyright (c) 2009 Helmut Merz helmutm@cy55.de
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#
+# loops.layout.browser.traveral
 
-"""
-Layout node traversers.
-
-$Id$
+""" Layout node traversers.
 """
 
 from zope.app.container.traversal import ItemTraverser
@@ -79,7 +60,7 @@ class NodeTraverser(ItemTraverser):
         if obj is None:
             try:
                 obj = super(NodeTraverser, self).publishTraverse(request, name)
-            except NotFound, e:
+            except NotFound:
                 viewAnnotations['pageName'] = name
                 return self.context
         return obj
diff --git a/loops/media/browser/admin.py b/loops/media/browser/admin.py
index fa7ac70..67d9e33 100644
--- a/loops/media/browser/admin.py
+++ b/loops/media/browser/admin.py
@@ -1,27 +1,8 @@
-#
-#  Copyright (c) 2008 Helmut Merz helmutm@cy55.de
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#
+# loops.media.browser.admin
 
-"""
-View for regenerating all transformed media assets.
+""" View for regenerating all transformed media assets.
 
 Authors: Johann Schimpf, Erich Seifert.
-
-$Id$
 """
 
 from logging import getLogger
@@ -72,7 +53,7 @@ class ChangeSubdirectories(object):
             found += 1
             sp = obj._storageParams
             subdir = sp.get('subdirectory', '')
-            print subdir
+            print(subdir)
             if self.search in subdir:
                 changed += 1
                 sp['subdirectory'] = subdir.replace(self.search, self.replace)
diff --git a/loops/target.py b/loops/target.py
index a1c7cee..b7e4b35 100644
--- a/loops/target.py
+++ b/loops/target.py
@@ -1,32 +1,13 @@
-#
-#  Copyright (c) 2006 Helmut Merz helmutm@cy55.de
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#
+# loops.target
 
-"""
-Adapter classes (proxies, in fact), for providing access to concepts and
+""" Adapter classes (proxies, in fact), for providing access to concepts and
 resources e.g. from forms that are called on view/node objects.
-
-$Id$
 """
 
 from zope.cachedescriptors.property import Lazy
 from zope.component import adapts
 from zope.i18nmessageid import MessageFactory
-from zope.interface import implements
+from zope.interface import implementer
 from zope import schema
 from zope.security.proxy import removeSecurityProxy
 
@@ -63,9 +44,9 @@ class TargetProxy(object):
     title = property(getTitle, setTitle)
 
 
+@implementer(IConcept)
 class ConceptProxy(TargetProxy):
 
-    implements(IConcept)
     adapts(IConceptView)
 
     def getConceptType(self): return self.target.conceptType
@@ -93,9 +74,9 @@ class ResourceProxy(TargetProxy):
     contentType = property(getContentType, setContentType)
 
 
+@implementer(IDocument)
 class DocumentProxy(ResourceProxy):
 
-    implements(IDocument)
     adapts(IDocumentView)
 
     def setData(self, data):
@@ -105,9 +86,9 @@ class DocumentProxy(ResourceProxy):
     data = property(getData, setData)
 
 
+@implementer(IMediaAsset)
 class MediaAssetProxy(ResourceProxy):
 
-    implements(IMediaAsset)
     adapts(IMediaAssetView)
 
     def setData(self, data): self.target.data = data
diff --git a/pyproject.toml b/pyproject.toml
index fea6fd9..ada7a00 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -36,6 +36,7 @@ dependencies = [
 server = [
 	"waitress", 
 	"ZConfig", 
+	"zope.app.catalog",
 	"zope.app.form",
 	"zope.app.intid",
 	"zope.app.publication",