diff --git a/src/rust/liblektor-rs/Cargo.toml b/src/rust/liblektor-rs/Cargo.toml
index 16435bdb492da2aa3ebe401c2a36ab4ecd016dda..57f84f400f13a38f1a349e6a3cb72792286a7d76 100644
--- a/src/rust/liblektor-rs/Cargo.toml
+++ b/src/rust/liblektor-rs/Cargo.toml
@@ -10,4 +10,5 @@ crate-type = [ "staticlib" ]
 log               = "0.4"
 libc              = "0.2.0"
 diesel_migrations = "2"
-diesel            = { version = "2", default-features = false, features = [ "sqlite" ] }
+diesel            = { version =  "2", default-features = false, features = [ "sqlite" ] }
+serde             = { version = "^1", default-features = false, features = [ "std", "derive" ] }
diff --git a/src/rust/liblektor-rs/migrations/2022-09-30-204512_initial/up.sql b/src/rust/liblektor-rs/migrations/2022-09-30-204512_initial/up.sql
index 6abf0cac6c52a22ee8a6e528187178541e5ec60f..7271ce36976b98ba140759733029962e8b2b22ee 100644
--- a/src/rust/liblektor-rs/migrations/2022-09-30-204512_initial/up.sql
+++ b/src/rust/liblektor-rs/migrations/2022-09-30-204512_initial/up.sql
@@ -24,7 +24,7 @@ CREATE TABLE kara
   , song_origin TEXT    NOT NULL
   , source_name TEXT    NOT NULL
   , language    TEXT    NOT NULL REFERENCES iso_639_1(code)
-  , kara_hash   TEXT    NOT NULL -- TEXT ABOVE + HASH OF FILE IN FS
+  , file_hash   TEXT    NOT NULL
   );
 
 -- We can have multiple kara makers for one kara.
diff --git a/src/rust/liblektor-rs/src/database/mod.rs b/src/rust/liblektor-rs/src/database/mod.rs
index ec02d326f6d0ff0d31bad6984f4081d8f535a3f0..9b910e96103b7ab917dc03508637e7864351fbe7 100644
--- a/src/rust/liblektor-rs/src/database/mod.rs
+++ b/src/rust/liblektor-rs/src/database/mod.rs
@@ -8,6 +8,10 @@ pub mod models;
 pub mod schema;
 pub mod unsafe_interface;
 
+use self::models::*;
+
+use crate::{database::schema::kara_tags, kurisu_api::v1 as api_v1};
+
 /// The migrations!
 const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
 
@@ -25,3 +29,68 @@ pub fn establish_connection(path: impl AsRef<str>) -> Result<SqliteConnection, S
     self::run_migration(&mut conn)?;
     Ok(conn)
 }
+
+/// All the information needed to add a kara recieved from a repo!
+pub type NewKaraRequest<'a> = (
+    models::NewKaraId,
+    models::NewKara<'a>,
+    models::NewLanguage<'a>,
+    Vec<models::AddKaraMaker<'a>>,
+    Vec<models::AddKaraTag>,
+);
+
+pub struct LktDatabaseConnection {
+    sqlite: SqliteConnection,
+}
+
+impl LktDatabaseConnection {
+    /// Get a tag id by its name.
+    pub fn get_tag_id_by_name(&mut self, tag_name: impl AsRef<str>) -> i32 {
+        use self::schema::tag::dsl::*;
+        tag.filter(name.is(tag_name.as_ref()))
+            .first::<Tag>(&mut self.sqlite)
+            .unwrap()
+            .id
+    }
+
+    /// Get a free local id for all karas.
+    pub fn get_kara_new_local_id(&mut self) -> i32 {
+        use self::schema::kara::dsl::*;
+        use diesel::dsl::*;
+        kara.select(max(id))
+            .first::<Option<i32>>(&mut self.sqlite)
+            .unwrap()
+            .unwrap_or(0)
+    }
+
+    /// Create a series of models from a kara signature from Kurisu's V1 API.
+    pub fn new_kara<'a>(&mut self, repo_id: u64, kara: api_v1::Kara<'a>) -> NewKaraRequest<'a> {
+        error!("todo: query the database for a new local id");
+        let local_id = self.get_kara_new_local_id();
+        let id = NewKaraId {
+            repo_id: repo_id as i32,
+            local_kara_id: local_id as i32,
+            repo_kara_id: kara.id as i32,
+        };
+        let lang = NewLanguage::from(kara.get_language());
+        let kara_makers = vec![AddKaraMaker {
+            id: local_id as i32,
+            name: kara.author_name,
+        }];
+        let tags = vec![AddKaraTag {
+            kara_id: local_id as i32,
+            tag_id: self.get_tag_id_by_name("number"),
+            value: Some(format!("{}", kara.song_number)),
+        }];
+        let kara = NewKara {
+            id: local_id as i32,
+            song_title: kara.song_name,
+            song_type: kara.song_type,
+            song_origin: kara.category,
+            source_name: kara.source_name,
+            language: lang.code,
+            file_hash: format!("{}", kara.unix_timestamp),
+        };
+        (id, kara, lang, kara_makers, tags)
+    }
+}
diff --git a/src/rust/liblektor-rs/src/database/models.rs b/src/rust/liblektor-rs/src/database/models.rs
index a56d60921d0e7d5a3c4324ab989a603bd370e4d2..63914a8daefb3807def36e2997d147d9c8dfb004 100644
--- a/src/rust/liblektor-rs/src/database/models.rs
+++ b/src/rust/liblektor-rs/src/database/models.rs
@@ -1,13 +1,78 @@
 //! Models used for querying, inserting or updating the database.
 
-use crate::database::{schema::*, *};
+use crate::{
+    database::{schema::*, *},
+    kurisu_api::v1 as api_v1,
+};
+
+// First the insertable things
+
+#[derive(Insertable)]
+#[diesel(table_name = repo)]
+pub struct NewRepo<'a> {
+    pub id: i32,
+    pub name: &'a str,
+}
+
+#[derive(Insertable)]
+#[diesel(table_name = repo_kara)]
+pub struct NewKaraId {
+    pub repo_id: i32,
+    pub repo_kara_id: i32,
+    pub local_kara_id: i32,
+}
 
 #[derive(Insertable)]
 #[diesel(table_name = kara)]
 pub struct NewKara<'a> {
+    pub id: i32,
     pub song_title: &'a str,
     pub song_type: &'a str,
     pub song_origin: &'a str,
     pub source_name: &'a str,
     pub language: &'a str,
+    pub file_hash: String,
+}
+
+#[derive(Insertable)]
+#[diesel(table_name = kara_makers)]
+pub struct AddKaraMaker<'a> {
+    pub id: i32,
+    pub name: &'a str,
+}
+
+#[derive(Insertable)]
+#[diesel(table_name = iso_639_1)]
+pub struct NewLanguage<'a> {
+    pub code: &'a str,
+    pub name_en: &'a str,
+    pub is_iso: bool,
+    pub is_macro: bool,
+}
+
+#[derive(Insertable)]
+#[diesel(table_name = kara_tags)]
+pub struct AddKaraTag {
+    pub kara_id: i32,
+    pub tag_id: i32,
+    pub value: Option<String>,
+}
+
+impl<'a> From<api_v1::Language<'a>> for NewLanguage<'a> {
+    fn from(lang: api_v1::Language<'a>) -> Self {
+        Self {
+            code: lang.code,
+            name_en: lang.code,
+            is_iso: false,
+            is_macro: false,
+        }
+    }
+}
+
+// Then the queriable things
+#[derive(Queryable)]
+#[diesel(table_name = tag)]
+pub struct Tag {
+    pub id: i32,
+    pub name: String,
 }
diff --git a/src/rust/liblektor-rs/src/database/schema.rs b/src/rust/liblektor-rs/src/database/schema.rs
index 78d438654b092080469df80b1d632119f5a9329b..d45ece40bea531314208616dbd3352cc5b07f7bb 100644
--- a/src/rust/liblektor-rs/src/database/schema.rs
+++ b/src/rust/liblektor-rs/src/database/schema.rs
@@ -25,7 +25,7 @@ diesel::table! {
         song_origin -> Text,
         source_name -> Text,
         language -> Text,
-        kara_hash -> Text,
+        file_hash -> Text,
     }
 }
 
diff --git a/src/rust/liblektor-rs/src/database/unsafe_interface.rs b/src/rust/liblektor-rs/src/database/unsafe_interface.rs
index 3f79a1fd074bb8f304b356ed2dd31deb8b926ec5..ebd5e1af6c7801fd31b1c69e6eb3c629972fe214 100644
--- a/src/rust/liblektor-rs/src/database/unsafe_interface.rs
+++ b/src/rust/liblektor-rs/src/database/unsafe_interface.rs
@@ -12,7 +12,7 @@ use std::mem::ManuallyDrop;
 #[no_mangle]
 pub unsafe extern "C" fn lkt_database_establish_connection(
     path: *const u8,
-) -> *mut SqliteConnection {
+) -> *mut LktDatabaseConnection {
     let mut path_len = 0;
     while *path.offset(path_len) != 0 {
         path_len += 1
@@ -20,7 +20,7 @@ pub unsafe extern "C" fn lkt_database_establish_connection(
     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 _,
+        Ok(conn) => Box::leak(Box::new(LktDatabaseConnection { sqlite: conn })) as *mut _,
         Err(err) => {
             error!("failed to establish connexion to {}: {err}", &path[..]);
             std::ptr::null_mut()
@@ -33,7 +33,7 @@ pub unsafe extern "C" fn lkt_database_establish_connection(
 /// 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) {
+pub unsafe extern "C" fn lkt_database_close_connection(db: *mut LktDatabaseConnection) {
     if db == std::ptr::null_mut() {
         error!("can't clost a connexion to a null database!")
     } else {
diff --git a/src/rust/liblektor-rs/src/kurisu_api/mod.rs b/src/rust/liblektor-rs/src/kurisu_api/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a3a6d96c3f59dd67ed1c89948bc4d13c42504661
--- /dev/null
+++ b/src/rust/liblektor-rs/src/kurisu_api/mod.rs
@@ -0,0 +1 @@
+pub mod v1;
diff --git a/src/rust/liblektor-rs/src/kurisu_api/v1.rs b/src/rust/liblektor-rs/src/kurisu_api/v1.rs
new file mode 100644
index 0000000000000000000000000000000000000000..438ab11210258755960a0186f55d92a383525e01
--- /dev/null
+++ b/src/rust/liblektor-rs/src/kurisu_api/v1.rs
@@ -0,0 +1,33 @@
+use serde::Deserialize;
+
+#[derive(Debug, Deserialize)]
+pub struct Kara<'a> {
+    pub id: u64,
+    pub source_name: &'a str,
+    pub song_name: &'a str,
+    pub song_type: &'a str,
+    pub song_number: u64,
+    pub category: &'a str,
+    pub language: &'a str,
+    pub author_name: &'a str,
+    pub author_year: &'a str,
+    pub is_new: u64,
+    pub upload_comment: &'a str,
+    pub popularity: u64,
+    pub unix_timestamp: u64,
+    pub size: u64,
+}
+
+#[derive(Debug, Deserialize)]
+pub struct Language<'a> {
+    pub code: &'a str,
+}
+
+impl<'a> Kara<'a> {
+    /// Get the language out of a kara
+    pub fn get_language(&self) -> Language<'a> {
+        Language {
+            code: self.language,
+        }
+    }
+}
diff --git a/src/rust/liblektor-rs/src/lib.rs b/src/rust/liblektor-rs/src/lib.rs
index a40f844be227dcd832ad83239bf1fea3e1f7aba3..7aacf0b761ce85ab72543a84933a3e375b476b15 100644
--- a/src/rust/liblektor-rs/src/lib.rs
+++ b/src/rust/liblektor-rs/src/lib.rs
@@ -2,6 +2,7 @@
 
 mod compat;
 mod database;
+pub mod kurisu_api;
 mod module;
 
 pub(crate) use compat::*;