feat: initial Go/JS/Rust Origin check-in client SDKs
CI / rust (push) Successful in 3m6s
CI / go (push) Successful in 33s
CI / js (push) Successful in 34s

Standalone clients that resolve a machine unique ID (OS machine ID first,
falling back to a generated UUID persisted to disk), detect hostname/IP/
container/orchestrator, and register+re-ping origin.warky.dev's public
service-checkin endpoint on a daily interval. Ported from icy2's internal
origincheckin package but extended to the full check-in contract and made
dependency-light for external reuse.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-01 15:25:30 +02:00
co-authored by Claude Sonnet 5
parent 282f76a021
commit 2590e404f5
40 changed files with 4105 additions and 181 deletions
+141
View File
@@ -0,0 +1,141 @@
import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
import { describe, expect, it } from "vitest";
import { OriginClient } from "../src/client.js";
function withServer(
handler: (req: IncomingMessage, res: ServerResponse) => void,
): Promise<{ server: Server; url: string }> {
return new Promise((resolve) => {
const server = createServer(handler);
server.listen(0, "127.0.0.1", () => {
const address = server.address();
const port = typeof address === "object" && address ? address.port : 0;
resolve({ server, url: `http://127.0.0.1:${port}` });
});
});
}
describe("OriginClient", () => {
it("requires serviceKey/type/version/name", () => {
expect(() => new OriginClient({ serviceKey: "", type: "", version: "", name: "" })).toThrow();
});
it("sends the expected payload and headers on checkinOnce", async () => {
let gotKey = "";
let gotBody: Record<string, unknown> = {};
const { server, url } = await withServer((req, res) => {
gotKey = req.headers["x-origin-service-key"] as string;
let raw = "";
req.on("data", (chunk) => {
raw += chunk;
});
req.on("end", () => {
gotBody = JSON.parse(raw);
res.writeHead(200, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
success: true,
service_instance_id: 42,
unique_install_id: gotBody.unique_install_id,
status: "provisioning",
pinged_at: "2026-08-01T00:00:00Z",
}),
);
});
});
try {
const client = new OriginClient({
url,
serviceKey: "test-key",
type: "icy2",
version: "v1.0.0",
name: "icy2-test",
installId: "test-install-id",
});
const resp = await client.checkinOnce();
expect(gotKey).toBe("test-key");
expect(gotBody.type).toBe("icy2");
expect(gotBody.name).toBe("icy2-test");
expect(gotBody.unique_install_id).toBe("test-install-id");
expect(resp.success).toBe(true);
expect(resp.service_instance_id).toBe(42);
} finally {
server.close();
}
});
it("throws on a non-2xx response", async () => {
const { server, url } = await withServer((_req, res) => {
res.writeHead(500);
res.end();
});
try {
const client = new OriginClient({
url,
serviceKey: "test-key",
type: "icy2",
version: "v1.0.0",
name: "icy2-test",
installId: "test-install-id",
});
await expect(client.checkinOnce()).rejects.toThrow();
} finally {
server.close();
}
});
it("does not throw for an unreachable server", async () => {
const client = new OriginClient({
url: "http://127.0.0.1:1",
serviceKey: "test-key",
type: "icy2",
version: "v1.0.0",
name: "icy2-test",
installId: "test-install-id",
});
await expect(client.checkinOnce()).rejects.toThrow();
});
it("start() checks in immediately and stop() prevents further calls", async () => {
let calls = 0;
const { server, url } = await withServer((_req, res) => {
calls += 1;
res.writeHead(200, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
success: true,
service_instance_id: 1,
unique_install_id: "x",
status: "provisioning",
pinged_at: "2026-08-01T00:00:00Z",
}),
);
});
try {
const client = new OriginClient({
url,
serviceKey: "test-key",
type: "icy2",
version: "v1.0.0",
name: "icy2-test",
installId: "test-install-id",
intervalHours: 24,
});
client.start();
await new Promise((resolve) => setTimeout(resolve, 50));
client.stop();
expect(calls).toBe(1);
} finally {
server.close();
}
});
});
+43
View File
@@ -0,0 +1,43 @@
import { describe, expect, it } from "vitest";
import { hostname, isContainerIdLike, parseCgroupContainerId } from "../src/hostInfo.js";
describe("parseCgroupContainerId", () => {
it("parses a cgroup v1 docker path", () => {
const data = "12:memory:/docker/9f8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b\n";
expect(parseCgroupContainerId(data)).toBe("9f8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b");
});
it("parses a cgroup v2 systemd scope", () => {
const data = "0::/system.slice/docker-9f8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b.scope\n";
expect(parseCgroupContainerId(data)).toBe("9f8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b");
});
it("returns empty on a non-container host", () => {
expect(parseCgroupContainerId("0::/user.slice/user-1000.slice/session-2.scope\n")).toBe("");
});
it("returns empty for empty input", () => {
expect(parseCgroupContainerId("")).toBe("");
});
});
describe("isContainerIdLike", () => {
it("accepts a hex id of valid length", () => {
expect(isContainerIdLike("9f8b7c6d5e4f")).toBe(true);
});
it("rejects short strings", () => {
expect(isContainerIdLike("short")).toBe(false);
});
it("rejects non-hex strings", () => {
expect(isContainerIdLike("session-2")).toBe(false);
});
it("rejects empty strings", () => {
expect(isContainerIdLike("")).toBe(false);
});
});
describe("hostname", () => {
it("returns a non-empty value", () => {
expect(hostname().length).toBeGreaterThan(0);
});
});
+44
View File
@@ -0,0 +1,44 @@
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import * as machineId from "../src/machineId.js";
function tempInstallIdPath(): string {
const dir = mkdtempSync(join(tmpdir(), "origin-client-test-"));
return join(dir, "install-id");
}
describe("resolveMachineId", () => {
afterEach(() => {
machineId.machineIdDetector.current = machineId.detectOsMachineId;
});
it("prefers an explicit override", () => {
machineId.machineIdDetector.current = () => "os-machine-id";
expect(machineId.resolveMachineId("explicit-id", tempInstallIdPath())).toBe("explicit-id");
});
it("uses the OS machine id when available", () => {
machineId.machineIdDetector.current = () => "os-machine-id";
expect(machineId.resolveMachineId(undefined, tempInstallIdPath())).toBe("os-machine-id");
});
it("falls back to a generated id and persists it across calls", () => {
machineId.machineIdDetector.current = () => undefined;
const path = tempInstallIdPath();
const first = machineId.resolveMachineId(undefined, path);
expect(first.length).toBeGreaterThan(0);
const second = machineId.resolveMachineId(undefined, path);
expect(second).toBe(first);
});
it("logs a warning when the OS id is unavailable", () => {
machineId.machineIdDetector.current = () => undefined;
const warn = vi.fn();
machineId.resolveMachineId(undefined, tempInstallIdPath(), { warn });
expect(warn).toHaveBeenCalled();
});
});