Skip to content

Lesson 02 — Typed RPC and desktop/core separation ​

Outcome ​

Make a tiny typed request from a client to the Go core, such as GetServerInfo, without exposing filesystem/process power to the renderer.

Why this comes now ​

Before product behavior, prove the most important ownership boundary. Once UI code starts directly reading files or spawning commands, extracting that logic later is painful.

Understand ​

The RPC API is not remote database access. Mutations will eventually be domain commands. For now one read-only health/version method is enough. Protobuf gives one contract source and generated types; Connect works over normal HTTP and supports Go and web clients.

Electron main owns starting the core and handing the renderer a narrow connection configuration. The renderer remains unprivileged.

Build the real project ​

  1. Define a minimal forge.v1.SystemService/GetServerInfo protobuf message/service.
  2. Configure Buf/code generation.
  3. Implement the Connect handler in Go.
  4. Generate the TypeScript client.
  5. From a tiny renderer/test client, show core version/status.
  6. Bind the core to loopback only and add a random per-launch/session credential before exposing command APIs.

Completion gate ​

Generated Go and TS code comes from one schema. A round trip succeeds. Renderer code contains no child_process, filesystem, or arbitrary Electron IPC command runner.

Pitfalls to avoid ​

Do not design 50 RPCs now. Do not hand-maintain duplicate DTOs. Do not add a generic ExecuteCommand(string) endpoint.

References ​

Required: Connect for Go, Connect for Web, Buf. Read also docs/architecture/RPC_AND_APP_BOUNDARY.md.

Checkpoint ​

Advance to Lesson 03 only after you can draw the renderer → RPC → application/core boundary from memory.

Forge is local-first. The docs are part of the product engineering system.