{"id":26436,"date":"2024-08-26T23:35:39","date_gmt":"2024-08-26T21:35:39","guid":{"rendered":"https:\/\/blog.mi.hdm-stuttgart.de\/?p=26436"},"modified":"2024-08-28T13:25:43","modified_gmt":"2024-08-28T11:25:43","slug":"welcome-to-the-future-of-government-tech-meet-guppyai","status":"publish","type":"post","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/08\/26\/welcome-to-the-future-of-government-tech-meet-guppyai\/","title":{"rendered":"Welcome to the Future of Government Tech &#8211; Meet GuppyAI!"},"content":{"rendered":"\n<p>If you\u2019ve ever attempted to accomplish anything in a German government office, you might feel like you&#8217;ve stepped into a time capsule. Fax machines? They&#8217;re still very much in use. Digital workflows? Not so much. But what if we told you there&#8217;s a new game-changer ready to revolutionize even the most tech-resistant corners of bureaucracy? Meet GuppyAI &#8211; your friendly, SMS-powered AI chatbot designed to help government workers upgrade their skills without the hassle.<\/p>\n\n\n\n<p>Let&#8217;s be honest &#8211; technology and government don\u2019t always mesh well. That&#8217;s where GuppyAI steps in to rewrite the story by putting cutting-edge AI within easy reach, using something as simple and familiar as SMS. Yes, you read that right &#8211; there\u2019s no need for fancy apps or confusing interfaces. If you can send a text, you can use GuppyAI.<\/p>\n\n\n\n<p>Behind the scenes, GuppyAI runs on state-of-the-art technology. We&#8217;ve integrated an on-premise SMS-Gateway with the Azure Cloud Platform via Service Bus, interacting with the ChatGPT 3.5 model through a sleek Node.js function app. Your conversations? They\u2019re securely stored in CosmosDB\u2019s NoSQL database for future reference. But don\u2019t worry, we\u2019ll delve into the technical details, the challenges we faced and all the other fun stuff later &#8211; just know that some serious innovation is at play.<\/p>\n\n\n\n<p>If you want to go on a deep dive into our code, feel free take a look at our <a href=\"https:\/\/github.com\/GuppyAI\" title=\"GitHub organization\">GitHub organization<\/a>!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Architecture<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/architecture-3.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"555\" data-attachment-id=\"26437\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/08\/26\/welcome-to-the-future-of-government-tech-meet-guppyai\/architecture-8\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/architecture-3.png\" data-orig-size=\"3188,1728\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"architecture\" data-image-description=\"&lt;p&gt;Overview of GuppyAI&amp;#8217;s architecture and information flow&lt;\/p&gt;\n\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/architecture-3-1024x555.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/architecture-3-1024x555.png\" alt=\"Chart showing the used elements in GuppyAI's architecture as well as the flow of information throughout the application. First, SMS messages arrive in the SMS-Gateway where they are parsed and published to the ingress queue in the Azure Service Bus. From there, they are picked up by an Azure Function which will process them, save their contents to a CosmosDB and relay them to the OpenAI API. Responses are then published to the egress queue by the same function where they are in turn picked up by the SMS-Gateway and sent back to the original inquirer.\" class=\"wp-image-26437\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/architecture-3-1024x555.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/architecture-3-300x163.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/architecture-3-768x416.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/architecture-3-1536x833.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/architecture-3-2048x1110.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>At the heart of GuppyAI lies a flexible and adaptable architectural style known as hexagonal architecture, also referred to as the ports and adapters pattern. This approach is designed to keep the core business logic of an application isolated from the external systems it interacts with, making it highly modular and easy to extend. The idea is simple: the core (or the &#8220;hexagon&#8221;) represents the central business logic, while the &#8220;ports&#8221; and &#8220;adapters&#8221; are the external systems or interfaces that interact with this core.<\/p>\n\n\n\n<p>In the context of GuppyAI, the Azure Function represents the core business logic. It handles the flow of data and decision-making, while all the connected systems &#8211; like the SMS gateway, Azure Service Bus, CosmosDB, and the AI API &#8211; act as adapters. These adapters can be swapped out or extended without significantly altering the core logic. This flexibility is a significant advantage for a project like GuppyAI, where the ability to easily add new message channels, change the persistence layer, or even replace the AI API is crucial for future development.<\/p>\n\n\n\n<p>The hexagonal architecture naturally supports these needs, making it easier to evolve the system as requirements change or new technologies emerge. Whether it&#8217;s integrating an additional messaging platform like instant messengers or email, or transitioning to a different database or AI service, the architecture allows for seamless adaptation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Information Flow<\/h3>\n\n\n\n<p>Guppy-AI&#8217;s information flow is made to be both simple and reliable. This is how it works:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>SMS Reception:<\/strong> An SMS message is sent by the user and received by the on-premise SMS gateway<\/li>\n\n\n\n<li><strong>Message Queuing:<\/strong> The gateway forwards the message to the Azure Service Bus, where it is queued for processing.<\/li>\n\n\n\n<li><strong>Function Trigger:<\/strong> The incoming message in the Service Bus triggers the Azure Function App. The function begins by persisting the user\u2019s message in CosmosDB and retrieving the entire conversation history associated with that user.<\/li>\n\n\n\n<li><strong>AI Processing:<\/strong> The complete conversation is then sent to the OpenAI API via the official Node.js library. The AI processes the input and generates a response.<\/li>\n\n\n\n<li><strong>Response Handling:<\/strong> The AI&#8217;s response is stored in CosmosDB alongside the conversation history. The function then sends this response back through the Azure Service Bus to the SMS gateway.<\/li>\n\n\n\n<li><strong>SMS Delivery:<\/strong> The SMS gateway receives the response from the Service Bus and sends it back to the user via SMS.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Scalability and Performance<\/h3>\n\n\n\n<p>GuppyAI&#8217;s architecture leverages the scalability of cloud-native services to handle a variety of workloads while remaining cost effective. Services such as the Azure Function App, CosmosDB, Azure Cognitive Services, and Azure Service Bus are designed to scale automatically based on demand. This ensures that the system can handle more traffic without requiring manual inputs.<\/p>\n\n\n\n<p>The SMS-Gateway, which is constrained by the throughput of its GSM modem, is the bottleneck in this configuration. Despite this limitation, using zero-scaling services reduces operating expenses, making it a perfect solution for a proof-of-concept project.<\/p>\n\n\n\n<p>One notable performance consideration is the cold start latency of the Azure Function App. When the function is inactive for a period, it can take 20-30 seconds to start up, introducing a delay in processing the first message. Once the function is active, the main performance constraint is the throughput of the GSM modem. Latency between cloud components is minimal, with most being located in the same geographical region except for the ChatGPT API, which is located in France.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Future Expansion and Upcoming Challenges<\/h3>\n\n\n\n<p>Looking ahead, GuppyAI&#8217;s architecture is well-positioned for expansion. The use of a pub-sub pattern in the Azure Service Bus makes it easy to add new communication channels, such as instant messengers or email, with minimal changes to the existing codebase. The architecture\u2019s flexibility also allows for other potential improvements, such as the introduction of a logging system and the creation of a development environment to facilitate testing and debugging.<\/p>\n\n\n\n<p>In terms of security, while it wasn&#8217;t a primary focus during the proof-of-concept phase, future iterations could benefit from placing components that don&#8217;t need internet access in a virtual private cloud (VPC) for enhanced security.<\/p>\n\n\n\n<p>Overall, the GuppyAI architecture is robust, flexible, and ready to evolve as the project grows. While there are some areas for improvement, the current setup provides a solid foundation for bringing AI-powered chat services to even the most tech-resistant corners of the German government.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">SMS-Gateway<\/h2>\n\n\n\n<p>The SMS-Gateway is the critical entry and exit point for all communications within GuppyAI, acting as the bridge between the traditional SMS protocol and our cloud-based AI system. Developed to run on a Raspberry Pi 3B with a GSM modem, this custom gateway manages SMS transmission and reception while connecting with the Azure cloud. We&#8217;ll get into the technical specifics of the SMS-Gateway&#8217;s development, the difficulties we encountered while implementing it, and its function within the larger GuppyAI architecture in this chapter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Elephant in the Room: Why not use Cloud Services for SMS?<\/h3>\n\n\n\n<p>When looking at our architecture, one might rightfully ask why we have used cloud services for any task in our use case but the reception and sending of SMS messages. This is were we will explain our partly dissatisfactory explanation on why we had to decide to not base this part of our application on cloud services.<\/p>\n\n\n\n<p>Upon our initial research on how to send and receive SMS, we stumbled over numerous cloud services offering exactly the functionality we needed. Among them were services from the three big cloud providers AWS, Google or Microsoft Azure and many different specialized SaaS (Software-as-a-Service) products. However, with all of them there were a few caveats we as a group of three students could not overcome. Some only operated in the US, some required us to use a so called short code, which are the short phone numbers you often see when you&#8217;re getting 2FA messages from PayPal and the like &#8211; way too expensive for us! Others would require a waiting time of up to six months just to get the phone number assigned.<\/p>\n\n\n\n<p>It was not too long until we figured, that what we wanted to achieve is not possible in the cloud with the resources we have at hands. So, there was only one possibility remaining: Running the service on premise and using a modem to send the SMS messages.<\/p>\n\n\n\n<p>We knew this would not be easy as there were many unanswered questions:<\/p>\n\n\n\n<p>What are modems we can consider for this? How do we communicate with the modem? Where do we get a mobile plan that offers what we need?<\/p>\n\n\n\n<p>After some research, we found out about the AT standard. The AT standard is a command set we could use to communicate with the modem. As almost any generic USB Stick used for mobile internet reception contains a modem that supports this standard, we just bought the cheapest LTE USB Stick we could find, the Brovi\/Huawei E3372-325.<\/p>\n\n\n\n<p>While we waited for our LTE USB Stick to arrive we researched about mobile plans for IoT appliances. In this regard we had the same devastating outcome as with our research about SMS cloud services. IoT mobile plans are expensive, sometimes reserved for businesses and most of the time they only offer data connectivity.<\/p>\n\n\n\n<p>We soon realized our best option would be to opt for a standard consumer mobile plan. The issue with this approach is that many mobile providers prohibit Machine-to-machine communication and similar use cases. Therefore, we chose a plan that would not prohibit our exact use case.<\/p>\n\n\n\n<p>However, <strong>we cannot recommend anyone to implement our approach for a non-research work without first consulting the respective mobile phone company or a lawyer.<\/strong> It is only our personal, inexpert assumption that our use case is not covered by the actions prohibited by the terms of service of the mobile provider we chose.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What&#8217;s this Funny Dialect<\/h3>\n\n\n\n<p>Legal warnings aside, the next step on our journey was to learn about the AT commands we would need to use to send and receive SMS messages.<\/p>\n\n\n\n<p>Some very useful resources for this topic were the following websites:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"http:\/\/www.nobbi.com\/atgsm.html\" target=\"_blank\" rel=\"noopener nofollow\" title=\"http:\/\/www.nobbi.com\/atgsm.html\">AT-Commands for GSM devices (German)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/infocenter.nordicsemi.com\/topic\/struct_nrf91\/struct\/nrf91_at_commands.html\" title=\"\">Nordic Semiconductors &#8211; nRF91 Series &#8211; AT Commands<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.rs-online.com\/5931\/0900766b80bec52c.pdf\" title=\"\">Teletonika AT Commands manual<\/a><\/li>\n<\/ul>\n\n\n\n<p>With these resources we could get learn the basics of how AT commands work:<\/p>\n\n\n\n<p>Every AT command starts with the letters &#8216;AT&#8217;. This is followed by the actual command.<\/p>\n\n\n\n<p>The simplest of all commands probably is&nbsp;<code class=\"\" data-line=\"\">ATZ<\/code>&nbsp;which just resets the modem. Actually, this command also is one of the rather odd ones. With most commands the &#8216;AT&#8217; sequence is followed by a &#8216;+&#8217; symbol. We could not definitively find out why this is but our theory is that the &#8216;+&#8217; symbol indicates a command that one can retrieve information from.<\/p>\n\n\n\n<p>One of those commands is&nbsp;<code class=\"\" data-line=\"\">AT+CMGF<\/code>. It is used to set the format for incoming messages. In most modems there are two formats implemented. <\/p>\n\n\n\n<p>The first one is PDU mode. You might know the term PDU (Protocol data unit) from other protocols like Ethernet. In the Ethernet protocol the protocol data unit would be a frame, in IP it would be a packet, in UDP a datagram. If you want to learn more about how a SMS PDU is structured and how to parse it, take a look at this developer&#8217;s guide from Siemens:&nbsp;<a href=\"http:\/\/www.alternative-technology.de\/sms_pdumode.pdf\">Developer&#8217;s Guide: SMS with the SMS PDU-Mode<\/a><\/p>\n\n\n\n<p>The second mode which comes in especially handy when manually interfacing with the modem is text mode where the PDU&#8217;s contents are displayed in a human readable way.<\/p>\n\n\n\n<p>With commands like&nbsp;<code class=\"\" data-line=\"\">AT+CMGF<\/code>&nbsp;there are multiple ways to interact with. If you just run it as is you will receive an error. Instead you have to specify how you want to use this command.<\/p>\n\n\n\n<p>If you want to know what mode the modem is currently operating in you will have to append a question mark (<code class=\"\" data-line=\"\">AT+CMGF?<\/code>), if you want to know what you could set it to, you would append an equality sign and a question mark (<code class=\"\" data-line=\"\">AT+CMGF=?<\/code>) and if you desire to change the mode the modem is operating in you would do this with an equality sign as well (e.g.&nbsp;<code class=\"\" data-line=\"\">AT+CMGF=1<\/code>&nbsp;for setting text mode).<\/p>\n\n\n\n<p>This short introduction was only the tip of the iceberg about sending and receiving SMS with a GSM modem, so we highly recommend reading the sources linked above.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Interfacing with the Modem<\/h4>\n\n\n\n<p>The first thing we did when both the modem and our SIM card arrived was to remove the SIM cards SIM-PIN. This would not only prevent us from forgetting the SIM-PIN, it would also remove the need to authenticate with the SIM card using AT commands when the SIM card is used in production.<\/p>\n\n\n\n<p>This out of the way, we were eager to test out all the commands we learned in the preceding days only to come to a halt: &#8220;How do we communicate with the modem?&#8221;. We already knew, that we have to connect to it through a so-called tty-device which is short for &#8220;teletype device&#8221;. With Linux, these tty-devices are accessible through the&nbsp;<code class=\"\" data-line=\"\">\/dev<\/code>&nbsp;directory.<\/p>\n\n\n\n<p>However, when plugging in the modem, no new devices were listed. The USB stick did not even provide us with a mobile data connection as it should &#8211; and does with other operating systems like Windows.<\/p>\n\n\n\n<p>Further research taught us that a USB devices can operate in several modes. Our modem operated as a block device (like a normal USB stick to store data) by default &#8211; probably to provide access to the drivers for Windows. However, this mode was not of any use to us.<\/p>\n\n\n\n<p>Gladly, we found this incredible blog post by Pavel Piatruk who dealt with the exact same USB Stick:&nbsp;<a href=\"https:\/\/blog.tanatos.org\/posts\/huawei_e3372h-325_brovi_with_linux\/\">Huawei E3372-325 &#8216;BROVI&#8217; and Linux (Ubuntu)<\/a>.<\/p>\n\n\n\n<p>He first developed a script that would switch the USB stick to HiLink mode allowing us to connect to the internet. This is probably the mode most users want the USB stick to operate in, however we are not the typical user in this case.<\/p>\n\n\n\n<p>So, we were even happier about Pavel Piatruk&#8217;s work as we found out that he did a follow up where he successfully switched the USB stick to the STICK mode:&nbsp;<a href=\"https:\/\/blog.tanatos.org\/posts\/huawei_e3372h-325_brovi_with_linux_stickmode\/\">Huawei E3372-325 &#8216;BROVI&#8217; and Linux (Ubuntu) &#8211; Stick mode<\/a>.<\/p>\n\n\n\n<p>With his guide we created a udev rule to switch the usb stick to the right mode. udev is a program that will handle hotplug events. It runs rules located in&nbsp;<code class=\"\" data-line=\"\">\/etc\/udev\/rules.d<\/code>&nbsp;whenever a device is plugged in.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\"># udev rules by Pavel Piatruk (https:\/\/blog.tanatos.org\/posts\/huawei_e3372h-325_brovi_with_linux_stickmode\/)\nACTION!=&quot;add&quot;, GOTO=&quot;modeswitch_rules_end&quot;\nSUBSYSTEM!=&quot;usb&quot;, GOTO=&quot;modeswitch_rules_end&quot;\n\nATTRS{bInterfaceNumber}!=&quot;00&quot;, GOTO=&quot;modeswitch_rules_end&quot;\nGOTO=&quot;modeswitch_rules_begin&quot;\n\nLABEL=&quot;modeswitch_rules_begin&quot;\nATTR{idVendor}==&quot;3566&quot;, ATTR{idProduct}==&quot;2001&quot;, RUN+=&quot;\/usr\/local\/bin\/brovi_modeswitch.sh  %k %p&quot;\nLABEL=&quot;modeswitch_rules_end&quot;\n\nSUBSYSTEM==&quot;net&quot;, ACTION==&quot;add&quot;,  ATTRS{idVendor}==&quot;3566&quot;, ATTRS{idProduct}==&quot;2001&quot;, NAME=&quot;wwan_brovi&quot;\nSUBSYSTEM==&quot;tty&quot;, ACTION==&quot;add&quot;, DEVPATH==&quot;*:1.[24]\/*&quot;, ATTRS{idVendor}==&quot;3566&quot;, ATTRS{idProduct}==&quot;2001&quot;, ENV{ID_MM_PORT_IGNORE}=&quot;1&quot;<\/code><\/pre>\n\n\n\n<p>The above udev rules will run the script located at&nbsp;<code class=\"\" data-line=\"\">\/usr\/local\/bin\/brovi_modeswitch.sh<\/code>&nbsp;every time a USB device with vendor id 3566 and product id 2001 (i.e. any USB device of the same make and model as we have it on hand) is added. It will also register the tty device ports with the tty subsystem.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-bash\" data-line=\"\">#!\/bin\/bash\n# This script is called by udev when a USB device is plugged in.\n# It will switch the device to the correct mode for the device.\n\n# Adapted from Pavel Piatruk (https:\/\/blog.tanatos.org\/posts\/huawei_e3372h-325_brovi_with_linux_stickmode\/)\n\nPATH=\/usr\/local\/sbin:\/usr\/local\/bin:\/usr\/sbin:\/usr\/bin:\/sbin:\/bin\n\nusb_modeswitch -b $BUSNUM -g $DEVNUM -v $ID_VENDOR_ID -p $ID_MODEL_ID  -X\necho $ID_VENDOR_ID $ID_MODEL_ID ff &gt; \/sys\/bus\/usb-serial\/drivers\/option1\/new_id\n\nexit 0<\/code><\/pre>\n\n\n\n<p>In the&nbsp;<code class=\"\" data-line=\"\">\/usr\/local\/bin\/brovi_modeswitch.sh<\/code>&nbsp;script the usb_modeswitch application is used to switch the USB device to STICK mode. Afterwards the &#8220;option&#8221; kernel module is notified about a new modem device being added.<\/p>\n\n\n\n<p>The &#8220;option&#8221; kernel module is the kernel driver for modems of all sorts. To make the kernel load this module at every boot we need to create the text file&nbsp;<code class=\"\" data-line=\"\">\/etc\/modules-load.d\/option.conf<\/code>&nbsp;containing just&nbsp;<code class=\"\" data-line=\"\">option<\/code>.<\/p>\n\n\n\n<p>After a reboot, we&#8217;re all set and when plugging in the USB stick, it is launched in STICK mode.<\/p>\n\n\n\n<p>For interfacing with the modem manually and trying out some AT Commands we used the&nbsp;<code class=\"\" data-line=\"\">socat<\/code>&nbsp;application. <code class=\"\" data-line=\"\">socat<\/code> is a command line utility with which one can establich bidirectional byte streams. Therefore it can be used to communicate through network sockets or &#8211; as it is required for our use case &#8211; to communicate with tty devices.<\/p>\n\n\n\n<p>The USB stick provides three tty devices when in stick mode:&nbsp;<code class=\"\" data-line=\"\">\/dev\/ttyUSB1<\/code>,&nbsp;<code class=\"\" data-line=\"\">\/dev\/ttyUSB2<\/code>&nbsp;and&nbsp;<code class=\"\" data-line=\"\">\/dev\/ttyUSB4<\/code>. However, only&nbsp;<code class=\"\" data-line=\"\">\/dev\/ttyUSB1<\/code>&nbsp;can be used to communicate with the modem itself.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-bash\" data-line=\"\">socat - \/dev\/ttyUSB1,crnl<\/code><\/pre>\n\n\n\n<p>With the above command we can open a connection to the modem. The&nbsp;<code class=\"\" data-line=\"\">,crnl<\/code>&nbsp;suffix tells socat that it should send the Carriage Return (<code class=\"\" data-line=\"\">\\r<\/code>) and New Line (<code class=\"\" data-line=\"\">\\n<\/code>) characters upon pressing enter.<\/p>\n\n\n\n<p><em>NOTE: For communicating with tty-devices your user needs to be in the group&nbsp;<code class=\"\" data-line=\"\">dialout<\/code><\/em><\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">^RSSI: 46\n\n^HCSQ: &quot;LTE&quot;,56,48,156,20\n\n^RRCSTAT: 1\n\n^RRCSTAT: 0\nAT+CSQ\n\n+CSQ: 24,99\n\nOK\n\n^HCSQ: &quot;LTE&quot;,56,48,151,20\nAT+CMGF?\n\n+CMGF: 0\n\nOK\nAT+CMGF=?\n^RSSI: 43\n\n^HCSQ: &quot;LTE&quot;,53,48,166,22\n\n+CMGF: (0,1)\n\nOK<\/code><\/pre>\n\n\n\n<p>In the above code block you can see an exemplary communication with the modem, where we asked for the current signal quality (AT+CSQ), the current message receiving format (AT+CMGF?) and what it could be set to AT+CMGF=?.<\/p>\n\n\n\n<p>As you can see, the modem regularly sends status information like ^HCSQ: &#8220;LTE&#8221;,56,48,156,20 which shows parameters of the current connection.<\/p>\n\n\n\n<p>However, at this point we were unable to send or receive SMS messages.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">AT+CMGL=4\n\nOK<\/code><\/pre>\n\n\n\n<p>Normally, the&nbsp;<code class=\"\" data-line=\"\">AT+CMGL=4<\/code>&nbsp;command should return the received messages. However, even though we sent messages to the phone number associated with the used SIM card, no messages seemed to appear.<\/p>\n\n\n\n<p>Also, after a few tries, the modem just stalled and did not answer to our commands until we replugged it. We figured this and the mysterious status messages may be due to a process communicating with the modem without our knowledge.<\/p>\n\n\n\n<p>So, we used&nbsp;<code class=\"\" data-line=\"\">lsof<\/code>, a command line utility to monitor file access, to look for the culprit.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">COMMAND    PID USER   FD   TYPE DEVICE SIZE\/OFF NODE NAME\nModemMana 3743 root    9u   CHR  188,1      0t0  644 \/dev\/ttyUSB1<\/code><\/pre>\n\n\n\n<p>And every few seconds, they appeared: ModemManager was our culprit! ModemManager is an application that allows modems to be used by other applications. For example, for dialup connections &#8211; like with 56k modems in the 90s!<\/p>\n\n\n\n<p>That&#8217;s not what we wanted. So, we just decided to turn off ModemManager using&nbsp;<code class=\"\" data-line=\"\">systemctl<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-bash\" data-line=\"\">systemctl stop ModemManager\nsystemctl disable ModemManager<\/code><\/pre>\n\n\n\n<p>Now, we could successfully receive SMS messages:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">AT+CMGL=4\n\n+CMT: &quot;SM&quot;,0\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX<\/code><\/pre>\n\n\n\n<p>Hurray! After polling for SMS messages using the AT+CMGL=4 command, we received an CMTI-answer indicating a newly received message. In the next line, we could see the received PDU (censored for privacy reasons).<\/p>\n\n\n\n<p>This confirmed that our approach could work and we just needed to interpret the gibberish the modem sends to us.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Implementing an SMS Gateway<\/h4>\n\n\n\n<p>Given the enormous effort required to receive messages, serialize and deserialize the PDUs, it was clear to us that we needed something to lift the weight of our shoulders. Something like a library that would do the heavy lifting. This is where Kent Gibson&#8217;s (aka. warthog618) library&nbsp;<a href=\"https:\/\/github.com\/warthog618\/modem\">modem<\/a>&nbsp;for Go comes into play.<\/p>\n\n\n\n<p>With his library we were able to concentrate on our own business logic and let the library do all the interfacing work &#8211; at least that&#8217;s what we thought.<\/p>\n\n\n\n<p>The first hurdle we had to take was receiving SMS messages:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-go\" data-line=\"\">handler := func(msg gsm.Message) {\n    \/\/ handle message here\n}\nerr := modem.StartMessageRx(handler)<\/code><\/pre>\n\n\n\n<p>In theory, we would just use the library&#8217;s StartMessageRx function and provide our own handler for GSM messages. However, the StartMessageRx function uses the&nbsp;<code class=\"\" data-line=\"\">AT+CNMI<\/code>&nbsp;command to instruct the modem to print out received messages to the tty-device. The problem is that no matter what we tried, the messages were only printed out in a very unreliable manner.<\/p>\n\n\n\n<p>Only receiving every third or fourth message was not feasible and thus we instead opted for polling the modem for new messages in a fixed interval.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-go\" data-line=\"\">err := modem.AddIndication(&quot;+CMT:&quot;, func(info []string) {\n    log.Debug().Msg(&quot;Receiving message!&quot;)\n\n    ...\n}, at.WithTrailingLine)\n\nfor {\n    select {\n    case &lt;-time.After(pollingTimeout):\n        _, err := modem.Command(&quot;+CMGL=4&quot;)\n        if err != nil {\n            ...\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>Using the AddIndication function we can create a hook that is called whenever the symbols&nbsp;<code class=\"\" data-line=\"\">+CMT<\/code>&nbsp;are read.&nbsp;<code class=\"\" data-line=\"\">+CMT<\/code>&nbsp;signals that a SMS message is received and is followed by the PDU-encoded message which will be handed over to the handler function due to the option at.WithTrailingLine being set.<\/p>\n\n\n\n<p>To actually receive any messages we do poll the modem for them with the command&nbsp;<code class=\"\" data-line=\"\">AT+CMGL=4<\/code>&nbsp;in a for loop.<\/p>\n\n\n\n<p>The other part of interfacing with the modem is sending SMS messages. In a similar way we thought this would be as simple as just using the functions provided by the library.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-go\" data-line=\"\">if len(message) &lt;= 160 {\n\tlogger.Debug().Msg(&quot;Sending short message&quot;)\n\t_, err = modem.SendShortMessage(phoneNumber, message)\n} else {\n\tlogger.Debug().Msg(&quot;Sending long message&quot;)\n\t_, err = modem.SendLongMessage(phoneNumber, message)\n}<\/code><\/pre>\n\n\n\n<p>Sadly, this wasn&#8217;t the case and this is due to the way SMS deals with long messages. If a message is too long to be sent in a single PDU, it is split up into multiple PDUs which are linked together through an identifier. However, the maximum length of a single PDU depends on the used character set.<\/p>\n\n\n\n<p>For example, if a message does not contain any special characters like emojis, the GSM-7-bit default encoding is used which allows for up to 160 characters. If there are characters which cannot be represented in this encoding, the 16-bit UCS-2 encoding is used instead. With this encoding, only 70 code points (not characters!) can be sent.<\/p>\n\n\n\n<p>So, how do we determine whether to use the SendLongMessage or the SendShortMessage method provided by the library? This algorithm would be rather complicated to implement. Therefore, we decided to also use the lower level APIs the library provides in this case:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-go\" data-line=\"\">numberOption := sms.To(phoneNumber)\n\npdus, err := sms.Encode([]byte(message), numberOption)\nif err != nil {\n    ...\n}\n\nvar binaryPdus [][]byte\n\nfor _, pdu := range pdus {\n    binaryPdu, err := pdu.MarshalBinary()\n    if err != nil {\n        ...\n    }\n\n    binaryPdus = append(binaryPdus, binaryPdu)\n}\n\n...\n\nfor _, binaryPdu := range binaryPdus {\n    if _, err := modem.SendPDU(binaryPdu); err != nil {\n        ...\n    }\n}<\/code><\/pre>\n\n\n\n<p>Using these lower level APIs we can deal with short or long messages exactly the same The sms.Encode function will just return a slice of PDUs which we will have to send. No matter, if we only need one PDU or twelve.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"cloud-infrastructure\">Cloud Infrastructure<\/h2>\n\n\n\n<p>The cloud infrastructure of GuppyAI is like the engine of a high-performance car &#8211; powerful, efficient, and finely tuned to deliver smooth performance. Just like every component of an engine is essential for driving the vehicle forward, our Azure services and Terraform &#8211; work seamlessly together to power GuppyAI&#8217;s intelligent messaging capabilities. In this chapter, we&#8217;ll &#8220;pop the hood&#8221; and examine how each of these elements contribute to our system.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"azure-service-bus\">Azure Service Bus<\/h3>\n\n\n\n<p>At the core of GuppyAI&#8217;s architecture is the Azure Service Bus, a reliable message broker that ensures smooth communication between the different components of our system. We chose Azure Service Bus primarily for its support of the pub-sub (publish-subscribe) messaging model, which allows us to efficiently manage load balancing. This capability ensures that messages are distributed evenly across available resources, preventing any single component from being overwhelmed.<\/p>\n\n\n\n<p>Another significant advantage of Azure Service Bus is the availability of client SDKs, which meant we didn\u2019t need to build and maintain a custom API for communication between the SMS-Gateway and the Azure cloud. This choice not only simplified our development process but also ensured that our system could easily scale and adapt as needed.<\/p>\n\n\n\n<p>The Azure Service Bus also integrates seamlessly with Azure Functions, automatically triggering the functions when new messages arrive. This tight integration made development smoother and allowed us to focus on building the core logic of GuppyAI without worrying about the complexities of message handling.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"azure-function\">Azure Function<\/h3>\n\n\n\n<p>Our Azure Function plays a pivotal role in our cloud architecture, acting as the brain of GuppyAI. Serverless computing allowed us to focus mainly on writing code without the overhead of managing and maintaining servers. For our project, this meant we could develop faster and with fewer resources, concentrating on what matters most: delivering a functional and reliable service.<\/p>\n\n\n\n<p>One of the key reasons we chose Azure Functions was, that it allows for an usage-based payment model. This was a great option to keep operational costs down, especially in the early phases of development, because costs are only created when the function is run. Furthermore, there is no need for complicated load balancers or other external scaling methods because Azure Functions can scale automatically based on demand.<\/p>\n\n\n\n<p>The ability of Azure Service Bus to trigger Azure Functions automatically was incredibly useful for us. This feature allowed for a streamlined workflow where messages from the SMS-Gateway would instantly activate the function, process the request, and generate a response. This seamless flow between services significantly simplified our architecture and reduced the potential for errors.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"terraform\">One-Click Provisioning using Terraform<\/h3>\n\n\n\n<p>We used Terraform to manage our cloud infrastructure. This tool allows us to define and provision our setup using code. We chose Terraform because we wanted to learn about and implement infrastructure as code (IaC).<\/p>\n\n\n\n<p>We used Azure Blob Storage as a remote backend for Terraform&#8217;s shared state, ensuring that our infrastructure state is consistent and accessible across our CI\/CD pipelines. This setup is crucial for collaboration, as it allows multiple team members to work on infrastructure changes without conflicts or inconsistencies.<\/p>\n\n\n\n<p>Terraform authenticates against Azure using a service principal and a client secret, ensuring secure and reliable access to our cloud resources. In terms of configuration, we organized our Terraform files by service, with variables extracted into a main Terraform file. This modular approach made our setup cleaner and easier to manage.<\/p>\n\n\n\n<p>Connecting outputs and inputs between Terraform resources was particularly important for handling connection strings and other sensitive information. By automating these connections, we reduced the risk of manual errors and ensured that our infrastructure is both secure and consistent.<\/p>\n\n\n\n<p>However, working with Terraform for the first time wasn&#8217;t without challenges. The vast number of configuration options can be overwhelming, and our team had to invest significant time in learning how to use Terraform effectively. Despite these initial hurdles, the benefits of IaC, particularly in terms of repeatability and the ability to avoid bugs, made Terraform a valuable tool in our project.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"integration-and-future-considerations\">Integration and Future Considerations<\/h3>\n\n\n\n<p>The use of cloud-native services has allowed us to build a cloud infrastructure that is both powerful and flexible. Each component plays a crucial role in ensuring that GuppyAI runs smoothly.<\/p>\n\n\n\n<p>Looking ahead, we\u2019re thrilled about the possibilities for scaling and improving our cloud setup like adding logging and monitoring strategies. Thanks to our flexible architecture, adding new features and services will be easy, making sure that GuppyAI is able to continue to evolve and improve.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"cicd\">CI\/CD<\/h2>\n\n\n\n<p>Our CI\/CD pipelines are the backbone of our development and deployment process. They handle everything from building the container for our Gateway to setting up the cloud infrastructure. Whenever there&#8217;s an update to the infrastructure or code, our pipelines automatically deploy the latest version of our function code. It&#8217;s like having a robot that constantly builds and improves our product!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"gateway-cicd-pipeline\">Building, Testing and Containerizing our SMS-Gateway<\/h3>\n\n\n\n<p>As the gateway is run on an on-premise non-cloud infrastructure, it is inevitable that some manual labor is involved in deploying it. However, to save us tedious work when rolling out new versions, we have tried to automate as much as possible.<\/p>\n\n\n\n<p>For this we used GitHub Actions for our CI pipeline &#8211; as with every other part in the project.<\/p>\n\n\n\n<p>The CI pipeline had to solve two problems for us:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Automatic testing of the application<\/li>\n\n\n\n<li>Building a container image for deploying the application<\/li>\n<\/ul>\n\n\n\n<p>While the first problem was very easy to solve thanks to the many references about how to setup a simple Go CI pipeline with GitHub Actions, the second problem was a little more intricate.<\/p>\n\n\n\n<p>Reason being that we insisted on not using Docker for any step in the development process as we do not support their current business model in regards to open source software. As a replacement we opted for using Podman for running the gateway and buildah for building it in our CI environment.<\/p>\n\n\n\n<p>While there is a&nbsp;<a href=\"https:\/\/github.com\/redhat-actions\/buildah-build\">GitHub Action for Buildah<\/a>&nbsp;that is maintained by RedHat, its documentation is sparse, especially when it comes to multi-platform container builds, which we needed for testing out the image on our AMD64 machines and deploying it on our ARM64 target machine. The issue of sparse documentation is only amplified by the rather low adoption of buildah in GitHub Actions.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-yaml\" data-line=\"\">- name: Buildah Action (arm64v8)\n  id: build_image_arm\n  uses: redhat-actions\/buildah-build@v2\n  with:\n      image: my-new-image\n      archs: arm64v8\n      build-args: ARCH=arm64v8\n      tags: ${{ env.REGISTRY }}\/${{ env.REPO_LC }}:${{ env.RELEASE_DATE }}${{ env.REF_LC }}-arm64v8\n      containerfiles: |\n        .\/Containerfile\n\n- name: Buildah Action (amd64)\n  id: build_image_amd64\n  uses: redhat-actions\/buildah-build@v2\n\n...\n\n- name: Push To GHCR\n  uses: redhat-actions\/push-to-registry@v2\n  id: push\n  with:\n      image: ${{ env.REGISTRY }}\/${{ env.REPO_LC }}\n      tags: ${{ steps.build_image_arm.outputs.tags }} ${{ steps.build_image_amd64.outputs.tags }} \n      registry: ${{ env.REGISTRY }}\n      username: ${{ github.actor }}\n      password: ${{ secrets.GITHUB_TOKEN }}\n      extra-args: |\n        --disable-content-trust\n\n- name: Create and push manifest\n  id: create_manifest\n  run: |\n      buildah manifest create ${{ env.REPO_LC }}:${{ env.RELEASE_DATE }}${{ env.REF_LC }} ${{ steps.build_image_arm.outputs.image-with-tag }} ${{ steps.build_image_amd64.outputs.image-with-tag }}\n      buildah login -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }} ghcr.io\/guppyai\/sms-gateway\n      buildah manifest push ${{ env.REPO_LC }}:${{ env.RELEASE_DATE }}${{ env.REF_LC }} docker:\/\/${{ env.REGISTRY }}\/${{ env.REPO_LC }}:${{ env.RELEASE_DATE }}${{ env.REF_LC }}\n      buildah manifest push ${{ env.REPO_LC }}:${{ env.RELEASE_DATE }}${{ env.REF_LC }} docker:\/\/${{ env.REGISTRY }}\/${{ env.REPO_LC }}:latest<\/code><\/pre>\n\n\n\n<p>Through trial and error we hacked together a CI pipeline that would build one image for AMD64 and ARM64 each, push them and then create a manifest which references these images as its architectural variations. While this adventure allowed us to learn about how multi-platform images are dealt with in OCI (Open Container Initiative) and Docker image registries, we hoped for a cleaner and more straight forward solution like it is already possible when using Docker.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-yaml\" data-line=\"\">  - name: Buildah Action\n    id: build_image\n    uses: redhat-actions\/buildah-build@v2\n    with:\n        image: my-new-image\n        archs: arm64,amd64\n        tags: ${{ env.REGISTRY }}\/${{ env.REPO_LC }}:${{ env.RELEASE_DATE }}${{ env.REF_LC }} ${{ env.REGISTRY }}\/${{ env.REPO_LC }}:latest\n        containerfiles: |\n        .\/Containerfile\n\n  - name: Push To GHCR\n    uses: redhat-actions\/push-to-registry@v2\n    id: push\n    with:\n        image: ${{ env.REGISTRY }}\/${{ env.REPO_LC }}\n        tags: ${{ steps.build_image.outputs.tags }}\n        registry: ${{ env.REGISTRY }}\n        username: ${{ github.actor }}\n        password: ${{ secrets.GITHUB_TOKEN }}<\/code><\/pre>\n\n\n\n<p>Later on through meticulously studying the buildah action&#8217;s documentation we found out that we were actually overcomplicating things and this straight forward solution exists. One can just supply the commma-separated target architectures to the buildah-build actions and et voila: two images and a combining manifest are automatically built and specified.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Our Applications go Cloud &#8211; Automagically!<\/h3>\n\n\n\n<p>For deploying code to the cloud, we&#8217;ve created two separate CI\/CD pipelines: one for building our infrastructure and another for our function code. This keeps things organized and helps us avoid mistakes. By managing each part independently, we can make changes without worrying about breaking the whole system.<\/p>\n\n\n\n<p>On every push to the repository, our pipelines validate and build the necessary components. When changes are merged into the main branch, the respective pipelines trigger deployments, ensuring that our production environment is always up-to-date with the latest stable code.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"infrastructure-validation-and-deployment\">Infrastructure Validation and Deployment<\/h4>\n\n\n\n<p>The infrastructure pipeline plays a critical role in maintaining the cloud environment for GuppyAI. To ensure that our infrastructure is free from configuration errors, we validate our Terraform configurations on every push using the <code class=\"\" data-line=\"\">terraform validate<\/code> command. This step catches any potential issues before they can impact the production environment.<\/p>\n\n\n\n<p>When changes are merged into the main branch, the infrastructure is automatically deployed to Azure. This process is handled by a GitHub Actions workflow that utilizes the <code class=\"\" data-line=\"\">hashicorp\/setup-terraform<\/code> and <code class=\"\" data-line=\"\">azure\/login<\/code> actions. These actions streamline the process of initializing Terraform and authenticating with Azure, allowing us to focus on the infrastructure code itself.<\/p>\n\n\n\n<p>One of the key aspects of our infrastructure deployment is the management of secrets. We store service principal credentials in GitHub Secrets, which are securely injected into the action runs as environment variables. This approach ensures that sensitive information remains protected while still being accessible to the deployment pipeline. Configuration parameters are also managed through GitHub environment variables, keeping our repository clean and secure.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"initial-function-deployment\">Initial Function Deployment<\/h4>\n\n\n\n<p>The initial deployment of the Azure Function is integrated into our infrastructure pipeline. As part of the infrastructure deployment process, the pipeline downloads the latest release of the function code from GitHub. This release is then deployed to Azure using the <code class=\"\" data-line=\"\">zip_deploy_file<\/code> parameter in our Terraform configuration. This method ensures that our function is up and running as soon as the infrastructure is provisioned, without requiring any manual intervention.<\/p>\n\n\n\n<p>Building, Testing, and Deploying the Function The function code pipeline is designed to ensure that every change to the application is thoroughly tested and reliably deployed. Our function is written in TypeScript, and the build process is managed using npm. The code, along with its dependencies, is packaged into a zip file, which is then ready for deployment.<\/p>\n\n\n\n<p>To maintain high code quality, we\u2019ve integrated unit tests using Jest into our pipeline. These tests are executed during the npm build process, allowing us to catch any issues early. Only if all tests pass does the pipeline proceed to the deployment stage.<\/p>\n\n\n\n<p>On every merge to the main branch, the pipeline creates a new GitHub release. This release is named with a timestamp in the <code class=\"\" data-line=\"\">YmdHMS<\/code> format, providing a clear versioning system that makes it easy to track deployments over time. The release is then deployed to Azure using the <code class=\"\" data-line=\"\">azure\/functions<\/code>-action, ensuring that our production environment always runs the latest tested code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Bringing it all together<\/h2>\n\n\n\n<p>Now, that all applications are built and deployed automatically, what&#8217;s missing is the glue in between all of them. At this point our SMS-Gateway only provides an Echo service as a proof of concept that sending and receiving SMS messages works as intended.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Interacting with the Service Bus<\/h3>\n\n\n\n<p>So, in order to connect the SMS-Gateway to the cloud services we needed to publish our received SMS messages to the service bus ingress and needed to send messages published to the service bus egress to their respective receivers.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"message-format\">Message Format<\/h4>\n\n\n\n<p>However, beforehand we have to think about what we do want to publish to and receive from the service bus. In these messages we need the following information:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The message&#8217;s content<\/li>\n\n\n\n<li>The sender&#8217;s address<\/li>\n<\/ul>\n\n\n\n<p>We decided to embed the content in the messages body and provide the sender&#8217;s address as a user defined property with key&nbsp;<code class=\"\" data-line=\"\">address<\/code>. These addresses will be encoded into a URI like follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">&lt;SCHEMA&gt;:\/\/&lt;ADDRESS&gt;\n<\/code><\/pre>\n\n\n\n<p>For a phone number and SMS this could look like the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">sms:\/\/+49123456789\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"technology-choices\">Technology Choices<\/h4>\n\n\n\n<p>Because it is very easy to setup and use, we chose the official Azure Service Bus SDK for Go to interact with the service bus. In an actual non-research environment we would probably evaluate the use of a generic AMQP (Advanced Message Queuing Protocol) implementation to try to escape from vendor-lock-in.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-go\" data-line=\"\">type MessageHandler interface {\n\tHandle(Message) error\n}<\/code><\/pre>\n\n\n\n<p>Conveniently, we have designed the gateway architecture in a way that allows to plug in so called message handlers to our broker. After being registered with the broker, a message handler&#8217;s Handle function will always be called when a new &#8220;Request&#8221;-type message is received.<\/p>\n\n\n\n<p>Thus, to send messages to the service bus, we just needed to add a MessageHandler implementation for it. Messages received from the service bus can be published using our broker&#8217;s Publish function.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"setting-up-the-connection\">Setting up the Connection<\/h4>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-go\" data-line=\"\">senderConnectionString := config.String(&quot;servicebus.sender.connectionstring&quot;)\nsenderClient, err := azservicebus.NewClientFromConnectionString(senderConnectionString, nil)\nif err != nil {\n    return nil, fmt.Errorf(&quot;creating service bus sender client: %w&quot;, err)\n}\n\nreceiverConnectionString := config.String(&quot;servicebus.receiver.connectionstring&quot;)\nreceiverClient, err := azservicebus.NewClientFromConnectionString(receiverConnectionString, nil)\nif err != nil {\n    return nil, fmt.Errorf(&quot;creating service bus receiver client: %w&quot;, err)\n}<\/code><\/pre>\n\n\n\n<p>Before we can interface with the service bus we have to authenticate with it.<\/p>\n\n\n\n<p>For this purpose we can use connection strings obtained from the Azure Service Bus Web Interface.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-go\" data-line=\"\">func (p *providerImpl) GetReceiver() (Receiver, error) {\n\tconfig := configuration.GetConfig()\n\tqueue := config.String(&quot;servicebus.receiver.queue&quot;)\n\n\treturn p.receiverClient.NewReceiverForQueue(queue, nil)\n}\n\nfunc (p *providerImpl) GetSender() (Sender, error) {\n\tconfig := configuration.GetConfig()\n\tqueue := config.String(&quot;servicebus.sender.queue&quot;)\n\n\treturn p.senderClient.NewSender(queue, nil)\n}<\/code><\/pre>\n\n\n\n<p>Using the authenticated clients we can access the respective queues with a <code class=\"\" data-line=\"\">azureservicebus.Sender<\/code> and <code class=\"\" data-line=\"\">azureservicebus.Receiver<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"receiving-messages\">Receiving Messages<\/h4>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-go\" data-line=\"\">func (listener *Listener) Listen(broker messaging.Broker) error {\n    for {\n        ...\n\n        messages, err = listener.receiver.ReceiveMessages(context.TODO(), 1, nil)\n        if err != nil {\n            log.Warn().Err(err).Msg(&quot;Could not receive messages from service bus!&quot;)\n            return err\n        }\n\n        ...\n\n        for _, message := range messages {\n            if err := listener.handleMessage(message, broker); err != nil {\n                return err\n            }\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p><code class=\"\" data-line=\"\">azureservicebus.Receiver<\/code> allows to receive messages using the appropriately named <code class=\"\" data-line=\"\">ReceiveMessages<\/code> function. In our gateway application we just continuously poll the service bus egress queue for new messages.<\/p>\n\n\n\n<p>After that the <code class=\"\" data-line=\"\">handleMessage<\/code> function is called, which will parse the message, dead letter it if its format is invalid or abandon it if it cannot serve the provided schema. Latter is the case if the address for example has the schema email but the gateway only supports sending SMS messages.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"sending-messages\">Sending Messages<\/h4>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-go\" data-line=\"\">func (m MessageHandler) Handle(message messaging.Message) error {\n\t...\n\n\terr := m.sender.SendMessage(context.TODO(), toServiceBusMessage(message), nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}<\/code><\/pre>\n\n\n\n<p>For the other direction, <code class=\"\" data-line=\"\">azureservicebus.Sender<\/code> provides us with a <code class=\"\" data-line=\"\">SendMessage<\/code> function which can be used to send messages to the configured queue.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-go\" data-line=\"\">handler, err := servicebus.NewMessageHandler(sender)\nif err != nil {\n    return err\n}\n\nbroker := messaging.NewBroker(handler)<\/code><\/pre>\n\n\n\n<p>This function is called inside a <code class=\"\" data-line=\"\">MessageHandler<\/code> implementation which can then be used by our broker.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Debugging Azure Functions<\/h3>\n\n\n\n<p>In the development of GuppyAI, one of the most significant challenges we faced was debugging our Azure Function without attached monitoring tools like Application Insights.<\/p>\n\n\n\n<p>Initially, we were met with the frustrating realization that our function wasn&#8217;t processing incoming messages, yet we had no clear indication of why this was happening. It wasn&#8217;t until we dove deeper into the issue that we discovered a misconfiguration in the Service Bus setup within the function, which was throwing errors and causing the failure.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"using-live-logs-with-azure-functions-vs-code-extension\">Using Live Logs with Azure Functions VS Code Extension<\/h4>\n\n\n\n<p>To overcome these initial hurdles, we turned to the Visual Studio Code extension for Azure Functions provided by Microsoft. This powerful tool allowed us to manage our functions directly from within our code editor, making the debugging process significantly smoother. After authenticating with our Microsoft accounts, we could deploy, manage, and most importantly, view live logs relayed directly to the integrated console in Visual Studio Code.<\/p>\n\n\n\n<p>These live logs proved invaluable. They allowed us to observe the crashes and error messages generated by the misconfigured Azure Service Bus, providing us with the insights we needed to correct the problem. The extension also offered access to log files related to the current deployment, which further aided in diagnosing and resolving the issues we encountered.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"running-the-function-locally-with-azure-core-tools\">Running the Function Locally with Azure Core Tools<\/h4>\n\n\n\n<p>Despite resolving the Service Bus configuration issue, we encountered another subtle problem: a faulty query to our Cosmos DB. This issue was particularly tricky because it didn&#8217;t cause any crashes or throw error messages. Instead, it led to empty results when attempting to retrieve a user&#8217;s conversation, leaving us puzzled.<\/p>\n\n\n\n<p>To tackle this, we disabled the Service Bus trigger in Azure and opted to run the function locally using Azure Core Tools. Running the function in a local environment allowed us to attach a debugger and gain a clearer view of the function&#8217;s operations. By attaching to the real infrastructure while running locally, we could trigger the function using the Service Bus and pinpoint the exact cause of the issue &#8211; the incorrect Cosmos DB query.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"lessons-learned-and-future-improvements\">Lessons Learned and Future Improvements<\/h4>\n\n\n\n<p>From these experiences, we learned a critical lesson: besides testing code before it is being deployed, effective trace logging and monitoring are essential for efficiently identifying and resolving issues in a cloud-based system. Without them, diagnosing problems can be a time-consuming and frustrating process. Moving forward, we plan to integrate Application Insights into our setup. This will enable us to access detailed logs directly from the Azure web dashboard, streamlining our debugging and error detection processes and ultimately making the development of GuppyAI smoother and more efficient.<\/p>\n\n\n\n<p>These improvements will not only save time but also enhance our ability to deliver a robust and reliable system, paving the way for future development and scaling of GuppyAI.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">On-Premise Host Setup using Ansible<\/h3>\n\n\n\n<p>For deploying the &#8211; now automatically built &#8211; container, we decided to use Ansible. Ansible is a tool that interprets YAML configuration files, so called playbooks, as a description on how a target system should look when the playbook is applied.<\/p>\n\n\n\n<p>The goal with using Ansible was that we would gain a way to turn a fresh install of Raspberry Pi OS on our Raspberry Pi 3B into a complete setup for use as a SMS-Gateway.<\/p>\n\n\n\n<p>Therefore, we laid out our playbook into three parts: Preparing the host for running containers using podman (i.e. installing podman), preparing the host for the GSM modem and installing the gateway container so that it would automatically start on a reboot.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-yaml\" data-line=\"\">- name: Install podman\n  hosts: raspberry\n  become: true\n  tasks:\n    - name: Get podman and install it\n      package:\n        name: &quot;podman&quot;\n        state: present\n- name: Install configuration\n  hosts: raspberry\n  become: true\n  tasks:\n      - name: Copy udev configuration\n        ansible.builtin.copy:\n            src: &quot;..\/files\/udev\/40-modem.rules&quot;\n            dest: &quot;\/etc\/udev\/rules.d\/40-modem.rules&quot;\n            mode: 0644\n      - name: Copy usb_modeswitch script\n        ansible.builtin.copy:\n            src: &quot;..\/files\/scripts\/brovi_modeswitch.sh&quot;\n            dest: &quot;\/usr\/local\/bin\/brovi_modeswitch.sh&quot;\n            mode: 0755\n      - name: Disable ModemManager\n        ansible.builtin.systemd_service:\n            name: ModemManager\n            daemon_reload: true\n            enabled: false\n            state: stopped\n      - name: Add option kernel module (GSM modem driver)\n        ansible.builtin.modprobe:\n            name: option\n            state: present\n            persistent: present\n      - name: Reboot\n        ansible.builtin.reboot:\n          reboot_timeout: 300\n          msg: &quot;Rebooting to apply configuration changes&quot;<\/code><\/pre>\n\n\n\n<p>The first two parts involved using the package module to install podman on our system, installing the scripts, kernel module and udev rules for setting up our GSM modem and rebooting the device for the changes to take effect.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-yaml\" data-line=\"\">- name: Install application\n  hosts: raspberry\n  become: true\n  vars:\n    version: &quot;240809223418feature_arm-images-arm64v8&quot;\n  vars_files:\n    - vault.yaml\n  tasks:\n      - name: Pull application image\n        containers.podman.podman_image:\n          name: &quot;ghcr.io\/guppyai\/sms-gateway:{{ version }}&quot;\n      - name: Re-create Gateway container\n        containers.podman.podman_container:\n          name: gateway\n          image: &quot;ghcr.io\/guppyai\/sms-gateway:{{ version }}&quot;\n          state: stopped\n          recreate: true\n          detach: true\n          security_opt: &quot;label=disable&quot;\n          device: &quot;\/dev\/ttyUSB1:\/dev\/ttyUSB1&quot;\n          group_add: &quot;dialout&quot;\n          env:\n            GATEWAY_LOGGING_LEVEL: &quot;info&quot;\n            GATEWAY_SMS_MODEM_BAUD: 115200\n            GATEWAY_SMS_MODEM_DEVICE: &quot;\/dev\/ttyUSB1&quot;\n            GATEWAY_SMS_MODEM_POLLING: 1s\n            GATEWAY_SMS_TRACING: 0\n            GATEWAY_MESSAGING_ALLOWLIST: &quot;{{ messaging.allowlist | join(&#039;,&#039;) }}&quot;\n            GATEWAY_SERVICEBUS_SENDER_QUEUE: &quot;{{ servicebus.sender.queue }}&quot;\n            GATEWAY_SERVICEBUS_RECEIVER_QUEUE: &quot;{{ servicebus.receiver.queue }}&quot;\n            GATEWAY_SERVICEBUS_SENDER_CONNECTIONSTRING: &quot;{{ servicebus.sender.connectionstring }}&quot;\n            GATEWAY_SERVICEBUS_RECEIVER_CONNECTIONSTRING: &quot;{{ servicebus.receiver.connectionstring }}&quot;\n      - name: Install systemd unit for running application\n        become: yes\n        copy:\n            src: &quot;..\/files\/systemd\/gateway.service&quot;\n            dest: &quot;\/etc\/systemd\/system\/gateway.service&quot;\n            mode: 0644\n      - name: Enable and start application\n        become: yes\n        ansible.builtin.systemd_service:\n          name: gateway\n          daemon_reload: true\n          enabled: true\n          state: restarted<\/code><\/pre>\n\n\n\n<p>The second part first tells Ansible to pull the gateway&#8217;s container image, create a container using it and installing a systemd unit file for starting it on every boot.<\/p>\n\n\n\n<p>Now, when looking at above snippet you might wonder what all those fancy curly braces mean. Let us introduce you to jinja2 templates and Ansible&#8217;s ability to use vaults. jinja2 is a templating language that allows one to include and manipulate variables through various functions like the&nbsp;<code class=\"\" data-line=\"\">join<\/code>&nbsp;function above to concatenate the list of allowed addresses to a comma-separated string. This templating language is one aspect of why Ansible is so powerful. One other aspect of that are Ansible Vaults. Vaults are encrypted yaml-files containing variables that can be loaded into a playbook if you know the passphrase they have been encrypted with.<\/p>\n\n\n\n<p>Using vaults, secrets can safely be stored in a git repository alongside the playbook they are used in &#8211; assuming the passphrases used to encrypt them are not compromised.<\/p>\n\n\n\n<p>All of these features were very useful to our goal of having a reproducible environment we could build using a single command.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">State of the Application<\/h2>\n\n\n\n<p>After connecting all of our services, the application is now automatically answering our SMS messages. <\/p>\n\n\n\n<div class=\"wp-block-media-text is-stacked-on-mobile\"><figure class=\"wp-block-media-text__media\"><img loading=\"lazy\" decoding=\"async\" width=\"610\" height=\"1024\" data-attachment-id=\"26466\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/08\/26\/welcome-to-the-future-of-government-tech-meet-guppyai\/first_screenshot\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/First_Screenshot.jpg\" data-orig-size=\"1076,1806\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;1&quot;}\" data-image-title=\"First_Screenshot\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/First_Screenshot-610x1024.jpg\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/First_Screenshot-610x1024.jpg\" alt=\"Chat conversation between a user and GuppyAI.\n\nUser: &quot;Hey, I am in office and someone is trying to call me every few hours. I do not want to answer the pohne. What could I do?&quot;\n\nGuppyAI: &quot;If you don't wnat to answer the phone, you have a few options: 1. You can turn your phone on silent or vibrate mode so that you don't hear the ringtone. 2. You can let the call go to vo&quot;\n\nUser: &quot;I didn't get all of you messages.&quot;\n\nGuppyAI: &quot;Sorry about that! Here are a few options for you if you don't want to answer the phone: 1. You can turn your phone on silent or vibrate mode so that you don't hear the ringtone. 2. You can let the&quot;\" class=\"wp-image-26466 size-full\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/First_Screenshot-610x1024.jpg 610w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/First_Screenshot-179x300.jpg 179w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/First_Screenshot-768x1289.jpg 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/First_Screenshot-915x1536.jpg 915w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/First_Screenshot.jpg 1076w\" sizes=\"auto, (max-width: 610px) 100vw, 610px\" \/><\/figure><div class=\"wp-block-media-text__content\">\n<p>Well, sort of&#8230; the application still has problems with long answers. This is due to us configuring a very conservative token limit with OpenAI&#8217;s API to limit the amount of SMS messages we send.<\/p>\n\n\n\n<p>However, even telling the LLM in our system prompt to keep its answers as short as possible, it would not always constrain to our character limit worth three SMS messages.<\/p>\n\n\n\n<p>For an actual application this would not be a deal breaker as the limit is fully self-induced by us to reduce costs.<\/p>\n\n\n\n<p>However, maybe resorting to another AI model trained to give short answers is worth looking into for this use case.<\/p>\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-media-text has-media-on-the-right is-stacked-on-mobile\"><div class=\"wp-block-media-text__content\">\n<p>In this example conversation you can see another of our features in action: Commands.<\/p>\n\n\n\n<p>Currently, we have integrated the STOP command that will just delete the entire conversation history from our database and allow you to start all over again.<\/p>\n\n\n\n<p>This is especially helpful if you want to use prompts that alter the way you interact with GuppyAI &#8211; just as you know from other AI solutions like OpenAI&#8217;s ChatGPT.<\/p>\n<\/div><figure class=\"wp-block-media-text__media\"><img loading=\"lazy\" decoding=\"async\" width=\"822\" height=\"1024\" data-attachment-id=\"26467\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/08\/26\/welcome-to-the-future-of-government-tech-meet-guppyai\/second_screenshot\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/Second_Screenshot.jpg\" data-orig-size=\"1071,1335\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;1&quot;}\" data-image-title=\"Second_Screenshot\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/Second_Screenshot-822x1024.jpg\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/Second_Screenshot-822x1024.jpg\" alt=\"Chat conversation between a user and GuppyAI.\n\nAt the beginning the user uses the &quot;STOP&quot; command to reset the chat.\nAfterwards, the user asks GuppyAI what to get for lunch. The chatbot answers with various questions regarding the user's dietary requirements.\" class=\"wp-image-26467 size-full\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/Second_Screenshot-822x1024.jpg 822w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/Second_Screenshot-241x300.jpg 241w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/Second_Screenshot-768x957.jpg 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/Second_Screenshot.jpg 1071w\" sizes=\"auto, (max-width: 822px) 100vw, 822px\" \/><\/figure><\/div>\n\n\n\n<p>So, as you could see in these examples, GuppyAI behaves like any other AI assistant but with bringing AI to SMS we are able to reach far more people with this revolutionizing technology.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What could be next?<\/h2>\n\n\n\n<p>Looking ahead, GuppyAI is in a great position for expansion thanks to its flexible Hexagon architecture. Right now, we&#8217;re using a pub-sub pattern within the Azure Service Bus, which makes it super easy to add new communication channels without having to mess with the current setup too much. <\/p>\n\n\n\n<p>If we were to further work on GuppyAI, our next step would be to implement a billing system to sustain our business. <\/p>\n\n\n\n<p>Afterwards, we&#8217;d try to go beyond SMS and add new message channels. These could be instant messengers like Telegram, e-mail or even fax. This would help us reach a much wider audience without having to do a lot of heavy lifting on the code side.<\/p>\n\n\n\n<p>But it&#8217;s not just about adding more ways to connect. We&#8217;d also plan to do some major upgrades to the system itself. As mentioned before, one of the big ones would be setting up a better monitoring system using Application Insights. This will make it easier for us to keep track of things and catch problems early, which means fewer headaches down the road.<\/p>\n\n\n\n<p>Last but not least, we&#8217;d also be improving on our development workflow by adding a staging environment. This would allow us to really test and debug everything before rolling out new features.<\/p>\n\n\n\n<p>It&#8217;s like having a sandbox to play in where we can make sure everything works perfectly before we go live. This is key to keeping things running smoothly as we&#8217;d continue to grow and adapt GuppyAI to new platforms and different use cases.<\/p>\n\n\n\n<p>All in all this journey has been very educating and was way more involving and fun than we anticipated in the beginning. Especially the wide variety of topics we could cover in this project &#8211; from decade old protocols to IoT-like applications, cloud computing and infrastructure as code &#8211; was extremely intriguing.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Read about our journey of developing GuppyAI, a friendly, SMS-powered AI chatbot designed to upgrade your skills without the hassle. <\/p>\n","protected":false},"author":1208,"featured_media":26437,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1,652,660,120,659,654,650,22],"tags":[456,227,389,216,4,1042,1041,514],"ppma_author":[1034,1038,1039],"class_list":["post-26436","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-allgemein","category-artificial-intelligence","category-chatgpt-and-language-models","category-cloud-technologies","category-devops","category-internet-of-things","category-scalable-systems","category-student-projects","tag-ansible","tag-azure","tag-github-actions","tag-golang","tag-linux","tag-podman","tag-sms","tag-terraform"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/architecture-3.png","jetpack-related-posts":[{"id":4024,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2018\/08\/22\/why-ai-is-a-threat-for-our-digital-security\/","url_meta":{"origin":26436,"position":0},"title":"Why AI is a Threat for our Digital Security","author":"Katharina Strecker","date":"22. August 2018","format":false,"excerpt":"Artificial intelligence has a great potential to improve many areas of our lives in the future. But what happens when these AI technologies are used maliciously? Sure, a big topic may be autonomous weapons or so called \u201ckiller robots\u201d. But beside our physical security - what about our digital one?\u2026","rel":"","context":"In &quot;Artificial Intelligence&quot;","block_context":{"text":"Artificial Intelligence","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/artificial-intelligence\/"},"img":{"alt_text":"Computer image recognition has beaten human-level image recognition in 2015","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/08\/human-level-image-recongition-1024x717.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/08\/human-level-image-recongition-1024x717.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/08\/human-level-image-recongition-1024x717.png?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":20620,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/10\/04\/pal\/","url_meta":{"origin":26436,"position":1},"title":"Palantir: An uncanny company?","author":"Niklas Janssen","date":"4. October 2021","format":false,"excerpt":"In the future data privacy could be one of the biggest issues of our time with growing supervision capabilities from big enterprises and governments. China is a telling example of what could happen if data privacy and anonymity on the internet is completely eroded. On the other hand, there is\u2026","rel":"","context":"In &quot;Secure Systems&quot;","block_context":{"text":"Secure Systems","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/system-designs\/secure-systems\/"},"img":{"alt_text":"image of multiple hard disks","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/08\/pexels-panumas-nikhomkhai-1148820-scaled.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/08\/pexels-panumas-nikhomkhai-1148820-scaled.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/08\/pexels-panumas-nikhomkhai-1148820-scaled.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/08\/pexels-panumas-nikhomkhai-1148820-scaled.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/08\/pexels-panumas-nikhomkhai-1148820-scaled.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/08\/pexels-panumas-nikhomkhai-1148820-scaled.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":3891,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2018\/08\/12\/disaster-prevention-in-germany\/","url_meta":{"origin":26436,"position":2},"title":"Disaster prevention in Germany","author":"Benedikt Schrade","date":"12. August 2018","format":false,"excerpt":"This article has two main topics: First a short overview about the organization of the disaster prevention in Germany. The second part of this article is about the consequences of a national blackout and how to prepare for this disaster. 1. Definitions Today the word \u201edisaster\u201d is often used in\u2026","rel":"","context":"In &quot;Secure Systems&quot;","block_context":{"text":"Secure Systems","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/system-designs\/secure-systems\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":27783,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2025\/07\/25\/open-source-ai-models-opportunities-and-challenges-for-enterprises\/","url_meta":{"origin":26436,"position":3},"title":"Open-Source AI Models \u2013 Opportunities and Challenges for Enterprises","author":"Julian Schniepp","date":"25. July 2025","format":false,"excerpt":"Note: This article was written for the module Enterprise IT (113601a) during the summer semester of 2025. Introduction AI has taken over the tech landscape, going from novelty and experimental technology to a critical piece of infrastructure in enterprise, and is perhaps the most spoken of technology related topic of\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/07\/image-1.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/07\/image-1.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/07\/image-1.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/07\/image-1.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":24427,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/03\/03\/ai-and-scaling-the-compute-for-the-new-moores-law\/","url_meta":{"origin":26436,"position":4},"title":"AI and Scaling the Compute for the new Moore\u2019s Law","author":"Marvin Blessing","date":"3. March 2023","format":false,"excerpt":"AI and Scaling the Compute becomes more relevant as the strive for larger language models and general purpose AI continues. The future of the trend is unknown as the rate of doubling the compute outpaces Moore's Law rate of every two year to a 3.4 month doubling. IntroductionRequiring compute beyond\u2026","rel":"","context":"In &quot;Artificial Intelligence&quot;","block_context":{"text":"Artificial Intelligence","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/artificial-intelligence\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/03\/image-4.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/03\/image-4.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/03\/image-4.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/03\/image-4.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/03\/image-4.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":2493,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/07\/29\/spying-on-everyone-cheap-and-simple\/","url_meta":{"origin":26436,"position":5},"title":"Spying on everyone &#8211; Cheap and Simple","author":"mk297","date":"29. July 2017","format":false,"excerpt":"Introduction Espionage, or less formally \u201csyping\u201d has become a huge public topic in Summer 2013. Edward Snowden revealed that The American NSA (National Security Agency) and the allied Great Britain GCHQ (Government Communications Headquarters) are surveilling, storing and examing all network traffic in- and outbound the US and Great Britain.\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/nx4271_3.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/nx4271_3.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/nx4271_3.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/nx4271_3.jpg?resize=700%2C400&ssl=1 2x"},"classes":[]}],"jetpack_sharing_enabled":true,"authors":[{"term_id":1034,"user_id":1208,"is_guest":0,"slug":"lucca_greschner","display_name":"Lucca Greschner","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/126fffc4b85a7a3bbcafaf5e1d4a06727f838ed9c83a8a7a95510ac54eb02f35?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""},{"term_id":1038,"user_id":1215,"is_guest":0,"slug":"kay_knpfle","display_name":"Kay Kn\u00f6pfle","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/2d1443758d2a9573ae4184248346519b07734a699bfbec76e2f3bc599651a542?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""},{"term_id":1039,"user_id":1214,"is_guest":0,"slug":"sebastian_maier","display_name":"Sebastian Maier","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/d83306efe5fe25b56f59d02434749384b7139899ff76d136aa3c403844d2c96d?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/26436","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/users\/1208"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/comments?post=26436"}],"version-history":[{"count":21,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/26436\/revisions"}],"predecessor-version":[{"id":26495,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/26436\/revisions\/26495"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media\/26437"}],"wp:attachment":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media?parent=26436"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/categories?post=26436"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/tags?post=26436"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/ppma_author?post=26436"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}