From 8d72dabca4c2ed842950c03a108d149abf0c23af Mon Sep 17 00:00:00 2001 From: Bunsy <268838774+bunnykong@users.noreply.github.com> Date: Wed, 26 Aug 2026 10:54:52 -0700 Subject: [PATCH] Resolve installed updater trust root --- PUBLIC-SOURCE.json | 18 +++++++------- packages/kernel/src/public/update-channel.ts | 24 +++++++++++++++---- .../kernel/test/public-update-channel.test.ts | 19 ++++++++++++++- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/PUBLIC-SOURCE.json b/PUBLIC-SOURCE.json index e8e6a42..b700d32 100644 --- a/PUBLIC-SOURCE.json +++ b/PUBLIC-SOURCE.json @@ -2,8 +2,8 @@ "schema_version": "bitter.public_source_projection.v0", "profile": "public-beta", "private_source": { - "commit": "f5c60a56ba300eeee88a1a704e8b70c609cc657d", - "tree": "e19e0b23bd78fef471c14b3f03492367655d8f73", + "commit": "1238df863e83698f48eebb63c3ee2a51e2809bce", + "tree": "25fef6423916f144d7b6c97f648fc6dad15987a0", "projection_manifest_path": "packages/kernel/public-source-projection-v0.json", "projection_manifest_sha256": "6915ea3e184159ce63193b97a28559f30de223214f278bf3bc5547b1f35d0c60" }, @@ -68,7 +68,7 @@ "scripts/verification/" ] }, - "projection_digest": "sha256:11c491570cf993d456385f8dc52e8b139a189f2d9510263dfbb7f67890085209", + "projection_digest": "sha256:0d1a97c06d3565fc8cc13ca7e5c9621bc3ba67635dd518ce188d721aa6c594b2", "files": [ { "path": ".github/workflows/ci.yml", @@ -785,10 +785,10 @@ { "path": "packages/kernel/src/public/update-channel.ts", "mode": "0644", - "size": 53429, - "sha256": "a5319019ea3f8846c00b55274725c8f89593acd858ac9e79d9e48eb8737a8469", + "size": 53823, + "sha256": "49031c5395ae2e981ed1353e66cc2d81e6e12fb2583c0a3441ecfdd5bce5710a", "source_path": "packages/kernel/src/public/update-channel.ts", - "source_blob": "411ddd719fad44a371f95dd882234abc812aebe0" + "source_blob": "44904e98f929736b8789c873a4d18b7303f79765" }, { "path": "packages/kernel/src/tui/sanitizer.ts", @@ -873,10 +873,10 @@ { "path": "packages/kernel/test/public-update-channel.test.ts", "mode": "0644", - "size": 63858, - "sha256": "df87c72d74adebc51668a7ecdf7248aa0c9360f52d07f251a16f9ca2b17e6a41", + "size": 64386, + "sha256": "4e3ab44e93ca1f4734a8e6f3579e8929e006face63be89ce435bf29fc2ee8708", "source_path": "packages/kernel/test/public-update-channel.test.ts", - "source_blob": "d36d2a919551a658481a766e59ff0983327aa1f5" + "source_blob": "a24cc50bd66555e661f20f37c4e0677144ab550a" }, { "path": "README.md", diff --git a/packages/kernel/src/public/update-channel.ts b/packages/kernel/src/public/update-channel.ts index 411ddd7..44904e9 100644 --- a/packages/kernel/src/public/update-channel.ts +++ b/packages/kernel/src/public/update-channel.ts @@ -3,7 +3,7 @@ import { createHash, createPublicKey, randomUUID, verify } from "node:crypto"; import { constants } from "node:fs"; import { type FileHandle, link, lstat, mkdir, open, rename, rm, unlink } from "node:fs/promises"; import { hostname } from "node:os"; -import { dirname, join, resolve } from "node:path"; +import { basename, dirname, join, resolve } from "node:path"; import { BitterError } from "../errors.ts"; @@ -375,10 +375,13 @@ export function resolvePublicBitterHome(env: EnvLike): string { const configuredManagedRoot = env.BITTER_MANAGED_ROOT?.trim() ? resolve(env.BITTER_MANAGED_ROOT) : null; + const managedBitterHome = configuredManagedRoot + ? bitterHomeFromManagedRoot(configuredManagedRoot) + : null; if ( configuredBitterHome && - configuredManagedRoot && - configuredManagedRoot !== join(configuredBitterHome, "current") + managedBitterHome && + managedBitterHome !== configuredBitterHome ) { fail( "kernel.public.update_trust_root_invalid", @@ -387,8 +390,8 @@ export function resolvePublicBitterHome(env: EnvLike): string { } const bitterHome = configuredBitterHome ? configuredBitterHome - : configuredManagedRoot - ? dirname(configuredManagedRoot) + : managedBitterHome + ? managedBitterHome : env.HOME?.trim() ? resolve(env.HOME, ".local", "share", "bitter") : null; @@ -401,6 +404,17 @@ export function resolvePublicBitterHome(env: EnvLike): string { return bitterHome; } +function bitterHomeFromManagedRoot(managedRoot: string): string { + const runtimeDirectory = dirname(managedRoot); + if ( + basename(runtimeDirectory) === "runtimes" && + /^sha256-[0-9a-f]{64}$/.test(basename(managedRoot)) + ) { + return dirname(runtimeDirectory); + } + return dirname(managedRoot); +} + function trustedReleasePublicKeyPath(env: EnvLike): string { return join(resolvePublicBitterHome(env), "trust", "release-public-key.pem"); } diff --git a/packages/kernel/test/public-update-channel.test.ts b/packages/kernel/test/public-update-channel.test.ts index d36d2a9..a24cc50 100644 --- a/packages/kernel/test/public-update-channel.test.ts +++ b/packages/kernel/test/public-update-channel.test.ts @@ -24,7 +24,11 @@ import { installSignedPublicRuntime, linkSignedPublicLauncher, } from "../src/public/runtime-update.ts"; -import { type UpdateFetch, checkPublicUpdateChannel } from "../src/public/update-channel.ts"; +import { + type UpdateFetch, + checkPublicUpdateChannel, + resolvePublicBitterHome, +} from "../src/public/update-channel.ts"; const NOW = new Date("2026-07-14T12:00:00.000Z"); const MANIFEST_URL = "https://registry.bitter.sh/channels/stable/darwin-arm64.json"; @@ -522,6 +526,19 @@ describe("public signed update channel", () => { ).rejects.toMatchObject({ code: "kernel.public.update_trust_root_invalid" }); }); + test("locates installed trust from a launcher-resolved immutable runtime", () => { + const bitterHome = join(tmpdir(), "bitter-installed-home"); + const physicalRuntime = join(bitterHome, "runtimes", `sha256-${"a".repeat(64)}`); + + expect(resolvePublicBitterHome({ BITTER_MANAGED_ROOT: physicalRuntime })).toBe(bitterHome); + expect( + resolvePublicBitterHome({ + BITTER_HOME: bitterHome, + BITTER_MANAGED_ROOT: physicalRuntime, + }), + ).toBe(bitterHome); + }); + test("exposes verification as check-only while keeping activation closed", async () => { const fixture = await makeFixture(roots, { sequence: 21 }); const envelope = await publicUpdate({