Recipes

DynWork: systemprompt sign-in with live manifest sync into a private OpenCode service

Discovered from README cross-links · yesterday

Security, Science & HardwareSoftware & DevOps

Forked OpenCode app signs in to systemprompt instances and syncs content live.

What they did

Built a fork of the OpenCode desktop app (DynWork) that connects to systemprompt 0.76 instances. The sign-in screen now runs on real auth: personal access token, one-time code, or a browser device link using a loopback listener on 127.0.0.1 with a checked state parameter. Multiple instances can be configured with one active at a time. The PAT is stored only as Electron safeStorage ciphertext, JWTs stay in main-process memory and renew at 80% of their lifetime, and no token reaches a renderer window or OpenCode's credential table or database. The packaged prod build runs its own OpenCode service on port 17495 with its own registration, config, DB, state and data under userData, so it never adopts or replaces OpenCode.app's service. A server plugin (@dynwork/server-plugin) maps the verified manifest into skills, sub-agents, remote MCP servers (JWT only for the instance's own origin) and rules as system context, plus a systemprompt model provider that calls the instance gateway with the JWT, base URL pinned and one retry on 401. The sync engine polls the active instance's signed manifest (30 s focused, 5 min unfocused, ETag, backoff), verifies the ed25519 signature against a pinned key, and pushes only changes; switching instances swaps content in under a second and signing out clears it. UI work covers a Settings → systemprompt page (instances, trust key, sync now, sign out, add instance) and a titlebar pill. Fork hygiene: one Vite build seam (@dynwork/build), a patch guard with a 250-line budget (94 used across 19 upstream files), a dynwork workflow, an upstream sync script with a range-diff report, and a local systemprompt 0.76 harness (sp-local.ts) with an end-to-end script (e2e-sp.ts). The client was written clean-room from observed HTTP behaviour and their own PROTOCOL.md, never from bridge source.

How it went

Full suite green: bun run check passes and bun test reports 0 failures across desktop (239), gui-plugins (256), branding (67), build (39), gui (172), server-plugin (65) and sp-client (84, live included). The e2e script ran green in 108 s against two local instances: PAT and one-time-code sign-in, a skill added and removed on the instance reaching DynWork in under 30 s, instance switching in 0.5 s, a moved app still loading the plugin, sign-out leaving nothing behind, and no PAT/JWT/code in plaintext across 621 profile files or the OpenCode DB. Security and code review ran on every security phase; the one real finding (a JWT for instance B could reach instance A during a switch while a call waited for renewal) is fixed and tested. Upstream rehearsal against upstream/dev stops on bun.lock at commit 2 because v2.2.5 is not an ancestor of dev; the fix is to regenerate the lockfile during the rebase, to be automated in the sync agent.

Worth knowing

Terminals (PTY), MCP stdio servers and LSPs started by the service see DynWork's XDG dirs; only the bash tool's shells get the user's environment back. The service drops a fixed list of the user's OPENCODE_* variables. systemprompt 0.76+ and macOS only. The browser device link needs the instance's own login (Entra on Dynapps instances). The e2e simulates window focus because macOS refuses focus to an app started from an unattended session; the sync code path is unchanged. Upstream sync is not yet automated and currently breaks on the lockfile.

Wiring

Order: (1) fork OpenCode and introduce one Vite build seam (@dynwork/build) plus a patch guard with a 250-line budget and a dynwork CI workflow; (2) write PROTOCOL.md from observed HTTP behaviour and build sp-client against it, with a local systemprompt 0.76 harness (sp-local.ts); (3) add the auth layer in the Electron main process — PAT via safeStorage ciphertext, JWT in memory with 80% renewal, device-link loopback listener on 127.0.0.1 with state check; (4) register a dedicated OpenCode service on port 17495 with its own config, DB, state and data under userData, isolated from OpenCode.app; (5) install @dynwork/server-plugin to map the verified manifest into skills, sub-agents, remote MCP servers (JWT scoped to the instance origin) and rules, plus the systemprompt model provider with pinned base URL and one 401 retry; (6) add the sync engine polling the signed manifest with ETag and backoff, verifying ed25519 against the pinned key and diffing before push; (7) wire Settings → systemprompt UI and the titlebar pill; (8) add e2e-sp.ts covering two local instances, sign-in paths, sync latency, switching, sign-out and a plaintext-secret scan over profile files and the OpenCode DB; (9) add an upstream sync script with a range-diff report.

Try it yourself

In this repo, add a systemprompt sign-in and live-sync layer to the OpenCode desktop fork. Requirements: (1) sign-in screen supporting personal access token, one-time code, and a browser device link via a loopback listener on 127.0.0.1 with a checked state param; multiple instances, one active. (2) Store the PAT only as Electron safeStorage ciphertext; keep JWTs in main-process memory, renew at 80% of lifetime, never expose tokens to renderer windows or OpenCode's credential table/DB. (3) Run a dedicated OpenCode service on port 17495 with its own registration, config, DB, state and data under userData; never adopt or replace OpenCode.app's service. (4) Add @dynwork/server-plugin that maps the verified manifest into skills, sub-agents, remote MCP servers (JWT only for the instance's own origin) and rules as system context, plus a systemprompt model provider calling the instance gateway with the JWT, base URL pinned, one retry on 401. (5) Poll the active instance's signed manifest (30 s focused / 5 min unfocused, ETag, backoff), verify the ed25519 signature against a pinned key, push only changes; switching swaps content in under a second, sign-out clears it. (6) Add Settings → systemprompt page and a titlebar pill. (7) Keep a single Vite build seam (@dynwork/build) and a patch guard with a 250-line budget. Write the client clean-room from observed HTTP behaviour and a PROTOCOL.md, not from bridge source. Add a local systemprompt 0.76 harness and an e2e script covering sign-in, sync latency, switching, sign-out and a plaintext-secret scan.

Source

Tools in this stack