diff --git a/.gitignore b/.gitignore
index 1d17dae..32bceab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 .venv
+horde.db
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 02a0261..1f8f801 100644
--- a/Makefile
+++ b/Makefile
@@ -14,3 +14,5 @@ web: frontend/build/web/index.html
 
 frontend/build/web/index.html: 
 	cd frontend && flutter build web
+
+frontend/build/web/index.html: $(shell find frontend/lib -name '*.dart')
\ No newline at end of file
diff --git a/backend/app.py b/backend/app.py
index 22cfb32..939dc09 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -1,14 +1,134 @@
-from quart import Quart, send_from_directory
+import sys
+import json
+import asyncio
 
-app = Quart(__name__, static_folder='../frontend/build/web')
+from database import create_sql_engine, User, Agent
+from sqlalchemy.orm import Session
+from sqlalchemy.sql.expression import select
+from quart import Quart, Response, request, send_from_directory, jsonify, websocket
 
-@app.route('/')
-async def hello():
-    return await send_from_directory(app.static_folder, 'index.html')
+class App:
+    def __init__(self, data_directory):
+        self.data_directory = data_directory
+        self.sql_engine = create_sql_engine(self.data_directory)
 
-@app.route('/<path:path>')
-async def serve_static(path):
-    return await send_from_directory(app.static_folder, path)
+    def sql_session(self):
+        return Session(self.sql_engine)
+
+app = App("./")
+
+def start_server(app):
+    server = Quart(__name__, static_folder='../frontend/build/web')
+
+    @server.route('/')
+    async def index():
+        return await send_from_directory(server.static_folder, 'index.html')
+
+    @server.route('/<path:path>')
+    async def serve_static(path):
+        return await send_from_directory(server.static_folder, path)
+
+    @server.route('/user/signup', methods=['POST'])
+    async def signup():
+        data = await request.get_json()
+
+        user_id = None
+
+        username = data['username']
+        email = data['email']
+        password = data['password']
+
+        with app.sql_session() as session:
+            result = session.execute(
+                select(User.id).where(User.email == email or User.username == username)
+            ).first()
+
+            if result is None:
+                user = User()
+                user.email = email
+                user.username = username
+                user.password = password
+
+                session.add(user)
+                session.commit()
+                
+                user_id = user.id
+                print(f"Created user #{user_id}: {username} ({email})")
+            else:
+                user_id = None
+                print(f"Signup error: '{email}' or '{username}' are already used")
+
+        if user_id is None:
+            return {"error": "existing credentials"}
+        else:
+            # token = auth_manager.dump_token(user_id)
+            return {"userId": user_id}
+
+    @server.websocket('/ws')
+    async def ws():
+        print("incoming ws")
+
+        # Authenticate before dedicating any resources
+        auth_req = json.loads(await websocket.receive())
+        if "username" not in auth_req:
+            print("Invalid auth request:", auth_req)
+            await websocket.send(json.dumps({'error': 'invalid auth'}))
+            return
+        
+        # TODO: Actually authenticate credentials against stored users
+        # TODO: Get the user id from the credentials, use them in create-agent
+        print(f"User {auth_req['username']} connected")
+
+        send_queue = asyncio.Queue()
+
+        async def sender_task():
+            nonlocal send_queue
+            while True:
+                msg = await send_queue.get()
+                await websocket.send(json.dumps(msg))
+
+        async def send(msg):
+            await send_queue.put(msg)
+
+        asyncio.create_task(sender_task())
+
+        while True:
+            req = json.loads(await websocket.receive())
+
+            action = req['action']
+            body = req.get('body', {})
+            nonce = req.get('nonce', None)
+
+            if action == "ping":
+                print("ping")
+                await send({'content': "pong"})
+            elif action == "create-agent":
+                with app.sql_session() as session:
+                    agent = Agent()
+
+                    agent.user_id = None
+                    agent.name = body['name']
+                    agent.description = body['description']
+                    agent.picture = body.get('picture')
+                    agent.kind = body['kind']
+                    agent.data = body['data']
+
+                    session.add(agent)
+                    session.commit()
+                    
+                    print(f"Created user #{agent.id}: {agent.name}")
+                    await send({'content': "agent created", 'agentId': agent.id, 'nonce': nonce})
+            else:
+                print("Unknown message: " + action)
+
+    server.run(debug=True)
 
 if __name__ == "__main__":
-    app.run(debug=True)
+    if len(sys.argv) > 1:
+        data_directory = sys.argv[1]
+    else:
+        print("No directory specified. Using current directory as default.")
+        data_directory = "."
+
+    app = App(data_directory)
+    start_server(app)
\ No newline at end of file
diff --git a/backend/requirements.txt b/backend/requirements.txt
index f8d6ab2..00a2728 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -1 +1,2 @@
 Quart==0.20.0
+sqlalchemy
\ No newline at end of file
diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart
index 7b7f5b6..325549e 100644
--- a/frontend/lib/main.dart
+++ b/frontend/lib/main.dart
@@ -1,122 +1,43 @@
+import 'dart:html' as html;
+import 'dart:io' as io;
 import 'package:flutter/material.dart';
+import 'chat_screen.dart';
+import 'backend.dart';
+
+class Config {
+  static String? backendUrl; // Declare it as nullable
+
+  static void loadConfig() {
+    // Check the platform for web or other
+    if (html.window.location.origin.isNotEmpty) {
+      // For web applications
+      backendUrl = html.window.location.origin; // Get the server URL from window.location
+    } else if (io.Platform.environment.containsKey('BACKEND_URL')) {
+      // For Android or other platforms, check for the environment variable
+      backendUrl = io.Platform.environment['BACKEND_URL'];
+    } else {
+      // If no value is determined, set it to null
+      backendUrl = null;
+    }
+  }
+}
 
 void main() {
+  Config.loadConfig(); // Load the configuration before running the app
+
+  Backend.setup('${Config.backendUrl}/ws');
   runApp(const MyApp());
 }
 
 class MyApp extends StatelessWidget {
   const MyApp({super.key});
 
-  // This widget is the root of your application.
   @override
   Widget build(BuildContext context) {
     return MaterialApp(
-      title: 'Flutter Demo',
-      theme: ThemeData(
-        // This is the theme of your application.
-        //
-        // TRY THIS: Try running your application with "flutter run". You'll see
-        // the application has a purple toolbar. Then, without quitting the app,
-        // try changing the seedColor in the colorScheme below to Colors.green
-        // and then invoke "hot reload" (save your changes or press the "hot
-        // reload" button in a Flutter-supported IDE, or press "r" if you used
-        // the command line to start the app).
-        //
-        // Notice that the counter didn't reset back to zero; the application
-        // state is not lost during the reload. To reset the state, use hot
-        // restart instead.
-        //
-        // This works for code too, not just values: Most code changes can be
-        // tested with just a hot reload.
-        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
-      ),
-      home: const MyHomePage(title: 'Flutter Demo Home Page'),
-    );
-  }
-}
-
-class MyHomePage extends StatefulWidget {
-  const MyHomePage({super.key, required this.title});
-
-  // This widget is the home page of your application. It is stateful, meaning
-  // that it has a State object (defined below) that contains fields that affect
-  // how it looks.
-
-  // This class is the configuration for the state. It holds the values (in this
-  // case the title) provided by the parent (in this case the App widget) and
-  // used by the build method of the State. Fields in a Widget subclass are
-  // always marked "final".
-
-  final String title;
-
-  @override
-  State<MyHomePage> createState() => _MyHomePageState();
-}
-
-class _MyHomePageState extends State<MyHomePage> {
-  int _counter = 0;
-
-  void _incrementCounter() {
-    setState(() {
-      // This call to setState tells the Flutter framework that something has
-      // changed in this State, which causes it to rerun the build method below
-      // so that the display can reflect the updated values. If we changed
-      // _counter without calling setState(), then the build method would not be
-      // called again, and so nothing would appear to happen.
-      _counter++;
-    });
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    // This method is rerun every time setState is called, for instance as done
-    // by the _incrementCounter method above.
-    //
-    // The Flutter framework has been optimized to make rerunning build methods
-    // fast, so that you can just rebuild anything that needs updating rather
-    // than having to individually change instances of widgets.
-    return Scaffold(
-      appBar: AppBar(
-        // TRY THIS: Try changing the color here to a specific color (to
-        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
-        // change color while the other colors stay the same.
-        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
-        // Here we take the value from the MyHomePage object that was created by
-        // the App.build method, and use it to set our appbar title.
-        title: Text(widget.title),
-      ),
-      body: Center(
-        // Center is a layout widget. It takes a single child and positions it
-        // in the middle of the parent.
-        child: Column(
-          // Column is also a layout widget. It takes a list of children and
-          // arranges them vertically. By default, it sizes itself to fit its
-          // children horizontally, and tries to be as tall as its parent.
-          //
-          // Column has various properties to control how it sizes itself and
-          // how it positions its children. Here we use mainAxisAlignment to
-          // center the children vertically; the main axis here is the vertical
-          // axis because Columns are vertical (the cross axis would be
-          // horizontal).
-          //
-          // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
-          // action in the IDE, or press "p" in the console), to see the
-          // wireframe for each widget.
-          mainAxisAlignment: MainAxisAlignment.center,
-          children: <Widget>[
-            const Text('You have pushed the button this many times:'),
-            Text(
-              '$_counter',
-              style: Theme.of(context).textTheme.headlineMedium,
-            ),
-          ],
-        ),
-      ),
-      floatingActionButton: FloatingActionButton(
-        onPressed: _incrementCounter,
-        tooltip: 'Increment',
-        child: const Icon(Icons.add),
-      ), // This trailing comma makes auto-formatting nicer for build methods.
+      title: 'Flutter Chat',
+      theme: ThemeData.dark(),
+      home: const ChatScreen(),
     );
   }
-}
+}
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 6d03df5..daaf485 100644
--- a/readme.md
+++ b/readme.md
@@ -2,9 +2,9 @@
 
 To run the Flask server, follow these steps:
 
-1. Build and activate the virtual environment and install the required dependencies:
+1. Build and activate the virtual environment, install the required dependencies, and build the gui by running make with the default target:
    ```bash
-   make install
+   make
    ```
 2. Run the server using the provided script:
    ```bash
@@ -17,3 +17,6 @@ Now, visit `http://127.0.0.1:5000` in your web browser to see the application in
 
 Frontend uses flutter. For Ubuntu you can use [this guideline](https://ubuntu.com/blog/getting-started-with-flutter-on-ubuntu) to install it.
 
+## Configuration
+
+You can set the backend URL for Android and native builds using the environment variable `BACKEND_URL`. Make sure to set this variable before running the application. Defaults to `null` if nothing is set.
diff --git a/run.sh b/run.sh
index 638f34a..f6e6484 100644
--- a/run.sh
+++ b/run.sh
@@ -1,4 +1,35 @@
 #!/bin/bash
-cd backend
-source ../.venv/bin/activate
-python app.py
+
+case "$1" in
+  "")
+    cd backend
+    source ../.venv/bin/activate
+    python app.py
+    ;;
+  "test")
+    # Setup test data directory
+    mkdir -p test-data
+    rm -f test-data/horde.db
+
+    # Start backend in the background
+    cd backend
+    source ../.venv/bin/activate
+    python app.py ../test-data &  
+    sleep 2
+  
+    # Run tests
+    cd ../testing
+    #bash signup.sh
+    #node login.js
+    node create-agent.js
+
+    # Finalize the backend server
+    kill $!
+    ;;
+  "watch")
+    echo "TODO: Implement watch functionality"
+    ;;
+  *)
+    echo "Error: unknown command '$1'"
+    ;;
+esac
