sam-4screen-desktop 2026-10-17:6:51:28

This commit is contained in:
2026-10-17 06:51:28 +11:00
parent a5c60d0dfc
commit 5b4f8e10a7
9 changed files with 62 additions and 95 deletions

View File

@@ -0,0 +1,141 @@
---
title: Central AI Hub
type: project
status: active
created: "2026-10-10"
updated: "2026-09-26"
tags:
- ai
- project
- architecture
- central
- hub
- showcase
aliases:
- The Hub
- central_ai_llm_master
skills:
- service design
- intent routing
- authorization and approval flows
- event-driven integration
tools:
- OmniRoute
- Home Assistant
- Jervis
- Paperclip
- Artemis
- Apprise
---
# Central AI Hub — Overview
## 💡 What is this project?
One small service that every way in reaches every ability through. The phone app,
the house voice (Jervis), chat (dsh), and the admin app all talk to the Hub. The
Hub works out what was asked, checks who asked, sends it to the right place, and
returns a short reply.
It exists to stop the same abilities being built three times. Build once. Every
way in gets it.
The Hub does five steps: **hear → understand → check → route → answer**.
It is **not** a central brain. It holds no personality and no memory. It is
**not** Paperclip — it never waits minutes for an answer.
## 🌟 Key Highlights (Portfolio & Employment)
- **Concept**: A thin shared router over several AI systems, with one place for
safety and approval. Not a monolith.
- **Tools & Skills Showcase**: service design, intent routing with an LLM,
authorization and tap-to-approve flows, deterministic handling of
high-consequence actions, integration across MQTT, Home Assistant, Paperclip,
and a local model gateway.
- **Practical Value**: One place to add a new ability. One place to control who
may do what. The house, the phone, and chat all behave the same.
## 📐 Architecture & Flow
```mermaid
flowchart LR
subgraph IN["Ways in"]
PHONE["Phone app<br/>wake word + voice"]
ADMIN["Admin app<br/>restricted actions"]
ESP["ESP32 voice<br/>Hey Jervis"]
DSH["dsh chat"]
end
HUB{{"HUB<br/>hear - understand - check - route - answer"}}
subgraph OUT["Destinations"]
HA["Home Assistant"]
SPK["Speakers<br/>Jervis TTS"]
NET["Internet control"]
ART["Artemis<br/>phone screen"]
PC["Paperclip<br/>slow jobs"]
WEB["Web search"]
APP["Apprise / NTFY"]
end
PHONE --> HUB
ESP --> HUB
DSH --> HUB
ADMIN --> HUB
HUB --> HA
HUB --> SPK
HUB --> NET
HUB --> ART
HUB --> PC
HUB --> WEB
HUB --> APP
PC -. result .-> HUB
```
### The dividing line
> Does this need a decision, or does it need to happen?
- **Happen** — fast and certain. Go straight to the destination. No LLM in the
action path.
- **Decision** — slow, multi-step, worth recording. Send to Paperclip.
### How it relates to the other projects
Every other project is a **peer**, not a sub-project. Each has its own folder,
repo, port, interface, and build. Each works without the Hub. The Hub is an
**extra** way in.
- The **Hub asks** a project ("what does Harry have on today?") using one small
contract, `/ask`. The project answers about its own data.
- A **project tells** the Hub something ("overdue homework found").
- **n8n triggers** the project (scan the inbox, scan the school).
- **High-consequence control** (cut the kids' internet) does **not** go through
the Hub. The admin app calls the internet control service directly, with
confirmation on the phone.
### Safety
- Voice is shared and has no identity. Anyone in the house can speak.
- The admin app is the only trusted door for restricted actions.
- A spoken keycode is not the lock. Voice may ask; the admin app confirms with
"Did you ask for this?".
## 🔗 Related Notes & Links
- Gitea Repo: https://gitea.lab.audasmedia.com.au/sam/central_ai_llm_master
- Interactive Map: [Archify Map](https://maps.lab.audasmedia.com.au/central_ai_llm_master/docs/)
- AI Context (Outline): https://outline.lab.audasmedia.com.au/collection/central-ai-hub-fYfUzsMooS
- Contract: `/ask` — see `docs/ask-contract.md` in the repo.
- Local path: `/home/sam/paseo/projects/central_ai_llm_master/`
- Related: [[Home Voice Assistant System]] · [[Family Home Lab]] · [[Artemis on GrapheneOS Pixel Mobile]] · [[Front Door Cam]]
<!--
NOTES FOR THE AGENT WRITING THIS NOTE
* `type: note` is intentional — this note is written into the INBOX and the
obsidian-sorter assigns the real type from its final folder.
* Keep `skills:` and `tools:` — they are custom fields the sorter preserves.
* Do NOT add a `type` other than `note`, and do NOT choose a folder.
* Add the `project` tag so the sorter constrains it to the projects bucket.
-->

View File

@@ -0,0 +1,125 @@
---
title: "Compass NHS — Harry and Finn's School Service"
type: project
status: active
created: "2026-10-15 20:00"
updated: "2026-09-26"
tags:
- project
- family
- architecture
- go
- compass
- finn
- school
- service
- sqlite
- htmx
- playwright
- apprise
aliases:
- Compass NHS
- School Service
skills:
- Go service with no heavy framework
- SQLite data modelling with versioned migrations
- HTMX server-rendered dashboard with no build step
- Headless browser automation (patchright) past a Cloudflare challenge
- HTML parsing into structured items
- Read-only HTTP contract for another service (the Hub)
- Docker and Caddy deployment
- n8n scheduling with a restricted SSH trigger
tools:
- Go
- SQLite
- HTMX
- patchright (headless Playwright)
- Docker
- Caddy
- n8n
- Apprise
- Home Assistant
- OmniRoute
---
# [[Compass NHS — Harry and Finn's School Service]]
## What is this project?
This project watches the school for Harry and Finn. Each morning it reads the
school system, Compass, and works out what each boy has due. It shows Sam one
page with what is overdue first. It then sends each boy his own short brief, and
one combined brief to Jo and Sam.
The point is simple. Nobody has to log into Compass in the morning. The family
learns about late work **before** it is late, not after.
## What it does
- **Reads Compass** for both children, twice a day apart or on demand.
- **Stores every item**: homework, tasks, notices and school events, each against
the right child.
- **Finds overdue work** and puts it first. Then due today, then due this week.
- **Shows a dashboard** with one tab per child. It works on a phone.
- **Writes important school events** to the Home Assistant calendar
`calendar.school`.
- **Sends the morning briefs** at 6:00am on Email and WhatsApp.
- **Answers the Hub** to a question such as "what does Harry have on today at
school?" That path is read-only.
## Why it matters (what it shows)
This is a small but complete home-lab product, not a script. The service is one
Go binary with SQLite. It has its own container, its own port, its own login and
its own Caddy route. It is read-only against the school: it logs in and reads,
and it never posts or messages.
The interesting engineering part is the login. Compass sits behind **Cloudflare**,
and the login form is protected by a managed bot challenge. A normal headless
browser gets a 403. The project uses **patchright**, a headless Playwright build
that behaves enough like a real browser to pass the challenge. The parent login
needed **no** two-factor code.
The other deliberate design is honesty about failure. A failed scan must never
look like a quiet day with no homework. The dashboard, the briefs and the Hub
answer all state the real scan state in plain words.
## Architecture
```mermaid
flowchart LR
N8N["n8n on .13<br/>06:00 daily trigger"] --> SVC["Compass / NHS service<br/>Go + SQLite :8792"]
SAM["Sam<br/>browser dashboard"] --> CADDY["Caddy on .35"] --> SVC
SVC -->|scan| PATCH["patchright scraper"] -->|login + pages| COMPASS["Compass NHS<br/>read-only"]
SVC -->|"create event"| HA["Home Assistant<br/>calendar.school"]
SVC -->|briefs| APPRISE["Apprise .35:8210<br/>Email + WhatsApp"]
APPRISE --> BOYS["Harry and Finn<br/>per-boy brief"]
APPRISE --> PARENTS["Jo and Sam<br/>combined brief"]
HUB["Hub :8788<br/>read-only /ask"] --> SVC
```
## What is still missing (state it plainly)
**Teams (FR-15) is DEFERRED.** The project does not scan Teams for homework. This
is a known gap that the brief set aside on purpose. Everything else in the build
spec is built, deployed and running. Compass alone already answers what is due
and overdue.
## Where it lives
- **Code**: Gitea repo `sam/compass_teams_nhs` (`main`).
- **Live**: <https://school.lab.audasmedia.com.au> — its own container on `.13`,
port 8792, behind Caddy on `.35`.
- **Interactive map**:
<https://maps.lab.audasmedia.com.au/compass_teams_nhs/docs/>
- **AI context**: `docs/outline/` in the repo, and the Outline collection
*Compass / NHS (School)*.
- **Tasks**: Vikunja project **Compass / NHS (School)** (id 21).
- **Diagram source**: `docs/architecture.mmd`, rendered to
`docs/diagrams/architecture.png`.
## Status
Built and deployed. Phases 1 to 9 are done. Teams (FR-15) is deferred. The service
scans both children, shows overdue work first, writes important events to the
calendar, and sends the briefs every morning at 6:00am.

View File

@@ -0,0 +1,114 @@
---
title: Email Agent — Family Inbox Triage
type: project
status: active
created: "2026-10-13 10:45"
updated: "2026-09-26"
tags:
- tool
- project
- email
- family
- architecture
- go
- inbox
- triage
- gmail
- htmx
aliases:
- Email Agent
- Family Inbox Triage
skills:
- Go backend development
- HTMX server-rendered UI
- IMAP and SMTP integration
- LLM classification (JEV) and drafting (OmniRoute)
- SQLite data modelling
- Docker and Caddy deployment
tools:
- Go
- HTMX
- SQLite
- Gmail IMAP/SMTP
- JEV (TypeSafe System One)
- OmniRoute
- Apprise
- Docker
- Caddy
---
# [[Email Agent — Family Inbox Triage]]
## What is this project?
One page that clears the family inbox. All family mail lands in a single Gmail
account, because PurelyMail forwards every domain address there. This app reads
that mail, sorts it into categories, recommends what to do, and lets Sam act on
whole groups at once.
The goal is simple: Sam opens one page, spends a few minutes, and knows that
nothing important was missed.
## What it does
- **Reads Gmail** over IMAP with an app password. It forces IPv4, because IPv6
to Gmail is broken from the server.
- **Classifies every message** with JEV (a TypeSafe System One model). Each mail
gets a category, an importance, a suggested action, and a confidence.
- **Shows a triage list** grouped by category, with the recommended action on
every row and a bulk select.
- **Acts in bulk**: delete, move, or mark. Deletes are recoverable — the app
moves mail to a Delete label, never hard-deletes.
- **Drafts replies** with an LLM through OmniRoute, from a template or a rule.
Sam edits the draft, then sends. It sends as the address the mail arrived at,
and there is no auto-send.
- **Spotlights unsubscribe links** and keeps an ignore list. It never follows a
link by itself.
- **Has a rules editor** — plain text, saved and versioned. The classifier obeys
the rules.
- **Sends a daily summary** by email through Apprise.
- **Answers the Hub**: a read-only question like "anything important in my
inbox?" returns a short answer.
## Why it matters (what it shows)
This is an end-to-end private AI tool, built and run in the home lab. It shows a
full path from an idea to a deployed service: a Go backend with no heavyweight
framework, an HTMX interface with no build step, a small SQLite store, real
mail-server protocols (IMAP and SMTP with an app password), two different model
paths (JEV for typed judgement, OmniRoute for text), and a strict "the human
confirms every action" design.
The design keeps the risky actions safe: no auto-send, no auto-delete, every
action logged, and deletes recoverable.
## Architecture
```mermaid
flowchart LR
SAM["Sam<br/>phone or desktop"] --> CADDY["Caddy on .35"]
CADDY --> UI["HTMX dashboard<br/>:8790"]
UI --> DB[("SQLite")]
UI --> GMAIL["Gmail<br/>IMAP + SMTP, IPv4"]
UI --> JEV["JEV<br/>classify"]
UI --> OMNI["OmniRoute<br/>draft reply"]
UI --> APPRISE["Apprise<br/>summary email"]
HUB["Central AI Hub :8788"] --> API["Hub API<br/>/ask /capabilities"]
API --> DB
N8N["n8n schedule"] --> JOBS["CLI jobs"]
JOBS --> DB
```
## Where it lives
- **Code**: Gitea repo `sam/email_agent`.
- **Live**: `https://mail.lab.audasmedia.com.au` (container on `.13`, port 8790).
- **Interactive map**: https://maps.lab.audasmedia.com.au/email_agent/docs/
- **AI context**: Outline collection `Email Agent`.
- **Tasks**: Vikunja project `Email Agent`.
## Status
Built and deployed. Phases 1 to 7 of the build spec are done and running. The
Phase 8 documentation pass wrote this note, the Gitea README and docs, the
Outline collection, the Vikunja board, and the architecture map.

View File

@@ -0,0 +1,66 @@
---
title: Graphify
type: project
status: active
created: "2026-10-13"
updated: "2026-09-26"
tags:
- project
- ai-agents
- architecture
- obsidian
- graphify
- tool
- knowledge-graph
- pi-agent
- showcase
skills:
- python
- knowledge-graphs
- mcp
- subagent-orchestration
- graph-analytics
tools:
- graphify (graphifyy)
- pi-graphify
- mcp
- networkx
---
# Graphify — Overview
## 💡 What is this project?
Graphify turns a folder of files into a knowledge graph. It reads the files, finds
concepts, and links them. The graph answers questions that cross many files.
I run it on my Obsidian vault. The graph holds 2,171 nodes and 2,770 edges across
128 communities. An AI agent reads the graph instead of all 134 notes.
## 🌟 Key Highlights (Portfolio & Employment)
- **Concept**: A persistent knowledge graph with an honest audit trail. Every link is tagged EXTRACTED, INFERRED, or AMBIGUOUS.
- **Tools & Skills Showcase**: Python, NetworkX community detection, an MCP server, pi extension tools, and subagent orchestration for semantic extraction.
- **Practical Value**: One query reads the graph instead of 134 files. The measured cost is 31 times fewer tokens per query.
## 📐 Architecture & Flow
```mermaid
flowchart LR
A["Obsidian vault<br/>134 notes"] --> B["detect + chunk"]
B --> C["semantic extraction<br/>pi subagents"]
C --> D["graph.json<br/>2171 nodes / 2770 edges"]
D --> E["cluster + analyze"]
E --> F["GRAPH_REPORT.md"]
E --> G["graph.html"]
D --> H["MCP server<br/>graphify-vault"]
H --> I["AI agents<br/>query_graph"]
```
## 🔗 Related Notes & Links
- **Graph data**: `/home/sam/graphify/obsidian-vault/graph.json`
- **Interactive HTML**: `/home/sam/graphify/obsidian-vault/graph.html`
- **Report**: `/home/sam/graphify/obsidian-vault/GRAPH_REPORT.md`
- **Interface**: MCP server `graphify-vault` (query from any folder), plus CLI and pi tools.
- **AI Context (Outline)**: collection `Graphify`

View File

@@ -0,0 +1,140 @@
---
title: "Internet Use Control — Kids' Screen Time Limits"
type: project
status: active
created: "2026-10-14 12:12"
updated: "2026-09-26"
tags:
- project
- family
- architecture
- go
- kids
- time
- internet-control
- mqtt
- android
- sqlite
- htmx
aliases:
- Internet Use Control
- Kids Screen Time
skills:
- Go backend development
- HTMX server-rendered dashboard UI
- SQLite data modelling and versioned migrations
- MQTT transport with TLS and per-device topic ACLs
- Android Accessibility Service and full-screen overlay (Kotlin)
- Android VpnService for device-local site filtering
- Versioned HTTP API design for a separate consumer
- Docker and Caddy deployment
tools:
- Go
- SQLite
- HTMX
- Mosquitto
- Docker
- Caddy
- Apprise
- Kotlin
- Android Accessibility Service
- Android VpnService
---
# [[Internet Use Control — Kids' Screen Time Limits]]
## What is this project?
This project limits screen time on the children's own devices. Harry's phone and
Finn's computer should follow their weekly schedule with nobody touching them. Sam
sees their usage on one page, and Sam changes a limit from his phone in seconds.
The rules apply to the kids' devices only. Nothing changes for the rest of the
house. There is no router change, no Pi-hole and no DNS change.
The main piece is the **Control Service**. It runs on `.13` in Docker. It holds the
schedules, the block lists, the allow lists and the usage in SQLite. It serves a
small web dashboard. It is the one place that writes the rules.
A device gets its rules over **MQTT**. The Control Service publishes the wanted
state for each device as a *retained* message, so a phone that reconnects is
limited again at once. The device also sends its status and its usage back over
MQTT. If a device cannot reach the broker, it keeps its last rules.
## What it does
- **Weekly schedules.** A grid per device, Monday to Sunday. A window means the
rules are on. A window can cross midnight.
- **Block lists.** An app (Android package name) or a site (hostname).
- **Allow lists.** Spotify at night, school sites. The device blocks everything
on the block list **except** the allow list.
- **Usage.** The dashboard shows a week per device: the daily total, the top apps
and the top sites.
- **Overrides.** A versioned HTTP API for the admin app (`lock`, `unlock`,
`grant`), plus one shared keycode that a child can type on the device for a
short exemption.
- **Alerts.** Apprise tells Sam when a limit is reached or a device is offline
too long.
- **A Hub answer.** The family Hub can ask "how long have the kids been on
screens today?" and gets a real answer. That question path is read-only.
## Why it matters (what it shows)
This is a full home-lab product, not a script. It shows a Go service with no
heavy framework, an HTMX dashboard with no build step, a small SQLite store with
versioned migrations, real MQTT with TLS, per-device accounts and topic access
control, and a versioned API that another project consumes.
The Android side is the interesting part. **Device Owner was rejected**, because
it needs a factory reset of the children's phones. Instead the phone blocks by
**interruption**: an Accessibility Service watches the foreground app, and a
full-screen overlay covers a blocked app. The child **can** disable it, and that
is accepted. The real enforcement is the **tamper alert**: when the Accessibility
Service stops, the app is force-stopped, or the overlay permission is removed,
the phone reports it and Sam gets an alert. Site filtering uses a device-local
`VpnService`. There is no wipe, no Device Owner and no managed profile.
## Architecture
```mermaid
flowchart LR
SAM["Sam<br/>browser dashboard"] --> CADDY["Caddy on .35"]
CADDY --> CS["Control Service<br/>Go + SQLite :8791"]
ADMIN["Admin app"] -->|"/api/v1"| CS
HUB["Hub"] -->|"read-only /ask"| CS
CS -->|"retained config"| MQ["Mosquitto on .13<br/>8883 TLS + topic ACLs"]
MQ -->|"own config topic"| PHONE["Kids' Android client<br/>Kotlin"]
PHONE -->|"status + telemetry"| MQ
MQ -->|reports| CS
CS --> APPRISE["Apprise<br/>alerts + tamper"]
```
A picture of the two MQTT planes: port **1883** stays plain and anonymous for
legacy consumers, and port **8883** is TLS with per-device credentials and topic
ACLs. A device on 8883 may read only its own config topic and write only its own
status and telemetry topics. Without that rule a child could publish a fake
"unlocked".
## What is still missing (state it plainly)
**The Phase 4 Windows computer client is deliberately deferred and is not
built.** There is no Python client and no process blocking on a computer. The
project stands at **9 of 10** phases for that single reason. Everything else in
the build spec is done and running.
## Where it lives
- **Code**: Gitea repo `sam/internet_use_control`.
- **Live**: <https://webrestrict.lab.audasmedia.com.au> (container on `.13`, port
8791, behind Caddy on `.35`).
- **Interactive map**: <https://maps.lab.audasmedia.com.au/internet_use_control/docs/>
- **AI context**: `docs/outline/` in the repo (00-index and four documents).
- **Tasks**: Vikunja project **Internet Use Control** (id 20).
- **Diagram source**: `docs/architecture.mmd`, rendered to
`docs/diagrams/architecture.png`.
## Status
Built and deployed. Phases 1, 2, 3, 5, 6, 7, 8, 9 and 10 are done. The phones
are limited by the Android client. Only the Windows client (Phase 4) remains, and
it is deferred on purpose.