Getting started
Install
uf is a single native binary. It needs a JavaScript host — Node.js, Bun or Deno — because Vite and your test bodies run there, but it is not installed through one.
From a release
Every tagged release publishes binaries for macOS and Linux, on both x86-64 and ARM64:
curl -fsSL https://setup.uniflowed.dev | shThe script picks the binary for your platform, checks it, and puts it on your
PATH. It works under sh, bash and zsh. Read it before you run it — it is
short, and it is served from https://setup.uniflowed.dev/install.sh if you
would rather fetch it first.
What it checks, and what that proves
Two separate things, because they answer two questions.
The SHA-256 beside the archive proves transit: the bytes that arrived are the bytes that host advertised, which catches a truncated download or a corrupting proxy. It proves nothing about origin — the archive and the checksum come from the same host, so whoever can replace one can replace the other.
The signature proves origin. Every release archive is signed with keyless
Sigstore from the release workflow's own GitHub OIDC identity, and the installer
verifies the bundle against that identity — a root that is not on the host
serving the archive. Install cosign and
the check runs; without it the installer says the signature was not checked
rather than implying it was.
| Variable | Default | What it does |
|---|---|---|
UF_VERIFY_ORIGIN | auto | require refuses an install whose origin nothing established; off skips the check |
UF_CHECKSUM_BASE | unset | A <base>/<version>/<asset>.sha256 layout on a host that does not serve the archive. The two digests must agree |
UF_VERSION | latest | A specific release |
UF_RELEASE_BASE | unset | A mirror serving the flat <base>/<version>/<asset> layout |
UF_INSTALL_ROOT, UF_BIN_DIR | XDG paths | Where the runtime and the shims go |
A signature that is present and does not verify always refuses, whatever
UF_VERIFY_ORIGIN says — and so does a second opinion that disagrees. The
failure prints cosign's own words, because a signature made by somebody else and
a cosign too old to read the bundle exit the same way and are not the same
problem. uf's own release pipeline installs with UF_VERIFY_ORIGIN=require, so
a release whose signature does not verify never reaches you.
The default is auto and not require for one reason, which will stop being
true: releases published before signing existed carry no bundle, and a default
that refused them would make every pinned UF_VERSION of an older release
uninstallable by the current script. Pass UF_VERIFY_ORIGIN=require today to
get the stricter behaviour; it is what the default becomes once every release a
person can still install is signed.
On Windows
There is no Windows build yet. The release workflow produces four artifacts,
all of them macOS and Linux, so irm https://setup.uniflowed.dev/install.ps1 | iex resolves — deliberately, so the URL says something useful — and then
prints an error telling you the same thing.
Two paths that do work: run uf under WSL2, where the Linux install above
applies unchanged; or build it from source with cargo install, below, which
needs no artifact.
From source
uf builds with a pinned nightly Rust toolchain — Flow's own crates need
box_patterns, which is still unstable — and vendors the Flow and React
Compiler sources as submodules.
git clone --recurse-submodules https://github.com/ubugeeei-prod/ufcd uf && cargo build --release --bin ufThe toolchain file in the repository selects the right nightly, so rustup will
fetch it on the first build. Expect the first build to take a while: it compiles
the Flow parser and the React Compiler.
The JavaScript host
uf does the parsing, transforming, scheduling and reporting itself, and hands the rest to a host:
| Host | Dev and build | Tests | Notes |
|---|---|---|---|
| Node.js | yes | yes | The default. Flow modules load through module.register(). |
| Bun | yes | yes | Flow modules load through a --preload plugin. |
| Deno | not yet | not yet | No Flow loader on any path. packages/vite/driver.js registers Node's hooks and Bun is started with --preload; Deno gets neither, so the first Flow module a command imports fails to parse. uf test refuses it up front with a message rather than failing later on a syntax error. See #246. |
uf detects what is available and picks one. To pin a project to a host:
uf use bunwhich writes the choice into uf.config.js. uf info prints what was detected
and what was chosen.
Checking the install
uf infoprints the version, the resolved host, and the project it found. If it reports no project, you are outside one — that is fine, and the next page starts one.