# Doris Dashboard

Local homepage for John's briefing, glanceable status, and "what matters right now" view.

## What it is

Doris Dashboard is the quiet front page for John's local information stack on NOMAD. It is not a general website and it is not meant to be another feed reader with a fresh coat of paint. It exists to turn Doris Digest output and a few homelab/task signals into one page John can open for a fast read.

The dashboard is meant to answer:

- what is worth looking at this morning
- what local or homelab signals need attention
- what chores or Paperless follow-ups are still hanging around
- what topics are surfacing too often and should be tuned down

## Why it exists

Without this stack, the raw inputs are noisy:

- Doris Digest candidates are too broad on their own
- Donetick and Paperless are useful but fragmented
- local weather/sky/homelab signals live in separate places

The dashboard turns that into one opinionated, local-only page that Doris can tune over time.

## Runtime model

- **Repo source:** `/home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard`
- **Live runtime:** `/opt/doris-dashboard`
- **Generated page:** `/opt/doris-dashboard/public/index.html`
- **LAN URL:** `http://10.5.30.7:8787/`
- **Exposure rule:** local-only unless John explicitly asks otherwise

## What it shows

- top briefing cards from Doris Digest candidates
- space / homelab / local / other signal lanes
- an experimental `New to You` lane for off-profile category sampling
- Donetick tasks
- Paperless review queue items
- weather, sky, and homelab pulse cards
- lightweight stats about candidate/source mix

## How it works

- `bin/generate_dashboard.py` reads Doris Digest candidates plus a few local/runtime data files.
- It scores, deduplicates, and groups stories into dashboard sections.
- It writes generated JSON artifacts under `data/` and the final static page under `public/`.
- `bin/dashboard_server.py` serves the page and handles interactive actions like topic feedback and Paperless review state.
- The wind watchdog writes a quiet forecast notice into `~/.hermes/state/wind_alert_watchdog.json`; the dashboard surfaces that for longer-range wind events before they are close enough to deserve a direct ping.

That means the dashboard is static-looking for the reader, but still has enough local interactivity for feedback and review flows.

## Live paths

Live runtime path:

```bash
/opt/doris-dashboard
```

Generated page:

```bash
/opt/doris-dashboard/public/index.html
```

## Generate manually

```bash
cd /opt/doris-dashboard
bin/generate_dashboard.py
```

## Preview locally

```bash
cd /opt/doris-dashboard/public
python3 -m http.server 8787
```

Then open:

```text
http://localhost:8787/
```

If viewing from another machine on the LAN, use NOMAD’s LAN IP instead of localhost and make sure the firewall allows the port.

## Cron refresh

Installed in `fizzlepoof`’s crontab:

```cron
# BEGIN DORIS DASHBOARD
DORIS_DASHBOARD_DIR=/opt/doris-dashboard
DORIS_DIGEST_DIR=/home/fizzlepoof/.openclaw/workspace/doris-digest
# Refresh local dashboard after digest collection windows; wrapper enforces Chicago daytime.
5,35 * * * * cd $DORIS_DASHBOARD_DIR && bin/cron_generate_dashboard.sh >> logs/generate.log 2>&1
# END DORIS DASHBOARD
```

Inspect with:

```bash
crontab -l | sed -n '/# BEGIN DORIS DASHBOARD/,/# END DORIS DASHBOARD/p'
```

## Data inputs

Main news source:

```bash
/home/fizzlepoof/.openclaw/workspace/doris-digest/data/candidates.json
/home/fizzlepoof/.openclaw/workspace/doris-digest/data/preferences.json
```

Local dashboard placeholders:

```bash
data/todos.json
data/calendar.json
data/notes.json
```

These are intentionally simple JSON arrays so calendar/Donetick integrations can be added later without rewriting the page.

## Next integrations to add

1. Donetick tasks
2. Calendar events
3. Weather / severe weather
4. Moon phase / ISS passes / meteor shower calendar
5. Homelab health pulse
6. Article feedback buttons or quick links back to Doris

## Privacy/deployment note

This dashboard is local-only on NOMAD unless John explicitly asks to expose it. Do not publish it externally without asking.

## LAN service

The dashboard is exposed on NOMAD's LAN IP via systemd:

```text
http://10.5.30.7:8787/
```

Service file:

```bash
/etc/systemd/system/doris-dashboard.service
```

Manage it:

```bash
sudo systemctl status doris-dashboard.service
sudo systemctl restart doris-dashboard.service
sudo systemctl stop doris-dashboard.service
```

The service runs as `fizzlepoof`, serves `public/`, binds `0.0.0.0:8787`, and regenerates the page before starting.

At setup time UFW was inactive, so no firewall rule was required.

## Buildout added 2026-05-12

The dashboard generator now builds a richer local homepage directly on NOMAD.

Added sections/data:

- Weather metrics for Clarksville via Open-Meteo (`data/weather.json`)
- Moon phase / sky note calculated locally (`data/sky.json`)
- Homelab pulse checks (`data/homelab.json`): dashboard service, cron, digest freshness, workspace disk
- Article export from Doris Digest candidates (`data/articles.json`)
- Dashboard metadata (`data/dashboard.json`)

The generated page still lives at:

```bash
/opt/doris-dashboard/public/index.html
```

And the LAN service remains:

```text
http://10.5.30.7:8787/
```

Verification commands:

```bash
cd /opt/doris-dashboard
python3 -m py_compile bin/generate_dashboard.py
bin/generate_dashboard.py
curl -fsS http://127.0.0.1:8787/ | grep 'Doris Dashboard'
systemctl is-active doris-dashboard.service
crontab -l | sed -n '/# BEGIN DORIS DASHBOARD/,/# END DORIS DASHBOARD/p'
```

Notes:

- Weather.gov and NASA API are not used; Open-Meteo worked reliably without an API key.
- Donetick live task import is still not enabled in the dashboard; `data/todos.json` remains the safe placeholder until the exact read-only endpoint is confirmed.
- Raw secrets are not written to dashboard output.

## Compact news layout

2026-05-13: News/article sections were changed from large cards to compact rows so the dashboard can keep plenty of articles without making John scroll forever.

Implementation:

- `generate_dashboard.py` renders article sections with `compact-card` rows by default.
- `public/style.css` contains the compact layout rules.
- Metrics/weather/homelab pulse remain large cards.

## Donetick integration

2026-05-13: The dashboard now imports open Donetick chores read-only during generation.

Implementation notes:

- Secrets remain encrypted in `/home/fizzlepoof/.openclaw/workspace/.secrets/donetick_secrets.json.enc`.
- `bin/generate_dashboard.py` decrypts secrets at runtime using the local encryption key and logs in with the Doris service account.
- It reads `/api/v1/chores` and `/api/v1/users` only.
- It writes sanitized task data to `data/todos.json` for the dashboard.
- It does not expose tokens, passwords, or raw secrets in generated HTML/JSON.

Verify:

```bash
cd /opt/doris-dashboard
bin/generate_dashboard.py
python3 - <<'PY'
import json
from pathlib import Path
for item in json.loads(Path('data/todos.json').read_text()):
    print(item.get('time'), item.get('title'), item.get('assignee'))
PY
```

## Donetick compact board + topic feedback

2026-05-14: The dashboard's Donetick section was reworked to match the tighter PD Dakboard pattern more closely.

Current behavior:

- Donetick renders compact task cards with due cue, task title, label chips, and a short subtitle.
- Tasks are sorted by urgency first (overdue/today/upcoming/no due date), then due time.
- The dashboard injects a pinned Paperless follow-up card into the Donetick area so that cleanup work stays visible.
- News cards now show `👍 / 👎` topic buttons.
- Topic feedback is persisted through the local dashboard server into `../doris-digest/data/feedback.json` with `origin: "dashboard"`.
- `Reset hidden topics` clears both browser state and dashboard-origin feedback entries.
- Paperless review cards now have `Acknowledge` / `Dismiss` actions persisted to `data/paperless_review_state.json`.
- Major dashboard sections are collapsible and remember collapsed state in the browser.

Important note:

- The interactive actions require the dashboard to be served by `bin/dashboard_server.py`, not plain `python -m http.server`.

## Runtime path sanity

The live systemd unit and crontab must point only at:

```bash
/opt/doris-dashboard
```

Do not run the dashboard from an agent workspace. Agent workspaces are for editing and staging; NOMAD host-run services belong under `/opt/<service>`.

## Cron fix — 2026-05-13

The dashboard cron now uses `bin/cron_generate_dashboard.sh`, which checks America/Chicago daytime internally. This avoids relying on `CRON_TZ`, which NOMAD's cron did not honor consistently.

## Paperless review queue contract

The dashboard displays sanitized Paperless intake triage items written by n8n at `data/paperless_review.json`. Example contract: `data/paperless_review.example.json`.

Expected shape is a JSON array. Each item may include:

- `id`, `title`, `original_filename`, `added`, `archive_url`
- `current.correspondent`, `current.document_type`, `current.tags`
- `triage.summary`, `triage.suggested_title`, `triage.document_type`, `triage.suggested_tags`, `triage.due_date`, `triage.needs_review`, `triage.urgency`, `triage.reason`, `triage.confidence`
- `safety.forced_review`, `safety.eligible_for_safe_apply`, `safety.apply_safe_enabled`
- `queued_at`

Do not write tokens, raw OCR text, API responses, or credentials into this file. It is display-only derived data for local review.
