diff --git a/.forgejo/workflows/build-release.yaml b/.forgejo/workflows/build-release.yaml
index 48e5e92..1170c10 100644
--- a/.forgejo/workflows/build-release.yaml
+++ b/.forgejo/workflows/build-release.yaml
@@ -6,7 +6,7 @@ on:
       - '[0-9]+\.[0-9]+\.[0-9]'
 
 jobs:
-  lint-and-typing:
+  backend-pylint:
     runs-on: docker
     container: nikolaik/python-nodejs:python3.11-nodejs21
     steps:
@@ -14,19 +14,16 @@ jobs:
         uses: https://code.forgejo.org/actions/checkout@v3
       - name: Install packages
         run: |
-          pip install -e .[lint,typing] -q --disable-pip-version-check -q
+          pip install -e . -q
           python -m pip list --format=columns --disable-pip-version-check
+          pip install pylint~=2.17.7 --disable-pip-version-check -q
       - name: Run pylint
         run: |
           pylint --version
           pylint **/*.py --exit-zero --rc-file pyproject.toml
-      - name: Run mypy
-        run: |
-          mypy --version
-          mypy .
 
   build-artifacts:
-    needs: ["lint-and-typing"]
+    needs: ["backend-pylint"]
     runs-on: docker
     container: nikolaik/python-nodejs:python3.11-nodejs21
     steps:
@@ -56,37 +53,3 @@ jobs:
         run: pip install twine
       - name: Upload package to registry
         run: python -m twine upload --repository-url ${{ secrets.REPOSITORY_URL }} -u ${{ secrets.TWINE_DEPLOY_USER }} -p ${{ secrets.TWINE_DEPLOY_PASSWORD }} dist/*
-
-  build-and-push-container:
-    needs: [ "publish-artifacts" ]
-    runs-on: dind
-    steps:
-      - name: Set up Docker Buildx
-        uses: docker/setup-buildx-action@v3
-      - name: Log into Docker Package Registry
-        uses: docker/login-action@v3
-        with:
-          registry: forgejo.neshweb.net
-          username: ${{ secrets.FORGEJO_USERNAME }}
-          password: ${{ secrets.FORGEJO_TOKEN }}
-      - name: Build and push to Docker Package Registry
-        uses: docker/build-push-action@v5
-        with:
-          build-args: |
-            PACKAGE_VERSION=${{ github.ref_name }}
-          push: true
-          tags: forgejo.neshweb.net/firq/dockge-cli:${{ github.ref_name }}
-
-  release:
-    needs: [ build-and-push-container, publish-artifacts ]
-    if: success()
-    runs-on: docker
-    steps:
-      - name: Release New Version
-        uses: actions/forgejo-release@v1
-        with:
-          direction: upload
-          url: https://forgejo.neshweb.net
-          release-dir: release
-          token: ${{ secrets.FORGEJO_TOKEN }}
-          tag: ${{  github.ref_name }}
\ No newline at end of file
diff --git a/.forgejo/workflows/check.yaml b/.forgejo/workflows/check.yaml
deleted file mode 100644
index 55de02a..0000000
--- a/.forgejo/workflows/check.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-on:
-  push:
-    branches: "**"
-
-jobs:
-  pylint:
-    runs-on: docker
-    container: nikolaik/python-nodejs:python3.11-nodejs21
-    steps:
-      - name: Checkout source code
-        uses: https://code.forgejo.org/actions/checkout@v3
-      - name: Install packages
-        run: |
-          pip install -e .[lint] -q --disable-pip-version-check -q
-          python -m pip list --format=columns --disable-pip-version-check
-      - name: Run pylint
-        run: |
-          pylint --version
-          pylint **/*.py --exit-zero --rc-file pyproject.toml
-
-  mypy:
-    runs-on: docker
-    container: nikolaik/python-nodejs:python3.11-nodejs21
-    steps:
-      - name: Checkout source code
-        uses: https://code.forgejo.org/actions/checkout@v3
-      - name: Install packages
-        run: |
-          pip install -e .[typing] -q --disable-pip-version-check -q
-          python -m pip list --format=columns --disable-pip-version-check
-      - name: Run mypy
-        run: |
-          mypy --version
-          mypy .
diff --git a/.forgejo/workflows/lint.yaml b/.forgejo/workflows/lint.yaml
new file mode 100644
index 0000000..a88449c
--- /dev/null
+++ b/.forgejo/workflows/lint.yaml
@@ -0,0 +1,20 @@
+on:
+  push:
+    branches: "**"
+
+jobs:
+  backend-pylint:
+    runs-on: docker
+    container: nikolaik/python-nodejs:python3.11-nodejs21
+    steps:
+      - name: Checkout source code
+        uses: https://code.forgejo.org/actions/checkout@v3
+      - name: Install packages
+        run: |
+          pip install -e . -q
+          python -m pip list --format=columns --disable-pip-version-check
+          pip install pylint~=2.17.7 --disable-pip-version-check -q
+      - name: Run pylint
+        run: |
+          pylint --version
+          pylint **/*.py --exit-zero --rc-file pyproject.toml
diff --git a/.gitignore b/.gitignore
index 460ee2f..2709b2f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,6 @@
 # Python stuff
 __pycache__/
 *.egg-info/
-*.mypy_cache/
 
 # Build artifacts
 dist/
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 88e754d..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,4 +0,0 @@
-FROM forgejo.neshweb.net/ci-docker-images/python-neshweb:3.11
-
-ARG PACKAGE_VERSION=0.1.0
-RUN pip install dockge-cli==${PACKAGE_VERSION}
diff --git a/README.md b/README.md
index 2f946e3..4c51468 100644
--- a/README.md
+++ b/README.md
@@ -1,67 +1,3 @@
 # dockge-cli
 
 A simple CLI application written in Python for communicating with Dockge using websockets
-
-## Background
-
-Dockge (spoken dock-ge or dockage) is a tool to manage docker-compose stacks from a web ui. It is developed by louislam, who also develops UptimeKuma.
-
-Dockge itself doesn't offer any kind of API or programmatic access, as it is just intended for managing stacks via UI.
-
-My current deployment solution for firq.dev and fgo-ta.com is based on Dockge, and I was over it always having to reload the stack whenever I pushed an update. Instead, I wanted to have this as a separate CI step, automatically redeploying a givens stack.
-
-As Dockge is using a websocket-based system under the hood, it was easy to take a look at how communication occurs. In general, communication is achieved by leveraging socket.io for the data. Since Python already offers a solution for socket.io, it is just a matter of emulating the calls the webui sends and receives.
-
-In the end, this is the current result that works pretty well for my understanding. I am still trying to improve upon some issues (login times out, stability, features), but in general this works as a fine solution for automatic stack updating.
-
-## Installation
-
-Install it from the custom package index using
-
-```shell
-pip install --extra-index-url https://forgejo.neshweb.net/api/packages/Firq/pypi/simple/ dockge-cli
-```
-
-Alternativly, install it using this repository. When installing for development, make sure to install with the additional dependencies
-
-```shell
-pip install -e .[lint,typing]
-```
-
-## Usage
-
-Call the CLI using `dockge-cli` or `dockge`.
-
-```shell
-usage: dockge_cli [-h] [--version] {host,login,logout,list,status,restart,start,stop,down,update,exit,help}
-
-CLI interface for interacting with Dockge
-
-positional arguments:
-  {host,login,logout,list,status,restart,start,stop,down,update,exit,help}
-
-options:
-  -h, --help            show this help message and exit
-  --version             show program's version number and exit
-```
-
-Help for each individual command can be invoked by calling `dockge-cli help <command>`
-
-## The magic behind this
-
-Generally, this makes use of the underlying Websockets API that the Dockge frontend uses to communicate with the server. By analyzing the traffic and looking into the codebase, I was able to reverse most of the packets that are being sent. This allows me to then contruct, send and receive my own packets, making the whole thing work.
-
-There are some things that need to be taken into account for this: For one, dockge uses socket.io for the websocket communication. This meant I had to find the corresponding socket.io version to get the correct version of python-socketio. In addition, I had to find out how the authorization mechanism behind this works.
-
-After finishing up the first prototype, the workings are as follows:
-
-1. A websocket session is established using socket.io - this happens automatically
-2. After the session is ready, the `login` command is sent together with a provided username and password
-3. Once the CLI is authorized, the selected command is sent
-4. The CLI waits for any response values and exits once the command has executed successfully
-
-To provide a smooth experience, both the credentials and the remote host URI are stored on disk. just like the `docker` cli, the credentials are not encrypted, meaning it is advised to either clear the credentials after use OR to use the `--username` and `--password` parameters. This is especially recommended for CI applications.
-
-## Known issues
-
-This CLI does not work when Mullvad is used, as Mullvad actively blocks port forwarding (which python-socketio uses)
diff --git a/dockge_cli/.gitignore b/dockge_cli/.gitignore
new file mode 100644
index 0000000..d36a0d4
--- /dev/null
+++ b/dockge_cli/.gitignore
@@ -0,0 +1 @@
+.temp/*
\ No newline at end of file
diff --git a/dockge_cli/__main__.py b/dockge_cli/__main__.py
deleted file mode 100644
index 660e368..0000000
--- a/dockge_cli/__main__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from .dockge_cli import cli
-cli()
diff --git a/dockge_cli/client/commands/__init__.py b/dockge_cli/client/commands/__init__.py
deleted file mode 100644
index 61eb6fc..0000000
--- a/dockge_cli/client/commands/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from ...models import Command
-from .mappings import mapping
-
-commands: dict[str, Command] = { c.cmd: c for c in mapping }
diff --git a/dockge_cli/client/commands/functions.py b/dockge_cli/client/commands/functions.py
deleted file mode 100644
index 3ee695d..0000000
--- a/dockge_cli/client/commands/functions.py
+++ /dev/null
@@ -1,137 +0,0 @@
-from urllib.parse import urlparse
-from getpass import getpass
-import re
-
-from ...models import Credentials
-from ...service import storage
-from ...service.connection import DockgeConnection
-from ..utils import stack_formatter, status_formatter, generic_formatter, credential_parser_factory
-
-class FunctionBindings():
-    """
-    Helper class that provides all the static methods in an organized way
-    This is an abstraction layer of the CLI, as those functions only do little preprocessing before calling the actural DockgeConnection
-    """
-    @staticmethod
-    def __setup():
-        """
-        Creates a connection and logs into Dockge
-        """
-        con = DockgeConnection()
-        con.connect_and_login()
-        return con
-
-    @staticmethod
-    def host(extra_args):
-        """
-        host command binding
-        """
-        if len(extra_args) > 0:
-            mat = re.search(r"((\w+\.)?\w+\.\w+(\/.+)?)", extra_args[0], re.IGNORECASE)
-            if mat is None:
-                raise ValueError("Given host did not match regex")
-            res = urlparse(f"https://{mat[0]}")
-            if all([res.scheme, res.netloc]):
-                storage.put("host", mat[0])
-            else:
-                raise ValueError(f"Malformed URL {extra_args[0]}")
-        print(storage.get("host"))
-
-    @staticmethod
-    def login(extra_args):
-        """
-        login command binding
-        """
-        print(f"WARNING! These credentials will be saved unencrypted in {storage._file.absolute()}")
-        if len(extra_args) > 0:
-            credentials = credential_parser_factory().parse_args(extra_args, namespace=Credentials)
-            storage.put("username", credentials.username, encoded=True)
-            storage.put("password", credentials.password, encoded=True)
-            return
-        storage.put("username", input("Username: "), encoded=True)
-        storage.put("password", getpass("Password: "), encoded=True)
-
-    @staticmethod
-    def logout(_):
-        """
-        logout command binding
-        """
-        storage.remove("username")
-        storage.remove("password")
-
-    @staticmethod
-    def exit(_):
-        """
-        exit command binding
-        """
-        storage.clear()
-
-    @staticmethod
-    def list(_):
-        """
-        list command binding
-        """
-        con = FunctionBindings.__setup()
-        stack_formatter(con.list_stacks())
-        con.disconnect()
-
-    @staticmethod
-    def status(extra_args):
-        """
-        status command binding
-        """
-        con = FunctionBindings.__setup()
-        status_formatter(con.list_stack(extra_args[0]))
-        con.disconnect()
-
-    @staticmethod
-    def restart(extra_args):
-        """
-        restart command binding
-        """
-        con = FunctionBindings.__setup()
-        generic_formatter(con.restart(extra_args[0]))
-        con.disconnect()
-
-    @staticmethod
-    def update(extra_args):
-        """
-        update command binding
-        """
-        con = FunctionBindings.__setup()
-        generic_formatter(con.update(extra_args[0]))
-        con.disconnect()
-
-    @staticmethod
-    def stop(extra_args):
-        """
-        stop command binding
-        """
-        con = FunctionBindings.__setup()
-        generic_formatter(con.stop(extra_args[0]))
-        con.disconnect()
-
-    @staticmethod
-    def start(extra_args):
-        """
-        start command binding
-        """
-        con = FunctionBindings.__setup()
-        generic_formatter(con.start(extra_args[0]))
-        con.disconnect()
-
-    @staticmethod
-    def down(extra_args):
-        """
-        down command binding
-        """
-        con = FunctionBindings.__setup()
-        generic_formatter(con.down(extra_args[0]))
-        con.disconnect()
-
-    @staticmethod
-    def help():
-        """
-        exit command binding - This should never be invoked
-        """
-        print("WTF")
diff --git a/dockge_cli/client/commands/mappings.py b/dockge_cli/client/commands/mappings.py
deleted file mode 100644
index a6ca52f..0000000
--- a/dockge_cli/client/commands/mappings.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from typing import List
-from ...models import Command
-from .functions import FunctionBindings
-
-mapping: List[Command] = [
-    Command(
-        cmd="host",
-        description="Sets and gets the URI of the dockge instance. Remove any unnecessary subdomains/protocols from the URI",
-        args=1,
-        optional=True,
-        func=FunctionBindings.host
-    ),
-    Command(
-        cmd="login",
-        description="Logs into a given dockge account, either with an interactive dialogue or by passing --user and --password",
-        args=2,
-        optional=True,
-        func=FunctionBindings.login
-    ),
-    Command(
-        cmd="logout",
-        description="Removes the credentials from the local storage.",
-        args=0,
-        optional=False,
-        func=FunctionBindings.logout
-    ),
-    Command(
-        cmd="list",
-        description="Lists all available stacks with their status",
-        args=0,
-        optional=False,
-        func=FunctionBindings.list
-    ),
-    Command(
-        cmd="status",
-        description="Returns the status of one stack",
-        args=1,
-        optional=False,
-        func=FunctionBindings.status
-    ),
-    Command(
-        cmd="restart",
-        description="Restarts a given stack",
-        args=1,
-        optional=False,
-        func=FunctionBindings.restart
-    ),
-    Command(
-        cmd="start",
-        description="Starts a given stack",
-        args=1,
-        optional=False,
-        func=FunctionBindings.start
-    ),
-    Command(
-        cmd="stop",
-        description="Stops a given stack",
-        args=1,
-        optional=False,
-        func=FunctionBindings.stop
-    ),
-    Command(
-        cmd="down",
-        description="Stop & Downs a given stack",
-        args=1,
-        optional=False,
-        func=FunctionBindings.down
-    ),
-    Command(
-        cmd="update",
-        description="Updates a stack",
-        args=1,
-        optional=False,
-        func=FunctionBindings.update
-    ),
-    Command(
-        cmd="exit",
-        description="Exits the CLI - this will reset all settings, including credentials and host",
-        args=0,
-        optional=False,
-        func=FunctionBindings.exit
-    ),
-    Command(
-        cmd="help",
-        description="Displays helping hints for commands",
-        args=1,
-        optional=True,
-        func=FunctionBindings.help
-    )
-]
diff --git a/dockge_cli/client/__init__.py b/dockge_cli/commands/__init__.py
similarity index 100%
rename from dockge_cli/client/__init__.py
rename to dockge_cli/commands/__init__.py
diff --git a/dockge_cli/commands/descriptors.json b/dockge_cli/commands/descriptors.json
new file mode 100644
index 0000000..90407a9
--- /dev/null
+++ b/dockge_cli/commands/descriptors.json
@@ -0,0 +1,74 @@
+[
+    {
+        "command": "host",
+        "description": "Sets and gets the URI of the dockge instance. Remove any unnecessary subdomains/protocols from the URI",
+        "args": 1,
+        "optional": true
+    },
+    {
+        "command": "login",
+        "description": "Logs into a given dockge account, either with an interactive dialogue or by passing --user and --password",
+        "args": 2,
+        "optional": true
+    },
+    {
+        "command": "logout",
+        "description": "Removes the credentials from the local storage.",
+        "args": 0,
+        "optional": false
+    },
+    {
+        "command": "list",
+        "description": "Lists all available stacks with their status",
+        "args": 0,
+        "optional": false
+    },
+    {
+        "command": "status",
+        "description": "Returns the status of one stack",
+        "args": 1,
+        "optional": false
+    },
+    {
+        "command": "restart",
+        "description": "Restarts a given stack",
+        "args": 1,
+        "optional": false
+    },
+    {
+        "command": "start",
+        "description": "Starts a given stack",
+        "args": 1,
+        "optional": false
+    },
+    {
+        "command": "stop",
+        "description": "Stops a given stack",
+        "args": 1,
+        "optional": false
+    },
+    {
+        "command": "down",
+        "description": "Stop & Downs a given stack",
+        "args": 1,
+        "optional": false
+    },
+    {
+        "command": "update",
+        "description": "Updates a stack",
+        "args": 1,
+        "optional": false
+    },
+    {
+        "command": "exit",
+        "description": "Exits the CLI - this will reset all settings, including credentials and host",
+        "args": 0,
+        "optional": false
+    },
+    {
+        "command": "help",
+        "description": "Displays helping hints for commands",
+        "args": 1,
+        "optional": true
+    }
+]
\ No newline at end of file
diff --git a/dockge_cli/commands/factory.py b/dockge_cli/commands/factory.py
new file mode 100644
index 0000000..5fc39e9
--- /dev/null
+++ b/dockge_cli/commands/factory.py
@@ -0,0 +1,30 @@
+import pathlib
+import json
+from typing import List, Callable
+from pydantic import BaseModel
+
+from ..components.bindings import binds
+
+class Descriptor(BaseModel):
+    command: str
+    description: str
+    args: int
+    optional: bool
+
+class Command(Descriptor):
+    binding: Callable
+
+_descriptor_file = pathlib.Path(__file__).parent / "descriptors.json"
+
+commands: dict[str, Command] = {}
+
+with open(_descriptor_file, "r", encoding="utf-8") as file:
+    descriptors: List[Descriptor] = json.load(file)
+    for descriptor in descriptors:
+        commands.update({
+            descriptor["command"]:
+            Command(
+                **descriptor,
+                binding=binds[descriptor["command"]]
+                )
+            })
diff --git a/dockge_cli/service/__init__.py b/dockge_cli/components/__init__.py
similarity index 100%
rename from dockge_cli/service/__init__.py
rename to dockge_cli/components/__init__.py
diff --git a/dockge_cli/components/bindings.py b/dockge_cli/components/bindings.py
new file mode 100644
index 0000000..aae7865
--- /dev/null
+++ b/dockge_cli/components/bindings.py
@@ -0,0 +1,117 @@
+from urllib.parse import urlparse
+from getpass import getpass
+
+from ..models.parser import Credentials
+from . import storage
+from .utils import stack_formatter, status_formatter, generic_formatter, get_credential_parser
+from .communicate import DockgeConnection
+
+class ExecutionCommands():
+    @staticmethod
+    def __setup():
+        con = DockgeConnection()
+        con.connect_and_login()
+        return con
+
+    @staticmethod
+    def host(extra_args):
+        if len(extra_args) > 0:
+            res = urlparse(extra_args[0])
+            if all([res.scheme, res.netloc]):
+                host = extra_args[0].rstrip("/").replace("https://", "").replace("wss://", "")
+                storage.put("host", host)
+            else:
+                raise ValueError(f"Malformed URL {extra_args[0]}")
+        print(storage.get("host"))
+
+    @staticmethod
+    def login(extra_args):
+        if len(extra_args) > 0:
+            credentials = get_credential_parser().parse_args(extra_args, namespace=Credentials)
+            storage.put("username", credentials.username, encoded=True)
+            storage.put("password", credentials.password, encoded=True)
+            return
+        storage.put("username", input("Username: "), encoded=True)
+        storage.put("password", getpass("Password: "), encoded=True)
+
+    @staticmethod
+    def logout(_):
+        storage.remove("username")
+        storage.remove("password")
+
+    @staticmethod
+    def exit(_):
+        storage.clear()
+
+    @staticmethod
+    def list(_):
+        con = ExecutionCommands.__setup()
+        stack_formatter(con.list_stacks())
+        con.disconnect()
+
+    @staticmethod
+    def status(extra_args):
+        if extra_args is None:
+            raise ValueError
+        con = ExecutionCommands.__setup()
+        status_formatter(con.list_stack(extra_args[0]))
+        con.disconnect()
+
+    @staticmethod
+    def restart(extra_args):
+        if extra_args is None:
+            raise ValueError
+        con = ExecutionCommands.__setup()
+        generic_formatter(con.restart(extra_args[0]))
+        con.disconnect()
+
+    @staticmethod
+    def update(extra_args):
+        if extra_args is None:
+            raise ValueError
+        con = ExecutionCommands.__setup()
+        generic_formatter(con.update(extra_args[0]))
+        con.disconnect()
+
+    @staticmethod
+    def stop(extra_args):
+        if extra_args is None:
+            raise ValueError
+        con = ExecutionCommands.__setup()
+        generic_formatter(con.stop(extra_args[0]))
+        con.disconnect()
+
+    @staticmethod
+    def start(extra_args):
+        if extra_args is None:
+            raise ValueError
+        con = ExecutionCommands.__setup()
+        generic_formatter(con.start(extra_args[0]))
+        con.disconnect()
+
+    @staticmethod
+    def down(extra_args):
+        if extra_args is None:
+            raise ValueError
+        con = ExecutionCommands.__setup()
+        generic_formatter(con.down(extra_args[0]))
+        con.disconnect()
+
+    @staticmethod
+    def help():
+        print("WTF")
+
+binds = {
+    "host":    ExecutionCommands.host,
+    "login":   ExecutionCommands.login,
+    "logout":  ExecutionCommands.logout,
+    "start":   ExecutionCommands.start,
+    "restart": ExecutionCommands.restart,
+    "stop":    ExecutionCommands.stop,
+    "down":    ExecutionCommands.down,
+    "exit":    ExecutionCommands.exit,
+    "list":    ExecutionCommands.list,
+    "status":  ExecutionCommands.status,
+    "update":  ExecutionCommands.update,
+    "help":    ExecutionCommands.help,
+}
diff --git a/dockge_cli/service/connection.py b/dockge_cli/components/communicate.py
similarity index 69%
rename from dockge_cli/service/connection.py
rename to dockge_cli/components/communicate.py
index 78e7582..4eb645d 100644
--- a/dockge_cli/service/connection.py
+++ b/dockge_cli/components/communicate.py
@@ -5,14 +5,8 @@ import socketio.exceptions
 from . import storage
 
 class DockgeConnection:
-    """
-    Provider class for Dockge
-    Provides all the functionality for connecting, logging in and executing commands
-    """
     class LoginException(BaseException):
-        """
-        Special exception when login fails too often
-        """
+        pass
 
     _sio: socketio.Client
     _host: str
@@ -32,7 +26,7 @@ class DockgeConnection:
     def _init_events(self):
         @self._sio.event
         def connect():
-            self.login()
+            self.connect()
             print("Connected!")
 
         @self._sio.event
@@ -63,34 +57,20 @@ class DockgeConnection:
             success = True
         else:
             print("Issue with login procedure")
-            print(data)
         return success
 
     # Functions
     def connect_and_login(self):
-        """
-        Connect to the websocket
-        """
-        # Dockge uses Socket.io for the websockets, so this URI and params are always the same
-        self._sio.connect(f"https://{self._host}/socket.io/")
-        self.login()
+        self._sio.connect(f"https://{self._host}/socket.io/", transports=['websocket'])
+        self.connect()
 
-    def login(self):
-        """
-        Log into dockge using basicauth
-        Retries 5 times when timeouts occur
-        """
+    def connect(self):
         if self._logged_in:
             return
 
         data = None
         retry, count = True, 0
 
-        if not storage.exists("username"):
-            raise ValueError("Missing username")
-        if not storage.exists("password"):
-            raise ValueError("Missing password")
-
         while retry and count < 5:
             try:
                 data = self._sio.call(
@@ -98,13 +78,12 @@ class DockgeConnection:
                     {
                         "username": storage.get("username", encoded=True),
                         "password": storage.get("password", encoded=True),
-                        "token": ""
+                        "token":""
                     },
-                    timeout=10
+                    timeout=5
                 )
                 retry = False
             except socketio.exceptions.TimeoutError:
-                print("Reached timeout for login, retrying ...")
                 retry = True
                 count += 1
 
@@ -113,9 +92,6 @@ class DockgeConnection:
         self._logged_in = True
 
     def list_stacks(self):
-        """
-        Requests stack list from dockge, returns list when event was sent
-        """
         self._sio.emit("agent", ("", "requestStackList"))
         while self._stacklist is None:
             time.sleep(0.5)
@@ -124,50 +100,29 @@ class DockgeConnection:
         return retval
 
     def list_stack(self, name: str):
-        """
-        Lists status for a stack
-        """
-        ret = self._sio.call("agent", ("", "serviceStatusList", name), timeout=10)
+        ret = self._sio.call("agent", ("", "serviceStatusList", name), timeout=5)
         return ret
 
     def restart(self, name):
-        """
-        Restarts a given stack
-        """
-        ret = self._sio.call("agent", ("", "restartStack", name), timeout=30)
+        ret = self._sio.call("agent", ("", "restartStack", name), timeout=10)
         return ret
 
     def update(self, name):
-        """
-        Updates a given stack
-        """
-        ret = self._sio.call("agent", ("", "updateStack", name), timeout=30)
+        ret = self._sio.call("agent", ("", "updateStack", name), timeout=10)
         return ret
 
     def stop(self, name):
-        """
-        Stops a given stack
-        """
-        ret = self._sio.call("agent", ("", "stopStack", name), timeout=30)
+        ret = self._sio.call("agent", ("", "stopStack", name), timeout=10)
         return ret
 
     def start(self, name):
-        """
-        Starts a given stack
-        """
-        ret = self._sio.call("agent", ("", "startStack", name), timeout=30)
+        ret = self._sio.call("agent", ("", "startStack", name), timeout=10)
         return ret
 
     def down(self, name):
-        """
-        Stops and downs a given stack
-        """
-        ret = self._sio.call("agent", ("", "downStack", name), timeout=30)
+        ret = self._sio.call("agent", ("", "downStack", name), timeout=10)
         return ret
 
     def disconnect(self):
-        """
-        Logs out of dockge
-        """
         self._sio.emit("logout")
         self._sio.disconnect()
diff --git a/dockge_cli/client/parser.py b/dockge_cli/components/parser.py
similarity index 58%
rename from dockge_cli/client/parser.py
rename to dockge_cli/components/parser.py
index f84628b..0cc13f6 100644
--- a/dockge_cli/client/parser.py
+++ b/dockge_cli/components/parser.py
@@ -2,19 +2,15 @@ import argparse
 import sys
 
 from .. import __version__
-from ..models import Arguments
-from .commands import commands
+from ..models.parser import Arguments
+from .bindings import binds
 
 def parse_arguments():
-    """
-    Create a parser and parse the arguments of sys.argv based on the Arguments Namespace
-    Returns arguments and extra arguments separately
-    """
     parser = argparse.ArgumentParser(
                         prog="dockge_cli",
                         description="CLI interface for interacting with Dockge",)
 
-    parser.add_argument("command", choices=list(commands.keys()), action="store", type=str, default=None)
+    parser.add_argument("command", choices=list(binds.keys()), action="store", type=str, default=None)
     parser.add_argument("--version", action="version", version=f"dockge_cli {__version__}")
 
     args = Arguments()
diff --git a/dockge_cli/client/run.py b/dockge_cli/components/run.py
similarity index 74%
rename from dockge_cli/client/run.py
rename to dockge_cli/components/run.py
index 22d0a2a..f4bfe2a 100644
--- a/dockge_cli/client/run.py
+++ b/dockge_cli/components/run.py
@@ -1,9 +1,6 @@
-from .commands import commands
+from ..commands.factory import commands
 
 def display_help(extra_args):
-    """
-    Display help dialogues for each command
-    """
     if not extra_args:
         print(f"{commands['help'].description}")
         return
@@ -14,10 +11,6 @@ def display_help(extra_args):
     print(f"{commands[extra_args[0]].description}")
 
 def run(command, args):
-    """
-    Runs a given command with the provided args
-    Alsso automatically maps the given command string to the correct Command class
-    """
     if command not in commands:
         raise ValueError("Invalid Command")
 
@@ -32,4 +25,4 @@ def run(command, args):
         display_help(args)
         return
 
-    c.func(args)
+    c.binding(args)
diff --git a/dockge_cli/components/storage.py b/dockge_cli/components/storage.py
new file mode 100644
index 0000000..e2d6ace
--- /dev/null
+++ b/dockge_cli/components/storage.py
@@ -0,0 +1,44 @@
+import os
+import pathlib
+import base64
+import yaml
+
+_storagepath = pathlib.Path(__file__).parents[1] / ".temp"
+_file = _storagepath / "storage.yaml"
+
+_storagepath.mkdir(exist_ok=True, parents=True)
+
+def fileexists():
+    if not _file.exists():
+        with open(_file, 'a', encoding="utf-8"):
+            os.utime(_file, None)
+
+def put(key: str, value: str, encoded=False):
+    fileexists()
+    with open(_file, "r+", encoding="utf-8") as file:
+        content: dict[str, str] = yaml.load(file, Loader=yaml.SafeLoader) or {}
+        content.update({ key: base64.b64encode(value.encode()) if encoded else value })
+    with open(_file, "w+", encoding="utf-8") as file:
+        yaml.dump(content, file, Dumper=yaml.SafeDumper)
+
+def remove(key: str):
+    fileexists()
+    with open(_file, "r", encoding="utf-8") as file:
+        content: dict[str, str] = yaml.load(file, Loader=yaml.SafeLoader) or {}
+        content.pop(key, None)
+    with open(_file, "w+", encoding="utf-8") as file:
+        yaml.dump(content, file, Dumper=yaml.SafeDumper)
+
+def get(key: str, encoded=False):
+    value: str = None
+    if not _file.exists():
+        return None
+    with open(_file, "r", encoding="utf-8") as file:
+        content: dict[str, str] = yaml.load(file, Loader=yaml.SafeLoader)
+        value = content.get(key, None)
+    if value is None:
+        return None
+    return base64.b64decode(value).decode() if encoded else value
+
+def clear():
+    _file.unlink()
diff --git a/dockge_cli/client/utils.py b/dockge_cli/components/utils.py
similarity index 76%
rename from dockge_cli/client/utils.py
rename to dockge_cli/components/utils.py
index ebddf26..6e9fa87 100644
--- a/dockge_cli/client/utils.py
+++ b/dockge_cli/components/utils.py
@@ -2,10 +2,7 @@ import argparse
 from tabulate import tabulate
 from ..models import StackStatus
 
-def credential_parser_factory():
-    """
-    Creates a new parser for login credentials
-    """
+def get_credential_parser():
     credentialparser = argparse.ArgumentParser(
         prog="login",
         description="Subparser for login credentials provided by CI"
@@ -15,30 +12,21 @@ def credential_parser_factory():
     return credentialparser
 
 def stack_formatter(stacks):
-    """
-    Prints a given stack list formatted as a table
-    """
     if not stacks["ok"]:
         raise RuntimeError("Stack GET didn't work")
 
     table, headers = [], ["Stackname", "Status"]
     for key, val in stacks["stackList"].items():
-        table.append([key, StackStatus(val['status']).name.lower()])
+        table.append([key, StackStatus(val['status']).name])
 
     print(tabulate(table, headers=headers, tablefmt="github"), "\n")
 
 def status_formatter(status):
-    """
-    Prints the status for a given stack
-    """
     print(f"Is Stack Ok? {'Yes' if status['ok'] else 'No'}")
     headers = ["Container", "Status"]
     table = [[k, v] for k, v in status["serviceStatusList"].items()]
     print(tabulate(table, headers=headers, tablefmt="github"), "\n")
 
 def generic_formatter(status):
-    """
-    Prints a generic dockge message
-    """
     print(f"Is Ok? {'Yes' if status['ok'] else 'No'}")
     print(f"Stack status: {status['msg']}")
diff --git a/dockge_cli/dockge_cli.py b/dockge_cli/dockge_cli.py
index af77054..bbbdc7e 100644
--- a/dockge_cli/dockge_cli.py
+++ b/dockge_cli/dockge_cli.py
@@ -1,9 +1,6 @@
-from .client.parser import parse_arguments
-from .client.run import run
+from .components.parser import parse_arguments
+from .components.run import run
 
 def cli():
-    """
-    main function for cli invocation
-    """
     command, args= parse_arguments()
     run(command.command, args)
diff --git a/dockge_cli/models/__init__.py b/dockge_cli/models/__init__.py
index ac7af80..ef15c9b 100644
--- a/dockge_cli/models/__init__.py
+++ b/dockge_cli/models/__init__.py
@@ -1,3 +1 @@
 from .codes import StackStatus
-from .command import Command
-from .parser import Arguments, Credentials
diff --git a/dockge_cli/models/codes.py b/dockge_cli/models/codes.py
index 569ce1d..b8f9a07 100644
--- a/dockge_cli/models/codes.py
+++ b/dockge_cli/models/codes.py
@@ -1,9 +1,7 @@
 from enum import Enum
 
 class StackStatus(Enum):
-    """
-    mapping for plaintext vs statuscode
-    """
-    INACTIVE = 1
-    RUNNING = 3
-    EXITED = 4
+    # pylint: disable=invalid-name
+    inactive = 1
+    running = 3
+    exited = 4
diff --git a/dockge_cli/models/command.py b/dockge_cli/models/command.py
deleted file mode 100644
index 61a13b3..0000000
--- a/dockge_cli/models/command.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import Callable
-from pydantic import BaseModel
-
-class Command(BaseModel):
-    """
-    Basic command structure for the CLI to automatically generate valid commands
-    """
-    cmd: str
-    func: Callable
-    args: int
-    optional: bool
-    description: str
diff --git a/dockge_cli/models/parser.py b/dockge_cli/models/parser.py
index e590ce9..c836e62 100644
--- a/dockge_cli/models/parser.py
+++ b/dockge_cli/models/parser.py
@@ -2,15 +2,9 @@ import argparse
 
 # pylint: disable=too-few-public-methods
 class Arguments(argparse.Namespace):
-    """
-    Default Arguments when calling the CLI
-    """
     command: str
 
 # pylint: disable=too-few-public-methods
 class Credentials(argparse.Namespace):
-    """
-    Special Argument Namespace for login credentials of the login commands
-    """
     username: str
     password: str
diff --git a/dockge_cli/py.typed b/dockge_cli/py.typed
deleted file mode 100644
index e69de29..0000000
diff --git a/dockge_cli/service/.gitignore b/dockge_cli/service/.gitignore
deleted file mode 100644
index 9e62042..0000000
--- a/dockge_cli/service/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.storage/*
\ No newline at end of file
diff --git a/dockge_cli/service/storage.py b/dockge_cli/service/storage.py
deleted file mode 100644
index 3edf415..0000000
--- a/dockge_cli/service/storage.py
+++ /dev/null
@@ -1,84 +0,0 @@
-import os
-import pathlib
-import base64
-import yaml
-
-_storagepath = pathlib.Path(__file__).parent / ".storage"
-_storagepath.mkdir(exist_ok=True, parents=True)
-_file = _storagepath / "storage.yaml"
-
-def create_file_when_missing():
-    """
-    Checks if storage file does exist, creates it when necessary
-    """
-    if _file.exists():
-        return
-    with open(_file, 'a', encoding="utf-8"):
-        os.utime(_file, None)
-
-def exists(key: str) -> bool:
-    """
-    Checks if a given key exists in the storage file
-    """
-    if not _file.exists():
-        return False
-
-    with open(_file, "r", encoding="utf-8") as file:
-        content: dict[str, str] = yaml.load(file, Loader=yaml.SafeLoader)
-    
-    return key in content
-
-
-def put(key: str, value: str, encoded=False):
-    """
-    Puts a given value with a given key into the storage file
-    Encodes the data as base64 when encoded is set to true
-    """
-    if not _file.exists():
-        create_file_when_missing()
-
-    with open(_file, "r", encoding="utf-8") as file:
-        content: dict[str, str] = yaml.load(file, Loader=yaml.SafeLoader) or {}
-        content.update({ key: str(base64.b64encode(value.encode()), "utf-8") if encoded else value })
-
-    with open(_file, "w+", encoding="utf-8") as file:
-        yaml.dump(content, file, Dumper=yaml.SafeDumper)
-
-def remove(key: str):
-    """
-    Removed a given key from the storage file
-    """
-    if not _file.exists():
-        create_file_when_missing()
-
-    with open(_file, "r", encoding="utf-8") as file:
-        content: dict[str, str] = yaml.load(file, Loader=yaml.SafeLoader) or {}
-        content.pop(key, None)
-
-    with open(_file, "w+", encoding="utf-8") as file:
-        yaml.dump(content, file, Dumper=yaml.SafeDumper)
-
-def get(key: str, encoded=False):
-    """
-    Retrieves a value for a given key from the storage file
-    If the value was encoded, encoded needs to be set True to decode it again
-    """
-    value: str | None = None
-    if not _file.exists():
-        return None
-
-    with open(_file, "r", encoding="utf-8") as file:
-        content: dict[str, str] = yaml.load(file, Loader=yaml.SafeLoader)
-        value = content.get(key, None)
-
-    if value is None:
-        return None
-    return base64.b64decode(value.encode()).decode() if encoded else value
-
-def clear():
-    """
-    Deletes the storage file
-    """
-    if not _file.exists():
-        return
-    _file.unlink()
diff --git a/pyproject.toml b/pyproject.toml
index b706b37..ef7c2f9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,10 +1,9 @@
 [project]
 name = "dockge_cli"
-version = "0.1.2"
+version = "0.0.1-a.2"
 dependencies = [
     "pyyaml~=6.0.1",
     "pydantic~=2.8.0",
-    "requests~=2.32.3",
     "python-socketio~=5.11.3",
     "websocket-client~=1.8.0",
     "tabulate ~=0.9.0",
@@ -12,7 +11,7 @@ dependencies = [
 requires-python = ">= 3.10"
 authors = [{name = "Firq", email = "firelp42@gmail.com"}]
 maintainers = [{name = "Firq", email = "firelp42@gmail.com"}]
-description = "CLI for interacting with dockge"
+description = "CLi for interacting with dockge"
 classifiers = [
     "Development Status :: 3 - Alpha",
     "Programming Language :: Python :: 3",
@@ -30,32 +29,10 @@ where = ["."]
 include = ["dockge_cli*"]
 
 [tool.setuptools.package-data]
-"*" = ["py.typed"]
-
-[project.optional-dependencies]
-lint = [
-    "pylint~=3.2.5",
-]
-typing = [
-    "mypy~=1.10.1",
-    "types-PyYAML~=6.0.12.20240311",
-    "types-tabulate~=0.9.0.20240106",
-]
+"*" = ["descriptors.json"]
 
 [tool.pylint."MAIN"]
-disable = [
-    "line-too-long",
-    "missing-module-docstring",
-]
-
-[tool.mypy]
-python_version = "3.11"
-warn_return_any = true
-warn_unused_configs = true
-
-[[tool.mypy.overrides]]
-module = 'socketio.*'
-ignore_missing_imports = true
+disable = [ "line-too-long", "missing-module-docstring", "missing-function-docstring", "missing-class-docstring" ]
 
 [build-system]
 requires = ["setuptools >= 61.0"]