diff --git a/CMakeLists.txt b/CMakeLists.txt
index e09acd20ab1dad41abc8e7b4b8845abef642273a..42ff35c6bf573989ded07c1986fe2169dd81b678 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -305,6 +305,7 @@ endif()
 target_include_directories(lkt     PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc)
 target_include_directories(lektord PRIVATE
     ${CMAKE_CURRENT_SOURCE_DIR}/inc
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/rust/liblektor-rs/inc
     ${CURL_INCLUDE_DIRS}
 )
 
diff --git a/src/main/server.c b/src/main/server.c
index 7d9225c13d5cc3c2bae72abf645185dbcfefad8b..e36d292568e4b4a2818075d628ea3cbffb589943 100644
--- a/src/main/server.c
+++ b/src/main/server.c
@@ -9,6 +9,8 @@
 #include <lektor/launch.h>
 #include <lektor/logfile.h>
 
+#include <liblektor-rs/database.h>
+
 #include <wait.h>
 #include <spawn.h>
 #include <libgen.h>
diff --git a/src/rust/liblektor-rs/inc/liblektor-rs/database.h b/src/rust/liblektor-rs/inc/liblektor-rs/database.h
new file mode 100644
index 0000000000000000000000000000000000000000..b398c88f7452822301f8b3e1f8d1f418218cbe3d
--- /dev/null
+++ b/src/rust/liblektor-rs/inc/liblektor-rs/database.h
@@ -0,0 +1,18 @@
+#if !defined(LIBLEKTOR_RS_DATABASE___)
+#define LIBLEKTOR_RS_DATABASE___
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct lkt_sqlite_connection;
+typedef struct lkt_sqlite_connection lkt_sqlite_connection;
+
+lkt_sqlite_connection *lkt_database_establish_connection(const char *);
+void lkt_database_close_connection(lkt_sqlite_connection *const);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // LIBLEKTOR_RS_DATABASE___
diff --git a/src/rust/liblektor-rs/src/database/mod.rs b/src/rust/liblektor-rs/src/database/mod.rs
index bf76fec285ed459045d8fab32af764b3b0a3a3b4..ec02d326f6d0ff0d31bad6984f4081d8f535a3f0 100644
--- a/src/rust/liblektor-rs/src/database/mod.rs
+++ b/src/rust/liblektor-rs/src/database/mod.rs
@@ -1,9 +1,12 @@
+//! Database implementation in rust for lektor.
+
 pub(self) use diesel::prelude::*;
 use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
 pub(self) use log::*;
 
 pub mod models;
 pub mod schema;
+pub mod unsafe_interface;
 
 /// The migrations!
 const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
diff --git a/src/rust/liblektor-rs/src/database/models.rs b/src/rust/liblektor-rs/src/database/models.rs
index d6727d48d878ba1d0d8df96e475b9e7fbda2a04b..a56d60921d0e7d5a3c4324ab989a603bd370e4d2 100644
--- a/src/rust/liblektor-rs/src/database/models.rs
+++ b/src/rust/liblektor-rs/src/database/models.rs
@@ -1,3 +1,5 @@
+//! Models used for querying, inserting or updating the database.
+
 use crate::database::{schema::*, *};
 
 #[derive(Insertable)]
diff --git a/src/rust/liblektor-rs/src/database/unsafe_interface.rs b/src/rust/liblektor-rs/src/database/unsafe_interface.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3f79a1fd074bb8f304b356ed2dd31deb8b926ec5
--- /dev/null
+++ b/src/rust/liblektor-rs/src/database/unsafe_interface.rs
@@ -0,0 +1,43 @@
+//! An unsafe interface around the rust implementation of the databse to be able
+//! to call it from C or C++ code.
+//!
+//! Be carefull when naming things because those names might collide with things
+//! defined in lektor's C code...
+
+use super::*;
+use std::mem::ManuallyDrop;
+
+/// Wrap the [`establish_connection`] function. On error log the message and
+/// return a [`std::ptr::null_mut`].
+#[no_mangle]
+pub unsafe extern "C" fn lkt_database_establish_connection(
+    path: *const u8,
+) -> *mut SqliteConnection {
+    let mut path_len = 0;
+    while *path.offset(path_len) != 0 {
+        path_len += 1
+    }
+    let len = path_len as usize;
+    let path = ManuallyDrop::new(String::from_raw_parts(path as *mut _, len, len));
+    match establish_connection(&path[..]) {
+        Ok(conn) => Box::leak(Box::new(conn)) as *mut _,
+        Err(err) => {
+            error!("failed to establish connexion to {}: {err}", &path[..]);
+            std::ptr::null_mut()
+        }
+    }
+}
+
+/// Free a database created by [`lkt_database_establish_connection`]. If a null
+/// pointer is passed to the function log the error and do nothing. If the
+/// passed pointer was not obtained by the correct function the behaviour is
+/// undefined.
+#[no_mangle]
+pub unsafe extern "C" fn lkt_database_close_connection(db: *mut SqliteConnection) {
+    if db == std::ptr::null_mut() {
+        error!("can't clost a connexion to a null database!")
+    } else {
+        let db = Box::from_raw(db);
+        drop(db);
+    }
+}