diff --git a/sherlock_project/web.py b/sherlock_project/web.py
new file mode 100644
index 0000000000..937e618ba4
--- /dev/null
+++ b/sherlock_project/web.py
@@ -0,0 +1,282 @@
+#! /usr/bin/env python3
+
+"""
+Sherlock: Optional Web UI
+
+A lightweight Flask wrapper around the Sherlock CLI that streams scan progress
+to the browser. Three display modes surface feedback at different levels of
+detail so users see continuous progress instead of waiting in silence for a
+full scan to complete.
+
+Run with:
+ python -m sherlock_project.web
+
+Requires Flask (not a core dependency):
+ pip install flask
+"""
+
+import os
+import re
+import subprocess
+import sys
+from typing import Iterator
+
+try:
+ from flask import Flask, Response, render_template_string, request
+except ImportError:
+ print("Flask is required to run the Sherlock web UI.")
+ print("Install it with: pip install flask")
+ sys.exit(1)
+
+
+DEFAULT_HOST = "127.0.0.1"
+DEFAULT_PORT = 5000
+SCAN_TIMEOUT_SECONDS = 10
+USERNAME_PATTERN = re.compile(r"^[A-Za-z0-9_.\-]{1,64}$")
+
+PAGE_TEMPLATE = r"""
+
+
+
+
+ Sherlock
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+
+
+def create_app() -> "Flask":
+ """Build and return the Flask application."""
+ app = Flask(__name__)
+
+ @app.route("/")
+ def index() -> str:
+ return render_template_string(PAGE_TEMPLATE)
+
+ @app.route("/search")
+ def search() -> Response:
+ username = request.args.get("u", "").strip()
+ if not USERNAME_PATTERN.fullmatch(username):
+ return Response("Invalid username.", status=400, mimetype="text/plain")
+ return Response(_stream_scan(username), mimetype="text/plain")
+
+ return app
+
+
+def _stream_scan(username: str) -> Iterator[str]:
+ """Run a Sherlock scan as a subprocess and yield its output line by line."""
+ proc = subprocess.Popen(
+ [
+ sys.executable,
+ "-u",
+ "-m",
+ "sherlock_project",
+ "--print-all",
+ "--no-color",
+ "--timeout",
+ str(SCAN_TIMEOUT_SECONDS),
+ username,
+ ],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ bufsize=1,
+ text=True,
+ cwd=os.getcwd(),
+ )
+ try:
+ assert proc.stdout is not None
+ for line in proc.stdout:
+ yield line
+ finally:
+ proc.wait()
+
+
+def main() -> None:
+ """Entry point for `python -m sherlock_project.web`."""
+ host = os.environ.get("SHERLOCK_WEB_HOST", DEFAULT_HOST)
+ port = int(os.environ.get("SHERLOCK_WEB_PORT", DEFAULT_PORT))
+ print(f"Sherlock web UI running at http://{host}:{port}")
+ create_app().run(host=host, port=port, debug=False)
+
+
+if __name__ == "__main__":
+ main()