Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Backend selection uses a verified protocol allowlist, not browser inference.
[Compatibility](./DEPLOYMENT.md#herdr-compatibility) defines supported versions,
legacy fallback, and clipboard limitations.

Herdr 0.9.0 endpoints require generation 1 and exact codecs
Herdr 0.9.0 and 0.9.4 endpoints require generation 1 and exact codecs
`shell.snapshot.v1`, `shell.surface.v1`, `shell.input.semantic.v1`, and
`shell.blob.v1`. Unknown generations/codecs fail closed. Attachment waits for the
initial snapshot; each viewer crops its pane from the shared tab surface.
Expand Down Expand Up @@ -700,8 +700,10 @@ do not multiply alerts. Finished/needs-attention map to completed/blocked;
pane-less custom alerts use their sound (`request` = blocked). The runtime relays
each one to Web Push and to browsers as `roamgate.task_notification`; the bridge
hello advertises `herdr_task_notifications` so pages disable their own status
tracker. The shell reconnects with capped backoff. Servers without endpoint
support (older Herdr, or endpoints disabled) fall back to the status tracker
tracker. Herdr 0.9.4's Antigravity/Grok foreground-idle policy stays authoritative;
Roamgate does not synthesize extra alerts for their background tasks. The shell
reconnects with capped backoff. Servers without endpoint support (older Herdr,
or endpoints disabled) fall back to the status tracker
below, relayed the same way; a temporarily disconnected endpoint server does not.

The `status` source tracks agent transitions through per-pane subscriptions and
Expand Down
24 changes: 15 additions & 9 deletions docs/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ use the remote host's shell and tools.
### Herdr compatibility

This source build supports verified legacy protocols 14–20 (Herdr 0.7.0–0.8.2)
and **tagged Herdr 0.9.0 / protocol 22**. Protocol 21 and unknown versions are
rejected at control/binary probes. Use a compatible Roamgate build or separate
and **tagged Herdr 0.9.0 and 0.9.4 / protocol 22**. Protocol 21 and unknown versions
are rejected at control/binary probes. Use a compatible Roamgate build or separate
server; **do not downgrade a live server**. Published binaries follow their
[release notes](https://github.com/powerfooI/roamgate/releases).
The [plugin](#herdr-plugin) separately requires Herdr 0.7.2+.
Tab reordering requires Herdr 0.7.2+; Herdr 0.7.0 and 0.7.1 retain their fixed
tab order. The bridge confirms support before enabling dragging.

Herdr 0.9.0 uses stable endpoint generation 1, distinct from protocol 22.
Herdr 0.9.0 and 0.9.4 use stable endpoint generation 1, distinct from protocol 22.
`ROAMGATE_DISABLE_ENDPOINT=1` explicitly selects legacy direct-terminal fallback.

| Area | Behavior / limitation |
Expand All @@ -47,7 +47,7 @@ Herdr supplies no producing-pane/input identity: a delayed write from A after B
becomes foreground can reach B's recent input owner. This is not source-PTY or
original-browser isolation. Detach/replacement/disposal invalidates ownership.
Reads are disabled; permission failures show copy-retry UI. Ordinary copy/paste
is unchanged. OSC 52 is unavailable on the **0.9.0 legacy fallback**; older
is unchanged. OSC 52 is unavailable on the **protocol 22 legacy fallback**; older
servers retain their existing relay.

Closing one workspace never implicitly closes its linked group. If Herdr requires
Expand Down Expand Up @@ -100,14 +100,18 @@ roamgate herdr setup
roamgate herdr status
```

If absent, setup downloads **Herdr 0.9.0**, verifies build-pinned SHA-256 hashes,
If absent, setup downloads **Herdr 0.9.4**, verifies build-pinned SHA-256 hashes,
and installs to `~/.local/bin/herdr` on Unix or
`%APPDATA%\roamgate\herdr\0.9.0` on Windows. It never replaces a binary it did not
`%APPDATA%\roamgate\herdr\0.9.4` on Windows. It never replaces a binary it did not
install. If already installed, it uses the first binary on PATH or in those
locations, leaving it in place. Unix's standard location supports `herdr update`;
Windows uses Roamgate's private versioned directory, not the official junctioned
store, and needs a newer verified Roamgate install for replacement.

The verified Windows asset is x64 only; managed setup has no native Windows
ARM64 Herdr asset. Windows ARM64 SSH hosts can run Herdr's x64 build under
emulation on a best-effort basis.

Setup installs/starts `herdr server` as a user service: `roamgate-herdr.service`
(Linux), `dev.roamgate.herdr` (macOS), or a per-user Windows scheduled task.
Definitions carry a Roamgate marker; unrelated existing definitions are untouched.
Expand Down Expand Up @@ -604,9 +608,11 @@ notifications (Herdr 0.9+), so Roamgate alerts match Herdr's toasts, including
runtime for this; Herdr counts it as a connected client for `notification show`,
but it never takes focus or tab geometry. `status` (or older Herdr servers) instead
observes `working -> blocked` and `working -> done/idle`; startup snapshots are
silent. Delivery is best-effort: OS/Focus settings, outages, expired
subscriptions, or stopped runtimes can prevent it. Messages expire after five
minutes, with no durable replay; HTTP 404/410 removes expired subscriptions.
silent. Herdr 0.9.4 treats Antigravity and Grok as idle once they return to the
foreground prompt, even while background tasks or subagents continue; Roamgate
preserves that policy. Delivery is best-effort: OS/Focus settings, outages,
expired subscriptions, or stopped runtimes can prevent it. Messages expire after
five minutes, with no durable replay; HTTP 404/410 removes expired subscriptions.

Turning notifications off revokes on the server before browser unsubscribe.
If revocation fails, reconnect/retry or revoke OS/browser permission immediately;
Expand Down
2 changes: 1 addition & 1 deletion server/src/bridge/protocol-compat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export const MAXIMUM_HERDR_PROTOCOL = 22;

export class HerdrCompatibilityError extends Error {}

// Retain the verified legacy codecs (14-20) and add only tagged v0.9.0 (22).
// Retain legacy codecs (14-20); tagged Herdr 0.9.0 and 0.9.4 both use 22.
// Protocol 21 and future versions must never be echoed through either codec.
export function isSupportedHerdrProtocol(
protocol: unknown,
Expand Down
17 changes: 13 additions & 4 deletions server/src/herdr/cli.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, test } from "bun:test";
import { runHerdrCommand } from "./cli";
import { VERIFIED_HERDR_VERSION } from "./release";

function fakeConfig() {
return {
Expand Down Expand Up @@ -46,11 +47,17 @@ describe("runHerdrCommand", () => {
...io,
loadConfig: () => fakeConfig() as never,
detect: () =>
Promise.resolve({ state: "running", version: "0.9.0", protocol: 22 }),
Promise.resolve({
state: "running",
version: VERIFIED_HERDR_VERSION,
protocol: 22,
}),
serviceStatus: () => ({ installed: true, active: true }),
});
expect(code).toBe(0);
expect(io.out.join("\n")).toContain("running (version 0.9.0");
expect(io.out.join("\n")).toContain(
`running (version ${VERIFIED_HERDR_VERSION}`,
);
expect(io.out.join("\n")).toContain("installed, active");
});

Expand All @@ -76,12 +83,14 @@ describe("runHerdrCommand", () => {
Promise.resolve({
outcome: "installed-and-started",
binaryPath: "/managed/herdr",
version: "0.9.0",
version: VERIFIED_HERDR_VERSION,
protocol: 22,
}),
});
expect(code).toBe(0);
expect(io.out.join("\n")).toContain("Installed Herdr 0.9.0");
expect(io.out.join("\n")).toContain(
`Installed Herdr ${VERIFIED_HERDR_VERSION}`,
);
expect(io.out.join("\n")).toContain("/managed/herdr");
});

Expand Down
12 changes: 12 additions & 0 deletions server/src/herdr/release.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ describe("installVerifiedHerdr", () => {
test("extracts the Windows zip layout via PowerShell", async () => {
const homeDir = scratch();
const appDataDir = scratch();
const previous = herdrManagedBinaryPath(
homeDir,
appDataDir,
"win32",
"0.9.0",
);
mkdirSync(dirname(previous), { recursive: true });
writeFileSync(previous, "previous-herdr");
const content = "fake-zip";
const expectedSha256 = createHash("sha256").update(content).digest("hex");
const installDir = dirname(
Expand Down Expand Up @@ -187,6 +195,10 @@ describe("installVerifiedHerdr", () => {
expect(commands).toHaveLength(1);
expect(commands[0].join(" ")).toContain("Expand-Archive");
expect(result.installed).toBe(true);
expect(result.binaryPath).toBe(
join(appDataDir, "roamgate", "herdr", "0.9.4", "herdr.exe"),
);
expect(await Bun.file(previous).text()).toBe("previous-herdr");
expect(
existsSync(join(result.binaryPath, "..", "conpty", "conpty.dll")),
).toBe(true);
Expand Down
12 changes: 6 additions & 6 deletions server/src/herdr/release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { join } from "node:path";
* Herdr release against Roamgate's supported protocol range
* (server/src/bridge/protocol-compat.ts).
*/
export const VERIFIED_HERDR_VERSION = "0.9.0";
export const VERIFIED_HERDR_VERSION = "0.9.4";
export const VERIFIED_HERDR_PROTOCOL = 22;

export type HerdrReleaseTarget =
Expand All @@ -23,15 +23,15 @@ export type HerdrReleaseTarget =

export const VERIFIED_HERDR_SHA256: Record<HerdrReleaseTarget, string> = {
"linux-x86_64":
"4fa1a01158dd8043da92d31b270780b0dcc10603038d9b61cac4d81ab63fb71f",
"493304fbefb0ed0ea0455f373b47f48d24e8774bc7974f4c916ad2e937675b23",
"linux-aarch64":
"9c8db20fb7e7427b138d5367113f1621ffd319f2f65d6f009e2594029115f0d2",
"9511cb70982c1ef115f232058e7ed7cf9640aa5a2c0ca78934cefda3aeb093fd",
"macos-x86_64":
"d0c920b2a126a74809fa1491411c9a097a44786cac9c2ca51b818a995581cf16",
"2015ca1de3e93b84cdcccbfd94fbbd65dd56338cea4d3e57a8660e977a3bd700",
"macos-aarch64":
"32b53df09872628059c789a69f02a6b8e29e14ddf26711421f3463f70c1aef17",
"ea20bfff9cb8c83a996081c926e6ff9370de8e543baa859bca48d19175d895ff",
"windows-x86_64":
"b4508c445de1c1a68c760a01735da2aba2fa214b2aafd4b07f732e49b2a64b11",
"0e4ccb5cd7b6ca464a69637cda66d3c93334ebc057c50c5878b8e89e1a20afee",
};

export function resolveHerdrReleaseTarget(
Expand Down
Loading