diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..f99a776
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,12 @@
+# Flask secret key for session management
+SECRET_KEY=your_secret_key_here
+
+# Yubico API credentials (get from https://upgrade.yubico.com/getapikey/)
+YUBICO_CLIENT_ID=your_client_id
+YUBICO_SECRET_KEY=your_secret_key
+
+# Your YubiKey ID (first 12 characters of any OTP generated by your YubiKey)
+YUBIKEY_ID=ccccccxxxxxx
+
+# Webhook secret for updating emergency.md
+WEBHOOK_SECRET=your_webhook_secret_here
diff --git a/.gitignore b/.gitignore
index 7d847cc..6bb0493 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,6 @@ __pycache__/
 .env
 .vs/
 .venv/
+emergency.md
+
+flask_session/
diff --git a/README.md b/README.md
index 2f77bf4..e86b81e 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,55 @@
-# python-webserver-template
+# Emergency Access System
 
-Python3 website template including git actions
\ No newline at end of file
+A secure emergency information access system using YubiKey authentication. This application allows authorized users to access critical emergency information stored in Markdown format.
+
+## Features
+
+- YubiKey authentication for secure access
+- Markdown rendering with syntax highlighting
+- Webhook for remote updates of emergency information
+- Mobile-friendly interface
+
+## Setup
+
+1. Clone this repository
+2. Install dependencies:
+   ```
+   pip install -r requirements.txt
+   ```
+3. Create a `.env` file based on `.env.example`:
+   ```
+   cp .env.example .env
+   ```
+4. Edit the `.env` file with your settings
+
+### YubiKey Configuration
+
+1. Get your YubiKey API credentials from https://upgrade.yubico.com/getapikey/
+2. Add your client ID and secret key to the `.env` file
+3. Determine your YubiKey ID (first 12 characters of any OTP generated by your YubiKey)
+4. Add the YubiKey ID to the `.env` file
+
+### Webhook Secret Generation
+
+The webhook requires a strong secret to secure remote updates. Use one of these methods to generate a secure secret:
+
+#### Option 1: Using Python
+
+```python
+import secrets
+print(secrets.token_hex(32))  # Generates a 64-character hex string
+```
+
+#### Option 2: Using OpenSSL
+
+```bash
+openssl rand -hex 32
+```
+
+#### Option 3: Using /dev/urandom (Linux/Mac)
+
+```bash
+head -c 32 /dev/urandom | xxd -p -c 32
+```
+
+Add the generated secret to your `.env` file:
diff --git a/requirements.txt b/requirements.txt
index 8cf7fcb..53c61cc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,8 @@
 flask
 gunicorn
 requests
-python-dotenv
\ No newline at end of file
+python-dotenv
+yubico-client
+Flask-Session
+markdown
+pygments
\ No newline at end of file
diff --git a/server.py b/server.py
index 240a1a8..d7a1224 100644
--- a/server.py
+++ b/server.py
@@ -9,16 +9,43 @@ from flask import (
     render_template,
     send_from_directory,
     send_file,
+    session,
+    url_for,
 )
 import os
 import json
 import requests
 from datetime import datetime
 import dotenv
+import markdown
+import markdown.extensions.fenced_code
+from flask_session import Session
+from yubico_client import Yubico
+from functools import wraps
+import hmac
+import hashlib
 
 dotenv.load_dotenv()
 
 app = Flask(__name__)
+app.config["SECRET_KEY"] = os.getenv("SECRET_KEY", os.urandom(24))
+app.config["SESSION_TYPE"] = "filesystem"
+app.config["SESSION_PERMANENT"] = False
+Session(app)
+
+# Yubikey settings
+YUBICO_CLIENT_ID = os.getenv("YUBICO_CLIENT_ID")
+YUBICO_SECRET_KEY = os.getenv("YUBICO_SECRET_KEY")
+YUBIKEY_ID = os.getenv("YUBIKEY_ID")  # The first 12 characters of your YubiKey OTP
+
+# Authentication function
+def login_required(f):
+    @wraps(f)
+    def decorated_function(*args, **kwargs):
+        if not session.get('authenticated'):
+            return redirect(url_for('login', next=request.url))
+        return f(*args, **kwargs)
+    return decorated_function
 
 
 def find(name, path):
@@ -74,8 +101,65 @@ def wellknown(path):
 # region Main routes
 @app.route("/")
 def index():
-    return render_template("index.html")
+    return render_template("index.html", authenticated=session.get('authenticated', False))
 
+# Login and authentication routes
+@app.route("/login", methods=["GET", "POST"])
+def login():
+    error = None
+    if request.method == "POST":
+        otp = request.form.get("otp", "")
+        
+        # Verify the first 12 characters of the OTP match the expected YubiKey ID
+        if not otp or len(otp) < 12 or otp[:12] != YUBIKEY_ID:
+            error = "Invalid YubiKey OTP"
+        else:
+            try:
+                # Initialize Yubico client
+                client = Yubico(YUBICO_CLIENT_ID, YUBICO_SECRET_KEY)
+                
+                # Verify the OTP with Yubico servers
+                verification = client.verify(otp)
+                if verification:
+                    session['authenticated'] = True
+                    next_url = request.args.get('next', url_for('emergency'))
+                    return redirect(next_url)
+                else:
+                    error = "YubiKey authentication failed"
+            except Exception as e:
+                error = f"Authentication error: {str(e)}"
+    
+    return render_template("login.html", error=error)
+
+@app.route("/logout")
+def logout():
+    session.pop('authenticated', None)
+    return redirect(url_for('index'))
+
+@app.route("/emergency")
+@login_required
+def emergency():
+    # Check if emergency.md exists
+    emergency_path = os.path.join(os.path.dirname(__file__), "emergency.md")
+    
+    if os.path.exists(emergency_path):
+        with open(emergency_path, 'r') as f:
+            emergency_content = f.read()
+            # Convert markdown to HTML with enhanced extensions
+            emergency_html = markdown.markdown(
+                emergency_content,
+                extensions=[
+                    'fenced_code',
+                    'codehilite',
+                    'tables',
+                    'nl2br',
+                    'sane_lists'  # Ensures proper list rendering
+                ]
+            )
+            return render_template("emergency.html", content=emergency_html)
+    else:
+        return render_template("emergency.html", 
+                              content="<p>No emergency information available.</p>")
 
 @app.route("/<path:path>")
 def catch_all(path: str):
@@ -102,6 +186,54 @@ def catch_all(path: str):
 # endregion
 
 
+# region Webhook
+@app.route("/webhook/update", methods=["POST"])
+def webhook_update():
+    # Get the webhook secret from environment
+    webhook_secret = os.getenv("WEBHOOK_SECRET")
+    if not webhook_secret:
+        return jsonify({"error": "Webhook not configured"}), 500
+    
+    # Verify X-Webhook-Signature header
+    signature = request.headers.get("X-Webhook-Signature")
+    if not signature:
+        return jsonify({"error": "Missing signature"}), 401
+    
+    # Get request body
+    payload = request.get_data()
+    
+    # Verify signature
+    expected_signature = hmac.new(
+        webhook_secret.encode(), 
+        payload, 
+        hashlib.sha256
+    ).hexdigest()
+    
+    if not hmac.compare_digest(signature, expected_signature):
+        return jsonify({"error": "Invalid signature"}), 401
+    
+    # Process the update
+    try:
+        data = request.json
+        if not data or not isinstance(data, dict) or "content" not in data:
+            return jsonify({"error": "Invalid payload format"}), 400
+        
+        emergency_content = data["content"]
+        emergency_path = os.path.join(os.path.dirname(__file__), "emergency.md")
+        
+        # Write the new content to the file
+        with open(emergency_path, "w") as f:
+            f.write(emergency_content)
+        
+        return jsonify({"success": True, "message": "Emergency content updated"}), 200
+    
+    except Exception as e:
+        app.logger.error(f"Webhook error: {str(e)}")
+        return jsonify({"error": f"Update failed: {str(e)}"}), 500
+
+# endregion
+
+
 # region Error Catching
 # 404 catch all
 @app.errorhandler(404)
diff --git a/templates/emergency.html b/templates/emergency.html
new file mode 100644
index 0000000..6fb64ae
--- /dev/null
+++ b/templates/emergency.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Emergency Information - Nathan.Woodburn/</title>
+    <link rel="icon" href="/assets/img/favicon.png" type="image/png">
+    <link rel="stylesheet" href="/assets/css/index.css">
+    <!-- Remove highlight.js as it's not needed with Pygments -->
+    <style>
+        .emergency-content {
+            max-width: 800px;
+            margin: 0 auto;
+            text-align: left;
+            padding: 20px;
+            background-color: #111;
+            border-radius: 8px;
+        }
+        .controls {
+            margin-top: 20px;
+        }
+        /* Additional styling for code blocks */
+        .codehilite {
+            padding: 0;
+            margin: 1em 0;
+            border-radius: 5px;
+            overflow: auto;
+        }
+        .codehilite pre {
+            padding: 10px;
+            margin: 0;
+            background-color: #1e1e1e;
+            border-radius: 5px;
+            overflow-x: auto;
+        }
+        /* Fix code display in dark theme */
+        .codehilite .k { color: #569cd6; } /* Keyword */
+        .codehilite .s, .codehilite .s1, .codehilite .s2 { color: #ce9178; } /* String */
+        .codehilite .c, .codehilite .c1 { color: #6a9955; } /* Comment */
+        .codehilite .n { color: #dcdcdc; } /* Name */
+        .codehilite .o { color: #d4d4d4; } /* Operator */
+        .codehilite .p { color: #d4d4d4; } /* Punctuation */
+        
+        /* YAML-specific styles */
+        .codehilite .l { color: #b5cea8; } /* Literals */
+        .codehilite .kn { color: #569cd6; } /* Key Name (YAML keys) */
+        
+        /* Styling for lists */
+        .emergency-content ol {
+            list-style-type: decimal;
+            padding-left: 30px;
+            margin: 15px 0;
+        }
+        
+        .emergency-content ol ol {
+            list-style-type: lower-alpha;
+        }
+        
+        .emergency-content ol ol ol {
+            list-style-type: lower-roman;
+        }
+        
+        .emergency-content li {
+            margin: 5px 0;
+            line-height: 1.5;
+        }
+        
+        /* Adding some spacing between list items for better readability */
+        .emergency-content li + li {
+            margin-top: 8px;
+        }
+        
+        /* Styling for unordered lists as well */
+        .emergency-content ul {
+            list-style-type: disc;
+            padding-left: 30px;
+            margin: 15px 0;
+        }
+        
+        .emergency-content ul ul {
+            list-style-type: circle;
+        }
+        
+        .emergency-content ul ul ul {
+            list-style-type: square;
+        }
+    </style>
+</head>
+
+<body>
+    <div class="spacer"></div>
+    <div class="centre">
+        <h1>Emergency Information</h1>
+        
+        <div class="emergency-content">
+            {{ content|safe }}
+        </div>
+        
+        <div class="controls">
+            <p><a href="/">Back to Home</a> | <a href="/logout">Logout</a></p>
+        </div>
+    </div>
+    
+    <!-- Script to make all content links open in a new tab -->
+    <script>
+        document.addEventListener('DOMContentLoaded', function() {
+            // Select all links in the emergency content
+            const contentLinks = document.querySelectorAll('.emergency-content a');
+            
+            // Add target="_blank" and rel="noopener" (for security) to each link
+            contentLinks.forEach(link => {
+                link.setAttribute('target', '_blank');
+                link.setAttribute('rel', 'noopener');
+            });
+        });
+    </script>
+</body>
+
+</html>
diff --git a/templates/index.html b/templates/index.html
index bb349f8..69fdc6c 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -13,6 +13,12 @@
     <div class="spacer"></div>
     <div class="centre">
         <h1>Nathan.Woodburn/</h1>
+        {% if authenticated %}
+            <p><a href="/emergency">Access Emergency Information</a></p>
+            <p><a href="/logout">Logout</a></p>
+        {% else %}
+            <p><a href="/login">Login with YubiKey</a></p>
+        {% endif %}
     </div>
 </body>
 
diff --git a/templates/login.html b/templates/login.html
new file mode 100644
index 0000000..5df189c
--- /dev/null
+++ b/templates/login.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Login - Nathan.Woodburn/</title>
+    <link rel="icon" href="/assets/img/favicon.png" type="image/png">
+    <link rel="stylesheet" href="/assets/css/index.css">
+    <style>
+        .error {
+            color: #ff5555;
+            margin-top: 10px;
+        }
+        input {
+            padding: 8px;
+            margin: 15px 0;
+            background-color: #222;
+            color: white;
+            border: 1px solid #555;
+            border-radius: 4px;
+        }
+        button {
+            padding: 8px 15px;
+            background-color: #444;
+            color: white;
+            border: none;
+            border-radius: 4px;
+            cursor: pointer;
+        }
+        button:hover {
+            background-color: #555;
+        }
+        form {
+            margin-top: 20px;
+        }
+    </style>
+</head>
+
+<body>
+    <div class="spacer"></div>
+    <div class="centre">
+        <h1>YubiKey Authentication</h1>
+        <p>Please insert your YubiKey and press it to authenticate.</p>
+        
+        {% if error %}
+            <div class="error">{{ error }}</div>
+        {% endif %}
+        
+        <form method="POST" action="/login">
+            <div>
+                <input type="text" id="otp" name="otp" placeholder="Press your YubiKey..." autofocus>
+            </div>
+            <div>
+                <button type="submit">Authenticate</button>
+            </div>
+        </form>
+        
+        <p><a href="/">Back to Home</a></p>
+    </div>
+</body>
+
+</html>
diff --git a/update.py b/update.py
new file mode 100644
index 0000000..c41bd09
--- /dev/null
+++ b/update.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+"""
+Example script to update emergency.md content via webhook.
+
+Usage:
+    python webhook_example.py <webhook_url> <webhook_secret> <markdown_file>
+
+Example:
+    python webhook_example.py https://emergency.example.com mysecret ./new_emergency.md
+"""
+
+import sys
+import hmac
+import hashlib
+import requests
+import json
+
+def update_emergency_content(webhook_url, webhook_secret, content_file):
+    # Read the content from the file
+    with open(content_file, 'r') as f:
+        content = f.read()
+    
+    # Prepare the payload
+    payload = json.dumps({"content": content})
+    
+    # Calculate the signature
+    signature = hmac.new(
+        webhook_secret.encode(),
+        payload.encode(),
+        hashlib.sha256
+    ).hexdigest()
+    
+    # Set headers
+    headers = {
+        "Content-Type": "application/json",
+        "X-Webhook-Signature": signature
+    }
+    
+    # Send the request
+    response = requests.post(f"{webhook_url}/webhook/update", data=payload, headers=headers)
+    
+    # Print the result
+    print(f"Status code: {response.status_code}")
+    print(f"Response: {response.text}")
+
+if __name__ == "__main__":
+    if len(sys.argv) != 4:
+        print(f"Usage: {sys.argv[0]} <webhook_url> <webhook_secret> <markdown_file>")
+        sys.exit(1)
+    
+    webhook_url = sys.argv[1]
+    webhook_secret = sys.argv[2]
+    content_file = sys.argv[3]
+    
+    update_emergency_content(webhook_url, webhook_secret, content_file)