Skip to content

Decisions only I can make #1

Description

@iderex

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.

  • Releases on this repository only, with checksums and the attestations from Produce a software bill of materials and a build provenance attestation for every artifact #87
    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.
  • 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.

Metadata

Metadata

Assignees

Labels

blocked-on-decisionWaits on an entry of #1 that is not answered yetplanningquestionFurther information is requested

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions