Skip to content

Repository files navigation

core

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.

Building it

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.

How the tree is arranged

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.

What the gate builds for, and what nothing here covers

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.

What this core sends, and to whom

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.

Sending a crash report by hand

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.

License

AGPL-3.0, copyright 2026 Nils Lehnen.

The full text is in LICENSE.

About

The shared core every Flowfin client uses: server access, session, cache, artwork and the speed budget

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages