release: ship runnable Linux and macOS binaries instead of identity text files #360
Labels
No labels
priority/P0
priority/P1
priority/P2
release/v0.1.0
status/blocked
status/planned
type/bug
type/design
type/test-gap
type/tracker
No milestone
No project
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
natsukium/felis#360
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Parent: #31. Decided 2026-09-23 after the
v0.1.0-rc.2rehearsal.Decision
The release ships runnable binaries for every supported target and drops the identity text files.
felis-x86_64-linux.txtandfelis-aarch64-darwin.txtpinned a store path in the binary cache; a user on a machine without Nix could not run anything from the release page for two of the three claimed targets. Binary assets replace them:x86_64-linux: one relocated tarball (felis-x86_64-linux.tar.gz) carrying the package's binaries, the glibc and windowing libraries they need, terminfo, completions and man pages (felis,felis-client,felis-daemon, terminfo, completions), built from the same gate-verified derivation the cache receives. It must work on a distribution without Nix, and the client's daemon auto-spawn (current_exe().parent().join("felis-daemon")) must keep finding the daemon inside the tree.aarch64-darwin:felis-aarch64-darwin.tar.gzwithfelis.app(client + daemon, fromnix/make-macos-app.sh), thefelisCLI beside it, the terminfo source, README and LICENSE. Every/nix/storedylib the Mach-O binaries link is copied intoContents/Frameworksand the install names rewritten, so the app runs on a Mac without Nix. Ad-hoccodesign; Developer ID signing and notarization are out of scope for v0.1.0 (first launch needs right-click → Open).x86_64-pc-windows-msvc: the zip, unchanged.felis-config.schema.jsonandfelis.proto: unchanged.The binary cache push stays as the Nix user's path; it is no longer what the release page points at for a non-Nix user.
Cascade
release.yml: the two identity steps stop producing.txtassets;verify.py --identitykeeps checking the built binary's reported identity against the tag before anything is packaged. New packaging jobs (or steps inlinux-publish/darwin-publish) upload the tarball and the tarball as run artifacts; thereleasejob downloads and attaches them; the asset-presence check names the new set; release notes name the assets.nix/: a derivation or bundler for the tarball; a dylib-bundling step for the app (may extendmake-macos-app.shbehind a flag sonix buildon a Nix Mac still produces the store-referencing app).docs/reference/workspace.mdasset table and release-gate paragraphs;docs/explanation/testing.md"Why a support claim needs a passed gate" (the durable-distribution clause now points at the binary assets);docs/how-to/install.md(a section per target for the release assets, keeping the flake path first);README.md"Status and compatibility"; the grep sweep for.txt,identity,store pathacross docs/.Acceptance criteria
felis.proto, and no.txt.felis --version, andfelis-clientopens a window and spawns its daemon, on anx86_64-linuxhost without Nix.felis.appfrom the tarball launches on a Mac without Nix (otool -Lshows no/nix/storepath).Amended 2026-09-23: Linux ships a tarball rather than an AppImage. A type-2 AppImage's FUSE mount ends with the launcher's child, but the daemon outlives the window; and the pinned nixpkgs has no appimagetool. See the "Triage plan (2026-09-23)" comment for the reviewed design.
Triage plan (2026-09-23)
Reviewed design (eight adversarial rounds, PASS). Deviates from the body above in one point: Linux ships a relocated tarball, not an AppImage; the reasons are in the Decision section. The issue body is amended accordingly.
Goal
A tag run attaches, for each of the three claimed targets, an archive a
person unpacks and runs on a host without Nix:
felis-x86_64-linux.tar.gzfelis-aarch64-darwin.tar.gzfelis.appwith the CLI inside it, abin/felislauncher, terminfo, README, LICENSE; no/nix/storereference; ad-hoc signedfelis-x86_64-pc-windows-msvc.zipfelis-config.schema.json,felis.protofelis-x86_64-linux.txtandfelis-aarch64-darwin.txtare dropped. Thecache push and the
verify.py --identitygates stay exactly as they are.Notarization is out of scope.
Decision: tarball, not AppImage (deviation from the issue text)
Issue #360 says AppImage for Linux. Two facts found while grounding the
design change that:
unmounts when its direct child exits. felis's daemon is designed to
outlive the window that spawned it (spawn.rs: "The child is not killed
on this binary's exit; it outlives the window"). A daemon executing
from the mount would take SIGBUS on its next page-in after the first
window closes. Working around it means an AppRun that copies the
payload to a persistent directory first, which is a self-extracting
installer wearing an AppImage extension.
appimagetool/appimagekit; the runtimewould be fetched from GitHub releases.
A tarball with the same relocation gives the user the same thing (unpack,
run
bin/felis), matches the Windows zip and the macOS tarball, and has nomount lifetime. The issue body and plan comment will record this change.
If an AppImage is wanted later it is an AppRun over this exact tree.
Facts the design rests on (verified on the built package)
nix build .#felison x86_64-linux:bin/felis,bin/felis-daemonare plain ELF;
bin/felis-clientis the makeWrapper shell script andbin/.felis-client-wrappedthe ELF (package.nixpostFixup).ldd: all three DT_NEED onlylibc,libm,libgcc_s, withPT_INTERP
/nix/store/…-glibc-2.42-84/lib/ld-linux-x86-64.so.2.libvulkan.so.1,libwayland-client.so.0,libwayland-egl.so.1,libxcb.so.1,libX11.so.6,libX11-xcb.so.1,libXcursor.so.1,libXi.so.6,libxkbcommon.so.0,libxkbcommon-x11.so.0. Fontconfig is the pureRust
fontconfig-parser(reads/etc/fonts/fonts.confand the XDGfont dirs itself); nothing links libfontconfig or libfreetype.
expat, libxml2, brotli, bzip2, libpng, zlib, dejavu) is dead weight for
the tarball.
ld.soaccepts--library-pathand--argv0.current_exe().parent().join(name)with a PATH fallback (spawn.rs
sibling_daemon, felis-clifrontend_program); the systemd hand-off uses the sibling path or aPATH lookup, else forks. One more reader: the SSH relay
(
crates/felis-daemon/src/relay.rs::spawn_persistent_daemon) re-execscurrent_exe()itself asserveto start a cold daemon on the remotehost.
gitea-runner-default.service, DynamicUsergitea-runner) hasSupplementaryGroups=dockerandRestrictNamespaces=no; the host runs a Docker daemon. Foreign-hostsmokes can therefore run in an Ubuntu container.
readelf -dover the closure:libwayland-client.so.0NEEDs
libffi.so.8; the X11 chain is libX11 → libxcb → libXau,libXdmcp; libXcursor → libXrender, libXfixes; libXi and libXrandr →
libXext.
libxkbcommon.so.0embeds the store paths oflibx11/share/X11/locale(compose tables) andxkeyboard-config, andhonors
XLOCALEDIR,XKB_CONFIG_ROOT,XKB_CONFIG_EXTRA_PATH.felis sessions spawn -- <cmd>starts a cold daemon (cli.md line 560);felis versiondeliberately does not (RemoteSpawn::Refuse).felis daemon stopexists for cleanup.make-macos-app.shcopies the client asContents/MacOS/felis(CFBundleExecutable) and the daemon beside it, writes Info.plist, no
signing, no install-name work. The CLI
felisis not in the app. Thedarwin runner builds with nix;
/usr/bin/codesignis part of the baseOS,
otool/install_name_toolare not guaranteed (Xcode CLT) butnixpkgs
cctoolsandsigtoolprovide them.stdenv ad-hoc signs outputs, and
install_name_toolinvalidates thatsignature, so every edited file must be re-signed.
Linux: relocated closure
Layout of
felis-x86_64-linux/Launcher
Each
bin/<name>is:Why the loader is invoked directly: PT_INTERP is an absolute path and the
tarball can live anywhere; running the bundled
ld.sowith--library-pathmakes the closure's libraries win without patchelf, and--argv0keeps clap's usage line and the daemon's process name honest.Why
.ld.sosits inbin/: under a direct loader exec,/proc/self/exeis the loader, so
current_exe().parent()is the loader's directory.Placing the loader in
bin/keepsbin/felis-daemonandbin/felis-client(the launchers) as the siblings the code alreadylooks for. The systemd hand-off passes the absolute launcher path,
which is fine as long as the tree is not deleted; that is the same
contract as any installed binary.
One Rust change, in the relay:
spawn_persistent_daemoncurrentlyexecs
current_exe(), which under the loader is.ld.soand would run.ld.so serve …. It changes to the same rule the client uses: thesibling
felis-daemonnext tocurrent_exe()when that file exists,else
current_exe()itself. In every existing layout ($out/bin, theWindows zip, a cargo target dir) the sibling is the very file
current_exe()names, so behavior there is unchanged; in the archive thesibling is the launcher. A unit test covers the sibling-present and
sibling-absent cases. Not wire, not CLI surface.
Why the launcher sets exactly one variable, and only as a default: the
client snapshots its whole environment for every session it creates
(
env_base.rs::capture) and the daemon hands that snapshot to PTYchildren (
serve.rs), so anything a launcher exports reaches the user'sshells and the host programs under them.
LOCPATHandGCONV_PATHaretherefore off the table: a host glibc given
LOCPATHlooks only there,and bundled gconv modules loaded into a host glibc are a crash. Locale
resolution is left to the bundled glibc's own fallback to the host's
/usr/lib/locale/locale-archive, which resolvesen_US.UTF-8on a hostthat has it (the smoke asserts this).
XLOCALEDIRis the one export:libxkbcommon's compose tables come from there and nothing else can
supply them; the variable is a standard libX11 one, the data is the
upstream X11 locale tree, so a host program that inherits it reads the
same tables it would have read from
/usr/share/X11/locale. A user whoset it keeps their value.
xkeyboard-configis not bundled andXKB_CONFIG_ROOTis not set: winit takes the keymap from the Waylandcompositor or the X server, never from RMLVO names. The launcher
template is the same for all three binaries.
Why host directories come after
lib/: the GPU stack (libvulkan.so.1,the ICDs it loads,
libEGL/libGL) must be the host's, because driversare host state. Everything the host supplies is loaded into a process
running the bundled glibc 2.42, which works when the host's driver libs
were linked against a glibc no newer than that. This is the one host
assumption; install.md states it and names Vulkan as a host requirement.
Bundled closure subset
Every path of the current 35-path closure is classified; the derivation
fails if a closure path is in neither list, so a new runtime dependency
breaks
nix build .#felis-distinstead of shipping or going missing.Bundled (
lib/*.so*copied intolib/):glibc(libraries only: itslib/gconvandlib/localeare not shipped, see the launcher notes;character conversion in the client is UTF-8 through Rust, and libX11's
XIM path needs only what
setlocaleresolves from the host archive),gcc-*-libgcc(thelibgcc_s.so.1the felis binaries resolve throughgcc-*-lib, whoselib/libgcc_s.so.1is a symlink to it;gcc-*-libitself is classified as bundled but contributes only that symlink, which
cp -Lfollows),libffi,libx11,libxcb,libxau,libxdmcp,libxcursor,libxrender,libxfixes,libxi,libxext,libxrandr,libxkbcommon,wayland. Plus one data tree:libx11'sshare/X11/locale→share/X11/locale, pointed at byXLOCALEDIR, socompose and dead-key input work off Nix. Fourteen of its
Composefilesincludetheen_US.UTF-8table by absolute store path; the copyrewrites that prefix to
%S(libX11's own token for the localedirectory, which libxkbcommon also expands), and the build then fails if
grep -r /nix/store share/finds anything. Plus the package's ownshare/terminfo,share/bash-completion,share/zsh,share/fishand
share/mantrees, copied as they are.Excluded:
vulkan-loader,libglvnd(host GPU stack);fontconfig,fontconfig-lib,freetype,expat,libxml2,brotli-lib,bzip2,libpng-apng,zlib,zlib-static,dejavu-fonts-minimal(nothinglinks the font stack);
libidn2,libunistring(glibc'slibnss_*extras, unused);
xgcc-*-libgcc(glibc's bootstraplibgcc_s.so.1,a different binary from gcc's; glibc's own libraries resolve
libgcc_s.so.1by name and get the bundled gcc one, which is what theyget on every NixOS system where gcc's is first on the path);
xkeyboard-config(keymaps arrive from the compositor or server);bash(the wrapper's interpreter);felisitself.Flattening policy: every file is copied with symlinks dereferenced
(
cp -L); a filename that would be written twice with differingcontents fails the build (
cmpbefore overwrite); after assembly,find -type lunder the tree must showno link whose target leaves the tree.
The derivation also verifies the bundle is closed:
readelf -dNEEDEDover
lib/andlibexec/must resolve insidelib/except for thenamed host-supplied set (
libvulkan.so.1,libEGL,libGL, and theICDs they load are dlopen targets, not NEEDED, so the NEEDED closure is
expected to be fully self-contained).
Derivation
packages.<system>.felis-dist(nix/dist.nix,callPackagewithfelis), output$out/felis-<triple>.tar.gzwhere triple isx86_64-linuxoraarch64-darwin. Tar is built reproducibly(
--sort=name --mtime=@1 --owner=0 --group=0 --numeric-owner,gzip -n)so a local
nix build .#felis-distyields the same bytes CI attaches.The tree is assembled by
nix/dist-linux.sh/nix/dist-macos.sh,which also run standalone on a
cargo build --releasetree the waymake-macos-app.shdoes, so the two paths cannot disagree.CI
New job
linux-package(runs-on nix, needs[verify-tag, linux-smoke]):nix build .#felis-dist, extract to$RUNNER_TEMP, then:three launchers and
.ld.so, the three ELF files,lib/libc.so.6,share/X11/locale/compose.dir, the compiledxterm-felisentry, thethree completion files, the
felis.1man page, README.md, LICENSE),and
grep -l /nix/storeover the three launcher scripts andgrep -rlovershare/finds nothing. ELF files keep store strings(RUNPATH in the binaries, and the loader
.ld.socarries its owncompiled-in cache, locale and gconv paths), all inert under
--library-path, so.ld.so,lib/andlibexec/are excluded fromthe textual check; instead
readelf -doverlib/andlibexec/must show every NEEDED resolved inside
lib/, and the container smokeis what proves the loader works without
/nix.docker run --rm -v <tree>:/opt/felis:ro ubuntu:24.04(glibc 2.39, no Nix), withapt-get install locales xvfb libvulkan1 mesa-vulkan-drivers libwayland-client0 libxkbcommon-x11-0so the host GPU stack is areal foreign one
(lavapipe over Ubuntu's own glibc, loaded into the bundled 2.42
process: the exact mixing the design relies on). Inside it:
a.
/opt/felis/bin/felis --version.b. Daemon autospawn:
felis --socket <tmp>/daemon.sock sessions spawn -- true(an isolated socket, as linux-smoke does), then assert thedaemon's
/proc/<pid>/exeis/opt/felis/bin/.ld.soand itscmdline names
/opt/felis/libexec/felis-daemon. Then a PTY child'sreal environment:
felis --socket … sessions spawn -- sh -c 'env > /tmp/child.env', and assert the file has noLOCPATH,GCONV_PATH,LD_LIBRARY_PATHorXKB_CONFIG_ROOT, and thatXLOCALEDIRis the bundled path. Thenfelis --socket … daemon stop --force(a plainstoprefuses while an exited session sitsin its five-second reap grace).
b1. Cold relay spawn, the
felis sshremote path: with/opt/felis/binon PATH and a fresh socket, run
felis-daemon relay --socket <tmp>/relay.sock < /dev/null(the relay exits on EOF; the daemon itspawned must not), then assert a daemon whose
/proc/<pid>/exeis.ld.sois serving that socket (felis --socket … sessions list),then
daemon stop --force.b2. Locale resolution under the bundled loader: after
locale-gen en_US.UTF-8 ja_JP.UTF-8in the container, run the host's own/usr/bin/locale charmapthrough the bundled loader(
LANG=en_US.UTF-8 /opt/felis/bin/.ld.so --library-path /opt/felis/lib /usr/bin/locale charmap) and require stdout to beexactly
UTF-8with empty stderr:localeprints asetlocalediagnostic and falls back to
ANSI_X3.4-1968when the categorydata cannot be loaded, so this is a real
setlocale(LC_ALL, "")probe against the host archive from the bundled glibc.
b3. Compose tables off Nix: with
XLOCALEDIRas the launcher sets itand
LANG=ja_JP.UTF-8(one of the fourteen rewritten locales),libxkbcommon's
libexec/xkbcommon/xkbcli-compile-compose --locale ja_JP.UTF-8, run directly through the bundled loader (thebin/xkbclidispatcher finds its helpers by a compiled-in storepath, so the helper itself is what the smoke copies into its
context; nothing of it enters the archive), must succeed and print
a non-empty table.
c. The GUI marker smoke from
linux-smoke(xvfb-run -a /opt/felis/bin/felis-client --socket … -- catwithFELIS_SMOKE_MARKER/FELIS_SMOKE_TIMEOUT_MS), which proves thearchive's client opens a window, renders through host Vulkan, and
spawns the archive's daemon. It runs under
LANG=en_US.UTF-8(generated in b2), the locale a desktop host actually has, and a
GUI-created PTY child's environment is checked the same way as in
b (the marker command is
sh -c 'env > …; cat'for that run, or asecond run with that command).
linux-packagethereforeneedslinux-smokeonly for ordering; the marker flow runs again on thearchive.
The image pull and apt install are the cost; the job's timeout allows
for them and the container is discarded.
verify.py --identityagainstbin/felis version --format jsonfromthe extracted tree, so the archive's own binary, not only the store
path, is tied to the tag.
felis-x86_64-linux= the tarball.linux-publishloses nothing.releaseneedslinux-packagetoo,downloads its artifact, and the asset loop becomes
artifacts/felis-x86_64-linux.tar.gz,artifacts/felis-aarch64-darwin.tar.gz,artifacts/felis-x86_64-pc-windows-msvc.zip, schema, proto. The"record what the artifact is" step keeps the identity check and drops
the heredoc. Release notes: "Linux and macOS ship as the attached
tarballs; the same builds are on the binary cache under the flake
reference
git+…?ref=refs/tags/<tag>."PR CI builds
felis-diston both targets so a broken bundler failsbefore a tag:
pr.yml's build job on Linux, anddarwin.yml's buildstep (
nix build .#felis .#felis-dist) on darwin.macOS: self-contained
felis.appplus CLILayout of
felis-aarch64-darwin/Why the CLI moves into the app:
felisexecsfelis-clientand bothspawn
felis-daemonby sibling lookup. The only directory that can holdall three as siblings on macOS is
Contents/MacOS; a loosebin/feliswould fall back to PATH and fail on a fresh host. Today the client is
installed as
Contents/MacOS/felis, which would collide, somake-macos-app.shgains--cli PATHand, when given, installs theclient as
felis-client, sets CFBundleExecutable to it and puts the CLIbeside it. CFBundleName/DisplayName stay
felis, so Finder and the Dockare unchanged.
nix/package.nixpasses--clitoo so the flake's$out/Applications/felis.apphas the same shape (grep confirms nothingin docs or hm-module names
Contents/MacOS/felis).Why
bin/felisis a script and not a symlink: whethercurrent_exeresolves through the symlink is a std detail; a script makes the
sibling directory unambiguous.
Dylib bundling and signing (
nix/dist-macos.sh)For every Mach-O under
Contents/MacOS: walkotool -Ltransitively,copy each
/nix/store/…/*.dylibintoContents/Frameworks(samecollision rule as Linux: a second source for one basename must be
byte-identical or the build fails), rewrite the
reference with
install_name_tool -change <store> @executable_path/../Frameworks/<name>(
@loader_path/<name>for dylib-to-dylib edges), set-idon the copy,and delete every
LC_RPATHthat names/nix/store(
install_name_tool -delete_rpath), read fromotool -l. The new pathsare always shorter than the 32-char-hash store path they replace, so
header padding is never the limit. Signing is inside-out and the bundle
is the last mutation, with
rcodesign(nixpkgsrcodesign0.29.0 inthe pinned revision;
sigtool0.1.3 signs single Mach-O files andcannot write a bundle's
_CodeSignature/CodeResourcesseal):rcodesign signeach Frameworks dylib, then eachContents/MacOSexecutable, then
rcodesign sign felis.app, which seals Info.plist andthe nested code. Ad-hoc (no identity) throughout.
otoolandinstall_name_toolcome from nixpkgscctools. All of it runs insidethe derivation, so nothing depends on Xcode CLT on the runner, and
Apple's
/usr/bin/codesign --verify --strict --deepin CI is theindependent check of the result. If
the walk finds no store reference, Frameworks is omitted and the step is
only the rpath scrub and re-sign.
CI
darwin-publishgrows steps after the cache push:nix build .#felis-dist, extract, then:Contents/MacOS, Info.plist,bin/felis, the compiledxterm-felisentry, the three completionfiles, the
felis.1man page, README.md, LICENSE), then anotool -lsweep overfelis.app/Contents/MacOS/*andFrameworks/*:no
LC_LOAD_DYLIB,LC_ID_DYLIB, orLC_RPATHnames/nix/store./usr/bin/codesign --verify --strict --deep felis.app(base OS tool).felis.app/Contents/MacOS/felis-client --versionrun directly mustexit 0 and print the tag's identity (
felis versionreports a clientthat fails to launch as
unavailableand still exits 0, andverify.py --identityreads only thecliobject, so neither wouldcatch a dylib dyld cannot resolve). Then
bin/felis --version, thenfelis --socket <tmp> sessions spawn -- trueand a check that thedaemon's executable is inside the extracted
felis.app, thendaemon stop --force, thenverify.py --identityonbin/felis version --format json. The macOS runner cannot open a window, so thedirect
--versionis the strongest client check available there; theLinux archive's client is exercised by the GUI marker smoke.
felis-aarch64-darwin= the tarball (replacing the txt).The macOS runner cannot open windows; the GUI claim rests on the user's
daily use as before. Gatekeeper: a downloaded tarball carries quarantine
and macOS propagates it on extraction, so install.md tells the user to
run
xattr -dr com.apple.quarantineon the extracted tree before firstuse. Right-click → Open is documented only as the app-only alternative:
it authorizes the app's main executable, not the separately signed CLI
inside it, so a user who wants
bin/felisneeds the xattr step. This isthe cost of no notarization and is stated, not hidden.
Docs
docs/reference/workspace.md: asset table and the "five files" prosebecome the new set; the darwin artifact sentence names the tarball.
docs/explanation/testing.md"A tag rebuilds rather than promotes" andthe P-2 durable-distribution sentence in workspace.md: the binary cache
remains the reproducibility story; the tarballs are what a non-Nix host
gets, built from the same store path.
docs/how-to/install.md: "The Linux tarball" and "The macOS tarball"sections beside "The Windows deliverable": unpack, run
bin/felis,host requirements (Vulkan driver and libvulkan on Linux; quarantine on
macOS), terminfo (
TERMINFO_DIRSor copy into~/.terminfo). Line 133about verification assets is rewritten.
README.mdlines 51-56 anddocs/README.mdline 32 (the installguide is no longer "Nix or source"): three archives, plus the cache for
Nix users.
docs/how-to/update-felis.md: an archive update path: drain andstop the daemon, unpack the new tarball into a fresh versioned
directory, then swap it in; never unpack over the old tree, which
would keep removed libraries and break the macOS resource seal.
docs/reference/testing.mdline 544: therelease.ymlrow names "thetwo identity files"; it names the two archives instead.
(standing decision for this campaign).
.txt,identity file,store pathin docs andworkflows.
Split into PRs
relay.rswith itstest,
nix/dist.nix+nix/dist-linux.sh,felis-distpackage on Linux,
linux-packagejob, release job asset/notes edits(darwin still uploads the txt in this PR; the loop lists the txt
until PR B), install.md Linux section, README, workspace.md rows.
--cliinmake-macos-app.sh,nix/dist-macos.sh,felis-diston darwin,darwin.ymlbuild step, darwin-publish steps,drop both txt mentions, docs sections, both testing.md pages,
update-felis.md, final grep sweep.
Each PR stays under the 800-line budget and leaves the release workflow
consistent at its merge. Both touch release.yml, so B follows A. Then
v0.1.0-rc.3proves the five-asset set.Acceptance criteria (issue #360, amended)
felis-x86_64-linux.tar.gz,felis-aarch64-darwin.tar.gz,felis-x86_64-pc-windows-msvc.zip,felis-config.schema.json,felis.proto./nix, runsfelis --version, spawns its own daemon (/proc/<daemon>/exeis thebundled loader), starts a cold daemon from the relay path, gives PTY
children an environment free of loader and locale overrides, resolves
en_US.UTF-8from the host archive underthe bundled glibc, and passes the GUI marker smoke through the host's
Vulkan stack.
man pages from the package, checked by a manifest after extraction.
another (
ja_JP.UTF-8)./nix/storein any load command or rpath,passes
codesign --verify --strict --deepon the bundle, and runsfelis-client --versiondirectly,felis --versionand the daemonspawn on the runner.
nix build .#felis-distfails when the closure gains a path outsidethe two lists or when a NEEDED library is unresolved inside
lib/.Smoking PR #361 surfaced #362: a default-config client fails with Font(NoFont) on Debian/Ubuntu. The archive smoke names a family as a stopgap.
v0.1.0-rc.5(run 4234) published with exactly the five assets:felis-x86_64-linux.tar.gz(21 MB),felis-aarch64-darwin.tar.gz(10 MB),felis-x86_64-pc-windows-msvc.zip,felis-config.schema.json,felis.proto. No txt. Both archive gates ran on the tag: the Linux tarball passed the Ubuntu 24.04 container smoke (CLI, daemon autospawn, relay spawn, PTY child environment, locale and compose probes, GUI marker through lavapipe); the macOS tarball passed the manifest,codesign --verify --strict --deep, the direct client launch, the launcher, the daemon spawn and the client identity gate.Follow-ups landed on the way: #364 (terminfo directory spelling on darwin, docker client on the runner's PATH), #365 (launcher root without
..). Open: #362 (default-config font failure on Debian/Ubuntu; the archive smoke names a family as a stopgap).