Repository navigation
wisp 0.0.5
LocalGhost wisp 0.0.5
Cut on 5 October 2026. The fifth cut of wisp; the name is the release line, the number the cut.
Everything in 0.0.1's notes still holds, and the pages for 0.0.2,
0.0.3 and 0.0.4 say what each cut changed.
What changed since 0.0.4
Wikipedia lives in the box's database
0.0.4 shipped a reader that read the mirror's file at every question and could not open the real
one (the first published edition keeps its title index in an uncompressed cluster 1.4 GB long).
The file is read once now, and never at a question: ghost.synthd imports it into the volume's
Postgres in the background, a slice a minute (an hour or two for the English edition without
pictures; Box Status and tools/health.sh show how far), every article's title, lead and text and
every redirect into two tables, and when it is all in the file is removed and a marker left in its
place, so update.sh fetches the file again only for a newer edition. A lookup is SQL: the title
in any case ("iphone" finds IPhone), a redirect, a place with its qualifier written without the
comma ("greenwich london"), a prefix, a likeness for a typo (pg_trgm, installed at unlock), and
last the words of a lead. The chat brings the article's lead for "what is X" and "tell me about X",
and for a question that names a thing ("how tall is the Eiffel Tower") the section the question
points at as well; the coin pages read the same tables; the identity line tells the model the box
has the copy by edition. A WIKIPEDIA page in the menu searches it and reads an article whole, with
its sections, for when the chat's answer wants checking. Nothing of a question leaves the box.
"Tell me about Greenwich" came back from the memories alone in 0.0.4 because six memories filled
the context before the article had its turn; the article keeps a place of its own.
The box writes the way the site writes
The chat answers in the site's voice (the web repo's writing guidelines): plain and to the point,
flat where sure and hedged where not, contractions, no bullet points, headings or bold unless a
list was asked for, no colons inside sentences, none of the words the guidelines ban, no praise
of the question, no summary at the end, no verdict, no "hope this helps". The rules ride with
every question, and the one thing a model of this size will not obey however it is told, the em
dash, is put right on the way out: em dashes become commas in the stream, token by token, and in
every stored answer, day story, memory and news summary.
The check-in has a page of its own
The daily check-in sat in the middle of MEMORIES, between the note about me and the voice notes,
with its history a list of lines behind a toggle. It is CHECK-IN now, a page in the menu under YOUR
ARCHIVE: the day; the last two weeks as a strip, one cell a day, each marked by the quadrant the
day was told by (▲ bright, ● easy, ◆ tense, ▼ heavy, ◇ mind, a dot for nothing), a tap on a cell
opening that day's check-in; then today's, the feelings laid out as the mood meter's four
quadrants with the mind row under them, the box's guesses already ticked and marked, the why
prefilled from the day, a voice note, the save. Once saved the page is what was said, the day as
the box tells it, and a recorder for more. Under that the past check-ins as rows, what recurred
this month, and every voice note on the box. Nothing about the storage moved (the check-in is
still a journal entry, synthd distils it with the rest), and nothing counts missed days: an empty
day is a dot, and the box asks once in the evening and is silent after, as before. The evening
reminder opens this page.
A memory has a page of its own, and a notification opens the thing
A notification that brought a memory back ("a year ago today you ...") used to land on MEMORIES
with the list filtered to that one title, chips and chrome and all. It opens the memory alone now:
what kind of thing it is, the title, its photos, the body in full, where it came from and when, the
day it was made from (a day's or an outing's memory names it, and the box sends that ref since this
cut), and edit and delete. A memory's title in the list opens the same page; a day's outing card
does too. The box's reminder for the check-in opens CHECK-IN; a nearby one opens NEAR YOU as before.
A box from the release alone
The release bundle used to carry the daemons and the two command-line tools, and a new box still
needed git, Go and make to be set up. Since this cut the bundle carries ghost-setup, ghost-qr,
ghost-update-guard, the tile cutters and the TPM repair tool, and the operator scripts with their
pins, so a box is set up from the bundle: verify the sums against the site key, unpack,
sudo localghost/tools/install.sh. The installer puts the release under
/opt/localghost/release/<version>/, the scripts under /opt/localghost/tools/ and the
command-line tools under /opt/localghost/bin/ (where a release taken from the phone puts them),
and runs setup.sh from the release, which sees the binaries and builds nothing. The engine and the
models are a step of setup.sh now rather than a command to know about (GHOST_LLAMA=0 leaves them
for update.sh engine), so the whole of it is: download and verify, install, scan, unlock, and
update.sh once unlocked for the streets and Wikipedia. The repository checkout still works the
same way, building first. The scripts are text and the build is the same, so the bundle is as
reproducible as it was.
Two things found on the way. The bundle's NOTES.md, there since the first cut, is not among the
files a box's unpacker admits at the top of a bundle, so a release taken from the phone (DEPLOY)
would have been refused by every box so far; the notes now sit beside the bundle in the set and
not in it, and a box on 0.0.4 can take this cut from the phone, the first time that is true. And a
release's setup binaries are left in the unpacked release by a DEPLOY, never staged with the cohort,
and ghost-update-guard is never replaced by a release at all, as its comment always said.
The volume as a file on any drive
The volume is one LUKS container, and cryptsetup is as happy with a file as with a whole disk (it
attaches a loop device of its own). So a box's volume can now be a file on a drive that is mounted
at boot, allocated in full (fallocate, copy-on-write off on btrfs), instead of a whole empty disk:
ghost-setup --image /mnt/data/localghost.img --size 500G, or the same answers at setup.sh's volume
step (a path and a size instead of a device; 20G at least). The unit waits for the drive
(RequiresMountsFor), the stable-name lookup leaves a file alone, and secd says plainly when the
file is not there at unlock (the drive not mounted, most often) rather than searching the disks.
The seal, the PINs and the wipe are unchanged: the file is ciphertext, a copy of it is ciphertext,
and there is no key outside the seal. The host sees a file of a size and a date, as it saw a disk
before. Not a network share.
The engine built for the card in the box
setup_llama.sh built for compute capability 8.9 by a number in the script, the reference box's
RTX 4070. It reads the capability from the driver now (nvidia-smi), one or several cards, and
says which it built for; a toolkit with no card the driver can see gets a CPU build, said loudly;
GHOST_CUDA_ARCHS=89 names it by hand. Nothing to edit for another card.
What belongs together, together
The distiller made memories as things came in, and left them that way: two memories for the same
person (the note's and the chats'), and a trip of ten days told as four outings and ten days. A
consolidation pass folds them now. One memory per person: what the note about me and my people
says of them is one part of it, every fact the chats and the check-ins added is another (each
with where it came from), other spellings of the name are kept as aliases, and two rows for one
person (the same name, or a first name and the full name) become one, the duplicate gone. Once a
day, with the model on the GPU, the box writes each person whose facts grew as a few sentences
from those facts and nothing else (a name or a place the facts never gave, or a number not in
them, and the text is thrown away; the facts stand as a list until the next try), with the facts
added since shown after it. A memory edited by hand is never merged into or rewritten. Outings
away from home that follow one another within three days are one trip, named by the country or
the places and the dates ("Canada, 12 to 19 September 2026"), with the outings and the days folded
under it; MEMORIES shows the trip in place of its parts (the days and outings chips still list
them), the trip's page lists them, and the model writes the trip from its outings' own texts and
its days' stories. A lone outing away for a night folds its days under itself the same way.
ghost-cli ghost.synthd consolidate shows the people and the trips as they stand; run=1 merges
and folds now, write=1 has the model write the people too.
Box Status says what ghost.synthd does
The synthd rows in Box Status were the memory count, the distill queue and the news feeds. They
say now when the last pass ran and what it did ("distilled 3 · people merged 2 · trips 1", and
LATE when a pass is more than half an hour old on an unlocked box), the memories by what they
are (people, trips, outings, days, distilled, about me, places, noticed), what the model has
written (outings, trips, people and days, of how many), the days told and how many are folded
under a trip, the consolidation (people written, with facts, with other names, the last daily
writing), Wikipedia as the daemon sees it (open with its edition and entry count and how many
questions it answered since start; downloading with the gigabytes so far; missing; or failed
with the reader's error), the about note's state and the chats answered. synthd writes its
account at the end of every pass (settings synthd_status); the rows read it and the tables.
The phone asks no rate service
A currency question the box did not answer used to go from the phone to Frankfurter, the ECB's
rates by another road. The box keeps the ECB's table itself and answers from it, so the phone's
tool is gone: the only money numbers are the box's own. What leaves the phone is shorter for it:
the question's words to a search engine, and a "who is" to Wikipedia's summary API on a box
without its own copy.
SETTINGS says what the box runs, and keeps a shelf
SETTINGS › SERVER said a version. It says now the build whole: "wisp 0.0.4 (1062b7f), built 4 Oct
2026, 17:12 UTC, Go 1.27.1" (GET /v1/update carries the commit, the time and the Go; a release's
time is its commit's, since the build is reproducible and a clock reading would make it not; a
build from source carries the moment make ran). The mirror's newest release reads the same way.
The phone said "no server release on the mirror" with 0.0.4 published there. The manifest lists
every file of every set, the elevation tiles among them, so it had grown past the megabyte the
phone read of it, and the server set's lines came after the cut. The phone reads it line by line
now and keeps the header and the server set; when it finds no release it says which step came up
empty (the mirror did not answer, the build has no server set, the notes did not match).
The mirror publishes one server release, the newest, so an earlier one cannot come from there. The
box keeps its own shelf: the signed set of every release the phone handed over (the last four,
the running one never dropped) stays beside the unpacked bundle, and SETTINGS lists them under ON
THE SHELF with a [ put on ] that asks once more. Putting one back on is a deploy from the shelf
(POST /v1/update/switch): the set is verified again against the pinned site key (the verifier is
told this manifest is an older one the box chose, and its "newest build used" marker is left
alone), unpacked again, put on, and on trial like a new release. ROLL BACK stays what it was, the
build that ran before the last change, kept whole.
BTC and ETH first on the widget and the lock screen
At home the widget and the lock-screen card led with the story and put the prices on the last
line, which a look could hide. The prices lead now: the widget's big line is BTC over ETH, each
with its day's change, always in view, the story under them and its outlets last; the card's
title is the prices and the story the line beneath, with its summary and the next two stories
when pulled open. Without a story yet the prices stand alone; without prices yet the story moves
up as before.
A check-in grows through the day
A note said after the check-in went to the journal as a voice note of its own. Until the day
ends the recorder on the check-in page adds to the check-in instead ([ add to today's check-in ]): the note is a check-in note of that day, journaled as said at the daily check-in,
and today's page and the past days' pages list every note said to the check-in in the order said
(GET /v1/checkins carries voices, the first of them still as voice for an older phone). The
past list marks a day with more than one.
A question asked aloud
The chat's composer has a microphone. A tap records, a second tap stops, and the take goes to
the box (POST /v1/voice/ask), which hears it with its speech engine and sends the words back;
they land in the field to read, fix and send like typed ones. Nothing of the audio is kept
anywhere: ghost.voiced hears the file from voiced/ask and removes it (no row, no archive, no
journal entry), and the phone drops its copy once the words are back. One whisper runs at a
time, so a question waits for a note being transcribed and the queue waits for a question. A box
without a speech engine says so and the field stays for typing.
One more paper, and the microphone asked for up front
The Wall Street Journal's world feed (Dow Jones's public RSS) joins the default list. A box seeded
by the earlier list gets it once at the next start (feeds.Added, a marker in settings); taken
off again, it stays off. The welcome screen asks for the microphone with the other permissions,
"voice notes to your check-in, and questions asked aloud; heard on your box, nowhere else", so the
first voice note and the first question asked aloud do not stop for a dialog.
Known gaps at 0.0.5
- A fresh box trusts the device-certificate header nginx passes over 127.0.0.1 until the operator
runsghost-ctl edge-passthrough --domain <box name>; a box reached by IP alone cannot take the
passthrough yet (nginx routes it by name), which is next. - The box CA key stays on the OS disk, by choice: a phone can be enrolled and refused while the
vault is locked. - Device certificates last ten years; shorter ones renewed by the phone are next.
- Releases are signed by the site key alone; an offline release key is planned.
- The APK is not byte-reproducible; the server set is.
- The weather is a chat answer; a card on HOME and the lock screen is next.
- CRYPTO50's day value against its live value showed a fault on 3 October whose cause is still
to be named (ghost-cli ghost.tallyd rates index=1lays the terms out); the guard holds the
index meanwhile. - After the first unlock the operator still runs
update.shby hand for the streets and
Wikipedia; secd queuing it is next. A.debis not built; the bundle andinstall.shget a box up
without one. - NEAR YOU, what you photograph and on this day are still sections of MEMORIES; a page each is next.
- Mail, the Mist (peer-to-peer backup) and the decoy volume are not built.