diff --git a/dbrepo-analyse-service/Pipfile.lock b/dbrepo-analyse-service/Pipfile.lock
index ae160f77733183750c665ce9c9053d76b34a18e0..c668b400e4fa26855881c6b78400b2c66becbc0f 100644
--- a/dbrepo-analyse-service/Pipfile.lock
+++ b/dbrepo-analyse-service/Pipfile.lock
@@ -175,20 +175,20 @@
         },
         "boto3": {
             "hashes": [
-                "sha256:159898f51c2997a12541c0e02d6e5a8fe2993ddb307b9478fd9a339f98b57e00",
-                "sha256:d0ca7a58ce25701a52232cc8df9d87854824f1f2964b929305722ebc7959d5a9"
+                "sha256:258ab77225a81d3cf3029c9afe9920cd9dec317689dfadec6f6f0a23130bb60a",
+                "sha256:eb21380d73fec6645439c0d802210f72a0cdb3295b02953f246ff53f512faa8f"
             ],
             "index": "pypi",
             "markers": "python_version >= '3.8'",
-            "version": "==1.36.0"
+            "version": "==1.36.1"
         },
         "botocore": {
             "hashes": [
-                "sha256:0232029ff9ae3f5b50cdb25cbd257c16f87402b6d31a05bd6483638ee6434c4b",
-                "sha256:b54b11f0cfc47fc1243ada0f7f461266c279968487616720fa8ebb02183917d7"
+                "sha256:dec513b4eb8a847d79bbefdcdd07040ed9d44c20b0001136f0890a03d595705a",
+                "sha256:f789a6f272b5b3d8f8756495019785e33868e5e00dd9662a3ee7959ac939bb12"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==1.36.0"
+            "version": "==1.36.1"
         },
         "certifi": {
             "hashes": [
@@ -268,7 +268,7 @@
                 "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87",
                 "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"
             ],
-            "markers": "platform_python_implementation != 'PyPy'",
+            "markers": "python_version >= '3.8'",
             "version": "==1.17.1"
         },
         "charset-normalizer": {
@@ -412,7 +412,7 @@
         },
         "dbrepo": {
             "hashes": [
-                "sha256:0d11a0e0ec942d5b0ddfadd9e9007ce6dab9c5b9cc433e0f53b4fafcfc597bef"
+                "sha256:251f3c2088bbd289cee86d5394b1e62e29aa081f994dd0845d895e3330f6a106"
             ],
             "path": "./lib/dbrepo-1.6.1.tar.gz"
         },
@@ -1427,11 +1427,11 @@
         },
         "referencing": {
             "hashes": [
-                "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c",
-                "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"
+                "sha256:363d9c65f080d0d70bc41c721dce3c7f3e77fc09f269cd5c8813da18069a6794",
+                "sha256:ca2e6492769e3602957e9b831b94211599d2aade9477f5d44110d2530cf9aade"
             ],
-            "markers": "python_version >= '3.8'",
-            "version": "==0.35.1"
+            "markers": "python_version >= '3.9'",
+            "version": "==0.36.1"
         },
         "requests": {
             "hashes": [
@@ -1553,11 +1553,11 @@
         },
         "s3transfer": {
             "hashes": [
-                "sha256:6563eda054c33bdebef7cbf309488634651c47270d828e594d151cd289fb7cf7",
-                "sha256:f43b03931c198743569bbfb6a328a53f4b2b4ec723cd7c01fab68e3119db3f8b"
+                "sha256:3f25c900a367c8b7f7d8f9c34edc87e300bde424f779dc9f0a8ae4f9df9264f6",
+                "sha256:8fa0aa48177be1f3425176dfe1ab85dcd3d962df603c3dbfc585e6bf857ef0ff"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==0.11.0"
+            "version": "==0.11.1"
         },
         "setuptools": {
             "hashes": [
@@ -1877,7 +1877,7 @@
                 "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87",
                 "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"
             ],
-            "markers": "platform_python_implementation != 'PyPy'",
+            "markers": "python_version >= '3.8'",
             "version": "==1.17.1"
         },
         "charset-normalizer": {
diff --git a/dbrepo-analyse-service/lib/dbrepo-1.6.1.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.6.1.tar.gz
index 5ce8fdab038ca28aa52e5c8544ce3bcfee7ca3fa..7914db1bb84dddf85611cda3b766c0c0cdc094c7 100644
Binary files a/dbrepo-analyse-service/lib/dbrepo-1.6.1.tar.gz and b/dbrepo-analyse-service/lib/dbrepo-1.6.1.tar.gz differ
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java
index 22099eafae1353d6061fc881812078ff212382f4..8a08f8231fe0de7babf1db995dc60786407425f8 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java
@@ -91,21 +91,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
         });
     }
 
-    @Test
-    @WithMockUser(username = USER_4_USERNAME)
-    public void list_publicDataPrivateSchemaNoRole_fails() throws QueryNotFoundException, DatabaseNotFoundException,
-            RemoteUnavailableException, SQLException, MetadataServiceException {
-
-        /* mock */
-        when(subsetService.findAll(DATABASE_3_PRIVILEGED_DTO, null))
-                .thenReturn(List.of(QUERY_1_DTO, QUERY_2_DTO, QUERY_3_DTO, QUERY_4_DTO, QUERY_5_DTO, QUERY_6_DTO));
-
-        /* test */
-        assertThrows(NotAllowedException.class, () -> {
-            generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, USER_4_PRINCIPAL);
-        });
-    }
-
     @Test
     @WithMockUser(username = USER_3_USERNAME)
     public void list_publicDataPrivateSchema_succeeds() throws DatabaseUnavailableException, NotAllowedException,
@@ -131,21 +116,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
         });
     }
 
-    @Test
-    @WithMockUser(username = USER_4_USERNAME)
-    public void list_publicDataAndPrivateSchemaNoRole_fails() throws DatabaseNotFoundException,
-            RemoteUnavailableException, MetadataServiceException {
-
-        /* mock */
-        when(credentialService.getDatabase(DATABASE_3_ID))
-                .thenReturn(DATABASE_3_PRIVILEGED_DTO);
-
-        /* test */
-        assertThrows(NotAllowedException.class, () -> {
-            generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, USER_4_PRINCIPAL);
-        });
-    }
-
     @Test
     @WithMockUser(username = USER_3_USERNAME)
     public void list_publicDataAndPrivateSchemaUnavailable_fails() throws SQLException, QueryNotFoundException,
@@ -316,21 +286,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
         });
     }
 
-    @Test
-    @WithMockUser(username = USER_4_USERNAME)
-    public void findById_publicDataAndPrivateSchemaNoRole_fails() throws DatabaseNotFoundException,
-            RemoteUnavailableException, MetadataServiceException {
-
-        /* mock */
-        when(credentialService.getDatabase(DATABASE_3_ID))
-                .thenReturn(DATABASE_3_PRIVILEGED_DTO);
-
-        /* test */
-        assertThrows(NotAllowedException.class, () -> {
-            generic_findById(DATABASE_3_ID, QUERY_5_ID, "application/json", null, USER_4_PRINCIPAL);
-        });
-    }
-
     @Test
     @WithMockUser(username = USER_3_USERNAME)
     public void findById_publicDataAndPrivateSchemaUnavailable_fails() throws DatabaseNotFoundException, RemoteUnavailableException,
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java
index 707df1600c8252b80c4e091e8dfb945e0975c1d4..46072e83dc14af22d09923d3e9462a53506aa4f8 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java
@@ -10,6 +10,7 @@ import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.List;
+import java.util.UUID;
 
 @Getter
 @Setter
@@ -53,6 +54,6 @@ public class DatabaseBriefDto {
 
     @NotNull
     @JsonProperty("owner_id")
-    private UserBriefDto ownerId;
+    private UUID ownerId;
 
 }
diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
index fe8e4385e2857be757ebc72904ef76c988b8fa57..c5482f70411433f5df08bd0281a60c75d132bf26 100644
--- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
+++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
@@ -907,6 +907,9 @@ public interface MetadataMapper {
         return database;
     }
 
+    @Mappings({
+            @Mapping(target = "ownerId", source = "owner.id")
+    })
     DatabaseBriefDto databaseToDatabaseBriefDto(Database data);
 
     AccessType accessTypeDtoToAccessType(AccessTypeDto data);
diff --git a/dbrepo-search-service/Pipfile.lock b/dbrepo-search-service/Pipfile.lock
index e700161ce55394f4a9edf485b4f10c7c00c49572..c0508dd3daf66ff03c848411ae47f1698da81014 100644
--- a/dbrepo-search-service/Pipfile.lock
+++ b/dbrepo-search-service/Pipfile.lock
@@ -360,7 +360,7 @@
         },
         "dbrepo": {
             "hashes": [
-                "sha256:0d11a0e0ec942d5b0ddfadd9e9007ce6dab9c5b9cc433e0f53b4fafcfc597bef"
+                "sha256:a08b6eb49c108466b231c1b2cae5be501043fe4208a782899ce103105e22e3c6"
             ],
             "path": "./lib/dbrepo-1.6.1.tar.gz"
         },
@@ -1330,11 +1330,11 @@
         },
         "referencing": {
             "hashes": [
-                "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c",
-                "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"
+                "sha256:363d9c65f080d0d70bc41c721dce3c7f3e77fc09f269cd5c8813da18069a6794",
+                "sha256:ca2e6492769e3602957e9b831b94211599d2aade9477f5d44110d2530cf9aade"
             ],
-            "markers": "python_version >= '3.8'",
-            "version": "==0.35.1"
+            "markers": "python_version >= '3.9'",
+            "version": "==0.36.1"
         },
         "requests": {
             "hashes": [
diff --git a/dbrepo-search-service/app.py b/dbrepo-search-service/app.py
index e4e8581a9ee484c198f5a00a76a58b5296dd4188..f9e2dbcc77ac11a6f7c8175e2b0d868f848c98af 100644
--- a/dbrepo-search-service/app.py
+++ b/dbrepo-search-service/app.py
@@ -5,17 +5,19 @@ from json import dumps
 from typing import List, Any
 
 import requests
-from clients.keycloak_client import User, KeycloakClient
-from clients.opensearch_client import OpenSearchClient
 from dbrepo.api.dto import Database, ApiError
 from flasgger import LazyJSONEncoder, Swagger, swag_from
-from flask import Flask, request
+from flask import Flask, request, Response
 from flask_cors import CORS
 from flask_httpauth import HTTPTokenAuth, HTTPBasicAuth, MultiAuth
 from jwt.exceptions import JWTDecodeError
 from opensearchpy import NotFoundError
 from prometheus_flask_exporter import PrometheusMetrics
 from pydantic import ValidationError
+from pydantic.deprecated.json import pydantic_encoder
+
+from clients.keycloak_client import User, KeycloakClient
+from clients.opensearch_client import OpenSearchClient, flatten
 
 logging.addLevelName(level=logging.NOTSET, levelName='TRACE')
 logging.basicConfig(level=logging.DEBUG)
@@ -123,18 +125,6 @@ template = {
                     }
                 }
             },
-            "SearchResultDto": {
-                "required": ["results"],
-                "type": "object",
-                "properties": {
-                    "results": {
-                        "type": "array",
-                        "items": {
-                            "type": "object"
-                        }
-                    }
-                }
-            },
             "SearchRequestDto": {
                 "required": ["search_term", "field_value_pairs"],
                 "type": "object",
@@ -208,13 +198,13 @@ app.json_encoder = LazyJSONEncoder
 
 
 @token_auth.verify_token
-def verify_token(token: str):
+def verify_token(token: str) -> bool | User:
     if token is None or token == "":
         return False
     try:
         client = KeycloakClient()
         return client.verify_jwt(access_token=token)
-    except JWTDecodeError as error:
+    except JWTDecodeError:
         return False
 
 
@@ -259,10 +249,10 @@ def general_filter(index, results):
         "table": ["id", "name", "description"],
         "identifier": ["id", "type", "creator"],
         "user": ["id", "username"],
-        "database": ["id", "name", "is_public", "details"],
+        "database": ["id", "name", "is_public", "is_schema_public", "details"],
         "concept": ["uri", "name"],
         "unit": [],
-        "view": ["id", "name", "creator", " created"],
+        "view": ["id", "name", "creator"],
     }
     if index not in important_keys.keys():
         raise KeyError(f"Failed to find index {index} in: {important_keys.keys()}")
@@ -289,7 +279,7 @@ def get_index(index: str):
     :param index: desired index
     :return: list of the results
     """
-    logging.info(f'Searching for index: {index}')
+    logging.debug(f'endpoint get search type: {index}')
     results = OpenSearchClient().query_index_by_term_opensearch("*", "contains")
     try:
         results = general_filter(index, results)
@@ -298,7 +288,7 @@ def get_index(index: str):
         max_pages = math.ceil(len(results) / results_per_page)
         page = min(request.args.get("page", 1, type=int), max_pages)
         results = results[(results_per_page * (page - 1)): (results_per_page * page)]
-        return dict({"results": results}), 200
+        return Response(dumps(results, default=pydantic_encoder)), 200, {'Content-Type': 'application/json'}
     except KeyError:
         return ApiError(status='NOT_FOUND', message=f'Failed to find get index: {index}',
                         code='search.index.missing').model_dump(), 404
@@ -313,11 +303,11 @@ def get_fields(field_type: str):
     :param field_type: The search type
     :return:
     """
-    logging.info(f'Searching in index database for type: {field_type}')
+    logging.debug(f'endpoint get search type fields: {field_type}')
     try:
         fields = OpenSearchClient().get_fields_for_index(field_type)
         logging.debug(f'get fields for field_type {field_type} resulted in {len(fields)} field(s)')
-        return fields, 200
+        return Response(dumps(fields, default=pydantic_encoder)), 200, {'Content-Type': 'application/json'}
     except NotFoundError:
         return ApiError(status='NOT_FOUND', message=f'Failed to find fields for search type {field_type}',
                         code='search.type.missing').model_dump(), 404
@@ -331,15 +321,19 @@ def get_fuzzy_search():
     Main endpoint for fuzzy searching.
     :return:
     """
-    search_term: str = request.args.get('q')
+    search_term: str | None = request.args.get('q')
+    logging.debug(f'endpoint get fuzzy search, q={search_term}')
     if search_term is None or len(search_term) == 0:
         return ApiError(status='BAD_REQUEST', message='Provide a search term with ?q=term',
                         code='search.fuzzy.invalid').model_dump(), 400
     logging.debug(f"search request query: {search_term}")
-    results = OpenSearchClient().fuzzy_search(search_term)
-    if "hits" in results and "hits" in results["hits"]:
-        results = [hit["_source"] for hit in results["hits"]["hits"]]
-    return dict({"results": results}), 200
+    user_id, error, status = KeycloakClient().userId(request.headers.get('Authorization'))
+    if error is not None and status is not None:
+        return error, status
+    results: [Database] = OpenSearchClient().fuzzy_search(search_term=search_term,
+                                                          user_id=user_id,
+                                                          user_token=request.headers.get('Authorization'))
+    return Response(dumps(results, default=pydantic_encoder)), 200, {'Content-Type': 'application/json'}
 
 
 @app.route("/api/search/<string:field_type>", methods=["POST"], endpoint="search_post_general_search")
@@ -353,27 +347,32 @@ def post_general_search(field_type):
     if request.content_type != "application/json":
         return ApiError(status='UNSUPPORTED_MEDIA_TYPE', message='Content type needs to be application/json',
                         code='search.general.media').model_dump(), 415
-    req_body = request.json
-    logging.info(f'Searching in index database for type: {field_type}')
+    value_pairs = request.json
+    logging.debug(f'endpoint get general search, field_type={field_type}, value_pairs={value_pairs}')
     t1 = request.args.get("t1")
     if not str(t1).isdigit():
         t1 = None
     t2 = request.args.get("t2")
     if not str(t2).isdigit():
         t2 = None
-    if t1 is not None and t2 is not None and "unit.uri" in req_body and "concept.uri" in req_body:
-        response = OpenSearchClient().unit_independent_search(t1, t2, req_body)
+    user_id, error, status = KeycloakClient().userId(request.headers.get('Authorization'))
+    if error is not None and status is not None:
+        return error, status
+    if t1 is not None and t2 is not None and "unit.uri" in value_pairs and "concept.uri" in value_pairs:
+        response: [Database] = OpenSearchClient().unit_independent_search(t1, t2, value_pairs, user_id)
     else:
-        response = OpenSearchClient().general_search(field_type, req_body)
+        response: [Database] = OpenSearchClient().general_search(field_type=field_type,
+                                                                 field_value_pairs=value_pairs,
+                                                                 user_id=user_id,
+                                                                 user_token=request.headers.get('Authorization'))
     # filter by type
+    tables = [table for table in flatten([database.tables for database in response]) if
+              table.is_public or table.is_schema_public or (user_id is not None and table.owner.id == user_id)]
+    views = [view for view in flatten([database.views for database in response]) if
+             view.is_public or view.is_schema_public or (user_id is not None and view.owner.id == user_id)]
     if field_type == 'table':
-        tmp = []
-        for database in response:
-            if database["tables"] is not None:
-                for table in database["tables"]:
-                    table["is_public"] = database["is_public"]
-                    tmp.append(table)
-        response = tmp
+        logging.debug(f'filtered to {len(tables)} tables')
+        response = tables
     if field_type == 'identifier':
         tmp = []
         for database in response:
@@ -393,12 +392,7 @@ def post_general_search(field_type):
                 tmp.append(view['identifier'])
         response = tmp
     elif field_type == 'column':
-        response = [x for xs in response for x in xs["tables"]]
-        for table in response:
-            for column in table["columns"]:
-                column["table_id"] = table["id"]
-                column["database_id"] = table["database_id"]
-        response = [x for xs in response for x in xs["columns"]]
+        response = flatten([table.columns for table in tables])
     elif field_type == 'concept':
         tmp = []
         tables = [x for xs in response for x in xs["tables"]]
@@ -414,15 +408,15 @@ def post_general_search(field_type):
                 tmp.append(column["unit"])
         response = tmp
     elif field_type == 'view':
-        response = [x for xs in response for x in xs["views"]]
-    return dict({'results': response, 'type': field_type}), 200
+        response = views
+    return Response(dumps(response, default=pydantic_encoder)), 200, {'Content-Type': 'application/json'}
 
 
 @app.route("/api/search/database/<int:database_id>", methods=["PUT"], endpoint="search_put_database")
 @metrics.gauge(name='dbrepo_search_update_database',
                description='Time needed to update a database in the search database')
 @auth.login_required(role=['update-search-index'])
-def update_database(database_id: int) -> Database | ApiError:
+def update_database(database_id: int):
     logging.debug(f"updating database with id: {database_id}")
     try:
         payload: Database = Database.model_validate(request.json)
@@ -431,7 +425,7 @@ def update_database(database_id: int) -> Database | ApiError:
         return ApiError(status='BAD_REQUEST', message=f'Malformed payload: {e}',
                         code='search.general.missing').model_dump(), 400
     database = OpenSearchClient().update_database(database_id, payload)
-    logging.info(f"Updated database with id : {database_id}")
+    logging.info(f"Updated database with id: {database_id}")
     return database.model_dump(), 202
 
 
@@ -442,7 +436,7 @@ def update_database(database_id: int) -> Database | ApiError:
 def delete_database(database_id: int):
     try:
         OpenSearchClient().delete_database(database_id)
-        return dumps({}), 202
+        return Response(dumps({})), 202
     except NotFoundError:
         return ApiError(status='NOT_FOUND', message='Failed to find database',
                         code='search.database.missing').model_dump(), 404
diff --git a/dbrepo-search-service/init/Pipfile.lock b/dbrepo-search-service/init/Pipfile.lock
index a8257b53df3cc300043d7aeb6412481708b9b1ff..bf53ace7e7551b8e0961373d6ab23e49ff53f300 100644
--- a/dbrepo-search-service/init/Pipfile.lock
+++ b/dbrepo-search-service/init/Pipfile.lock
@@ -254,9 +254,10 @@
         },
         "dbrepo": {
             "hashes": [
-                "sha256:0d11a0e0ec942d5b0ddfadd9e9007ce6dab9c5b9cc433e0f53b4fafcfc597bef"
+                "sha256:251f3c2088bbd289cee86d5394b1e62e29aa081f994dd0845d895e3330f6a106"
             ],
-            "path": "./lib/dbrepo-1.6.1.tar.gz"
+            "path": "./lib/dbrepo-1.6.1.tar.gz",
+            "version": "==1.6.1"
         },
         "docker": {
             "hashes": [
@@ -278,7 +279,6 @@
                 "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.8'",
             "version": "==2.3.3"
         },
         "frozenlist": {
@@ -643,7 +643,6 @@
                 "sha256:6598df0bc7a003294edd0ba88a331e0793acbb8c910c43edf398791e3b2eccda"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.8' and python_version < '4'",
             "version": "==2.8.0"
         },
         "packaging": {
@@ -934,7 +933,6 @@
                 "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.8'",
             "version": "==8.3.4"
         },
         "python-dateutil": {
@@ -951,7 +949,6 @@
                 "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.8'",
             "version": "==1.0.1"
         },
         "pytz": {
@@ -967,7 +964,6 @@
                 "sha256:5a694a64f48a751079999c37dccf91a6210077d845d09adf7c3ce23a876265a7"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.9' and python_version < '4'",
             "version": "==7.1.2"
         },
         "requests": {
@@ -998,7 +994,6 @@
                 "sha256:0bdf270b5b7f53915832f7c31dd2bd3ffdc20b534ea6b32231cc7003049bd0e1"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.7'",
             "version": "==0.0.1rc1"
         },
         "tinydb": {
@@ -1290,7 +1285,6 @@
                 "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.9'",
             "version": "==7.6.10"
         },
         "iniconfig": {
@@ -1323,7 +1317,6 @@
                 "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.8'",
             "version": "==8.3.4"
         }
     }
diff --git a/dbrepo-search-service/init/app.py b/dbrepo-search-service/init/app.py
index 9fe915f92c50d2b712058783d4eecf1b087cc8f7..f8f671bade77541508aec72cd19066157cd162f3 100644
--- a/dbrepo-search-service/init/app.py
+++ b/dbrepo-search-service/init/app.py
@@ -1,12 +1,11 @@
 import json
-import os
 import logging
+import os
+from logging.config import dictConfig
 from typing import List
 
 import opensearchpy.exceptions
 from dbrepo.RestClient import RestClient
-from logging.config import dictConfig
-
 from dbrepo.api.dto import Database
 from opensearchpy import OpenSearch
 
@@ -46,6 +45,8 @@ class App:
     search_username: str = None
     search_password: str = None
     search_instance: OpenSearch = None
+    system_username: str = None
+    system_password: str = None
 
     def __init__(self):
         self.metadata_service_endpoint = os.getenv("METADATA_SERVICE_ENDPOINT", "http://metadata-service:8080")
@@ -53,6 +54,8 @@ class App:
         self.search_port = int(os.getenv("OPENSEARCH_PORT", "9200"))
         self.search_username = os.getenv("OPENSEARCH_USERNAME", "admin")
         self.search_password = os.getenv("OPENSEARCH_PASSWORD", "admin")
+        self.system_username = os.getenv("SYSTEM_USERNAME", "admin")
+        self.system_password = os.getenv("SYSTEM_PASSWORD", "admin")
 
     def _instance(self) -> OpenSearch:
         """
@@ -84,7 +87,8 @@ class App:
 
     def fetch_databases(self) -> List[Database]:
         logging.debug(f"fetching database from endpoint: {self.metadata_service_endpoint}")
-        client = RestClient(endpoint=self.metadata_service_endpoint)
+        client = RestClient(endpoint=self.metadata_service_endpoint, username=self.system_username,
+                            password=self.system_password)
         databases = []
         for index, database in enumerate(client.get_databases()):
             logging.debug(f"fetching database {index}/{len(databases)} details for database id: {database.id}")
@@ -93,16 +97,17 @@ class App:
         return databases
 
     def save_databases(self, databases: List[Database]):
-        logging.debug(f"save {len(databases)} database(s)")
+        index = f'database'
+        logging.debug(f"save {len(databases)} database(s) in index: {index}")
         for doc in databases:
             doc: Database = doc
             try:
-                self._instance().delete(index="database", id=doc.id)
-                logging.debug(f"deleted database with id {doc.id}")
+                self._instance().delete(index=index, id=doc.id)
+                logging.debug(f"truncated database with id {doc.id} in index: {index}")
             except opensearchpy.NotFoundError:
-                logging.warning(f"Database with id {doc.id} does not exist, skip.")
-            self._instance().create(index="database", id=doc.id, body=doc.model_dump())
-            logging.debug(f"created database with id {doc.id}")
+                pass
+            self._instance().create(index=index, id=doc.id, body=doc.model_dump())
+            logging.info(f"Saved database with id {doc.id} in index: {index}")
 
 
 if __name__ == "__main__":
diff --git a/dbrepo-search-service/init/clients/keycloak_client.py b/dbrepo-search-service/init/clients/keycloak_client.py
index afa36a1112ce41b5686641f5691df3f44075cf2f..2e15d00a9b272233feb7bab4cf6166c63b151e04 100644
--- a/dbrepo-search-service/init/clients/keycloak_client.py
+++ b/dbrepo-search-service/init/clients/keycloak_client.py
@@ -1,14 +1,17 @@
 import logging
 from dataclasses import dataclass
-import requests
-from flask import current_app
 from typing import List
 
+import requests
+from dbrepo.api.dto import ApiError
+from flask import current_app
 from jwt import jwk_from_pem, JWT
+from jwt.exceptions import JWTDecodeError
 
 
 @dataclass(init=True, eq=True)
 class User:
+    id: str
     username: str
     roles: List[str]
 
@@ -30,8 +33,22 @@ class KeycloakClient:
             raise AssertionError("Failed to obtain user token(s)")
         return response.json()["access_token"]
 
-    def verify_jwt(self, access_token: str) -> User:
+    def verify_jwt(self, access_token: str) -> ApiError | User:
         public_key = jwk_from_pem(str(current_app.config["JWT_PUBKEY"]).encode('utf-8'))
         payload = JWT().decode(message=access_token, key=public_key, do_time_check=True)
-        logging.debug(f"JWT token client_id={payload.get('client_id')} and realm_access={payload.get('realm_access')}")
-        return User(username=payload.get('client_id'), roles=payload.get('realm_access')["roles"])
+        return User(id=payload.get('uid'), username=payload.get('client_id'),
+                    roles=payload.get('realm_access')["roles"])
+
+    def userId(self, auth_header: str | None) -> (str | None, ApiError, int):
+        if auth_header is None:
+            return None, None, None
+        try:
+            user = self.verify_jwt(auth_header.split(" ")[1])
+            logging.debug(f'mapped JWT to user.id {user.id}')
+            return user.id, None, None
+        except JWTDecodeError as e:
+            logging.error(f'Failed to decode JWT: {e}')
+            if str(e) == 'JWT Expired':
+                return None, ApiError(status='UNAUTHORIZED', message=f'Token expired',
+                                      code='search.user.unauthorized').model_dump(), 401
+            return None, ApiError(status='FORBIDDEN', message=str(e), code='search.user.forbidden').model_dump(), 403
diff --git a/dbrepo-search-service/init/clients/opensearch_client.py b/dbrepo-search-service/init/clients/opensearch_client.py
index bf782a7a7e8245c5cd57eea5a641b7f14c46df07..35c26f03f5f684adc2652c199db7f99afd6cfc13 100644
--- a/dbrepo-search-service/init/clients/opensearch_client.py
+++ b/dbrepo-search-service/init/clients/opensearch_client.py
@@ -7,7 +7,9 @@ from collections.abc import MutableMapping
 from json import dumps, load
 
 from dbrepo.api.dto import Database
+from dbrepo.api.exceptions import ForbiddenError, NotExistsError
 from opensearchpy import OpenSearch, NotFoundError
+from requests import head
 
 from omlib.constants import OM_IDS
 from omlib.measure import om
@@ -20,16 +22,22 @@ class OpenSearchClient:
     The client to communicate with the OpenSearch database.
     """
     host: str = None
+    instance: OpenSearch = None
+    metadata_endpoint: str = None
+    password: str = None
     port: int = None
+    system_username: str = None
+    system_password: str = None
     username: str = None
-    password: str = None
-    instance: OpenSearch = None
 
     def __init__(self, host: str = None, port: int = None, username: str = None, password: str = None):
         self.host = os.getenv('OPENSEARCH_HOST', host)
+        self.metadata_endpoint = os.getenv('METADATA_SERVICE_ENDPOINT', 'http://metadata-service:8080')
+        self.password = os.getenv('OPENSEARCH_PASSWORD', password)
         self.port = int(os.getenv('OPENSEARCH_PORT', port))
+        self.system_username = os.getenv('SYSTEM_USERNAME', 'admin')
+        self.system_password = os.getenv('SYSTEM_PASSWORD', 'admin')
         self.username = os.getenv('OPENSEARCH_USERNAME', username)
-        self.password = os.getenv('OPENSEARCH_PASSWORD', password)
 
     def _instance(self) -> OpenSearch:
         """
@@ -43,18 +51,6 @@ class OpenSearchClient:
                                        http_auth=(self.username, self.password))
         return self.instance
 
-    def get_database(self, database_id: int) -> Database:
-        """
-        Gets a database by given id.
-
-        @param database_id: The database id.
-
-        @returns: The database, if successful.
-        @throws: opensearchpy.exceptions.NotFoundError If the database was not found in the Search Database.
-        """
-        response: dict = self._instance().get(index="database", id=database_id)
-        return Database.model_validate(response["_source"])
-
     def update_database(self, database_id: int, data: Database) -> Database:
         """
         Updates the database data with given id.
@@ -68,9 +64,7 @@ class OpenSearchClient:
         logging.debug(f"updating database with id: {database_id} in search database")
         self._instance().index(index="database", id=database_id, body=dumps(data.model_dump()))
         response: dict = self._instance().get(index="database", id=database_id)
-        database = Database.model_validate(response["_source"])
-        logging.info(f"Updated database with id {database_id} in index 'database'")
-        return database
+        return Database.model_validate(response["_source"])
 
     def delete_database(self, database_id: int) -> None:
         """
@@ -142,27 +136,50 @@ class OpenSearchClient:
                 fields_list.append(entry)
         return fields_list
 
-    def fuzzy_search(self, search_term=None):
-        logging.info(f"Performing fuzzy search")
-        fuzzy_body = {
-            "query": {
-                "multi_match": {
-                    "query": search_term,
-                    "fuzziness": "AUTO",
-                    "fuzzy_transpositions": True,
-                    "minimum_should_match": 3
-                }
-            }
-        }
-        logging.debug(f'search body: {fuzzy_body}')
+    def fuzzy_search(self, search_term: str, user_id: str | None = None, user_token: str | None = None) -> [Database]:
         response = self._instance().search(
             index="database",
-            body=fuzzy_body
+            body={
+                "query": {
+                    "multi_match": {
+                        "query": search_term,
+                        "fuzziness": "AUTO",
+                        "prefix_length": 2
+                    }
+                }
+            }
         )
-        logging.info(f"Found {len(response['hits']['hits'])} result(s)")
-        return response
-
-    def general_search(self, field_type: str = None, field_value_pairs: dict = None):
+        results: [Database] = []
+        if "hits" in response and "hits" in response["hits"]:
+            results = [Database.model_validate(hit["_source"]) for hit in response["hits"]["hits"]]
+        logging.debug(f'found {len(results)} results')
+        return self.filter_results(results, user_id, user_token)
+
+    def filter_results(self, results: [Database], user_id: str | None = None, user_token: str | None = None) -> [
+        Database]:
+        filtered: [Database] = []
+        for database in results:
+            if database.is_public or database.is_schema_public:
+                logging.debug(f'database with id {database.id} is public or has public schema')
+                filtered.append(database)
+            elif user_id is not None and user_token is not None:
+                try:
+                    url = f'{self.metadata_endpoint}/api/database/{database.id}/access/{user_id}'
+                    logging.debug(f'requesting access from url: {url}')
+                    response = head(url=url, auth=(self.system_username, self.system_password))
+                    if response.status_code == 200:
+                        logging.debug(f'database with id {database.id} is draft and access was found')
+                        filtered.append(database)
+                    else:
+                        logging.warning(
+                            f'database with id {database.id} is not accessible: code {response.status_code}')
+                except (ForbiddenError, NotExistsError) as e:
+                    logging.warning(f'database with id {database.id} is draft but no access was found')
+        logging.debug(f'filtered {len(filtered)} results')
+        return filtered
+
+    def general_search(self, field_type: str = None, field_value_pairs: dict = None, user_id: str | None = None,
+                       user_token: str | None = None) -> [Database]:
         """
         Main method for searching stuff in the opensearch db
 
@@ -203,10 +220,14 @@ class OpenSearchClient:
             index="database",
             body=dumps(body)
         )
-        results = [hit["_source"] for hit in response["hits"]["hits"]]
-        return results
-
-    def unit_independent_search(self, t1: float, t2: float, field_value_pairs):
+        results: [Database] = []
+        if "hits" in response and "hits" in response["hits"]:
+            results = [Database.model_validate(hit["_source"]) for hit in response["hits"]["hits"]]
+        logging.debug(f'found {len(results)} results')
+        return self.filter_results(results, user_id, user_token)
+
+    def unit_independent_search(self, t1: float, t2: float, field_value_pairs: dict, userId: str | None = None) -> [
+        Database]:
         """
         Main method for searching stuff in the opensearch db
 
@@ -287,16 +308,12 @@ class OpenSearchClient:
         body = ''
         for search in searches:
             body += '%s \n' % dumps(search)
-        responses = self._instance().msearch(
+        response = self._instance().msearch(
             body=dumps(body)
         )
-        response = {
-            "hits": {
-                "hits": flatten([hits["hits"]["hits"] for hits in responses["responses"]])
-            },
-            "took": responses["took"]
-        }
-        return response
+        results = flatten([hits["hits"]["hits"] for hits in response["responses"]])
+        return [database for database in results if
+                database.is_public or database.is_schema_public or (userId is not None and database.owner.id == userId)]
 
 
 def key_to_attr_name(key: str) -> str:
diff --git a/dbrepo-search-service/init/database.json b/dbrepo-search-service/init/database.json
index 363624ff059daa02d4edb58f3f6bce1b5b4664dd..59cbd78438a5fff391d1237c70a0384d7b397a83 100644
--- a/dbrepo-search-service/init/database.json
+++ b/dbrepo-search-service/init/database.json
@@ -572,37 +572,6 @@
           }
         }
       },
-      "owner": {
-        "properties": {
-          "id": {
-            "type": "text",
-            "fields": {
-              "keyword": {
-                "type": "keyword",
-                "ignore_above": 256
-              }
-            }
-          },
-          "qualified_name": {
-            "type": "text",
-            "fields": {
-              "keyword": {
-                "type": "keyword",
-                "ignore_above": 256
-              }
-            }
-          },
-          "username": {
-            "type": "text",
-            "fields": {
-              "keyword": {
-                "type": "keyword",
-                "ignore_above": 256
-              }
-            }
-          }
-        }
-      },
       "tables": {
         "properties": {
           "columns": {
@@ -640,6 +609,12 @@
               "is_null_allowed": {
                 "type": "boolean"
               },
+              "is_public": {
+                "type": "boolean"
+              },
+              "is_schema_public": {
+                "type": "boolean"
+              },
               "mean": {
                 "type": "float"
               },
@@ -827,55 +802,6 @@
           "num_rows": {
             "type": "long"
           },
-          "owner": {
-            "properties": {
-              "id": {
-                "type": "text",
-                "fields": {
-                  "keyword": {
-                    "type": "keyword",
-                    "ignore_above": 256
-                  }
-                }
-              },
-              "qualified_name": {
-                "type": "text",
-                "fields": {
-                  "keyword": {
-                    "type": "keyword",
-                    "ignore_above": 256
-                  }
-                }
-              },
-              "name": {
-                "type": "text",
-                "fields": {
-                  "keyword": {
-                    "type": "keyword",
-                    "ignore_above": 256
-                  }
-                }
-              },
-              "orcid": {
-                "type": "text",
-                "fields": {
-                  "keyword": {
-                    "type": "keyword",
-                    "ignore_above": 256
-                  }
-                }
-              },
-              "username": {
-                "type": "text",
-                "fields": {
-                  "keyword": {
-                    "type": "keyword",
-                    "ignore_above": 256
-                  }
-                }
-              }
-            }
-          },
           "queue_name": {
             "type": "text",
             "fields": {
@@ -933,6 +859,9 @@
               "is_public": {
                 "type": "boolean"
               },
+              "is_schema_public": {
+                "type": "boolean"
+              },
               "name": {
                 "type": "text",
                 "fields": {
diff --git a/dbrepo-search-service/init/lib/dbrepo-1.6.1.tar.gz b/dbrepo-search-service/init/lib/dbrepo-1.6.1.tar.gz
index 5ce8fdab038ca28aa52e5c8544ce3bcfee7ca3fa..7914db1bb84dddf85611cda3b766c0c0cdc094c7 100644
Binary files a/dbrepo-search-service/init/lib/dbrepo-1.6.1.tar.gz and b/dbrepo-search-service/init/lib/dbrepo-1.6.1.tar.gz differ
diff --git a/dbrepo-search-service/lib/dbrepo-1.6.1.tar.gz b/dbrepo-search-service/lib/dbrepo-1.6.1.tar.gz
index 5ce8fdab038ca28aa52e5c8544ce3bcfee7ca3fa..7914db1bb84dddf85611cda3b766c0c0cdc094c7 100644
Binary files a/dbrepo-search-service/lib/dbrepo-1.6.1.tar.gz and b/dbrepo-search-service/lib/dbrepo-1.6.1.tar.gz differ
diff --git a/dbrepo-search-service/os-yml/get_fuzzy_search.yml b/dbrepo-search-service/os-yml/get_fuzzy_search.yml
index bc54419eb9735fe731fb12a5e911070ebc29f80e..db2ef87b3268f10329ba11117ee323208d940af5 100644
--- a/dbrepo-search-service/os-yml/get_fuzzy_search.yml
+++ b/dbrepo-search-service/os-yml/get_fuzzy_search.yml
@@ -19,6 +19,9 @@ responses:
     content:
       application/json:
         schema:
-          $ref: '#/components/schemas/SearchResultDto'
+          type: array
+          properties:
+            id:
+              type: string
   415:
     description: Wrong accept type
diff --git a/dbrepo-search-service/test/test_jwt.py b/dbrepo-search-service/test/test_jwt.py
index 59cd4ee1168117d0aeb6bf3549fe5088edc379b9..96ce8410da4be0598fd6d635e7decc9950bd42e8 100644
--- a/dbrepo-search-service/test/test_jwt.py
+++ b/dbrepo-search-service/test/test_jwt.py
@@ -12,9 +12,9 @@ class JwtTest(unittest.TestCase):
 
     def response(self, roles: [str]) -> dict:
         return dict({
-            "client_id": "username",
-            "realm_access": {
-                "roles": roles
+            'client_id': 'username',
+            'realm_access': {
+                'roles': roles
             }
         })
 
@@ -37,13 +37,13 @@ class JwtTest(unittest.TestCase):
     def test_verify_token_empty_token_fails(self):
         with app.app_context():
             # test
-            user = verify_token("")
+            user = verify_token('')
             self.assertFalse(user)
 
     def test_verify_token_malformed_token_fails(self):
         with app.app_context():
             # test
-            user = verify_token("eyEYEY12345")
+            user = verify_token('eyEYEY12345')
             self.assertFalse(user)
 
     def test_verify_token_succeeds(self):
@@ -59,25 +59,25 @@ class JwtTest(unittest.TestCase):
     def test_verify_password_no_username_fails(self):
         with app.app_context():
             # test
-            user = verify_password(None, "pass")
+            user = verify_password(None, 'pass')
             self.assertFalse(user)
 
     def test_verify_password_empty_username_fails(self):
         with app.app_context():
             # test
-            user = verify_password("", "pass")
+            user = verify_password('', 'pass')
             self.assertFalse(user)
 
     def test_verify_password_no_password_fails(self):
         with app.app_context():
             # test
-            user = verify_password("username", None)
+            user = verify_password('username', None)
             self.assertFalse(user)
 
     def test_verify_password_empty_password_fails(self):
         with app.app_context():
             # test
-            user = verify_password("username", "")
+            user = verify_password('username', '')
             self.assertFalse(user)
 
     def test_verify_password_succeeds(self):
@@ -87,11 +87,12 @@ class JwtTest(unittest.TestCase):
                 mock.post('http://auth-service:8080/api/auth/realms/dbrepo/protocol/openid-connect/token',
                           json=self.response([]))
                 # test
-                user = verify_password("username", "password")
+                user = verify_password('username', 'password')
                 self.assertIsNotNone(user)
 
     def test_get_user_roles_succeeds(self):
         with app.app_context():
             # test
-            roles: [str] = get_user_roles(User(username="username", roles=[]))
+            roles: [str] = get_user_roles(
+                User(id='b98415d8-28bc-4472-84ff-3d09cc79aff6', username='username', roles=[]))
             self.assertEqual([], roles)
diff --git a/dbrepo-search-service/test/test_opensearch_client.py b/dbrepo-search-service/test/test_opensearch_client.py
index e37a96db10aaf44c9f588907295a2622c1fc0d38..9da77adfde53e155dddc36f364bea9d974964125 100644
--- a/dbrepo-search-service/test/test_opensearch_client.py
+++ b/dbrepo-search-service/test/test_opensearch_client.py
@@ -165,27 +165,6 @@ class OpenSearchClientTest(unittest.TestCase):
             # test
             OpenSearchClient().delete_database(database_id=req.id)
 
-    def test_get_database_succeeds(self):
-        with app.app_context():
-            # mock
-            OpenSearchClient().update_database(database_id=req.id, data=req)
-
-            # test
-            database = OpenSearchClient().get_database(database_id=req.id)
-            self.assertEqual(req.id, database.id)
-
-    def test_get_database_fails(self):
-        with app.app_context():
-
-            # mock
-            OpenSearchClient().update_database(database_id=req.id, data=req)
-
-            # test
-            try:
-                OpenSearchClient().get_database(database_id=req.id)
-            except opensearchpy.exceptions.NotFoundError:
-                pass
-
     def test_get_fields_for_index_database_succeeds(self):
         with app.app_context():
             # mock
@@ -210,8 +189,7 @@ class OpenSearchClientTest(unittest.TestCase):
             OpenSearchClient().update_database(database_id=req.id, data=req)
 
             # test
-            response = OpenSearchClient().fuzzy_search(search_term="test")
-            self.assertTrue(len(response) > 0)
+            OpenSearchClient().fuzzy_search(search_term="test_tuw")
 
     def test_unit_independent_search_fails(self):
         with app.app_context():
diff --git a/dbrepo-ui/components/search/AdvancedSearch.vue b/dbrepo-ui/components/search/AdvancedSearch.vue
index b312b2dc5291812fd98930e8585b50e389c341c5..8197cd3fb8a960f44440cc8238c0a1a42cb7f57e 100644
--- a/dbrepo-ui/components/search/AdvancedSearch.vue
+++ b/dbrepo-ui/components/search/AdvancedSearch.vue
@@ -384,8 +384,8 @@ export default {
       this.loading = true
       const searchService = useSearchService()
       searchService.general_search(this.searchType, this.advancedSearchData)
-        .then(({results, type}) => {
-          this.$emit('search-result', {results, type})
+        .then((results) => {
+          this.$emit('search-result', results)
         })
         .finally(() => {
           this.loading = false
@@ -443,7 +443,7 @@ export default {
         return
       }
       this.resetAdvancedSearchFields()
-      this.$emit('search-result', { results: [], type: this.searchType })
+      this.$emit('search-result', [])
       const searchService = useSearchService()
       this.loadingFields = true
       searchService.fields(this.searchType)
diff --git a/dbrepo-ui/composables/search-service.ts b/dbrepo-ui/composables/search-service.ts
index 62be8b9bc7160f70e33969970fbbba1c500d288f..b61f8358cf74167d1fef5c6f2de54ef017d4c7aa 100644
--- a/dbrepo-ui/composables/search-service.ts
+++ b/dbrepo-ui/composables/search-service.ts
@@ -18,11 +18,11 @@ export const useSearchService = (): any => {
     })
   }
 
-  async function fuzzy_search(term: string): Promise<SearchResultDto> {
+  async function fuzzy_search(term: string): Promise<DatabaseDto[]> {
     const axios = useAxiosInstance()
     console.debug('fuzzy search for term', term)
-    return new Promise<SearchResultDto>((resolve, reject) => {
-      axios.get<SearchResultDto>(`/api/search?q=${term}`)
+    return new Promise<DatabaseDto[]>((resolve, reject) => {
+      axios.get<DatabaseDto[]>(`/api/search?q=${term}`)
         .then((response) => {
           console.info('Searched for term', term)
           resolve(response.data)
diff --git a/dbrepo-ui/nuxt.config.ts b/dbrepo-ui/nuxt.config.ts
index b8e55da8023daf790048ab9bfd5f48a0b82d2ba3..4bce6ec5c5b3dc7f025734d9bf15d8e914123f79 100644
--- a/dbrepo-ui/nuxt.config.ts
+++ b/dbrepo-ui/nuxt.config.ts
@@ -75,8 +75,8 @@ export default defineNuxtConfig({
         }
       },
       api: {
-        client: 'https://s155.datalab.tuwien.ac.at',
-        server: 'https://s155.datalab.tuwien.ac.at',
+        client: 'http://localhost',
+        server: 'http://gateway-service',
       },
       upload: {
         client: 'http://localhost/api/upload/files',
diff --git a/dbrepo-ui/pages/search.vue b/dbrepo-ui/pages/search.vue
index b13a0f0fc2d7985775fe8c3c450c0acb8785bbd0..b23c896448e12845d0d4b3f92ce0284b5886bd55 100644
--- a/dbrepo-ui/pages/search.vue
+++ b/dbrepo-ui/pages/search.vue
@@ -27,7 +27,8 @@
       v-if="isDatabaseSearch"
       :loading="loading"
       :databases="results" />
-    <div>
+    <div
+      v-else>
       <v-card
         v-for="(result, idx) in results"
         :key="idx"
@@ -38,10 +39,13 @@
         <v-divider class="mx-4" />
         <v-card-title
           class="text-primary text-decoration-underline">
-          <a v-if="link(result)" :href="link(result)">
+          <a
+            v-if="link(result)"
+            :href="link(result)">
             {{ title(result) }}
           </a>
-          <span v-else>
+          <span
+            v-else>
             {{ title(result) }}
           </span>
         </v-card-title>
@@ -66,23 +70,15 @@
         </v-card-text>
       </v-card>
     </div>
-    <v-dialog
-      v-model="createDbDialog"
-      persistent
-      max-width="640">
-      <DatabaseCreate @close="closed" />
-    </v-dialog>
   </div>
 </template>
 
 <script>
-import DatabaseCreate from '@/components/database/DatabaseCreate.vue'
 import AdvancedSearch from '@/components/search/AdvancedSearch.vue'
 import { useUserStore } from '@/stores/user'
 
 export default {
   components: {
-    DatabaseCreate,
     AdvancedSearch
   },
   data () {
@@ -90,7 +86,6 @@ export default {
       results: [],
       type: 'database',
       loading: false,
-      createDbDialog: null,
       userStore: useUserStore()
     }
   },
@@ -136,10 +131,13 @@ export default {
       if (!queryKeys || queryKeys.length !== 1 || !queryKeys.includes('q')) {
         return
       }
+      if (!this.q) {
+        return
+      }
       this.loading = true
       const searchService = useSearchService()
       searchService.fuzzy_search(this.q)
-        .then(({results}) => {
+        .then((results) => {
           this.results = results
           this.loading = false
         })
@@ -294,19 +292,8 @@ export default {
       }
       return tags
     },
-    closed (event) {
-      this.dialog = false
-      if (event.success) {
-        this.$router.push(`/database/${event.database_id}/info`)
-      }
-    },
-    onSearchResult ({results, type}) {
+    onSearchResult (results) {
       this.results = results
-      if (!type) {
-        return
-      }
-      console.debug('search for type', type, ':', results)
-      this.type = type
     },
     capitalizeFirstLetter(string) {
       if (!string) {
diff --git a/docker-compose.yml b/docker-compose.yml
index 7159ff58cbb59eec4d114603c71c5ea4c52d51b1..315f6bf1884c1e49d2ac870ab53f50d7a6b8c449 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -303,12 +303,14 @@ services:
       AUTH_SERVICE_CLIENT_SECRET: ${AUTH_SERVICE_CLIENT_SECRET:-MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG}
       AUTH_SERVICE_ENDPOINT: ${AUTH_SERVICE_ENDPOINT:-http://auth-service:8080}
       COLLECTION: ${COLLECTION:-['database','table','column','identifier','unit','concept','user','view']}
+      LOG_LEVEL: ${LOG_LEVEL:-info}
       METADATA_SERVICE_ENDPOINT: ${METADATA_SERVICE_ENDPOINT:-http://metadata-service:8080}
       OPENSEARCH_HOST: ${OPENSEARCH_HOST:-search-db}
       OPENSEARCH_PORT: ${OPENSEARCH_PORT:-9200}
       OPENSEARCH_USERNAME: ${SEARCH_DB_USERNAME:-admin}
       OPENSEARCH_PASSWORD: ${SEARCH_DB_PASSWORD:-admin}
-      LOG_LEVEL: ${LOG_LEVEL:-info}
+      SYSTEM_USERNAME: "${SYSTEM_USERNAME:-admin}"
+      SYSTEM_PASSWORD: "${SYSTEM_PASSWORD:-admin}"
     healthcheck:
       test: curl -sSL localhost:8080/health | grep 'UP' || exit 1
       interval: 10s
@@ -402,11 +404,14 @@ services:
       context: ./dbrepo-search-service/init
       network: host
     environment:
+      LOG_LEVEL: ${LOG_LEVEL:-info}
       METADATA_SERVICE_ENDPOINT: ${METADATA_SERVICE_ENDPOINT:-http://metadata-service:8080}
       OPENSEARCH_HOST: ${OPENSEARCH_HOST:-search-db}
       OPENSEARCH_PORT: ${OPENSEARCH_PORT:-9200}
       OPENSEARCH_USERNAME: ${SEARCH_DB_USERNAME:-admin}
       OPENSEARCH_PASSWORD: ${SEARCH_DB_PASSWORD:-admin}
+      SYSTEM_USERNAME: "${SYSTEM_USERNAME:-admin}"
+      SYSTEM_PASSWORD: "${SYSTEM_PASSWORD:-admin}"
     depends_on:
       dbrepo-search-db:
         condition: service_healthy
diff --git a/helm/dbrepo/Chart.yaml b/helm/dbrepo/Chart.yaml
index d2ff855534c9bbea91e408d2fee96a566994647d..22d1865df5866213c931df8b2b522af33137c1e6 100644
--- a/helm/dbrepo/Chart.yaml
+++ b/helm/dbrepo/Chart.yaml
@@ -30,17 +30,17 @@ dependencies:
   - name: mariadb-galera
     alias: datadb
     version: 13.2.7
-    repository: oci://registry-1.docker.io/bitnamicharts
+    repository: https://charts.bitnami.com/bitnami
     condition: datadb.enabled
   - name: mariadb-galera
     alias: metadatadb
     version: 13.2.7
-    repository: oci://registry-1.docker.io/bitnamicharts
+    repository: https://charts.bitnami.com/bitnami
     condition: metadatadb.enabled
   - name: rabbitmq
     alias: brokerservice
     version: 14.0.0
-    repository: oci://registry-1.docker.io/bitnamicharts
+    repository: https://charts.bitnami.com/bitnami
     condition: brokerservice.enabled
   - name: seaweedfs
     alias: storageservice
@@ -50,15 +50,15 @@ dependencies:
   - name: grafana
     alias: dashboardservice
     version: 11.4.2
-    repository: oci://registry-1.docker.io/bitnamicharts
+    repository: https://charts.bitnami.com/bitnami
     condition: dashboardservice.enabled
   - name: prometheus
     alias: metricdb
     version: 1.3.22
-    repository: oci://registry-1.docker.io/bitnamicharts
+    repository: https://charts.bitnami.com/bitnami
     condition: metricdb.enabled
   - name: nginx
     alias: gatewayservice
     version: 18.3.1
-    repository: oci://registry-1.docker.io/bitnamicharts
+    repository: https://charts.bitnami.com/bitnami
     condition: gatewayservice.enabled
\ No newline at end of file
diff --git a/helm/seaweedfs/Chart.lock b/helm/seaweedfs/Chart.lock
index 1195241a0686ef99b5cde492d2191b3417193279..edcc38c41f0c6b2b35f8d740566918ddb16f17fc 100644
--- a/helm/seaweedfs/Chart.lock
+++ b/helm/seaweedfs/Chart.lock
@@ -4,9 +4,9 @@ dependencies:
   version: 20.2.1
 - name: postgresql
   repository: oci://registry-1.docker.io/bitnamicharts
-  version: 16.4.2
+  version: 16.4.3
 - name: common
   repository: oci://registry-1.docker.io/bitnamicharts
   version: 2.29.0
-digest: sha256:b9f5516ae118fdca61c19cfe79cee8bd77ad96978dd8c06ad003646d5691aca5
-generated: "2025-01-14T10:19:53.265063382+01:00"
+digest: sha256:4c967f771b303ca0db9ba2e355790152448c77a05d3f6c69eda6c234bc3f60c6
+generated: "2025-01-17T15:24:18.141765362+01:00"
diff --git a/helm/seaweedfs/charts/postgresql-16.4.2.tgz b/helm/seaweedfs/charts/postgresql-16.4.2.tgz
deleted file mode 100644
index aaa8899b4dad449edbaa98aefefbce4fa4de756d..0000000000000000000000000000000000000000
Binary files a/helm/seaweedfs/charts/postgresql-16.4.2.tgz and /dev/null differ
diff --git a/helm/seaweedfs/charts/postgresql-16.4.3.tgz b/helm/seaweedfs/charts/postgresql-16.4.3.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..429f7ed063f6655796792fbe711b027e147ddda4
Binary files /dev/null and b/helm/seaweedfs/charts/postgresql-16.4.3.tgz differ
diff --git a/lib/python/dbrepo/api/dto.py b/lib/python/dbrepo/api/dto.py
index dc7f0e191210b70978ec7b26834293705d3012e9..fa7eb063fc453c3dd8fabef04f141d07012c94a3 100644
--- a/lib/python/dbrepo/api/dto.py
+++ b/lib/python/dbrepo/api/dto.py
@@ -5,7 +5,7 @@ from dataclasses import field
 from enum import Enum
 from typing import List, Optional, Annotated
 
-from pydantic import BaseModel, PlainSerializer, Field
+from pydantic import BaseModel, PlainSerializer
 
 Timestamp = Annotated[
     datetime.datetime, PlainSerializer(lambda v: v.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z', return_type=str)
@@ -987,9 +987,10 @@ class DatabaseBrief(BaseModel):
     internal_name: str
     description: Optional[str] = None
     is_public: bool
+    is_schema_public: bool
     identifiers: Optional[List[Identifier]] = field(default_factory=list)
     contact: UserBrief
-    owner: UserBrief
+    owner_id: str
 
 
 class Unique(BaseModel):
diff --git a/lib/python/tests/test_unit_database.py b/lib/python/tests/test_unit_database.py
index affa94b496b1a8bf104fb12264256ad91e6fc8fb..eeeea68832ac33e2f013a3c2deaa3d4eec33122c 100644
--- a/lib/python/tests/test_unit_database.py
+++ b/lib/python/tests/test_unit_database.py
@@ -24,10 +24,11 @@ class DatabaseUnitTest(unittest.TestCase):
             DatabaseBrief(
                 id=1,
                 name='test',
-                owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'),
+                owner_id='8638c043-5145-4be8-a3e4-4b79991b0a16',
                 contact=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'),
                 internal_name='test_abcd',
-                is_public=True)
+                is_public=True,
+                is_schema_public=True)
         ]
         with requests_mock.Mocker() as mock:
             # mock