Repository navigation
Releases: denislov/fingerprint-browser
Release list
FpBrowser 0.6.0
Added
-
A started profile shows what it is (Runtime/Window): a profile now opens a
local page first, served by this program on a loopback port for as long as the
profile runs. It says which profile the session is - the name, the core's
version and major, the proxy's name, the ports, when it started, and any
fingerprint switch this core cannot honour - and it answers where one request
leaves from: one request through that profile's engine to the configured
endpoint, the same reading the Proxies page's Test takes, reported as an
address or as the class of fault that stopped it. That is the engine's exit
address and the page says so; whether the browser itself was pointed at the
engine is still the window's Verify question, and a profile with no proxy is
told that nothing was asked rather than shown a number it did not earn.The page talks to nothing but this program: every number reaches it over
loopback, and the policy it is served under (default-src 'none'with
connect-src 'self') is what keeps that true. It is read-only - no endpoint
changes anything, none takes a parameter, and a request that names another
Hostis refused, so no other page in the browser can read it and none can
make the machine ask about an address of its choosing. Nothing the request
carried is echoed back and the proxy appears only as its name.The page holds the port it was reserved rather than releasing it, which is the
one place in this program with no release-to-bind window: the browser is sent
to an address something is already answering on. It ends with the session - a
stop, a crash and a reclaim all drop it - and a session adopted from a previous
run has none, because that run's page died with it.Identity page on the Settings page turns it off and on; it is on unless the
reader says otherwise, and turning it off is enough to leave it out of the next
launch. See identity page.The key is written only in the off state: while the page is on (the default),
config.jsonhas nostatus_pageentry, so a configuration written by this
build stays readable by a build that predates the switch. Once the page has
been turned off, the file carriesstatus_page, which an older build refuses
as an unknown field - it reports the file as unreadable, falls back to
defaults and refuses to save. To go back to an older build after using the
switch, remove that line fromconfig.json; the page is then on again. -
A core newer than the newest measured one is named as unmeasured
(Runtime/Window):CoreCapabilities::NEWEST_MEASURED_MAJORis 148, and a
profile whose core is above it is warned at start that its fingerprint
switches were emitted from the measured rule without having been read back on
that build. The Download Core dialog names the published versions above the
line. fingerprint-chromium 150 is the first: on its Windows asset,
getImageDataon a canvas that has been drawn on with a seed hangs for a
region of 4x4 pixels or more (1x1 and 2x2 return), so the fingerprint suite
could not certify it; the Windows 148 control passes the same suite. The
evidence and the reproduction are in
fingerprint-matrix.md. -
A failure matrix (Docs): one page maps the failures each subsystem is
built to survive to the behavior that keeps data, processes and inputs safe,
the test that proves it, and the opt-in acceptance runs that need a real
browser, engine or network. See
failure-matrix.md.
Changed
-
The minimum Rust version is declared (Build): the workspace's
Cargo.toml
carriesrust-version = "1.98.1", inherited by every crate - the same
compilerrust-toolchain.tomlpins and the gate and releases use. An older
compiler is refused bycargowith a clear message instead of failing
somewhere inside a dependency;crates/app/src/version.rsfails the gate if
the two declarations drift. -
The architecture page points at the references instead of restating them
(Docs):docs/architecture.mdkeeps the positioning, the architecture diagram,
the dependency direction and the principles; the design prose that duplicated
the subsystem references became links, and the reference pages gained the
rationale that previously lived only on this page (the appearance palette
rules, the profile filter, the Xray inbound, the argument order, and more). -
The sidebar folds instead of jumping (Window): the rail's width is
sampled from a spring rather than taken from the layout, so collapsing and
opening it is something the reader watches happen. The spring is the design
system's own (Theme::motion, the same place this window's colours come from),
and it carries velocity: a toggle pressed twice redirects the rail from where
the first press got to instead of restarting it. A settled sidebar asks for no
frames at all, and a reader whose system asks for less motion gets the fold
instantly - the same switch that the component library's dialogs and
notifications already answer to.
Removed
- The implementation plan is drained (Docs):
docs/implementation-plan.md
is deleted. The facts that existed only there moved into the docs that own
them - exit modes, releases, UI shell, settings, diagnostics, backup and
restore, proxies and Xray, traffic verification - and its one open item,
reproducing remote upstream forwarding on Windows, is tracked as
issue #5.
Fixed
- A stranger's port can no longer pass for the engine (Runtime): the
SOCKS port is reserved and released just before Xray is spawned, so
another process can hold it in between; readiness accepted any listener
there, which let an engine that never bound pass on the strength of a
stranger's socket. The launch path now waits for the SOCKS5 greeting the
inbound is built to answer - the rule CDP readiness already applies by
reading/json/versioninstead of accepting any listener. What readiness
proves is otherwise unchanged: not that traffic flows, which remains the
diagnostic's question.
FpBrowser 0.5.0
Changed
-
The database journals ahead of the file (Storage): writes - a saved
profile, an imported row, a re-probed core - now commit in WAL mode with
synchronous = NORMALinstead of creating, syncing and deleting a rollback
journal around each one. What the trade costs is named where it is made: after
a power loss the last few commits can be gone, where the default would have
kept them; the database itself stays consistent. The two sidecar files this
adds (app.db-wal,app.db-shm) are listed in--diagnostics, and a
filesystem that cannot support WAL keeps the old mode. The profile table also
gains indexes on its core and proxy columns, which the delete paths and the
"in use" lookups scan. -
A first frame that reads less and waits less (Storage/Window): starting no
longer reads every table twice, every frame queries only the page on screen
rather than every page's rows, and the activity log keeps its file open
instead of re-opening it per line. A machine with several registered browser
cores no longer waits for each core's--versionin turn before the window
opens - the probes run together, so the wait is the slowest core's rather than
their sum. -
A core is registered without stopping the window (Window): the Add Core
form, an edit that re-points a core, and a browser a finished download
unpacked all read the binary's--versionbefore storing it - a process the
window does not control, started on the thread that draws it. That read runs
on a worker now, so a slow or hung binary no longer freezes the window: the
form says it is checking, a second press waits for the answer instead of
asking again, a refusal is written on the form where the path that was
refused still is, and a save that worked closes the form once its answer has
landed. The banner notices are unchanged, so a downloaded core is still
announced, and refused, exactly like one added by hand. -
The window redraws when something changed (Window): the tick used to
redraw the whole window once a second whether or not anything had moved. The
runtime rows are compared with what is on screen first now, and a frame is
asked for only when the answer is that something is different. The one
redraw the clock still owns belongs to the two places that read the clock
themselves - a log line's age, and how long a running profile has been up. -
The two downloads share one implementation (Window): the browser-core and
Xray-engine downloads had their own copy each of the same machinery - the
GitHub API request, the reading of the release list, the streaming fetch with
its.partrename and progress reports, the download's state in the window,
the progress row and the footer's two buttons. That machinery is one
implementation now (releases.rs, its sentences intext/releases.rs, the
dialogs' common pieces inui/downloads.rs), and each feature keeps what it
actually differs in: which assets a release offers, what a finished download
becomes, and what each dialog says. Nothing a reader sees, downloads or finds
on disk has changed.
Fixed
- A database that will not open says why (Startup): an installation whose
app.dbcannot be opened - an unreadable file, a directory without
permission, another program holding it - used to end in a panic message and
nothing else. It now says what failed, on the terminal, in the configured
language, writes the report--diagnosticswrites beside it (the question it
answers is "why will it not start"), and exits with code 1.
FpBrowser 0.4.0
Added
- Use system proxy on the Settings page (Runtime): a machine that reaches the
internet through a proxy of its own now gets both hops. With the switch on, the
engine dials the profile's proxy through this machine's proxy instead of
connecting to it directly, which is what a profile whose proxy is only
reachable that way needs. The reading is taken from the process's environment
first (all_proxy, thenhttps_proxy, thenhttp_proxy, either case), and
then from the platform's own setting - GNOME throughgsettings, macOS through
scutil --proxy, Windows throughreg query- and turning the switch on
reports, once, as a toast, which of the three answers this machine gave: the
address that was found, that a proxy is chosen by a script and cannot be read,
or that nothing was found. A machine with nothing to find is not an error; the
switch stays on for a machine whose proxy appears later, and the reading is
taken once per click rather than while the page is drawn, because on three of
the four platforms it is a process to spawn. - The switch is baked in at start, like the Xray executable: the row says next
start, and a config is built with the value the run began with, while the
machine's proxy itself is read afresh for every launch, so changing the proxy
on the machine needs no restart. A proxy test uses the same hop a launch would,
so a test cannot pass where the launch fails.FP_BROWSER_SYSTEM_PROXY(on,
off, and the words a switch is otherwise spelled with) decides the switch
ahead of the config file for a machine whose proxy is stored somewhere this
does not read - KDE's setting, for one - and while it is set a click on the
switch is refused by name rather than storing a value that would move back
under the reader's hand. What is deliberately not read is a proxy chosen by a
script: a PAC file or WPAD is reported as itself, and a machine with nothing
readable goes on dialling the profile's proxy directly, which is what the toast
says when the switch is turned on. See
through this machine's proxy for that
and for the two consequences worth knowing first - an HTTP hop resolves the
profile's host name rather than this machine doing it, and an HTTP hop cannot
carry UDP, so a profile that tunnels UDP itself fails the connection instead of
leaking around the proxy.
Changed
- The program is renamed from
fingerprint-browser/Fingerprint Browserto
fp-browser/FpBrowser. The executable, the desktop entry and the
Start-menu entry now carry the short name - the installed command is
fp-browser- and the title bar, the tray tooltip, a diagnostics report and
--versionshowFpBrowser. Nothing a user has on disk moves: the data
directory is stillFpBrowser, the configuration directory is still
fp-browser, and theFP_BROWSER_*variables are unchanged, so no setting is
re-entered and no file is migrated. A command line that named
fingerprint-browserhas to namefp-browser, and on Linux the Debian package
is nowfp-browser- a name of its own, so the oldfingerprint-browser
package is removed separately after the new one is installed. On Windows the
installer's application id is unchanged, so an upgrade replaces the installed
copy in place.
Fingerprint Browser 0.3.0
Added
- The Browser Cores page can fetch a core instead of only being told where one
is. Download Core opens a dialog listing what
adryfish/fingerprint-chromiumhas published, newest first: one row per
version, with the publication date and one button for each system that version
was built for - the one this window runs on filled in and named as such, the
other two beside it for a machine that is not this one. The asset offered is
chosen by what the release actually carries rather than by what the tag says:
the Linuxtar.xzand the Windows portable.zipare preferred over the
AppImage and the installer, because those are the two this program can unpack
by itself. - Downloading is one click and runs on a worker: the file is streamed to a
.partname under<data directory>/cores/<tag>, renamed once it is complete,
unpacked beside it, and searched for the browser binary - by exact name, so
chrome_sandbox,chrome-wrapperandchromedriverare not mistaken for it,
and by the one directory a macOS bundle keeps its executable in. The binary is
then registered through the same probe the Add Core form uses, so a core's
major always comes from the version the binary reports and never from the
release tag. Progress is reported on the row it belongs to, one download runs
at a time, and a second click is refused with the name of the one already
running rather than queued behind it. - A download that did not produce a core is not reported as one. An installer or
a disk image is saved and the reader is told where it is and what to do with
it; an archive with no browser this program recognises is named by the
directory it was unpacked into; a binary that cannot be probed is named by its
path as well as by the probe's own reason. The cores folder is reachable from
the dialog, and is created when it does not exist yet. - The HTTP client is
ureq- the one the runtime already uses - withtarand
xz2for the Linux archive andzipfor the Windows one. Both unpackers
refuse to write outside the directory they were given, which is why a release
archive is unpacked with a library rather than by running the platform'star. - Two opt-in acceptance tests cover what the fakes cannot:
core_releases::tests::the_real_repository_answers_with_releases_this_can_read
andcore_releases::tests::a_real_release_downloads_and_unpacks_into_a_browser
dial GitHub and move a real core. Both are ignored by default; run them with
--ignoredwhen the request shape, the parsing or the unpacking changes. - The Settings page can fetch Xray too, from a card under the Xray executable
row - the same door the core page opens, and open the same way: always, because
the reader decides which engine to run, and never without a click. It lists what
XTLS/Xray-core
has published, newest first, and offers the build for the machine this window
is running on - only that one, because the architecture is in the archive's
file name and the wrong archive is not a choice but a mistake, and the button
says which platform and architecture it is about to fetch. A release that
published nothing for this machine says so where the button would be. - What a download unpacks is the engine and upstream's licence, under
<data directory>/xray-releases/<tag>, and nothing else: the geo data that
travels in the same archive is left out, and no entry is allowed to write
outside the directory it was given. The path is written into the Xray executable
setting, which takes effect at the next start as that setting always does; when
FP_BROWSER_XRAY_BINis set the download says so instead of claiming an engine
is installed, because the variable outranks the file it just wrote. Where the
binary came from is recorded beside it - repository, release, file, URL, the
archive's SHA-256 and upstream's.dgstURL for it - and the diagnostics report
carries that record when there is one, and says nothing when the engine is one
the reader supplied.
Fixed
- A download that is still arriving is no longer reported as failed on a slow
line. The HTTP client's body timeout is a budget for the whole body - its clock
is not restarted by a read that arrives - and both downloaders had set it to two
minutes believing it bounded a stalled connection. That made a hundred-megabyte
core need a line better than a megabyte a second, and the twenty-megabyte engine
archive better than 170 KB/s, which is the failure a reader on a slower line got
instead of their browser. Both now use one budget, generous enough that only a
transfer which has effectively stopped trips it.
Fingerprint Browser 0.2.0
Added
- The window answers the keyboard. A profile row is a target now: Tab reaches it,
Enter opens the panel that describes it, and Escape puts that panel away and
gives the keyboard back to the row it came from - as does closing it with the
pointer. Which row the keyboard is on is drawn as the ring the rest of the
window's controls use, which is deliberately not the same thing as the row being
chosen: a reader can see both at once, and tell them apart. Two keys belong to
the window rather than to any control, and are bound without a key context so
that a dialog, a menu or a dropdown - all of which bind the same key themselves -
win over them: Ctrl+F puts the keyboard in the profile filter, switching to
the page that has the field first, and Escape closes the top layer, which
means the dialog when one is open and the details panel otherwise, and never the
window. A window that opens with nothing focused answers Tab with nothing at all- keys are matched along the path from the focused element, and there is no path
- so the first frame puts the keyboard on the view itself, which is not a tab
stop, and Tab from there reaches the first control in the sidebar. A row's focus
handle is kept per profile rather than made per frame: a handle made fresh each
frame would drop the keyboard at every runtime poll, and the test for that is
the_keyboard_stays_on_its_row_when_the_runtime_reports.
- The Linux packaging also builds a Debian package.
packaging/linux/package.shwritesfingerprint-browser_<version>_<arch>.deb
beside the archive, out of the same staged files: the binary in/usr/bin, the
menu entry, the icon in its two themes, and the documentation under
/usr/share/doc/fingerprint-browser- the licence under policy's name for it,
and the changelog compressed and namedchangelog.gz. Its dependencies are
asked ofdpkgrather than written down - they are the packages that carry the
libraries the binary asks the loader for - so the list cannot go stale the next
time a library moves between packages, and a machine withoutdpkg-debsays so
before the build rather than after it. The release workflow installs the
package withdpkg --install, runs the binary from/usr/bin, and removes it
again.
Fixed
-
Fixed Windows CI gate warnings in
crates/runtime/src/supervisor/ownership.rs
where thetreeparameter was unused under Windows compilation. -
Fixed Windows extended UNC path prefix (
\\?\) comparison mismatch during browser
data recovery incrates/application/src/browser_data.rs. -
Fixed Winsock non-blocking socket mode inheritance on accepted sockets in real Xray
integration tests (crates/runtime/tests/xray_real.rs). -
Enabled real fingerprint verification tests on Windows and adapted dynamic host
platform recognition and font fallback measurements in
crates/runtime/tests/fingerprint_real.rs. -
Selected navigation items, choice chips, details tabs and log filters retain
their selected colours on hover and press, using the button's selected visual
state rather than only its accessibility toggle state. -
Proxy and core lists now keep names and actions readable in narrow windows.
Below 1364 logical pixels, secondary metadata moves under the resource name;
wide windows retain the aligned columns. Layout regressions cover Chinese and
English at 960, 1200, 1364 and 1440 pixels, including opening each row menu. -
Sidebar navigation, settings groups, shared choice chips and the three close
choices now use keyboard-accessible buttons. Appearance and language choices
also share a wrapping row instead of stretching into separate full-width rows. -
The About menu opens the diagnostics/about settings group. Ctrl+F no longer
changes the underlying page or steals focus while a dialog is open. -
A record the database refuses is now reported as what it actually is. Every
constraint failure used to be reported as "id already exists", so a profile
naming a core that was not stored was told its identifier was taken; the
classification now reads SQLite's extended error code and separates a taken
identifier, a missing reference (naming which one), a value the schema refuses,
and a real database failure. The in-memory backend, which had no reference rules
at all - so the application's own tests proved nothing about them - now refuses
the same writes for the same reasons, and one contract test runs against both. -
A profile whose browser could not be stopped is no longer reported as stopped.
When a session adopted from a previous run could not be identified, read or
terminated, the stop failure went to the log while the session record, the
temporary Xray configuration and the profile's state were all cleared anyway - so
a live browser was left running with nothing tracking it, nothing refusing to
start it again, and no record for the next run to find. The stop now reports what
happened; a failure keeps the record, the configuration and the session, leaves
the profile running so it can be stopped again, and says so in the window. -
Deleting a proxy a profile uses no longer quietly leaves that profile without a
proxy. The database's foreign key cleared the reference (ON DELETE SET NULL)
while the service refused the same deletion, so any path that did not go through
the service - or two of them at once - silently changed a profile's network
egress. The key is now restrictive, which is the same rule where nothing can go
around it. -
Restoring a configuration backup is now all or nothing. It used to delete every
stored core, proxy and profile and then write the file's records one at a time,
with the file only being validated as each record was written - so a file with an
empty core name, a profile naming a core that was not in it, or a disk that
filled up halfway left an installation holding neither the old configuration nor
the new one. Restore now validates the whole file first (the rules a form is
held to, plus repeated identifiers, dangling references, and a backup whose
credentials were exported away, each refused with its own sentence) and then
replaces everything in a single database transaction, so a failure at any point
leaves the previous configuration exactly as it was. -
A start, a browser-data copy and a configuration replacement can no longer
overlap on the same profile. A start returns as soon as its command is queued,
so the runtime's snapshot still said the profile was stopped while its browser
was coming up - long enough to begin copying a profile that was already
starting, or to begin a second copy that cleared a destination the first was
still writing. Each operation now takes a lease on the profiles it will use, the
worker that runs a copy owns that lease and gives it back when it returns, fails
or panics, and replacing the configuration is refused while any profile is held. -
A profile's start page must be a page. It is appended to the browser's command
line as an argument of its own, so a value from an imported configuration that
spelled a switch ---no-proxy-server, say - was Chromium's switch to obey, and
the proxy the profile's fingerprint depends on was off. The domain validation,
the service that stores a profile, the configuration import and the launch
planner all refuse it now, and the error names what is wrong with it. -
A browser-data copy can no longer destroy the data it is reading. Every path in
a run is resolved and compared before the first write, so a backup directory
inside the profile it copies, a restore whose destination contains its source, a
destination reached through a symlink, and two profiles pointed at overlapping
directories are all refused with nothing written. -
A browser-data copy is built whole beside its destination and renamed onto it
only once it is complete, so a copy that fails partway leaves the directory that
was there exactly as it was. A run killed between the two renames is put back by
the next start - or, for a destination inside a backup directory, by the next
copy - and the profiles it was put back for are named in the window. -
A desktop that will not take a tray icon no longer hides the window. "Keep
running" put the window away whether or not the icon had been created, and on a
desktop with no StatusNotifierItem host - a refusals path the code already
logged and then ignored - the window went with nothing left to bring it back: a
running program with no window, no icon and no way to reach it. The icon is now
started through an injected starter (tray::TrayStarter), a refusal keeps the
window on screen, and the banner names the desktop's own reason. The behaviour
is pinned by a test that hands the window a starter which always fails. -
A long banner no longer pushes its own dismiss button off the window. The
message for three missing cores is a page of text, and with nothing in the row
allowed to shrink it took the whole width and carried the button past the edge,
which left a failure the reader could not put away. The sentence truncates with
the whole of it on its tooltip, and the log keeps it in full. -
The New Profile dialog is titled and confirmed in the interface's own language.
It said New profile and Create in English whatever the language was set
to, because the two strings were passed to the dialog as literals instead of
coming from the message catalogue; the profile editor's platform fields were
hardcoded the same way.
Changed
- Writing a configuration backup, importing one, restoring one and re-reading a
core's version no longer run on the thread that draws the window. All four are
slow in the way a page switch is not - an export reads every core, proxy and
profile, an import then writes every record in the file one at a time, a restore
replaces the whole configuration in one transaction...
Fingerprint Browser 0.1.0
Added
- Three ways to leave, and the question that picks between them. Closing the
window can keep the program running in the background, leave the program while
the browsers and proxy tunnels it started keep running, or stop everything -
and the choice can be remembered, or changed on the Settings page. "In the
background" means the window is minimized and a tray icon brings it back or
leaves: a StatusNotifierItem over D-Bus on Linux (ksni, pure Rust, so no GTK
or libdbus enters the packages) and the shell's notification area on Windows
(tray-icon); the tray's Quit… always asks rather than obeying the
remembered answer, so the tray cannot become a program nobody can leave. - A start adopts the sessions a previous run deliberately left running, so
they are running profiles again rather than leftovers to clean up. What a crash
left is reclaimed exactly as before, and a session record that cannot be marked
is released and logged rather than stranded. A signal still means stop
everything. See exit modes. - One window per data directory. A second copy cannot take the lock on the data
directory, prints which process holds it, and exits 3 - instead of opening the
same database and reading the first copy's running browsers as orphans to stop.
The lock belongs to the operating system, so a crash cannot strand it, and
--help,--versionand--diagnosticsare still answered while a copy runs.
The lock file is one more row in the diagnostics report's file list. --versionprints the version, the commit the binary was built from and the
platform; the same version is in the window's title bar, and the build, the
platform and the data directory are the first line of the activity log.--help, in the language the config file names, and refusals for arguments
this program does not have.--diagnosticswrites one report about this installation for a bug report:
the build, every effective setting with the source it came from, the state and
permission bits of every file the installation keeps, and the end of the
activity log. The same report is behind a button on the Settings page. It
never opens the database and never reads browser data.- A program icon, generated from
scripts/make-icon.py: a PNG, an ICO with
seven sizes, and an SVG. On Windows it also goes into the executable's resource
section, with the product name and version. - A Linux archive (
packaging/linux/package.sh) with the binary, an installer
for the current user, a menu entry and the documentation, and a Windows
installer (packaging/windows/package.ps1) built with Inno Setup. Each ships
with a checksum, and neither ships or downloads a browser or a proxy engine. .github/workflows/release.yml, which builds both artifacts for av*tag,
refuses a tag or a changelog that disagrees with the version, and attaches the
artifacts to the release. Nothing has been released yet.
Changed
- The binary is named
fingerprint-browser, which is what the help text and the
documentation have been telling people to run. - The distributed build is built with
[profile.release]: thin LTO, one codegen
unit, and symbols kept so a crash can be read. Notpanic = "abort", which
would take the supervisor's child-process cleanup with it. - An empty profile list says how to get a browser core in the order a reader can
act on it: the page inside the window first, thenFP_BROWSER_CHROMIUM_BINand
a restart. It used to name only the second, while offering a button for the
first. The whole first run is now written down in
first run.
Fixed
- A build made after a commit no longer reports the commit before it. The build
script relied on cargo's default rule - run again when a file in this package
changes - and a commit changes whatgit describeanswers without touching any
file, so a clean tree could produce a binary that named an older commit and
called itself-dirty. It now watches the git files that answer is read from. - A configuration file left in the old per-platform directory is folded into the
data directory at the next start, and the README no longer claims settings live
outside the data directory - which stopped being true when the config file
moved in beside the database.