Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
922 commits
Select commit Hold shift + click to select a range
bbebe93
fix(sync): asked-ness belongs to the peer, so it goes when the peer does
MorningLightMountain713 Aug 31, 2026
fca4fcc
fix(ports): the second review's findings, and the exits that had no v…
MorningLightMountain713 Sep 1, 2026
783e011
fix(peers): the dial asks the owner what this node is, rather than be…
MorningLightMountain713 Sep 1, 2026
18a7054
fix(ports): a sibling ask is good for a window, and its answer for on…
MorningLightMountain713 Sep 1, 2026
f2128ba
fix(ci): a tag carrying the release version is not proof of a re-run
MorningLightMountain713 Sep 1, 2026
b57282a
Merge remote-tracking branch 'origin/development' into feat/release-gate
MorningLightMountain713 Sep 1, 2026
a9ed2f0
fix(ci): the gate refuses what it cannot verify, and says what fixes it
MorningLightMountain713 Sep 1, 2026
d64f5ec
docs(release): the release process, for whoever is cutting one
MorningLightMountain713 Sep 1, 2026
3ee1c82
fix(ci): the gate refuses what it cannot verify, and says what fixes it
MorningLightMountain713 Sep 1, 2026
a4fe016
Merge pull request #1788 from RunOnFlux/feat/release-gate
Cabecinha84 Sep 1, 2026
dd0e4b0
fix(ports): the port test's secret rides at the front of an answer we…
MorningLightMountain713 Sep 2, 2026
0a22634
docs(ports): the endpoint is described where it is enforced, and only…
MorningLightMountain713 Sep 2, 2026
d8e2fce
fix(ports): the sibling question answers or says nothing, whichever l…
MorningLightMountain713 Sep 2, 2026
36a4e9e
test(ports): the address that stands for "somewhere else" is one ever…
MorningLightMountain713 Sep 2, 2026
2e28d22
fix(keys): a node that cannot sign says so, instead of signing with a…
MorningLightMountain713 Sep 2, 2026
6ec0555
fix(api): one privilege on checkappavailability, and it is not one ev…
MorningLightMountain713 Sep 2, 2026
3ac2ce0
style(ports): the blank-line runs the second review reported fixed
MorningLightMountain713 Sep 2, 2026
04e2257
fix(ports): a sealed neighbour's ports are read, not counted as none
MorningLightMountain713 Sep 2, 2026
aea70ec
refactor(spawner): our own address is derived once, and the names say…
MorningLightMountain713 Sep 2, 2026
989d69d
refactor(placement): being named by the spec, said as that
MorningLightMountain713 Sep 2, 2026
8415ee0
docs(spawner): a stand-down and a throw cost the app the same, and th…
MorningLightMountain713 Sep 2, 2026
c053c1e
fix(keys): a node that cannot sign announces nothing, and records not…
MorningLightMountain713 Sep 2, 2026
d4abebe
fix(ports): the ports endpoint answers any listed Fluxnode, and refus…
MorningLightMountain713 Sep 2, 2026
f986518
fix(api): the UPnP keep-alive pokes the address that asked, under the…
MorningLightMountain713 Sep 2, 2026
d2c4611
test(keys): the keyless-node test stubs only the key it is about
MorningLightMountain713 Sep 2, 2026
8727abf
test(harness): the keep-alive is driven against a real node, at the a…
MorningLightMountain713 Sep 2, 2026
8e3ea91
test(harness): the keep-alive case names a node that answers, so the …
MorningLightMountain713 Sep 3, 2026
da7cd38
test(spawner): the stand-down is asserted where it is behaviour, not …
MorningLightMountain713 Sep 3, 2026
7dc0b1b
fix(ports): the port check answers with its decision, so the reason i…
MorningLightMountain713 Sep 3, 2026
84eb20a
test(spawner): the port check's refusal is exercised, and its stub an…
MorningLightMountain713 Sep 3, 2026
05280b2
Merge pull request #1795 from RunOnFlux/fix/same-ip-port-collision
Cabecinha84 Sep 3, 2026
1225604
feat(dos): a residential node not running ArcaneOS is put into DOS
Cabecinha84 Aug 13, 2026
cfc639d
fix(dos): the tampering enforcer releases only the sticky slot it sti…
Cabecinha84 Aug 13, 2026
ca4da31
feat(placement): the location table says which organisations run acce…
MorningLightMountain713 Aug 19, 2026
122906c
feat(geolocation): the network is classified on evidence, and a stati…
MorningLightMountain713 Aug 19, 2026
4b03e0b
feat(dos): a residential node holds, then evacuates, and only then go…
MorningLightMountain713 Aug 19, 2026
b60ac17
fix(apps): a node gives up an app only where the data provably lives …
MorningLightMountain713 Aug 19, 2026
f28fe23
test(harness): cover a residential node handing its apps back before DOS
MorningLightMountain713 Aug 19, 2026
c331b07
fix(geolocation): no location table means no verdict, and the verdict…
MorningLightMountain713 Aug 19, 2026
29fd3b3
test(harness): the stub publishes organisation classes, so the table …
MorningLightMountain713 Aug 19, 2026
2d0c767
test(harness): suite 55 reads the evidence the node persists, not the…
MorningLightMountain713 Aug 19, 2026
ae2d26a
test(harness): suite 55 lets the node learn about each block before t…
MorningLightMountain713 Aug 19, 2026
1e64a96
test(harness): suite 55 waits for what it asserts
MorningLightMountain713 Aug 19, 2026
f5d453f
docs(explorer): isSynced means "this block was the tip", not "the nod…
MorningLightMountain713 Aug 19, 2026
a6fa03e
test(harness): cover the stateful refusal and the pacing between depa…
MorningLightMountain713 Aug 19, 2026
6a60a61
test(harness): the give-up pass reports what it decided, and cover th…
MorningLightMountain713 Aug 19, 2026
9b726b1
test(harness): subscribe the runner to the give-up events it waits on
MorningLightMountain713 Aug 19, 2026
848e0e1
test(harness): let the stateful app reach full strength before enforcing
MorningLightMountain713 Aug 19, 2026
694237b
test(harness): drive the chain for the evacuation waits, and declare …
MorningLightMountain713 Aug 19, 2026
1e23976
test(harness): app ports are allocated, not hardcoded
MorningLightMountain713 Aug 19, 2026
dee584a
test(harness): say why nothing was placed, and cover the three untest…
MorningLightMountain713 Aug 19, 2026
8e72065
test(harness): put the node back in service before each test that see…
MorningLightMountain713 Aug 19, 2026
092d376
test(harness): the queue step must outlast a give-up pass, or orderin…
MorningLightMountain713 Aug 19, 2026
35a152f
test(harness): size the queue step to the property, not to production…
MorningLightMountain713 Aug 19, 2026
a514535
test(harness): drive the chain at a stated rate to a stated deadline
MorningLightMountain713 Aug 19, 2026
b23e934
feat(explorer): the poll interval is configurable, and the harness co…
MorningLightMountain713 Aug 19, 2026
d5dcd40
test(harness): open both settling windows together, or position decid…
MorningLightMountain713 Aug 19, 2026
ea2eed5
test(harness): propagation < pass interval < queue step, and all thre…
MorningLightMountain713 Aug 19, 2026
591162b
fix(apps): the queue ticket outlasts the pass that reads it
MorningLightMountain713 Aug 20, 2026
f3a37d7
fix(apps): the record-expiry keys the code stopped reading
MorningLightMountain713 Aug 20, 2026
207bd94
fix(apps): an election that cannot answer is not an election that sai…
MorningLightMountain713 Aug 20, 2026
fc34a77
fix(apps): the table decides what kind of network a node is on, or no…
MorningLightMountain713 Aug 20, 2026
254123b
fix(apps): the settling window counts the verdict watched, not the ti…
MorningLightMountain713 Aug 20, 2026
767b021
fix(apps): an address is static until this node sees it move
MorningLightMountain713 Aug 20, 2026
1a86a89
fix(apps): a watched address change is not overruled by its neighbours
MorningLightMountain713 Aug 20, 2026
1d80a52
fix(placement): an unknown class code costs that verdict, not the who…
MorningLightMountain713 Aug 20, 2026
87a03a5
fix(apps): a refusal that will not stop is a stuck node, and now says so
MorningLightMountain713 Aug 20, 2026
bd3de60
fix(dos): a deferred tampering DOS claims the slot when it comes free
MorningLightMountain713 Aug 20, 2026
8f4184a
fix(apps): the bounded correctness set
MorningLightMountain713 Aug 20, 2026
ee5a7f2
test: five tests that did not test what they claimed
MorningLightMountain713 Aug 20, 2026
ad47a92
test(harness): the poll is paced against the window that decides the tip
MorningLightMountain713 Aug 20, 2026
e05138b
docs: three claims in the prose that the code no longer supports
MorningLightMountain713 Aug 20, 2026
f19bdbb
fix(geolocation): the boot pass decides from a record it never read
MorningLightMountain713 Aug 20, 2026
b0a3359
test(harness): a tick that decides nothing to do has to say so
MorningLightMountain713 Aug 20, 2026
3c32b88
test(harness): a compressed harness is a set of ratios, so derive them
MorningLightMountain713 Aug 20, 2026
4e05940
fix(dos): the decision event carries the verdict, not just the boolean
MorningLightMountain713 Aug 20, 2026
3171bf4
docs(harness): record the coupled pair that is now checked at fleet boot
MorningLightMountain713 Aug 20, 2026
73eedd0
test(harness): suite 71 stamps its broadcasts when it sends them
MorningLightMountain713 Aug 20, 2026
99f427c
fix(apps): the clean-shutdown grace is a config value, and a boot is …
MorningLightMountain713 Aug 20, 2026
5332dd7
test(harness): preseed CloudUI so a legacy fleet does not download it…
MorningLightMountain713 Aug 20, 2026
64d59dc
test(harness): order the DOS against the removal on one stream, not t…
MorningLightMountain713 Aug 20, 2026
52b51f6
test(harness): await both events before ordering them
MorningLightMountain713 Aug 20, 2026
5daa148
Revert "test(harness): preseed CloudUI so a legacy fleet does not dow…
MorningLightMountain713 Aug 20, 2026
8dd4e3d
fix(geolocation): a static address is one this node can see on its ow…
MorningLightMountain713 Aug 21, 2026
34358e9
perf(iplocation): the cached table comes back with the schema, not af…
MorningLightMountain713 Aug 21, 2026
5ea1a76
fix(evacuation): the elected primary stops writing rather than refusi…
MorningLightMountain713 Aug 21, 2026
ba8730e
fix(evacuation): a stand-down tells the controller, not just docker
MorningLightMountain713 Aug 21, 2026
e268c14
fix(evacuation): the junior copy leaves first, so the primary is not …
MorningLightMountain713 Aug 23, 2026
3448ed9
fix(geolocation): a datacentre verdict rests on hosting evidence, not…
MorningLightMountain713 Aug 23, 2026
dfeac67
docs(apps): three comments that named things the code no longer does
MorningLightMountain713 Aug 23, 2026
6e242c1
test(harness): suite 55 reads the container list at the right level, …
MorningLightMountain713 Aug 23, 2026
bf528f5
test(harness): the FDM-outage test reaches its state by order, becaus…
MorningLightMountain713 Aug 23, 2026
bee2652
fix(network): a public address counts wherever it sits on the interface
MorningLightMountain713 Aug 24, 2026
01da6cd
fix(geolocation): a restored record agrees with itself about static
MorningLightMountain713 Aug 24, 2026
2c18a83
fix(residential): the queue ticket is an uninterrupted observation, n…
MorningLightMountain713 Aug 25, 2026
d23029c
fix(apps): a surplus copy that is writing stays, and the next copy trims
MorningLightMountain713 Aug 25, 2026
7d1dbca
feat(dos): the node says which stage of being staged out it is in
MorningLightMountain713 Aug 25, 2026
3667484
docs: the internal architecture repository is not referenced from shi…
MorningLightMountain713 Aug 25, 2026
feb8296
test(harness): suite 96 builds its shape from the seed rule, not from…
MorningLightMountain713 Aug 25, 2026
887ea9f
fix(residential): the ticket tolerates a late pass, and the knobs tha…
MorningLightMountain713 Aug 25, 2026
b1a040b
test(harness): suite 55 drives blocks at the rate the node consumes them
MorningLightMountain713 Aug 25, 2026
6e93770
test(harness): suite 55 halves its pass period, which halves every de…
MorningLightMountain713 Aug 25, 2026
af0313d
test(harness): suite 96 clears the cold start per holder, not through…
MorningLightMountain713 Aug 25, 2026
0cc179b
test(harness): a stub that fails to speak says so, and a holder wait …
MorningLightMountain713 Aug 25, 2026
7172290
test(harness): a stub peer waits for an audience before it says anything
MorningLightMountain713 Aug 25, 2026
9505403
test(harness): a control call that fails says which one and why
MorningLightMountain713 Aug 25, 2026
98aa6b4
test(harness): suite 96 declares both cold-start keys per holder
MorningLightMountain713 Aug 25, 2026
9086461
fix(apps): a declined surplus is reported as a decision, not as silence
MorningLightMountain713 Aug 25, 2026
075e47d
test(harness): suite 96's second test actually exercises the rule
MorningLightMountain713 Aug 25, 2026
bc2416a
test(harness): one driveUntil, in the framework
MorningLightMountain713 Aug 25, 2026
ab2ff60
test(harness): a poll that could not be taken is not a poll that failed
MorningLightMountain713 Aug 25, 2026
b1baf52
test(harness): suite 13 waits for the node to notice before asking wh…
MorningLightMountain713 Aug 25, 2026
66178a3
fix(apps): a 2xx that is not a success body is not FDM answering
MorningLightMountain713 Aug 31, 2026
ba6f017
fix(evacuation): the newest copy holding the writer still reaches the…
MorningLightMountain713 Aug 31, 2026
a2984d7
docs(residential): say which kind of empty node reports HOLD
MorningLightMountain713 Aug 31, 2026
56a00b6
fix(placement): a placement hold is released by its owner, and only b…
MorningLightMountain713 Aug 31, 2026
aa5b649
test(harness): the FDM outage is restored on every path out of the test
MorningLightMountain713 Aug 31, 2026
973ae8a
test(harness): every seeded component takes an allocated port, not a …
MorningLightMountain713 Aug 31, 2026
5226a63
test(harness): suite 71 seeds inside the window its own broadcasts li…
MorningLightMountain713 Aug 31, 2026
68baa28
test(harness): suite 71 declares the acceptance window its own setup …
MorningLightMountain713 Aug 31, 2026
e9f1998
test(harness): a pending node can be refused before it starts, and su…
MorningLightMountain713 Aug 31, 2026
a574c1a
fix(spawner): a count that included a claim is not remembered for twe…
MorningLightMountain713 Aug 31, 2026
edc0ae2
test(harness): a g: placement waits for the holder to register before…
MorningLightMountain713 Aug 31, 2026
45a4781
fix(spawner): only the claimed half of the count is forgotten, not th…
MorningLightMountain713 Aug 31, 2026
9d1c1dd
test(harness): the peer's claim is restated without ever being droppe…
MorningLightMountain713 Aug 31, 2026
c791f12
test(harness): the bare-mountpoint probe stops racing the repair it p…
MorningLightMountain713 Aug 31, 2026
ee8b245
fix(harness): each seeded syncthing app gets a port of its own
MorningLightMountain713 Aug 31, 2026
35a7016
docs(harness): the seeded-port counter names the one that replaces it
MorningLightMountain713 Aug 31, 2026
b843fa6
test(harness): this branch's port allocator gives way to the one in t…
MorningLightMountain713 Aug 31, 2026
7fad650
test(harness): teardown reports which step spent the budget
MorningLightMountain713 Aug 31, 2026
22e06b8
test(harness): suite 55 waits for the published table its verdict needs
MorningLightMountain713 Aug 31, 2026
3983f97
fix(harness): the allocator removal takes the call sites the rebase r…
MorningLightMountain713 Aug 31, 2026
708c35b
fix(harness): the seeded-port counter goes, as its own docstring inst…
MorningLightMountain713 Aug 31, 2026
2a834dc
fix(apps): findSyncedPeer reads the config the way the config is retu…
MorningLightMountain713 Aug 31, 2026
d68a6bd
test(harness): suite 96 heals the partition it made, and waits on the…
MorningLightMountain713 Aug 31, 2026
9cb5975
test(harness): teardown stops the fleet concurrently, which is where …
MorningLightMountain713 Aug 31, 2026
b8b3869
test(harness): the stand-down budget is a departure per app held, not…
MorningLightMountain713 Aug 31, 2026
93ee0af
test(harness): the stand-down tests get a fleet of their own
MorningLightMountain713 Aug 31, 2026
cd139f2
test(harness): a node's static IP is declared, not inherited from the…
MorningLightMountain713 Aug 31, 2026
3be5dd5
test(harness): suite 89 waits for the app to be stoppable before repl…
MorningLightMountain713 Aug 31, 2026
a3875ec
test(harness): the one-holder-at-a-time tests get a fleet of their ow…
MorningLightMountain713 Aug 31, 2026
8c043fb
test(harness): the hold-out is asserted in both directions, and can b…
MorningLightMountain713 Aug 31, 2026
7644a5b
fix(spawner): the pass over an app is a fact when it changes, a tally…
MorningLightMountain713 Aug 31, 2026
f5d61c0
test(harness): anchor the candidacy wait, or it answers from before t…
MorningLightMountain713 Aug 31, 2026
a2e5a0c
test(harness): the db client is closed while the server it talks to i…
MorningLightMountain713 Sep 1, 2026
bdb5eab
test(harness): the capture sidecars are killed by PID, not by their s…
MorningLightMountain713 Sep 1, 2026
e80631f
fix(harness): a deferred node is a hole in the thresholds too, not ju…
MorningLightMountain713 Sep 1, 2026
02559ca
fix(id): a failed privilege check answers none, so the stale session …
MorningLightMountain713 Sep 1, 2026
75e9b36
fix(geolocation): the refresh loop is one owned chain, not one per ca…
MorningLightMountain713 Sep 1, 2026
7476ea2
test(harness): suite 96 drives no block while the peer its decision r…
MorningLightMountain713 Sep 1, 2026
9a12057
fix(evacuation): a stand-down is marked on the stop's verdict, not on…
MorningLightMountain713 Sep 1, 2026
6c070c0
test(spawner): the placement hold is asserted as behaviour, and the e…
MorningLightMountain713 Sep 3, 2026
88079c0
fix(residential): the policy tick answers with what it concluded, so …
MorningLightMountain713 Sep 3, 2026
cf2ffa3
Merge pull request #1784 from RunOnFlux/feat/dos-residential-non-arcane
Cabecinha84 Sep 3, 2026
9c4fbd8
fix(appsync): the pool of outstanding state-sync requests is topped u…
MorningLightMountain713 Sep 3, 2026
3cfb9c1
fix(peers): a node that is not accepting connections refuses the upgr…
MorningLightMountain713 Sep 3, 2026
91c3791
test(budgets): the tests that do real work are timed against what rea…
MorningLightMountain713 Sep 3, 2026
bf789e3
fix(appsync): a state sync completes on three peers rather than three…
MorningLightMountain713 Sep 3, 2026
cd75621
fix(appsync): a node that has not caught up declines a sync request i…
MorningLightMountain713 Sep 3, 2026
457ca9a
fix(appsync): each outstanding sync request is a slot with its own de…
MorningLightMountain713 Sep 3, 2026
cf1e32a
fix(appsync): a peer that can refuse is asked whatever its uptime, an…
MorningLightMountain713 Sep 3, 2026
6018265
feat(auth): the support team is a list of Flux IDs, not one
Cabecinha84 Sep 3, 2026
73c89d9
feat(auth): the support team is the four Flux IDs it is held by
Cabecinha84 Sep 3, 2026
da53344
feat(apps): a support fork names the whole team, not one address
Cabecinha84 Sep 3, 2026
2bf6a89
test(harness): the chain starts above the fork this PR adds
Cabecinha84 Sep 3, 2026
726d1c2
fix(appsync): one record per outstanding sync request, and one thing …
MorningLightMountain713 Sep 4, 2026
25a4da1
test(appsync): the production requirement is measured against a fleet…
MorningLightMountain713 Sep 4, 2026
7593777
test(appsync): the cooldown case is driven at a configured throttle, …
MorningLightMountain713 Sep 4, 2026
c14a3e2
fix(peers): the socket a refused upgrade is answered on is one this s…
MorningLightMountain713 Sep 4, 2026
000572a
fix(appsync): a node that has judged its own peer set unreliable stop…
MorningLightMountain713 Sep 4, 2026
ae66953
test(sync): the fleet a joiner syncs from is one that can answer it
MorningLightMountain713 Sep 4, 2026
17c1e8b
test(harness): a fleet boots into a network that is already up, unles…
MorningLightMountain713 Sep 4, 2026
c333566
test(appsync): the production-requirement fleet is big enough for the…
MorningLightMountain713 Sep 4, 2026
181360a
test(appsync): the app-state sync suite takes a two-digit number like…
MorningLightMountain713 Sep 4, 2026
567cf84
test(harness): which nodes a stub is a peer of is declared, and made …
MorningLightMountain713 Sep 4, 2026
4ba9654
test(sync): the stubs a hash is resolved from ephemerally are nobody'…
MorningLightMountain713 Sep 4, 2026
7295a31
fix(appsync): a peer is credited with answering when its answer arriv…
MorningLightMountain713 Sep 4, 2026
6f06d2d
test(harness): a holder is placed once its stamp exists, not once its…
MorningLightMountain713 Sep 4, 2026
2d2ca85
fix(appsync): the sync budget bounds the attempt, and a peer already …
MorningLightMountain713 Sep 4, 2026
9793734
fix(appsync): one fallback for every node, because it is a measuremen…
MorningLightMountain713 Sep 4, 2026
6443bad
docs(appsync): two comments describe what the code does rather than w…
MorningLightMountain713 Sep 4, 2026
09e4e28
fix(sync): a node answers the temp stream on the same terms as the re…
MorningLightMountain713 Sep 5, 2026
d48f2b4
fix(sync): a request is opened only once its signature exists, and a …
MorningLightMountain713 Sep 5, 2026
16efb6d
test(harness): each suite establishes the premise it asserts rather t…
MorningLightMountain713 Sep 5, 2026
77da64e
fix(syncthing): a folder that is not promoted records which gate it l…
MorningLightMountain713 Sep 5, 2026
7c9e971
test(harness): a g-app placement orders the stamps it is asked to order
MorningLightMountain713 Sep 5, 2026
a0572fe
fix(appsync): a peer is credited when it has finished, and a connecti…
MorningLightMountain713 Sep 6, 2026
f1e2e30
Merge pull request #1797 from RunOnFlux/fix/app-state-sync-liveness
Cabecinha84 Sep 6, 2026
25ac660
fix(docker): a missing container is an error the caller can read, and…
Cabecinha84 Aug 30, 2026
91550df
fix(apps): a soft redeploy that never removed the app must not uninst…
Cabecinha84 Aug 30, 2026
7254414
fix(apps): a component redeploy must not uninstall the app, and repor…
Cabecinha84 Aug 31, 2026
bdc9c7d
fix(docker): a log poll must settle and release the docker connection…
Cabecinha84 Aug 31, 2026
828f9ca
fix(docker): a failed log poll reports on both channels and resolves …
MorningLightMountain713 Sep 3, 2026
1b31705
fix(apps): a container FluxOS removed itself is not evidence of tampe…
MorningLightMountain713 Sep 3, 2026
c906402
test(apps): the component redeploy argument contract is pinned, and t…
MorningLightMountain713 Sep 3, 2026
a02043a
test(apps): a component redeploy is exercised end to end, and reports…
MorningLightMountain713 Sep 3, 2026
3f06d1b
fix(apps): a component redeploy answers through the stream it opened,…
MorningLightMountain713 Sep 3, 2026
ecae08c
test(apps): a component redeploy is judged on the envelope it ends with
MorningLightMountain713 Sep 3, 2026
994aa37
test(apps): drop the redeploy assertion that could not fail
MorningLightMountain713 Sep 3, 2026
98e5685
test(apps): a hard component redeploy wipes that component and only t…
MorningLightMountain713 Sep 3, 2026
8d8872d
fix(apps): the endpoint that opened the response is the only thing th…
MorningLightMountain713 Sep 4, 2026
9e6a789
fix(apps): a component redeploy reports only what it actually reinsta…
MorningLightMountain713 Sep 4, 2026
c6ff877
fix(apps): the automatic component redeploy takes the bare app name too
MorningLightMountain713 Sep 4, 2026
291b850
fix(docker): an app's logs keep a floor of history instead of none
MorningLightMountain713 Sep 4, 2026
2b475ec
refactor(apps): delete the log stream endpoint no route could reach
MorningLightMountain713 Sep 4, 2026
c6e589e
feat(apps): a log poll answers when docker does, and from where the r…
MorningLightMountain713 Sep 4, 2026
58bb5a4
test(apps): a log poll loses nothing between polls, on a real fleet
MorningLightMountain713 Sep 4, 2026
7141a63
docs(device): drop the changelog for a package that is gone
MorningLightMountain713 Sep 4, 2026
586f83c
perf(docker): find a container by asking docker about it, not by list…
MorningLightMountain713 Sep 4, 2026
c721c79
fix(apps): an install says which of three things it did, and the endp…
MorningLightMountain713 Sep 4, 2026
9056284
fix(docker): a log position is exact, cheap, and only for a reader th…
MorningLightMountain713 Sep 4, 2026
73da3c3
fix(apps): a failed soft install finishes its teardown before the end…
MorningLightMountain713 Sep 5, 2026
90fff26
feat(apps): a log answer says what is ahead of a cursor separately fr…
MorningLightMountain713 Sep 5, 2026
cc7f57f
test(harness): an app's specification can be changed, not only create…
MorningLightMountain713 Sep 5, 2026
9da5d45
test(unit): the suite stops hand-writing globalState and uses the mod…
MorningLightMountain713 Sep 5, 2026
9226052
fix(apps): anything that can start work asks whether the node is alre…
MorningLightMountain713 Sep 5, 2026
fede52b
fix(apps): a log position is answered from a bounded read, or the rea…
MorningLightMountain713 Sep 5, 2026
4f03f51
feat(apps): an app's logs are pushed as the container writes them
MorningLightMountain713 Sep 5, 2026
8d7feed
docs(reconciler): the removal note is dropped by a successful recreat…
MorningLightMountain713 Sep 5, 2026
c69d8d0
fix(apps): an exec that cannot start answers its caller instead of ex…
MorningLightMountain713 Sep 5, 2026
102d018
fix(applogs): one docker stream per container, claimed before the dae…
MorningLightMountain713 Sep 6, 2026
a47e2c6
fix(applogs): a viewer that unsubscribes leaves the room it was watching
MorningLightMountain713 Sep 6, 2026
3aca7ce
fix(apps): a page of long lines that fitted is not an overflow
MorningLightMountain713 Sep 6, 2026
ead9e3b
fix(apps): an install releases the node on every path out of it
MorningLightMountain713 Sep 6, 2026
2699b39
perf(apps): a log read releases the event loop while it decodes
MorningLightMountain713 Sep 6, 2026
8f86664
test(harness): the spec-update reinstall suite is 85
MorningLightMountain713 Sep 6, 2026
f4bdef8
test(harness): a wait for a block-driven pass is budgeted in blocks
MorningLightMountain713 Sep 6, 2026
fcdde5c
docs(test): the comment names the event add() emits
MorningLightMountain713 Sep 6, 2026
2facd3b
fix(applogs): the connection's one subscription is claimed when the s…
MorningLightMountain713 Sep 6, 2026
f5db3c2
fix(redeploy): one component's failed teardown no longer uninstalls t…
MorningLightMountain713 Sep 6, 2026
94377c3
fix(applogs): a batch of lines is appended, not spread into an argume…
MorningLightMountain713 Sep 6, 2026
5b18eb5
fix(applogs): a feed that ends releases the connections that were hol…
MorningLightMountain713 Sep 6, 2026
404bd2d
fix(images): an image tag parses in time that does not grow with its …
MorningLightMountain713 Sep 6, 2026
ccff2d0
fix(apps): the announcement period stops absorbing the work it announces
MorningLightMountain713 Sep 6, 2026
029c13b
fix(applogs): a connection follows the containers it asks for, each h…
MorningLightMountain713 Sep 7, 2026
7c651f8
fix(apps): the announce interval comes from the row it refreshes, and…
MorningLightMountain713 Sep 7, 2026
38766b4
fix(controller): a stopped loop stays stopped, and a stop waits for w…
MorningLightMountain713 Sep 7, 2026
2e0ed92
fix(applogs): a subscription ends with the feed it named, and the lim…
MorningLightMountain713 Sep 7, 2026
3e5b4aa
fix(applogs): a positioned read carries the overlap it is measured ag…
MorningLightMountain713 Sep 7, 2026
b1788fa
docs(apps): a refused reinstall does not leave the app as it was
MorningLightMountain713 Sep 7, 2026
566e9de
fix(docker): a caller's position cannot size the read past what the l…
MorningLightMountain713 Sep 7, 2026
fad9d1e
fix(controller): a runner that throws reaches the process that restar…
MorningLightMountain713 Sep 7, 2026
e4718f8
fix(applogs): a line no reader can hold is cut, and a viewer is told …
MorningLightMountain713 Sep 7, 2026
a4877cc
test(harness): a log pane is complete, not in the container's order
MorningLightMountain713 Sep 7, 2026
cfb784a
Merge pull request #1794 from RunOnFlux/fix/container-not-found-crash…
Cabecinha84 Sep 7, 2026
0c9fc4b
feat(apps): the support fork falls a week ahead, not at the tip
Cabecinha84 Sep 7, 2026
5473d92
Merge pull request #1796 from RunOnFlux/feat/support-team-fluxid-list
Cabecinha84 Sep 7, 2026
13e92bf
Merge branch 'master' into development
Cabecinha84 Sep 7, 2026
0b7d1a3
chore(release): bump version to 8.18.0 and carry the release fingerprint
Cabecinha84 Sep 7, 2026
17c30a9
Merge branch 'master' into development
Cabecinha84 Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@
/ZelApps/
/docs/
/dev/
# ESM, and these rules are written for the CommonJS backend: an ESM import must
# carry its .js extension, which import/extensions is configured to forbid. It
# needs a config of its own rather than this one, so it is out of scope here
# instead of silently wrong.
/test-infra/
5 changes: 4 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module.exports = {
extends: [
'eslint:recommended',
],
plugins: [],
plugins: ['import'],
rules: {
'max-len': [
'error',
Expand All @@ -25,6 +25,9 @@ module.exports = {
ignoreTrailingComments: true,
},
],
// Two spaces, and enforced: the repo carried no indent rule at all, so a
// mis-indented block read as intentional to every reader and to CI alike.
indent: ['error', 2, { SwitchCase: 1 }],
'no-console': 'off',
'linebreak-style': [
'error',
Expand Down
12 changes: 9 additions & 3 deletions .github/workflows/integration-harness.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ jobs:
echo "leftover harness containers: $(docker ps -aq --filter label=flux-e2e-run | wc -l)"

- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: 20

Expand All @@ -77,6 +77,12 @@ jobs:

- name: Build harness images
run: |
# The fixtures' app binary. It is gitignored and checkout runs
# git clean, so it is NEVER present here - eight suites push it, and
# suite 63 needs it built from THIS branch's test-app.c (a cached one
# has no BURN_CPU, so the container idles and 63's throttle wait dies
# at 150s looking like a broken throttler rather than a missing file).
bash test-infra/test-app/build.sh
docker compose -f test-infra/docker-compose.yml -p flux-e2e build \
fluxos-01 daemon-stub syncthing-stub fdm-stub peer-stub external-http-stub
docker pull mongo:8
Expand Down Expand Up @@ -113,7 +119,7 @@ jobs:

- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: e2e-logs
path: ${{ runner.temp }}/e2e-logs
Expand Down
150 changes: 105 additions & 45 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,71 @@ name: Node CI

on: [push]

# Cross-repo pushes and dispatches use per-run GitHub App tokens minted below; the run's own token
# needs read only.
permissions:
contents: read

jobs:
# Publication is a request, not a write: the fluxhashes signer fetches this commit itself,
# derives the tree hash from the bytes it fetched, and publishes list, signed document and
# provenance in one commit. The credential can start, cancel and re-run workflows on fluxhashes
# and delete their logs; it cannot write to the repository, and it sends pointers rather than hash
# values. That bounds what the credential does, but it does not make the list unreachable through
# it: a fork network shares one object store, so a dispatched commit may be any commit ever pushed
# to this repository or to a public fork of it, and the signer derives what it is pointed at. The
# credential is held by whoever can land a workflow change on any branch here, who can already get
# a hash listed by pushing -- the same trust boundary, not a defence against it.
# NEW_HASH rides along as a tripwire the signer checks against its own computation -- a mismatch
# is a red signing run, never a listed value. Forks publish nothing.
#
# Its own job, deliberately: the hash describes a checkout nothing else has touched, and a
# publication failure (fluxhashes unavailable, token expired) stays a red publish job instead of
# blocking the test suite.
publish:
runs-on: ubuntu-22.04
if: github.repository == 'RunOnFlux/flux'
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- name: Check Hash
run: |
set -eo pipefail
newhash=$(find ./ZelBack -type f -exec md5sum {} + | awk '{print $1}' | LC_ALL=C sort | md5sum | awk '{printf $1}')
# d41d8... is the md5 of an empty stream, which the pipeline yields whenever nothing was
# hashed: ZelBack absent (pipefail catches that) or present holding no regular files
# (find exits 0 and emits nothing, which pipefail cannot see). It is a well-formed hash
# meaning "a node whose ZelBack holds no files is genuine FluxOS". The signer refuses to
# list it, but a claim it cannot match aborts the signing run rather than this one --
# so fail here, in the repository that produced it.
if [ "$newhash" = d41d8cd98f00b204e9800998ecf8427e ]; then
echo 'nothing was hashed -- this checkout has no ZelBack files'
exit 1
fi
echo $newhash
echo NEW_HASH=$newhash >> $GITHUB_ENV
# The credential is a GitHub App scoped to fluxhashes with Actions permission only: the
# workflow mints a short-lived installation token per run, so there is no long-lived token
# anywhere and nothing to renew.
- name: Mint the dispatch token
id: dispatch-token
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.FLUXHASHES_APP_ID }}
private-key: ${{ secrets.FLUXHASHES_APP_KEY }}
owner: RunOnFlux
repositories: fluxhashes
- name: Request hash publication
env:
GH_TOKEN: ${{ steps.dispatch-token.outputs.token }}
run: |
gh api -X POST repos/RunOnFlux/fluxhashes/actions/workflows/sign-hashlist.yml/dispatches \
-f ref=master \
-f "inputs[commit]=${GITHUB_SHA}" \
-f "inputs[ref]=${GITHUB_REF_NAME}" \
-f "inputs[ref_type]=${GITHUB_REF_TYPE}" \
-f "inputs[claimed_hash]=${NEW_HASH}"

build:
runs-on: ${{ matrix.os }}

Expand All @@ -19,44 +83,11 @@ jobs:
- 27017:27017

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v7
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node-version }}
- name: Check Hash
run: |
newhash=$(find ./ZelBack -type f -exec md5sum {} + | awk '{print $1}' | LC_ALL=C sort | md5sum | awk '{printf $1}')
echo $newhash
echo NEW_HASH=$newhash >> $GITHUB_ENV
- name: Get current hashes
run: |
mkdir hashes
wget 'https://raw.githubusercontent.com/RunOnFlux/fluxhashes/master/src/hashes/hashes.js' -P hashes
hashfile=`cat hashes/hashes.js | sed "s/return/_/gi" | sed "s/\n/_/gi"`
echo HASH_FILE=$hashfile >> $GITHUB_ENV
- name: Show hashes
run: |
echo $HASH_FILE
echo $NEW_HASH
- name: Patch hashes
if: ${{ !contains(env.HASH_FILE, env.NEW_HASH) }}
run: |
newhash=$(find ./ZelBack -type f -exec md5sum {} + | awk '{print $1}' | LC_ALL=C sort | md5sum | awk '{printf $1}')
sed -i "s/ ];/ '$newhash',\n ];/gi" hashes/hashes.js
tail -n 200 hashes/hashes.js
- name: Push hashes to fluxhashes
if: ${{ !contains(env.HASH_FILE, env.NEW_HASH) }}
uses: cpina/github-action-push-to-another-repository@main
env:
API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }}
with:
source-directory: "hashes"
destination-github-username: "RunOnFlux"
destination-repository-name: "fluxhashes"
user-email: runonfluxbot@gmail.com
target-branch: master
target-directory: src/hashes/
- name: install flux and flux benchmark daemons
run: |
echo 'deb https://apt.runonflux.io/ '$(lsb_release -cs)' main' | sudo tee /etc/apt/sources.list.d/flux.list
Expand Down Expand Up @@ -93,14 +124,43 @@ jobs:
CI: true
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
- name: Push docs to other repo # Push services directory from RunOnFlux/flux to RunOnFlux/fluxjsdocs repo to build JSDocs separately.
uses: cpina/github-action-push-to-another-repository@main
env:
API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }}
# Only development publishes the docs. Without the ref condition every branch build races for
# fluxjsdocs master, so the published JSDocs are whichever branch happened to build last --
# an unmerged feature branch as often as not.
- name: Mint the docs token
if: github.repository == 'RunOnFlux/flux' && github.ref == 'refs/heads/development'
id: docs-token
uses: actions/create-github-app-token@v3
with:
source-directory: "ZelBack/src/services"
destination-github-username: "RunOnFlux"
destination-repository-name: "fluxjsdocs"
user-email: runonfluxbot@gmail.com
target-branch: master
target-directory: services/
app-id: ${{ secrets.FLUXJSDOCS_APP_ID }}
private-key: ${{ secrets.FLUXJSDOCS_APP_KEY }}
owner: RunOnFlux
repositories: fluxjsdocs
# Git directly, rather than a third-party action. The action this replaces was referenced by
# a personal repository's default branch, so every build ran whatever happened to be on it at
# that moment -- and was handed a token that can write to fluxjsdocs. Nothing outside Flux
# touches the credential now. fluxjsdocs is public, so the clone is anonymous and the token
# authenticates only the push, which also keeps it out of the clone's stored config.
- name: Push docs to fluxjsdocs # Publishes ZelBack/src/services so JSDocs builds separately.
if: github.repository == 'RunOnFlux/flux' && github.ref == 'refs/heads/development'
env:
GH_TOKEN: ${{ steps.docs-token.outputs.token }}
run: |
set -eo pipefail
CLONE=$(mktemp -d)
git clone --quiet --depth 1 --single-branch --branch master \
https://github.com/RunOnFlux/fluxjsdocs.git "$CLONE"
# Replace rather than merge, so a file deleted here disappears there too.
rm -rf "$CLONE/services"
mkdir -p "$CLONE/services"
cp -a ZelBack/src/services/. "$CLONE/services/"
git -C "$CLONE" config user.email runonfluxbot@gmail.com
git -C "$CLONE" config user.name runonfluxbot
git -C "$CLONE" add -A services
if git -C "$CLONE" diff --cached --quiet; then
echo 'docs unchanged, nothing to push'
exit 0
fi
git -C "$CLONE" commit --quiet -m "Update from https://github.com/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}"
git -C "$CLONE" push --quiet \
"https://x-access-token:${GH_TOKEN}@github.com/RunOnFlux/fluxjsdocs.git" master
161 changes: 161 additions & 0 deletions .github/workflows/release-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
name: release-gate

# Required status check on release PRs (development -> master). Verifies, before a release can
# merge, that what master will become is already approved: the version moves forward, the tree's
# hash is in the signed document central serves, and the old-fluxbench fallback list carries it.
#
# This check is the gate the human sees; release-tag.yml re-verifies the merged tree before
# cutting the tag and GitHub Release. Enforcement comes from branch protection listing this check
# as required -- the workflow itself has no power to block a merge.
#
# Deliberately strict: central not serving a validly signed document is a red run, not a skip. A
# gate that passes when its source is missing is not a gate.
#
# Read-only: no secrets, no pushes.
#
# Tests: tests/ci/release-workflows.sh -- runs offline, no secrets. It extracts steps from this
# file verbatim, so renaming one breaks it loudly rather than silently skipping it.

on:
pull_request:
branches: [master]

permissions:
contents: read

concurrency:
group: release-gate-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
gate:
runs-on: ubuntu-22.04
steps:
# The default pull_request checkout is the merge preview -- the tree master will actually
# hold after the merge -- which is the thing worth judging, not the PR head alone.
- uses: actions/checkout@v7

- name: Version moves forward
run: |
git fetch --quiet --depth 1 origin master
BASE=$(git show origin/master:package.json | jq -r .version)
CANDIDATE=$(jq -r .version package.json)
echo "master: ${BASE} candidate: ${CANDIDATE}"
# Strictly newer, compared numerically per part. String inequality would call 8.17.10
# older than 8.17.9.
#
# The shape is checked before anything is compared. Splitting on dots and comparing
# whatever falls out lets a version the parser cannot read be judged by whichever part
# happens to differ: "8.18" has no third part and passes, "8.18.0-rc1" passes on its
# minor while "8.17.2-rc1" is refused on a NaN. Refuse what cannot be read instead.
node -e '
const parse = (label, value) => {
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec(value);
if (!parts) {
throw new Error(`the ${label} version is not three numeric parts: ${value}`);
}
return parts.slice(1).map(Number);
};
const base = parse("master", process.argv[1]);
const head = parse("candidate", process.argv[2]);
const delta = head[0] - base[0] || head[1] - base[1] || head[2] - base[2];
if (!(delta > 0)) {
throw new Error("the candidate version does not move past master -- bump package.json in the release PR");
}
' "$BASE" "$CANDIDATE"

# The tree judged below is the merge preview, which is a ref nowhere. Its hash is in the
# signed document only because some branch tip already holds the same ZelBack subtree --
# normally this PR's head, because master carries no code of its own: every commit landing
# directly on master has been version metadata or operational data under helpers/, outside
# the hashed tree.
#
# When master DOES carry ZelBack changes the head lacks, the merged tree exists on no branch,
# no signing run can ever cover it, and the check below fails telling the reader to wait for
# a signing run that will never come. Refuse here instead, and name the thing that fixes it.
#
# Deliberately compares the ZelBack subtree, not the whole tree: master diverging under
# helpers/ is routine and cannot change the hash.
- name: The merged ZelBack is a tree the signer can have seen
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
set -eo pipefail
git fetch --quiet --depth 1 origin "refs/pull/${PR_NUMBER}/head"
PREVIEW=$(git rev-parse 'HEAD:ZelBack')
HEAD_TREE=$(git rev-parse 'FETCH_HEAD:ZelBack')
if [ "$PREVIEW" != "$HEAD_TREE" ]; then
echo "the merge preview's ZelBack is ${PREVIEW}, this PR head's is ${HEAD_TREE}."
echo "master carries ZelBack changes that are not on ${GITHUB_HEAD_REF}, so the merged"
echo "tree exists on no branch and no signing run can ever cover it."
echo "fix: merge master into ${GITHUB_HEAD_REF} and re-run -- its tip will then carry the"
echo "merged tree and the signer will pick it up. Waiting will not help."
exit 1
fi
echo "the merged ZelBack is this PR head's ZelBack (${PREVIEW})"

- name: Tree hash is in the signed document central serves
run: |
set -eo pipefail
NEW_HASH=$(find ./ZelBack -type f -exec md5sum {} + | awk '{print $1}' | LC_ALL=C sort | md5sum | awk '{printf $1}')
# d41d8... is the md5 of an empty stream, produced whenever nothing was hashed: ZelBack
# absent (pipefail catches that) or present holding no regular files (find exits 0 and
# emits nothing, which pipefail cannot see). The signer refuses to list that value, so
# the membership check below would fail anyway -- but it would fail as "not in the signed
# document", sending the reader to the signing chain instead of the missing directory.
if [ "$NEW_HASH" = d41d8cd98f00b204e9800998ecf8427e ]; then
echo 'nothing was hashed -- this checkout has no ZelBack files'
exit 1
fi
echo "tree hash: ${NEW_HASH}"
# exported for the node script below; GITHUB_ENV only reaches subsequent steps
export NEW_HASH
echo "NEW_HASH=${NEW_HASH}" >> "$GITHUB_ENV"

curl -sS -m 30 -o hashlist-signed.json https://hashes.runonflux.io/hashlist

# Verify the signature the way a consumer does, against the published public keys
# (RunOnFlux/fluxhashes SIGNING.md), so a stale deploy answering every path with the
# unsigned array -- or anything else that is not the signed document -- is a red run.
#
# Single-quoted, with the hash read from the environment: inside a double-quoted shell
# string the ${...} below would be the shell's, not node's.
node -e '
const crypto = require("crypto");
const fs = require("fs");
const PINNED_PUBLIC_KEYS = [
"14837066068b258bfbd0749702056f7065361af44aed48761834744391cbbaaa",
"fee7b0ccf2323954af68a249eaa61f957239eb222329e08a5b6a50ced649bae8",
];
const SPKI_ED25519_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
const document = JSON.parse(fs.readFileSync("hashlist-signed.json", "utf8"));
const payload = Buffer.from(document.payload_b64, "base64");
const signature = Buffer.from(document.sig_b64, "base64");
const verified = PINNED_PUBLIC_KEYS.some((hex) => crypto.verify(
null,
payload,
crypto.createPublicKey({
key: Buffer.concat([SPKI_ED25519_PREFIX, Buffer.from(hex, "hex")]),
format: "der",
type: "spki",
}),
signature,
));
if (!verified) {
throw new Error("central is not serving a validly signed hash list");
}
const { seq, hashes } = JSON.parse(payload.toString("utf8"));
if (!hashes.includes(process.env.NEW_HASH)) {
throw new Error(`the tree hash is not in the signed document (seq ${seq}) -- wait for the signing run to cover the release candidate`);
}
console.log(`signed document seq ${seq} carries the tree hash`);
'

# Old fluxbench falls back to helpers/hashes.json via GitHub raw when central is unreachable.
# The entry rides the release PR instead of a commit straight to master. This step retires
# with old fluxbench.
- name: Fallback list carries the hash
run: |
jq -e --arg h "$NEW_HASH" 'index($h) != null' helpers/hashes.json > /dev/null \
|| { echo "helpers/hashes.json does not contain ${NEW_HASH} -- add it in the release PR"; exit 1; }
echo "helpers/hashes.json carries the tree hash"
Loading
Loading