Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Changelog

One entry per release, newest first. An entry says what changed and what it
means for somebody moving from the release before it.

## What this board promises about compatibility

Nothing, and that is the useful answer rather than an evasive one. This is a
board for experiments that do not have to finish, the runner exists to read this
repository's own records, and
[CONTRIBUTING.md](CONTRIBUTING.md) says that nothing on another board is allowed
to depend on anything here. A version number implying guarantees I do not intend
to keep would be worse than no number at all.

What I do undertake is that a change is not made silently. That is the one thing
publishing an artefact creates that cannot be withdrawn afterwards, which
[docs/decisions/0021-what-this-board-publishes.md](docs/decisions/0021-what-this-board-publishes.md)
names as a cost of publishing at all: once somebody is running a version, they
have an expectation of continuity whether or not anybody offered them one. This
file is what that expectation is answered with.

The experiment record format may change. A change to it is decided in a record
under [docs/decisions/](docs/decisions/), and what happens to the records already
on the default branch on the day it changes is
[docs/decisions/0013-how-the-record-format-changes.md](docs/decisions/0013-how-the-record-format-changes.md),
which is where that was decided rather than restated here. A change that would
invalidate records already on the default branch is announced in the entry for
the release that carries it, under a heading naming it as such, and this file is
where somebody upgrading looks for that heading.

The exit codes the runner returns are the one part of it anything may key on,
and they are a contract in
[docs/decisions/0011-the-exit-codes.md](docs/decisions/0011-the-exit-codes.md).
The command interface around them is not a contract: a verb may be renamed and
the text a verb prints may be rewritten, and neither is announced anywhere except
here.

## v0.1.0 - 2026-09-04

The first release, so there is nothing to upgrade from and no behaviour that
changed. What this entry is for is saying what a downloaded file now is, since
until this tag the only way to run the runner was to build it out of the
repository it checks, which is a weaker position to check from.

**What is published.** A binary for each of the six platforms
[docs/decisions/0012-the-supported-platforms.md](docs/decisions/0012-the-supported-platforms.md)
fixes, with `NOTICE.md`, `LICENSE` and `privacy.md` beside them, plus the
third-party notices and the bill of materials rendered from the module table
inside a published binary. `SHA256SUMS` covers every one of those files and
`SHA256SUMS.sig` covers `SHA256SUMS`. The notes on the release carry the two
commands that check a download, and the signing keys they verify against are the
ones the platform publishes for the account rather than a key shipped in the
release.

**What the binary reports about itself.** `lab version` prints the version the
toolchain stamped from version control at build time rather than a constant
written into a source file, so the published binary reports the tag. Read off
the published asset rather than off a build made here, after checking its digest
against the line for it in `SHA256SUMS`:

sha256sum --check --ignore-missing SHA256SUMS
lab_v0.1.0_windows_amd64.exe: OK

./lab_v0.1.0_windows_amd64.exe version
lab v0.1.0
built from commit ed16621442c00a3ad47edd918d68edf68341753e, 2026-09-01T09:14:10Z

A build from an ordinary checkout reports a version derived from the commit
instead, a build from a tree carrying changes version control does not hold says
so on its own line, and the output explains which of the three it is handing you.

**Three things this release is not**, none of them new here. It is not something
anybody is asked to install, it is not advertised, and nothing on another board
depends on it, which is the scope
[CONTRIBUTING.md](CONTRIBUTING.md) opens with and which the release notes repeat
to whoever is holding the file rather than reading the repository. Who the
operator of an artefact is meant to be, and what publishing one costs, is
[docs/decisions/0021-what-this-board-publishes.md](docs/decisions/0021-what-this-board-publishes.md).

**What the archives do not contain.** There are no archives. The files travel as
separate assets beside the binaries, because an archive records a modification
time per entry and two runs from one tag would then produce two archives whose
bytes differ while the files inside them are identical, which would make the
reproducibility claim a claim about the archiver.
155 changes: 155 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Code of conduct

## Who this covers, and where

Everybody who takes part here: an issue, a pull request, a review, a commit
message, an experiment record, and a private message that started from one of
those. It covers me. I hold this repository and I am inside these rules rather
than above them, and if that sentence were not here the rest of this document
would be a set of rules for other people.

The door is open on purpose, which is
[docs/decisions/0024-who-may-run-an-experiment-here.md](docs/decisions/0024-who-may-run-an-experiment-here.md),
and this file is part of what that costs. A board anybody may propose an
experiment on owes anybody who shows up a written answer to what happens when
somebody behaves badly.

## What is expected

Argue with the work. This board already runs that way for evidence: a claim
carries the command that produced it, and a disagreement is settled by executing
something rather than by who said it. The same rule pointed at people means the
thing under discussion is a record, a measurement or a change, never the person
who wrote it.

Let an experiment fail. Everything here is public from the first commit, a
failed experiment included, and the whole arrangement depends on it being cheap
to write down an answer of no. [CONTRIBUTING.md](CONTRIBUTING.md) says that
nothing on this board treats a no as a lesser outcome, and that anybody who feels
a check pushing them towards a more flattering answer should report it as a
defect in the check. Treating somebody's failed experiment as a failure of theirs
is the behaviour that would make those sentences false, and it is the one this
board can least afford.

Keep measured and assumed apart in the sentence. Being wrong in public is the
ordinary cost of working this way, and a correction is a repair rather than a
verdict on whoever needed it. Somebody who writes down that they did not measure
something has done the harder thing.

Take a refusal with its reason. A change sent back here comes back with what was
wrong, and disagreeing with that reason is fair. Reopening it unchanged somewhere
else is not.

## What is not acceptable

- An attack on a person rather than on their work, including one dressed as a
question about their competence.
- Demeaning somebody for who they are, or for a group they belong to.
- Harassment, in the open or in messages that started here and continued
somewhere else.
- Publishing somebody's private details - a legal name, an address, an employer,
anything they did not put here themselves - without their consent.
- Unwanted attention of a sexual kind, and sexual material anywhere in this
repository or its tracker.
- Sustained disruption: a settled argument reopened without new evidence, a
thread derailed on purpose, a review answered with volume instead of
substance.
- Threatening any of the above, whether or not it is carried out.

That list holds what has to be named and it is a floor rather than a boundary.
Behaviour nobody wrote down is judged by the section above it and is not
permitted by its absence from this one.

One entry on that list is also a rule about the tree, and the overlap is worth
seeing rather than tripping over. Somebody else's personal data is something this
board may never hold, which is
[docs/decisions/0006-everything-here-is-public.md](docs/decisions/0006-everything-here-is-public.md),
and git history does not forget. So publishing it here is a conduct problem and
an exposure at once, and it is handled as both: what was published is removed and
said out loud rather than quietly reverted.

## Reporting

Two routes, in this order.

**A private report on this repository.** The form under Security is the only
private channel this repository has:

https://github.com/Flowfin/lab/security/advisories/new

It is labelled for a vulnerability, because that is what it was built for. A
conduct report sent through it is not misfiled: it reaches the same person, it
stays private, and submitting it publishes nothing. Whether the form answers at
all is a setting on the repository rather than a promise in a file, so it is read
rather than asserted:

gh api repos/Flowfin/lab/private-vulnerability-reporting
{"enabled":true}

Run 2026-09-05. That reading needs administrative access to this repository, so
it is not one a reader of this board can make. What a reader can do is open the
address above and see whether the form is there.

[SECURITY.md](SECURITY.md) names the same form for a vulnerability report. The
two documents point at one door on purpose: a second private channel would be a
second thing to keep alive, and a spare channel is unused right up to the moment
it is needed.

**A message through GitHub to the account that holds this repository**, which is
https://github.com/iderex. Use this where the first route does not fit - where
the report is about the form, or about me, or where you would rather it did not
sit in a security queue.

Do not open an issue about a conduct problem. An issue here is public from the
moment it is submitted, and it names the person it is about to everybody before
anybody has read it. That is the same reason this board has a security policy at
all, one file over.

No mailbox is published, here or anywhere else on this board. An address in a
document outlives whoever was reading it and stays in the history after it is
changed, and an address nobody watches is worse than none, because the document
promises a reply. Both routes above are accounts on a service that already
authenticates whoever sends the message, and neither needs anything kept alive to
go on working.

## What happens after a report arrives

I read it and I answer it. If something is missing I ask for it, and if I decide
it is not a problem you get the reason rather than silence.

There is no deadline and there will not be one, for the reason
[SECURITY.md](SECURITY.md) gives about the other route: a window this board
cannot hold is worse than none, because a reporter left past it cannot tell a
busy week from a report that never arrived.

What I can do is limited, and stating it exactly is better than implying more:
ask somebody to stop, edit or delete content on this repository, close or lock a
thread, block an account from this repository, and report an account to GitHub.
None of that reaches anybody outside this repository and none of it is a sanction
anywhere else.

Your name does not appear in whatever follows unless you ask for it to. Where
something has to be said in public it is said about the behaviour and the
outcome.

## Where this document is weak

**One person holds it.** A report about my own behaviour comes to me, and there
is no second reader, no appeal and no independent body. That is the honest state
of a board with one holder rather than an arrangement I am recommending, and it
is why the route that does not go through me is written here rather than left to
be found:

https://github.com/contact/report-abuse

**Nothing enforces this.** No check in this repository reads this file and none
could. Every leg of the run judges bytes in the tree, and how somebody spoke to
somebody else is not a byte in the tree. What stands behind this document is that
I do what it says, and the way to find out that I did not is to report it. A
document that looked enforced would be worse than this sentence.

**It is not the Contributor Covenant**, and that is deliberate. That document's
enforcement section describes a body of people, a ladder of consequences and a
review of appeals. None of those exists here, and publishing a ladder nobody
climbs would describe an apparatus this board does not have, which is the failure
the paragraph above is written against.
80 changes: 58 additions & 22 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,34 @@ Report privately, through the form under Security on this repository:

https://github.com/Flowfin/lab/security/advisories/new

The route is a repository setting rather than a file, and it does not answer
today:
That is the whole channel. A report sent anywhere else is not received: no
mailbox is published for this board, and there is no second private route to
fall back to. A report about somebody's behaviour rather than about the software
takes the same form, and
[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) is where that is written down.

The route is a repository setting rather than a file, and this paragraph said
until now that it does not answer. It answers:

gh api repos/Flowfin/lab/private-vulnerability-reporting
{"enabled":false}
{"enabled":true}

Run 2026-09-05, against the same repository as the earlier reading of the same
command, which returned `{"enabled":false}` on 2026-08-19 and is what the
paragraph here was written around. The address did not move when the setting
changed, which is why it was safe to publish while the door was shut.

Run 2026-08-19. So this file names the destination and says plainly that it is
shut, rather than sending a reporter to a door that does not open. The address
is the one the form uses once the setting is on, so nothing here moves when it
changes. Until then the honest alternative is a public issue carrying as little
detail as the report can carry: the software, the shape of the problem, and an
offer to send the rest privately. That is a poor arrangement and is written
down as one. Issue #10 is where this file lands and where the gap belongs.
WHAT THAT ENDS IS THE ALTERNATIVE THIS FILE USED TO OFFER. It said that until
the form opened, the honest fallback was a public issue carrying as little
detail as a report can carry. There is no such fallback now and there should not
be one: a public issue about a vulnerability is the outcome a security policy
exists to prevent, and it was written down here as a poor arrangement while it
was the only one. Do not open one. The form above is where a report goes.

With no policy in this repository, what a reader has been shown until now is
the organisation default, which says the private form is enabled on every
repository here and that a first answer arrives within a few days. Neither is
true of this repository today. This file is what governs this one.
The organisation default policy is still served on repositories in this
organisation that carry no policy of their own, and it says the private form is
enabled on every repository here. That is now true of this one. This file is
what governs this one, and where the two differ this one wins.

## What is here, because the description undersells it

Expand Down Expand Up @@ -62,12 +72,23 @@ somebody's licence is in scope.

A workflow that grants more than its job needs, or that can be made to run a
fork's code with a token. Nothing here is triggered by `pull_request_target` or
`workflow_run`, which I checked across all thirteen files in
`workflow_run`, which I checked across all fifteen files in
`.github/workflows` today, every workflow declares an empty or read-only scope
at the top level, `persist-credentials` is off at every checkout, and the
actions are pinned by commit. Anything that undoes one of those is worth
reporting before something uses it.

A published artefact that does not match what this board says it published.
There is a release now, and every file in it is covered by `SHA256SUMS` with
`SHA256SUMS.sig` over that file, verified against the signing keys the platform
publishes for the account that cut it rather than against a key shipped beside
the signature. The release notes carry the two commands. A downloaded file whose
digest does not match its line, a checksum file whose signature does not verify,
or a release asset that appeared or changed after the release was published, are
each a report I want. Which of those is a compromise and which is a mistake on my
side is not something a reader can tell from outside, and that is exactly why it
comes here rather than being assumed to be the second.

A break in a claim this repository publishes. It says the runner opens no
network connection and writes nothing to the tree it walks, and
`cmd/lab/network_test.go` holds the first of those. Making either sentence
Expand Down Expand Up @@ -136,10 +157,20 @@ There is no server, no socket, no account, no session, no database and no
stored personal data anywhere in this repository. The runner reads files and
prints what it found, so most of what a reader arrives with does not exist
here: nothing to log in to, nothing to escalate into, nothing to enumerate, and
no request path to inject anything into. Nothing is published as a binary
either. `gh api repos/Flowfin/lab/releases` and `gh api repos/Flowfin/lab/tags`
both answer with an empty list today, so there is no artefact anybody
downloaded and none to have been tampered with.
no request path to inject anything into.

WHAT THIS PARAGRAPH USED TO SAY NEXT WAS THAT NOTHING IS PUBLISHED AS A BINARY,
AND IT QUOTED TWO EMPTY LISTS FOR IT. Both answer with one entry now:

gh api repos/Flowfin/lab/releases --jq 'length'
1
gh api repos/Flowfin/lab/tags --jq 'length'
1

Run 2026-09-05. So there is an artefact somebody can have downloaded, and the
sentence saying there is none to have been tampered with has stopped being true.
What replaces it is the paragraph above about a published artefact, which is a
report I want rather than a class this file waves away.

A refusal you disagree with is not a vulnerability, and neither is a check that
is too strict about a legitimate tree, nor a supply-chain score lower than you
Expand All @@ -163,6 +194,11 @@ window, so stating one would be a promise that goes quietly wrong on the first
busy week, and a reporter told to expect an answer by a date who does not get
one is left guessing whether the report arrived at all.

What is covered is the default branch as it stands. There are no releases and
no tags, no version is maintained in parallel, and nothing to backport a fix
to: a fix is a commit on `main`, and whoever wants it takes a newer checkout.
What is covered is the default branch as it stands, and the most recent release.
This paragraph said there were no releases and no tags; there is one of each, and
the reading is in the section above. No version is maintained in parallel and
there is nothing to backport a fix to: a fix is a commit on `main`, the release
after it carries the fix, and whoever wants it takes a newer checkout or a newer
artefact. An older release is not patched in place and its assets are not
replaced, because replacing a file that a published checksum and signature
already cover is the shape a reader has no way to tell from tampering.
Loading
Loading