diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index d5a2c7632e717937308cdc6de08d08933ddbb7ef..d555dbf5163fbd73ad4b63f5bddb64d591ff0c61 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,11 +1,14 @@
-Changes in Release 2.2.x
+Changes in Release 2.3.0
 --------------------------------------------------------------------------------
 
  * Add AAindex databases (Kawashima et al., 2000)
  * Added experimental molecular structure format OMF (OpenStructure Minimal 
    Format)
  * Removed GUI components in containers
- * DSSP 4.0 support in DSSP binding (https://github.com/PDB-REDO/dssp)
+ * Support to call external DSSP program has been deprecated.
+   ost.bindings.dssp.AssignDSSP still exists with the "old" interface but mimics
+   the equivalent behaviour with the OpenStructure internal secondary structure
+   and solvent accessibility algorithms.
  * mol.alg.PDBize does not turn plain polymer chains (not marked peptide or
    nucleotide) into ligand chains anymore
  * Remove ENABLE_IMG flag in cmake build system - img module is always built now
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ba6cfe36f4e204a17cc4bd53e33e9c55cf021599..c118d3d0a100c940124a48699f21918d43dc815a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,7 +14,7 @@ cmake_policy(SET CMP0060 NEW)
 project(OpenStructure CXX C)
 set (CMAKE_EXPORT_COMPILE_COMMANDS 1)
 set (OST_VERSION_MAJOR 2)
-set (OST_VERSION_MINOR 2)
+set (OST_VERSION_MINOR 3)
 set (OST_VERSION_PATCH 0)
 set (OST_VERSION_STRING ${OST_VERSION_MAJOR}.${OST_VERSION_MINOR}.${OST_VERSION_PATCH} )
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake_support)
diff --git a/docker/Dockerfile b/docker/Dockerfile
index ed7b8286f28749ab09d157bf5621664443257119..0711b1a5aad8bf01ebfc501c57bf0f2f77269d6a 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -2,7 +2,7 @@ FROM ubuntu:20.04
 
 # ARGUMENTS
 ###########
-ARG OPENSTRUCTURE_VERSION="2.2.0"
+ARG OPENSTRUCTURE_VERSION="2.3.0"
 ARG SRC_FOLDER="/usr/local/src"
 ARG CPUS_FOR_MAKE=2
 ARG MSMS_VERSION="2.6.1"
@@ -32,7 +32,6 @@ RUN apt-get update -y && apt-get install -y cmake \
                                             doxygen \
                                             swig \
                                             clustalw \
-                                            dssp \
                                             locales && \
                                             # CLEANUP
                                             rm -rf /var/lib/apt/lists/*
diff --git a/modules/base/pymod/table.py b/modules/base/pymod/table.py
index 6c9a039f91430512bd800a3c82a51d9d02b047cd..1e20e4eae4100d265a6e7c3c008d9216a4e1516a 100644
--- a/modules/base/pymod/table.py
+++ b/modules/base/pymod/table.py
@@ -2959,10 +2959,11 @@ Statistics for column %(col)s
       plt.title(title, size='x-large', fontweight='bold')
       plt.ylabel(y_title, size='x-large')
       plt.xlabel(x_title, size='x-large')
-      
-      plt.xscale('log', basex=10)
+      try:
+        plt.xscale('log', basex=10)
+      except:
+        plt.xscale('log', base=10) # breaking change in matplotlib 3.5
       plt.xlim(0.001, 1.0)
-      
 
       if save:
         plt.savefig(save)
diff --git a/modules/bindings/doc/dssp.rst b/modules/bindings/doc/dssp.rst
index 3a51a870c9c740b495baa5caa53c0d1475818d77..1a087bcfae35cbdab99549f234fd6e2fe4dd5667 100644
--- a/modules/bindings/doc/dssp.rst
+++ b/modules/bindings/doc/dssp.rst
@@ -13,6 +13,18 @@ hydrogen bonding patterns and geometric features.
 
 The program can be downloaded from `<http://swift.cmbi.ru.nl/gv/dssp/>`_.
 
+.. warning::
+  Support of the DSSP program has been deprecated. OpenStructure provides
+  functionality to assign equivalent secondary structures
+  (:func:`ost.mol.alg.AssignSecStruct`) and solvent accessibility
+  (:func:`ost.mol.alg.Accessibility`). You're advised to use these
+  algorithms.
+
+  :func:`AssignDSSP` still exists and provides the "old" interface but
+  internally uses the OpenStructure impmlementations and does not call
+  an external program anymore.
+
+
 Examples
 --------------------------------------------------------------------------------
 
diff --git a/modules/bindings/pymod/blast.py b/modules/bindings/pymod/blast.py
index 2cff9231cffc91418d604e2caabb0c57292f647a..ba82c33973b12cb7989ae7130fcd6b48ec4d4a4a 100644
--- a/modules/bindings/pymod/blast.py
+++ b/modules/bindings/pymod/blast.py
@@ -148,12 +148,12 @@ def CreateDB(infasta, dbout, mkdb_cmd=None):
   """
   if mkdb_cmd==None:
     try:
-      exe=settings.Locate('formatdb')
-      args=[exe, '-i', infasta, '-n', dbout]
+      exe=settings.Locate('makeblastdb')
+      args=[exe, '-in', infasta, '-out', dbout, '-dbtype', 'prot']
     except:
       try:
-        exe=settings.Locate('makeblastdb')
-        args=[exe, '-in', infasta, '-out', dbout, '-dbtype', 'prot']
+        exe=settings.Locate('formatdb')
+        args=[exe, '-i', infasta, '-n', dbout]
       except:
         raise RuntimeError('could not find makeblastdb/formatdb executable')
   else:
@@ -177,10 +177,10 @@ def BlastVersion(blast_location=None):
   """
 
   try:
-    blast_exe=settings.Locate('blastall',explicit_file_name=blast_location)
+    blast_exe=settings.Locate('blastp',explicit_file_name=blast_location)
   except:
     try:
-      blast_exe=settings.Locate('blastp', explicit_file_name=blast_location)
+      blast_exe=settings.Locate('blastall', explicit_file_name=blast_location)
     except:
       raise RuntimeError('could not find blast executable')
 
@@ -242,10 +242,10 @@ def Blast(query, database, gap_open=11, gap_ext=1, matrix='BLOSUM62',
 
   if blast_location==None:
     try:
-      blast_exe=settings.Locate('blastall')
+      blast_exe=settings.Locate('blastp')
     except:
       try:
-        blast_exe=settings.Locate('blastp')
+        blast_exe=settings.Locate('blastall')
       except:
         raise RuntimeError('could not find blast executable')
   else:
diff --git a/modules/bindings/pymod/dssp.py b/modules/bindings/pymod/dssp.py
index 803ea195004b440212604c5f1d494a53962bdb24..ee179c9a6966283027f5ef93d8c704bc5ba7e8ea 100644
--- a/modules/bindings/pymod/dssp.py
+++ b/modules/bindings/pymod/dssp.py
@@ -59,12 +59,94 @@ def _CalcRelativeSA(residue_type, absolute_sa):
     rel=float(absolute_sa)/(solvent_max_list[residue_indices.find(residue_type)])
   return rel
   
+#
+#def AssignDSSP(ent, pdb_path="", extract_burial_status=False, tmp_dir=None, 
+#               dssp_bin=None):
+#  """
+#  Assign secondary structure states to peptide residues in the structure. This
+#  function uses the DSSP command line program.
+#
+#  If you already have a DSSP output file and would like to assign the secondary 
+#  structure states to an entity, use :func:`LoadDSSP`.
+#  
+#  :param ent: The entity for which the secondary structure should be calculated
+#  :type ent: :class:`~ost.mol.EntityHandle` or :class:`~ost.mol.EntityView`
+#  :param extract_burial_status: If true, also extract burial status and store
+#                                as float-property
+#                                ``relative_solvent_accessibility`` at residue
+#                                level
+#  :param tmp_dir: If set, overrides the default tmp directory of the
+#                  operating system
+#  :param dssp_bin: The path to the DSSP executable
+#  :raises: :class:`~ost.settings.FileNotFound` if the dssp executable is not 
+#      in the path.
+#  :raises: :class:`RuntimeError` when dssp is executed with errors
+#  """
+#
+#  if not ent.IsValid():
+#    raise ValueError('model entity is not valid')
+#  if ent.atom_count==0:
+#    raise ValueError('model entity does not contain any atoms')
+#
+#  dssp_abs_path=settings.Locate(['dsspcmbi','dssp','mkdssp'], env_name='DSSP_EXECUTABLE', 
+#                                explicit_file_name=dssp_bin)
+#  if os.path.isdir(dssp_abs_path):
+#    raise RuntimeError('"%s" is a directory. Specify path to DSSP binary' % dssp_abs_path)
+#  if not os.access(dssp_abs_path, os.X_OK):
+#    raise RuntimeError('"%s" is not executable' % dssp_abs_path)
+#    
+#  pdb_path=tempfile.mktemp(suffix=".pdb",prefix="temp_entity", dir=tmp_dir)
+#  io.SavePDB(ent, pdb_path)
+#  temp_dssp_path=tempfile.mktemp(suffix=".out",prefix="dssp", dir=tmp_dir)  
+#
+#  cmd = [dssp_abs_path, pdb_path, temp_dssp_path]
+#  s = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+#  if s.returncode == 0:
+#    try:
+#      LoadDSSP(temp_dssp_path, ent, extract_burial_status)
+#      _Cleanup(temp_dssp_path, pdb_path)
+#      return ent
+#    except:
+#      pass # continue with dssp 4 fallback
+#  
+#  # either dssp execution failed or output file cannot be loaded
+#  # both can be the result of using dssp 4 (https://github.com/PDB-REDO/dssp) 
+#  # => try fallback
+#
+#  # dssp 4 desperately wants a CRYST1 record (REMOVE IF NOT NEEDED ANYMORE!)
+#  # Be aware, Even more stuff is needed if you don't set the CLIBD_MON env var
+#  # The dssp binding has therefore been deprecated and we mimic to "old" interface
+#  # using OpenStructure internal functionality
+#  with open(pdb_path, 'r') as fh:
+#    file_content = fh.read()
+#  with open(pdb_path, 'w') as fh:
+#    fh.write('CRYST1    1.000    1.000    1.000  90.00  90.00  90.00 P 1           1          ' +
+#             os.linesep + file_content)
+#
+#  # explicitely request dssp output format
+#  cmd = [dssp_abs_path, '--output-format', 'dssp', pdb_path, temp_dssp_path]
+#  s_fallback = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+#  
+#  if s_fallback.returncode == 0:
+#    try:
+#      LoadDSSP(temp_dssp_path, ent, extract_burial_status)
+#      _Cleanup(temp_dssp_path, pdb_path)
+#      return ent
+#    except:
+#      pass # continue with cleanup and raise error
+#      
+#  _Cleanup(temp_dssp_path, pdb_path)
+#  raise RuntimeError('Failed to assign DSSP')
+
 
 def AssignDSSP(ent, pdb_path="", extract_burial_status=False, tmp_dir=None, 
                dssp_bin=None):
   """
   Assign secondary structure states to peptide residues in the structure. This
-  function uses the DSSP command line program.
+  function replaces the "old" AssignDSSP which relies on the DSSP command line
+  program and uses OpenStructure internal functionality only. The sole purpose
+  is to retain the "old" interface and you're adviced to directly use
+  :func:`ost.mol.alg.AssignSecStruct` and :func:`ost.mol.alg.Accessibility`.  
 
   If you already have a DSSP output file and would like to assign the secondary 
   structure states to an entity, use :func:`LoadDSSP`.
@@ -76,11 +158,8 @@ def AssignDSSP(ent, pdb_path="", extract_burial_status=False, tmp_dir=None,
                                 ``relative_solvent_accessibility`` at residue
                                 level
   :param tmp_dir: If set, overrides the default tmp directory of the
-                  operating system
-  :param dssp_bin: The path to the DSSP executable
-  :raises: :class:`~ost.settings.FileNotFound` if the dssp executable is not 
-      in the path.
-  :raises: :class:`RuntimeError` when dssp is executed with errors
+                  operating system - deprecated, has no effect
+  :param dssp_bin: The path to the DSSP executable - deprecated, has no effect
   """
 
   if not ent.IsValid():
@@ -88,52 +167,24 @@ def AssignDSSP(ent, pdb_path="", extract_burial_status=False, tmp_dir=None,
   if ent.atom_count==0:
     raise ValueError('model entity does not contain any atoms')
 
-  dssp_abs_path=settings.Locate(['dsspcmbi','dssp','mkdssp'], env_name='DSSP_EXECUTABLE', 
-                                explicit_file_name=dssp_bin)
-  if os.path.isdir(dssp_abs_path):
-    raise RuntimeError('"%s" is a directory. Specify path to DSSP binary' % dssp_abs_path)
-  if not os.access(dssp_abs_path, os.X_OK):
-    raise RuntimeError('"%s" is not executable' % dssp_abs_path)
-    
-  pdb_path=tempfile.mktemp(suffix=".pdb",prefix="temp_entity", dir=tmp_dir)
-  io.SavePDB(ent, pdb_path)
-  temp_dssp_path=tempfile.mktemp(suffix=".out",prefix="dssp", dir=tmp_dir)  
-
-  cmd = [dssp_abs_path, pdb_path, temp_dssp_path]
-  s = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
-  if s.returncode == 0:
-    try:
-      LoadDSSP(temp_dssp_path, ent, extract_burial_status)
-      _Cleanup(temp_dssp_path, pdb_path)
-      return ent
-    except:
-      pass # continue with dssp 4 fallback
-  
-  # either dssp execution failed or output file cannot be loaded
-  # both can be the result of using dssp 4 (https://github.com/PDB-REDO/dssp) 
-  # => try fallback
-
-  # dssp 4 desperately wants a CRYST1 record (REMOVE IF NOT NEEDED ANYMORE!)
-  with open(pdb_path, 'r') as fh:
-    file_content = fh.read()
-  with open(pdb_path, 'w') as fh:
-    fh.write('CRYST1    1.000    1.000    1.000  90.00  90.00  90.00 P 1           1          ' +
-             os.linesep + file_content)
-
-  # explicitely request dssp output format
-  cmd = [dssp_abs_path, '--output-format', 'dssp', pdb_path, temp_dssp_path]
-  s_fallback = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
-  
-  if s_fallback.returncode == 0:
-    try:
-      LoadDSSP(temp_dssp_path, ent, extract_burial_status)
-      _Cleanup(temp_dssp_path, pdb_path)
-      return ent
-    except:
-      pass # continue with cleanup and raise error
-      
-  _Cleanup(temp_dssp_path, pdb_path)
-  raise RuntimeError('Failed to assign DSSP')
+  mol.alg.AssignSecStruct(ent)
+  if extract_burial_status:
+    mol.alg.Accessibility(ent, algorithm=mol.alg.DSSP)
+    # map float properties from Accessibility algorithm to the original
+    # properties that have been set in the binding
+    for r in ent.residues:
+      if r.HasProp("asaAbs"):
+        asa = round(r.GetFloatProp("asaAbs")) # original DSSP output is rounded
+        asa_rel = _CalcRelativeSA(r.one_letter_code, asa) # there would be the
+                                                          # asaRel property but
+                                                          # it relates to the
+                                                          # non-rounded asa
+        r.SetFloatProp("solvent_accessibility", asa)
+        r.SetFloatProp("relative_solvent_accessibility", asa_rel)
+        if asa_rel < 0.25:
+          r.SetStringProp("burial_status", 'b')
+        else:
+          r.SetStringProp("burial_status", 'e')
 
 
 def LoadDSSP(file_name, model, extract_burial_status=False,
diff --git a/modules/io/src/mol/omf.cc b/modules/io/src/mol/omf.cc
index 5d62e7fd1abd2e90da441f8baa54c2aaa0846e1b..3d3c144deb6d9a962c5178a1779c804fa2cb9705 100644
--- a/modules/io/src/mol/omf.cc
+++ b/modules/io/src/mol/omf.cc
@@ -1118,6 +1118,13 @@ ost::mol::EntityHandle OMF::GetBU(int bu_idx) const{
 }
 
 void OMF::ToStream(std::ostream& stream) const {
+
+  uint32_t magic_number = 42;
+  stream.write(reinterpret_cast<char*>(&magic_number), sizeof(uint32_t));
+
+  uint32_t version = 1;
+  stream.write(reinterpret_cast<char*>(&version), sizeof(uint32_t));
+
   Dump(stream, residue_definitions_);
   Dump(stream, biounit_definitions_);
   Dump(stream, chain_data_);
@@ -1127,12 +1134,31 @@ void OMF::ToStream(std::ostream& stream) const {
 }
 
 void OMF::FromStream(std::istream& stream) {
+
+  uint32_t magic_number;
+  stream.read(reinterpret_cast<char*>(&magic_number), sizeof(uint32_t));
+  if(magic_number != 42) {
+    throw ost::Error("Cannot read corrupted OMF stream");
+  }
+
+  uint32_t version;
+  stream.read(reinterpret_cast<char*>(&version), sizeof(uint32_t));
+  if(version != 1) {
+    std::stringstream ss;
+    ss << "OST version only supports OMF version 1. Got "<<version;
+    throw ost::Error(ss.str());
+  }
+
   Load(stream, residue_definitions_);
   Load(stream, biounit_definitions_);
   Load(stream, chain_data_);
   Load(stream, bond_chain_names_);
   Load(stream, bond_atoms_);
   Load(stream, bond_orders_);
+
+  if(!stream.good()) {
+    throw ost::Error("Cannot read corrupted OMF stream");
+  }
 }
 
 void OMF::FillChain(ost::mol::ChainHandle& chain, ost::mol::XCSEditor& ed,
diff --git a/singularity/Singularity b/singularity/Singularity
index c36ec32d9f843842586c5415375a47a6d4118a21..dd26e2a39ff55ef5417c5f215ffc0bdcc5775a69 100644
--- a/singularity/Singularity
+++ b/singularity/Singularity
@@ -1,5 +1,5 @@
 BootStrap: docker
-From: registry.scicore.unibas.ch/schwede/openstructure:2.2.0-focal
+From: registry.scicore.unibas.ch/schwede/openstructure:2.3.0-focal
 %post
 ##############################################################################
 # POST