feat: persist APN relay secret across restarts, add dev logging and server identification to pair UI

- Electron desktop: auto-start PushRelay with secret persisted in electron-store
- CLI serve (Tauri): persist relay secret to Global.Path.state/relay-secret (mode 0600)
- Pair endpoint now returns relayURL, serverID, relaySecretHash for debugging
- Desktop settings-pair component shows server name, relay URL, and secret hash above QR
- Add console.debug logging for pairing fetch lifecycle
- Export PushRelay from node.ts entry point for Electron consumption
This commit is contained in:
Ryan Vogel 2026-04-17 22:31:02 +00:00
commit 754951bbbd
7 changed files with 154 additions and 28 deletions

View file

@ -10,6 +10,10 @@ declare module "virtual:opencode-server" {
export const listen: typeof import("../../../opencode/dist/types/src/node").Server.listen
export type Listener = import("../../../opencode/dist/types/src/node").Server.Listener
}
export namespace PushRelay {
export const start: typeof import("../../../opencode/dist/types/src/node").PushRelay.start
export const stop: typeof import("../../../opencode/dist/types/src/node").PushRelay.stop
}
export namespace Config {
export const get: typeof import("../../../opencode/dist/types/src/node").Config.get
export type Info = import("../../../opencode/dist/types/src/node").Config.Info

View file

@ -1,8 +1,20 @@
import { randomBytes } from "node:crypto"
import { app } from "electron"
import { DEFAULT_SERVER_URL_KEY, WSL_ENABLED_KEY } from "./constants"
import { getUserShell, loadShellEnv } from "./shell-env"
import { store } from "./store"
const DEFAULT_RELAY_URL = "https://apn.dev.opencode.ai"
const RELAY_SECRET_KEY = "relaySecret"
function getOrCreateRelaySecret(): string {
const existing = store.get(RELAY_SECRET_KEY)
if (typeof existing === "string" && existing.length > 0) return existing
const secret = randomBytes(18).toString("base64url")
store.set(RELAY_SECRET_KEY, secret)
return secret
}
export type WslConfig = { enabled: boolean }
export type HealthCheck = { wait: Promise<void> }
@ -32,7 +44,7 @@ export function setWslConfig(config: WslConfig) {
export async function spawnLocalServer(hostname: string, port: number, password: string) {
prepareServerEnv(password)
const { Log, Server } = await import("virtual:opencode-server")
const { Log, Server, PushRelay } = await import("virtual:opencode-server")
await Log.init({ level: "WARN" })
const listener = await Server.listen({
port,
@ -41,6 +53,18 @@ export async function spawnLocalServer(hostname: string, port: number, password:
password,
})
const relayURL = (process.env.OPENCODE_EXPERIMENTAL_PUSH_RELAY_URL ?? DEFAULT_RELAY_URL).trim()
const relaySecretInput = (process.env.OPENCODE_EXPERIMENTAL_PUSH_RELAY_SECRET ?? "").trim()
const relaySecret = relaySecretInput || getOrCreateRelaySecret()
if (relayURL && relaySecret) {
PushRelay.start({
relayURL,
relaySecret,
hostname,
port: listener.port,
})
}
const wait = (async () => {
const url = `http://${hostname}:${port}`