Repository navigation
LocalGhost wisp 0.0.7
Cut on 23 October 2026. The seventh 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, 0.0.4, 0.0.5 and 0.0.6 say what each cut
changed.
A box on 0.0.6 that takes 0.0.7 rewrites its PIN registry at secd's first start (the roles of
the entries leave the file; the PINs do not change), cuts its road tiles once more to put the
water in (hours for a continent, the old tiles served meanwhile), and from then on logs the
phone out of its session on a wrong PIN, so the app shows the box as down until the right one.
A box that already moved the phone's TLS into secd should check cat /etc/ghost/edge reads
tls (the move writes it now; an earlier move left it to a hand); a plain request without a
certificate is refused either way. The relay's own changes are in the web repository
(deploy/mail, install.sh as the deploy user). A phone on 0.0.6 takes the 0.0.7 app and
renews its certificate as before; its calendar and contacts arrive at the box at the first sync
after the permissions are given.
What changed since 0.0.6
A check-in for the day just ended
At one in the morning the day you want to tell is the one that just ended, not the one the
clock has started. The check-in page opens on yesterday in the small hours while yesterday is
still empty, and has yesterday and today as two switches under the heading at any hour; an
empty day on the two-week strip, tapped, becomes the day the form fills in, with the box's
guesses and the why prefilled from that day. The box files a check-in under the day its title
names: one written on the 10th for the 9th is stamped at the 9th's noon, so the day's story,
which reads the journal by the clock, tells the feeling under the right day; a check-in for the
day it is written on keeps its real time.
The widget fits its cell
The widget's phrase, its sound and its meaning grow with the height the launcher gives it,
held back by its width so no line runs out of the cell, up to about one and a half times; the
dim lines under them (the head, the prices, what comes next, the buttons) grow by a quarter of
that at most, since the long ones run out of width first. The sizes chosen on the widget
(small, medium, large) are the size at two rows.
The map stays cool
The map is drawn into a composited layer, a texture kept between frames, so the YOU marker's
breathing, the drawer sliding over the map and a line changing under it replay the texture
rather than re-render the world's paths, the tiles and the roads; only the camera, a landed tile
or the data draw them again. The marker breathes a few times when the map opens or the fix
moves, then holds still: an animation that never ended kept the window rendering at the
display's rate for as long as the map was open, with everything under it, and the phone got
hot and the drawer stuttered.
Every trip stays on the feed
The MEMORIES feed was the newest two hundred rows by the clock, and a trip from June fell off
it once two hundred newer days and outings had been written: it was there, then it was gone.
The feed now lists whole memories first (every trip, person and fact, newest first) and the
parts folded under a trip after them, six hundred rows by default, and a trip's page asks the
box for its outings and days by the trip itself rather than looking for them in the feed.
The top of MEMORIES
One row of chips that wrap: add a memory, jot a note, about me, what you photograph, near you,
on this day; the lit one is open and its card sits under the row. The line that pointed at the
check-in is gone (CHECK-IN is in the menu).
A trip of one outing, and a trip that dissolves says why
A week in one town is one outing, and until now it took two outings to make a trip, so a trip
spent in one place never showed as one. A lone outing away for a night or more is a trip now
(its days fold under it as before). When a trip dissolves (its outings re-clustered, no longer
away, or gone) synthd logs the trip and what became of each outing, so a trip that vanishes
from MEMORIES can be traced in the log.
The weather and the maps say the truth
Two cards lied. The maps card counted tiles in a folder that never held them (geo/landtiles
rather than landtiles, and the wrong suffix), so a box with the coast and the roads cut said
"no map data on the box". The weather card read tallyd's "no places" flag at the wrong level of
its answer, so a box whose place list was empty said "not pulled yet" for ever. Both read the
right thing now; the weather card also says when the pull fails (the last error, how many
batches) and when the place list cannot be read.
The place list is built from the box's GeoNames rows with a population, and the population
column came on 4 October: a set imported before it has every row at 0 and gives the weather
nothing to pull. ghost.framed now notices such a set at start and imports the same files again
in the background to fill the column; ghost-cli ghost.framed geo-import does the same by hand.
The screen's stray line
The sweep that ran down the page on some page changes is gone. In its place a stray scanline
blinks somewhere in the middle three fifths of the glass for a blink or two, like interference
caught, and is gone within half a second; never at an edge, where it read as a border, and still
only now and then (CrtMood's roll, the same quiet).
The menu, and PERMISSIONS
INTEGRATIONS is one row under THE BOX (its cards open the pages; the three sub-rows and the
doubled label are gone). PERMISSIONS joins SETTINGS below the divider: every permission the
phone asks the OS for, in one list with the reason next to each and where it stands, GRANT ALL
running the same chain as the welcome (one dialog after another in the order the OS allows),
OPEN APP SETTINGS for a line the OS will not ask about again, Health Connect's own sheet, and
the battery setting that quietly stops the trail and the sync when the app is "optimised".
The weather by the hour, and the next day as a line
A pull carries the hourly forecast of three days now, with the current conditions and the
days, and a place is pulled again after sixteen hours rather than a day. The sky now is the
hour of the forecast the clock is in at the place (the pull's own reading only while the pull
is under ninety minutes old): a reading eight hours old no longer stands for "now". HOME's
weather card draws the next day as a line, the temperature across the hours with the chance of
rain as bars under it and the hour under every sixth; the lock-screen card and the widget take
the hour's temperature too. The pace stays under Open-Meteo's free day: six thousand places,
eighteen variables, every sixteen hours.
INTEGRATIONS, quicker, each in its own manner
The page asks the four daemons at once rather than one after another (its worst case was the
sum of four five-second waits, and one of them built a whole monitor report), the maps card is
counted once a minute, and the page draws its last answer the moment it opens while the box is
asked again behind it. Each integration's pull is drawn in its own manner: Wikipedia's pages
turn on their way and stack at the box, the news comes in bursts of lines, the market as a
ticker of bars jogging up and down, the weather falls in drops, the maps' tiles slot into a
grid beside the box, speech is a wave along the line.
The widget leads with the brief, and its news turns
Abroad, the widget opens with the box's brief under its head: BTC and ETH with their changes
on one line, the story of the moment on the next (a tall widget adds the weather where the
phone last is), and the phrase under them with how to say it, what it means and its buttons.
The "next" and "then" lines are gone (what comes next is NEXT's to show). The story turns with
the phrase: every rotation and every NEXT shows the next of the day's stories, where the
widget used to carry the top story alone from one brief to the next. The lock-screen card's
pulled-open text carries the same turning story.
The news digest as a list
The news notification was one block of text, the stories' leads run together. The box writes
the digest a story a line with a bullet first and the outlets after a dot ("• Rates held at 4%.
· 3 outlets"); the phone's notification page draws every line as a point, the list shows the
first story and how many more, and the OS's expanded notification reads the same lines. A
digest from before this cut (bare lines) is drawn as points too.
German for the day
The German pack carries fifty-four phrases for the day's business, all at level one: the
numbers (one to three, four to six, seven to ten, the tens), the time (what time is it, at
eight, half past, today and tomorrow, how long does it take), the shop (I would like, do you
have, that's all, cash or card, the receipt, a bag, too expensive, just looking, I'll take it),
the cafe (to take away, to eat here, still or sparkling, tap water, what do you recommend,
vegetarian, the same again, nothing else, separately, keep the change), the taxi, the bus and
the train (left, right, straight on, is it far, to the station, to the airport, to this
address, stop here, which bus, does this go to, a ticket to, single or return, which platform,
the next train, the next stop, where do I get off, is this seat free) and the hotel (the Wi-Fi
password, leaving luggage, checkout time). "Excuse me" and "Do you speak English?" step back
(the latter to level three); the practical lines weigh more, so they come round first.
The chat keeps the box's own numbers in
"How is the BTC price now compared to a few days ago?" read two memories, the photos and three
archive matches, filled the chat's six context lines, and left the model to say the box had
nothing on the price, when the box keeps a price a minute from seven exchanges. The box's own
figures (its prices, its rates, its news) have a place of their own in the context now, outside
the cap the memories and the archive share, and first in it; a question that looks back
("compared to a few days ago", "last week", "the trend") brings the last week of the box's
daily closes as well, so the comparison is made from the box's numbers.
The weather, forecast by the box
The box forecasts the weather itself now. It pulls the forecast models' own grids from the
centres that publish them, ECMWF's IFS (open data), the German weather service's ICON-EU over
Europe and NOAA's GFS, every run as it comes (a look every half hour, some 600 MB a run across
the three), reads them with a GRIB2 decoder of its own (simple and complex packing, and the
CCSDS coding ECMWF uses, checked against the reference library over nineteen fixtures),
reduces each run to the same six thousand places as before and blends the models into one
forecast per place: a weighted mean, a model standing alone far from the others left out,
their disagreement kept with every hour, the temperature moved from the model's ground height
to the town's with the box's own heights, the sky's word from the blended fields, the sun
from the place and the date. The phone reads the same rows as before; HOME's card names the
runs under the line ("the box's own forecast from ICON-EU 09Z, IFS 06Z, GFS 06Z · computed
12 min ago"), INTEGRATIONS › Weather names the centres, the chat says whose forecast it is.
Where the phone is, the forecast is finer still: every run keeps the model's own grid around
the phone's last position, and the box reads the forecast at that exact point with the
ground's height there, rather than at the nearest listed town; in Germany and its neighbours
the German service's 2 km model (ICON-D2) joins the blend for it, pulled only while the phone
is there. HOME says "where you are, near Neu-Ulm". Open-Meteo fills the table until the first
run is in and stands down after; it is gone next release. No weather service is asked anything about anyone: every box pulls the same global
fields. docs/WEATHER.md is the design, with what comes after (the stations, the radar, the
Met Office with a key).
The Met Office, with a key
A box whose owner has a Met Office Weather DataHub key puts it, with the order's name, in
SETTINGS › YOUR BOX › MET OFFICE; the box keeps both (the key is never read back, only "set")
and pulls the UK 2 km model's run for the order's region while the phone is in the UK, as the
fourth voice in the blend and the finest. The same section says where the box thinks home is,
from the trail's nights, so the order's region can be drawn around it. The DataHub's free
plan (a gigabyte a month) covers a region of two degrees, hourly to two days, four runs a day.
The first build refused the key for being too long; the DataHub's key is a token of over a
thousand characters, and the box now takes it.
The health data, diagnosed, and more of it
A box with no health data could not say why. The phone now sends its account of Health
Connect with every sync and on request from SETTINGS › HEALTH: whether Health Connect is
there, which permissions are granted and which not, what each record type holds (how many
records, from when to when, written by which app), how the last sync went, and one verdict
line ("Health Connect is empty: Samsung Health is installed but not sharing; Samsung Health ›
Settings › Health Connect › allow every data type"). The box keeps the last account, writes
it to its log, and ghost-cli ghost.tallyd health shows it. The sync also takes what a watch
measures at night and at rest now: the stages of sleep (deep, light, REM, awake), the resting
heart rate, the heart rate variability, the blood oxygen, the breathing rate, the VO2 max and
the body fat, each a line on HEALTH. CONNECT HEALTH asks for the new permissions once.
The first build of this shipped the six new kinds in code and not in the manifest, and Health
Connect offers only what the manifest declares: its sheet showed eight toggles, SETTINGS counted
"9 of 15", and ALLOW THE REST could not. Declared now. The probe covers the new kinds too, and
skips a kind the person did not allow, so the verdict speaks of data, not of a declined
permission. When some kinds are empty the verdict names them ("nothing in Health Connect for
sleep, heart rate, …; 1 kind(s) read"), because a Galaxy Watch's app shares each with Health
Connect one toggle at a time (Samsung Health › ⋮ › Settings › Health Connect), and a count
left the person guessing which. The box's readout showed the phone's account only as root could
read it; the file is handed to the run user now, as the inbox is.
HEALTH, three pages deep
The HEALTH screen was one list of thirty-day strips. It is a dashboard now, and it drills. The
top chooses the window (a week, a month, a season, a year, everything); under it every kind the
box holds sits in four groups, movement, sleep, heart and body, each a tile with the latest
value, how it stands to the window's mean and the window drawn small. A tile opens the kind: the
window drawn large with its mean, the stats (latest, mean, median, max and min with their days,
the total where it sums), the last seven days against the seven before and the last thirty
against the thirty before, the pattern by weekday ("most on Saturdays, least on Tuesdays"), and
every day listed with its bar. A day opens the day: all its numbers against the 30-day mean,
sleep's efficiency where the stages came, the heart rate's curve through the day from the
five-minute readings the box keeps (/v1/health/day), the day before and after, and the way to
the day's own page. Stats, not judgements: no goals, no scores, no streaks.
The kind page also says how much a kind varies day to day, this week against last, a year of
months as bars, and the records over everything kept. The dashboard opens with THIS WEEK.
The step count, the watch's own
The box's day sat under the watch's face (15,909 against 18,776). Samsung Health's tile
merges its devices, the watch and the phone, by the larger count in each slice of the day;
Health Connect's total for the day does not. The sync rebuilds that merge from the raw
records (ten-minute bins, the larger device per bin, summed, never both) and the day keeps the
larger of the merge and Health Connect's total.
The step count, argued from evidence
A day's steps on the box can sit under what the watch's face said. The diagnostics now carry
yesterday's steps four ways: Health Connect's own total (what the sync stores), Samsung Health's
records alone, the plain sum of every record, and that sum by the device that counted (watch,
phone), with the hour the last record ended. A plain sum above the total is Health Connect's
dedup of overlapping records; a watch sum under the watch's face is the watch's app not writing
them all; a last record before midnight is a day still to arrive, which the six-hourly sync
picks up, since it rewrites the last seven days each time. SETTINGS › HEALTH shows the line with
the verdict; the box logs it.
The weather, a page and a copy
The HOME card opens WEATHER with a tap: the sky now, two days as the line with the models'
disagreement drawn as a band around it, the days with their sunrise and sunset, every hour of
the days the box forecast (the chance of rain, the millimetres, the wind, the word, and ± how
far the models stood apart), and SOURCES, each model run the box read with a line on what the
model is and how old the forecast and the phone's copy are. The phone keeps the last forecast
it read, so the card and the page open on it before the box has answered again.
The place kept across the lock
Putting the app in the background locked it, as it should, and every unlock opened on HOME
whatever the person had been looking at: the gate replaced the shell and the shell's place was
lost with it. The place is kept now; the data behind it is still torn down and read again.
The health data, read with the app closed
Health Connect refuses a read from a background worker without its own permission for that,
and the app never asked for it, so the six-hourly sync failed every time and the box got a day
only when SETTINGS › HEALTH was pressed. CONNECT HEALTH asks for reading in the background now,
the sync says in its own words when a read is refused, and the app also ships the week on
opening when the last run is an hour old, which needs no permission beyond the kinds.
Box Status says what the daemons do
Four cards said "stub ok", "index empty (corpus not built)" or "retrieval offline (synthd not
built)", lines from the first week. They say what the daemon has done: synthd the memories it
keeps and when its last pass ran, cued the cues it raised this week, noted the journal
entries and this week's, shadowd the observations it has made.
The recorder, thumb-sized, with a pause
The voice recorder's buttons are the size of a thumb: record, then stop and pause side by
side, the clock and the level readable from arm's length; a paused note keeps the microphone
open and the clock still, and resume carries on in the same take. The past check-ins list shows
one row a day (a day checked in twice shows the row with a feeling), and the day column fits
"Wed 30 Sep".
The stock take
A pass over both sides for waste, the worst of it fixed here:
- The database client sent a statement again after a thirty-second timeout, as if the socket
had died; a slow statement ran twice and a slow index build never finished. A statement that
went out and was not answered is never resent now, and index builds over millions of rows
(Wikipedia's) wait as long as they take. - The frame-to-original lookup matched on a substring of the hash, which no index serves, so
the stock-take of a big archive grew with the square of its size; it is a range on the index. - A memory lookup by kind and ref had no index; every pass did one per outing, trip and day.
- The outings pass re-read every photo and tag every half hour because its change signature
counted the trail's points (one more every quarter hour); it now watches the trail only up to
two days ago. - Thumbnails and previews, named by hash, go out immutable with an ETag, so a scroll back costs
no request; POST bodies are capped (the unlock's PIN body was not). - The maps card counted a million road tiles on every poll; once a minute now.
- On the phone: the lock-screen card and the widget parsed the phrase packs on the main thread
at every alarm (an ANR at boot); they have a thread of their own. The notifications poll ran
every ten seconds on the gate and the PIN screen, where there is no session; it runs in the
shell only, every half minute. A sync rebuilt its notification for every chunk uploaded; twice
a second now. The YOU marker's pulse restarted with every fix; it restarts when the fix moves
thirty metres. The recorder's level no longer recomposes the whole check-in form ten times a
second. The unlock's tickers stop when the unlock is over. The memories filter runs once per
change, not per keystroke's recomposition.
An editor build keeps the permissions
Installing a build from the editor over a cut, or a cut over an editor build, asked for every
permission again and lost the enrolment: the two carried different signing keys, and Android
uninstalls first when the key changes. With the release keystore and its password in
~/.config/localghost/release.env (what tools/app_keystore.sh --store-pass writes), the
app's build signs every variant with it and each install is an update. The other cause, on a
machine that installs from the editor alone, was the editor itself: Android Studio's
"Clear app storage before deployment" was on in the run configuration, and every Run wiped
the app's data before installing. BUILDING.md says where the box is.
Smaller
-
A burst's representative holding a refusal in place of a caption no longer passes it to its
siblings at every stock-take (the "caption without sections discarded, frame queued again"
lines in searchd's log, the same frames every pass); health.sh reports the heights' packs. -
DAY says when the box did not answer, for the story and the photos, instead of "no story
yet" and "none from this day". -
The check-in's "why?" starts a capital after a full stop, and the page scrolls with the
keyboard: the line being typed stays above it as the text grows. The strip's key and its hint
are a line each (side by side, the hint wrapped a word a line down the right). -
Dates read the British way everywhere the app prints one (10 Oct 2026, not Oct 10, 2026);
"day(s)" and "frame(s)" are a word each.
The box has an address of its own, on LocalGhost's shared mail domain
(vlad@ghost.localghost.ai, so a sender learns nothing about the person) or on a domain of
the person's own whose zone holds nothing of the box. Mail for it lands on a relay LocalGhost
runs on the internet (mx.localghost.ai; the box never listens there), which seals
every message to the box's key the moment it arrives and keeps nothing it can read; the box
fetches the sealed files every two minutes while it is open, checks where each came from with
nothing but DNS (SPF, DKIM with both RSA and Ed25519, DMARC alignment, the server's reverse
name, a screen of the headers for the ways a message lies, a spam score from all of that),
takes it apart (the text, an HTML message reduced to its words with nothing loaded, the
attachments kept as files except what runs or hides things that run), stores it, and raises one
notification. A sender learns the domain, which is LocalGhost's, and the relay's address, never the box's.
MAIL in the app: INBOX, SENT and ARCHIVE, a search, every message with its verdicts in plain
words and its thread, REPLY, reply to ALL, FORWARD, ARCHIVE, DELETE, and a composer. Nothing
leaves until SEND is tapped: the box builds the message, signs it with its own DKIM keys (the
public halves in the domain's DNS, so any receiver can check it was the box), seals it to the
relay, and the relay delivers it over TLS, retries for a day, and reports a bounce into the
inbox. The box never sends on its own, never replies by itself, and the chat does not read the
mail. SETTINGS › MAIL has the line for the relay's boxes.conf (it carries the box's public keys,
which LocalGhost publishes) and, for an own domain, the five DNS records to paste; docs/MAIL.md is the design and docs/MAIL_SETUP.md the person's
steps. The relay itself lives in the web repository (deploy/mail/), its own Go module with no
dependencies, since the site's machine is where it runs; it runs as a system user of its own
with the port as its only privilege, and reads an outbound message the way receivers do.
The map, cooler, and with water
The map's base (the sea, the land, the coast tiles, the roads and their names) is drawn once into
a layer of its own, a quarter of a screen wider than the view on every side, and every frame of a
pan or a pinch moves and scales that layer instead of stroking a few hundred paths again; it is
drawn afresh when the view leaves the margin, when the zoom drifts far enough to blur it, when a
tile lands and the moment the finger lifts. The trails, the dots and the names stay live. This is
the heat: a long look at the streets was re-rendering a city sixty times a second.
Inland water is on the map: lakes, reservoirs and the banks of wide rivers filled in the sea's
colour with a shore line, rivers, canals and streams as blue lines, their names in italic. The
box cuts it from the same OpenStreetMap files as the roads, into the same tiles, in the same
build (the lakes' multipolygon relations read from the end of the file, their shores stitched);
a box with road tiles cuts them once more when it next starts (hours for a continent, the old
tiles served meanwhile) and INTEGRATIONS › Maps says "roads and water" when it is done.
Trips, by country
A week or more in one country is a trip of its own: ten days in Greece then ten in Romania are
two trips, not one. A shorter stay joins the neighbouring trip it is closer to in time, so eight
days in Germany then three in Italy are one trip, "Germany and Italy". A country is in the title
from two days in it; an afternoon on the way through is said in the body instead ("Through San
Marino on the way"). A title names three countries at most; a trip that would name four is cut
into two, in time order.
The calendar
The phone's calendar, every calendar it syncs (Google's, Samsung's, a work account's), a year
back and a year ahead, with each event's place, guests and their answers, and notes, is read on
the phone and kept on the box. It is sent when the calendar changes (Android says when), every
six hours, and on opening the app, and skipped when nothing changed; the box replaces the window
whole, so an event deleted on the phone leaves the box at the next sync. No calendar service is
asked anything and nothing is ever written to a calendar.
The calendar is a card under INTEGRATIONS (the phone's own, after the box's), not a menu row; it
opens the agenda from today, a month at a time either way, and a search across two years; each event opens to its guests, their answers and its notes. HOME has NEXT, the next
three events in the coming day and a half. A day's page has what the calendar had that day, and
the day's story knows it (as planned, not as proof it happened). The chat answers questions
about plans ("what's on tomorrow", "am I free on Friday", "when is the dentist") from the box's
copy and never sends them to the web. SETTINGS › CALENDAR has the permission, a switch, the last
sync, SYNC NOW and FORGET. docs/PHONE_SOURCES.md has the plan for the next sources: contacts,
notifications (WhatsApp with them), the call log and SMS, screen time.
The contacts
The phone's contacts, every account it syncs merged into one person each, with their names,
numbers, addresses, organisation and birthday (not the photos, not the notes), are read on the
phone and kept on the box: sent when they change, every six hours, and on opening the app, and
replaced whole each time, so someone deleted on the phone leaves the box. Nothing is written to
the address book. CONTACTS is a card under INTEGRATIONS beside the calendar's: everyone by name,
starred first, a search over names, companies, numbers and addresses, each person opened to
their numbers, addresses and birthday. The chat answers "what's Ana's number", "Paul's email",
"whose birthday is this week" and "who is +44 7700 900123" from the box's copy, and the day's
story says whose birthday it was. SETTINGS › CONTACTS has the permission, a switch, the last
sync, SYNC NOW and FORGET.
The heights, on the box
The mirror carries the Copernicus DEM at 90 m for the whole world in 69 packs of a 30-degree
block each, 67 GB on disk (the first box took the lot in twenty minutes). A box takes them with
sudo GHOST_GEO_ELEVATION=all ./tools/update.sh maps
or a region, sudo GHOST_GEO_ELEVATION="34:72,-25:45" ./tools/update.sh maps (latitudes 34 to
72, longitudes 25 west to 45 east: Europe; several boxes space-separated). Every file's hash
is checked against the signed manifest; a run that stops resumes where it was. Packs already on
the box are kept current by any later update.sh maps, asked or not. ghost.framed indexes new
packs within ten minutes and draws every day's path again with its climb and its highest point;
sudo ./tools/ns.sh ./bin/ghost-cli ghost.framed elevation says how many packs are in and,
with lat= lon=, the height at a point. The phone shows the heights on MAP (a day's trail
panel) and in the DAY story; INTEGRATIONS › Maps and heights says "heights N packs".
The same update.sh maps run brings the time zones (set tz) to a box that has none; ghost.framed
builds its zone grid from them at its next start, and from then the box's "today" and its
evening are the person's, not UTC's.
What a security audit found, closed
Both trees were run through Cloudflare's security-audit skill (ten hunters, a coverage critic and
one independent verifier per candidate, source and offline tests only). Six findings reproduced
and are closed in this release: the mail parser unfolded headers in quadratic time and judged
with no deadline, so one crafted message could hold the box's mail loop for hours (linear parse,
a header block cut at 128 KB or 2000 lines, judging bounded to a minute, a message that fails
three times given up on with its original kept); the PIN registry on the OS disk stored each
entry's role in the clear beside its hash, so the wipe PIN was identifiable (the roles are now
masked under a key only the PIN derives, the real entries sit at random positions, and an old
registry is rewritten at the next start); a wrong PIN was documented to revoke the live session
and the revoke sat on a path a failed unlock never reached (it revokes the moment the unlock
fails); a phone could pre-issue spare certificates through the key rotation and retiring the
phone left them working (retire covers what a device minted, a device has one rotation
outstanding at a time, and DEVICES counts them); the log guard applied a model reply that
lowered a daemon's level to debug (only a quieter level is applied, in code); and a relay
authorised an outbound message by its last From header while receivers read the first (the
relay reads headers the way receivers do). Twelve leads whose decisive fact needs a device, the
model, Postgres or nginx are in the audit report with owner checks; the caps they asked for
(attachments per message, header fields, DKIM signatures, SPF queries, location batch points,
Postgres parameters) are in this release too.
The door, shut for anyone without a certificate
ghost-ctl edge-passthrough now writes the edge flag itself once its check passes, so the move
to secd's own TLS and the refusal of plain HTTP are one step; a plain request with no
certificate header gets the down page in either mode, every refusal is the same 503 page, and
the stream never carries plain bytes to secd.
Known gaps at 0.0.7
- 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 request without the header is refused
either way); a box reached by IP alone cannot take the passthrough yet. - The box CA key stays on the OS disk, by choice.
- A phone left for two weeks has to be enrolled again with a fresh QR; carrying its data over
to the new enrolment is 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.
- CRYPTO50's day value against its live value showed a fault on 3 October whose cause is still
to be named. - The Mist (peer-to-peer backup) and the decoy volume are not built. Mail from a box on a home
connection is refused by the large receivers until the sending IP has a reverse name. - The box's forecast has no verification yet: nothing scores yesterday's forecast against what
happened, so the blend's weights are set by hand. (An earlier version of this page said the
forecast had no run through a real centre yet; the development box has run on the real
centres' runs since 10 October, through Germany, Italy and the UK.) The mirror's daemon
(docs/MIRROR.md) is specified and not built.