# TOOLS.md - Local Notes Skills define *how* tools work. This file is for *your* specifics — the stuff that's unique to your setup. ## James Server Hardware (forge — 192.168.1.16) - **CPU:** Intel i7-6700K @ 4.0GHz (4c/8t) - **RAM:** 64GB DDR4 - **GPU:** NVIDIA GTX 970 4GB - **Storage:** 477GB NVMe (Samsung 950 PRO 512GB) - **OS:** Ubuntu 24.04.1 LTS headless + minimal GUI for headed Chrome - **Hostname:** forge See `memory/infrastructure.md` for full infrastructure map. ## What Goes Here Things like: - Camera names and locations - SSH hosts and aliases - Preferred voices for TTS - Speaker/room names - Device nicknames - Anything environment-specific ## Examples ```markdown ### Cameras - living-room → Main area, 180° wide angle - front-door → Entrance, motion-triggered ### SSH - home-server → 192.168.1.100, user: admin ### TTS - Preferred voice: "Nova" (warm, slightly British) - Default speaker: Kitchen HomePod ``` ### James Dashboard - **URL:** http://100.123.216.65:9200 (Tailscale) or http://localhost:9200 - **Purpose:** Visual status board for tasks, briefings, history **Tasks API:** - `GET /api/tasks` - list all tasks - `POST /api/tasks` - add task - `PATCH /api/tasks/:id` - update task - `DELETE /api/tasks/:id` - remove task - **Fields:** title, text, priority, status, owner, domain, notes - **Priority:** high, medium, low - **Status:** pending, in-progress, done - **Owner:** "johan" or "james" (who owns the task) - **Domain:** Kaseya, inou, Sophia, ClawdNode, Infrastructure, Personal, etc. **News API:** - `GET /api/news` - list news (newest first, max 20) - `POST /api/news` - add news item - `DELETE /api/news` - clear all news - `DELETE /api/news/:id` - remove specific item - **Fields:** title, body, type, source (optional), url (optional) - **Type:** info, success, warning, error **Briefings API:** - `GET /api/briefings` - list briefings (newest first, last 30) - `POST /api/briefings` - add briefing - `GET /api/briefings/:id` - get single briefing - `DELETE /api/briefings/:id` - remove briefing - **Fields:** title, date, weather, markets, news, tasks, summary **Deliveries API:** - `GET /api/deliveries` - list active deliveries (excludes delivered) - `GET /api/deliveries?all=true` - list all including delivered - `POST /api/deliveries` - add delivery (prefer upsert instead) - `PUT /api/deliveries/upsert` - smart upsert: matches by tracking_number or description+retailer, updates existing or creates new. **Always use this for shipping email triage.** - `GET /api/deliveries/:id` - get single delivery - `PATCH /api/deliveries/:id` - update delivery - `DELETE /api/deliveries/:id` - remove delivery - **Fields:** description, carrier, retailer, tracking_number, tracking_url, expected_date, status, notes - **Status values:** shipped, in_transit, out_for_delivery, delayed, delivered **Status API:** - `GET /api/status` - list all status items - `POST /api/status` - set status `{"key": "...", "value": "...", "type": "info|warning|error"}` - `GET /api/status/:key` - get single status - `DELETE /api/status/:key` - remove status - **Use for:** Claude usage, system health, key metrics displayed at top of dashboard **Workflow:** - Morning brief → POST to /api/briefings, send Signal link to dashboard - Track tasks with owner field (mine vs Johan's) - Keep briefing history for reference - Update Claude usage status: `scripts/claude-usage-check.sh` (auto-updates dashboard) ### Forge = James' Home (192.168.1.16) - forge IS the James server now. See hardware section above. - **GPU:** GTX 970 4GB (Driver 580.126.09, CUDA 13.0) - **Ollama:** Installed (0.15.4) - **Python:** /home/johan/ocr-env/ (venv with PyTorch 2.2 + CUDA 11.8) - **SSH:** Key auth only (password auth disabled) - **Firewall:** UFW active, SSH + LAN allowed - **Owner:** James ⚡ (full autonomy) **OCR Service (GLM-OCR):** - **URL:** http://192.168.3.138:8090 - **Service:** `systemctl --user status ocr-service` (on forge) - **Source:** `/home/johan/ocr-service/server.py` - **Model:** `/home/johan/models/glm-ocr` (zai-org/GLM-OCR, 2.47 GB) - **VRAM usage:** 2.2 GB idle (model loaded), peaks ~2.8 GB during inference - **Performance:** ~2s small images, ~25s full-page documents (auto-resized to 1280px max) - **Endpoints:** - `GET /health` — status + GPU memory - `POST /ocr` — single image OCR (multipart: file + prompt + max_tokens) - `POST /ocr/batch` — multi-image OCR - **Auto-resize:** Images capped at 1280px longest edge (prevents OOM on GTX 970) - **Usage from james:** `curl -X POST http://192.168.3.138:8090/ocr -F "file=@image.png"` - **Patched env:** transformers 5.0.1.dev0 (from git) + monkey-patch for PyTorch 2.2 compat ### Home Network - **Public IP:** 47.197.93.62 (not static, but rarely changes) - **Location:** St. Petersburg, Florida - **Caddy (reverse proxy):** 192.168.0.2 / Tailscale: 100.84.42.55 (caddy) - SSH: `tailscale ssh root@caddy` or `ssh root@caddy` (key installed) - Config: `/etc/caddy/Caddyfile` ### James Server (Hetzner) - **LAN IP:** 192.168.1.16 - **Gateway UI:** http://192.168.1.16:18789/ - **Agents:** - Main (James): http://192.168.1.16:18789/ - Mail Agent: http://192.168.1.16:18789/agents/mailagent ### Uptime Kuma - **URL:** http://zurich.inou.com:3001 - **User:** james - **Password:** WW8ipJfY27ELf7nnouaKLCL6 ### Openprovider (Domain Registrar) - **URL:** https://cp.openprovider.eu - **User:** johan.jongsma@iasobackup.com - **Password:** !!Helder06 ### Test Devices - **ThinkPhone 1** (Motorola/Lenovo) — Johan's Android test device ### Git Server (Zurich) - **Host:** zurich.inou.com - **User:** git - **URL format:** `git@zurich.inou.com:.git` - **Repos:** azure-backup, clawdnode-android, inou-mobile, mail-agent - **Auth:** SSH keys (claude@macbook, james@server, johan@ubuntu2404 authorized) - **Create new repo:** `ssh git@zurich.inou.com "git init --bare .git"` ### myCigna (Health Insurance Portal) - **URL:** https://my.cigna.com - **Username:** tjjongsma - **Password:** QFL&ARHXGW4R - **2FA:** Email to tj@jongsma.me (I can grab the code from MC) or SMS to ***-2475 - **Account holder:** Tatyana - **Covered:** Tatyana, Johan, Michael, Sophia - **Access method:** Real Chrome on Xvfb:99, CDP on port 9224 (headless Playwright gets WAF-blocked) ### SSH Hosts - **hostkey50304** → 82.22.36.202 / **zurich.inou.com** (CH/Switzerland VPS) - Location: Zürich, likely Equinix ZH (Josefstrasse 225) - Upstream: Cogent Communications - Specs: 4 vCore, 6GB RAM, 120GB SSD - OS: Fresh install (2025-01-27) - User: root - **Purpose:** Security infrastructure (geographic diversity for monitoring, SOC2 compliance). NOT for hosting inou.com. - **Home Assistant** → 192.168.1.252 - User: root - Port: 22 - **⚠️ STRICT RULES:** - NO changes without Johan's explicit permission - **NEVER** change lights during night hours - **NEVER** play audio on speakers/tablets during night hours - Night = Sophia's sleep/care time — disruptions are dangerous ### Browser Profiles | Profile | Type | Port | Use Case | |---------|------|------|----------| | **chrome** | Relay (your actual Chrome) | 18792 | X.com, paranoid sites, full auth | | **fast** | Headless Chromium | 9223 | General automation, tolerant sites | | **clawd** | Headless (managed) | 18800 | Default, no auth | #### Chrome Relay (profile="chrome") — Best for authenticated sites Attaches to your actual Chrome browser via extension. No session copying, no detection issues. **Setup (one-time):** 1. Extension installed at `~/.clawdbot/browser/chrome-extension` 2. Load in Chrome: `chrome://extensions` → Developer mode → Load unpacked 3. Pin the extension to toolbar **Usage:** 1. Navigate to site in Chrome, make sure you're logged in 2. Click the Clawdbot extension icon (badge shows **ON**) 3. I use `browser(profile="chrome")` to control that tab **When to use Chrome Relay:** - X.com (very aggressive bot detection) - Sites requiring 2FA/login you've already completed - Anything that blocks headless browsers #### Headless Chromium (profile="fast") — For general automation Runs headless with synced cookies from Chrome. Some paranoid sites (X.com) detect and block it. **Setup script:** `~/clawd/scripts/browser-setup.sh` ```bash scripts/browser-setup.sh start-login # Open Chrome, login to sites scripts/browser-setup.sh sync # Close Chrome first! Then sync scripts/browser-setup.sh start-headless # Start headless on port 9223 scripts/browser-setup.sh status # Check what's running scripts/browser-setup.sh stop # Stop all ``` **⚠️ Important:** Close Chrome before running `sync` — copying a live profile invalidates sessions! **Usage:** `browser(action="...", profile="fast")` #### Browsing Tips - Use `web_fetch` for simple page reads (faster, cheaper than full browser) - Use `browser` when you need JavaScript rendering, interactions, or auth - For large pages, use `maxChars` on snapshots to avoid context bloat - Keep `targetId` from snapshot responses for stable tab references ### bird (X/Twitter CLI) - **Wrapper:** `~/clawd/scripts/bird` (sets auth tokens automatically) - **Config:** `~/.config/bird/config.json5` (tokens stored but not read properly - use wrapper) - **Usage:** `~/clawd/scripts/bird read ` or `bird read ` - **Commands:** `bird search`, `bird home`, `bird mentions`, `bird news`, `bird user-tweets @handle` - **For X.com access** — use bird instead of browser (faster, no bot detection issues) - **Auth:** Using @johanjongsma account ### Proton Mail Bridge (Headless) - **Service:** `systemctl --user status protonmail-bridge` - **Account:** tj@jongsma.me (Tanya & Johan Jongsma) - **IMAP:** 127.0.0.1:1143 (STARTTLS) - **SMTP:** 127.0.0.1:1025 (STARTTLS) - **Bridge Password:** BlcMCKtNDfqv0cq1LmGR9g - **Keychain:** `pass` (no gnome-keyring needed) - **Config:** `~/.config/protonmail/bridge-v3/prefs.json` → `{"preferred_keychain": "pass"}` ### Message Bridge (WhatsApp backend) - whatsmeow - **Service:** `systemctl --user status message-bridge` - **Port:** 8030 - **Source:** `/home/johan/dev/message-bridge/` - **Data:** `~/.message-bridge/` - **Linked Number:** +1 727 225 2475 (Johan's ThinkPhone) - **Note:** MC wraps this — use MC API for WhatsApp, not this directly **Direct API (for debugging):** - `GET /status` - connection status - `GET /messages` - list messages - `GET /qr?format=png` - QR code for linking (if disconnected) - `POST /send` - send message ### Message Center (MC) - Unified Messaging API - **Service:** `systemctl --user status mail-bridge` - **Port:** 8025 - **Source:** `/home/johan/dev/mail-bridge/` (branch: mc-unified) - **Data:** `~/.message-center/` (cursors.json, orchestration.db) - **Health:** `curl http://localhost:8025/health` **Connectors:** - `tj_jongsma_me` — IMAP (tj@jongsma.me via Proton Bridge) - `johan_jongsma_me` — IMAP (johan@jongsma.me via Proton Bridge) - `whatsapp` — HTTP wrapper for message-bridge on port 8030 **Unified API:** - `GET /messages/new` — unseen messages from all sources - `GET /messages?since=24h` — replay window - `GET /messages/{id}` — single message - `POST /messages/ack` — advance consumer cursor **Actions:** - `POST /messages/{id}/archive` — mark seen/archive - `POST /messages/{id}/delete` — delete message - `POST /messages/{id}/reply` — send reply `{"body": "..."}` - `POST /messages/{id}/to-docs` — forward attachments to ~/documents/inbox/ **Orchestration DB:** `~/.message-center/orchestration.db` - Tracks: first_seen, last_action, acked_by per message - Actions persist across restarts **Webhook:** POSTs `{"event": "new"}` to `http://localhost:18789/hooks/messages` ### Telegram Bot (@jamesjongsma_bot) - **Bot:** @jamesjongsma_bot → t.me/jamesjongsma_bot - **Bot ID:** 8510971070 - **Token:** `8510971070:AAFFgv_UO_9L0Ulp2DRKHD-IWKkrarJNTIc` - **Owner account:** @johanjongsma (Telegram ID: 8454563068) - **Channel config:** `channels.telegram` in openclaw.json, `dmPolicy: open`, `allowFrom: ["*"]` (required — open policy won't start without explicit allowFrom) - **Status:** Live as of 2026-02-18 - **Note:** Created via BotFather from Johan's @johanjongsma account ### Commands - `/screenshot` → Pull latest screenshot from Mac desktop (uses screenshot skill) ### Uptime Kuma (Zurich) - **URL:** https://kuma.inou.com (DNS → zurich 82.22.36.202, Caddy reverse proxy → localhost:3001) - **User:** james / JamesKuma2026! - **Env vars for kuma.py:** ``` UPTIME_KUMA_URL=http://localhost:13001 # (via SSH tunnel: ssh -L 13001:127.0.0.1:3001 root@zurich.inou.com) UPTIME_KUMA_USERNAME=james UPTIME_KUMA_PASSWORD=JamesKuma2026! ``` - **Python venv:** `/home/johan/clawd/skills/uptime-kuma/.venv/` - **Notification channels:** - [1] Clawdbot Signal Alert (webhook → OC) — for MC alerts - [2] Johan Direct (ntfy) — for OC/network alerts - **Monitors:** - [1-5] inou.com (HTTP, API, Zurich VPS, DNS, SSL Cert) - [6] Forge — OpenClaw (push, token: r1G9JcTYCg) → ntfy - [7] Forge — Message Center (push, token: rLdedldMLP) → OC webhook - [8] Home Network (Public) (ping 47.197.93.62) → ntfy ### ntfy (Zurich — self-hosted) - **URL:** https://ntfy.inou.com (Caddy → localhost:2586) - **User:** james / JamesNtfy2026! - **API Token:** tk_ggphzgdis49ddsvu51qam6bgzlyxn - **Alert topic:** forge-alerts (anonymous read allowed for iOS app) - **Johan subscribes to:** https://ntfy.inou.com/forge-alerts (in ntfy iOS app) ### ntfy (Push Notifications) - **Server:** https://ntfy.inou.com (self-hosted on Zurich) - **Topic:** `forge-alerts` (anonymous read, auth required to publish) - **Auth:** `Authorization: Bearer tk_k120jegay3lugeqbr9fmpuxdqmzx5` (james admin account) - **Markdown:** Supported — use `-H "Markdown: yes"` header - **Headers:** Title, Priority (1-5), Tags (comma-separated emoji shortcodes) - **Use for:** Alerts when OC/Signal is down, urgent pings, status reports - **Johan subscribed on:** Android (ntfy app, Feb 15 2026) ### Health Push Script - **Script:** `/home/johan/scripts/health-push.sh` - **Cron:** `* * * * *` (every minute) - **Logic:** Checks MC health + OC health locally, pushes to Kuma only if healthy - **Alert routing:** - MC down → James (OC webhook) — James can investigate - OC down → Johan direct (ntfy) — James IS the thing that's down - Home network down → Johan direct (ntfy) — everything at home is down ### OpenProvider API (Domain Registrar) - **API:** `https://api.openprovider.eu/v1beta/` - **Creds:** `/home/johan/.config/openprovider.env` — `OP_USERNAME`, `OP_PASSWORD` - **Auth:** POST `/auth/login` → bearer token - **NS change:** PUT `/domains/{id}` with `{"name_servers":[...]}` - **List domains:** GET `/domains` - **I have full access** — don't ask Johan to change NS manually! ## Why Separate? Skills are shared. Your setup is yours. Keeping them apart means you can update skills without losing your notes, and share skills without leaking your infrastructure. --- Add whatever helps you do your job. This is your cheat sheet. ### Govee H5122 Buttons - **Button 1:** `event.gv5122775b_button_1` (MAC: D2:2D:83:86:77:5B) - **Automation:** `automation.govee_button_mbed_pendants_toggle` → toggles `switch.mbed_pendants` - **Button 2:** `event.gv51222839_button_1` (MAC: D2:2D:80:C6:28:39) — Office - **Automation:** `automation.govee_button_2_suction_machine` → toggles suction machine - **Pairing:** Hold 5 sec (LED flashes), press once to broadcast, add from HA UI quickly - **Note:** BLE proxy must be enabled on nearby Athom sensor (Office1 b372f4 has it now) ### Office Tablet (office1.tbl) - **Media player:** `media_player.lenovo_tab_m8_7` - **TTS notify:** `notify.lenovo_tab_m8_text_to_speech_7` - **Overlay notify:** `notify.lenovo_tab_m8_overlay_message_7` - **Screen:** `light.office_tbl_screen` - **Fully Kiosk media_player:** `media_player.office_tbl` - **Use for:** James voice output testing, announcements