Repository navigation
foldyard 0.5.0
Every VM needs one fy machine stop && fy up after upgrading: the VM's boot setup changed (the
proxy settings it gives build steps and stack containers), and until it restarts foldyard refuses
a running Lima VM as stale. No config edits are needed. Outside a foldyard project, project verbs
now refuse instead of running as a project named after the directory. New: fy prereq checks
what your computer needs, and fy prereq --fetch bakes a base VM that new projects clone in
seconds; [box].packages bakes system packages into the box image once; and fy changelog
prints what changed since the foldyard your foldyard.toml declares.
Changed
- Outside a foldyard project,
fysays so instead of inventing one. With nofoldyard.toml
in the current directory or above (and noFOLDYARD_REPO), a project verb (fy mode,
fy doctor,fy up,fy host…) used to run as a project named after the directory, writing
a mode file and allocating a port band for it in~/.foldyard/ports.json. It now prints one
line pointing atfy initandfy prereq, exits 1 and writes nothing. That includes a git
repo that has nofoldyard.toml. - Help,
fy prereq,fy docs,fy init,fy machine baseandfy skill listwork anywhere,
quietly. Every verb,--helpincluded, used to open with "✗ not a git repository" outside a
git repo, includinguvx foldyard prereqin an empty directory. - A refused request in the Network Log now shows what was asked. A blocked row used to read
only "⛔ refused by the allowlist". Now it shows the method and target:CONNECT host:portfor
HTTPS (refused before TLS, so the path is never seen), and the method and path for plain HTTP.
The text reads "blocked — not on the allowlist". A request refused because its Host header
named a different host than the one it was sent to now says so, instead of blaming the
allowlist. It is also no longer offered as a host to grant, since a grant would not let it
through. The egress log's blocked rows carrymethod,pathandmismatchto match. - A refused HTTPS request now shows its method, path and User-Agent in the Network Log, and a
refused host is never connected to. The proxy used to refuse an ungranted host atCONNECT,
before TLS, so the row could only sayCONNECT host:443. It now answers theCONNECTitself,
decrypts the tunnel and returns the403to the request inside. No connection to the refused
host is ever opened: the proxy also refuses, at the point where every upstream connection is
made, any connection to a host nothing grants. Previously a separate check on each path made
that guarantee, so it now holds in one place. Unchanged: a port other than:443and an image
build are still refused atCONNECT. One visible difference: a client in the box that keeps
its own trust store (it doesn't trust the proxy's CA) now gets a certificate error for a
refused host instead of the readable403. The log still records it as a blockedCONNECT.
Added
fy prereqchecks what your computer needs and prints how to install it. Lima (and its
version), podman, a hypervisor, and on Linux QEMU,/dev/kvmand asystemd --usersession,
plus disk, memory and foldyard's own egress proxy. Each missing piece comes with the install
command for the package manager you have; foldyard never runs it. It needs no project, so
uvx foldyard prereqworks before foldyard is installed, and it exits non-zero until
everything required is there.- New projects' VMs are clones of a base VM: seconds instead of minutes, and almost nothing
downloaded.fy prereq --fetch(orfy machine base bake) bakes a project-less base VM once:
Lima's podman template with nothing mounted, booted, the generic box image built in it. After
that,fy up/fy box upcreate a new project's VM as alimactl cloneof it with the
project's own sizing and mounts, andfy box upre-tags the box image it carries in instead of
building one. From an empty directory to a walled box took 26 s on macOS. Existing VMs are
never re-cloned; a base out of date with Lima's template is not used, and one over 30 days old
is flagged.FOLDYARD_BASE_VM=0turns cloning off.fy docs base-vmhas the rules. - Node's
fetch()and npm's install scripts go through the proxy without project wiring.
Node'sfetch()ignoresHTTPS_PROXYunlessNODE_USE_ENV_PROXYis set, so under the VM
firewall it failed with anECONNREFUSEDagainst a raw IP that named no proxy. An install
script that fetches with its own client (supabase's CLI download) reads only
npm_config_https_proxy. The box now getsNODE_USE_ENV_PROXY=1,npm_config_https_proxy
andnpm_config_proxy(defaults:[box].envmay override each), and with the VM firewall on
every stack container gets the two npm settings. Stack containers don't get
NODE_USE_ENV_PROXY: it would send calls between Node services to the proxy, which can't
resolve a service name. An app container that wants it sets it itself, with aNO_PROXY
naming the stack's services (fy docs networking, "Node and npm"). The stack containers'
settings are part of the VM's boot setup, sofy uprefuses a running VM until you run
fy machine stop && fy uponce. fy doctorpoints out compose settings that override what foldyard provides. With the VM
firewall on, foldyard gives every container the proxy settings, the proxy's CA and npm's proxy
settings, and a value your compose file sets wins without a word, including a proxy address
that has gone stale or a mount of.devbox-ca. The newstack env overridesrow names each
one per service. It's advisory: a value you need, such as a private CA bundle, can stay. Empty
values aren't flagged, since blanking the proxy is how a stack-only service stays off it. If
your project wired any of these by hand before foldyard provided them, you can drop them now.
A second row,stack container env, reads what each running container actually has, so it
also catches an image's ownENV(anSSL_CERT_FILEof its own) and a container created
before the VM provided these settings, which keeps the proxy but lacks the CA until you
recreate it (fy down, thenfy up). Containers that blank the proxy aren't flagged there
either.- The networking guide says when stack containers trust the proxy, and how to fix Turbopack.
Only the VM firewall gives containers the proxy's CA, so a container you point at the proxy
yourself needs it on.next buildfetching Google Fonts ignoresNODE_EXTRA_CA_CERTS;
NEXT_TURBOPACK_EXPERIMENTAL_USE_SYSTEM_TLS_CERTS=1makes it readSSL_CERT_FILE. [box].packagesinstalls system packages once, not on every new box. List them
(packages = ["nodejs", "just"]) and foldyard bakes them into a cached layer on top of the box
image, rebuilt only when the list or the base image changes (or byfy box build). Until now
the only way to add a package to the packaged box was a[[box.tools]]step, which reinstalled
it, through the egress wall, every time a box was created. The layer uses whatever package
manager the image has (apt, apk, dnf, microdnf, yum, zypper, pacman), so it works on your own
[box].imagetoo. A box that is already running keeps its old image until
fy box down && fy box up.fy changelogprints what changed between the foldyard yourfoldyard.tomldeclares and the
one installed. A project can sit any number of releases behind thefyrunning it, and
catching its config up meant reading the whole changelog on GitHub. Now the releases in between
(aftermin_foldyard_version, orrecommended_foldyard_versionif higher, up to the installed
version) print oldest first, from the changelog the install itself ships, so it works offline
and inside the box.--actionkeeps only what can need a step (each release's summary plus
Security, Removed, Deprecated and Changed);--since/--untilpick the range by hand and work
outside a project. Raisemin_foldyard_versiononce the config is caught up and the next run
starts there.fy doctor's version row points at it when the install is ahead of the repo.- A project that declares no foldyard version still gets a starting point:
fy changelog
falls back to the datefoldyard.tomlwas last modified and shows the releases from then,
labelled as an estimate (a clone or checkout rewrites the file's time, so it can start late).
--sincealso takes a date (--since 2026-09-01) to set it by hand.
Fixed
fy verify's push check works in a walled box, and on a public origin. Since 0.3.2 its
gitran with the compose client's environment, which carries no proxy: in a box behind the
VM firewall git went direct, was refused, and every origin read "git remote UNREACHABLE … push
refusal UNPROVEN". It now runs with the box's own environment, as every other box check does.
And the probe is a dry-run push (git push --dry-run, which sends nothing) instead of
git ls-remote: a read proves nothing about pushing — a public origin answers it with no
credential, which made the row FAIL on every public repo — while the push handshake needs write
access on any repo.fy logsworks on the podman engine. It asked compose for the last 50 lines with-n 50,
which docker compose reads as--tail 50but the bundled podman-compose reads as its
value-less--namesswitch followed by a service called50, so everyfy logs [svc]on
podman failed with "missing services [50]". It now sends--tail 50, which both read the same.
A new test hands every compose command foldyard sends to podman-compose's own argument parser,
so a flag the two providers read differently fails the suite instead of a user's command.fy up,fy box upandfy box buildwait for the host supervisor's daemons, and refuse
when one can't run. A launch used to start the supervisor and carry on: right after it
replaced the supervisor (an upgraded foldyard, say), the first build could reach the egress
proxy before it was listening and fail with apt's "connection refused". Each launch now waits
until the supervisor reports every daemon it needs as running. A daemon blocked on a spawn gate
(a missinghost.envsecret, a foreign process on its port) or crash-looping stops the launch
with its reason. Before, the launch carried on and failed somewhere later and less clearly.- A worktree's launch is served before its box exists. The supervisor only ran daemons for
worktrees whose box was already up, so with one worktree's box running and main's not, a second
worktree'sfy box upbuilt with no proxy listening on main's port, which every image build
goes through. A launch now tells the supervisor which worktree it is bringing up. - A launch replaces a supervisor started from another checkout of the same project. A
supervisor serves the checkout it was started from: its worktree registry and its adopted
config. A second clone'sfy upused to reuse it, and its worktrees were never served. A launch
from an unregistered worktree now stops at once and names the fix (fy worktree add <name>). - Node's built-in
fetchnow goes through the proxy in image builds, and a build that fails
because a tool skipped the proxy now says so. Node'sfetchignoresHTTPS_PROXYunless
NODE_USE_ENV_PROXY=1is set, and Node 24's corepack (0.35 and later) uses it. So
corepack preparein anode:24image connected directly, the VM firewall refused it, and the
proxy logged nothing. foldyard now setsNODE_USE_ENV_PROXY=1in every build step (noARG
needed in the Dockerfile; a DockerfileENVfor it still wins), as it already did in the box.
When a build fails, foldyard also lists the connections the VM firewall refused while it ran,
by IP and port, with the likely causes. Before, a failure like this left no trace. Existing
VMs are refused as stale until you runfy machine stop && fy up, which installs the change. - A VM whose boot never finished now says where it stopped. Lima runs its own provisioning
before foldyard's boot script — on a VM created from the template, that's its podman install
(dnf, over the Fedora mirrors). A stalled mirror madelimactl startgive up after 10
minutes, and the nextfy upthen said the VM "did not apply its boot provisioning" and
pointed at a boot log that didn't exist yet, because foldyard's script never started. Both now
say the VM is up but Lima's boot scripts haven't finished, show how to see what's still running
and where the guest console log is, and say to runfy upagain once it settles, restarting
only if it's stuck. A VM whose base was baked with
fy machine base bakeskips that install altogether. - A host you grant for the session when
fy uporfy box upasks now keeps that grant.
Session and once grants end when the host supervisor restarts, and the launch verbs asked about
recommended hosts before starting a new supervisor (after a foldyard update, say). An answer
of "session" or "once" was then wiped a moment later by the supervisor that started, and a host
whose session grant was about to end wasn't asked about at all, so the box hit refusals for a
host you had just allowed. The launch now ends the old grants first, then asks. fy machine base bakeandfy prereq --fetchrun from any directory. Both are meant to
work outside a project, but they refused to start from a directory whose name isn't a plain
name, such as_temp,My Projects, a folder under iCloud'sMobile Documents, or/. The
error talked about "the checkout's directory name" and[project].nameto someone in no project
at all. The bake no longer reads a project name.