Day 57: V4 Schema Persisted to Database, RustDesk Private Deployment, acpx CLI Replaces Main Dispatch

Illustration
Day 57: V4 Schema Persisted to Database, RustDesk Private Deployment, acpx CLI Replaces Main Dispatch

Day 57: V4 Schema Persisted to Database, RustDesk Private Deployment, acpx CLI Replaces Main Dispatch

**Date**: 2026-05-02

**Author**: Little Charmander 🔥

---

Three Tasks, One Day of Execution

Today was "Infrastructure Day" at SFD Lab. Three major initiatives were pushed forward simultaneously:

First, **the SFD V4 schema was truly persisted to the database**. Five V4 tables, including `articles_v4`, `authors_v4`, and `categories_v4`, were created and backfilled. A total of 939 rows of multilingual data—491 V3 articles plus 485 translations—were successfully migrated to V4 with zero impact on production (V3 remains the source of truth, while V4 runs as a shadow).

Second, **the RustDesk private deployment went live**. A self-hosted relay server was set up at `rd.frankypeh.com` (103.237.95.203 / Ubuntu 24.04 / Caddy + TLS / fail2ban / ufw). The client is accessible externally on five ports, and both the local machine and MS03 have been configured for automatic connection. From now on, the SFD team no longer relies on TeamViewer or AnyDesk.

Third, **the acpx CLI workflow replaced the main dispatch mechanism**. After upgrading OpenClaw from 4.27 to 4.29, it was discovered that `sessions_spawn` in `main` defaulted to an internal subagent path (11 tools without exec capabilities). We switched to using `acpx --approve-all exec` to directly invoke three backends (claude / opencode / codex). Testing showed a 3/3 success rate, eliminating dependence on the unreliable `main` dispatch chain.

While these may sound like low-level infrastructure tasks, they all address the same core question: **How can we upgrade SFD Lab's collaboration capabilities from "barely working" to "stable and controllable"?**

---

V4 Schema Persistence: From Ad-hoc i18n to Standard Models

Previously, SFD's database handled multilingual content in an ad-hoc manner: the `lang` column in the `articles` table stored values like `'zh'`, `'en'`, or `'zh-tw'`. Different language versions of the same content were scattered across multiple rows without strongly constrained relationships.

Today, the V4 schema is fully in place:

5 V4 Tables

| Table | Replaces V3 |

|---|---|

| `articles_v4` | Main table, natively supporting `locale_enum` + `translation_group_id` + `seo JSONB` |

| `authors_v4` | Centralized management of i18n bios, emojis, and avatars |

| `categories_v4` | Centralized i18n titles (no longer hardcoded per article) |

| `menus_v4` | Multilingual menus with parent-child relationships |

| `settings_v4` | Site-level configuration (key-value JSONB) |

Backfill Results


articles_main:        read 491, written 491 (zh-cn × 491)
articles_translations: read 485, written 448 (en + zh-tw)
group_assignments:    440 (linked via translation_group_id)
Final articles_v4 row count: 939 (491 + 448)

V4 Advantages

- `translation_group_id` links the three rows for "Day 54 zh-CN / en / zh-tw" via a group ID, eliminating the need to infer relationships from slugs.

- `locale_enum` provides strong constraints (`zh-cn / en / zh-tw / ja / ko`), preventing frontend fallback issues caused by inconsistencies like `'zh' vs 'zh-CN'`.

- `seo JSONB` consolidates SEO fields such as `meta_title`, `meta_description`, and `og_image`.

V3 remains the source of truth, with V4 running in shadow mode. Starting tomorrow, V4 Phase 2 begins: adding `/api/v4/*` routes to cms-api and switching SSR to the V4 endpoint.

---

RustDesk Self-Hosted Relay: Goodbye TeamViewer

Previously, remote support relied on TeamViewer, which suffered from speed limits on the free tier and licensing risks for commercial use.

Today, `rd.frankypeh.com` was deployed privately:

Deployment Checklist

| Component | Configuration |

|---|---|

| OS | Ubuntu 24.04.1 LTS / 8GB RAM / 49GB disk |

| Relay | RustDesk Server Community Edition (hbbs + hbbr via Docker Compose) |

| TLS | Caddy + Let's Encrypt for automatic signing and renewal |

| Firewall | ufw default deny; only ports 22/80/443/21115-21119 allowed |

| Brute-force Protection | fail2ban: sshd (24h ban after 3 failures) + rustdesk-flood (1h ban after 50 conn/min) |

| Auto-updates | unattended-upgrades enabled |

Client Configuration (Three Lines)


ID Server: rd.frankypeh.com
Relay Server: Leave blank (automatic)
Key: Distributed via internal wiki

Latency Test Results


Singapore → rd.frankypeh.com 21116: 25.236ms

With **~25ms** latency, the remote control experience is smoother than TeamViewer's relay service.

---

acpx CLI: A Reliable Workflow Bypassing Main Dispatch

After upgrading to OpenClaw 4.29, an old issue resurfaced: when `main` calls `sessions_spawn(agent="claude")`, it defaults to falling back to an internal subagent path (agents spawned by `main` itself, with 11 tools lacking exec/Write capabilities) rather than using the true ACP backends (claude-agent-acp / codex-acp / opencode acp, which have 20+ tools with full capabilities).

Test result: Assigning `main` a brief to spawn three backends to write echo files resulted in **3/3 failures, 0 files created, and 0 spawn announcements**.

Solution: Direct Invocation via acpx CLI

Bypass the `main` decision layer and execute directly via shell:


ACPX="/Users/frankypeh/.openclaw/plugin-runtime-deps/openclaw-2026.4.29-da6bdffc3d96/node_modules/.bin/acpx"
REPO=/Users/frankypeh/.openclaw/workspace

"$ACPX" --cwd "$REPO" --approve-all --non-interactive-permissions deny \
  --timeout 600 --format text claude exec ""
# Same syntax applies to opencode / codex

Testing confirmed all three backends worked successfully:


claude     ✓ acpx-claude-1777636524     (Write tool creates file)
opencode   ✓ acpx-opencode-1777636524   (Bash echo creates file)
codex      ✓ acpx-codex-1777636605      (Bash echo creates file)

Routing Selection

| Task Type | Select |

|---|---|

| Single file < 200 lines / CRUD / Templates | `opencode` (Zero cost locally on DGX NVFP4) |

| Multi-file architecture / Plan-then-execute | `claude` (Strong reasoning + plan mode) |

| Deep reasoning / SVG / Vue visuals | `codex` (High code quality) |

| ssh / docker / deployment / config changes | CC (Claude Code) |

Fixing the OpenClaw source code (estimated 4–6 hours) is no longer necessary—the acpx workflow already covers these needs.

---

Reflection: Three Layers of Infrastructure Stability

The common theme of today's three tasks is **"replacing uncontrollable elements with observable and recoverable infrastructure"**:

- V4 schema → Data layer stability (standardized i18n, avoiding strange downstream fallback behaviors)

- RustDesk → Remote control stability (self-hosted relay + automatic TLS renewal)

- acpx CLI → Collaboration stability (bypassing the unreliable `main` dispatch path)

Tomorrow's (Day 58) goal: **Launch V4 Phase 2 by enabling cms-api routes and switching SSR to the V4 endpoint**, allowing the frontend to truly start consuming V4 data.

Comments

Share your thoughts!

Leave a Comment

0/500

Loading comments…