Day two
The handful of nufi-box commands that matter after the install, and the one check that catches the failure nobody expects.
Everything below is ./nufi-box from the directory you installed from.
Is it healthy?
./nufi-box doctorEleven checks in plain words. Ten of them ask the box about itself. The eleventh is the one worth knowing about.
The name, not just the box
The box announces nufi.local on the LAN with the address it had on install
day. Change network, pick up a new DHCP lease, unplug a dock — and the name now
points at a stranger while the box itself is in perfect health. Every laptop in
the room fails to reach it; the box has no idea.
!! nufi.local points at 192.168.1.99, this machine is 192.168.1.25 — run: nufi-box announce./nufi-box announceThat re-publishes the name at the address the box has now and records it, so
the certificate and the banner follow the machine. Run doctor before any
demo. This is the single most common way a working box looks broken.
What is running
./nufi-box status # every service, the model, disk, last backup
./nufi-box logs # all of it, following
./nufi-box logs librechat # one serviceStart, stop, restart
./nufi-box up
./nufi-box down
./nufi-box restartRestart is also the cure for one specific confusion: the app caches the model list per gateway and never expires it, so a model you have just added shows up only after the app restarts.
Departments
./nufi-box drive add finance # folder, share, team and agentPeople
./nufi-box user add hana@legal.example --name "Hana Kim"
./nufi-box user listThe box has no sign-up page — who may sign in is an administrator's decision,
not a self-service one. user add prints a generated password once; it is not
stored anywhere it can be read back.
Note that this is a different thing from invite, which is about a laptop
joining the mesh so it can reach the box from outside the office. user add is
about a person being allowed to sign in at all.
Studio routines
./nufi-box flows install # put the department routines into Studio; safe to repeat
./nufi-box flows list # every flow, with its idflows install keeps a routine that is already there, edits and all, and
creates only what is missing.
From home
./nufi-box mesh up # join the box to the coordinator
./nufi-box mesh status
./nufi-box invite alice --os macos --drives legal,hr
./nufi-box members
./nufi-box revoke aliceinvite writes one file a laptop opens to join. On the LAN you need none of
it. What the coordinator is, what the three values are, and how to name a box
so two can share a network: On the LAN and from home.
The certificate
./nufi-box ca-cert # the file every laptop trusts onceUpdating
./nufi-box update # fetch the newest release, apply it, check it
./nufi-box update --ref nufi-box-v1.2.0 # a specific tag instead of the latest main
./nufi-box update --rollback # put back what the last update replacedIt asks you to type the box name to confirm, unless run with --yes (needed
when there is no terminal to confirm on — a script, a timer). It resolves the
ref (or main) to a commit sha through the GitHub API before fetching, so
nufi-box status can later say truthfully what commit the box is running; if
the API is unreachable it fetches the ref directly and says sha unknown
instead of guessing.
Then: it fetches the whole archive (deliberately — pulling just deploy/box
out of it is a trick that only works on macOS's tar, and fails plainly on
every Ubuntu box), backs up the box first (nufi-box backup), snapshots the
current files — deploy/box, the two directories it depends on beside it,
and the digest of every image running now — applies the new ones, and
re-runs install-box.sh — the same installer day one used, which already
keeps .env and every answer and secret, so an update never re-asks the
four questions. Each run downloads about 185 MB and unpacks about 340 MB in
a temporary directory, removed once the update finishes either way.
Then it runs nufi-box doctor. If that passes, you are done. If it fails —
or if the installer itself fails, or the file copy does — update rolls
back on its own — the files and the images go back to what they were, the
stack restarts, and it prints where the backup from this run landed. It does
not restore that backup automatically: your data is meant to carry
forward across an update by design (migrations), and putting the database
back is a decision for a person holding the backup's path
(nufi-box restore <path>), not something a failed check should do by
itself.
Not signed yet. The archive comes straight from GitHub over TLS — the
same transport a browser gets, nothing more. There is no signature on it and
nothing checks one. A signed bundle from updates.nufi.me is the next phase
(see the README's "What is not built yet").
Something is wrong
./nufi-box doctor # first — what, in plain words
./nufi-box support # then — package it up to sendsupport gathers what an engineer actually needs for the first reply on a
support thread — the box's version, docker compose ps, doctor's own
output, the last 300 lines of every service's logs, the compose config, and a
few small files (schedules.ini, caddy/mesh.caddy, a per-department folder
listing, the data/backup listing) — into one .tar.gz next to
data/support/ (or wherever --to DIR says). It prints the path, the size,
and who to send it to.
Nothing in it is a secret. env-keys.txt lists the names of every key in
.env and whether each has a value, never the value — and never a fragment
of one, since it reads .env as entries rather than lines, so a multi-line
secret (the console's signing key is a PEM) counts as one key, not one per
line of it. compose.yml is docker compose config with every value that
looks like a secret — a .env key ending in _KEY, _SECRET, _PASSWORD,
PEM, TOKEN or _IV — blanked out everywhere it appears, and again by
that entry's own name. Every other file in the bundle goes through the
same value-level redaction before the tar is made — a log line, not just
compose.yml's own rendering, can carry a secret too. No document's name
ever leaves the box either: the bundle lists each department's folder and a
file count, never a file's name. It works on a box that will not start and
one whose doctor is failing: a check that cannot answer says so in the
bundle instead of stopping the rest. The one thing that does stop it is the
sweep itself — it needs python3, and without a working one the bundle is
not packed: support prints NOT SWEPT, leaves the raw directory behind
with a README.txt that says so, and exits non-zero.
This is the local half only. support does not open a connection from
Dudaji into the box, and nothing on the box listens for one — that remote
half is still not built.