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.