You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Some decisions on this board are not the plan's to make. They are collected here
so that a reader looking for open questions has one place to look, and so that no
other issue on this board asks one. Nothing below is decided. Each entry states
the options and what each option costs, and stops there.
Everything downstream that waits on an answer says so and names this issue.
1. The licence of this repository
There is no licence. gh api repos/Flowfin/core --jq '.license' returns null,
and gh api repos/Flowfin/core/contents --jq '.[].name' lists .github, NOTICE.md and README.md. So the repository is under default copyright today,
which means nobody may copy, modify or distribute it, and NOTICE.md already
tells a reader that "the license contains the full warranty and liability
disclaimer" while pointing at a file that does not exist. That sentence is wrong
until this is answered, and it is the first thing a careful reader checks.
This has to be answered before the first line of shared code exists, because a
licence chosen after code has been published is a licence that has to be
renegotiated with every contributor who wrote some of it.
The options, and what each costs.
MIT. Four paragraphs, no obligations beyond attribution. Any client can link
the core, including one nobody in this organisation writes and one that is not
open at all. It carries no patent grant, so a contributor keeps whatever patent
claim they hold over what they contributed.
Apache-2.0. The same permissive effect plus an explicit patent grant and
defensive termination. It costs a NOTICE handling convention and a longer
file. It cannot be combined with GPL-2.0-only code, which matters only if
something GPL-2.0-only ever has to link this core.
MPL-2.0. Per-file copyleft. Changes to the core's own files come back; a client
that links the core does not become MPL. Compatible with store distribution and
with proprietary clients. It costs a per-file header convention and refuses a
private fork of the core itself.
LGPL-3.0. Similar intent to MPL, but through the relinking requirement, which
is the clause that collides with static linking and with app store
distribution terms. A statically linked core inside a signed iOS binary is the
case that has caused other projects the most trouble.
GPL-3.0. Every client that links the core is then GPL-3.0 as well, including
one written by somebody outside this organisation. Whether that is the point or
the end of the eleven-client plan depends on entry 2 below, since the four
options there differ in whether a client links the core at all.
One fact worth having in view rather than as an argument: the sign-on plugin in
this organisation is GPL-3.0, from gh api repos/Flowfin/jellyfin-plugin-sso --jq '.license.spdx_id'. It inherited
that from the archived project it revived rather than choosing it. The core is
not derived from that code, so nothing obliges it to match. Consistency across
one organisation is still worth something, and so is the fact that a core and a
plugin are used in different ways by different people.
2. The language, and what shared means
Stated in the kickoff and repeated here so the answer lands in one place. A core
serving Android, iOS, a desktop and a television has to cross runtimes, and the
four candidates cost different things.
Rust with a foreign function interface per platform. One implementation, real
work at each boundary, a build chain per target, and a generated binding layer
that has to be tested as code rather than assumed. Memory-safe by default,
which is the property that costs the most to buy any other way.
Kotlin Multiplatform. Cheap on Android and on a JVM desktop, an extra runtime
layer on Apple platforms, and it decides the language of every client that uses
it rather than leaving that open. Television platforms differ in what they will
host.
C++. Reaches every target with the least ceremony and is the most expensive to
keep safe. Every memory-safety property has to be bought with tooling, review
and sanitiser runs that are themselves gate legs somebody maintains.
No shared code. A written specification plus a conformance suite, with each
client implementing it in whatever fits its platform. Maximum duplication,
minimum coupling, and the specification becomes the artefact that has to be
perfect because nothing else is shared.
What the answer decides beyond the language itself: whether M9 ships a binding
layer or only a suite, whether M2's gate has one build leg or one per target
triple, whether the benchmark harness in M7 measures the core once or once per
runtime, and whether the licence in entry 1 reaches the clients through linking
or does not reach them at all.
3. The oldest Jellyfin server the core must work against
Every additional server line is a matrix leg in every gate, a compatibility shim
where an endpoint changed shape, and a second set of fixtures for the fake server
the suite runs against. The number is a product decision rather than a technical
one, so it belongs here.
Newest stable line only. Cheapest gate, and it excludes operators who have not
upgraded, which on self-hosted software is most of them for a while.
The two lines the plugins in this organisation already carry. The sign-on
plugin's own description names 10.11 on .NET 9 and 12.0 on .NET 10, so the
organisation already pays for two lines somewhere and the fixtures could be
shared in spirit if not in code.
Everything still in common use, including older 10.x. Widest reach, and the
compatibility shims start to shape the interfaces the clients see, which is the
expensive kind of cost because it does not go away when the old line does.
4. Whether the public interface is frozen before the first client ships
The core has no user until a client exists, and freezing an interface that has
never been used is how a bad interface becomes permanent. Not freezing it means
every client that starts early pays for every change.
Stay at 0.x and break freely until one client has shipped against it. Cheapest
for the core, and the client that goes first absorbs the cost.
Freeze a small surface at 1.0 now and add to it only. Expensive if the small
surface turns out to be the wrong one, and it will not be known which until a
client uses it.
Two tiers, a frozen subset and an explicitly unstable remainder, with the
boundary published. Costs a policy somebody enforces and a check that says
which tier a symbol is in.
5. The address a report reaches
Two documents on this board need a route to a person and neither can invent one. #24 owes a finder a private route for a vulnerability. #107 owes somebody a route
for a conduct report, and a code of conduct with no address is a document that is
useless at the moment it is needed. The two routes may be the same or different.
GitHub's private vulnerability reporting on its own. A switch rather than a
mailbox, it authenticates the reporter, and it costs nothing to keep watched
because the notifications arrive where the work already happens. It covers a
vulnerability and nothing else, so a conduct report has no route, and a
reporter without an account has none either.
A role address on the organisation's domain. One address that outlives whoever
reads it, and it publishes no person's own address. It costs a mailbox somebody
has to actually watch, and an unwatched address is worse than none, because the
document promises a reply.
A personal address. Nothing to set up and it certainly reaches somebody. It
ties the project's public contact to one person permanently, since an address
published in a document stays in the history after it is changed.
Different routes for the two cases. Honest, because a vulnerability and a
conduct report are not the same problem and rarely want the same reader. It
doubles what has to be watched.
Whichever it is, both documents state what a reporter can expect and by when,
which is already #24's condition.
6. Where a release is published, and under what name
#94 builds the release artifacts and #91 decides what the first release
contains. Neither can say where the bytes go, because that needs an account
somebody owns, a name claimed before somebody else claims it, and a promise about
continuity, none of which the plan can invent. Entry 2 narrows the options, since
one answer produces a library per target triple and another produces a document
and a suite, but it does not make this choice.
The chosen language's public registry as well. A client author adds one line to
a manifest and gets updates the ordinary way. It costs an account that outlives
whoever made it, a name taken early, and a package that is close to impossible
to withdraw once anything depends on it.
A registry under the organisation rather than the language's public one. Keeps
the name inside the organisation, and makes every client author configure an
extra source, which is the step a client author skips.
Nothing published anywhere until a client exists. Cheapest and most honest
while there is no consumer, and it means the first client is also the first
test of the publishing route, at the moment when something else is already
going wrong.
The answer also decides what #93's version scheme is a promise to. A number in a
public registry is a promise to strangers. A tag on this repository is a note to
whoever reads the repository.
Done when
Each entry has an answer written into this issue, and every issue
that names this one as its blocker has been updated to carry the answer it was
waiting for.
Some decisions on this board are not the plan's to make. They are collected here
so that a reader looking for open questions has one place to look, and so that no
other issue on this board asks one. Nothing below is decided. Each entry states
the options and what each option costs, and stops there.
Everything downstream that waits on an answer says so and names this issue.
1. The licence of this repository
There is no licence.
gh api repos/Flowfin/core --jq '.license'returnsnull,and
gh api repos/Flowfin/core/contents --jq '.[].name'lists.github,NOTICE.mdandREADME.md. So the repository is under default copyright today,which means nobody may copy, modify or distribute it, and
NOTICE.mdalreadytells a reader that "the license contains the full warranty and liability
disclaimer" while pointing at a file that does not exist. That sentence is wrong
until this is answered, and it is the first thing a careful reader checks.
This has to be answered before the first line of shared code exists, because a
licence chosen after code has been published is a licence that has to be
renegotiated with every contributor who wrote some of it.
The options, and what each costs.
the core, including one nobody in this organisation writes and one that is not
open at all. It carries no patent grant, so a contributor keeps whatever patent
claim they hold over what they contributed.
defensive termination. It costs a
NOTICEhandling convention and a longerfile. It cannot be combined with GPL-2.0-only code, which matters only if
something GPL-2.0-only ever has to link this core.
that links the core does not become MPL. Compatible with store distribution and
with proprietary clients. It costs a per-file header convention and refuses a
private fork of the core itself.
is the clause that collides with static linking and with app store
distribution terms. A statically linked core inside a signed iOS binary is the
case that has caused other projects the most trouble.
one written by somebody outside this organisation. Whether that is the point or
the end of the eleven-client plan depends on entry 2 below, since the four
options there differ in whether a client links the core at all.
One fact worth having in view rather than as an argument: the sign-on plugin in
this organisation is GPL-3.0, from
gh api repos/Flowfin/jellyfin-plugin-sso --jq '.license.spdx_id'. It inheritedthat from the archived project it revived rather than choosing it. The core is
not derived from that code, so nothing obliges it to match. Consistency across
one organisation is still worth something, and so is the fact that a core and a
plugin are used in different ways by different people.
2. The language, and what shared means
Stated in the kickoff and repeated here so the answer lands in one place. A core
serving Android, iOS, a desktop and a television has to cross runtimes, and the
four candidates cost different things.
work at each boundary, a build chain per target, and a generated binding layer
that has to be tested as code rather than assumed. Memory-safe by default,
which is the property that costs the most to buy any other way.
layer on Apple platforms, and it decides the language of every client that uses
it rather than leaving that open. Television platforms differ in what they will
host.
keep safe. Every memory-safety property has to be bought with tooling, review
and sanitiser runs that are themselves gate legs somebody maintains.
client implementing it in whatever fits its platform. Maximum duplication,
minimum coupling, and the specification becomes the artefact that has to be
perfect because nothing else is shared.
What the answer decides beyond the language itself: whether M9 ships a binding
layer or only a suite, whether M2's gate has one build leg or one per target
triple, whether the benchmark harness in M7 measures the core once or once per
runtime, and whether the licence in entry 1 reaches the clients through linking
or does not reach them at all.
3. The oldest Jellyfin server the core must work against
Every additional server line is a matrix leg in every gate, a compatibility shim
where an endpoint changed shape, and a second set of fixtures for the fake server
the suite runs against. The number is a product decision rather than a technical
one, so it belongs here.
upgraded, which on self-hosted software is most of them for a while.
plugin's own description names 10.11 on .NET 9 and 12.0 on .NET 10, so the
organisation already pays for two lines somewhere and the fixtures could be
shared in spirit if not in code.
compatibility shims start to shape the interfaces the clients see, which is the
expensive kind of cost because it does not go away when the old line does.
4. Whether the public interface is frozen before the first client ships
The core has no user until a client exists, and freezing an interface that has
never been used is how a bad interface becomes permanent. Not freezing it means
every client that starts early pays for every change.
for the core, and the client that goes first absorbs the cost.
surface turns out to be the wrong one, and it will not be known which until a
client uses it.
boundary published. Costs a policy somebody enforces and a check that says
which tier a symbol is in.
5. The address a report reaches
Two documents on this board need a route to a person and neither can invent one.
#24 owes a finder a private route for a vulnerability. #107 owes somebody a route
for a conduct report, and a code of conduct with no address is a document that is
useless at the moment it is needed. The two routes may be the same or different.
mailbox, it authenticates the reporter, and it costs nothing to keep watched
because the notifications arrive where the work already happens. It covers a
vulnerability and nothing else, so a conduct report has no route, and a
reporter without an account has none either.
reads it, and it publishes no person's own address. It costs a mailbox somebody
has to actually watch, and an unwatched address is worse than none, because the
document promises a reply.
ties the project's public contact to one person permanently, since an address
published in a document stays in the history after it is changed.
conduct report are not the same problem and rarely want the same reader. It
doubles what has to be watched.
Whichever it is, both documents state what a reporter can expect and by when,
which is already #24's condition.
6. Where a release is published, and under what name
#94 builds the release artifacts and #91 decides what the first release
contains. Neither can say where the bytes go, because that needs an account
somebody owns, a name claimed before somebody else claims it, and a promise about
continuity, none of which the plan can invent. Entry 2 narrows the options, since
one answer produces a library per target triple and another produces a document
and a suite, but it does not make this choice.
attached. Nothing to register and nothing to own. Every client author fetches a
file and pins its checksum by hand, which is work they will do once and then
stop doing.
a manifest and gets updates the ordinary way. It costs an account that outlives
whoever made it, a name taken early, and a package that is close to impossible
to withdraw once anything depends on it.
the name inside the organisation, and makes every client author configure an
extra source, which is the step a client author skips.
while there is no consumer, and it means the first client is also the first
test of the publishing route, at the moment when something else is already
going wrong.
The answer also decides what #93's version scheme is a promise to. A number in a
public registry is a promise to strangers. A tag on this repository is a note to
whoever reads the repository.
Done when
Each entry has an answer written into this issue, and every issue
that names this one as its blocker has been updated to carry the answer it was
waiting for.