15 Commits
Author SHA1 Message Date
bbassieandClaude Opus 5.5 e6554e09c7 Add HostManager for host records and host tokens
controller.hosts is now a HostManager wrapping the hosts datastore, the
same way controller.users wraps the users datastore. It owns creating
hosts (moved from HostPairingManager), revoking them (moved from
ControlConnection) and signing and verifying host tokens (moved from
Controller, generateHostToken is renamed signHostToken to match
signUserToken).

UserManager gains verifyUserToken, replacing the copies of the user
token check in WsServer and routes. The WsServer copy accepted a token
without iat when the user had no tokenValidAfter; the shared check
always requires iat, which jsonwebtoken adds by default.

Follows Cooldude2606's comment on #1161.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 00:37:37 +00:00
bbassieandClaude Opus 5.5 01cf789f5d Add base instances for instance config inheritance
Instances can be marked as a base with instance.is_base and other
instances inherit config values from it through instance.base_id.
The controller copies base values into inheriting configs and keeps
track of inherited fields in the hidden instance.inherited_fields, so
hosts receive resolved configs and need no changes. Setting a field
stops inheriting it, `instance config inherit` undoes that.

Bases cannot be assigned to a host, are hidden from `instance list`
unless --bases is given, and cannot be deleted or unmarked while
other instances inherit from them. Chains of bases are allowed and
cycles are rejected.

Part of #200

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 11:54:21 +00: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 abb04f3258 Create FactorioServer and Instance through async factories
FactorioServer.fromOptions finds the Factorio install and generates the
RCON password before constructing, so _version and _dataDir are never
null and setTargetVersion and init are gone. checkForUpdates is now a
module function called before fromOptions.

Instance.fromConfig resolves the version, downloads Factorio if needed
and builds the server, then constructs the instance with it. It runs
before the instance is connected, so the starting and stopped statuses
and the version requests go through the host link. FactorioVersionsRequest
and LatestReleasesRequest now accept host as a source, and the host
tracks starting instances so a second start during a download is
rejected. Plugin loading stays a separate loadPlugins step after the
instance is connected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 00:11:22 +00:00
Hornwitser 5dcaca4d0b Migrate to ECMAScript Modules
Migrate the usage of CommonJS specific code and imports to ESM.  Roughly
summarised this:
- Changes all require() calls to import statement or expressions.
- Changes usage of module.exports to export statements.
- Replaces require.resolve with import.meta.resolve.
- Replaces conditional imports with conditional subpath imports.
- Replaces module === require.main with import.meta.main.
- Adds the full filename with extention to all relative imports.
- Changes all usage of __dirname and __filename to their import.meta
  equivalents.
- Sets "type": "module" in package.json files.
- Removes usage of "use strict".
- Adds .ts an an alias of .js for the webpack build.

Due to differences in how require and import resolves files the plugin
entrypoints will have to include the filename and extension after this
migration in order to work.
2026-09-11 22:43:57 +02:00
bbassieandClaude Opus 4.8 09c667252c Deliver broadcasts to all hosts, not just the local one
An event broadcast from an instance to all hosts only reached the host
running that instance. The host side of an instance connection has the
host's own address as its source, so a broadcast aimed at hosts counts as
addressed to it, and a link that a message is addressed to handles it and
stops. The event was delivered locally and never routed onwards.

A broadcast is addressed to every link of the type it targets, so arriving
at one of them does not mean it has arrived everywhere. Keep routing such a
message after handling it locally. Only links that route are affected, as
instances and controls have no router and would otherwise throw on every
broadcast they receive.

The only links whose source type matches a broadcast target and which route
are the host and its instance connections, both matching a broadcast to
hosts, so add the missing case for that to the host router. It forwards to
the controller, which already passes broadcasts on to every other host, and
stops when the message came from the controller to begin with.

Drop the exclusion this bug had in the routing test matrix, which now
covers it along with the plugin filtering variants.

Fixes #575

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 17:39:25 +00:00
Hornwitser fb3fe09466 Remove tlsCa option
The migration from phin to fetch in #755 was hampred by the lack of an
option to set the CA when using fetch.  This meant places that used phin
to talk to the controller with the tlsCa option could not be migrated to
use fetch.

Remove the tlsCa option to unblock this dependency. The replacement for
this option is to set the NODE_EXTRA_CA_CERTS environment variable to a
path to the certificate file instead, which works with fetch.
2026-03-04 17:43:32 +01:00
Cooldude2606andHornwitser 2235294c59 Refactor user and instance manager (#837)
* Refactor UserManager to use Datastore

* Rename lib.User to UserDetails also adds to/fromUserDetails

* Fix existing tests

* Add user class tests

* Add tests for finaliseUsers

* Move HostDetails to own file with tests

* Move InstanceDetails to its own file with tests

* Rename HostInfo and add tests

* Rename InstanceInfo and add tests

- Also changes the names of the host properties related to instances
- Also removes the need to specify the location of InstanceInfo

* Make json round trip test validate schema

* Add InstanceManager

* Fix lint errors

* Extra tests

* Add final test coverage

* Add one last test to InstanceManager

* Response to comments

* Use generator rather than return anonumous generator

Co-authored-by: Hornwitser <github@hornwitser.no>

* Fix lint issue with generator

---------

Co-authored-by: Hornwitser <github@hornwitser.no>
2026-03-01 23:04:37 +00:00
Cooldude2606 17db53403a Move filepath into config class 2025-05-12 21:30:48 +01:00
Cooldude2606 bdf02a5ea2 Made user ids case insensitive 2024-11-22 04:06:34 -08:00
Cooldude2606 609db31027 Add hosts, instances, systems 2024-05-22 16:39:22 +01:00
Cooldude2606 d1fa5e67d4 Improve error throwing for sending events 2024-05-18 18:42:53 +01:00
Cooldude2606 fb4a5e40f3 Add tests for loopbacks 2024-05-18 03:33:55 +01:00
Hornwitser 2a57ddb02e Add note about send and bcast test functions 2024-01-24 03:08:24 +01:00
Hornwitser 847945cfdd Fix plugin broadcasts not being filtered
When a plugin broadcasts an event to instances there may be hosts and
instances in the cluster which does not have the plugin loaded.  This
would cause an error to be logged for every instance which does not have
the plugin loaded.

Filter out broadcast events sent to hosts and instances such that
instances and hosts that does not have the event do not received them.
2024-01-24 01:56:01 +01:00