diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..9bc1645
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,8 @@
+FROM python:3.12-bookworm
+
+EXPOSE 8080
+
+COPY . /site
+RUN pip install /site
+
+ENTRYPOINT [ "launch-site" ]
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..0d3d37e
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+nicegui~=2.10
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..fa54a3f
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,27 @@
+# Copyright (C) 2025 Daniel McKnight - All Rights Reserved
+from os.path import join, dirname
+
+from setuptools import setup, find_packages
+
+
+def get_requirements(requirements_filename: str):
+ requirements_file = join(dirname(__file__), requirements_filename)
+ with open(requirements_file, 'r', encoding='utf-8') as r:
+ requirements = r.readlines()
+ requirements = [r.strip() for r in requirements if r.strip()
+ and not r.strip().startswith("#")]
+ return requirements
+
+setup(
+ name="site-mcknight-tech",
+ author="Daniel McKnight",
+ author_email="daniel@mcknight.tech",
+ description="McKnight Tech Website",
+ packages=find_packages(),
+ include_package_data=True,
+ package_data={"site_mcknight_tech": ["page_content/*"]},
+ install_requires=get_requirements("requirements.txt"),
+ entry_points={
+ "console_scripts": ['launch-site=site_mcknight_tech.__main__:main']
+ }
+)
\ No newline at end of file
diff --git a/site_mcknight_tech/__init__.py b/site_mcknight_tech/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/site_mcknight_tech/__main__.py b/site_mcknight_tech/__main__.py
new file mode 100755
index 0000000..098aaa0
--- /dev/null
+++ b/site_mcknight_tech/__main__.py
@@ -0,0 +1,94 @@
+# Copyright (C) 2025 Daniel McKnight - All Rights Reserved
+
+from os.path import isfile, join, dirname
+from typing import Optional
+
+from fastapi.openapi.models import RequestBody
+from nicegui import ui
+from fastapi.requests import Request
+
+# TODO: Read from configuration
+_tab_to_content = {
+ "home": "page_content/home.md",
+ "portfolio": "page_content/portfolio.md",
+ "resume": "page_content/resume.md",
+ "bio": "page_content/bio.md"
+}
+
+_error_page = "page_content/404.md"
+_footer_block = "page_content/footer.html"
+
+
+@ui.page("/{name}")
+def _render_path(name: str):
+ """
+ Parse a path element to render a page
+ :param name: path element of the request
+ """
+ render_site(name)
+
+def _get_content_from_file(file_name: str) -> str:
+ """
+ Take a file name/path and return its contents
+ :param file_name: Relative or absolute path to a file
+ """
+ file_name = join(dirname(__file__), file_name)
+ if not isfile(file_name):
+ file_name = _error_page
+ with open(file_name, 'r') as f:
+ return f.read()
+
+
+def render_tab(tab_name: str):
+ """
+ Render a simple webpage tab from a single file
+ :param tab_name: Tab name to render
+ """
+ file_name = _tab_to_content.get(tab_name, _error_page)
+ with ui.scroll_area().classes('w-full h-screen no-wrap'):
+ ui.markdown(_get_content_from_file(file_name),
+ extras=['cuddled-lists', 'fenced-code-blocks', 'tables'])
+
+
+def render_header() -> ui.tab_panels:
+ """
+ Render sticky page header with navigation
+ """
+ tab_names = _tab_to_content.keys()
+ with ui.tabs().classes('w-full') as tabs:
+ tab_list = {name: ui.tab(name) for name in tab_names}
+ with ui.tab_panels(tabs).classes('w-full mx-auto h-screen').style('max-width: 1200px') as header:
+ for name, t in tab_list.items():
+ with ui.tab_panel(t):
+ render_tab(name)
+ return header
+
+
+def render_site(page_name: Optional[str] = None):
+ """
+ Renders the site with the requested page active
+ :param page_name: Page to render. If `None` or invalid, `Home` will be rendered
+ """
+ header = render_header()
+ if page_name and page_name in _tab_to_content.keys():
+ header.set_value(page_name)
+ else:
+ header.set_value(list(_tab_to_content.keys())[0])
+ ui.query('.nicegui-content').classes('h-screen no-wrap')
+ ui.html(_get_content_from_file(_footer_block), tag="footer").classes("rounded-lg shadow-sm m-4 w-full text-center")
+ ui.add_css("""
+ .fa {
+ font-size: 30px;
+ text-decoration: none;
+ margin: 10px 5px;
+ vertical-align: bottom;
+ }
+ """)
+
+def main():
+ render_site()
+ ui.run(dark=None, reload=False, title="Daniel McKnight")
+
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/site_mcknight_tech/page_content/404.md b/site_mcknight_tech/page_content/404.md
new file mode 100644
index 0000000..debde83
--- /dev/null
+++ b/site_mcknight_tech/page_content/404.md
@@ -0,0 +1 @@
+Oops. This content hasn't been written yet...
\ No newline at end of file
diff --git a/site_mcknight_tech/page_content/bio.md b/site_mcknight_tech/page_content/bio.md
new file mode 100644
index 0000000..dfd72d1
--- /dev/null
+++ b/site_mcknight_tech/page_content/bio.md
@@ -0,0 +1,15 @@
+## About Me
+I'm a software engineer with a passion for conversational AI, and open source development. With a background in physics
+and a range of interests that extend from software development to 3D printing, woodworking, and circuit design,
+I am constantly finding new projects to apply my skills to.
+
+At Neon AI, I have found the perfect environment to pursue my interests in tech. With a range of projects, my work at
+Neon AI aligns with my commitment to responsible software development. All the projects I've worked on respect user
+privacy, and many are run on user-owned hardware, ensuring that users maintain control over their data and their systems.
+
+I have also had the pleasure of working with a large software stack, including voice technologies,
+model training and customization (LLMs, Speech Synthesis, and Intent Determination), data organization, system
+architecture and design, code and workflow optimization, documentation, and infrastructure management. I've called out a
+few projects that highlight my work in these areas in my Portfolio.
+
+Yes, an LLM helped me write this 😉.
diff --git a/site_mcknight_tech/page_content/footer.html b/site_mcknight_tech/page_content/footer.html
new file mode 100644
index 0000000..26bd77f
--- /dev/null
+++ b/site_mcknight_tech/page_content/footer.html
@@ -0,0 +1,47 @@
+
+
+
+
diff --git a/site_mcknight_tech/page_content/home.md b/site_mcknight_tech/page_content/home.md
new file mode 100644
index 0000000..b5fa04c
--- /dev/null
+++ b/site_mcknight_tech/page_content/home.md
@@ -0,0 +1,8 @@
+# Daniel McKnight
+## Coder, Sysadmin, Manager
+
+I work in the design and implementation of voice assistants, LLMs, infrastructure, and plenty more. Check out a selection
+of my work [in my portfolio](https://www.mcknight.tech/portfolio) or learn some more about me and my experience
+[in my bio](https://www.mcknight.tech/bio) and [resume](https://www.mcknight.tech/resume), respectively.
+I built this site using [NiceGUI](https://nicegui.io/), with the source code available
+[on Forgejo](https://forge.mcknight.tech/d_mcknight/website-mcknight-tech).
diff --git a/site_mcknight_tech/page_content/portfolio.md b/site_mcknight_tech/page_content/portfolio.md
new file mode 100644
index 0000000..9c5c828
--- /dev/null
+++ b/site_mcknight_tech/page_content/portfolio.md
@@ -0,0 +1,50 @@
+# Project Portfolio
+I have developed and contributed to hundreds of open source repositories on GitHub (as well as some Private ones that
+I cannot share here). This is just a selection of some of my favorite projects and what I like about them.
+
+## [Neon Core](https://github.com/neongeckocom/NeonCore)
+This is the primary project I contribute to as part of my work at NeonGecko. It started as work with the (now defunct)
+open source Mycroft AI project, turned into a proprietary fork, and then we ended up open sourcing that fork. Working on
+this project, I learned a lot about contributing to open source and managing open source projects. At the start, I learned
+how to be a helpful individual contributor, and later I was on the other side, learning how to interface with the community
+to balance internal goals with community feedback.
+
+## [`neon-data-models`](https://github.com/neongeckocom/neon-data-models)
+This repository contains barely any functional code, but it was an exercise in careful planning.
+It is structured into a logical hierarchy with documentation and unit tests.
+This project was not fully integrated to the extent I planned, but it proved invaluable in defining/validating
+messages sent between modules. The use of Pydantic also means that validator methods can be used to adapt an old-style
+input into a new one when an API changes.
+
+## [`neon-users-service`](https://github.com/neongeckocom/neon-users-service)
+This project defines a service for managing a user database with a flexible backend; initially, it supports SQLite for
+a standalone system (and easy development/testing) and MongoDb, which is what we used for production at NeonGecko.
+I implemented a very simple CRUD (Create/Read/Update/Delete) design, with RBAC (Role-Based Access Controls) to enable
+easy integration with applications. The design enables the service to be easily adapted to any user database and to
+allow other services to easily operate on user entries (with the proper permissions, of course).
+
+## [`neon-hana`](https://github.com/neongeckocom/neon-hana)
+This is primarily a RESTful API used to access other services. This is one of the more complex FastAPI applications
+I have written; it implements JWT authentication and Pydantic models to validate requests/responses.
+
+## [`neon-diana`](https://github.com/neongeckocom/neon-diana-utils)
+This isn't the prettiest project, but it is my first foray into writing Helm charts. The module provides some
+developer-oriented CLI tools for generating Helm charts to deploy the same services NeonGecko uses in production
+systems.
+
+## [`neon-debos`](https://github.com/neongeckocom/neon_debos)
+This project was my first exposure to Go templates. It started as a fork of another similar project which I adapted to
+our needs. As part of this project, I used squashfs/overlayfs to enable safe OS updates with methods to roll back or
+reset an installation in case of any errors. To support this, I compiled custom kernels and implemented a customized
+initramfs.
+
+## [`.github`](https://github.com/neongeckocom/.github)
+This repository contains a number of shared GitHub Actions, in addition to the standard issue templates and other special
+files GitHub supports here. Creating this repository was part of an effort to consolidate code and make sprawling
+repositories more manageable.
+
+## [`neon-docs`](https://neongeckocom.github.io/neon-docs/)
+Just what it says, this is all documentation.
+
+## [This Site](https://forge.mcknight.tech/d_mcknight/website-mcknight-tech)
+I built this site using [nicegui](https://nicegui.io/)!
diff --git a/site_mcknight_tech/page_content/resume.md b/site_mcknight_tech/page_content/resume.md
new file mode 100644
index 0000000..bf1f847
--- /dev/null
+++ b/site_mcknight_tech/page_content/resume.md
@@ -0,0 +1,51 @@
+## Work Experience
+### Neon Gecko Inc.
+Software Developer, Developer Relations Manager, Lead Hardware Developer
+
+December 2017 – Present. Responsibilities Include:
+
+- Managing multiple projects and work from individual contributors, one-off contractors, and long-term contract workers
+- Working with management and developers to develop roadmaps, set timelines, and spec projects
+- Extensive Python programming
+- Extensive GitOps and workflow design, automation, and management
+- Being fluent in voice assistant concepts and technologies
+- Having a strong understanding of LLM technologies and model fine-tuning
+- Deploying and maintaining infrastructure with Kubernetes, XCP-ng, and various cloud providers
+- Kotlin for Android app development and Google Play Store publication
+
+### Best Buy/Geek Squad
+Consultation Agent, Sales Associate
+
+November 2015 - December 2017
+
+- Experience with Windows and Mac system administration, troubleshooting, and repair
+
+## Technologies
+### Languages
+- Python
+- SQL
+- MongoDb
+- Kotlin
+
+### Frameworks
+- Git and GitHub
+- FastAPI
+- SocketIO
+- Sentry
+- Kubernetes
+
+## Education
+### University of Washington
+Bachelor of Science in Physics, June 2018
+
+Secretary, Physics Club at UW Bothell
+
+### Bellevue College
+Associate of Science in Engineering, June 2016
+
+Member, Phi Theta Kappa Honors Society
+
+### Mercer Island High School
+High School Diploma, June 2012
+
+Recipient, Washington State Honors Award