diff --git a/inc/lektor/common.h b/inc/lektor/common.h
index 6699edeffb2658f0948d96c562b063981efaec15..c65f732a5fc441a5d90858350852c337ef4af2ad 100644
--- a/inc/lektor/common.h
+++ b/inc/lektor/common.h
@@ -180,5 +180,10 @@ long long_length(long integer);
 void *safe_malloc(size_t size);
 void *safe_zero_malloc(size_t size);
 int safe_snprintf(char *dest, const size_t max_len, const char *format, ...);
+char *safe_strncpy(char *dest, const char *src, size_t n);
+
+/* Iterate throught a string, copy each element in the dest string.
+   The save ptr must contains a null integer. */
+int iter_string(const char *str, const char *sep, char *dest, const size_t dest_len, size_t *save);
 
 #endif /* __LKT_COMMON_H__ */
diff --git a/inc/lektor/config.def b/inc/lektor/config.def
index 8c442b0174fc5749145561b10e4186218a738fca..e32c0f78000a0ce91c3712dfad0448c69758b937 100644
--- a/inc/lektor/config.def
+++ b/inc/lektor/config.def
@@ -13,6 +13,8 @@ value("kara_dir",       "/home/kara") /* All the folder and sub folder
                                          must be on the same partition */
 value("db_path",        "/home/kara/kara.db")
 
+/* The repo module, here to be able to sync the bakabase with kurisu or
+   any other server, if configured correctly */
 section("repo")
 value("obfuscate",      "1")
 value("module",         "repo")
@@ -23,6 +25,9 @@ value("json",           "https://kurisu.iiens.net/api")
 value("id_json",        "https://kurisu.iiens.net/api?id=%ld")
 value("id_kara",        "https://kurisu.iiens.net/download.php?id=%ld")
 
+/* The player module, so that people can 'sing' when using lektor. By
+   default it's sdl, but one can create any module, for audio support
+   only for example. */
 section("player")
 value("module",         "sdl2")
 value("autoclear",      "true")
@@ -33,3 +38,16 @@ value("def_repeat",     "false")
 value("font_size",      "20")
 value("font_name",      "Hack Nerd Font")
 value("msg_duration",   "4")
+
+/* To place hooks, when some things are done in lektor. The default value 'none' is here so that
+   nothing happens. Like any module, the value can point to a module or a function in the internal
+   register. To specify multiple modules, use a comma separated list, line this:
+   `kara_load = print_stdout, print_stderr`
+   For the moment, only functions in the server register are supported.
+   NOTE: All the functions will recieve a va_list as a unique argument. */
+section("hook")
+value("kara_load",   "none") /* Before a kara is loaded to be played,
+                                the 'player' module needs to support it */
+value("queue_begin", "none") /* On play, when the player is not already playing or paused */
+
+// vi:syntax=c
diff --git a/inc/lektor/config.h b/inc/lektor/config.h
index 4515f0e352df48e9bc35978cedd42993206534ce..5db459f178c1983f23810a844a5423535f1f81a3 100644
--- a/inc/lektor/config.h
+++ b/inc/lektor/config.h
@@ -9,6 +9,7 @@
 /* Forward definition of the lkt_state structure */
 struct lkt_state;
 
+/* The default configuration, stored as a string */
 #define section(sct)        "\n[" sct "]\n"
 #define value(key, val)     key " = " val "\n"
 #define value_opt(key, val) value(key, val)
@@ -44,4 +45,8 @@ void config_default_file(char *dest, size_t len);
 #define env_set(var, val)   setenv(var, val, 1)
 #define env_get             getenv
 
+/* Handle everything for a hook */
+void config_handle_hook(volatile sqlite3 *db, const char *hook_name, int arg_count, ...);
+#define HANDLE_HOOK(db, n, ...) config_handle_hook(db, n, GET_VA_COUNT(__VA_ARGS__), __VA_ARGS__)
+
 #endif /* __LKT_CONFIG_H__ */
diff --git a/inc/lektor/database.h b/inc/lektor/database.h
index 1c709d90f14bad6ccd2c65add613ebd80a1b0a06..dc7f91c396eab8d9d23d7342999731c26f162540 100644
--- a/inc/lektor/database.h
+++ b/inc/lektor/database.h
@@ -144,8 +144,9 @@ bool database_validate_conf  (volatile sqlite3 *db);
 bool database_config_set     (volatile sqlite3 *db, const char *section, const char *key,
                               const char *value);
 bool database_config_get_text(volatile sqlite3 *db, const char *section, const char *key,
-                              char *value,
-                              size_t len);
+                              char *value, size_t len);
+bool database_config_get_text_nospace(volatile sqlite3 *db, const char *section,
+                                      const char *key, char *value, size_t len);
 bool database_config_get_int (volatile sqlite3 *db, const char *section, const char *key,
                               int *value);
 bool database_config_exists  (volatile sqlite3 *db, const char *section, const char *key);
diff --git a/inc/lektor/reg.h b/inc/lektor/reg.h
index 9439439d1f63e3ad939160a70a5e6f60c8a0ee16..f1b4ec8ea92842ba4f3673a6235720c6974a59a1 100644
--- a/inc/lektor/reg.h
+++ b/inc/lektor/reg.h
@@ -46,6 +46,9 @@ struct lkt_module {
     REG_DECLARE(reg)                    \
     static struct module_reg *as = reg;
 
+/* The server register */
+#define REG_SERVER __reg__
+
 /* Call a function from a reg.
  * NOTE:
  * - Use the MOD_CALL macro to call a function that takes arguments
@@ -66,5 +69,6 @@ int reg_import(const char *mod, struct module_reg **ret, void **handle);
 
 /* Set the __reg__ pointer. */
 void reg_export(struct module_reg *reg);
+void reg_global(struct module_reg **reg_ptr);
 
 #endif /* __LKT_REG_H__ */
diff --git a/src/base/common.c b/src/base/common.c
index 7d76ad345b8bcaff011d6388eb564475c1bf98ef..093e5ff3fe816a7b1bba9e9a04a02031d2fec86a 100644
--- a/src/base/common.c
+++ b/src/base/common.c
@@ -274,3 +274,27 @@ long_length(long integer)
     }
     return count;
 }
+
+char *
+safe_strncpy(char *dest, const char *src, size_t n)
+{
+    char *ret = strncpy(dest, src, n);
+    dest[n - 1] = '\0';
+    return ret;
+}
+
+int
+iter_string(const char *str_list, const char *sep, char *dest, const size_t dest_len, size_t *save)
+{
+    size_t len = strcspn(&str_list[*save], sep);
+
+    if (len == 0)
+        return 1;
+    if (len >= dest_len)
+        return -1;
+
+    strncpy(dest, &str_list[*save], len);
+    dest[len] = '\0';
+    *save += len + 1;
+    return 0;
+}
diff --git a/src/base/config.c b/src/base/config.c
index 4d5d7d50564dfdbe22a04befaa19cef117c30d8f..5326bf9c479d827619a7ef7c12975049dc136af5 100644
--- a/src/base/config.c
+++ b/src/base/config.c
@@ -296,3 +296,33 @@ config_open(volatile sqlite3 *db)
 error:
     return ret;
 }
+
+void
+config_handle_hook(volatile sqlite3 *db, const char *hook_name, int arg_count, ...)
+{
+    char hook_handlers[LKT_LINE_MAX];
+    if (!database_config_get_text_nospace(db, "hook", hook_name, hook_handlers, LKT_LINE_MAX)) {
+        LOG_ERROR("HOOK", "Failed to get the hook handlers list for hook '%s'", hook_name);
+        return;
+    }
+
+    va_list va;
+    va_start(va, arg_count);
+
+    size_t save = 0;
+    char name[LKT_LINE_MAX];
+    int code;
+    struct module_reg *server_reg;
+    reg_global(&server_reg);
+
+    while (0 == (code = iter_string(hook_handlers, ",", name, LKT_LINE_MAX, &save))) {
+        if (STR_MATCH("none", name))
+            continue;
+        reg_call(server_reg, name, 1, va);
+    }
+
+    if (code == -1)
+        LOG_ERROR("HOOK", "Failed to parse the handle list for hook '%s'", hook_name);
+
+    va_end(va);
+}
diff --git a/src/base/reg.c b/src/base/reg.c
index 5a92130142a629d67b315b9cb96468e633cc9ea7..ec42e88851878a5971851aabf93d4c279c353742 100644
--- a/src/base/reg.c
+++ b/src/base/reg.c
@@ -20,6 +20,12 @@ reg_export(struct module_reg *reg)
     __reg__ = reg;
 }
 
+void
+reg_global(struct module_reg **reg_ptr)
+{
+    *reg_ptr = __reg__;
+}
+
 void *
 __reg_get(struct module_reg *reg, const char *name)
 {
diff --git a/src/database/config.c b/src/database/config.c
index ec8c5b139abcb0279ef7a3a4c3022f6b8e2d8af1..86fd2d605dc58eaec6adfd56db04057c101b9c0c 100644
--- a/src/database/config.c
+++ b/src/database/config.c
@@ -67,6 +67,32 @@ error:
     return ret;
 }
 
+bool
+database_config_get_text_nospace(volatile sqlite3 *db, const char *section, const char *key,
+                                 char *value, size_t len)
+{
+    static const char *SQL_STMT =
+        "SELECT REPLACE(value, ' ', '')"
+        " FROM config"
+        " WHERE section = ? AND key = ?;\n";
+    sqlite3_stmt *stmt = 0;
+    bool ret = false;
+    char *row;
+
+    SQLITE_PREPARE(db, stmt, SQL_STMT, error);
+    SQLITE_BIND_TEXT(db, stmt, 1, section, error);
+    SQLITE_BIND_TEXT(db, stmt, 2, key, error);
+    SQLITE_STEP_ROW(db, stmt, error);
+
+    row = (char *) sqlite3_column_text(stmt, 0);
+    strncpy(value, row, len);
+    value[len - 1] = 0;
+    ret = true;
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
+
 bool
 database_config_exists(volatile sqlite3 *db, const char *section,
                        const char *key)