diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7b86b0c38885f4f3418212a1f6ad0efc5fab4bbd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+gen/*
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..863723aaa30f5fdc6ee2ac9d161759276c1ac987
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,43 @@
+stages:
+  - build
+  - lint
+  #- test
+  #- analyze
+
+variables:
+  IMAGE_PATH: "${CI_REGISTRY_IMAGE}"
+  GOLANG_VERSION: "1.24"
+  GOLANG_MINOR_VERSION: "${GOLANG_VERSION}.0"
+  DOCKER_TLS_CERTDIR: "/certs"
+
+lint-buf:
+  stage: lint
+  image:
+    name: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/bufbuild/buf
+    entrypoint: [""]
+  script:
+    - buf format
+  needs: []
+
+# Build stage
+.build: &build
+  stage: build
+  image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/docker:latest
+  services:
+    - name: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/docker:latest
+      alias: docker
+  before_script:
+    - apk add git
+    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+    - docker login -u $CI_DEPENDENCY_PROXY_USER -p $CI_DEPENDENCY_PROXY_PASSWORD $CI_DEPENDENCY_PROXY_SERVER
+  needs: []
+
+build-kms:
+  script:
+    - docker build -t kms -f kms/Dockerfile .
+  <<: *build
+
+build-ctrl:
+  script:
+    - docker build -t ctrl -f ctrl/Dockerfile .
+  <<: *build
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..7f5157dd66f101983f9d6bfd4be55a6785bc8229
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2025 da/net research group Hochschule Darmstadt
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..16b2ab1f520ea4e9c4638dcfc8041556128d8234
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,4 @@
+
+generate-plantuml:
+	@echo "Generating PlantUML diagrams if plantUML is installed..."
+	plantuml -o . api/_Docs/*/**.plantuml
\ No newline at end of file
diff --git a/README.md b/README.md
index 39fe18c5e8854c0a8b285e170f22639cc41007d7..b3657ee867ff12ad143cc08daa2344f5ff995e1f 100644
--- a/README.md
+++ b/README.md
@@ -1,93 +1,13 @@
 # Costaquanta
 
+This repository is used for development and testing of QKDN software.
+It's use-case is research and development only.
+If you are interested in production grade QKDN software, please feel free to contact us.
 
+## Overview
 
-## Getting started
+This repository is separated into different parts, mainly API definitions and code.
+We follow a SDN approach which is *controller centric*.
+This means that a central `QKDN-Controller` is responsible to manage and configure the network.
 
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
-
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
-
-## Add your files
-
-- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
-
-```
-cd existing_repo
-git remote add origin https://code.fbi.h-da.de/danet/costaquanta.git
-git branch -M main
-git push -uf origin main
-```
-
-## Integrate with your tools
-
-- [ ] [Set up project integrations](https://code.fbi.h-da.de/danet/costaquanta/-/settings/integrations)
-
-## Collaborate with your team
-
-- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
-
-## Test and Deploy
-
-Use the built-in continuous integration in GitLab.
-
-- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
-
-***
-
-# Editing this README
-
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
-
-## Suggestions for a good README
-
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
-
-## Name
-Choose a self-explaining name for your project.
-
-## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
-
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
-
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
-
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
-
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
-
-## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
-
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
-
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
-
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
-
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
-
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
-
-## License
-For open source projects, say how it is licensed.
-
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
+- [API v1](api/_Docs/v1/Readme.md)
diff --git a/api/_Docs/v1/Readme.md b/api/_Docs/v1/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..eb5c0c16569a28f01db7d7aecf6856c68febe17b
--- /dev/null
+++ b/api/_Docs/v1/Readme.md
@@ -0,0 +1,152 @@
+# Documentation API v1
+
+
+This API consists of multiple interfaces.
+
+The folder `ctrl` includes all interfaces the SDN controller offers, `kms` contains the services of the KMS.
+While in practice these APIs are somewhat coupled, they are designed to solve a subset of the whole problem space.
+
+Each `.proto` file contains comments describing its use case, this document describes the general ideas behind it.
+
+## Configuration of the KMS
+
+The configuration for each KMS is done via the `configuration` API each KMS offers to the controller.
+It is meant for relatively static configuration that is needed for each KMS to function.
+This explicitly excludes routing information, as these are done when an actual forwarding of a payload should happen.
+All fields and functions are described in the `config.proto` file.
+We recommend that the KMS only store these information at runtime in memory, so that they loose them on restart/reboot.
+
+## KeySyncing
+
+Before a KeyForwarding can start, we need to make sure that all interconnected KMS (peer KMS) have a synchronized KeyStore to encrypt and decrypt the data while forwarding.
+Some APIs integrate the syncing and forwarding into one API.
+We see it as two distinct problems and separate them.
+This also makes the support of different APIs for the connection of the quantum module easier, as otherwise there is a coupling between these two.
+
+### General Structure
+
+To further abstract the stores we use the following model:
+![Different KeyStores and pots](gfx/keystore_pots.drawio.png)
+
+Each KMS has a `KeyStore` for each connected KMS (`peerKMS`) which stores the `keys` and `key_ids` for this connection.
+Each `key_store` is then further divided into two `pots` and an `qm_buffer`.
+The two pots are called `forwarding_pot` and `receiving_pot`.
+Keys in the `forwarding_pot` are only used to encrypt the payload if the KMS want to forward a payload to this peer.
+Keys in the `receiving_pot` are only used to decrypt the payload if the KMS receives a payload from this peer.
+The `qm_buffer` stores all keys received from the `quantum module` which are not yet sorted into one of the pots.
+With this distinction we don't need to further reserve or otherwise inform the peer, that we want to use a key.
+It should make the usage more robust.
+Currently the API doesn't use a two phase commit, but this could be a useful improvement for a future version.
+
+### Leader Election
+
+After the KMS start or loose connection to each other, they start with an empty KeyStore.
+First, they have to elect a `leader`.
+The other KMS is then the `follower`.
+
+![Leader Election](gfx/leader_election.png)
+
+To do this, both KMS request being the leader by sending a request to the other (`RequestBeingLeader()`).
+The request simply contains their `kms_id`.
+If a KMS receives such a request it simply checks if the incoming `kms_id` is bigger than its own via a simple string comparison and answers the request with `true` or `false`.
+Answering to such a request **DOES NOT** decide of you are a leader or follower, only after KMS receives an answer of the other KMS it uses it's answer to establish its own role.
+If a leader is elected the leader should clear all keys from its `pots`, as well as the `qm_buffer` and then call `ClearAllKeys()`, so that the follower does the same.
+This is is necessary, as otherwise there would be edge cases where a resync would never be possible.
+
+**IMPORTANT:** The sequence diagram is just an example, there is no specific order in which the `RequestLeader` requests are happening.
+
+### Process of KeySyncing with QmTypePush
+
+After each KMS knows wether it is `leader` or `follower`, the normal syncing operation begins.
+The leader is responsible to call `DistributeKeys()` when necessary, e.g. when the pots run low on keys.
+The leader has to check both pots, as the follower can not put keys into its pots by itself.
+
+![KeyDistribution](gfx/key_distribution.png)
+
+When and how often this call is done is not specified, but we recommend using an amount of keys which should be in each `pot` at all times.
+If this value is undercut, the leader takes new keys from its `qm_buffer` and starts a distribution via `DistributeKeys()`.
+The follower tries to do the given insertion into its pots.
+If there is an error, e.g. it doesn't have a `key_id` in its `qm_buffer`, it can simply insert all other keys and answer the leader with all `key_ids` that it couldn't insert.
+The leader then removes these keys from its pots as well.
+
+![KeySync](gfx/key_sync.png)
+
+Periodically the leader can call `SyncKeyPots()` where all `key_ids` are synced between the two peers to catch some edge cases.
+This call is just added as a fallback, it should normally not be necessary.
+
+
+### Process of KeySyncing with ETSI014
+
+If the used quantum modules don't support some kind of push model but use ETSI014 instead, the mechanism changes a bit.
+The leader election and usage of the two `pots` is identical.
+The only difference is, that instead of using a `qm_buffer`, both KMS simply request the keys on demand when distributing the keys to their `pots`.
+This means that if the leader wants to add e.g. 100 keys to their pots, instead of taking 100 keys from their `qm_buffer`, it requests 100 keys via ETSI014.
+The follower then doesn't check whiter it has the keys in its `qm_buffer`, but requests the keys via their key_id from its quantum module via ETSI014.
+
+
+## Forwarding (with route creation)
+
+In this example we have three Key management Systems (KMS), connected in a straight line `KMS1 <-> KMS2 <-> KMS3` where `KMS1` and `KMS3` are connected to some sort of User, which could be a real user or some other entity like an Access Key Management System (AKMS).
+
+The forwarding process is as follows: 
+
+1. Start KMS requests a route from the controller to a target
+2. Controller calculates a route and informs all KMS on that route about it
+3. KMS announces a forwarding to the controller
+4. Key forwarding happens
+5. Last KMS informs the controller, that the forwarding is done
+6. Start and end KMS give the payload to their corresponding users
+
+
+According to the API specification it looks like this:
+
+![complete forwarding process](gfx/forwarding_complete.png)
+
+*Keep in mind that the exchange of the payload between the User and the KMS is currently not part of this specification.*
+
+Its important to note that the payload **never** gets transmitted to the controller
+
+Let's look at it one by one.
+
+### Route generation
+
+After a KMS gets the command to exchange a payload it has to request a route to the target, so that it and all KMS on the way know where to send the payload to and how it should be encrypted (e.g. AES_256_GCM, OTP,...).
+
+![route generation process](gfx/route_generation.png)
+
+It is important that the initial `RequestRoute()` request stays open until all KMS are informed about the route. 
+This ensures that the forwarding process in the starting KMS is blocked. 
+The answer from the controller to this request doesn't contain routing information, only an `route_id`, as the KMS gets its routing information in the same way as all other KMS on the route.
+
+*The API doesn't specify that the controller has to distribute the routing information in order.*
+
+### Forwarding
+
+This process requires, that a valid route is configured in the network, as described above.
+Most likely the KMS1 is doing the forwarding directly after the route is configured.
+
+![route generation process](gfx/forwarding_short.png)
+
+The KMS starts by calling `AnnouncePayloadRelay(request_id, route_id)` on the controller, which wraps the whole forwarding process and makes the controller aware of what happens.
+This enables the controller to keep track of the process and in case of an error, do something about it.
+Error handling is described in the next section.
+After the announcement, the payload is forwarded hop by hop.
+Only the last KMS on the route then has to inform the controller about the successful forwarding process via `PayloadRelayFinishedResponse()`.
+The controller then closes the initial  `AnnouncePayloadRelay(request_id, route_id)` request, so that both KMS know the relay is finished and can hand over the payload to their users/applications.
+
+### Error handling in the forwarding process
+
+In the case of a KMS encountering an error which prevents it from completing its forwarding process, it has to report this to the controller.
+
+![route generation error process](gfx/forwarding_error.png)
+
+In this example `KMS2` in not able to reach `KMS3` to forward the payload.
+It therefore informs the controller via `PayloadRelayError(request_id, route_id, error)`.
+Because the KMS provides the request_id and route_id, as well as the error itself the controller then can log this and potentially calculate a solution.
+It then response to the wrapping `AnnouncePayloadRelay` call from `KMS1` with an error.
+The controller also states wether it was an `INTERNAL`, `EXTERNAL` or `UNKNOWN` error and may give a recommendation on what the KMS should do now.
+This recommendation could be that the KMS should just retry, request a new route and retry, wait and retry or abort.
+The idea behind this is to make the system more robust.
+If for example a KMS is suddenly offline the controller may be able to just route around it, but therefore the KMS has to request a new route.
+
+*Keep in mind that it is possible for some errors to get reported to the controller more than once, e.g. if KMS3 fails in a way that it returns an error to KMS2 and reports the error itself to the controller. The controller is responsible to handle this*
diff --git a/api/_Docs/v1/gfx/forwarding_complete.plantuml b/api/_Docs/v1/gfx/forwarding_complete.plantuml
new file mode 100644
index 0000000000000000000000000000000000000000..340c1359737a2ece1fd3a7b6a757ddfbd46fc724
--- /dev/null
+++ b/api/_Docs/v1/gfx/forwarding_complete.plantuml
@@ -0,0 +1,35 @@
+@startuml
+title Forwarding process for three KMS
+
+participant "User 1" as User1
+participant "KMS 1" as KMS1
+participant "KMS 2" as KMS2
+participant "KMS 3" as KMS3
+participant "QKDN-Controller" as QKDNController
+participant "User 2" as User2
+
+User1 -> KMS1 : PayloadExchangeRequest()
+
+KMS1 -> QKDNController : RequestRoute(source_kms_id, target_kms_Id, crypto_algorithm)
+QKDNController -> QKDNController : calculateRoute()
+QKDNController -> KMS1 : AddRoute(id, next_hop_id, prev_hop_id, crypto_algorithm)
+QKDNController <-- KMS1 : AddRouteResponse()
+QKDNController -> KMS2 : AddRoute(id, next_hop_id, prev_hop_id, crypto_algorithm)
+QKDNController <-- KMS2 : AddRouteResponse()
+QKDNController -> KMS3 : AddRoute(id, next_hop_id, prev_hop_id, crypto_algorithm)
+QKDNController <-- KMS3 : AddRouteResponse()
+KMS1 <-- QKDNController : RequestRouteResponse(id)
+
+KMS1 -> QKDNController : AnnouncePayloadRelay(request_id, route_id)
+KMS1 -> KMS2 : ForwardPayload(request_id, route_id, key_ids, payloads)
+KMS1 <-- KMS2 : ForwardPayloadResponse()
+KMS2 -> KMS3 : ForwardPayload(request_id, route_id, key_ids, payloads)
+KMS2 <-- KMS3 : ForwardPayloadResponse()
+KMS3 -> QKDNController : PayloadRelayFinished(request_id, route_id)
+QKDNController -> KMS3 : PayloadRelayFinishedResponse()
+KMS1 <-- QKDNController : AnnouncePayloadRelayResponse()
+
+User1 <-- KMS1 : PayloadExchangeResponse()
+KMS3 -> User2 : PayloadExchange()
+
+@enduml
\ No newline at end of file
diff --git a/api/_Docs/v1/gfx/forwarding_complete.png b/api/_Docs/v1/gfx/forwarding_complete.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a907643d8f89162318408b8cb8c6fac7629d88f
Binary files /dev/null and b/api/_Docs/v1/gfx/forwarding_complete.png differ
diff --git a/api/_Docs/v1/gfx/forwarding_error.plantuml b/api/_Docs/v1/gfx/forwarding_error.plantuml
new file mode 100644
index 0000000000000000000000000000000000000000..7a01cf85e9e50324b13d81eab606f5bec8fb7a83
--- /dev/null
+++ b/api/_Docs/v1/gfx/forwarding_error.plantuml
@@ -0,0 +1,21 @@
+@startuml
+title Forwarding process for three KMS
+
+participant "KMS 1" as KMS1
+participant "KMS 2" as KMS2
+participant "KMS 3" as KMS3
+participant "QKDN-Controller" as QKDNController
+
+
+KMS1 -> QKDNController : AnnouncePayloadRelay(request_id, route_id)
+KMS1 -> KMS2 : ForwardPayload(request_id, route_id, key_ids, payloads)
+KMS1 <-- KMS2 : ForwardPayloadResponse()
+
+KMS2 -[#red]>x KMS3 : ForwardPayload(request_id, route_id, key_ids, payloads)
+
+KMS2 -> QKDNController : PayloadRelayError(request_id, route_id, error)
+KMS2 <-- QKDNController : PayloadRelayErrorResponse()
+
+KMS1 <-- QKDNController : AnnouncePayloadRelayResponse(error)
+
+@enduml
\ No newline at end of file
diff --git a/api/_Docs/v1/gfx/forwarding_error.png b/api/_Docs/v1/gfx/forwarding_error.png
new file mode 100644
index 0000000000000000000000000000000000000000..2fd14f126799b36332e3efa1f2ded0f6b5b7c9a8
Binary files /dev/null and b/api/_Docs/v1/gfx/forwarding_error.png differ
diff --git a/api/_Docs/v1/gfx/forwarding_short.plantuml b/api/_Docs/v1/gfx/forwarding_short.plantuml
new file mode 100644
index 0000000000000000000000000000000000000000..106944e2f120320ff355830c07b33ff0b335138b
--- /dev/null
+++ b/api/_Docs/v1/gfx/forwarding_short.plantuml
@@ -0,0 +1,18 @@
+@startuml
+title Short forwarding process for three KMS
+
+participant "KMS 1" as KMS1
+participant "KMS 2" as KMS2
+participant "KMS 3" as KMS3
+participant "QKDN-Controller" as QKDNController
+
+KMS1 -> QKDNController : AnnouncePayloadRelay(request_id, route_id)
+KMS1 -> KMS2 : ForwardPayload(request_id, route_id, key_ids, payloads)
+KMS1 <-- KMS2 : ForwardPayloadResponse()
+KMS2 -> KMS3 : ForwardPayload(request_id, route_id, key_ids, payloads)
+KMS2 <-- KMS3 : ForwardPayloadResponse()
+KMS3 -> QKDNController : PayloadRelayFinished(request_id, route_id)
+QKDNController -> KMS3 : PayloadRelayFinishedResponse()
+KMS1 <-- QKDNController : AnnouncePayloadRelayResponse()
+
+@enduml
\ No newline at end of file
diff --git a/api/_Docs/v1/gfx/forwarding_short.png b/api/_Docs/v1/gfx/forwarding_short.png
new file mode 100644
index 0000000000000000000000000000000000000000..ee8773852e9d743e3b7a0cbffee97b6199f471da
Binary files /dev/null and b/api/_Docs/v1/gfx/forwarding_short.png differ
diff --git a/api/_Docs/v1/gfx/key_distribution.plantuml b/api/_Docs/v1/gfx/key_distribution.plantuml
new file mode 100644
index 0000000000000000000000000000000000000000..27c2fbc13a0d3878ac2cbc2300ae4459e24388d8
--- /dev/null
+++ b/api/_Docs/v1/gfx/key_distribution.plantuml
@@ -0,0 +1,20 @@
+@startuml
+title Key Distribution
+
+participant "Leading KMS" as lKMS
+participant "Following KMS" as fKMS
+
+loop until true
+    lKMS -> lKMS : Check wether new keys should be distributed.
+end
+
+lKMS -> fKMS : DistributeKeys(kms_id, forwarding_pot_key_ids, receiving_pot_key_ids)
+activate fKMS
+fKMS -> fKMS : Check if all keys can be inserted into pot
+fKMS -> fKMS : Insert all keys into pots that are possible
+lKMS <-- fKMS : DistributeKeysResponse(forwarding_pot_denied_key_ids, receiving_pot_denied_key_ids)
+deactivate
+
+lKMS -> lKMS : Remove all denied keys from pots
+
+@enduml
diff --git a/api/_Docs/v1/gfx/key_distribution.png b/api/_Docs/v1/gfx/key_distribution.png
new file mode 100644
index 0000000000000000000000000000000000000000..d8571ba78d3bf83f48e02e7a9b8e2ec4eaf26533
Binary files /dev/null and b/api/_Docs/v1/gfx/key_distribution.png differ
diff --git a/api/_Docs/v1/gfx/key_sync.plantuml b/api/_Docs/v1/gfx/key_sync.plantuml
new file mode 100644
index 0000000000000000000000000000000000000000..e1b947978f0bec433ecf77d88f545adae6a7ea9d
--- /dev/null
+++ b/api/_Docs/v1/gfx/key_sync.plantuml
@@ -0,0 +1,19 @@
+@startuml
+title Periodic Key Sync
+
+participant "Leading KMS" as lKMS
+participant "Following KMS" as fKMS
+
+loop until true
+    lKMS -> lKMS : Check wether sync should happen
+end
+
+lKMS -> fKMS : SyncKeyPots(kms_id, forwarding_pot_key_ids, receiving_pot_key_ids)
+activate fKMS
+fKMS -> fKMS : Check if all keys are already in pots
+lKMS <-- fKMS : SyncKeyPotsResponse(forwarding_pot_missing_key_ids, receiving_pot_missing_key_ids)
+deactivate
+
+lKMS -> lKMS : Remove all keys missing in follower from pots
+
+@enduml
diff --git a/api/_Docs/v1/gfx/key_sync.png b/api/_Docs/v1/gfx/key_sync.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b2c8102b8622efefe5b4fae458f2c37ebb19090
Binary files /dev/null and b/api/_Docs/v1/gfx/key_sync.png differ
diff --git a/api/_Docs/v1/gfx/keystore_pots.drawio b/api/_Docs/v1/gfx/keystore_pots.drawio
new file mode 100644
index 0000000000000000000000000000000000000000..15fb8a2f678fa8bf981b2d5308cdad656f374fd2
--- /dev/null
+++ b/api/_Docs/v1/gfx/keystore_pots.drawio
@@ -0,0 +1,136 @@
+<mxfile host="Electron" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/26.2.2 Chrome/134.0.6998.178 Electron/35.1.2 Safari/537.36" version="26.2.2">
+  <diagram name="Page-1" id="fYzRfmUkyXz8pNIbEzCB">
+    <mxGraphModel dx="1128" dy="792" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
+      <root>
+        <mxCell id="0" />
+        <mxCell id="1" parent="0" />
+        <mxCell id="4NN6SHD3ibbEYs6tM123-1" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" vertex="1" parent="1">
+          <mxGeometry x="400" y="260" width="200" height="190" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-2" value="KMS Overview" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
+          <mxGeometry x="70" y="220" width="140" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-4" value="KMS" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
+          <mxGeometry x="470" y="270" width="60" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-5" value="KeyStore" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
+          <mxGeometry x="510" y="310" width="80" height="120" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-6" value="KeyStore" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
+          <mxGeometry x="410" y="310" width="80" height="120" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-7" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" vertex="1" parent="1">
+          <mxGeometry x="120" y="260" width="200" height="190" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-8" value="KMS" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
+          <mxGeometry x="190" y="270" width="60" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-9" value="KeyStore" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
+          <mxGeometry x="180" y="310" width="80" height="120" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-11" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" vertex="1" parent="1">
+          <mxGeometry x="680" y="260" width="200" height="190" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-12" value="KMS" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
+          <mxGeometry x="750" y="270" width="60" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-14" value="KeyStore" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
+          <mxGeometry x="740" y="310" width="80" height="120" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-15" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="4NN6SHD3ibbEYs6tM123-7" target="4NN6SHD3ibbEYs6tM123-1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="390" y="610" as="sourcePoint" />
+            <mxPoint x="440" y="560" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-16" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="4NN6SHD3ibbEYs6tM123-1" target="4NN6SHD3ibbEYs6tM123-11">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="390" y="610" as="sourcePoint" />
+            <mxPoint x="440" y="560" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-18" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
+          <mxGeometry x="120" y="620" width="200" height="295" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-19" value="KeyStore" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
+          <mxGeometry x="70" y="580" width="140" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-20" value="KeyStore" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
+          <mxGeometry x="190" y="630" width="60" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-21" value="Receiving&lt;div&gt;Pot&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;" vertex="1" parent="1">
+          <mxGeometry x="230" y="670" width="80" height="120" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-22" value="Forwarding Pot" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
+          <mxGeometry x="130" y="670" width="80" height="120" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-31" value="QM_Buffer*" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
+          <mxGeometry x="130" y="805" width="180" height="100" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-32" value="*only when using&amp;nbsp;&lt;div&gt;QmTypePush&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
+          <mxGeometry x="300" y="885" width="170" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-35" value="QM" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
+          <mxGeometry x="180" y="480" width="80" height="60" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-36" value="QM" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
+          <mxGeometry x="410" y="480" width="80" height="60" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-37" value="QM" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
+          <mxGeometry x="510" y="480" width="80" height="60" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-38" value="QM" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
+          <mxGeometry x="740" y="480" width="80" height="60" as="geometry" />
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-39" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="4NN6SHD3ibbEYs6tM123-35" target="4NN6SHD3ibbEYs6tM123-9">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="360" y="630" as="sourcePoint" />
+            <mxPoint x="410" y="580" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-40" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="4NN6SHD3ibbEYs6tM123-36" target="4NN6SHD3ibbEYs6tM123-6">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="360" y="630" as="sourcePoint" />
+            <mxPoint x="410" y="580" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-41" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="4NN6SHD3ibbEYs6tM123-37" target="4NN6SHD3ibbEYs6tM123-5">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="360" y="630" as="sourcePoint" />
+            <mxPoint x="410" y="580" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-42" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="4NN6SHD3ibbEYs6tM123-38" target="4NN6SHD3ibbEYs6tM123-14">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="360" y="630" as="sourcePoint" />
+            <mxPoint x="410" y="580" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-43" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;dashed=1;dashPattern=1 2;" edge="1" parent="1" source="4NN6SHD3ibbEYs6tM123-35" target="4NN6SHD3ibbEYs6tM123-36">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="360" y="630" as="sourcePoint" />
+            <mxPoint x="410" y="580" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-44" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;dashed=1;dashPattern=1 2;" edge="1" parent="1" source="4NN6SHD3ibbEYs6tM123-37" target="4NN6SHD3ibbEYs6tM123-38">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="270" y="520" as="sourcePoint" />
+            <mxPoint x="420" y="520" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-45" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="80" y="560" as="sourcePoint" />
+            <mxPoint x="930" y="560" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="4NN6SHD3ibbEYs6tM123-46" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="80" y="570" as="sourcePoint" />
+            <mxPoint x="930" y="570" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+      </root>
+    </mxGraphModel>
+  </diagram>
+</mxfile>
diff --git a/api/_Docs/v1/gfx/keystore_pots.drawio.png b/api/_Docs/v1/gfx/keystore_pots.drawio.png
new file mode 100644
index 0000000000000000000000000000000000000000..7090208d836c347761932af36810a16ebd234d4c
Binary files /dev/null and b/api/_Docs/v1/gfx/keystore_pots.drawio.png differ
diff --git a/api/_Docs/v1/gfx/leader_election.plantuml b/api/_Docs/v1/gfx/leader_election.plantuml
new file mode 100644
index 0000000000000000000000000000000000000000..536c870bba703b1288bc880365e7c0680fefa369
--- /dev/null
+++ b/api/_Docs/v1/gfx/leader_election.plantuml
@@ -0,0 +1,22 @@
+@startuml
+title Leader Election
+
+participant "KMS 1" as KMS1
+participant "KMS 2" as KMS2
+
+KMS1 -> KMS2 : RequestBeingLeader(kms_id=1)
+activate KMS2
+KMS2-> KMS2 : CheckLeaderRequest()
+KMS2 --> KMS1 : RequestBeingLeaderResponse(request_accepted=false)
+deactivate KMS2
+KMS1 -> KMS1 : StartBeingFollower()
+
+
+KMS2 -> KMS1 : RequestBeingLeader(kms_id=2)
+activate KMS1
+KMS1 -> KMS1 : CheckLeaderRequest()
+KMS1 --> KMS2 : RequestBeingLeaderResponse(request_accepted=true)
+deactivate KMS1
+KMS2 -> KMS2 : StartBeingLeader()
+
+@enduml
\ No newline at end of file
diff --git a/api/_Docs/v1/gfx/leader_election.png b/api/_Docs/v1/gfx/leader_election.png
new file mode 100644
index 0000000000000000000000000000000000000000..e460fbafacc41b908b92ef2f4895c3829108d09a
Binary files /dev/null and b/api/_Docs/v1/gfx/leader_election.png differ
diff --git a/api/_Docs/v1/gfx/route_generation.plantuml b/api/_Docs/v1/gfx/route_generation.plantuml
new file mode 100644
index 0000000000000000000000000000000000000000..681fb53cbfe32b440dab1ab98130c05bc70965ff
--- /dev/null
+++ b/api/_Docs/v1/gfx/route_generation.plantuml
@@ -0,0 +1,20 @@
+@startuml
+title Route creation process for three KMS
+
+participant "KMS 1" as KMS1
+participant "KMS 2" as KMS2
+participant "KMS 3" as KMS3
+participant "QKDN-Controller" as QKDNController
+
+
+KMS1 -> QKDNController : RequestRoute(source_kms_id, target_kms_Id, crypto_algorithm)
+QKDNController -> QKDNController : calculateRoute()
+QKDNController -> KMS1 : AddRoute(id, next_hop_id, prev_hop_id, crypto_algorithm)
+QKDNController <-- KMS1 : AddRouteResponse()
+QKDNController -> KMS2 : AddRoute(id, next_hop_id, prev_hop_id, crypto_algorithm)
+QKDNController <-- KMS2 : AddRouteResponse()
+QKDNController -> KMS3 : AddRoute(id, next_hop_id, prev_hop_id, crypto_algorithm)
+QKDNController <-- KMS3 : AddRouteResponse()
+KMS1 <-- QKDNController : RequestRouteResponse(id)
+
+@enduml
\ No newline at end of file
diff --git a/api/_Docs/v1/gfx/route_generation.png b/api/_Docs/v1/gfx/route_generation.png
new file mode 100644
index 0000000000000000000000000000000000000000..86bc300ff84c80f651d140bfab72e436d6d6bdd8
Binary files /dev/null and b/api/_Docs/v1/gfx/route_generation.png differ
diff --git a/api/ctrl/v1/routing.proto b/api/ctrl/v1/routing.proto
new file mode 100644
index 0000000000000000000000000000000000000000..10defc4ce198efc48eb0ee489cbb9ee6d174a803
--- /dev/null
+++ b/api/ctrl/v1/routing.proto
@@ -0,0 +1,145 @@
+syntax = "proto3";
+package ctrl.v1;
+
+import "shared/v1/util.proto";
+
+// CtrlRoutingService for creating and using routes as a KMS
+// It is offered by the controller and is used by all KMS
+service CtrlRoutingService {
+  // RequestRoute is used to create a route between a start and end KMS.
+  // It is called by the start KMS, as it needs the route information before it can actually forward a payload.
+  rpc RequestRoute(RequestRouteRequest) returns (RequestRouteResponse);
+  // AnnouncePayloadRelay is used by the KMS to announce, that a payload relay for a given route is about to start
+  // This in ONLY called by the start KMS and NOT every KMS on the route.
+  // It is meant as an info to the controller, that an actual payload is going on.
+  // The controller only closes this connection and sends a response, when it is informed by the target KMS that the relay is finished.
+  // If the forwarding gets interrupted, the controller can inform the start KMS about the error via the response as well and give a recommendation on how to continue.
+  rpc AnnouncePayloadRelay(AnnouncePayloadRelayRequest) returns (AnnouncePayloadRelayResponse);
+  // PayloadRelayFinished is used by the target KMS to inform the controller, that the payload relay is finished.
+  // The controller then can close the AnnouncePayloadRelay connection.
+  // Both KMS can then deliver the payload to its users.
+  rpc PayloadRelayFinished(PayloadRelayFinishedRequest) returns (PayloadRelayFinishedResponse);
+  // PayloadRelayError is used by the KMS to inform the controller, that an error occurred during the payload relay.
+  // This can be used by any KMS on the route, if it encounters an error it cant solve by itself
+  // The controller can then inform the start KMS about the error and the relay attempt is aborted.
+  rpc PayloadRelayError(PayloadRelayErrorRequest) returns (PayloadRelayErrorResponse);
+  // Used by the KMS to inform the controller about the fill level of the keystore.
+  // Currently it is desired to send this request every time the key store level changes for a peer of the KMS.
+  rpc PushKeyStoreFillLevel(PushKeyStoreFillLevelRequest) returns (PushKeyStoreFillLevelResponse);
+}
+
+// RequestRouteRequest is used to request a route between two KMS.
+message RequestRouteRequest {
+  // source_kms_id is the ID of the KMS that wants to send a payload.
+  string source_kms_id = 1;
+  // target_kms_id is the ID of the KMS that should receive a payload.
+  string target_kms_id = 2;
+  // crypto_algorithm is the algorithm that should be used for the route.
+  // This is relevant as a route which supports a desired algorithm can then be chosen by the controller, as maybe not all KMS support all algorithms.
+  shared.v1.CryptoAlgorithm crypto_algorithm = 3;
+  // maybe later add more optional fields (QM vendor, etc.)
+}
+
+// RequestRouteResponse is used to respond to a RequestRouteRequest.enum
+// If you receive this answer as a start KMS, you know that the route is configured on each KMS and you can start the forwarding process.
+message RequestRouteResponse {
+  // route_id is the ID of the route that was created.
+  // All KMS on the route should use this ID to identify the route.
+  string route_id = 1;
+}
+
+// AnnouncePayloadRelayRequest is used to announce a payload relay.
+message AnnouncePayloadRelayRequest {
+  // request_id is the ID of the request. It is used to distinguish parallel requests from the same source.
+  string request_id = 1;
+  string route_id = 2;
+}
+
+// AnnouncePayloadRelayResponse is used to respond to an AnnouncePayloadRelayRequest.
+// It contains an optional error field, which is used to inform the start KMS about an error that occurred during the payload relay.
+message AnnouncePayloadRelayResponse {
+  // Contains the error information, if an error occurred.
+  // This also includes a recommendation on how to proceed.
+  optional AnnouncePayloadRelayError error = 1;
+}
+
+// PayloadRelayFinishedRequest is used to inform the controller, that the payload relay is finished.
+message PayloadRelayFinishedRequest {
+  string request_id = 1;
+  string route_id = 2;
+}
+
+message PayloadRelayFinishedResponse {}
+
+// PayloadRelayErrorRequest is used to inform the controller, that an error occurred during the payload relay.
+// This can be used by any KMS on the route, if it encounters an error it cant solve by itself.
+message PayloadRelayErrorRequest {
+  string request_id = 1;
+  string route_id = 2;
+  // error is the error that occurred during the payload relay.
+  PayloadRelayErrorType error = 3;
+}
+
+message PayloadRelayErrorResponse {}
+
+// PayloadRelayErrorType are the possible error types that can occur during the payload relay.
+enum PayloadRelayErrorType {
+  KEY_RELAY_ERROR_TYPE_UNSPECIFIED = 0;
+  KEY_RELAY_ERROR_TYPE_PEER_NOT_REACHABLE = 1;
+  KEY_RELAY_ERROR_TYPE_NO_KEY_AVAILABLE = 2;
+  KEY_RELAY_ERROR_TYPE_ROUTE_NOT_FOUND = 3;
+  KEY_RELAY_ERROR_TYPE_CRYPTO_FAILED = 4;
+}
+
+// AnnouncePayloadRelayError handling
+message AnnouncePayloadRelayError {
+  AnnouncePayloadRelayErrorType error_type = 1;
+  Recommendation retry_recommendation = 2;
+}
+
+// AnnouncePayloadRelayErrorType are the possible error types that can occur during the payload relay.
+// It is indented to be not very helpful, as to not provide security critical information to the start KMS.
+// It is only important to know if it is an error byt he user or start KMS, by the rest of the system or an unknown error.
+enum AnnouncePayloadRelayErrorType {
+  // Used if the error is external, meaning not from the system itself. Maybe something was malformed, the payload was send with the wrong crypto, etc.
+  ANNOUNCE_KEY_RELAY_ERROR_TYPE_EXTERNAL = 0;
+  // Used if the error is internal, meaning it is not the fault of the user or start KMS.
+  // This could be a faulty KMS, outage or similar.
+  ANNOUNCE_KEY_RELAY_ERROR_TYPE_INTERNAL = 1;
+  // Used if the error is unknown.
+  ANNOUNCE_KEY_RELAY_ERROR_TYPE_UNKNOWN = 2;
+  // More options possible, add later when found
+}
+
+// Recommendation is used to inform the start KMS about a recommendation on how to proceed with the payload relay.
+enum Recommendation {
+  // Used if no recommendation is given.
+  RECOMMENDATION_UNSPECIFIED = 0;
+  // Used if the start KMS should retry the payload forwarding.
+  RECOMMENDATION_RETRY = 1;
+  // Used if the start KMS should retry the payload forwarding with a new route.
+  // This means that the KMS should request a new route from the controller.
+  // This could happen if e.g. a KMS runs out of keys. If the controller thinks an alternative route is possible, it will provide a valid route if requested.
+  RECOMMENDATION_NEW_ROUTE_AND_RETRY = 2;
+  // Used if the start KMS should abort the payload forwarding, as the controller sees now way of continuing.
+  // This could be the case if e.g. the target KMS is not reachable or the route is not valid anymore.
+  RECOMMENDATION_ABORT = 3;
+  // Used if the start KMS should wait and retry the payload forwarding.
+  // This could be the case if e.g. a KMS has run out of keys, but is expected to have keys again in a short time.
+  // The wait time is not specified, so a timeframe in the seconds should be expected.
+  RECOMMENDATION_WAIT_AND_RETRY = 4;
+}
+
+// The message contains the information about the fill level of ONE keystore of the KMS.
+message PushKeyStoreFillLevelRequest {
+  // The id of the KMS itself
+  string kms_id = 1;
+  // The ID of the peer KMS for which this information is valid.
+  // If a KMS has two peers, it has two key stores which have a fill level.
+  // To separate the two, the peer ID is used.
+  string peer_id = 2;
+  // The fill level of the keystore in number of keys. This is not a size in bytes, but the number of keys that are available.
+  int64 fill_level = 3;
+}
+
+message PushKeyStoreFillLevelResponse {}
diff --git a/api/kms/v1/config.proto b/api/kms/v1/config.proto
new file mode 100644
index 0000000000000000000000000000000000000000..aa6ef97870b0d07fd8033f95c1e702d95cbca5be
--- /dev/null
+++ b/api/kms/v1/config.proto
@@ -0,0 +1,166 @@
+syntax = "proto3";
+package kms.v1;
+
+// Service from a KMS for the controller to configure the KMS
+service ConfigService {
+  // Gets and sets general config for the KMS, which is independent to peers and quantum modules.
+  rpc GetKmsConfig(GetKmsConfigRequest) returns (GetKmsConfigResponse);
+  rpc SetKmsConfig(SetKmsConfigRequest) returns (SetKmsConfigResponse);
+
+  // A Peer is a neighboring KMS that is directly connected to this KMS.
+  // If configured it can be used on a forwarding route.
+  // Each Peer requires an connected quantum module to work.
+  rpc CreatePeer(CreatePeerRequest) returns (CreatePeerResponse);
+  rpc GetPeer(GetPeerRequest) returns (GetPeerResponse);
+  rpc GetPeerList(GetPeerListRequest) returns (GetPeerListResponse);
+  rpc UpdatePeer(UpdatePeerRequest) returns (UpdatePeerResponse);
+  rpc DeletePeer(DeletePeerRequest) returns (DeletePeerResponse);
+
+  // Adds information about a connected quantum module.option
+  // One quantum module is needed for each peer to work properly.
+  rpc CreateQuantumModule(CreateQuantumModuleRequest) returns (CreateQuantumModuleResponse);
+  rpc GetQuantumModule(GetQuantumModuleRequest) returns (GetQuantumModuleResponse);
+  rpc GetQuantumModuleList(GetQuantumModuleListRequest) returns (GetQuantumModuleListResponse);
+  rpc UpdateQuantumModule(UpdateQuantumModuleRequest) returns (UpdateQuantumModuleResponse);
+  rpc DeleteQuantumModule(DeleteQuantumModuleRequest) returns (DeleteQuantumModuleResponse);
+}
+
+// The general config for each KMS.
+message KmsConfig {
+  // Its own ID
+  string id = 1;
+  // Its own name
+  string name = 2;
+  // This is the address that the KMS will listen on for incoming connections for the controller.bool
+  // If changed through the controller, the controller needs to update its information as well.bool
+  // Otherwise the controller cant reach the KMS anymore.
+  string controller_addr_bind = 4;
+  // The address of the controller to connect to for routing and forwarding.
+  string controller_addr = 5;
+  // Represents the bind for the services the KMS offers for other KMS/
+  // Used for forwarding and key sync operations
+  string intercom_bind = 6;
+  // QuantumPushAddrBind is only needed if a quantum module of type push is used.
+  string quantum_push_addr_bind = 7;
+}
+
+message SetKmsConfigRequest {
+  KmsConfig config = 1;
+}
+message SetKmsConfigResponse {}
+
+message GetKmsConfigRequest {}
+message GetKmsConfigResponse {
+  KmsConfig config = 1;
+}
+
+// Represents a peer KMS.
+message Peer {
+  // The id is used for all identifications by other systems
+  string id = 1;
+  // Just for human interaction.
+  string name = 2;
+  // The ID of the quantum module that is used for this peer.
+  string quantum_module_id = 3;
+  // Address of the peer KMS intercom service.
+  string peer_addr = 5;
+}
+
+message CreatePeerRequest {
+  Peer peer = 1;
+}
+message CreatePeerResponse {}
+
+message GetPeerRequest {
+  string id = 1;
+}
+message GetPeerResponse {
+  Peer peer = 1;
+}
+
+message GetPeerListRequest {}
+message GetPeerListResponse {
+  repeated Peer peers = 1;
+}
+
+message UpdatePeerRequest {
+  Peer peer = 1;
+}
+message UpdatePeerResponse {}
+
+message DeletePeerRequest {
+  string id = 1;
+}
+message DeletePeerResponse {}
+
+message QuantumModule {
+  string id = 1;
+  string name = 2;
+  string vendor_name = 3;
+  string model_name = 4;
+  // MaxKeyFillLevel is the maximum number of keys to be stored in the storage
+  // related to one KMS peer (should be the same for the peer on the other side,
+  // uses default if not provided).
+  string max_key_fill_level = 5;
+  oneof qm_type {
+    QmTypeEtsi014 qm_type_etsi_014 = 6;
+    QmTypePush push = 7;
+  }
+}
+
+message QmTypePush {
+  // ID the quantum module uses to talk to us
+  string id = 1;
+}
+
+message QmTypeEtsi014 {
+  // Addr can be a hostname:port or an IP:port combination.
+  string addr = 1;
+  // LocalSaeId is the related ID of the local SAE (secure application entity).
+  string local_sae_id = 5;
+  // TargetId is the related ID of the target SAE.
+  string target_sae_id = 6;
+  // FetchMode sets the leader or follower mode.
+  FetchMode fetch_mode = 7;
+  // KeyFetchInterval in seconds for how often keys should be requested from a
+  // QuantumModule (optional, uses default if not provided).
+  string key_fetch_interval = 8;
+  // KeyFetchAmount is the amount of keys to be requested from a QuantumModule
+  // (optional, uses default if not provided).
+  string key_fetch_amount = 9;
+  // KeyFetchSize is the size of the keys to be requested from a QuantumModule
+  string key_fetch_size = 10;
+}
+
+enum FetchMode {
+  FETCH_MODE_UNSPECIFIED = 0;
+  FETCH_MODE_LEADER = 1;
+  FETCH_MODE_FOLLOWER = 2;
+}
+
+message CreateQuantumModuleRequest {
+  QuantumModule quantum_module = 1;
+}
+message CreateQuantumModuleResponse {}
+
+message GetQuantumModuleRequest {
+  string id = 1;
+}
+message GetQuantumModuleResponse {
+  QuantumModule quantum_module = 1;
+}
+
+message GetQuantumModuleListRequest {}
+message GetQuantumModuleListResponse {
+  repeated QuantumModule quantum_modules = 1;
+}
+
+message UpdateQuantumModuleRequest {
+  QuantumModule quantum_module = 1;
+}
+message UpdateQuantumModuleResponse {}
+
+message DeleteQuantumModuleRequest {
+  string id = 1;
+}
+message DeleteQuantumModuleResponse {}
diff --git a/api/kms/v1/forwarding.proto b/api/kms/v1/forwarding.proto
new file mode 100644
index 0000000000000000000000000000000000000000..1a5da5ca852209ec1e14f1af44fdf29275625dc1
--- /dev/null
+++ b/api/kms/v1/forwarding.proto
@@ -0,0 +1,30 @@
+syntax = "proto3";
+package kms.v1;
+
+// The ForwardingService is offered by each KMS to forward payloads through the network.
+service ForwardingService {
+  // ForwardPayload is the actual call to forward a payload.
+  // Is closed after the payload is received.option
+  // All further error handling has to be done by the next KMS afterwards.
+  rpc ForwardPayload(ForwardPayloadRequest) returns (ForwardPayloadResponse);
+}
+
+message ForwardPayloadRequest {
+  // request_id is the ID of the request. It is used to distinguish parallel requests from the same source.
+  string request_id = 1;
+  // route_id is the ID of the route that is used for this forwarding.
+  // It should stay the same for each KMS on the route.
+  string route_id = 2;
+
+  // key_ids is the list of key IDs that are used for this payload.
+  // The IDs are local to the send and receiving KMS of a forwarding request, as they are received of the local quantum module.
+  // It is a list, as depending on the crypto algorithm, one key might not be enough to encrypt the whole payload.
+  // E.g. if keys are stored as 256 bit keys and otp is used, you would need two 256 bit keys to encrypt/decrypt a 512 bit payload.
+  // If more than one payload is send, it is not intended to use this field to encrypt each payload with a different key!
+  repeated string key_ids = 3;
+  // The actual encrypted payloads that are send.
+  // Must be decrypted and then encrypted with key material for the next KMS.
+  repeated bytes payloads = 6;
+}
+
+message ForwardPayloadResponse {}
diff --git a/api/kms/v1/keysync.proto b/api/kms/v1/keysync.proto
new file mode 100644
index 0000000000000000000000000000000000000000..4eb1a87f354cc44880ddf197232e54f782d32691
--- /dev/null
+++ b/api/kms/v1/keysync.proto
@@ -0,0 +1,86 @@
+syntax = "proto3";
+package kms.v1;
+
+// This service is responsible of syncing key ids between two peer KMS.
+// IMPORTANT: Please note that is is for syncing IDs, not the actual keys.
+// The actual keys are send to each KMS via its quantum module.
+// Keys from a QM should NEVER be synced or otherwise distributed when using the forwarding or syncing!
+// Furthermore it is expected that the KeyStores/Pots are locked while doing the syncing.
+service KeySyncService {
+  // Leader election by KmsID. Each KMS sends the request to the other KMS and requests the leader role.
+  // If the ID is higher, it is accepted, otherwise denied.
+  // Only after a KMS sends this request and gets the answer, it can assume its role and start with the sync process.
+  rpc RequestBeingLeader(RequestBeingLeaderRequest) returns (RequestBeingLeaderResponse);
+
+  // Distribute Keys into Pots. Currently uses a simplified algorithm to distribute keys.
+  // Should probably change to a two phase commit protocol later.
+  rpc DistributeKeys(DistributeKeysRequest) returns (DistributeKeysResponse);
+
+  // For periodically syncing of all keys in the two pots. Currently uses a simplified algorithm to distribute keys.
+  // Should probably change to a two phase commit protocol later.
+  rpc SyncKeyPots(SyncKeyPotsRequest) returns (SyncKeyPotsResponse);
+
+  // After recovering from a fatal error, the KMS can call this method to clear all keys from the pots and the qm_buffer.
+  // The latter is necessary, as otherwise there would be edge cases where a resync would never be possible.
+  rpc ClearAllKeys(ClearAllKeysRequest) returns (ClearAllKeysResponse);
+}
+
+message RequestBeingLeaderRequest {
+  // The kms id ot the KMS that sends the request.
+  string kms_id = 1;
+}
+message RequestBeingLeaderResponse {
+  // Wether the leader request is accepted or not.
+  // To make this check, both sides have to use the same algorithm to compare the IDs
+  // As we use the ID as a string, we can use the default string comparison.
+  bool request_accepted = 3;
+}
+
+message DistributeKeysRequest {
+  // The kms id ot the KMS that sends the request.
+  string kms_id = 1;
+  // The IDs for the key the receiving KMS should put into its forwarding pot for this peer connection
+  // The sending KMS is doing the opposite and puts them into its receiving pot.
+  repeated string forwarding_pot_key_ids = 2;
+  // The IDs for the key the receiving KMS should put into its receiving pot for this peer connection
+  // The sending KMS is doing the opposite and puts them into its forwarding pot.
+  repeated string receiving_pot_key_ids = 3;
+}
+
+message DistributeKeysResponse {
+  // The key IDs that the follower can't add to its forwarding pot.
+  // Most likely because the key is not in it's buffer.
+  // All key IDs returned here should be removed from the receiving pot of the leader.
+  repeated string forwarding_pot_denied_key_ids = 1;
+  // The key IDs that the follower can't add to its receiving pot.
+  // Most likely because the key is not in it's buffer.
+  // All key IDs returned here should be removed from the forwarding pot of the leader.
+  repeated string receiving_pot_denied_key_ids = 2;
+}
+
+message SyncKeyPotsRequest {
+  // The kms id ot the KMS that sends the request.
+  string kms_id = 1;
+  // The IDs for the key the receiving KMS should already have in its forwarding pot for this peer connection.
+  // The sending KMS has this key IDs in its receiving pot.
+  repeated string forwarding_pot_key_ids = 2;
+  // The IDs for the key the receiving KMS should already have in its receiving pot for this peer connection.
+  // The sending KMS has this key IDs in its forwarding pot.
+  repeated string receiving_pot_key_ids = 3;
+}
+
+message SyncKeyPotsResponse {
+  // The key IDs that the follower doesn't currently have in its forwarding pot.
+  // All key IDs returned here should be removed from the receiving pot of the leader.
+  repeated string forwarding_pot_missing_key_ids = 1;
+  // The key IDs that the follower doesn't currently have in its receiving pot.
+  // All key IDs returned here should be removed from the forwarding pot of the leader.
+  repeated string receiving_pot_missing_key_ids = 2;
+}
+
+message ClearAllKeysRequest {
+  // The kms id ot the KMS that sends the request.
+  string kms_id = 1;
+}
+
+message ClearAllKeysResponse {}
diff --git a/api/kms/v1/routing.proto b/api/kms/v1/routing.proto
new file mode 100644
index 0000000000000000000000000000000000000000..9de83a1c8ea6fa538ad5f834f2dfbfb465a8e7f9
--- /dev/null
+++ b/api/kms/v1/routing.proto
@@ -0,0 +1,56 @@
+syntax = "proto3";
+package kms.v1;
+
+import "shared/v1/util.proto";
+
+// KmsRoutingService contains all calls that are used by the controller
+// to interact with a KMS so configure routes.
+service KmsRoutingService {
+  rpc AddRoute(AddRouteRequest) returns (AddRouteResponse);
+  rpc GetRoute(GetRouteRequest) returns (GetRouteResponse);
+  rpc GetRouteList(GetRouteListRequest) returns (GetRouteListResponse);
+  rpc UpdateRoute(UpdateRouteRequest) returns (UpdateRouteResponse);
+  rpc DeleteRoute(DeleteRouteRequest) returns (DeleteRouteResponse);
+}
+
+message AddRouteRequest {
+  Route route = 1;
+}
+message AddRouteResponse {
+  Route route = 1;
+}
+
+message GetRouteRequest {
+  string id = 1;
+}
+message GetRouteResponse {
+  Route route = 1;
+}
+
+message GetRouteListRequest {}
+message GetRouteListResponse {
+  repeated Route routes = 1;
+}
+
+message UpdateRouteRequest {
+  Route route = 1;
+}
+message UpdateRouteResponse {}
+
+message DeleteRouteRequest {
+  string id = 1;
+}
+message DeleteRouteResponse {}
+
+// The description of route information for a KMS.
+message Route {
+  // In other aspects used as route_id
+  string id = 1;
+  // The ID of the KMS that a payload should be forwarded to if this route is used.
+  string next_hop_id = 2;
+  // The ID of the KMS that a payload should be coming from if this route is used.
+  string prev_hop_id = 3;
+  // The crytpo algorithm that is used for this route.
+  // Is relevant for encrypting and decrypting a payload.
+  shared.v1.CryptoAlgorithm crypto_algorithm = 4;
+}
diff --git a/api/shared/v1/util.proto b/api/shared/v1/util.proto
new file mode 100644
index 0000000000000000000000000000000000000000..80b23fd57022cce06cd6ed433ba3203407986da1
--- /dev/null
+++ b/api/shared/v1/util.proto
@@ -0,0 +1,11 @@
+syntax = "proto3";
+package shared.v1;
+
+// Describes the crypto algorithms that are possible.enum
+// More options can be added, this is a current selection useful for us.
+enum CryptoAlgorithm {
+  CRYPTO_ALGORITHM_UNSPECIFIED = 0;
+  CRYPTO_ALGORITHM_AES_256_GCM = 1;
+  CRYPTO_ALGORITHM_OTP = 2;
+  CRYPTO_ALGORITHM_HYBRID = 3;
+}
diff --git a/buf.gen.yaml b/buf.gen.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..bdb567ffb60bba06e1e8fd9d8fcb5275628de354
--- /dev/null
+++ b/buf.gen.yaml
@@ -0,0 +1,15 @@
+version: v2
+
+managed:
+    enabled: true
+    override:
+        - file_option: go_package_prefix
+          value: code.fbi.h-da.de/danet/costaquanta/gen/go
+
+plugins:
+    - remote: buf.build/protocolbuffers/go
+      out: gen/go
+      opt: paths=source_relative
+    - remote: buf.build/grpc/go
+      out: gen/go
+      opt: paths=source_relative
diff --git a/buf.yaml b/buf.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d6dbbdd432675cb1668d99a1ba750395d4a77d63
--- /dev/null
+++ b/buf.yaml
@@ -0,0 +1,10 @@
+# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml
+version: v2
+modules:
+    - path: api/
+lint:
+    use:
+        - STANDARD
+breaking:
+    use:
+        - FILE
diff --git a/ctrl/Dockerfile b/ctrl/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..707eab33e26224ad1d10ec2572d33d41ce03afac
--- /dev/null
+++ b/ctrl/Dockerfile
@@ -0,0 +1,21 @@
+ARG GOLANG_VERSION=1.24
+
+FROM golang:$GOLANG_VERSION-alpine AS builder
+
+WORKDIR /app/
+
+COPY go.* .
+
+RUN go mod download
+
+COPY . .
+
+RUN --mount=type=cache,target=/root/go/pkg/mod \
+    --mount=type=cache,target=/root/.cache/go-build \
+    CGO_ENABLED=0 GOOS=linux go build -tags musl -buildmode=pie -o ctrl.bin ctrl/cmd/main.go
+
+FROM alpine:3.21
+
+COPY --from=builder ./app/ctrl.bin app/ctrl.bin
+
+ENTRYPOINT ["/app/ctrl.bin"]
diff --git a/ctrl/cmd/main.go b/ctrl/cmd/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..f06940d7cfad9bc3a560e16bbfeceada4c248261
--- /dev/null
+++ b/ctrl/cmd/main.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+
+	config "code.fbi.h-da.de/danet/costaquanta/ctrl/internal"
+)
+
+func main() {
+	var bindAddr string
+
+	flag.StringVar(
+		&bindAddr,
+		"bindaddr",
+		"127.0.0.1:1337",
+		"default adress and port to bind the controller to",
+	)
+
+	flag.Parse()
+
+	cfg := config.GetConfig()
+
+	fmt.Printf("%+v\n", cfg)
+	fmt.Printf("Binding to address %s", bindAddr)
+}
diff --git a/ctrl/internal/config.go b/ctrl/internal/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..e11ef5ef79fee00d890dc85298f4fb9b4c5ce2c9
--- /dev/null
+++ b/ctrl/internal/config.go
@@ -0,0 +1,19 @@
+package config
+
+import "github.com/caarlos0/env/v11"
+
+type Config struct {
+	AddrBind string
+
+	UUID string
+	Name string
+}
+
+func GetConfig() Config {
+	cfg, err := env.ParseAs[Config]()
+	if err != nil {
+		panic(err)
+	}
+
+	return cfg
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..bc042e58eed559848946e325c820f0fcd9f84703
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
+module code.fbi.h-da.de/danet/costaquanta
+
+go 1.24
+
+require github.com/caarlos0/env/v11 v11.3.1
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..1724948ebb6c354479037ecb2ccb4ccd25f4c3f0
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,2 @@
+github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
+github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
diff --git a/kms/Dockerfile b/kms/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..b4c8503e8d2233e79ffdd0c93c58c80254628117
--- /dev/null
+++ b/kms/Dockerfile
@@ -0,0 +1,21 @@
+ARG GOLANG_VERSION=1.24
+
+FROM golang:$GOLANG_VERSION-alpine AS builder
+
+WORKDIR /app/
+
+COPY go.* .
+
+RUN go mod download
+
+COPY . .
+
+RUN --mount=type=cache,target=/root/go/pkg/mod \
+    --mount=type=cache,target=/root/.cache/go-build \
+    CGO_ENABLED=0 GOOS=linux go build -tags musl -buildmode=pie -o kms.bin kms/cmd/main.go
+
+FROM alpine:3.21
+
+COPY --from=builder ./app/kms.bin app/kms.bin
+
+ENTRYPOINT ["/app/kms.bin"]
diff --git a/kms/cmd/main.go b/kms/cmd/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..b6cd3a5e6ae5141030c07b00484bb723e287c8eb
--- /dev/null
+++ b/kms/cmd/main.go
@@ -0,0 +1,33 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+
+	config "code.fbi.h-da.de/danet/costaquanta/kms/internal"
+)
+
+func main() {
+	var bindAddr string
+	var ctrlAddr string
+
+	flag.StringVar(
+		&bindAddr,
+		"bindaddr",
+		"127.0.0.1:1337",
+		"default adress and port to bind the controller interface to",
+	)
+	flag.StringVar(
+		&ctrlAddr,
+		"ctrladdr",
+		"127.0.0.1:1337",
+		"address and port of the controller to manage this kms",
+	)
+
+	flag.Parse()
+
+	cfg := config.GetConfig()
+
+	fmt.Printf("%+v\n", cfg)
+	fmt.Printf("Binding to address %s", bindAddr)
+}
diff --git a/kms/internal/config.go b/kms/internal/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..6f69adb5b6dc35c04e5691252c21f3906c2156f0
--- /dev/null
+++ b/kms/internal/config.go
@@ -0,0 +1,20 @@
+package config
+
+import "github.com/caarlos0/env/v11"
+
+type Config struct {
+	AddrBind string
+	CtrlAddr string
+
+	UUID string
+	Name string
+}
+
+func GetConfig() Config {
+	cfg, err := env.ParseAs[Config]()
+	if err != nil {
+		panic(err)
+	}
+
+	return cfg
+}