Skip to content

herdr-core: show a PC's other machines through its own hc connection, not SSH bridges #2497

Closed

Description

@nahwan-kim

Problem

With OVIS_RUNTIME=herdr-core, Ovis shows only the terminals of its own core. Remote PCs still go through an SSH bridge on herdr's TUI server (docs/guide.md, Herdr app terminals: "Remote PCs keep running herdr's TUI server").

herdr-core has its own machines (hc machine add, machine.list), and the Herdr app uses them. The app keeps one connection to the local core and reaches every other machine through it: a remote terminal is <machine>/t3 (hc ls), its layout is doc.get {name: "herdr-app", on: <machine>} (herdr-app src/mirror_sync.rs), and attach/input use the same prefixed address (herdr-app src/hc.rs).

So for someone using the Herdr app on two machines:

  • the app shows spaces on both machines;
  • Ovis on herdr-core shows this machine's spaces, and for the other machine either nothing or a remote PC of herdr's TUI server, whose sessions are unrelated to the app's;
  • the other machine's terminals also come back from terminal.list here as <machine>/tN with no tab in this core's layout document, so they land under Other terminals next to this PC's own.

Seen with hc 0.1.15 (macOS) holding a machine on hc 0.1.14 (macOS), on main at 6797a8b.

Proposal

On herdr-core, take remote PCs from the core's machines:

  • Roster. Each machine in machine.list (not temporary) is a PC. Its state follows the machine's (connected or not). This PC's own panes are the unprefixed addresses only.
  • Snapshot. A PC's terminals are terminal.list's <machine>/… entries. Its workspaces and tabs come from doc.get {name: "herdr-app", on: <machine>}, laid out as the local ones are (server/herdr-core/snapshot.ts).
  • Terminal, input, read. These go over the same hc connection with the prefixed address. This is the attach the Herdr app makes; no PTY host, no extra hc process (as in feat(herdr-core): stream core terminals over the server's hc connection #2447).
  • Status and alerts. These come from events.subscribe on the same connection, which already carries the prefixed terminals' events ("terminal":"lab/t14", checked with hc events).
  • Adding a PC. This is hc machine add <ssh target>; that machine needs hc, and the core keeps the SSH session from then on. Nothing of Ovis is installed on that PC: no bundle, no bridge, no second server.
  • Creating workspaces/tabs and starting agents on a remote PC. These map to terminal.run with on: <machine> and the same herdr.space/herdr.tab labels as feat(herdr-core): create and close workspaces, tabs and panes, and start agents, on herdr-core #2449.

Open questions:

  1. Should a server on herdr-core still list SSH remote PCs on herdr's TUI server beside these, or are those a herdr-runtime feature only?
  2. What should a PC's id be: the hc machine's id (stable across renames) or its name?

Where

Remote PCs, Other (server/runtime plumbing)

Where it exists today

The Herdr app works this way. It runs one local core, reaches other machines through it, and draws their spaces from each machine's herdr-app document. hc run --on <machine> and hc exec --on do the same from the CLI.

Workarounds and alternatives

What I run now, in a fork: Ovis with OVIS_RUNTIME=herdr-core on every PC. A connection server on herdr-core tunnels over SSH to each PC's own Ovis, which it finds by a runtime: "herdr-core" field in the bridge descriptor. It also checks /api/bridge's herdr.runtime (#2451) before using it, and never starts its TUI bridge there. It works, but:

  • every PC needs Ovis installed and kept running by hand;
  • a server on herdr-core then cannot show herdr TUI remote PCs at all;
  • it duplicates what the core already does (SSH, reconnects, versions).

The hc-machines design above avoids all three. If it is wanted, I can send it as a PR.

A smaller fix stands on its own either way: leave <machine>/… terminals out of this PC's snapshot and events, so another machine's terminals stop showing under Other terminals. I can send that as its own PR if useful.

Activity

  1. grandprixgp commented on Nov 2, 2026

    @grandprixgp
    Contributor

    Adding a use case, since this design assumes the machine holding the other machines can run Ovis on herdr-core. In my setup, it can't.

    My setup looks like this:

    • Clients: Android, and Windows laptop/desktop
    • Server: a Windows host
    • Guests on that host: Linux VMs and Windows VMs

    The Windows host's core already holds a Linux guest as an hc machine (hc 0.1.16). hc ls there lists its terminals as <machine>/tN, and the Herdr app shows both together. So the core is the natural place for Ovis to get one view of everything.

    But OVIS_RUNTIME=herdr-core refuses to start on Windows ("no Windows named pipe support yet"), so today I can only run Ovis on a Linux guest. That guest holds no machines, and for it to hold the host I'd have to open SSH into the Windows host. I also haven't tested whether hc machine add works against a Windows target.

    So the hc-machines design here looks right to me, but for my setup it only helps alongside herdr-core on Windows.

    Two questions:

    1. Is anyone already working on this issue? I don't want to duplicate it.
    2. Would herdr-core on Windows be welcome as its own feature? If so, I can open a feature request, and I'm happy to test it on this setup or send a PR once there's a maintainer's yes.
  2. nahwan-kim commented on Nov 2, 2026

    @nahwan-kim
    ContributorAuthor

    Thanks for the use case, that's a useful setup to have on record.

    1. I'm not working on this right now, and I don't know of anyone else who is.
    2. The "no Windows named pipe support yet" refusal is a separate runtime limitation, not part of this design. Please open it as its own feature request with your setup (Windows host, Linux/Windows VMs, Android and Windows clients). Whether it's taken is the maintainers' call.

    This proposal only assumes the core that holds the machines can run Ovis on herdr-core, so your setup would benefit from it once that works on Windows. I haven't tested hc machine add against a Windows target either.

  3. Ploppy3 commented on Nov 2, 2026

    @Ploppy3

    Adding today's occurrence, with Ovis 0.4.22 (release, Linux) and hc 0.1.17 on both ends.

    Setup

    • The computer runs Ovis with OVIS_RUNTIME=herdr-core. Its core holds vps, added with hc machine add ubuntu@vps; hc machine ls shows it connected.
    • In the Herdr desktop app, a space created on the vps machine starts terminal vps/t14 (/home/ubuntu, bash), labelled herdr.space=spcbce5b343adb1390ea2c8d2dc12f3490, herdr.tab=tb95c750c5dafd590daa3c8313926b93a7, where=ubuntu / 1.

    What Ovis shows

    • No PC for the vps machine: GET /api/machines lists only local. server/herdr-core/ never asks for machine.list, so a machine of this core can never appear.
    • The remote terminal is instead grouped as a workspace of the local PC. server/herdr-core/snapshot.ts groups() takes every live terminal from terminal.list; one whose herdr.space is not in this core's herdr-app document falls into the "not placed" branch, which fabricates a group labelled from labels.where — here a workspace named ubuntu under ordinateur-robin. It holds vps/t14, whose cwd and process live on the VPS.

    Removing the unrelated TUI machine inherited from herdr machine list does not change this: the TUI PC goes away and the fabricated ubuntu workspace stays.

    Expected

    As proposed above: vps is a PC of its own, its spaces come from doc.get {name: "herdr-app", on: "vps"}, its panes are vps/tN, and New workspace/tab create with terminal.run { on: "vps" }.

    Smaller defect that stands alone

    A <machine>/tN address must never become a workspace, tab or pane of this PC, even before the full support lands. Today it silently invents a local workspace, which is what makes a VPS space read as a computer space in the sidebar.

  4. added a commit that references this issue on Nov 5, 2026
    d2a2c6a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions