John's System Configuration

Documentation of personal system configuration patterns and conventions.

Topics

SOCKS5 and Service Tunnels — stunnel + s5pin.py for reaching remote services as local ports

VNC Setup — x11vnc (display :0 mirror) and tightvncserver (display :1 virtual Xfce desktop) on pomelo

VNC Alt+Tab / Window Switching — RealVNC SendSpecialKeys fix; ≤/≥ as XFCE window switcher (in progress)

Infrastructure TLS / Stunnel PKI — mutual-TLS PKI for pomelo↔server tunnels; cert locations and renewal

Modular Shell Configuration

Uses a modular approach to shell configuration rather than one monolithic .bashrc file.

~/.bashrc Pattern

The main ~/.bashrc loops over files in ~/.bashrcs/ and sources them IF they are executable.

Benefits:• Easy enable/disable: chmod +x to enable, chmod -x to disable• Modular: separate concerns into different files• No commenting out blocks of code• Clean organization

Shell preferences

• No colour in the shell prompt or output — plain text only.• PS1 set via ~/.bashrcs/ps1 (executable). Current format: \D{%a %d %b} \u@\h:\w\$ — gives e.g. Fri 06 Mar john@pomelo:~/py/envoy$

Example: API Keys

File: ~/.bashrcs/api_keys

Contains: OpenAI API key exports

Permissions: 700 (read/write/execute for owner only)

Usage in cron: . /home/john/.bashrcs/api_keys — sources the file to load environment variables

System-Level: /etc/rcs/

Similar pattern exists at system level:

• Directory: /etc/rcs/• Run from: rc.local• Purpose: System-level initialization scripts• Same executable flag pattern for enable/disable

Cron Environment

Cron runs with minimal environment — doesn't source ~/.bashrc or ~/.profile

Solution: Explicitly source needed config files in crontab:

. /home/john/.bashrcs/api_keys; python3 orchestrator.py

Mail Infrastructure

IMAP: Dovecot on cv.critchley.biz (port 993 IMAPS). Accessed locally via stunnel on port 143 (/etc/stunnel/stunnel.conf). Envoy connects to hostname imap (resolves to localhost).WebDAV maildir: webdav.critchley.biz/mail/john — used by both Envoy and popit3. Credentials in ~/.netrc.Local SMTP: Postfix on localhost. Some providers (Hotmail, Swisscom/bluewin) block SMTP from this host.

Email Accounts

• envoy@critchley.biz — Envoy's IMAP account• jsr_critchley@hotmail.com — Outlook/Hotmail (SMTP: smtp-mail.outlook.com:587, OAuth2)• john.critchley@bluewin.ch — Swisscom/bluewin account• john@critchley.biz — Primary address

Best Practices

• Keep sensitive files (like api_keys) with 600 or 700 permissions• Use absolute paths in cron jobs• Source only what's needed for each cron job• Document which scripts depend on which config files

WebDAV and Writeback (WB) Access

Do NOT use the /z mount for this. /z is a davfs2 mount of the same share and is convenient for interactive poking about, but davfs2 caches writes locally and uploads them asynchronously. A write through /z can return success — and read back correctly through the same mount, because it is served from cache — while nothing has reached the server. Data lands on flush/unmount, not on close, and is lost if the mount or host goes first. Never use /z in a script, or anywhere the write is expected to have arrived. Use the client methods above against the URL, and verify with a HEAD afterwards.

• Files on gravlax that live under the WebDAV share (https://webdav.critchley.biz/, maps to /var/www/webdav/) should be read via a WebDAV client (python webdav4 or requests with ~/.netrc), not SSH — simpler and avoids SSH permission issues. Credentials live in ~/.netrc (machine webdav.critchley.biz, login john). Never hard-code the password.

• WB (writeback) is https://webdav.critchley.biz/writeback/ — where John posts command output/screenshots for Claude. To "check WB", list the share, take the newest file by modified time, and read it:

import netrc, io
from webdav4.client import Client
user, _, pw = netrc.netrc().authenticators("webdav.critchley.biz")
c = Client("https://webdav.critchley.biz/writeback/", auth=(user, pw))
files = [e for e in c.ls("/", detail=True) if e.get("type") == "file"]
latest = max(files, key=lambda e: e.get("modified") or "")
buf = io.BytesIO(); c.download_fileobj(latest["name"], buf)
print(latest["name"], buf.getvalue().decode())

• To save a remote file (e.g. from a DAP host) into a note, POST to the notes API from gravlax rather than reading it locally first; wrap the content with python json.dumps for correct escaping.

Desktop / X Session

• xearth is built and installed at ~/bin/xearth (from ~/src/xearth-1.1/) but is NOT wired to the root window — xfdesktop still runs as XFCE session Client4. An attempt to replace xfdesktop with xearth failed (xearth ran but did not paint the root window; likely a compositing / xfwm4 issue). To re-attempt: set /sessions/Failsafe/Client4_Command via xfconf-query to xearth and killall xfdesktop; revert by setting it back to xfdesktop.

Direct-wired NAS on pomelo's enp1s0 (added 2026-09-15)

A WD My Cloud NAS is connected directly to pomelo's spare ethernet port (enp1s0, no switch) — no DHCP server on that link by default, so it never got an IPv4 address on its own. Fixed with: pomelo statically configured as 192.168.99.1/24 on enp1s0 (nmcli connection modify netplan-enp1s0 ipv4.method manual ipv4.addresses 192.168.99.1/24), plus a dedicated dnsmasq instance bound only to enp1s0 (--port=0 to disable its DNS side) handing out 192.168.99.50-150 with a fixed reservation for the NAS's MAC → 192.168.99.2 (--dhcp-host=<mac>,192.168.99.2). The NAS picks this up within seconds of the dnsmasq instance starting.

For the Mac (on the normal 192.168.0.x WiFi LAN) to reach the NAS on 192.168.99.x through pomelo: IP forwarding on pomelo (usually already on, from Docker), a static route on the Mac (route -n add 192.168.99.0/24 192.168.0.2), and — the part that actually blocked it — an explicit iptables -I DOCKER-USER -i wlo1 -o enp1s0 -j ACCEPT (and the reverse direction). Docker sets the FORWARD chain's default policy to DROP and routes everything through DOCKER-USER first; that chain is the right place for custom rules since Docker won't flush it on restart. Without this rule, ping/SMB to the NAS from the Mac just times out with no obvious cause.

Before connecting this NAS to any other network: disable its own DHCP server first (via its web admin UI). If it's running one and gets plugged into a LAN that already has a DHCP server (e.g. the home router), it becomes a rogue DHCP server and can hand out bad leases to other devices on that network.

Throughput: transfers to the NAS from the Mac ran at ~10-13MB/s, not gigabit — confirmed the bottleneck is the Mac's WiFi (802.11n, 270Mbps PHY), not pomelo's routing (enp1s0 to the NAS negotiates a full 1000Mb/s) or software forwarding overhead (negligible at these speeds). Plan: get a switch, wire pomelo + NAS + the Mac (via its unused en0 port) all into it — removes the WiFi hop entirely for that traffic. Not yet done (2026-09-15).

version 7  ·  updated 2026-09-15