diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml
new file mode 100644
index 0000000..b7d44ef
--- /dev/null
+++ b/.github/workflows/docker-publish.yml
@@ -0,0 +1,32 @@
+name: Docker Publish (GHCR)
+on:
+ release:
+ types: [published]
+permissions:
+ contents: read
+ packages: write
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: docker/setup-buildx-action@v3
+ - uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - uses: docker/metadata-action@v5
+ id: meta
+ with:
+ images: ghcr.io/${{ github.repository }}
+ tags: |
+ type=semver,pattern={{version}}
+ type=semver,pattern={{major}}.{{minor}}
+ type=raw,value=latest
+ - uses: docker/build-push-action@v5
+ with:
+ context: .
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml
new file mode 100644
index 0000000..7e995a7
--- /dev/null
+++ b/.github/workflows/pypi-publish.yml
@@ -0,0 +1,21 @@
+name: PyPI Publish
+on:
+ release:
+ types: [published]
+permissions:
+ contents: read
+ id-token: write
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.11'
+ - name: Install build tools
+ run: pip install build
+ - name: Build package
+ run: python -m build
+ - name: Publish to PyPI
+ uses: pypa/gh-action-pypi-publish@release/v1
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..991beeb
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,19 @@
+name: Release
+on:
+ push:
+ tags:
+ - 'v*'
+permissions:
+ contents: write
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ generate_release_notes: true
+ fail_on_unmatched_files: false
diff --git a/.gitignore b/.gitignore
index 11f94c6..e1d587b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,20 @@ build/
# OneByJorah portfolio tooling
.venv-screenshot/
+
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+env/
+venv/
+.venv/
+*.egg-info/
+.eggs/
+dist/
+build/
+*.egg
+.pytest_cache/
+.mypy_cache/
diff --git a/README.md b/README.md
index c2c6d2d..eb7cf7e 100644
--- a/README.md
+++ b/README.md
@@ -4,14 +4,15 @@
# DirWatch
-**A lightweight, self-hosted dashboard for monitoring Active Directory domain controller health — replication, SYSVOL, LDAP bind, and service status at a glance.**
+**Lightweight, self-hosted dashboard for Active Directory domain controller health — replication, SYSVOL, LDAP bind, and service status at a glance**
-
-
-
-
-
-
+[](https://github.com/OneByJorah/DirWatch/releases)
+[](https://pypi.org/project/dirwatch/)
+[](https://www.docker.com/)
+[](https://www.python.org/downloads/)
+[](https://flask.palletsprojects.com/)
+[](https://learn.microsoft.com/powershell/)
+[](https://opensource.org/licenses/MIT)
@@ -21,106 +22,113 @@
DirWatch surfaces the health of your Active Directory domain controllers in a single dark-theme dashboard. A PowerShell collector emits a normalized JSON status file (replication state, SYSVOL, LDAP bind, DHCP scope, and service status) and the Flask app renders it as status cards, DC detail, alerts, and trends.
-It is aimed at Windows admins who want a quick, dependency-light DC health view without standing up SCOM or a full monitoring stack.
+Built for Windows admins who want a quick, dependency-light DC health view without standing up SCOM or a full monitoring stack.
## Quick Start
+### pip
+
```bash
-git clone https://github.com/OneByJorah/DirWatch.git
-cd DirWatch
-pip install -r requirements.txt
-python3 app.py
+pip install dirwatch
```
-Open **http://localhost:5000**. Or run it in Docker:
+### Docker (recommended)
```bash
+git clone https://github.com/OneByJorah/DirWatch.git
+cd DirWatch
docker compose up -d
```
-> [!NOTE]
-> The app reads `mock_dc_status.json` at startup. Swap in your own collector output by mounting a real file at the same path. The included `collectors/mock_dc_collector.ps1` generates sample data.
+Open **http://localhost:5000**.
-## Features
+### From Source
-- **Domain controller overview** — per-DC status cards with health, replication, SYSVOL, and LDAP bind state.
-- **Replication monitoring** — track replication health between controllers.
-- **Service status** — DNS, DHCP, and ADWS service state per DC.
-- **Alerts** — severity-coded alert feed (critical / warning / info) in the dashboard.
-- **Public status endpoint** — `/public` returns a simple "all systems operational" health string.
-- **Dark NOC UI** — responsive sidebar layout with stats grid and DC detail pages.
-- **Collector scripts** — PowerShell collectors for DC status, with Teams/Telegram notification placeholders.
-- **Docker & gunicorn** — production image runs gunicorn as a non-root user.
+```bash
+git clone https://github.com/OneByJorah/DirWatch.git
+cd DirWatch
+pip install -r requirements.txt
+python3 app.py
+```
-## Architecture
+Open **http://localhost:5000**.
-```
-PowerShell collector ──▶ mock_dc_status.json ──▶ Flask app (:5000) ──▶ Browser
- │
- ├──▶ Dashboard (/)
- └──▶ Public status (/public)
+## Install
+
+### pip
+
+```bash
+pip install dirwatch
```
-The app loads the JSON status file once at startup and renders it through Jinja2 templates. DC data can be refreshed by regenerating the file with a collector.
+### Docker
-## Configuration
+```bash
+git clone https://github.com/OneByJorah/DirWatch.git
+cd DirWatch
+docker compose up -d
+```
+
+### From Source
-Basic operation needs no environment variables — the app reads `mock_dc_status.json` from its working directory. The Compose/entrypoint flow accepts:
+```bash
+git clone https://github.com/OneByJorah/DirWatch.git
+cd DirWatch
+pip install -r requirements.txt
+python3 app.py
+```
-| Variable | Default | Description |
-|----------|---------|-------------|
-| `FLASK_ENV` | `production` | Flask environment |
-| `MOCK_MODE` | `true` | Use mock DC data (no live AD required) |
-| `PORT` | `5000` | App port used by the entrypoint |
-| `AD_DOMAIN` | — | AD domain (live mode) |
-| `AD_SERVER` | — | DC hostname (live mode) |
-| `AD_USERNAME` / `AD_PASSWORD` | — | AD bind credentials (live mode) |
-| `LOG_LEVEL` | `INFO` | Log verbosity |
+## Features
-## API / Routes
+- **DC health at a glance** — replication state, SYSVOL, LDAP bind, DHCP scope, service status
+- **PowerShell collector** — emits normalized JSON status file from any Windows domain controller
+- **Flask dashboard** — dark-theme status cards, DC detail view, alerts, and trends
+- **Self-hosted** — no external dependencies, no cloud required
+- **Docker support** — run with `docker compose up -d`
+- **Dependency-light** — Flask + PowerShell, no SCOM, no full monitoring stack
-| Endpoint | Method | Description |
-|----------|--------|-------------|
-| `/` | GET | DC status dashboard |
-| `/public` | GET | Public "all systems operational" status string |
+## Tech Stack
-## Dashboard Panels
+- **Backend** — Python 3.11, Flask 3
+- **Collector** — PowerShell 5.1 (Windows)
+- **Deployment** — Docker Compose, pip install
+- **Data** — JSON status file (mock_dc_status.json, replaceable with real collector output)
-| Panel | Description |
-|-------|-------------|
-| **DC Overview** | Status of all domain controllers |
-| **Replication** | Inter-DC replication health |
-| **Services** | DNS / DHCP / ADWS service state |
-| **Alerts** | Recent severity-coded alerts |
+## Package Badges
-## Use Cases
+[](https://github.com/OneByJorah/DirWatch/releases)
+[](https://pypi.org/project/dirwatch/)
+[](https://www.docker.com/)
+[](https://opensource.org/licenses/MIT)
-1. **Windows admins** — a fast DC health check without SCOM.
-2. **Homelab AD labs** — monitor test domain controllers.
-3. **Pre-maintenance checks** — confirm replication and services before a change window.
+## Configuration
-## Tech Stack
+DirWatch reads `mock_dc_status.json` at startup. Swap in your own collector output by mounting a real file at the same path.
-Python 3.11, Flask 3, Jinja2, PowerShell 5.1 collectors, gunicorn, Docker Compose.
+The included `collectors/mock_dc_collector.ps1` generates sample data for testing.
-## Screenshots
+| File | Description |
+|------|-------------|
+| `mock_dc_status.json` | DC status data (replace with real collector output) |
+| `collectors/mock_dc_collector.ps1` | Sample PowerShell collector |
+| `app.py` | Flask application entry point |
-| Dashboard | Full view |
-|---|---|
-|  |  |
+## Architecture
-A short `docs/assets/demo.gif` walkthrough is also included.
+```
+PowerShell Collector (collectors/mock_dc_collector.ps1)
+ │
+ ▼
+ mock_dc_status.json (normalized JSON)
+ │
+ ▼
+ Flask App (app.py) ──▶ Dark-theme dashboard (http://localhost:5000)
+```
## Contributing
-Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). [Open an issue](https://github.com/OneByJorah/DirWatch/issues) to report a bug or request a feature.
+Contributions are welcome. [Open an issue](https://github.com/OneByJorah/DirWatch/issues) to report a bug or request a feature.
## License
MIT — see [LICENSE](LICENSE).
-
-## Connect
-
-- [jorahone.com](https://jorahone.com)
-- [GitHub Org](https://github.com/OneByJorah)
-- [info@jorahone.com](mailto:info@jorahone.com)
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..7367abc
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,40 @@
+[build-system]
+requires = ["setuptools>=61.0", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "dirwatch"
+version = "1.0.0"
+description = "Lightweight self-hosted dashboard for monitoring Active Directory domain controller health — replication, SYSVOL, LDAP bind, and service status at a glance."
+readme = "README.md"
+license = {text = "MIT"}
+authors = [{name = "OneByJorah", email = "info@jorahone.com"}]
+keywords = ["active-directory", "domain-controller", "monitoring", "dashboard", "flask", "powershell", "windows", "sysadmin", "ad-health", "jorahone"]
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Environment :: Web Environment",
+ "Intended Audience :: System Administrators",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Topic :: System :: Systems Administration",
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+]
+requires-python = ">=3.11"
+dependencies = [
+ "flask>=3.0",
+ "requests>=2.31",
+]
+
+[project.optional-dependencies]
+dev = ["pytest", "black", "flake8"]
+
+[project.urls]
+Homepage = "https://github.com/OneByJorah/DirWatch"
+Repository = "https://github.com/OneByJorah/DirWatch"
+Issues = "https://github.com/OneByJorah/DirWatch/issues"
+
+[tool.setuptools.packages.find]
+include = ["dirwatch*"]