Repository navigation
F20: first installation in one command — the executable verifies its release, install.sh for a new computer, prepared prompt for a non-standard installation - #62
Conversation
…release, install.sh for a new computer, prepared prompt for a non-standard installation Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Independent |
agentrozjedemeai
left a comment
There was a problem hiding this comment.
@immakermatty Exact-head review of aa63155 (base 0fa47cf). REQUEST_CHANGES; no merge.
[P1] install.sh:139-145, 91-125 — The new wget-only path follows redirects with wget -q -O without refusing HTTPS -> HTTP (docs/update.md:222-225 explicitly acknowledges this). On a computer without signed-in gh, a plaintext hop can supply both a forged manifest and matching executable. The script digest comparison then succeeds and executes that untrusted executable before its self-verification; malicious bytes can simply skip --verify-release. Thus this path does not preserve the stated HTTPS first-use boundary. This is not a request to pin the manifest digest. Please fail closed on a downgrade, or drop the wget-only fallback until every redirect can be constrained to HTTPS; add a negative redirect test. The existing wget shim tests only copy files and cannot detect protocol downgrade.
[CI gate] On this exact head, Platform checks: Ubuntu SUCCESS, macOS FAILURE (tests/cli.test.ts default 5,000 ms timeout, 756 pass / 5 skip / 1 fail / 1 unhandled error); Macroscope SKIPPED. The independent macOS bun run check comment reports 758 pass / 4 skip, but does not turn the live failed check green. In my isolated clone, bun install --frozen-lockfile, focused serial tests with 20 s timeout (27 pass), lint, typecheck, and git diff --check passed; an initial default-timeout focused run also hit a 5 s test timeout. Please repair/rerun the macOS check and obtain green exact-head checks.
Other reviewed boundaries: verifyOwnRelease precedes install-base writes and checks version, commit, artifact size/digest and the same attestation routine used by update; refused fixture verifications left no installation. The staged Machines install --base path is intentionally offline and unchanged in the core. curl constrains redirect protocols; the script uses private temporary storage with cleanup and does not call sudo or edit profiles. No claim of a real GitHub release / Sigstore end-to-end proof, or native wget/Rosetta execution.
…allback Review of pull request 62: with wget -q -O the script followed redirects by wget's rules and could not refuse a downgrade to plain HTTP. A plaintext hop could then supply a forged manifest and a matching executable; the digest comparison would pass and the executable would start before its own attestation check, which malicious bytes skip. GNU wget 1.x cannot restrict the scheme of a redirect, and following redirects by hand would have to be right for GNU wget, wget2 and BusyBox wget alike without a way to prove it, so the wget path is removed rather than widened: a computer with only wget is told how to install curl and nothing is requested. The latest lookup already refuses anything but the exact-tag HTTPS URL of the repository; a test now names the http:// case. The curl shim of the tests honours --proto-redir like curl 8 (its refusal text observed against a loopback TLS server redirecting to plain HTTP) and serves a forged manifest and executable over an http:// hop; the new test proves the shim would hand them out without the flag, and that the script stops at the first download, runs nothing and installs nothing. The tests run under LAZURIO_TEST_SHELL (/bin/sh by default; also /bin/dash and bash --posix). docs/update.md states the curl-only trade-off; F20 gains the rejected wget alternative. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
It compiles src/cli.ts and runs the binary about a dozen times. On the hosted runners it took 2.0 to 4.4 s on main as well (Ubuntu 4.35 s and macOS 4.01 s on 0fa47cf), against bun's default of 5 s; on this branch the macOS runner hit 5.0 s. Locally main and this branch take the same time (five alternating runs each: 2.07-2.23 s and 2.12-2.25 s), so the slice did not slow it; it is runner load. The "unhandled error" of that run was the same timeout: bun killed the abandoned test's child (exit 143) and the test body's next assertion failed after the test had ended. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
agentrozjedemeai
left a comment
There was a problem hiding this comment.
@immakermatty Exact-head re-review: APPROVED for b2ace73 against main 0fa47cf. No merge performed.
The prior HTTPS downgrade finding is addressed: install.sh now requires curl, constrains initial transfers and followed redirects to HTTPS, and rejects an http:// latest-tag redirect. The wget-only route is gone. The negative fixture proves a forged matching manifest/executable would be served without --proto-redir and is rejected before execution with it; HOME and TMPDIR remain clean. The compiled CLI test has an explicit 30 s timeout.
Exact-head required Platform checks: ubuntu-24.04 SUCCESS and macos-14 SUCCESS (run 36415402131, SHA verified); optional Macroscope SKIPPED. Zero unresolved review threads. In an isolated clean clone, install-script and CLI focused tests passed 12/12 with a 30 s local test override, install-script tests passed 10/10 under /bin/dash and /bin/bash --posix, lint/typecheck and diff --check passed. One initial local default-5-s run timed out in a different install-script test under host load; it passed on rerun with 30 s override. Non-blocking follow-up: give this other slow test an explicit timeout if this recurs in CI. This does not claim a real GitHub release/Sigstore end-to-end installation or a deployed lazurio.ai/install route.
…ee times (#70) The attestation test of install.sh runs the fixture installer with a signed-in gh, a signed-out gh and a failing attestation, each a real process, and timed out at bun's 5 s default under a load average of five (seen in #62's review and in the author's check of #69). It gets the 30 s the CLI tests already use. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…hem (Anička, DEV-6645) (#226) * Shell: the design system re-vendored at fdcc9b4 (three radii, one floating shadow) src/shell/vendor/lazurio/tokens.css is copied byte for byte from HumanAndMachine-ai/design-system-lazurio fdcc9b4 (#62). Since the previous pin 5bbc1f7 (#54) only this file of the vendored ones changed, in #58 and #59 (Anička, 2026-10-04): the comment on the radii now names three radii, one per kind of thing, and --lz-shadow-float (0 8px 24px, ink at 10 %) is new, the one shadow of a layer floating above the page. The logo symbol, the stones and Iconoir's licence are byte for byte as before. src/shell/vendor/README.md names the new source commit, what changed and the new SHA-256; tests/apps-view.test.ts ("every vendored file has the hash its README records") passes against it. Nothing reads the new token yet; the shell's styles take it in the next commit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Shell: the rail and the Environment picker as Anička revised the wireframe (DEV-6645) The elements follow the shell wireframe at prototypes-lazurio 1acd615 and the design system at fdcc9b4 (prototypes-lazurio #11, #14, #15, #17; design-system-lazurio #57, #58, #59, #62). The rail is one grid step, 64 px instead of 72, and sets --lazurio-rail-width to 64px: 40 px items with radius-md around 32 px marks with radius-sm, concentric corners, an item under the pointer brings the quiet surface without changing its shape, smaller icons (16 px), "+" a 32 px dashed square, the account's photo 28 px, dividers 24 px. The active space keeps B3; its spread shadows now fill exactly the 40 px item. Your personal monogram is a mark on the raised colour with a hairline, never an ink block, in the rail and in the picker. Without roles the rail stands on paper like the app's column. Every radius is one of the three tokens; the list under the picker, the ⌘⇧E dialog and the rail's label carry --lz-shadow-float with a hairline, and the switch's active tab a hairline instead of its drop shadow. The picker's list is one line per Environment of the space (switcherList in view.ts): no Organization head and no title, a search field only past seven Environments, only "Všechny Organizace" at its foot; widened, the field and every space by its title, still without heads and without a foot. The current row has the check, and "tady jsi" is for screen readers only. ⌘⇧E keeps the Organization's heads above the same rows and its key hints. The keys (switcherKey) work with or without the field: arrows wrap, Home and End outside the field, Enter, Escape; without the field the focus lands on the current Environment, else the first, and moves with the keys and the pointer. The Launchpad page's fallbacks of --lazurio-rail-width are 64px. Tests: tests/shell-picker.test.ts pins the list, the threshold, the foot, the jump's heads, the keys and the rail's width; the suites that asserted the head in the picker now assert it in the jump only. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Launchpad: the Organization's name in the Apps head opens its Dashboard (DEV-6645) The picker no longer leads to an Organization's Dashboard (prototypes-lazurio #17, design-system-lazurio #62): the Organization's name in the page's head does, as the wireframe's .lp-head__link. appsHeading in apps-view.ts makes the name a link to the Organization's page as the shell document names it (the same lookup "Přístup k modulu" uses), titled "Dashboard Organizace" / "Organization Dashboard", in the same window. The personal space ("Osobní"), the home without a group and an Organization the document does not name stay plain text. The link has the heading's colour and no underline, and is underlined 2 px with a 4 px offset under the pointer. Tested in tests/apps-view.test.ts: an Organization, one the shell document does not name, one the Folder cannot read, the personal space and the home. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Docs: F36 addendum, the rail and the picker as the wireframe revised them (DEV-6645) docs/decisions.md records the addendum of 2026-10-06, decided by Anička and confirmed by Matěj: the 64 px rail with one corner family and the personal monogram as a mark, three radii, one shadow for a floating layer, the compact picker without the Organization's head, the keys with or without the search field, ⌘⇧E keeping its heads, the Organization's name in the Apps head opening its Dashboard, what the colour roles now colour, and that no v1 document or interface name changes. It names what it supersedes in the Organization-rail addendum (point 2), the colour addendum (point 1) and the addendum of 2026-10-05 (point 6), which carry dated notes; what is left as it is (the picker's button and gear, the icons' stroke) and where the shell keeps its own rule; the alternatives; and how it was verified, with the measurements against the wireframe at prototypes-lazurio 1acd615. docs/launchpad-development.md describes the same in the target shell (the rail, the picker, the selection, the Apps header), in the shell as built ("The look", the column head on an Organization's Dashboard, a space without an Environment), in the colour roles table and the test list. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Shell: the picker and its gear as the design system draws them; icons at the wireframe's stroke of 1.6 (DEV-6645) Matěj asked that the Environments and the Dashboard follow the wireframe as it stands, so the column head now takes the design system's values (design-system-lazurio fdcc9b4) as the wireframe draws it since prototypes-lazurio #8, still its state at 1acd615: - The picker is lz-picker: at least --lz-space-48 tall, --lz-space-8 around, --lz-space-12 between its parts, radius-md; 52 px with its two lines (was 47). Its glyph is lz-picker__mark, --lz-space-32 with radius-sm, an Organization's mark or your monogram 24 px inside, the kind's icon filling it; the name --lz-size-nav, the line --lz-size-meta. The row is lz-picker-row, --lz-space-4 apart. - The gear is lz-button--icon: a --lz-space-48 square with radius-sm in the picker's ink (was 40 px wide and muted). Under the pointer it keeps the quiet surface of the column's other controls: the design system's quiet button takes paper, which does not show on the paper column; under a host's roles both are --lazurio-hover. - In a narrow host (@container max-width 160px) the picker and the gear stand as 48 px squares, one above the other; the 40 px override is gone. - The glyph takes the picker's ink, so on a dark host without roles a kind's icon is light instead of near-black (issue #169). - The switch's tabs take line-height normal, 49 px as the wireframe. Every interface icon, src/shell/icons.ts and the Launchpad page's 32 inlined symbols, is drawn at stroke 1.6, as the wireframe's Icon.tsx has done since before 1cbad15 (the provenance notes said 1.5); the notes now cite 1acd615. The F36 addendum gains point 5 (the picker and its gear) and loses the two items it left as they were; its verification and colour-roles point say what changed. No test pinned the old sizes. Checked: bun run check (1463 pass, 7 skip, 0 fail). In Chrome against the wireframe: the column head 191 x 52 beside the 48 px gear, the 32 px glyph, 15 and 14 px text, the switch 49 px, icons at 1.6, all equal; in T3 Code's dark sidebar at 208 and 264 px, its light one, MausBot's Midnight, a dark host without roles and a 64 px host, nothing overflows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Docs: the F36 addendum's verification says the column head, not the page, was drawn narrow (DEV-6645) The verification of the addendum on the rail and the picker said that narrow widths were not exercised, while the column head was drawn in a 64 px host and at the 160 px limit. It now says that the column head was checked in the Launchpad's preview too, and that the page at narrow window widths was not exercised. The paragraph is rewrapped; nothing else changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Why
Decisions of the Principal on 2026-09-28: the first installation of Lazurio on a new computer is one command, an install script served from
https://lazurio.ai/install; it installs the CLI and the Launchpad, afterwardslazurio updatetakes over. Lazurio is installed exactly the standard way everywhere; a deviation is reported and an agent straightens it by the manual.The existing
install.shverified the release attestation only whenghwas on the computer. A customer's new laptop has nogh, so there the download was trusted on HTTPS alone.What changes (decision F20)
install.shdownloads the executable, the manifest and the attestation into a private directory, holds the executable against the manifest's SHA-256 before running it, and runs<executable> install --verify-release <dir>. The executable uses the same verification aslazurio update(now one shared function) and refuses to install otherwise. A refusal leaves nothing behind.lazurio install --basefrom a pinned file, as the Machines role uses it, needs no network. Both paths are named in the code and the docs.install.shrewritten for a computer with onlycurlandsha256sumorshasum; every download and every redirect is held to HTTPS (--proto '=https' --proto-redir '=https'). There is nowgetfallback: nowgetfamily can refuse a redirect from HTTPS to HTTP in a way that could be tested here, so a computer withoutcurlis told which package to install and the script stops before any request. Otherwise: plain messages, no sudo, no shell profile edits, refusal of unsupported platforms with the supported ones named, the next command printed at the end.lazurio install prompt: the prepared prompt for an agent that straightens a non-standard installation (what the standard is, what was found, what the agent may do, what only on the operator's instruction, how success is proven). Czech and English. Read-only.docs/update.mdwith the contract for the website route, README, decision F20.The trust chain, stated plainly
release.ymldid not publishgh attestation verify, when a signed-inghexistsThe first installation stays trust on first use. Every later update is authenticated by the installed product.
What does not change
.github/workflows/release.yml: attaching and attestinginstall.shas a release asset is a separate pull request. Until it merges, no release carries the asset and the interim address is the script of this repository.src/launchpad/*,src/tools/*, the instruction template revision, the update contract.Not verified, stated plainly
--verify-releaseexists, that older executable refuses the call and nothing is installed.install.shasset and do not know--verify-release.https://lazurio.ai/installis not deployed; the website route is a separate change in another repository.Open questions for the Principal (not blocking)
~/Lazurio.Verification
bun run checkon this head, run by the delegating agent after mergingmain: see the comment below.Refs #50
🤖 Generated with Claude Code