diff --git a/src/main/java/me/x150/sipprivate/SipoverPrivateMain.java b/src/main/java/me/x150/sipprivate/SipoverPrivate.java
similarity index 50%
rename from src/main/java/me/x150/sipprivate/SipoverPrivateMain.java
rename to src/main/java/me/x150/sipprivate/SipoverPrivate.java
index 550ee74..698320e 100644
--- a/src/main/java/me/x150/sipprivate/SipoverPrivateMain.java
+++ b/src/main/java/me/x150/sipprivate/SipoverPrivate.java
@@ -1,26 +1,25 @@
 package me.x150.sipprivate;
 
 import net.fabricmc.api.ModInitializer;
-
+import net.minecraft.client.MinecraftClient;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
-public class SipoverPrivateMain implements ModInitializer {
+public class SipoverPrivate implements ModInitializer {
 
-    public static Logger LOGGER = LogManager.getLogger();
-
-    public static final String MOD_ID = "sipoverprivate";
+    public static final String MOD_ID   = "sipoverprivate";
     public static final String MOD_NAME = "SipoverPrivate";
+    public static Logger LOGGER = LogManager.getLogger();
+    public static MinecraftClient client = MinecraftClient.getInstance();
 
-    @Override
-    public void onInitialize() {
+    public static void log(Level level, String message) {
+        LOGGER.log(level, "[" + MOD_NAME + "] " + message);
+    }
+
+    @Override public void onInitialize() {
         log(Level.INFO, "Initializing");
         //TODO: Initializer
     }
 
-    public static void log(Level level, String message){
-        LOGGER.log(level, "["+MOD_NAME+"] " + message);
-    }
-
 }
\ No newline at end of file
diff --git a/src/main/java/me/x150/sipprivate/command/Command.java b/src/main/java/me/x150/sipprivate/command/Command.java
new file mode 100644
index 0000000..eb13a5e
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/command/Command.java
@@ -0,0 +1,59 @@
+package me.x150.sipprivate.command;
+
+import net.minecraft.client.MinecraftClient;
+
+/**
+ * A class representing a command
+ */
+public abstract class Command {
+
+    /**
+     * The minecraft instance
+     */
+    public final  MinecraftClient mc;
+    /**
+     * The main command name
+     */
+    private final String          command;
+    /**
+     * The command's aliases
+     */
+    private final String[]        alias;
+
+    /**
+     * Constructs a new command instance
+     *
+     * @param command The name of the command
+     * @param aliases Triggers for the command
+     */
+    public Command(String command, String... aliases) {
+        this.command = command;
+        this.alias = aliases;
+        this.mc = MinecraftClient.getInstance();
+    }
+
+    /**
+     * Gets the command's name
+     *
+     * @return The command name
+     */
+    public String getCommand() {
+        return command;
+    }
+
+    /**
+     * Gets the command's aliases
+     *
+     * @return The command's aliases
+     */
+    public String[] getAliases() {
+        return alias;
+    }
+
+    /**
+     * Called when the command is executed
+     *
+     * @param args The arguments (excluding prefix and command name) specified
+     */
+    public abstract void runCommand(String[] args);
+}
diff --git a/src/main/java/me/x150/sipprivate/command/CommandManager.java b/src/main/java/me/x150/sipprivate/command/CommandManager.java
new file mode 100644
index 0000000..667c069
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/command/CommandManager.java
@@ -0,0 +1,49 @@
+package me.x150.sipprivate.command;
+
+import me.x150.sipprivate.command.commands.BindCommand;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class CommandManager {
+
+    /**
+     * A list of all registered commands
+     */
+    private final List<Command> commands = new ArrayList<>();
+
+    /**
+     * Constructs a new command manager
+     */
+    public CommandManager() {
+        addCommands();
+    }
+
+    /**
+     * Registers the commands
+     */
+    public void addCommands() {
+        addCommand(new BindCommand());
+    }
+
+    /**
+     * Adds a command to the list of registered commands, registering it
+     *
+     * @param command The command instance
+     */
+    private void addCommand(Command command) {
+        commands.add(command);
+    }
+
+    /**
+     * Gets all registered commands
+     *
+     * @return The list of registered commands
+     */
+    @NotNull public List<Command> getCommands() {
+        return Collections.unmodifiableList(commands);
+    }
+
+}
diff --git a/src/main/java/me/x150/sipprivate/command/commands/BindCommand.java b/src/main/java/me/x150/sipprivate/command/commands/BindCommand.java
new file mode 100644
index 0000000..bc96df9
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/command/commands/BindCommand.java
@@ -0,0 +1,38 @@
+package me.x150.sipprivate.command.commands;
+
+import me.x150.sipprivate.command.Command;
+import me.x150.sipprivate.keybinding.KeybindingManager;
+import me.x150.sipprivate.module.Module;
+import me.x150.sipprivate.module.ModuleManager;
+import me.x150.sipprivate.util.ChatUtil;
+
+public class BindCommand extends Command {
+
+    public BindCommand() {
+        super("BindCommand", "bind");
+    }
+
+    @Override public void runCommand(String[] args) {
+        if (args.length < 2) {
+            ChatUtil.send("I need the module and key to bind");
+            return;
+        }
+        String mod = args[0];
+        String key = args[1];
+        if (key.length() != 1) {
+            ChatUtil.send("One character as key allowed only");
+            return;
+        }
+
+        int kc = key.toUpperCase().charAt(0);
+        Module m = ModuleManager.instance().getModuleByName(mod);
+        if (m == null) {
+            ChatUtil.send("Cant find that module");
+            return;
+        }
+        ChatUtil.send("BIND " + m.getName() + " -> " + kc + " (" + ((char) kc) + ")");
+        //m.config.get("Keybind").setValue(kc);
+        m.keybind.setValue(kc);
+        KeybindingManager.reload();
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/config/BooleanSetting.java b/src/main/java/me/x150/sipprivate/config/BooleanSetting.java
new file mode 100644
index 0000000..02a2481
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/config/BooleanSetting.java
@@ -0,0 +1,26 @@
+package me.x150.sipprivate.config;
+
+/**
+ * A setting describing a boolean
+ */
+public class BooleanSetting extends SettingBase<Boolean> {
+
+    public BooleanSetting(Boolean defaultValue, String name, String description) {
+        super(defaultValue, name, description);
+    }
+
+    @Override public Boolean parse(String value) {
+        return (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("1"));
+    }
+
+    public static class Builder extends SettingBase.Builder<Builder, Boolean, BooleanSetting> {
+
+        public Builder(Boolean defaultValue) {
+            super(defaultValue);
+        }
+
+        @Override public BooleanSetting get() {
+            return new BooleanSetting(defaultValue, name, description);
+        }
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/config/DoubleSetting.java b/src/main/java/me/x150/sipprivate/config/DoubleSetting.java
new file mode 100644
index 0000000..bc465f8
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/config/DoubleSetting.java
@@ -0,0 +1,56 @@
+package me.x150.sipprivate.config;
+
+public class DoubleSetting extends SettingBase<Double> {
+    int    prec;
+    double min, max;
+
+    public DoubleSetting(Double defaultValue, String name, String description, int precision, double min, double max) {
+        super(defaultValue, name, description);
+        this.prec = precision;
+        this.min = min;
+        this.max = max;
+    }
+
+    @Override public Double parse(String value) {
+        try {
+            return Double.parseDouble(value);
+        } catch (Exception ignored) {
+            return defaultValue;
+        }
+    }
+
+    @Override public void setValue(Double value) {
+        if (value > max || value < min) {
+            return;
+        }
+        super.setValue(value);
+    }
+
+    public static class Builder extends SettingBase.Builder<Builder, Double, DoubleSetting> {
+        double min = Double.NEGATIVE_INFINITY, max = Double.POSITIVE_INFINITY;
+        int prec = 2;
+
+        public Builder(double defaultValue) {
+            super(defaultValue);
+        }
+
+        public Builder precision(int prec) {
+            this.prec = prec;
+            return this;
+        }
+
+        public Builder min(double min) {
+            this.min = min;
+            return this;
+        }
+
+        public Builder max(double max) {
+            this.max = max;
+            return this;
+        }
+
+        @Override public DoubleSetting get() {
+            return new DoubleSetting(defaultValue, name, description, prec, min, max);
+        }
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/config/EnumSetting.java b/src/main/java/me/x150/sipprivate/config/EnumSetting.java
new file mode 100644
index 0000000..10c3723
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/config/EnumSetting.java
@@ -0,0 +1,46 @@
+package me.x150.sipprivate.config;
+
+import java.util.Arrays;
+
+public class EnumSetting<T extends Enum<?>> extends SettingBase<T> {
+    private T[] values;
+
+    @SuppressWarnings("unchecked") public EnumSetting(T defaultValue, String name, String description) {
+        super(defaultValue, name, description);
+        try {
+            this.values = (T[]) defaultValue.getClass().getMethod("values").invoke(null);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override public T parse(String value) {
+        for (T t : values) {
+            if (value.equalsIgnoreCase(t.toString())) {
+                return t;
+            }
+        }
+        return defaultValue;
+    }
+
+    public T[] getValues() {
+        return values;
+    }
+
+    @Override public void setValue(T value) {
+        if (Arrays.stream(values).noneMatch(t -> t.equals(value))) {
+            return;
+        }
+        super.setValue(value);
+    }
+
+    public static class Builder<T extends Enum<?>> extends SettingBase.Builder<Builder<T>, T, EnumSetting<T>> {
+        public Builder(T defaultValue) {
+            super(defaultValue);
+        }
+
+        @Override public EnumSetting<T> get() {
+            return new EnumSetting<>(defaultValue, name, description);
+        }
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/config/IntSetting.java b/src/main/java/me/x150/sipprivate/config/IntSetting.java
new file mode 100644
index 0000000..d178f62
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/config/IntSetting.java
@@ -0,0 +1,41 @@
+package me.x150.sipprivate.config;
+
+public class IntSetting extends SettingBase<Integer> {
+    int min, max;
+
+    public IntSetting(Integer defaultValue, String name, String description, int min, int max) {
+        super(defaultValue, name, description);
+        this.min = min;
+        this.max = max;
+    }
+
+    @Override public Integer parse(String value) {
+        try {
+            return Integer.parseInt(value);
+        } catch (Exception ignored) {
+            return defaultValue;
+        }
+    }
+
+    public static class Builder extends SettingBase.Builder<Builder, Integer, IntSetting> {
+        int min = Integer.MIN_VALUE, max = Integer.MAX_VALUE;
+
+        public Builder(Integer defaultValue) {
+            super(defaultValue);
+        }
+
+        public Builder min(int min) {
+            this.min = min;
+            return this;
+        }
+
+        public Builder max(int max) {
+            this.max = max;
+            return this;
+        }
+
+        @Override public IntSetting get() {
+            return new IntSetting(defaultValue, name, description, min, max);
+        }
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/config/ModuleConfig.java b/src/main/java/me/x150/sipprivate/config/ModuleConfig.java
new file mode 100644
index 0000000..ccf8a7e
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/config/ModuleConfig.java
@@ -0,0 +1,36 @@
+package me.x150.sipprivate.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ModuleConfig {
+    List<SettingBase<?>> settings = new ArrayList<>();
+    List<SettingsGroup>  groups   = new ArrayList<>();
+
+    public <S extends SettingBase<?>> S create(S in) { // used as a proxy to make a one liner
+        settings.add(in);
+        return in;
+    }
+
+    public SettingsGroup create(SettingsGroup in) {
+        groups.add(in);
+        return in;
+    }
+
+    public SettingBase<?> get(String name) {
+        for (SettingBase<?> setting : getSettings()) {
+            if (setting.getName().equals(name)) {
+                return setting;
+            }
+        }
+        return null;
+    }
+
+    public List<SettingBase<?>> getSettings() {
+        return settings;
+    }
+
+    public List<SettingsGroup> getGroups() {
+        return groups;
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/config/SettingBase.java b/src/main/java/me/x150/sipprivate/config/SettingBase.java
new file mode 100644
index 0000000..e026d47
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/config/SettingBase.java
@@ -0,0 +1,184 @@
+package me.x150.sipprivate.config;
+
+import java.util.function.Consumer;
+
+/**
+ * A class depicting a setting
+ *
+ * @param <V> The type of which value should be stored here
+ */
+public abstract class SettingBase<V> {
+    /**
+     * The name and description of this setting
+     */
+    public final String name, description;
+    /**
+     * The default value of this setting
+     */
+    V defaultValue;
+    /**
+     * The current value of this setting
+     */
+    V value;
+
+    /**
+     * Constructs a new Setting
+     *
+     * @param defaultValue The default value
+     * @param name         The name
+     * @param description  The description
+     */
+    public SettingBase(V defaultValue, String name, String description) {
+        this.name = name;
+        this.description = description;
+        this.defaultValue = this.value = defaultValue;
+    }
+
+    /**
+     * Parses a string to its value, not implemented in the base class
+     *
+     * @param value The value we want to parse
+     * @return The parsed output
+     */
+    public abstract V parse(String value);
+
+    /**
+     * Parses and sets a value, implemented because intellij idea is slightly retarded
+     *
+     * @param value The value we want to parse
+     */
+    public void accept(String value) {
+        this.setValue(this.parse(value));
+    }
+
+    /**
+     * Gets the current value of this setting
+     *
+     * @return The value of this setting
+     */
+    public V getValue() {
+        return value;
+    }
+
+    /**
+     * Sets the value of this setting
+     *
+     * @param value The new value
+     */
+    public void setValue(V value) {
+        this.value = value;
+    }
+
+    /**
+     * Gets the description of this setting
+     *
+     * @return The description
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Gets the name of this setting
+     *
+     * @return The name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Gets the default value of this setting
+     *
+     * @return The default value
+     */
+    public V getDefaultValue() {
+        return defaultValue;
+    }
+
+    /**
+     * Builds a new Setting
+     *
+     * @param <B> The builder class
+     * @param <V> The type of value we want to parse
+     * @param <S> The setting class
+     */
+    @SuppressWarnings("unchecked") public abstract static class Builder<B extends Builder<?, ?, ?>, V, S extends SettingBase<?>> {
+        /**
+         * Name and description
+         */
+        String name = "none", description = "";
+        /**
+         * The default value
+         */
+        V           defaultValue;
+        /**
+         * Event listener when the value changed
+         */
+        Consumer<V> changed;
+
+        /**
+         * Constructs a new builder
+         *
+         * @param defaultValue The default value
+         */
+        protected Builder(V defaultValue) {
+            this.defaultValue = defaultValue;
+        }
+
+        /**
+         * Sets the name of this setting
+         *
+         * @param name The name
+         * @return The current builder
+         */
+        public B name(String name) {
+            this.name = name;
+            return getThis();
+        }
+
+        /**
+         * Sets the description of this setting
+         *
+         * @param description The description
+         * @return The current builder
+         */
+        public B description(String description) {
+            this.description = description;
+            return getThis();
+        }
+
+        /**
+         * Sets the default value of this setting
+         *
+         * @param defaultValue The default value
+         * @return The current builder
+         */
+        public B defaultValue(V defaultValue) {
+            this.defaultValue = defaultValue;
+            return getThis();
+        }
+
+        /**
+         * Sets the changed listener of this setting
+         *
+         * @param changed The listener
+         * @return The current builder
+         */
+        public B onChanged(Consumer<V> changed) {
+            this.changed = changed;
+            return getThis();
+        }
+
+        /**
+         * Constructs the setting, not implemented in base class
+         *
+         * @return The setting
+         */
+        public abstract S get();
+
+        protected B getThis() {
+            return (B) this;
+        }
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/config/SettingsGroup.java b/src/main/java/me/x150/sipprivate/config/SettingsGroup.java
new file mode 100644
index 0000000..4a91838
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/config/SettingsGroup.java
@@ -0,0 +1,46 @@
+package me.x150.sipprivate.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A group of settings
+ */
+public class SettingsGroup {
+    String name, description;
+    List<SettingBase<?>> settings;
+
+    SettingsGroup(String name, String description, List<SettingBase<?>> settings) {
+        this.name = name;
+        this.description = description;
+        this.settings = settings;
+    }
+
+    public List<SettingBase<?>> getSettings() {
+        return settings;
+    }
+
+    public static class Builder {
+        String name = "none", description = "";
+        List<SettingBase<?>> s = new ArrayList<>();
+
+        public Builder name(String name) {
+            this.name = name;
+            return this;
+        }
+
+        public Builder description(String desc) {
+            this.description = desc;
+            return this;
+        }
+
+        public Builder settings(SettingBase<?>... settings) {
+            s.addAll(List.of(settings));
+            return this;
+        }
+
+        public SettingsGroup get() {
+            return new SettingsGroup(name, description, s);
+        }
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/config/StringSetting.java b/src/main/java/me/x150/sipprivate/config/StringSetting.java
new file mode 100644
index 0000000..82d764a
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/config/StringSetting.java
@@ -0,0 +1,23 @@
+package me.x150.sipprivate.config;
+
+public class StringSetting extends SettingBase<String> {
+
+    public StringSetting(String defaultValue, String name, String description) {
+        super(defaultValue, name, description);
+    }
+
+    @Override public String parse(String value) {
+        return value;
+    }
+
+    public static class Builder extends SettingBase.Builder<Builder, String, StringSetting> {
+
+        public Builder(String defaultValue) {
+            super(defaultValue);
+        }
+
+        @Override public StringSetting get() {
+            return new StringSetting(defaultValue, name, description);
+        }
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/keybinding/Keybind.java b/src/main/java/me/x150/sipprivate/keybinding/Keybind.java
new file mode 100644
index 0000000..a8531bc
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/keybinding/Keybind.java
@@ -0,0 +1,7 @@
+package me.x150.sipprivate.keybinding;
+
+/**
+ * A container class for keybinds
+ */
+public record Keybind(int keycode) {
+}
diff --git a/src/main/java/me/x150/sipprivate/keybinding/KeybindingManager.java b/src/main/java/me/x150/sipprivate/keybinding/KeybindingManager.java
new file mode 100644
index 0000000..62677b4
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/keybinding/KeybindingManager.java
@@ -0,0 +1,56 @@
+package me.x150.sipprivate.keybinding;
+
+import me.x150.sipprivate.module.Module;
+import me.x150.sipprivate.module.ModuleManager;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The keybind manager
+ */
+public class KeybindingManager {
+
+    /**
+     * List of all the modules with their respective keybinds
+     */
+    public static final Map<Module, Keybind> keybindMap = new HashMap<>();
+
+    /**
+     * Init the keybinding manager
+     */
+    public static void init() {
+        for (Module module : ModuleManager.instance().getModules()) {
+            keybindMap.put(module, new Keybind(module.keybind.getValue()));
+        }
+    }
+
+    /**
+     * Update a single keybind via keyboard event
+     *
+     * @param kc     The key which was changed
+     * @param action The action performed (0 = release, 1 = pressed, 2 = repeat pressed when holding)
+     */
+    public static void updateSingle(int kc, int action) {
+        if (kc == -1) {
+            return; // JESSE WE FUCKED UP
+        }
+        if (action == 1) { // key pressed
+            for (Module o : keybindMap.keySet().toArray(new Module[0])) {
+                Keybind kb = keybindMap.get(o);
+                if (kb.keycode() == kc) {
+                    o.toggle();
+                }
+            }
+        }
+    }
+
+    /**
+     * Reloads the keybind manager
+     */
+    public static void reload() {
+        keybindMap.clear();
+        init();
+    }
+
+}
diff --git a/src/main/java/me/x150/sipprivate/module/Category.java b/src/main/java/me/x150/sipprivate/module/Category.java
new file mode 100644
index 0000000..ec5eb7b
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/module/Category.java
@@ -0,0 +1,18 @@
+package me.x150.sipprivate.module;
+
+/**
+ * Enum containing each category of module
+ */
+public enum Category {
+
+    COMBAT("Combat"), MOVEMENT("Movement"), RENDER("Render"), EXPLOIT("Exploit"), PLAYER("Player"), MISC("Miscellaneous");
+    String name;
+
+    Category(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/module/Module.java b/src/main/java/me/x150/sipprivate/module/Module.java
new file mode 100644
index 0000000..2818ee7
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/module/Module.java
@@ -0,0 +1,123 @@
+package me.x150.sipprivate.module;
+
+import me.x150.sipprivate.config.IntSetting;
+import me.x150.sipprivate.config.ModuleConfig;
+import net.minecraft.client.MinecraftClient;
+
+/**
+ * A container describing a module
+ */
+public abstract class Module {
+
+    /**
+     * The minecraft instance
+     */
+    protected static final MinecraftClient mc = MinecraftClient.getInstance();
+    /**
+     * The configuration of this module
+     */
+    public final           ModuleConfig    config;
+    /**
+     * The name of this module
+     */
+    private final          String          name;
+    /**
+     * The description of this module
+     */
+    private final          String          description;
+    /**
+     * The category this module belongs in
+     */
+    private final          Category        category;
+    /**
+     * The keybind of this module
+     */
+    public                 IntSetting      keybind;
+    /**
+     * Whether this module is currently enabled
+     */
+    private                boolean         enabled;
+
+    /**
+     * Constructs a new module
+     *
+     * @param name        The name of the module
+     * @param description The description of this module
+     * @param category    Which category this module belongs in
+     */
+    public Module(String name, String description, Category category) {
+        this.name = name;
+        this.description = description;
+        this.category = category;
+        this.config = new ModuleConfig();
+        keybind = config.create(new IntSetting.Builder(-1).min(-1).name("Keybind").description("The keybind to toggle the module with").get());
+        // this.config.create("Keybind", -1).description("The keybind to toggle the module with");
+    }
+
+    /**
+     * Gets the name of this module
+     *
+     * @return This module's name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Gets the description of this module
+     *
+     * @return This module's description
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Gets the category of this module
+     *
+     * @return This module's category
+     */
+    public Category getCategory() {
+        return category;
+    }
+
+    /**
+     * Whether this module is enabled or not
+     *
+     * @return true if the module is enabled, false if otherwise
+     */
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    /**
+     * Sets this module's enabled state
+     *
+     * @param enabled The new enabled state
+     */
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+        if (this.enabled) {
+            onEnable();
+        } else {
+            onDisable();
+        }
+    }
+
+    /**
+     * Turns this module on if it's off, off when otherwise
+     */
+    public void toggle() {
+        setEnabled(!isEnabled());
+    }
+
+    /**
+     * Gets called when the module switches from disabled to enabled, not implemented in base class
+     */
+    protected abstract void onEnable();
+
+    /**
+     * Gets called when the module switches from enabled to disabled, not implemented in base class
+     */
+    protected abstract void onDisable();
+}
diff --git a/src/main/java/me/x150/sipprivate/module/ModuleManager.java b/src/main/java/me/x150/sipprivate/module/ModuleManager.java
new file mode 100644
index 0000000..90ff340
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/module/ModuleManager.java
@@ -0,0 +1,69 @@
+package me.x150.sipprivate.module;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The module manager
+ */
+public class ModuleManager {
+
+    private static final ModuleManager instance = new ModuleManager();
+    /**
+     * A list of all registered modules
+     */
+    private final        List<Module>  modules  = new ArrayList<>();
+
+    private ModuleManager() {
+        addModules();
+    }
+
+    public static ModuleManager instance() {
+        return instance;
+    }
+
+    /**
+     * Adds all modules we want to register
+     */
+    public void addModules() {
+
+    }
+
+    /**
+     * Registers a module
+     *
+     * @param module The module to register
+     */
+    private void addModule(Module module) {
+        modules.add(module);
+    }
+
+    /**
+     * Returns all modules
+     *
+     * @return The modules
+     */
+    @NotNull public List<Module> getModules() {
+        return Collections.unmodifiableList(modules);
+    }
+
+    /**
+     * Returns the first module which has the name we specified, null if no module matches
+     *
+     * @param name The name we want to find
+     * @return The module found, null otherwise
+     */
+    @Nullable public Module getModuleByName(String name) {
+        for (Module m : modules) {
+            if (m.getName().equalsIgnoreCase(name)) {
+                return m;
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/util/ChatUtil.java b/src/main/java/me/x150/sipprivate/util/ChatUtil.java
new file mode 100644
index 0000000..3e6cdcd
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/util/ChatUtil.java
@@ -0,0 +1,16 @@
+package me.x150.sipprivate.util;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.network.MessageType;
+import net.minecraft.text.Text;
+
+public class ChatUtil {
+    /**
+     * Sends a message to the user, client side
+     *
+     * @param s The message to send. Formatting using ยง is allowed
+     */
+    public static void send(final String s) {
+        MinecraftClient.getInstance().inGameHud.addChatMessage(MessageType.SYSTEM, Text.of(s), MinecraftClient.getInstance().player.getUuid());
+    }
+}
diff --git a/src/main/java/me/x150/sipprivate/util/ConfigManager.java b/src/main/java/me/x150/sipprivate/util/ConfigManager.java
new file mode 100644
index 0000000..73832e5
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/util/ConfigManager.java
@@ -0,0 +1,222 @@
+package me.x150.sipprivate.util;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import me.x150.sipprivate.SipoverPrivate;
+import me.x150.sipprivate.config.SettingBase;
+import me.x150.sipprivate.keybinding.KeybindingManager;
+import me.x150.sipprivate.module.Module;
+import me.x150.sipprivate.module.ModuleManager;
+import org.apache.commons.io.FileUtils;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterOutputStream;
+
+public class ConfigManager {
+
+    static final  List<Module> toBeEnabled = new ArrayList<>();
+    static final  File         CONFIG_FILE;
+    public static boolean      loaded      = false;
+    public static boolean      enabled     = false;
+
+    static {
+        CONFIG_FILE = new File(SipoverPrivate.client.runDirectory, "config.sip");
+    }
+
+    /**
+     * Encrypts a byte array with a key
+     *
+     * @param in  The byte array to encrypt
+     * @param key The key to use
+     * @return The encrypted byte array
+     * @throws Exception If something goes wrong
+     */
+    static byte[] encrypt(byte[] in, String key) throws Exception {
+        byte[] k = key.getBytes(StandardCharsets.UTF_8);
+        MessageDigest msgd = MessageDigest.getInstance("SHA-1");
+        k = msgd.digest(k);
+        k = Arrays.copyOf(k, 16);
+        SecretKeySpec sks = new SecretKeySpec(k, "AES");
+        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+        cipher.init(Cipher.ENCRYPT_MODE, sks);
+        //        return Base64.getEncoder().encodeToString(cipher.doFinal(in.getBytes(StandardCharsets.UTF_8)));
+        return cipher.doFinal(in);
+    }
+
+    /**
+     * Compresses a byte array using GZIP Deflate
+     *
+     * @param in The input
+     * @return The compressed output
+     * @throws Exception If something goes wrong
+     */
+    public static byte[] compress(byte[] in) throws Exception {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        try (DeflaterOutputStream dos = new DeflaterOutputStream(os)) {
+            dos.write(in);
+        }
+        return os.toByteArray();
+    }
+
+    /**
+     * Decompressed a byte array using GZIP Inflate
+     *
+     * @param in The compressed data
+     * @return The decompressed date
+     * @throws Exception If something goes wrong
+     */
+    public static byte[] decompress(byte[] in) throws Exception {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        try (InflaterOutputStream ios = new InflaterOutputStream(os)) {
+            ios.write(in);
+        }
+
+        return os.toByteArray();
+    }
+
+    /**
+     * Decrypts a byte array with a key
+     *
+     * @param in  The byte array to decrypt
+     * @param key The key used to encrypt the byte array
+     * @return The decrypted byte array
+     * @throws Exception If something goes wrong
+     */
+    static byte[] decrypt(byte[] in, String key) throws Exception {
+        byte[] k = key.getBytes(StandardCharsets.UTF_8);
+        MessageDigest msgd = MessageDigest.getInstance("SHA-1");
+        k = msgd.digest(k);
+        k = Arrays.copyOf(k, 16);
+        SecretKeySpec sks = new SecretKeySpec(k, "AES");
+        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+        cipher.init(Cipher.DECRYPT_MODE, sks);
+        return cipher.doFinal(in);
+    }
+
+    /**
+     * Saves the current state of the client to the file
+     */
+    public static void saveState() {
+        if (!loaded || !enabled) {
+            System.out.println("Not saving config because we didnt load it yet");
+            return;
+        }
+        System.out.println("Saving state");
+        JsonObject base = new JsonObject();
+        JsonArray enabled = new JsonArray();
+        JsonArray config = new JsonArray();
+        for (Module module : ModuleManager.instance().getModules()) {
+            if (module.isEnabled()) {
+                enabled.add(module.getName());
+            }
+            JsonObject currentConfig = new JsonObject();
+            currentConfig.addProperty("name", module.getName());
+            JsonArray pairs = new JsonArray();
+            for (SettingBase<?> dynamicValue : module.config.getSettings()) {
+                JsonObject jesus = new JsonObject();
+                jesus.addProperty("key", dynamicValue.getName());
+                jesus.addProperty("value", dynamicValue.getValue() + "");
+                pairs.add(jesus);
+            }
+            currentConfig.add("pairs", pairs);
+            config.add(currentConfig);
+        }
+        base.add("enabled", enabled);
+        base.add("config", config);
+        try {
+            FileUtils.writeByteArrayToFile(CONFIG_FILE, encrypt(compress(base.toString().getBytes(StandardCharsets.UTF_8)), "amogus"));
+            //            FileUtils.write(CONFIG_FILE, encrypt(base.toString(), "amogus"), StandardCharsets.UTF_8);
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.out.println("Failed to save config!");
+        }
+    }
+
+    /**
+     * Loads the state we saved earlier from the file
+     */
+    public static void loadState() {
+        if (loaded) {
+            return;
+        }
+        loaded = true;
+        try {
+            if (!CONFIG_FILE.isFile()) {
+                //noinspection ResultOfMethodCallIgnored
+                CONFIG_FILE.delete();
+            }
+            if (!CONFIG_FILE.exists()) {
+                return;
+            }
+            byte[] retrv = FileUtils.readFileToByteArray(CONFIG_FILE);
+            //            String retrv = FileUtils.readFileToString(CONFIG_FILE, StandardCharsets.UTF_8);
+            String decr = new String(decompress(decrypt(retrv, "amogus")));
+            JsonObject config = new JsonParser().parse(decr).getAsJsonObject();
+            if (config.has("config") && config.get("config").isJsonArray()) {
+                JsonArray configArray = config.get("config").getAsJsonArray();
+                for (JsonElement jsonElement : configArray) {
+                    if (jsonElement.isJsonObject()) {
+                        JsonObject jobj = jsonElement.getAsJsonObject();
+                        String name = jobj.get("name").getAsString();
+                        Module j = ModuleManager.instance().getModuleByName(name);
+                        if (j == null) {
+                            continue;
+                        }
+                        if (jobj.has("pairs") && jobj.get("pairs").isJsonArray()) {
+                            JsonArray pairs = jobj.get("pairs").getAsJsonArray();
+                            for (JsonElement pair : pairs) {
+                                JsonObject jo = pair.getAsJsonObject();
+                                String key = jo.get("key").getAsString();
+                                String value = jo.get("value").getAsString();
+                                SettingBase<?> val = j.config.get(key);
+                                if (val != null) {
+                                    val.accept(value);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (config.has("enabled") && config.get("enabled").isJsonArray()) {
+                for (JsonElement enabled : config.get("enabled").getAsJsonArray()) {
+                    String name = enabled.getAsString();
+                    Module m = ModuleManager.instance().getModuleByName(name);
+                    if (m != null) {
+                        toBeEnabled.add(m);
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            KeybindingManager.reload();
+        }
+    }
+
+    /**
+     * Enables all modules to be enabled, when we are in game
+     */
+    public static void enableModules() {
+        if (enabled) {
+            return;
+        }
+        enabled = true;
+        for (Module module : toBeEnabled) {
+            module.setEnabled(true);
+        }
+    }
+
+}
diff --git a/src/main/java/me/x150/sipprivate/util/Utils.java b/src/main/java/me/x150/sipprivate/util/Utils.java
new file mode 100644
index 0000000..fe4c79f
--- /dev/null
+++ b/src/main/java/me/x150/sipprivate/util/Utils.java
@@ -0,0 +1,16 @@
+package me.x150.sipprivate.util;
+
+import java.awt.Color;
+
+public class Utils {
+
+    /**
+     * Returns an RGB color based on time
+     *
+     * @return The color
+     */
+    public static Color getCurrentRGB() {
+        return new Color(Color.HSBtoRGB((System.currentTimeMillis() % 4750) / 4750f, 0.5f, 1));
+    }
+
+}
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index bf909d8..213f219 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -3,7 +3,7 @@
   "id": "sipoverprivate",
   "version": "${version}",
   "name": "SipoverPrivate",
-  "description": "YOOOOO $2500 WE GETTIN RICH BOYYS",
+  "description": "YOOOOO 2500 KROMER WE GETTIN RICH BOYYS",
   "authors": [
     "0x150",
     "Kopamed"
@@ -15,7 +15,7 @@
   "environment": "*",
   "entrypoints": {
     "main": [
-      "me.x150.sipprivate.SipoverPrivateMain"
+      "me.x150.sipprivate.SipoverPrivate"
     ],
     "client": [],
     "server": []