# mithal-space-monitor

A lightweight, stdlib-only uptime/latency/SSL monitor with a single-page
Chart.js dashboard, packaged into one container.

## What's inside

| File | Purpose |
|---|---|
| `monitor.py` | Standard-library-only Python script. Every `CHECK_INTERVAL` seconds it checks DNS resolution time, HTTP status + latency, search-endpoint latency, and SSL cert expiry, then writes a rolling `RETENTION_HOURS` window to a JSON file. |
| `index.html` | Single-page dashboard (HTML/CSS/JS + Chart.js via CDN). Polls the JSON file every 30s and renders 24h uptime %, a 60-minute latency line chart, SSL expiry, and a table of the last 10 checks. |
| `entrypoint.sh` | Starts `monitor.py` and `python -m http.server` side by side, forwards signals, and exits the container if either process dies (so the orchestrator restarts it). |
| `Dockerfile` | `python:3.12-alpine` base, non-root user, healthcheck, no external Python deps. |

## Configuration (environment variables)

| Variable | Default | Description |
|---|---|---|
| `TARGET_URL` | `https://mithal.space` | URL to monitor |
| `SEARCH_PATH` | `/search?q=test` | Path appended to the target's origin for the search-latency check |
| `CHECK_INTERVAL` | `60` | Seconds between checks |
| `RETENTION_HOURS` | `24` | Rolling window kept in the JSON log |
| `REQUEST_TIMEOUT` | `10` | Per-request timeout (seconds) |
| `PORT` | `8080` | Dashboard HTTP server port |

## Build & run locally

```bash
docker build -t mithal-space-monitor .

docker run -d \
  --name mithal-monitor \
  -p 8080:8080 \
  -e TARGET_URL="https://mithal.space" \
  -e SEARCH_PATH="/search?q=test" \
  -v mithal_monitor_data:/app/data \
  mithal-space-monitor

# open http://localhost:8080
```

The `-v mithal_monitor_data:/app/data` volume is optional but recommended so
your 24h history survives a container restart/redeploy.

## Push to a registry

```bash
docker tag mithal-space-monitor registry.example.com/yourorg/mithal-space-monitor:latest
docker push registry.example.com/yourorg/mithal-space-monitor:latest
```

## Deploy

This image is a single process group exposing one HTTP port, so it runs
as-is on most container platforms:

- **Cloud Run / Container Apps / Fly.io**: deploy the image, set `PORT`
  to match the platform's expected port (Cloud Run injects `PORT`
  automatically - the entrypoint already respects it), mount a persistent
  volume if the platform supports one (otherwise history resets on redeploy,
  which is fine - it just rebuilds over the next `RETENTION_HOURS`).
- **Kubernetes**: run as a `Deployment` with 1 replica, a `Service` of type
  `ClusterIP`/`LoadBalancer`, and optionally a `PersistentVolumeClaim`
  mounted at `/app/data`. The built-in `HEALTHCHECK` maps naturally to a
  liveness probe on `GET /`.
- **Plain VM / docker-compose**: use the `docker run` command above behind
  your existing reverse proxy / TLS terminator.

## Notes & extension points

- Everything in `monitor.py` uses only the Python standard library
  (`urllib`, `socket`, `ssl`, `json`) - no `pip install` step, no
  dependency surface in the image.
- Data is written atomically (`write → temp file → os.replace`) so the
  dashboard never reads a half-written JSON file.
- To monitor multiple targets, run one container per target (each with its
  own `TARGET_URL`/port), or extend `monitor.py` to loop over a list of
  targets and extend `index.html` with a target selector.
- Add basic auth / IP allowlisting at your reverse proxy if the dashboard
  shouldn't be public.