# Surface

> Surface — drive the office wall display

- Skill: `cjpitzi/surface` (Agent Skill)
- Install (CLI): `npx skillmds@latest add cjpitzi/surface`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cjpitzi/surface/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: cjpitzi (https://skillmd.com/u/cjpitzi)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cjpitzi/surface

---


# Surface — drive the office wall display

Chris has a permanent, **interactive** office display (KVM-attached, on pve2
HDMI-2) driven by the **`surface`** command (on PATH; private repo at
`~/repos/office-presence`). `surface <X>` means: put the right **live** view of X
on that display. **You pick the source and build the URL.** This is additive —
still answer in chat / trigger other skills.

Plain text/code ("surface foo.py") stays in chat unless the user says "on the
display".

The display is a **persistent, full browser** (tabs + address bar), not a locked
kiosk. `surface page/url/show` each open a **new focused tab** — they never
replace what's up or restart the browser, so Chris's own tabs and earlier
surfaced views stay open and he can flip between them. `surface home` refocuses
the household dashboard tab; `surface stop` closes just the tabs `surface page`
opened. Tabs accumulate over a session — that's expected; close them from the
browser UI or leave them.

## Preconditions

- **LAN-only.** `surface` SSHes directly to pve2. A network-isolated cloud/cowork
  session **cannot** reach it — say so rather than pretending. Confirm reachability
  with `surface state` if unsure.
- Authed services (Grafana ✓ already logged in, Axiom, private HA) need the display
  logged in once at the KVM. Public GitHub / public-dashboard URLs just work.

## Workflow: identify → show → file

### 1. Identify the source + scope

Pick what fits X; compose multiple into panes if it spans sources.

| Source | Use for | How to build the URL |
|---|---|---|
| **Grafana Cloud** (`pitzilabs.grafana.net`, display logged in) | metrics & Loki logs: throughput, host/NAS stats, the Claude runner fleet | `grafana` MCP `search_dashboards` → find dashboard/panel → `…/d/<uid>/<slug>?viewPanel=<id>&from=now-30d&to=now&refresh=10s` |
| **Axiom** | Firewalla logs (ACL/blocks, Zeek) — event/log-shaped X | Axiom dataset/query URL (one-time KVM login) |
| **GitHub** (public PitziLabs repos render with no login) | repos, PRs, **Actions runs** | `github.com/<org>/<repo>/actions/runs/<id>` — find via `github` MCP / `gh` |
| **Home Assistant** | home/device state | a Lovelace view URL |
| **Firewalla** MCP | when X isn't in Grafana/Axiom (`get_alarms`, `search_flows`, `get_wan_usage`, `get_network_stats`, `get_top_talkers`) | use the data to decide which dashboard/view to surface |

`homelab-observability` maps what's measured where. **Prefer a real, navigable
link** over a constructed static page; build a page only for something with no URL
(a diagram) or to compose several sources into panes.

### 2. Show it

```bash
surface url   NAME          # a saved favorite (fastest — check `surface list` first) → new tab
surface show  URL [opts]    # any http(s) URL → new focused tab
surface page  NAME          # a built library page (offline Mermaid) → new tab
surface push-site DIR       # publish a static site dir to pve2 (durable, http://web.lan/)
surface home                # focus the household dashboard (home) tab
```
Options: `--open` (xdg-open the snapshot), `--no-snap`, `--settle SECS`, `--port N`.
For anchor deep-links into a long page, add `--settle 4` so the scroll lands before
the snapshot.

### 3. Verify (always) — vision check

```bash
surface verify        # lists the open tabs + grabs a frame of the frontmost
```
Then **Read the PNG** it wrote (`./surface-<UTC>.png`) and confirm the frontmost
tab shows the expected content — **not** a login wall, blank, or error. The tab
landing frontmost is the one `surface` just opened, so the frame is your check
that it rendered. Use `surface snap` for an extra frame.

### 4. File it for fast recall

For anything worth seeing again:

```bash
surface save-url NAME URL "Title"     # writes urls.json
```
Then commit `office-presence` so it persists, and tell Chris the short name:

```bash
cd ~/repos/office-presence && git add urls.json && git commit -m "Add '<name>' URL favorite — <what>"
```
For multi-source composites, add a page under `pages/` + a `library.json` entry
instead. End commit messages with the `Co-Authored-By` trailer.

## State & recovery

```bash
surface state    # open tabs + page-server state (and a reachability check)
surface list     # library pages + URL favorites
surface stop     # stop the page server, close its tabs, focus home
```
The persistent browser launches on the household dashboard across pve2 reboots;
systemd `Restart=always` recovers crashes, and `dashboard-refresh.timer` reloads
just the dashboard tab every 6h to clear its heap leak. The pve2 plumbing
(`display-tab` for tab control, `display-show` to (re)launch, `snapshot-server` on
:9999, `dashboard-display.service`) is deployed from **this repo's `host/`** by the
gitops loop — if `surface` fails on the SSH/`display-tab` step, check that plumbing.
If `display-tab` reports "no DevTools endpoint", the screen is in fullscreen kiosk
mode (no debug port) — `surface home` relaunches the interactive browser.

## Example

"surface the last 30 days of host throughput" → `grafana` MCP `search_dashboards`
for the throughput panel → `surface show '…/d/<uid>/…?viewPanel=<id>&from=now-30d&to=now&refresh=10s'`
→ `surface verify` + Read the PNG → `surface save-url host-throughput-30d <url> "Host throughput 30d"`
→ commit. Next time: `surface url host-throughput-30d`.

