diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
index 092000ee25907f9b053cea6e4349411764facfd4..b7c12c5fe86eee2208104781ad3ba74ebc248c04 100644
--- a/.idea/dataSources.xml
+++ b/.idea/dataSources.xml
@@ -5,7 +5,7 @@
       <driver-ref>mariadb</driver-ref>
       <synchronize>true</synchronize>
       <jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
-      <jdbc-url>jdbc:mariadb://id.local:3306/id</jdbc-url>
+      <jdbc-url>jdbc:mariadb://id:3306/id</jdbc-url>
       <working-dir>$ProjectFileDir$</working-dir>
     </data-source>
   </component>
diff --git a/.idea/dataSources/5bc27beb-c8ab-420d-bdbc-055b37ae9e39.xml b/.idea/dataSources/5bc27beb-c8ab-420d-bdbc-055b37ae9e39.xml
index 718f6a6137f4609e990a7e964db80aff545209b2..f35c05106109d72e05aa060297dbcb7e2b9f7b19 100644
--- a/.idea/dataSources/5bc27beb-c8ab-420d-bdbc-055b37ae9e39.xml
+++ b/.idea/dataSources/5bc27beb-c8ab-420d-bdbc-055b37ae9e39.xml
@@ -1217,8 +1217,8 @@ phpmyadmin|schema||phpmyadmin|localhost|UPDATE|G</Grants>
     </schema>
     <schema id="327" parent="1" name="id">
       <Current>1</Current>
-      <IntrospectionTimestamp>2023-11-10.19:39:44</IntrospectionTimestamp>
-      <LocalIntrospectionTimestamp>2023-11-10.19:39:47</LocalIntrospectionTimestamp>
+      <IntrospectionTimestamp>2023-11-16.19:58:39</IntrospectionTimestamp>
+      <LocalIntrospectionTimestamp>2023-11-16.19:58:50</LocalIntrospectionTimestamp>
       <CollationName>utf8mb4_general_ci</CollationName>
     </schema>
     <schema id="328" parent="1" name="phpmyadmin">
@@ -1248,94 +1248,237 @@ phpmyadmin|schema||phpmyadmin|localhost|UPDATE|G</Grants>
       <Engine>InnoDB</Engine>
       <CollationName>utf8mb4_general_ci</CollationName>
     </table>
-    <table id="338" parent="327" name="password_resets">
+    <table id="338" parent="327" name="apps">
       <Engine>InnoDB</Engine>
       <CollationName>utf8mb4_general_ci</CollationName>
     </table>
-    <column id="339" parent="337" name="id">
+    <table id="339" parent="327" name="badges">
+      <Engine>InnoDB</Engine>
+      <CollationName>utf8mb4_general_ci</CollationName>
+    </table>
+    <table id="340" parent="327" name="password_resets">
+      <Engine>InnoDB</Engine>
+      <CollationName>utf8mb4_general_ci</CollationName>
+    </table>
+    <table id="341" parent="327" name="profiles">
+      <Engine>InnoDB</Engine>
+      <CollationName>utf8mb4_general_ci</CollationName>
+    </table>
+    <column id="342" parent="337" name="id">
       <DasType>varchar(7)|0s</DasType>
       <NotNull>1</NotNull>
       <Position>1</Position>
     </column>
-    <column id="340" parent="337" name="email">
+    <column id="343" parent="337" name="email">
       <DasType>text|0s</DasType>
       <NotNull>1</NotNull>
       <Position>2</Position>
     </column>
-    <column id="341" parent="337" name="created_date">
+    <column id="344" parent="337" name="created_date">
       <DasType>date|0s</DasType>
       <DefaultExpression>current_timestamp()</DefaultExpression>
       <NotNull>1</NotNull>
       <Position>3</Position>
     </column>
-    <column id="342" parent="337" name="display_name">
+    <column id="345" parent="337" name="display_name">
       <DasType>text|0s</DasType>
       <Position>4</Position>
     </column>
-    <column id="343" parent="337" name="password">
+    <column id="346" parent="337" name="password">
       <DasType>text|0s</DasType>
       <NotNull>1</NotNull>
       <Position>5</Position>
     </column>
-    <column id="344" parent="337" name="verified">
+    <column id="347" parent="337" name="verified">
       <DasType>tinyint(1)|0s</DasType>
       <NotNull>1</NotNull>
       <Position>6</Position>
     </column>
-    <index id="345" parent="337" name="PRIMARY">
+    <column id="348" parent="337" name="has_pfp">
+      <DasType>tinyint(1)|0s</DasType>
+      <DefaultExpression>0</DefaultExpression>
+      <NotNull>1</NotNull>
+      <Position>7</Position>
+    </column>
+    <column id="349" parent="337" name="is_admin">
+      <DasType>tinyint(1)|0s</DasType>
+      <DefaultExpression>0</DefaultExpression>
+      <NotNull>1</NotNull>
+      <Position>8</Position>
+    </column>
+    <index id="350" parent="337" name="PRIMARY">
       <ColNames>id</ColNames>
       <Type>btree</Type>
       <Unique>1</Unique>
     </index>
-    <index id="346" parent="337" name="email">
+    <index id="351" parent="337" name="email">
       <ColNames>email</ColNames>
       <Type>hash</Type>
       <Unique>1</Unique>
     </index>
-    <key id="347" parent="337" name="PRIMARY">
+    <key id="352" parent="337" name="PRIMARY">
       <NameSurrogate>1</NameSurrogate>
       <Primary>1</Primary>
       <UnderlyingIndexName>PRIMARY</UnderlyingIndexName>
     </key>
-    <key id="348" parent="337" name="email">
+    <key id="353" parent="337" name="email">
       <UnderlyingIndexName>email</UnderlyingIndexName>
     </key>
-    <column id="349" parent="338" name="id">
+    <column id="354" parent="338" name="id">
+      <DasType>int(10)|0s</DasType>
+      <NotNull>1</NotNull>
+      <Position>1</Position>
+    </column>
+    <column id="355" parent="338" name="owner_id">
+      <DasType>varchar(7)|0s</DasType>
+      <Position>2</Position>
+    </column>
+    <column id="356" parent="338" name="title">
+      <DasType>text|0s</DasType>
+      <NotNull>1</NotNull>
+      <Position>3</Position>
+    </column>
+    <column id="357" parent="338" name="description">
+      <DasType>text|0s</DasType>
+      <Position>4</Position>
+    </column>
+    <foreign-key id="358" parent="338" name="apps_ibfk_1">
+      <ColNames>owner_id</ColNames>
+      <OnDelete>set-null</OnDelete>
+      <OnUpdate>cascade</OnUpdate>
+      <RefColNames>id</RefColNames>
+      <RefTableName>accounts</RefTableName>
+    </foreign-key>
+    <index id="359" parent="338" name="PRIMARY">
+      <ColNames>id</ColNames>
+      <Type>btree</Type>
+      <Unique>1</Unique>
+    </index>
+    <index id="360" parent="338" name="apps_ibfk_1">
+      <ColNames>owner_id</ColNames>
+      <Type>btree</Type>
+    </index>
+    <key id="361" parent="338" name="PRIMARY">
+      <NameSurrogate>1</NameSurrogate>
+      <Primary>1</Primary>
+      <UnderlyingIndexName>PRIMARY</UnderlyingIndexName>
+    </key>
+    <column id="362" parent="339" name="id">
       <AutoIncrement>1</AutoIncrement>
       <DasType>int(11)|0s</DasType>
       <NotNull>1</NotNull>
       <Position>1</Position>
     </column>
-    <column id="350" parent="338" name="auth_id">
+    <column id="363" parent="339" name="app_id">
+      <DasType>int(11)|0s</DasType>
+      <NotNull>1</NotNull>
+      <Position>2</Position>
+    </column>
+    <column id="364" parent="339" name="title">
+      <DasType>text|0s</DasType>
+      <NotNull>1</NotNull>
+      <Position>3</Position>
+    </column>
+    <column id="365" parent="339" name="description">
+      <DasType>text|0s</DasType>
+      <Position>4</Position>
+    </column>
+    <column id="366" parent="339" name="image">
+      <DasType>text|0s</DasType>
+      <NotNull>1</NotNull>
+      <Position>5</Position>
+    </column>
+    <foreign-key id="367" parent="339" name="badges_ibfk_1">
+      <ColNames>app_id</ColNames>
+      <RefColNames>id</RefColNames>
+      <RefTableName>apps</RefTableName>
+    </foreign-key>
+    <index id="368" parent="339" name="PRIMARY">
+      <ColNames>id</ColNames>
+      <Type>btree</Type>
+      <Unique>1</Unique>
+    </index>
+    <index id="369" parent="339" name="app_id">
+      <ColNames>app_id</ColNames>
+      <Type>btree</Type>
+    </index>
+    <key id="370" parent="339" name="PRIMARY">
+      <NameSurrogate>1</NameSurrogate>
+      <Primary>1</Primary>
+      <UnderlyingIndexName>PRIMARY</UnderlyingIndexName>
+    </key>
+    <column id="371" parent="340" name="id">
+      <AutoIncrement>8</AutoIncrement>
+      <DasType>int(11)|0s</DasType>
+      <NotNull>1</NotNull>
+      <Position>1</Position>
+    </column>
+    <column id="372" parent="340" name="auth_id">
       <DasType>tinytext|0s</DasType>
       <NotNull>1</NotNull>
       <Position>2</Position>
     </column>
-    <column id="351" parent="338" name="owner_id">
+    <column id="373" parent="340" name="owner_id">
       <DasType>varchar(7)|0s</DasType>
       <NotNull>1</NotNull>
       <Position>3</Position>
     </column>
-    <column id="352" parent="338" name="expiration">
+    <column id="374" parent="340" name="expiration">
       <DasType>int(11)|0s</DasType>
       <NotNull>1</NotNull>
       <Position>4</Position>
     </column>
-    <foreign-key id="353" parent="338" name="password_resets_ibfk_1">
+    <foreign-key id="375" parent="340" name="password_resets_ibfk_1">
       <ColNames>owner_id</ColNames>
       <RefColNames>id</RefColNames>
       <RefTableName>accounts</RefTableName>
     </foreign-key>
-    <index id="354" parent="338" name="PRIMARY">
+    <index id="376" parent="340" name="PRIMARY">
       <ColNames>id</ColNames>
       <Type>btree</Type>
       <Unique>1</Unique>
     </index>
-    <index id="355" parent="338" name="owner_id">
+    <index id="377" parent="340" name="owner_id">
       <ColNames>owner_id</ColNames>
       <Type>btree</Type>
     </index>
-    <key id="356" parent="338" name="PRIMARY">
+    <key id="378" parent="340" name="PRIMARY">
+      <NameSurrogate>1</NameSurrogate>
+      <Primary>1</Primary>
+      <UnderlyingIndexName>PRIMARY</UnderlyingIndexName>
+    </key>
+    <column id="379" parent="341" name="id">
+      <DasType>varchar(7)|0s</DasType>
+      <NotNull>1</NotNull>
+      <Position>1</Position>
+    </column>
+    <column id="380" parent="341" name="description">
+      <DasType>text|0s</DasType>
+      <Position>2</Position>
+    </column>
+    <column id="381" parent="341" name="public_avatar">
+      <DasType>tinyint(1)|0s</DasType>
+      <DefaultExpression>0</DefaultExpression>
+      <NotNull>1</NotNull>
+      <Position>3</Position>
+    </column>
+    <column id="382" parent="341" name="public_display_name">
+      <DasType>tinyint(1)|0s</DasType>
+      <DefaultExpression>0</DefaultExpression>
+      <NotNull>1</NotNull>
+      <Position>4</Position>
+    </column>
+    <foreign-key id="383" parent="341" name="profiles_ibfk_1">
+      <ColNames>id</ColNames>
+      <RefColNames>id</RefColNames>
+      <RefTableName>accounts</RefTableName>
+    </foreign-key>
+    <index id="384" parent="341" name="PRIMARY">
+      <ColNames>id</ColNames>
+      <Type>btree</Type>
+      <Unique>1</Unique>
+    </index>
+    <key id="385" parent="341" name="PRIMARY">
       <NameSurrogate>1</NameSurrogate>
       <Primary>1</Primary>
       <UnderlyingIndexName>PRIMARY</UnderlyingIndexName>
diff --git a/.idea/dataSources/5bc27beb-c8ab-420d-bdbc-055b37ae9e39/storage_v2/_src_/schema/id.Gw0AAA.meta b/.idea/dataSources/5bc27beb-c8ab-420d-bdbc-055b37ae9e39/storage_v2/_src_/schema/id.Gw0AAA.meta
index 4867f21f60727cab4e27dc27f09a455ab4ac4c8c..d4880e4fd594cdbe86f337e7a3c56583cd952f80 100644
--- a/.idea/dataSources/5bc27beb-c8ab-420d-bdbc-055b37ae9e39/storage_v2/_src_/schema/id.Gw0AAA.meta
+++ b/.idea/dataSources/5bc27beb-c8ab-420d-bdbc-055b37ae9e39/storage_v2/_src_/schema/id.Gw0AAA.meta
@@ -1,2 +1,2 @@
 #n:id
-!<md> [1699645184000, 0, null, null, -2147483648, -2147483648]
+!<md> [1700164719000, 0, null, null, -2147483648, -2147483648]
diff --git a/.idea/id.iml b/.idea/id.iml
index f61ecb3f4ae2fbfe0221c26ba247e0f25e00076e..21a8ff4b05fa6e1993a2919d515f5b660518e8f9 100644
--- a/.idea/id.iml
+++ b/.idea/id.iml
@@ -16,6 +16,9 @@
       <excludeFolder url="file://$MODULE_DIR$/vendor/sentry/sentry" />
       <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/deprecation-contracts" />
       <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/options-resolver" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/erusev/parsedown" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/erusev/parsedown-extra" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/kornrunner/blurhash" />
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
diff --git a/.idea/php.xml b/.idea/php.xml
index 5e0063f5db257150b04a9e9c79760345dcc7ebe7..4c1495a2e80694338714628e426051ed65559ca4 100644
--- a/.idea/php.xml
+++ b/.idea/php.xml
@@ -23,12 +23,20 @@
       <path value="$PROJECT_DIR$/vendor/phpmailer/phpmailer" />
       <path value="$PROJECT_DIR$/vendor/psr/http-factory" />
       <path value="$PROJECT_DIR$/vendor/psr/http-message" />
+      <path value="$PROJECT_DIR$/vendor/erusev/parsedown" />
+      <path value="$PROJECT_DIR$/vendor/erusev/parsedown-extra" />
+      <path value="$PROJECT_DIR$/vendor/kornrunner/blurhash" />
     </include_path>
   </component>
   <component name="PhpProjectSharedConfiguration" php_language_level="8.1" />
   <component name="PhpStanOptionsConfiguration">
     <option name="transferred" value="true" />
   </component>
+  <component name="PhpUnit">
+    <phpunit_settings>
+      <PhpUnitSettings custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" />
+    </phpunit_settings>
+  </component>
   <component name="PsalmOptionsConfiguration">
     <option name="transferred" value="true" />
   </component>
diff --git a/404.html b/404.html
index e2066aa24a6cf786c9518608e9eca1853bcd07e2..811b6d0c2408d250ef187b1657798d0c8509fbb6 100644
--- a/404.html
+++ b/404.html
@@ -1,2 +1,3 @@
 <h1>404</h1>
-<p>Sorry, that page couldn't be found.</p>
\ No newline at end of file
+<p>Sorry, that page couldn't be found.</p>
+
diff --git a/account.php b/account.php
index 2dd71ad8bac19ad05290c02b7e0d087b56005713..d4408c1338ae23c3bca986486bac7695cd9cf054 100644
--- a/account.php
+++ b/account.php
@@ -1,6 +1,6 @@
 <?php 
 
-if (!isset($_SESSION['auth'])) {
+if (!$_SESSION['auth']) {
     header('Location: /signin?callback=/account');
     exit;
 }
@@ -16,7 +16,7 @@ function get_gravatar_url( $email ) {
   
     // Grab the actual image URL
     return 'https://www.gravatar.com/avatar/' . $hash;
-  }
+}
 
 $stmt = $pdo->prepare('SELECT * FROM accounts WHERE id = ? LIMIT 1');
 $stmt->execute([$_SESSION['id']]);
@@ -59,6 +59,9 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") {
 
     $message = "Updated sucessfully. Changes might take a few minutes to take effect.";
 
+    header('Location: /profile');
+    die("Redirecting...");
+
 }
 
 skip_submit:
@@ -73,61 +76,68 @@ if (isset($message )) {
         } 
 ?>
 
-<div id="profile">
-    <img src="<?= get_gravatar_url($user['email']) ?>">
-    <div class="details">
-        <span class="displayname"><?= $user['display_name'] ?></span>
-        <span class="bcid"><?= format_bcid($user['id']); ?></span>
-        <time datetime="<?= $user["created_date"] ?>">Since <?= $user["created_date"]; ?></time>
-    </div>
-</div>
-
-<form method="post">
-    <fieldset>
-        <legend>Profile</legend>
-        <div class="container">
-            <label>BCID</label>
-            <input type="text" disabled value="<?= format_bcid($user['id']) ?>">
-        </div>
-
-        <div class="container">
-            <input type="checkbox" disabled checked="<?= $user['verified'] ?>" >
-            <label> Verified email</label>
-        </div>
-
-        <div class="container">
-            <label for="email">Email address</label>
-            <input type="email" name="email" id="email" value="<?= $user['email'] ?>">
+<div id="wrapper">
+    <div id="profile">
+        <img src="<?= get_avatar_url($user['id']); ?>">
+        <div class="details">
+            <span class="displayname"><?= $user['display_name'] ?></span>
+            <span class="bcid"><?= format_bcid($user['id']); ?></span>
+            <time datetime="<?= $user["created_date"] ?>">Since <?= $user["created_date"]; ?></time>
         </div>
+    </div>
 
-        <div class="container">
-            <label for="display_name">Display name</label>
-            <input type="text" name="display_name" id="display_name" value="<?= $user['display_name'] ?>">
-        </div>
-    </fieldset>
-    <fieldset>
-        <legend>Password</legend>
-        <p>You only need to insert values here if you're resetting your password.</p>
-        <div class="container">
-            <label for="old_password">Current password</label>
-            <input type="password" name="old_password" id="old_password">
-        </div>
-        <div class="container">
-            <label for="new_password">New password</label>
-            <input type="password" name="new_password" id="new_password">
+    <aside>
+
+        <form method="post">
+            <fieldset>
+                <legend>Profile</legend>
+                <div class="container">
+                    <label>BCID</label>
+                    <input type="text" disabled value="<?= format_bcid($user['id']) ?>">
+                </div>
+
+                <div class="container">
+                    <input type="checkbox" disabled checked="<?= $user['verified'] ?>" >
+                    <label> Verified email</label>
+                </div>
+
+                <div class="container">
+                    <label for="email">Email address</label>
+                    <input type="email" name="email" id="email" value="<?= $user['email'] ?>">
+                </div>
+
+                <div class="container">
+                    <label for="display_name">Display name</label>
+                    <input type="text" name="display_name" id="display_name" value="<?= $user['display_name'] ?>">
+                </div>
+            </fieldset>
+            <fieldset>
+                <legend>Password</legend>
+                <p>You only need to insert values here if you're resetting your password.</p>
+                <div class="container">
+                    <label for="old_password">Current password</label>
+                    <input type="password" name="old_password" id="old_password">
+                </div>
+                <div class="container">
+                    <label for="new_password">New password</label>
+                    <input type="password" name="new_password" id="new_password">
+                </div>
+                <div class="container">
+                    <label for="repeat_new_password">Repeat new password</label>
+                    <input type="password" name="repeat_new_password" id="repeat_new_password">
+                </div>
+            </fieldset>
+
+            <button class="primary" type="submit"><i class="fa-fw fa-solid fa-floppy-disk"></i> Save</button>
+        </form>
+
+        <div class="dangerzone">
+            <h2>Danger Zone</h2>
+            <p><a href="/signout" class="button"><i class="fa-fw fa-solid fa-person-through-window"></i> Sign out</a>
+                <a href="/dangerous/delete_account" class="button danger"><i class="fa-fw fa-solid fa-trash"></i> Delete account</a></p>
         </div>
-        <div class="container">
-            <label for="repeat_new_password">Repeat new password</label>
-            <input type="password" name="repeat_new_password" id="repeat_new_password">
-        </div>
-    </fieldset>
+    </aside>
+</div>
 
-    <button class="primary" type="submit"><i class="fa-fw fa-solid fa-floppy-disk"></i> Save</button>
-</form>
 
-<div class="dangerzone">
-    <h2>Danger Zone</h2>
-    <p><a href="/signout" class="button"><i class="fa-fw fa-solid fa-person-through-window"></i> Sign out</a>
-        <a href="/dangerous/delete_account" class="button danger"><i class="fa-fw fa-solid fa-trash"></i> Delete account</a></p>
-</div>
 
diff --git a/accounts_handler.php b/accounts_handler.php
index e82d637e8d099a78cbb51aeee42341856b88766e..c262cac6b5403ffae8a5b8f1ead96687083b9b9d 100644
--- a/accounts_handler.php
+++ b/accounts_handler.php
@@ -1,6 +1,43 @@
 <?php
 // This file carries functions related to accounts.
 
+function get_avatar_url($bcid):string {
+	global $pdo;
+
+	$sql = "SELECT has_pfp FROM `accounts` WHERE id = ?";
+
+	try {
+		$stmt = $pdo -> prepare($sql);
+		$stmt->execute([$bcid]);
+		$has_pfp = $stmt->fetch();
+	} catch (PDOException $e) {
+		http_response_code(500);
+		die($e);
+	}
+
+	$appendix = "default.png";
+
+	if ($has_pfp['has_pfp']) {
+		$appendix = $bcid;
+	}
+
+	return 'https://cdn.byecorps.com/id/profile/'.$appendix;
+
+}
+
+function get_display_name($bcid, $use_bcid_fallback=true):string {
+	$display_name = db_execute("SELECT display_name FROM accounts WHERE id = ?", [$bcid])['display_name'];
+	if (!empty($display_name)) {
+		return $display_name;
+	}
+
+	if ($use_bcid_fallback) {
+		return $bcid;
+	}
+
+	return "";
+}
+
 // Password resets
 const PASSWORD_RESET_VALIDITY = 300; // in seconds.
 function create_password_reset($bcid):string {
diff --git a/admin.php b/admin.php
index 5151a282d26137a66267a0768199466200ab5b73..d92d0298bbf00ec60899a2c1b416adbb60fc2cb2 100644
--- a/admin.php
+++ b/admin.php
@@ -1,16 +1,23 @@
-<?php
-?>
-
 <h1>Admin panel</h1>
 <p>If you're not Bye and you're seeing this, I'm fucked!</p>
 
 <h2>Accounts</h2>
 <ul>
 	<li>
-		<a href="/admin/accounts">List of accounts</a>
+		<a href="/admin/list/accounts">List of accounts</a>
 	</li>
 </ul>
 
+<h2>Apps</h2>
+<ul>
+    <li>
+        <a href="/admin/list/apps">List of applications</a>
+    </li>
+    <li>
+        <a href="/admin/create/app">App creator</a>
+    </li>
+</ul>
+
 <h2>Init</h2>
 <ul>
 	<li>
diff --git a/admin_accounts.php b/admin_accounts.php
index 483fcf797020092e202304882888ac826d391334..3a73e80b4e98000b0c62c831306ebb992b38771c 100644
--- a/admin_accounts.php
+++ b/admin_accounts.php
@@ -1,9 +1,4 @@
-<?php 
-
-if ($_SESSION['id'] != "281G3NV") {
-    http_response_code(401);
-    die("<img src='https://http.cat/401.jpg'>");
-}
+<?php
 
 $sql = "SELECT * FROM accounts";
 $result = $pdo-> query($sql);
@@ -12,14 +7,12 @@ if (!$result) {
     die("<img src='https://http.cat/500.jpg'>");
 }
 
-
 $count_req = $pdo->query("SELECT COUNT(*) FROM accounts");
 $count = $count_req->fetchColumn();
 
 
 ?>
 
-<h2 class="subheading">Admin</h2>
 <h1>Accounts</h1>
 
 <p>There is currently <?= $count ?> accounts registered.</p>
diff --git a/admin_apps.php b/admin_apps.php
new file mode 100644
index 0000000000000000000000000000000000000000..8ae771aff8f416aa93a15f12d7e6c4bff4237859
--- /dev/null
+++ b/admin_apps.php
@@ -0,0 +1,23 @@
+<?php
+
+$result = db_query("SELECT * FROM apps");
+
+$count_req = db_query("SELECT COUNT(*) FROM apps");
+$count = $count_req->fetchColumn();
+
+?>
+<h1>Apps</h1>
+
+<p>There is currently <?= $count ?> apps registered.</p>
+
+
+<ul>
+    <?php
+    foreach ($result as $row) {
+        echo "<li><pre>";
+        print_r($row);
+		echo "</li>";
+    }
+    ?>
+</ul>
+
diff --git a/admin_apps_create.php b/admin_apps_create.php
new file mode 100644
index 0000000000000000000000000000000000000000..bc3bec97b7531471e3155d7918b2187ea5739d08
--- /dev/null
+++ b/admin_apps_create.php
@@ -0,0 +1,39 @@
+<?php
+
+function generate_app_id(): int
+{
+	return mt_rand(100000000, 999999999);
+}
+
+function check_app_id($app_id): bool
+{
+    $app = db_execute("SELECT * FROM apps WHERE id = ? LIMIT 1", [$app_id]);
+    return empty($app);
+}
+
+if ($_SERVER['REQUEST_METHOD'] == "POST") {
+    $app_id = generate_app_id();
+    db_execute("INSERT INTO apps (id, owner_id, title, description) VALUES (?, ?, ?, ?)", [$app_id, $_POST['owner'], $_POST['title'], $_POST['description']]);
+	die();
+}
+
+?>
+
+<h1>App Creator</h1>
+
+<form method="post">
+	<label for="title">Title</label>
+	<input type="text" name="title" id="title">
+	<label for="description">Description</label>
+	<textarea name="description" id="description" cols="30" rows="10"></textarea>
+	<label for="owner">App owner</label>
+	<select name="owner" id="owner">
+		<?php
+			$users = db_query("SELECT * FROM accounts");
+			foreach ($users as $row) {
+				echo "<option value='".$row['id']."'>".get_display_name($row['id'])." (".$row['id'].") </option>";
+			}
+		?>
+	</select>
+	<button type="submit">Create app</button>
+</form>
\ No newline at end of file
diff --git a/bcid.svg b/bcid.svg
new file mode 100644
index 0000000000000000000000000000000000000000..8911001a67c1a5b078bc56a3f1c5c2c4aa5931d3
--- /dev/null
+++ b/bcid.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 32 32" shape-rendering="crispEdges">
+    <metadata>Made with Pixels to Svg https://codepen.io/shshaw/pen/XbxvNj</metadata>
+    <path stroke="#cbdbfc" d="M2 4h28M2 5h1M29 5h1M0 6h3M29 6h3M0 7h1M31 7h1M0 8h1M31 8h1M0 9h1M31 9h1M0 10h1M31 10h1M0 11h1M31 11h1M0 12h1M31 12h1M0 13h1M31 13h1M0 14h1M31 14h1M0 15h1M31 15h1M0 16h1M31 16h1M0 17h1M31 17h1M0 18h1M31 18h1M0 19h1M31 19h1M0 20h1M31 20h1M0 21h1M31 21h1M0 22h1M31 22h1M0 23h1M31 23h1M0 24h1M31 24h1M0 25h3M29 25h3M2 26h1M29 26h1M2 27h28" />
+    <path stroke="#ffffff" d="M3 5h26M3 6h26M1 7h30M1 8h30M1 9h30M1 10h6M8 10h1M10 10h6M21 10h1M25 10h6M1 11h7M9 11h9M19 11h3M23 11h2M26 11h5M1 12h6M12 12h6M19 12h3M23 12h2M26 12h5M1 13h6M12 13h6M19 13h3M23 13h2M26 13h5M1 14h6M12 14h6M19 14h3M23 14h2M26 14h5M1 15h6M12 15h6M19 15h3M23 15h2M26 15h5M1 16h15M21 16h1M25 16h6M1 17h6M12 17h19M1 18h5M13 18h18M1 19h5M13 19h3M24 19h1M26 19h5M1 20h5M13 20h18M1 21h5M13 21h3M17 21h1M21 21h1M26 21h5M1 22h30M1 23h30M1 24h30M3 25h26M3 26h26" />
+    <path stroke="#000000" d="M7 10h1M9 10h1M16 10h5M22 10h3M8 11h1M18 11h1M22 11h1M25 11h1M7 12h5M18 12h1M22 12h1M25 12h1M7 13h5M18 13h1M22 13h1M25 13h1M7 14h5M18 14h1M22 14h1M25 14h1M7 15h5M18 15h1M22 15h1M25 15h1M16 16h5M22 16h3M7 17h5M6 18h7M6 19h7M16 19h8M25 19h1M6 20h7M6 21h7M16 21h1M18 21h3M22 21h4" />
+</svg>
\ No newline at end of file
diff --git a/composer.json b/composer.json
index b051582fb837ddee240f8d0d98a4841c30225cc5..7e441b34ff42f544cd93c87640f7c2914adc8602 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,9 @@
 {
     "require": {
         "sentry/sdk": "^4.0",
-        "phpmailer/phpmailer": "^6.8"
+        "phpmailer/phpmailer": "^6.8",
+        "erusev/parsedown": "^1.7",
+        "erusev/parsedown-extra": "^0.8.1",
+        "kornrunner/blurhash": "^1.2"
     }
 }
diff --git a/composer.lock b/composer.lock
index 426f4ed2d3c803724bc24de54539cd145041c8cd..1f2897815808ceccf9e2e9bde58e3c1d0ca6f7b6 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,109 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "909990e5dfeb8b9d8bb3572e0f7de24e",
+    "content-hash": "9baad85ecd1e18878c3fe588203305a1",
     "packages": [
+        {
+            "name": "erusev/parsedown",
+            "version": "1.7.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/erusev/parsedown.git",
+                "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
+                "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Parsedown": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Emanuil Rusev",
+                    "email": "hello@erusev.com",
+                    "homepage": "http://erusev.com"
+                }
+            ],
+            "description": "Parser for Markdown.",
+            "homepage": "http://parsedown.org",
+            "keywords": [
+                "markdown",
+                "parser"
+            ],
+            "support": {
+                "issues": "https://github.com/erusev/parsedown/issues",
+                "source": "https://github.com/erusev/parsedown/tree/1.7.x"
+            },
+            "time": "2019-12-30T22:54:17+00:00"
+        },
+        {
+            "name": "erusev/parsedown-extra",
+            "version": "0.8.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/erusev/parsedown-extra.git",
+                "reference": "91ac3ff98f0cea243bdccc688df43810f044dcef"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/91ac3ff98f0cea243bdccc688df43810f044dcef",
+                "reference": "91ac3ff98f0cea243bdccc688df43810f044dcef",
+                "shasum": ""
+            },
+            "require": {
+                "erusev/parsedown": "^1.7.4"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "ParsedownExtra": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Emanuil Rusev",
+                    "email": "hello@erusev.com",
+                    "homepage": "http://erusev.com"
+                }
+            ],
+            "description": "An extension of Parsedown that adds support for Markdown Extra.",
+            "homepage": "https://github.com/erusev/parsedown-extra",
+            "keywords": [
+                "markdown",
+                "markdown extra",
+                "parsedown",
+                "parser"
+            ],
+            "support": {
+                "issues": "https://github.com/erusev/parsedown-extra/issues",
+                "source": "https://github.com/erusev/parsedown-extra/tree/0.8.x"
+            },
+            "time": "2019-12-30T23:20:37+00:00"
+        },
         {
             "name": "guzzlehttp/psr7",
             "version": "2.6.1",
@@ -181,6 +282,54 @@
             },
             "time": "2021-10-08T21:21:46+00:00"
         },
+        {
+            "name": "kornrunner/blurhash",
+            "version": "v1.2.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/kornrunner/php-blurhash.git",
+                "reference": "bc8a4596cb0a49874f0158696a382ab3933fefe4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/kornrunner/php-blurhash/zipball/bc8a4596cb0a49874f0158696a382ab3933fefe4",
+                "reference": "bc8a4596cb0a49874f0158696a382ab3933fefe4",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.3|^8.0"
+            },
+            "require-dev": {
+                "ext-gd": "*",
+                "ocramius/package-versions": "^1.4|^2.0",
+                "phpstan/phpstan": "^0.12",
+                "phpunit/phpunit": "^9",
+                "vimeo/psalm": "^4.3"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "kornrunner\\Blurhash\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Boris Momčilović",
+                    "email": "boris.momcilovic@gmail.com"
+                }
+            ],
+            "description": "Pure PHP implementation of Blurhash",
+            "homepage": "https://github.com/kornrunner/php-blurhash",
+            "support": {
+                "issues": "https://github.com/kornrunner/php-blurhash/issues",
+                "source": "https://github.com/kornrunner/php-blurhash.git"
+            },
+            "time": "2022-07-13T19:38:39+00:00"
+        },
         {
             "name": "phpmailer/phpmailer",
             "version": "v6.8.1",
diff --git a/credits.html b/credits.html
new file mode 100644
index 0000000000000000000000000000000000000000..1b46f189b6cde9d3eafd2436c2f66ed12ba9b164
--- /dev/null
+++ b/credits.html
@@ -0,0 +1,206 @@
+<style>
+
+	main {
+		display: flex;
+		flex-direction: column;
+	}
+
+	#credits {
+		max-height: 80vh;
+		width: 100%;
+		flex:1;
+
+		scroll-behavior: smooth;
+
+		overflow: hidden;
+
+	}
+
+	#credits > :last-child {
+		margin-top: 40vh;
+		margin-bottom: 65vh;
+	}
+
+	#credits .title {
+		position: sticky;
+		top: 0;
+
+		padding-top: 2rem;
+		padding-bottom: 2rem;
+
+		background: linear-gradient(to top, #ffffff00, var(--background) 2rem);
+
+		display: flex;
+		align-items: end;
+		gap: 0.5rem;
+	}
+
+	@media screen and (prefers-color-scheme: dark) {
+		#credits .title {
+            background: linear-gradient(to top, #12121200, var(--background-dark) 2rem);
+		}
+	}
+
+	#credits .title > * {
+		margin: 0;
+	}
+
+	#credits .spacer {
+		display: block;
+		/*position: relative;*/
+		height: 65vh;
+
+		transition: height 0.5s ease-in-out;
+	}
+
+	#fps {
+		position: fixed;
+		bottom: .5rem;
+		left: .5rem;
+
+		font-family: monospace;
+	}
+
+	.fa-ul {
+		position: relative;
+		z-index: -10;
+	}
+</style>
+
+<div id="fps"></div>
+
+<div id="credits">
+	<div class="title">
+		<h1 class="logo"><span class="bc-1">Bye</span><span class="bc-2">Corps</span><span class="bc-3"> ID</span></h1>
+		<span class="subtitle">v. <?= current_git_commit() ?></span>
+	</div>
+
+	<p>ByeCorps ID is a <a href="https://byecorps.com">ByeCorps</a> service created by <a href="https://byemc.xyz">Bye</a>. It wouldn't be possible without the work of other amazing people.</p>
+	<button id="start">Show credits.</button>
+
+	<div class="spacer"></div>
+
+	<h2>Credits</h2>
+	<ul>
+		<li><a href="https://bye.omg.lol">Bye</a>, who programmed this entire thing.</li>
+		<li><a href="https://adam.omg.lol">Adam Newbold</a> for writing the code this site depends on for routing</li>
+		<li>PHP, the language it's built in.</li>
+		<li>Composer, to keep all the libs together.</li>
+		<li>Caddy, the webserver it's usually running on.</li>
+		<li>MariaDB, the MySQL server.</li>
+		<li>PhpStorm by JetBrains and Visual Studio Code by Microsoft, both of which were used to make this service.</li>
+	</ul>
+
+	<h2>Music</h2>
+	<ul class="fa-ul music">
+		<li><span class="fa-li fa-fw fa-solid fa-compact-disc fa-spin"></span> <strong>Now playing</strong>:<br>"Screen Saver" Kevin MacLeod (<a href="https://incompetech.com">incompetech.com</a>)<br>
+			Licensed under Creative Commons: By Attribution 4.0 License<br>
+			<a href="http://creativecommons.org/licenses/by/4.0/">http://creativecommons.org/licenses/by/4.0/</a></li>
+		<li><span class="fa-li fa-fw fa-solid fa-music"></span>"Electrodoodle" Kevin MacLeod (<a href="https://incompetech.com">incompetech.com</a>)<br>
+Licensed under Creative Commons: By Attribution 4.0 License <br>
+<a href="http://creativecommons.org/licenses/by/4.0/">http://creativecommons.org/licenses/by/4.0/</a></span></li>
+	</ul>
+
+	<h2>Third-party libraries</h2>
+
+	<p>ByeCorps ID relies on the following third-party libraries:</p>
+	<ul>
+		<li><code>sentry/sdk</code> for diagnostics.</li>
+		<li><code>phpmailer/phpmailer</code> for email.</li>
+		<li><code>erusev/parsedown</code> and <code>erusev/parsedown-extra</code> for parsing Markdown right in PHP.</li>
+		<li><code>kornrunner/blurhash</code> for generating blurhashes.</li>
+	</ul>
+
+	<p>Getting the FPS of your display powered by <a href="https://stackoverflow.com/a/5111475">this StackOverflow answer</a>.</p>
+	<a id="final"></a>
+
+	<p>Thank you for using ByeCorps ID.</p>
+</div>
+
+<script>
+	//autoscroll
+
+	const credits = document.getElementById("credits");
+	const finalLine = document.getElementById("final");
+	credits.scrollTop = 0;
+	let fakeScrollTop = 1;
+	let speed = 15 // pixels per second
+	// speed = 100
+
+	const music = new Audio("https://cdn.byecorps.com/id/music/Screen Saver.mp3");
+	music.loop = true;
+	const silence = new Audio("https://cdn.byecorps.com/id/music/500-milliseconds-of-silence.mp3");
+
+	var filterStrength = 60;
+	var frameTime = 0, lastLoop = new Date, thisLoop;
+
+	let fps = 60;
+
+	setInterval(function(){
+		document.getElementById("fps").innerText = (1000/frameTime).toFixed(0) + ` / Speed ${speed}`;
+		fps = 1000/frameTime;
+	},1000);
+
+	function setFPS() {
+		var thisFrameTime = (thisLoop=new Date) - lastLoop;
+		frameTime+= (thisFrameTime - frameTime) / filterStrength;
+		lastLoop = thisLoop;
+
+		requestAnimationFrame(setFPS);
+	}
+
+
+	function main() {
+		let frameFraction = 1 / fps;
+		fakeScrollTop += speed * frameFraction;
+		credits.scrollTop = Math.floor(fakeScrollTop);
+
+		if (credits.scrollTop >= credits.scrollTopMax) {
+			music.loop = false;
+			setTimeout(function () {
+				credits.style.overflowY = "auto";
+				credits.scrollTo(0, 0);
+				document.getElementsByClassName("spacer")[0].style.height = 0;
+			}, 2500);
+		} else {
+			requestAnimationFrame(main);
+		}
+
+	}
+
+	function startMain() {
+		document.getElementById("start").style.display = "none";
+		music.play();
+		setTimeout(function () {
+			requestAnimationFrame(main);
+		}, 500);
+	}
+
+	async function checkForAutoplay() {
+		try {
+			await silence.play();
+			// so if that works, just start the credits.
+			document.getElementById("start").style.display = "none";
+
+			startMain();
+		} catch (e) {
+			document.getElementById("start").style.display = "block";
+
+			console.error(e);
+		}
+	}
+
+	document.getElementById("start").onclick = startMain;
+
+	requestAnimationFrame(setFPS);
+
+	checkForAutoplay();
+
+
+
+
+
+
+
+
+</script>
diff --git a/database.php b/database.php
new file mode 100644
index 0000000000000000000000000000000000000000..bd1fcbd9487b4e596f9438e2b0d3269c225106a0
--- /dev/null
+++ b/database.php
@@ -0,0 +1,17 @@
+<?php
+// Functions for interacting with the database. Requires PDO is initialised at $pdo.
+
+function db_execute($sql, $variables=[]) {
+	global $pdo;
+
+	$stmt = $pdo->prepare($sql);
+	$stmt->execute($variables);
+	return $stmt->fetch();
+
+}
+
+function db_query($sql) {
+	global $pdo;
+
+	return $pdo->query($sql);
+}
diff --git a/docs.php b/docs.php
new file mode 100644
index 0000000000000000000000000000000000000000..83fcf34fe5fd4d56016b7b26333e6608a456cc41
--- /dev/null
+++ b/docs.php
@@ -0,0 +1,41 @@
+<?php
+
+$Parsedown = new ParsedownExtra();
+
+// Loads the relevant file from /docs
+
+$file_path = __DIR__ . $path;
+
+/* If it's a dir, check for index.md
+If it's a file.md, load it. */
+
+// First check if the file exists as a directory
+if (file_exists($file_path)) {
+	// Check if the index.md exists
+	if (file_exists($file_path . '/index.md')) {
+		// And if so, make that the file path.
+		$file_path .= "/index.md";
+	} else { // If not, 404.
+		http_response_code(404);
+		include("404.html");
+		include("footer.php");
+		die($file_path);
+	}
+} elseif (file_exists($file_path . '.md')) { // Check if $file.md exists.
+	// And if so, make that the file path.
+	$file_path .= '.md';
+}  else { // now if not, just show the standard 404 page.
+	http_response_code(404);
+	include("404.html");
+	include("footer.php");
+	die($file_path);
+}
+
+echo $file_path;
+echo "<br>";
+
+$file = fopen($file_path, "r");
+$file_contents = fread($file, filesize($file_path));
+fclose($file);
+
+echo $Parsedown->parse($file_contents);
diff --git a/docs/hosting/configphp.md b/docs/hosting/configphp.md
new file mode 100644
index 0000000000000000000000000000000000000000..a9ec9d32f511df08ca49e5cf4c5d04c4bc54cf3d
--- /dev/null
+++ b/docs/hosting/configphp.md
@@ -0,0 +1,17 @@
+# config.php
+
+Here's all the variables in `config.php`:
+
+|        Variable | Description / Example                                                                                                                                             |
+|----------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+|      `BASE_URL` | The url generated for emails. Should be "https://id.byecorps.com" in production.                                                                                  |
+|    `DB_ADDRESS` | The address for the database. Usually `localhost`                                                                                                                 |
+|   `DB_USERNAME` | Username for connecting to the database.                                                                                                                          |
+|   `DB_PASSWORD` | Password for the database.                                                                                                                                        |
+|   `DB_DATABASE` | The database to connect to.                                                                                                                                       |
+|   `PDO_OPTIONS` | `<br/>const PDO_OPTIONS = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false,];` |
+|        `DB_DSN` | `mysql:host='.DB_ADDRESS.';dbname='.DB_DATABASE.';charset=utf8mb4`, for PDO.                                                                                      |
+|   `SENTRY_DSN ` | Used for Sentry.                                                                                                                                                  |
+|     `MAIL_HOST` | SMTP host for emails                                                                                                                                              |
+| `MAIL_USERNAME` | SMTP username                                                                                                                                                     |
+| `MAIL_PASSWORD` | SMTP password                                                                                                                                                     |
\ No newline at end of file
diff --git a/docs/hosting/index.md b/docs/hosting/index.md
new file mode 100644
index 0000000000000000000000000000000000000000..8d7e14a3dbc08205ef2dbf1c780c37c2f3391725
--- /dev/null
+++ b/docs/hosting/index.md
@@ -0,0 +1,7 @@
+# Hosting
+
+Here's everything you need to know about hosting ByeCorps ID. This mainly covers the `config.php` file.
+
+## Contents 
+
+- [config.php](hosting/configphp)
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000000000000000000000000000000000000..cdf3ce4a56ff4547310f6186c0400c5810aae40b
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1 @@
+# ByeCorps ID docs
\ No newline at end of file
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..55f8337278fc94f2ad9f2c340658d36f65ecf594
Binary files /dev/null and b/favicon.ico differ
diff --git a/favicon.svg b/favicon.svg
new file mode 100644
index 0000000000000000000000000000000000000000..bc9d79088f265d7081295243561fc7d39a403200
--- /dev/null
+++ b/favicon.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   viewBox="0 -0.5 16 16"
+   shape-rendering="crispEdges"
+   version="1.1"
+   id="svg1"
+   sodipodi:docname="favicon.svg"
+   width="16"
+   height="16"
+   inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs1" />
+  <sodipodi:namedview
+     id="namedview1"
+     pagecolor="#ffffff"
+     bordercolor="#000000"
+     borderopacity="0.25"
+     inkscape:showpageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1"
+     inkscape:zoom="30.692308"
+     inkscape:cx="0.96115288"
+     inkscape:cy="9.8070175"
+     inkscape:window-width="1920"
+     inkscape:window-height="973"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg1" />
+  <metadata
+     id="metadata1">Made with Pixels to Svg https://codepen.io/shshaw/pen/XbxvNj</metadata>
+  <path
+     stroke="#000000"
+     d="m 4.0000001,0.16666691 h 1.3333334 m 1.3333334,0 h 2.6666667 m -4,1.33333329 H 6.6666669 M 4.0000001,2.8333335 H 11.999999 M 4.0000001,4.1666669 H 11.999999 M 4.0000001,5.5000002 H 11.999999 M 4.0000001,6.8333335 H 11.999999 M 4.0000001,9.5000002 H 11.999999 M 2.6666669,10.833333 H 13.333333 M 2.6666669,12.166667 H 13.333333 M 2.6666669,13.5 H 13.333333 M 2.6666669,14.833333 H 13.333333"
+     id="path1"
+     style="stroke-width:1.33333" />
+  <style
+     id="style1">@media (prefers-color-scheme:dark){:root{filter:invert(100%)}}</style>
+</svg>
diff --git a/footer.php b/footer.php
index 5ea6a24aaca9bcf975dee9ff8859fcffdb29bcc3..5740daeb8568f93348447b6ddcde9fb8efbb45fc 100644
--- a/footer.php
+++ b/footer.php
@@ -1,3 +1,3 @@
 <footer>
-    &COPY; ByeCorps <?php echo(date("Y")); ?>
+    &COPY; ByeCorps <?php echo(date("Y")); ?> <a href="/credits">Credits</a>
 </footer>
\ No newline at end of file
diff --git a/forgot_password.php b/forgot_password.php
index 3795b61628a5a118bc0fda3d3d8a160acd159595..f96d8ee59f87faa2d6f3c38162cfbda277ebae6a 100644
--- a/forgot_password.php
+++ b/forgot_password.php
@@ -3,7 +3,7 @@ use PHPMailer\PHPMailer\SMTP;
 use PHPMailer\PHPMailer\PHPMailer;
 use PHPMailer\PHPMailer\Exception;
 
-if (isset($_SESSION['auth'])) {
+if ($_SESSION['auth']) {
     header('Location: /account');
 }
 
diff --git a/head.php b/head.php
index 51e19924a2a6d798280ce355b083dd52fb743085..2d5c6551c5b885fd9e1a649c95bdd8f8be085702 100644
--- a/head.php
+++ b/head.php
@@ -7,5 +7,7 @@
 ></script>
 <title><?php if (isset($doc_title)) { echo $doc_title." | "; } ?>ByeCorps ID</title>
 
+<link rel="shortcut icon" href="favicon.svg" type="image/svg">
+
 <link rel="stylesheet" href="/styles/global.css">
 <link rel="stylesheet" href="/fontawesome/css/all.css">
\ No newline at end of file
diff --git a/header.php b/header.php
index 7cae72e9a1c9d2c932df49c5f5795bee60002ad0..81242f3b14dd9c71f6015374eaa30f3d170841e5 100644
--- a/header.php
+++ b/header.php
@@ -2,8 +2,7 @@
 
 <?php 
 
-if (!isset($_SESSION['auth'])) goto skip_auth;
-
+if (!$_SESSION['auth']) goto skip_auth;
 
 if ($_SESSION['auth']) {
     $sql = "SELECT display_name FROM accounts WHERE id = ?";
@@ -12,7 +11,7 @@ if ($_SESSION['auth']) {
     $name = $stmt->fetchColumn();
 }
 
-if ($name == '') {
+if (empty($name)) {
     $name = '<code class=bcid>'.format_bcid($_SESSION['id']).'</code>';
 }
 
@@ -20,18 +19,20 @@ skip_auth:
 
 ?>
 
-<link rel="stylesheet" href="./styles/global.css">
-<link rel="stylesheet" href="./fontawesome/css/all.css">
+
 
 <header>
     <div class="start">
-        <a href="/" id="sitetitle"><span class="bc-1">Bye</span><span class="bc-2">Corps</span><span class="bc-3"> ID</span></a></div>
+        <a href="/" id="sitetitle">
+            <span class="bc-1">Bye</span><span class="bc-2">Corps</span><span class="bc-3"> ID</span>
+        </a></div>
 
     <div class="end">
 
-        <?php if (!isset($_SESSION['auth'])) goto signed_out; ?>
+        <?php if (!$_SESSION['auth']) goto signed_out; ?>
+        <?php if ($user['is_admin']) echo "<a href='/admin'>Admin panel</a>"; ?>
         <div class="loggedin">
-            <a href="/account" class="account">Hey there, <?= $name ?>! <i class="fa-solid fa-fw fa-angle-right"></i></a>
+            <a href="/account" class="account">Hey there, <?= $name ?>!</a>
         </div>
         <?php signed_out: ?>
 
diff --git a/index.php b/index.php
index 29c6d78e54aafd9ed72a1688ae231c0413bf62f2..843ce30d869bc3c92e529361dc9e55c393996903 100644
--- a/index.php
+++ b/index.php
@@ -1,24 +1,35 @@
 <?php
-require __DIR__ . '/vendor/autoload.php';
+require_once __DIR__ . '/vendor/autoload.php';
 
 session_start();
 
+use kornrunner\Blurhash\Blurhash;
+
+if (empty($_SESSION)) {
+    $_SESSION['auth'] = false;
+}
+
 include("config.php");
 
 $pdo = new PDO(DB_DSN, DB_USERNAME, DB_PASSWORD, PDO_OPTIONS);
 
-include("id_handler.php");
 include("time_handler.php");
 require "misc_functions.php";
+require "database.php";
+include("id_handler.php");
 include("accounts_handler.php");
 
-//\Sentry\init([
-//    'dsn' => SENTRY_DSN,
-//    // Specify a fixed sample rate
-//    'traces_sample_rate' => 1.0,
-//    // Set a sampling rate for profiling - this is relative to traces_sample_rate
-//    'profiles_sample_rate' => 1.0,
-//]);
+if ($_SESSION['auth']) {
+	$user = db_execute("SELECT * FROM `accounts` WHERE id = ? LIMIT 1", [$_SESSION['id']]);
+}
+
+\Sentry\init([
+    'dsn' => SENTRY_DSN,
+    // Specify a fixed sample rate
+    'traces_sample_rate' => 1.0,
+    // Set a sampling rate for profiling - this is relative to traces_sample_rate
+    'profiles_sample_rate' => 1.0,
+]);
 
 function does_variable_exists( $variable ) {
     return (isset($$variable)) ? "true" : "false";
@@ -56,7 +67,10 @@ $paths = array(
     "/" => ["landing.php"],
     "/admin" => ['admin.php'],
     "/admin/init/database" => ["admin_initdatabase.php"],
-    "/admin/accounts" => ["admin_accounts.php"],
+    "/admin/list/accounts" => ["admin_accounts.php"],
+    "/admin/list/apps" => ["admin_apps.php"],
+    "/admin/create/app" => ["admin_apps_create.php"],
+
     "/account" => ["account.php", "Your account"],
     "/signin" => ["signin.php", "Sign in"],
     "/signup" => ["signup.php", "Sign up"],
@@ -64,6 +78,9 @@ $paths = array(
     "/forgot/password" => ["forgot_password.php", "Forgot password"],
     "/admin/signinas" => ["signinas.php"],
     "/reset/password" => ["reset_password.php", "Reset password"],
+    "/docs" => ["docs.php", "Docs"],
+    "/credits" => ["credits.html", "Credits"],
+    "/profile" => ["profile.php", "Profile"],
 );
 
 if (isset($paths[$path])) {
@@ -91,12 +108,19 @@ else {
         <?php 
 
         if (!empty($uri)) {
-            print_r ($uri);
-	        if ($uri[0] == "admin" && $_SESSION['id'] != "281G3NV") {
+//            print_r ($uri);
 
+            if ($uri[0] == "admin") {
+                echo "<h2 class=\"subheading\">Admin</h2>";
+            }
+
+	        if ($uri[0] == "admin" && !$user['is_admin']) {
 		        http_response_code(401);
 		        die("<img src='https://http.cat/401.jpg'>");
 	        }
+            if ($uri[0] == "docs") {
+                $include = "docs.php";
+            }
         }
 
 
diff --git a/landing.php b/landing.php
index c60788c252d7307f596c10918f74e57fa000f71a..6e59f94a6ac4b5fe7cf9a5481ced1200f3341640 100644
--- a/landing.php
+++ b/landing.php
@@ -1,5 +1,6 @@
 <div class="hero">
     <div class="hero-text">
+        <img src="https://byecorps.b-cdn.net/id/bcid.svg" alt="ByeCorps ID Logo" class="logo">
         <h1><span class="bc-1">Bye</span><span class="bc-2">Corps</span><span class="bc-3"> ID</span></h1>
         <p>Log into ByeCorps and beyond with a single ID.</p>
         <!-- <p><input type="email" name="loginEmail" id="loginEmail" placeholder="Email" /></p> -->
diff --git a/misc_functions.php b/misc_functions.php
index 149d3f44f82de03ecdd93b22cb554e2ed01571ce..1164dad2e9916ee611f9cbf047f1f82ae328e9b7 100644
--- a/misc_functions.php
+++ b/misc_functions.php
@@ -7,4 +7,8 @@ function generateRandomString($length = 10) {
 		$randomString .= $characters[random_int(0, $charactersLength - 1)];
 	}
 	return $randomString;
+}
+
+function current_git_commit():string {
+	return trim(exec("git log --pretty=\"%h\" -n1 HEAD 2>&1"));
 }
\ No newline at end of file
diff --git a/profile.php b/profile.php
new file mode 100644
index 0000000000000000000000000000000000000000..e0fa5050d8548e0a0c85b358b66f692a2ef5efda
--- /dev/null
+++ b/profile.php
@@ -0,0 +1,64 @@
+
+<link rel="stylesheet" href="/styles/profiles.css">
+
+<?php
+
+if (!$_SESSION['auth']) {
+    header('Location: /signin?callback=/profile');
+}
+
+$profile = db_execute("SELECT * FROM `profiles` WHERE id = ? LIMIT 1", [$user['id']]);
+
+if (empty($profile)) {
+    $profile = [
+            "id" => "0000000",
+            "public_display_name" => false,
+            "public_avatar" => false,
+            "description" => null,
+    ];
+}
+
+$avatar = "https://cdn.byecorps.com/id/profile/default.png";
+$display_name = "";
+
+if ($_SESSION['id'] != $profile['id']) {
+    if ($profile['public_avatar']) {
+        $avatar = get_avatar_url($profile['id']);
+    }
+    if ($profile['public_display_name']) {
+        $display_name = get_display_name($profile['id'], false);
+    }
+} else {
+    $avatar = get_avatar_url($profile['id']);
+    $display_name = get_display_name($profile['id'], false);
+}
+
+
+
+?>
+
+<div id="profile">
+    <img src="<?= $avatar ?>" class="avatar" alt="Avatar">
+    <div class="info">
+        <div class="displayname"><?= $display_name ?></div>
+        <div class="bcid"><?= $profile['id'] ?></div>
+    </div>
+</div>
+
+<div id="details">
+    <div id="badges">
+        <h2>Badges</h2>
+    </div>
+
+    <div id="info">
+        <h2>Info</h2>
+
+        <table>
+            <tr>
+                <th>Joined</th>
+                <td><?= $user['created_date'] ?></td>
+            </tr>
+        </table>
+
+    </div>
+</div>
diff --git a/signin.php b/signin.php
index 03614f1a8e31aa8199e57f96314a456bceda8b8b..6de8f741c52a6ce6bd4d8db377db6631b1d77308 100644
--- a/signin.php
+++ b/signin.php
@@ -28,7 +28,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
         if (isset($query['callback'])) {
             header("Location: ".$query['callback']);
         } else {
-            header("Location: /account");
+            header("Location: /profile");
+//            echo "<pre>";
+//            var_dump($user);
+//            var_dump($_SESSION);
+//            die();
         }
 
         exit;
@@ -52,5 +56,5 @@ if (isset($message)) {
 </form>
 
 <p class="center">
-    <!--<a href="/forgot_password">Forgot password?</a> ·--> New? <a href="/register">Register</a> for a ByeCorps ID.
+    <a href="/forgot/password">Forgot password?</a> &bull; New? <a href="/register">Register</a> for a ByeCorps ID.
 </p>
\ No newline at end of file
diff --git a/signout.php b/signout.php
index fb565e57360646f122fa4d7f6a11fbaf650d7245..c128d187c90ee6a85e9f665ce23bfa91ebfd7d60 100644
--- a/signout.php
+++ b/signout.php
@@ -1,5 +1,7 @@
 <?php 
 
+$_SESSION['id'] = null;
+$_SESSION['auth'] = false;
 session_destroy();
 
 ?>
diff --git a/styles/colours.css b/styles/colours.css
index 61ee27517e8a278c46ab60795184abbb0d9c2022..f11cb9708f1cd8b01925623b41f061fc3d7058de 100644
--- a/styles/colours.css
+++ b/styles/colours.css
@@ -19,10 +19,21 @@
     --red-8: #e03131;
     --green-5: #51cf66;
     --green-8: #2f9e44;
+    --grey-0: #f8f9fa;
+    --grey-2: #e9ecef;
+    --grey-8: #343a40;
+    --grey-9: #212529;
+
+    --background: white;
+    --background-dark: #121212;
 
     color-scheme: light dark;
 }
 
+html {
+    background: var(--background, white);
+}
+
 button, .button {
     background-color: #1f302b40;
     color: var(--white);
@@ -47,7 +58,7 @@ header a {
     color: var(--dark-slate-gray);
 }
 
-input {
+input, textarea {
     all: unset;
     padding: 1em;
     text-align: start;
@@ -91,6 +102,10 @@ input[data-com-onepassword-filled="dark"] {
 }
 
 @media screen and (prefers-color-scheme: dark) {
+    html {
+        background: var(--background-dark, #121212);
+    }
+
     button.primary, .button.primary {
         color: var(--flax);
         background-color: var(--dark-slate-gray);
@@ -105,7 +120,7 @@ input[data-com-onepassword-filled="dark"] {
         color: var(--flax);
     }
 
-    input {
+    input, textarea {
 
         background-color: #2c2c2c77;
     }
diff --git a/styles/design.css b/styles/design.css
index 27cc92ba8293d0b6cc5407bb65952ec6bd90d492..acdac55c5340f0e32ad8b62a212f6581ee661db9 100644
--- a/styles/design.css
+++ b/styles/design.css
@@ -21,7 +21,7 @@ header a {
 
 /* inputs */
 
-input {
+input, textarea {
   all: unset;
   padding: 1em;
   text-align: start;
@@ -45,3 +45,34 @@ input:disabled {
   opacity: 0.75;
   cursor: not-allowed;
 }
+
+table {
+    background-color: var(--grey-2);
+    width: 100%;
+    font-size: 1.1rem;
+    padding: .5rem;
+    border-radius: 1.0rem;
+}
+
+table > tbody > tr {
+    display: grid;
+    align-items: center;
+    gap: 0.5rem;
+    grid-template-columns: auto 1fr;
+}
+
+table > tbody > tr > td {
+    background: var(--grey-0);
+    border-radius: .5em;
+    padding: .5em;
+}
+
+@media screen and (prefers-color-scheme: dark) {
+    table {
+        background-color: var(--grey-9);
+    }
+
+    table > tbody > tr > td {
+        background: var(--grey-8);
+    }
+}
diff --git a/styles/layout.css b/styles/layout.css
index 6e4f2b445b7cde423835c6a36ac0150132191352..03d20f2a0b881c6c8df2e87b83b25253943c7639 100644
--- a/styles/layout.css
+++ b/styles/layout.css
@@ -4,6 +4,8 @@ body {
 
     display: flex;
     flex-direction: column;
+
+    min-height: 100vh;
 }
 
 header {
@@ -20,6 +22,7 @@ header .start {
     display: flex;
 
     gap: 1rem;
+
 }
 
 header .middle {
@@ -30,12 +33,21 @@ header .middle {
 header .end {
     margin-left: auto;
     display: flex;
+    gap: 1rem;
+}
+
+header .avatar {
+    height: 1.5em;
 }
 
 main {
     height: 100%;
     flex: 1;
     padding: 1rem 1rem;
+
+    max-width: 1500px;
+    margin: auto;
+    width: 100%;
 }
 
 footer {
@@ -62,7 +74,7 @@ legend {
 }
 
 form:has(fieldset) {
-    
+
     /* fit two fieldsets side by side */
     display: grid;
     grid-template-columns: 1fr 1fr;
@@ -123,8 +135,16 @@ form {
     gap: 1rem;
 }
 
+.hero .logo {
+    height: 128px;
+}
+
 .accountnav {
     display: flex;
     gap: 1rem;
 }
 
+#wrapper {
+    display: flex;
+}
+
diff --git a/styles/profiles.css b/styles/profiles.css
new file mode 100644
index 0000000000000000000000000000000000000000..147c159de3c5d79444ff78dd00131f0c8fff2089
--- /dev/null
+++ b/styles/profiles.css
@@ -0,0 +1,39 @@
+
+#profile {
+    display: flex;
+    gap: 1rem;
+    padding: 1rem;
+    width: 750px;
+    border-radius: 2em;
+    align-items: center;
+
+    margin: auto;
+
+    background: var(--grey-0);
+}
+
+#profile > .avatar {
+    height: 150px;
+
+    border-radius: 1em;
+}
+
+#profile > .info > .displayname {
+    font-size: 2.5rem;
+    font-weight: bolder;
+}
+
+#profile > .info > .bcid {
+    font-size: 1.5rem;
+}
+
+#details {
+    display: grid;
+    grid-template-columns: 3fr 1fr;
+}
+
+@media screen and (prefers-color-scheme: dark) {
+    #profile {
+        background: var(--grey-9);
+    }
+}
diff --git a/styles/types.css b/styles/types.css
index 83ae2a8d44b8905ae76e919f3df2d17293854305..7b09f26aead241523cc6cdeedb456bb5e5495706 100644
--- a/styles/types.css
+++ b/styles/types.css
@@ -6,7 +6,7 @@
 @import url(/fontawesome/css/all.css);
 
 html {
-    font-family: 'montserrat', sans-serif;
+    font-family: montserrat, sans-serif;
     font-weight: 400;
     -webkit-font-smoothing: antialiased;
     -moz-osx-font-smoothing: grayscale;
@@ -22,6 +22,10 @@ h2.subheading + h1 {
     margin-top: 0;
 }
 
+#sitetitle, .font-montserrat, h1, h2, h3, h4, h5, h6, legend {
+    font-family: 'montserrat', sans-serif;
+}
+
 .bc-1 {
     font-weight: 700;
 }