Every Flowfin client needs the same things and none of them should write those twice: talking to a Jellyfin server, holding a session, caching what was fetched, decoding artwork, tracking playback position, and measuring whether the speed budget was met. Eleven clients written independently drift in what they cache, in when they give up on a slow server, and in what they call fast. The speed budget is written as numbers a build can miss, and a number nothing measures is a wish, so this is where those numbers are instrumented. What shared means technically is decided: one Rust library reaching each client through a foreign function interface generated per platform, recorded with its costs in 0011. The core draws nothing: a core that knows about widgets stops being shared the first time two platforms disagree about a list.
Planning happens on the issue tracker first. Every decision that shapes the architecture is written down there with its reasons before the code that depends on it exists.
A fresh clone needs a Rust toolchain and a network. cargo, the formatter and
the analyser all arrive with the toolchain, and the manifest declares one
dependency, which is fetched on the first build.
THIS PARAGRAPH SAID THERE WAS NOTHING TO FETCH AND THAT THE MANIFEST DECLARED NONE. Both stopped being true when the cache key derivation landed: 0041 requires a cryptographic digest, 0011 measures that the toolchain offers none, and 0103 is the rule that admitted one. What is there, and what it reaches, is read rather than written here:
cargo tree -e normal
That record is still what may ever be added, and every entry carries the clause that admitted it and what would retire it beside it in Cargo.toml. The version is pinned in rust-toolchain.toml, which the toolchain manager reads by itself, so a fresh clone gets the right compiler without being told to install one. A compiler that arrived some other way is told which version this tree expects, by number, rather than meeting a compile error.
Two commands, and they are the two the gate runs rather than variants of them:
cargo build --locked --all-targets
cargo test --locked
--locked is there in both so that a build which would rewrite Cargo.lock
fails instead of proceeding quietly. --all-targets is there so that the first
command builds the tests as well as the library.
THIS PARAGRAPH SAID THAT MADE IT A BUILD OF EVERYTHING RATHER THAN OF HALF OF
IT, AND IT IS NOT. --all-targets selects the test targets carrying
test = true, so the two targets Cargo.toml declares with test = false are
not compiled by it, and a file in either of them that stops compiling leaves this
command green. Measured rather than reasoned about:
printf '\nthis is not rust and will not compile;\n' >> tests/needs_a_real_server_or_real_hardware.rs
cargo build --locked --all-targets ; echo "exit=$?"
exit=0
cargo build --locked --test needs_a_real_server_or_real_hardware ; echo "exit=$?"
exit=101
Both are compiled anyway, by the second step of the build check, which derives
the excluded set from Cargo.toml and builds each one without running it. One of
the two is also run, by .github/workflows/thread-detector.yml. So the paragraph
above is about what that command reaches rather than about what is left uncovered:
bash .github/excluded-targets/excluded-targets.sh check | tail -1
Every target the ordinary command leaves out compiles.
One directory under src/ per thing
0003 says the core owns, so
that the boundary is visible in the tree and not only in a document:
src/server/, src/session/, src/cache/, src/artwork/, src/playback/ and
src/measurement/.
Three directories beside those six are not concerns from that record and say so in
their own first paragraph. src/failure/ holds the error vocabulary the six map
onto, src/diagnostics/ holds the sink a client supplies, and src/clock/ holds
the one source all three clocks reach the core through, because
0102
states a rule per clock and a reading taken anywhere else would be a deadline no
test can move.
What each type is, and the statement about which thread a client may call it from,
is written where a reader meets the type; tests/thread_statements.rs is what
refuses a change that breaks one of those statements. What each directory holds
is read rather than listed here, because a list of what has landed is wrong on the
next landing:
git ls-tree --name-only origin/main src/
THIS PARAGRAPH COUNTED TWO DIRECTORIES AND SAID THERE WAS NO BEHAVIOUR IN ANY OF
THEM. There are three, and the claim that none of them holds behaviour stopped
being true of this tree. It was written when the tree held none; src/lib.rs
carries the correction for the sentence it made about the same thing, and this
file was not brought into step with it. Both errors ran in the direction that
makes the tree look emptier than it is, in the first file anybody reads about this
repository.
Read this before assuming a green tick covers the platform you are writing a client for.
The library is compiled for one target triple per client platform family on every pull request. The set is data rather than prose, so it is read rather than quoted:
grep -v '^#' .github/targets/targets | grep .
Each line carries the reason that platform is there, and a line carrying a triple and no reason is refused by the check itself. Why the set is shaped that way, and why the whole leg reports under one check-run name instead of one per platform, is 0113.
What that leg proves is that the core compiles, and nothing else. Not one line of the suite runs on any of those triples. The suite runs on the runner's own host and nowhere else, which is 0011's sentence that the core's own tests test the library rather than the binding.
Four things no run here touches, named rather than left as silence. A platform outside that register, including a television running webOS or Tizen and a desktop on Windows on ARM. The second architecture of a family that has one entry, so macOS on Intel, Linux on ARM and 32-bit Windows are not compiled. The binding layer, which does not exist in this tree, so a green run says nothing about the interface a client actually calls. And the optimised build, since each triple is compiled once, unoptimised.
One more, from a different leg. The thread detector reaches neither the Windows nor the Android target, so a race that appears only on one of those is outside every run this repository makes. Compiling for both here is not the detector reaching either.
Nothing, other than to the server an operator configured. There is no telemetry, no analytics and no crash reporting here: no data about a person, a device or a failure leaves for anybody but that server, and there is no setting that turns such a route on, because there is no route to turn on.
That is the position in
0068 and it is checked
rather than promised. The invariants check refuses a telemetry, analytics or
crash-reporting package in the resolved dependency graph, which is the way one
of them usually arrives - as a dependency rather than as a decision, so that the
decision never gets made. The refused names are data rather than code:
git grep -A1 '^id: no-reporting-dependency' -- .github/invariants/rules
Two bounds on that, stated rather than left to be discovered. The check is a
name list and not a purpose test, so a reporter published under a name nobody has
written there is not refused by it. And it reads the dependency graph, so it
cannot see a few lines written directly in this tree that kept something and sent
it; what stands against that is no-network-outside-the-transport in the same
register, which refuses a socket opened anywhere in src/ outside the one
transport, and the review.
The position is that nothing is sent automatically, not that nothing may ever be sent. If you want me to see a crash, open an issue with it. Strip it first: the server address, the account name, the token, the device identity, and any title or identifier out of a library are the fields 0068 lists as personal, and an issue here is public from the moment it is submitted.
Where the crash is a security problem, SECURITY.md is the route instead, and it says plainly whether that route is open today.
There is no private destination for an ordinary crash report. Which address this repository publishes, for a vulnerability or for anything else, is entry 5 of #1 and is undecided, so a public issue with the fields above removed is the whole of the by-hand route today.
See NOTICE.md for the intended-use notice.
See SECURITY.md for how to report a security problem, what this repository treats as one, and what a reporter gets back.
AGPL-3.0, copyright 2026 Nils Lehnen.
The full text is in LICENSE.