From 1d5b62a50173ff3f5648214353aab15ae6311a30 Mon Sep 17 00:00:00 2001 From: Wanjohi Date: Sun, 11 Oct 2026 17:33:18 +0300 Subject: [PATCH 1/2] fix(api): a Steam sign-in on any host links the account A sign-in on a person's own host recorded the enrolment but never linked the Steam account, so their first launch asked them to link it again with nothing in the app to do it from. The QR sign-in is the same proof on their host as on ours. (cherry picked from commit 24ad9f6b788ef39568606e6b10d9ef532b127e33) --- apps/api/app/routes/enrolment.ts | 27 ++++++++------------------- apps/api/test/enrolment.test.ts | 6 +++--- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/apps/api/app/routes/enrolment.ts b/apps/api/app/routes/enrolment.ts index 1391e62d..e7eff5e2 100644 --- a/apps/api/app/routes/enrolment.ts +++ b/apps/api/app/routes/enrolment.ts @@ -1,7 +1,5 @@ import { Actor } from '@nestri/core/actor'; -import { Env } from '@nestri/core/env'; import { ErrorCodes, VisibleError } from '@nestri/core/error'; -import { Machine } from '@nestri/core/machine/index'; import { Enrolment } from '@nestri/core/steam/enrolment'; import { Identity } from '@nestri/core/user/identity'; import { Hono } from 'hono'; @@ -31,32 +29,23 @@ import { ErrorResponses, machineOnly, Result, validator } from '../utils'; */ export namespace EnrolmentApi { /** - * A sign-in on one of Nestri's own machines is also proof of the Steam - * account, so it links it: the person scanned a QR code on hardware we - * run, the Steam id is the subject of the token Valve issued there, and - * who they are came from the edge. Asking them to sign in to Steam a - * second time, in a browser, would prove nothing new. - * - * **Only the fleet.** On a host somebody brought, the report is that - * person's machine vouching for itself, and a machine can say anything. - * Linking from it would let any host operator attach any Steam account to - * any user enrolled on it. + * A Steam sign-in on a host is also proof of the Steam account, so it + * links it: the person scanned a QR code with the Steam app, the Steam id + * is the subject of the token Valve issued there, and who they are came + * from the edge. Asking them to sign in to Steam a second time, in a + * browser, would prove nothing new. * * Never a reason to refuse the report. The sign-in happened and the * enrolment is recorded either way; a Steam account already linked to * somebody else stays theirs, and is left for the person to sort out on * the website. */ - async function linkIfFleet(machineId: string, userId: string, steamId: string) { - const fleet = Env.get().FLEET_ORGANISATION_ID; - if (!fleet) return; - const machine = await Machine.fromID(machineId); - if (machine?.organisationId !== fleet) return; + async function link(machineId: string, userId: string, steamId: string) { try { await Identity.linkSteam({ userId, steamId }); } catch (err) { if (err instanceof VisibleError) { - console.warn('a fleet sign-in was not linked', { machineId, userId, reason: err.message }); + console.warn('a sign-in was not linked', { machineId, userId, reason: err.message }); return; } throw err; @@ -102,7 +91,7 @@ export namespace EnrolmentApi { userId: body.userId, steamId: body.steamId }); - await linkIfFleet(Actor.machineID, body.userId, body.steamId); + await link(Actor.machineID, body.userId, body.steamId); return c.json({ data: enrolment }); } ) diff --git a/apps/api/test/enrolment.test.ts b/apps/api/test/enrolment.test.ts index 9331c35f..9e56106d 100644 --- a/apps/api/test/enrolment.test.ts +++ b/apps/api/test/enrolment.test.ts @@ -393,7 +393,7 @@ describe('The enrolment surface refuses a token', () => { }); }); -describe('A sign-in on the fleet links the Steam account', () => { +describe('A sign-in on a host links the Steam account', () => { /** A fleet host the API knows is the fleet, and a person to enrol on it. */ async function fleetHost(label: string) { const operator = await Fixtures.owner(`${label}-operator`); @@ -436,11 +436,11 @@ describe('A sign-in on the fleet links the Steam account', () => { expect(await linked(steamId(9001))).toHaveLength(1); }); - test('a host somebody brought cannot link anything', async () => { + test('a host somebody brought links it too', async () => { const host = await registeredHost('own-host-link'); const res = await enrol(host, { userId: host.userId, steamId: steamId(9002) }); expect(res.status).toBe(200); - expect(await linked(steamId(9002))).toHaveLength(0); + expect((await linked(steamId(9002))).map((r) => r.user_id)).toEqual([host.userId]); }); test('an account already linked elsewhere stays there, and the report still lands', async () => { From 5f00d9bb9b6b37ffedba9fd6510a963bcb1ca4cd Mon Sep 17 00:00:00 2001 From: Wanjohi Date: Sun, 11 Oct 2026 17:33:47 +0300 Subject: [PATCH 2/2] chore(install): desktop app 0.1.1, host agent 0.2.14 --- apps/api/install/desktop-version.ts | 2 +- apps/api/install/install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/install/desktop-version.ts b/apps/api/install/desktop-version.ts index ed4cfd70..62522fa2 100644 --- a/apps/api/install/desktop-version.ts +++ b/apps/api/install/desktop-version.ts @@ -3,4 +3,4 @@ * release of that version is published, the same way the host installer's * default is, so what a person downloads changes only through review. */ -export const DESKTOP_VERSION = '0.1.0'; +export const DESKTOP_VERSION = '0.1.1'; diff --git a/apps/api/install/install.sh b/apps/api/install/install.sh index ce9ca2d1..47e147a2 100755 --- a/apps/api/install/install.sh +++ b/apps/api/install/install.sh @@ -24,7 +24,7 @@ set -eu API="${NESTRI_API:-https://api.nestri.io}" # Pinned, not "latest", so the script and the binary it installs are a pair # somebody chose. Bump when cutting a release; NESTRI_HOST_VERSION overrides it. -DEFAULT_VERSION="0.2.13" +DEFAULT_VERSION="0.2.14" VERSION="${NESTRI_HOST_VERSION:-$DEFAULT_VERSION}" BIN_DIR="${NESTRI_BIN_DIR:-/usr/local/bin}" STATE_DIR=/var/lib/nestri