Commit Graph
3482 Commits
Author SHA1 Message Date
Danielv123 e5701a8dae Approve standalone hosts like any other host
A standalone install no longer adds its host to the controller database
by signing a token and writing hosts.json itself. The host asks to join
when it first starts and is approved once with the link it prints.
2026-10-30 23:15:22 +01:00
Danielv123 a4894aae9d Simplify approving hosts
The list of waiting hosts opens the approval dialog instead of having its
own name input and buttons, so hosts are approved in one place. Removes
the limit on requests per minute, the limits on pending requests already
cap what an address can do.

Also updates text that still said the installer makes the requests.
2026-10-30 22:59:32 +01:00
Danielv123 95422e6d21 Merge remote-tracking branch 'origin/master' into host-pairing
# Conflicts:
#	packages/host/host.ts
2026-10-30 21:50:38 +01:00
Danielv123 1ed46dff9c Fix issues found in review of host pairing
- Saves of a datastore run one at a time, and changes made while saving
  are saved by the next save. Overlapping saves from approving and
  revoking hosts could write an older snapshot after a newer one.
- Map subscribers fetch everything again when a new session starts, as
  the controller only sends values that exist. A host revoked while a
  browser was disconnected stayed in its list.
- The host token check accepts the same pre-rename tokens as the
  WebSocket, so upgrading a host with such a token does not make it ask
  to join again.
2026-10-30 21:49:53 +01:00
Cooldude2606andClaude Opus 5.5 9b654b1ad8 Start the controller and host CLIs faster (#1159)
* Answer the disconnect prepare in the close old connections test

The test's first connector had no Link, so the controller's disconnect
prepare went unanswered and it waited the full 10 s timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Start the controller and host CLIs faster

Like ctl, the controller and host now compile schema validators on first
use instead of at import, which cost about 120 ms per invocation. The
Controller and Host classes are only imported by the commands that
start them, so config, bootstrap and the other commands no longer load
express, ws and the rest of the servers. clusteriocontroller config set
goes from 413 ms to 231 ms and clusteriohost config set from 412 ms to
225 ms.

The integration tests run the bins, like npx does, instead of the
package directories.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 20:43:33 +00:00
Bastiaan e7c21fb907 Merge pull request #1150 from bbassie/fix/node-engine-range
Require Node.js 22.18 or 24.2 for import.meta.main
2026-10-30 20:41:37 +00:00
Danielv123 2cf7c66187 Merge remote-tracking branch 'origin/master' into host-pairing
# Conflicts:
#	test/integration/clusterio.js
2026-10-30 21:41:06 +01:00
Cooldude2606 cc8888fc69 Merge pull request #1160 from bbassie/ci/split-tests
Split each CI test job into two halves
2026-10-30 20:40:52 +00:00
bbassieandClaude Opus 5.5 b9d7033724 Test old Node.js versions only with the latest Factorio
Splitting the test jobs doubled them to 14, over GitHub's concurrent
job limit together with the other jobs. Every Factorio version now runs
on the latest Node.js only, and the other Node.js versions only with
the latest Factorio, which is 10 test jobs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 20:31:57 +00:00
bbassieandClaude Opus 5.5 75cdfbad8d Split the test jobs in two halves
The clusterioctl, plugin system and instance integration tests take
about half of the test time, so they run on one runner and the rest of
the tests on another, each half with its own controller and host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 20:31:32 +00:00
Cooldude2606andClaude Opus 5.5 3707fc35fc Drop unused dependencies from web_ui and tests (#1163)
* Take FieldData from antd instead of rc-field-form

web_ui depended on rc-field-form 1.x for a single type import, while
antd 5 uses rc-field-form 2.x. Derive the type from antd's FormProps
and drop the dependency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Move typescript to web_ui devDependencies

web_ui only needs typescript to build its own types. The runtime
dependency is a leftover from webpack's ts-loader, Vite doesn't use it,
and it pulls in a 30 MB native compiler for anyone installing web_ui.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use native FormData in controller route tests

Node's FormData and Blob do what the form-data package did here, and
fetch sets the multipart Content-Type header itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Remove leftover typedoc.json files

TypeDoc was dropped in #1147, but these configs still ship in the
published packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 20:29:12 +00:00
BastiaanandClaude Opus 5.5 0f7c8be060 Use only the jimp modules the host export needs (#1162)
jimp pulls in every image format and plugin, about 60 packages and
26 MB that only export.ts uses. Build the Jimp class from @jimp/core
with the PNG format and the crop and resize plugins instead. The
generated spritesheet is byte-identical to the one from jimp.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 20:24:07 +00:00
Danielv123 13c75b9e35 Link to an approval dialog from the waiting host
A host waiting for approval logs a link to the Hosts page with the code
in it. Opening it shows a dialog with the code, the address of the host,
a name prefilled with the requested one and a large Approve button. A
code that is no longer waiting shows a message saying so.
2026-10-30 21:23:03 +01:00
Danielv123 4a49a198f0 Pair hosts when they start instead of in the installer
The installer only writes the controller URL and host name. Running the
host requests to join the cluster if it has not been approved yet, or if
the controller no longer accepts its token, and waits until an admin
approves it. Once approved it saves its id and token and starts as
normal. A rejected host exits with code 8.

clusteriohost run --user-token approves the host right away with the
token of a user allowed to approve hosts. The token is not saved.

This moves the pairing client into the host package, where it uses the
same certificate setup as the host's connection to the controller, and
removes --no-pair and the rerun handling from the installer.
2026-10-30 21:13:14 +01:00
Cooldude2606 53a4fce934 Merge pull request #1151 from bbassie/fix/stop-while-starting
Fix stopping an instance while it is starting
2026-10-30 20:04:22 +00:00
Bastiaan ed2057e4a2 Merge pull request #1149 from bbassie/feature/compat-ignore-control
Ignore control in the version compat message check
2026-10-30 19:57:48 +00:00
bbassieandClaude Opus 5.5 8738c74d7f Remove leftover typedoc.json files
TypeDoc was dropped in #1147, but these configs still ship in the
published packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 18:44:54 +00:00
bbassieandClaude Opus 5.5 c9db2fb29e Use native FormData in controller route tests
Node's FormData and Blob do what the form-data package did here, and
fetch sets the multipart Content-Type header itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 18:44:54 +00:00
bbassieandClaude Opus 5.5 41e3f50dbe Move typescript to web_ui devDependencies
web_ui only needs typescript to build its own types. The runtime
dependency is a leftover from webpack's ts-loader, Vite doesn't use it,
and it pulls in a 30 MB native compiler for anyone installing web_ui.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 18:44:54 +00:00
bbassieandClaude Opus 5.5 5df375d490 Take FieldData from antd instead of rc-field-form
web_ui depended on rc-field-form 1.x for a single type import, while
antd 5 uses rc-field-form 2.x. Derive the type from antd's FormProps
and drop the dependency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 18:44:54 +00:00
bbassieandClaude Opus 5.5 0312a3b099 Start the controller and host CLIs faster
Like ctl, the controller and host now compile schema validators on first
use instead of at import, which cost about 120 ms per invocation. The
Controller and Host classes are only imported by the commands that
start them, so config, bootstrap and the other commands no longer load
express, ws and the rest of the servers. clusteriocontroller config set
goes from 413 ms to 231 ms and clusteriohost config set from 412 ms to
225 ms.

The integration tests run the bins, like npx does, instead of the
package directories.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 17:29:42 +00:00
bbassieandClaude Opus 5.5 a174c355d2 Answer the disconnect prepare in the close old connections test
The test's first connector had no Link, so the controller's disconnect
prepare went unanswered and it waited the full 10 s timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 17:19:36 +00:00
Daniel 8a3aa56c11 Merge pull request #1157 from bbassie/refactor/plugin-state
Replace the PluginDetails booleans with a single state
2026-10-30 17:52:51 +01:00
Daniel c13b1fa78f Merge pull request #1155 from bbassie/fix/research-sync-session-loss
Resync research_sync after a new controller session
2026-10-30 17:50:58 +01:00
Daniel 31e132d4fd Merge pull request #1158 from bbassie/ci/setup-speed
Speed up CI setup and the coverage upload
2026-10-30 17:44:48 +01:00
Danielv123 37e79d9c41 Remove tests that pin implementation details
Drops tests that restate message declarations, check that removed
commands are gone, test internal helpers or repeat the same behaviour
at another layer.
2026-10-30 10:11:14 +01:00
Danielv123 87d4f5c64e Replace host tokens with host pairing
Hosts join the cluster by running the installer, which shows a code and
waits until the request is approved under Hosts > Add host in the web
interface or with clusterioctl host approve. An admin can pass their
user token to the installer to approve the host right away. The token
is only used for that and is not saved.

The controller only accepts hosts that have a record, and only
approving a host creates one. Revoking a host deletes its record, so
its token can no longer be used to reconnect. Hosts that have connected
before keep working.

Removes host generate-token, revoke-token and create-config from
clusterioctl, bootstrap generate-host-token, the host token messages and
the core.host.generate_token, revoke_token and create_config
permissions, which are also stripped from stored roles. Adds
clusterioctl host pending, approve, reject and revoke, and the
core.host.approve and core.host.revoke permissions.

Standalone installs add the host to the controller database directly,
since the controller is not running during the install.
2026-10-30 06:11:32 +01:00
bbassieandClaude Opus 5.5 b989815829 Speed up CI setup and coverage upload
pnpm/action-setup installs a bootstrap pnpm and then switches to the
packageManager version, about 10 s per job; npm installs it in 3 s.
setup-node took 4 s probing package managers for a cache it never
used. The pnpm store cache took 4 s to restore and 5 s to save, more
than downloading the packages costs, and the build cache no longer pays
off with TypeScript 7: pnpm install is 13-23 s without either cache
against 17-31 s with them.

The Codecov action searched the workspace and uploaded the raw V8
coverage in coverage/tmp next to lcov.info, 72 MB taking 11 s.

The version compatibility job ran tsc --build again after pnpm install
had already built everything.

Cancel superseded runs of branches and pull requests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 00:26:28 +00:00
bbassieandClaude Opus 5.5 814a4a201a Replace the PluginDetails booleans with a single state
loaded, enabled, pendingInstall, removed, noEntrypoint and nameTaken
allowed far more combinations than can happen, and every consumer had
to know which took priority. PluginDetails now has one state, with
loaded, loadsOnRestart and removed as getters over it.

2.0.3 web UIs and ctl send PluginListRequest straight to hosts and
require loaded and enabled, so those are still sent next to the
optional state. Plugins listed by 2.0.3 hosts get their state from
loaded and enabled.

The web UI planner gets the same treatment: MachinePlugin has one
state in place of disabled, noEntrypoint, dependencyTaken and
nameTaken. ctl host plugin list shows the state.

Closes #1134

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 00:05:23 +00:00
bbassieandClaude Opus 5.5 5589609b1b Resync research after a new controller session
While the host has no controller session, events between the instance
and the controller are dropped in both directions, and research_sync
only synced technologies on instance start. A technology finished during
the outage stayed unresearched on the controller and on every other
instance until the instance restarted.

On a new session the instance now runs the same technology sync as on
start, before taking the cluster queue. Contributions made while the
controller is unreachable are summed per technology and sent on connect
or resume, since progress from several instances can't be rebuilt from a
state sync that keeps the highest progress. A finish made while the
connection was dropped triggers the sync on resume too.

sync_technologies now runs while the game is running, so it skips
writing progress to a technology that finished after the dump.

Fixes #1123

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 00:04:15 +00:00
bbassieandClaude Opus 5.5 666dd48412 Show the instance control button as loading while starting or stopping
The button offered Stop while the instance was starting whenever the
start came from somewhere else, such as Load save, another tab or ctl.
It now shows the action in progress as loading until the status
settles. Kill stays available from the dropdown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 22:52:09 +00:00
bbassieandClaude Opus 5.5 eb1e2e2a5e Wait for a start in progress before stopping an instance
A stop request sent while the instance was starting ran alongside the
start. During save preparation it did nothing and the instance came up
running anyway. Once Factorio was launched it put the server into the
stopping state, the start then failed with "Expected state running but
state is stopping" and its cleanup killed the server mid shutdown.

Stop and restart requests now wait for any start, stop or save in
progress to finish first, then stop the instance if it is running.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 22:52:09 +00:00
Cooldude2606 fa2883d74e Merge pull request #1147 from bbassie/build/typescript-7
Drop TypeDoc and move to TypeScript 7
2026-10-29 17:55:01 +00:00
bbassieandClaude Opus 5.5 3079426712 List node in the types of the node tsconfigs
Since TypeScript 6 the types option defaults to an empty list, so
@types/node was only loaded because winston and undici-types reference
it. Name it explicitly. The emitted files do not change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 01:11:00 +00:00
bbassieandClaude Opus 5.5 7d421847f5 Drop the self-referencing constructor type on ModPortalGetAllResponse
TypeScript 7.1 reports TS5088 for it: the inferred type of the static
Response property references itself through the declared constructor.
The other inline Response classes do not declare it either.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 01:11:00 +00:00
bbassieandClaude Opus 5.5 885de50625 Build with TypeScript 7
TypeScript 7 is the native port of the compiler. The repository builds
with it unchanged: no new type errors, the emitted JavaScript differs
only in the order of react/jsx-runtime imports, and the declarations
only in union and property order, which TypeScript 5.9 consumers read
the same way. The plugin template moves to it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 01:11:00 +00:00
bbassieandClaude Opus 5.5 c724f144e7 Drop TypeDoc
The generated API docs were never published or built in CI, and
typedoc's peer range stops at TypeScript 6.0. TSDoc comments stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 01:11:00 +00:00
Cooldude2606 96dddb1526 Merge pull request #1146 from bbassie/feature/compat-breaks
Allow known breaks in the version compatibility test
2026-10-29 00:51:37 +00:00
bbassieandClaude Opus 5.5 3c2ee3e372 Require an impact for each known compat break
Entries now explain why end users are not or only minimally affected,
and the docs say which version to key by and point to the breaking
changes decision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:45:27 +00:00
bbassieandClaude Opus 5.5 9bd6effd40 Allow known breaks in the version compatibility test
The version-compat job fails every pull request once master breaks
compatibility with the latest release on purpose. List those breaks in
scripts/version-compat-breaks.json keyed by the published version they
break against, each with a pr number and reason. Listed messages warn
instead of failing, connect skips the cluster test, entries for other
versions are ignored, and a listed message that did not change fails.

Add the two src changes from #1129 against 2.0.3 so master passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:27:37 +00:00
Cooldude2606andClaude Opus 5.5 a82af49c05 Keep auto paused instances paused (#1143)
* Keep auto paused instances paused

Every RCON command makes an auto paused Factorio server run one tick, so
subspace_storage's 1 s inventory update and 5 s ping, plus a
statistics_exporter gather on every Prometheus scrape, kept a server with
nobody online running at about 1.3 ticks per second.

The instance now records the auto_pause server setting it was started
with and exposes autoPaused, true while that setting is on and no players
are online, along with an autoPauseChanged hook. subspace_storage skips
its timers while paused and sends a ping and inventory as soon as a player
joins. statistics_exporter gathers once after the game pauses and keeps
serving those values until it resumes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Check connection and status in subspace_storage timers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:05:32 +00:00
bbassieandClaude Opus 5.5 1eea756ab3 Check connection and status in subspace_storage timers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 23:58:13 +00:00
Cooldude2606 2fb5300ec5 Merge pull request #1140 from bbassie/fix/remove-web-plugin-column
Remove the web UI status column from the plugin pages
2026-10-28 23:54:46 +00:00
bbassieandClaude Opus 5.5 11e067a933 Keep auto paused instances paused
Every RCON command makes an auto paused Factorio server run one tick, so
subspace_storage's 1 s inventory update and 5 s ping, plus a
statistics_exporter gather on every Prometheus scrape, kept a server with
nobody online running at about 1.3 ticks per second.

The instance now records the auto_pause server setting it was started
with and exposes autoPaused, true while that setting is on and no players
are online, along with an autoPauseChanged hook. subspace_storage skips
its timers while paused and sends a ping and inventory as soon as a player
joins. statistics_exporter gathers once after the game pauses and keeps
serving those values until it resumes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 23:40:30 +00:00
bbassieandClaude Opus 5.5 9aa232e2cd Refer to the web build in the plugin load error
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 23:35:35 +00:00
bbassieandClaude Opus 5.5 82b7c16dba Remove the web UI status column from the plugin pages
Users don't need the web UI load state apart from the plugin's own state now
that the web UI reloads itself. A web load error now shows under the plugin's
versions and on its view page, only for enabled plugins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 23:35:35 +00:00
Cooldude2606 ebdbb1fe56 Merge pull request #1141 from bbassie/test/plugin-integration
Finish the plugin system integration tests
2026-10-28 23:35:32 +00:00
bbassieandClaude Opus 5.5 6f70ccbf85 Add a test plugin without a module for the host tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 23:29:50 +00:00
Cooldude2606 188de979b6 Merge pull request #1130 from bbassie/build/vite-plus
Draft: Replace webpack with Vite and load plugins as ES modules (vote)
2026-10-28 23:17:45 +00:00
bbassieandClaude Opus 5.5 9ab36151bd Finish the plugin system integration tests
Adds the last three checks from #342 to test_plugin: an InstanceEcho
request handled on a running instance, a Lua module with a remote
interface that the request calls over RCON, and log tagging checks for
the plugin field and host/instance ids on the controller, host and
instance log lines.

test_plugin now has a module, so the create_scenario and patch unit
tests that relied on it having none use global_chat instead.

Closes #342

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 22:01:27 +00:00