Skip to content

fix(prepare): name the local plugin that lacks provenance metadata - #59

Merged
viktor-ferenczi merged 2 commits into
mainfrom
fix/prepare-managed-missing-metadata
Sep 21, 2026
Merged

viktor-ferenczi merged 2 commits into
mainfrom
fix/prepare-managed-missing-metadata

Conversation

@viktor-ferenczi

@viktor-ferenczi viktor-ferenczi commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

-prepareManaged looked for <dll>.xml / <name>.xml next to a LocalPlugin and passed the path straight to RefuseLink, which calls File.GetAttributes. For a local plugin without metadata this raised an unhandled FileNotFoundException (reported from Quasar guided cluster setup with a UI-plugin companion DLL in Local/).

The path is now checked first and the existing InvalidDataException("Local plugin needs GitHubPlugin provenance metadata: <id>") is thrown. The message names the plugin ID and both metadata paths that were tried (<name>.xml and <name>.dll.xml). The sibling error for a metadata file that exists but is not a GitHubPlugin now names that file too.

Testing: dotnet build Legacy/Legacy.csproj -c Release succeeds; no unit test covers ManagedPreparation yet, so this is compile-checked only.

Quasar side (leaves such plugins out of the cluster profile before preparation) is CometWorks/quasar#163. Local ticket: SE1-0043.

-prepareManaged looked for <dll>.xml / <name>.xml next to a local plugin
and passed the path to RefuseLink, which reads file attributes. For a
plugin without metadata that raised an unhandled FileNotFoundException
instead of the InvalidDataException that names the plugin.

SE1-0043
Name where the metadata was expected (both accepted file names) when it
is missing, and the file that was read when it is not a GitHubPlugin.

SE1-0043
viktor-ferenczi added a commit to CometWorks/quasar that referenced this pull request Sep 21, 2026
Fixes for the review of #137 at `3c07ea8`. One commit per finding; the
local ticket number (SE1-00xx) is in each commit body. Multi-Host
behaviour is out of scope.

## Fixed

**Guided setup could never produce a working cluster**
- Executor tokens are named by Host ID; the `host-` prefix made every
poll fail with `executor_host_mismatch`. A roster already written with
the prefix is rewritten in place (SE1-0016).
- The cluster spec emits administrators as SteamID64 numbers and rejects
`MaxPlayers < 2` before setup binds; the Gateway aborted at startup on
strings (SE1-0017).
- Local plugins without provenance metadata (UI-plugin companion DLLs)
are left out of the cluster profile and named, instead of crashing
`-prepareManaged` (SE1-0043, Quasar side; Magnetar side is
CometWorks/magnetar#59).
- The failed-setup error is shown once (SE1-0044).

**Quasar.Host stays up and recovers**
- Survives Gateway HTTP timeouts, per-cluster reconcile faults and a
faulted Quasar tunnel (SE1-0022).
- A reused PID is no longer a permanent unmanaged conflict: launch
records carry boot ID + start ticks, final-state records are not
inspected (SE1-0021).
- A spawn cancelled by the lease budget no longer leaves a permanent
`Launching` record; a started process whose identity cannot be committed
is stopped and recorded as Failed (SE1-0023).
- Invalid attachment / Gateway spec files are ignored; an unrecoverable
activation or restore pauses only its cluster instead of exit code 2 on
every start (SE1-0037).
- Failed Gateway respawns back off 5 s … 5 min (SE1-0031, partial).

**Quasar cluster management cannot be wedged by one bad file or one busy
cluster**
- An unreadable operation record is quarantined as `.corrupt` and
logged; the store recovers after a transient write failure; atomic
writes are fsynced; store failures answer with the JSON envelope
(SE1-0020).
- Local operations are closed on every failure path and failed as
`interrupted_by_restart` at startup, so they no longer block cluster
deletion; unhandled `InvalidOperationException` on cluster routes is a
JSON 409 (SE1-0026).
- The reconciler, pending-operation and update loops skip a cluster
whose lifecycle gate is busy (SE1-0027).
- Unacknowledged Gateway requests expire after 10 minutes and can be
withdrawn with `DELETE …/operations/{operationId}` (SE1-0035).
- A torn `cluster-credentials.json` or `host.json` no longer aborts
startup or breaks tunnel authentication for other Hosts (SE1-0037).
- Stuck update, partial (SE1-0019): failed lifecycle shutdown is retried
under a new key, Stopping/Starting record `LastError` when overdue, and
`POST …/update/abandon` plus an **Abandon update** button unlock the
lifecycle tools (refused while Hosts are half-activated).
- Scheduled cluster backups, partial (SE1-0034): failures are logged and
retried, one cluster or one corrupt `backup.json` no longer breaks the
rest, and the panel says they need a stopped cluster.

**Smaller**
- `server.json` `goalState` is numeric again so a downgrade still sees
standalone servers; the cluster API keeps names (SE1-0036).
- `QUASAR_HOST_BINARY` lets Host enrollment work from a development
build; documented (SE1-0041).
- Predefined DS worlds are listed once when a Steam and a managed DS
install both exist (SE1-0042).
- Removed committed `.playwright-mcp/` artifacts (SE1-0038).

**Found by the live re-test**
- Guided setup did not treat `InvalidDataException` as a failure: the
status stayed on its last phase and the exception ended the operator's
Blazor circuit (SE1-0045).
- The web worker ignored SIGTERM while a Host was connected: the Host
tunnel WebSockets held Kestrel's graceful shutdown until the 30 minute
`ShutdownTimeout`. They now end on `ApplicationStopping` (SE1-0039).

**Found by running a managed cluster with a headless client**
- Guided setup sent Magnetar's game version as `binaryVersion`; the
Registry compares the node's `Sandbox.Game.dll` assembly version, so
every node was rejected and the cluster never left Bootstrapping
(SE1-0049).
- A managed Gateway is Steam-only. `QUASAR_CLUSTER_TEST_DIRECT_LISTEN` /
`QUASAR_CLUSTER_TEST_DISABLE_STEAM` (test only, private addresses only)
add the Direct Transport frontend and drop Steam in the generated
specification; needs CometWorks/cluster#18 (SE1-0048).
- The Steam frontend needs Valve's `steamclient.so` under the Host
account and nothing provided it: guided setup and conversion now send
the copy from Quasar's managed SteamCMD to the Gateway Host, which
stages it with a checksum and an atomic rename (SE1-0048).

**Testing without GitHub releases** (SE1-0046)
- `QUASAR_CLUSTER_ARCHIVE_URL` /
`Quasar:ManagedRuntime:ClusterArchiveUrl` points a test instance at a
locally built `ClusterForLinux-<version>.tar.gz` (`file://` or HTTP(S))
with its `SHA256SUMS` next to it. Verification is unchanged, the GitHub
token is never sent there, and such a package is accepted only while the
override is set.
- `QUASAR_MAGNETAR_ARCHIVE_URL` also accepts `file://`; a rebuilt local
archive is installed again. Both are documented in
`Docs/BuildingAndDevelopment.md`.

## Testing
- `dotnet build Quasar.sln -c Release`: 0 errors, no new warnings in
changed files.
- `Quasar.Tests`: 411 passed (385 before; 26 new tests, one per fix
where practical).
- `Quasar.Host --self-test`: ok, extended for token names and roster
migration, process identity, invalid state files, respawn backoff.
- Live on the review install (web worker on `127.0.0.1:18080`, Host
`local1` started by hand):
  - managed-credentials route writes token name `local1`;
- zero-length file in `Operations/Clusters` → `/api/ready` 200, file
renamed `.corrupt`, path logged;
- Host attached to a listener that never answers survives repeated 15 s
timeouts;
- `Gone` launch record whose PID is held by an unrelated process →
`recovery-readiness` 200 (was 409), same for a `Running` record from
another boot;
  - `/world-templates` shows one row per predefined world.
- Re-test after cluster v1.1.3 was released: guided setup ran to "Ready
to start" on the review install. `admission.json` has numeric
administrators, `tokens.json` names the Host ID, a failed setup shows
one alert, a Gateway that exits at start is respawned with growing
delays.
- End to end with a headless client (locally built cluster releases from
CometWorks/cluster#18, fed through
`QUASAR_CLUSTER_ARCHIVE_URL=file://…`, Direct Transport only, no Steam
on the Host account): the managed cluster reached Serving with two
regular nodes and the World Authority, the Host's executor heartbeat was
accepted, a headless Pulsar client from the cluster bench connected and
spawned a living character (Gateway session `Ready`), server-measured
movement was 23.6 m walking and 28.6 m / 42.4 m jetpack flight (up and
strafe end at the ceiling and walls of the corridor the character spawns
in), a rejoin re-possessed the same character, and goal Off shut the
serving cluster down cleanly in 8 s. The `steamclient.so` Host route was
exercised directly; a cluster serving through Steam was not started, and
the Abandon update button was not seen in the browser.

## Deliberately left for later
- SE1-0018: resolved for now by cluster v1.1.3; the exact-bytes
PluginSdk pin will block setup again at the next Magnetar release.
- SE1-0019 remainder: timeouts that act, rollback, abandon during a
half-applied activation, CLI command.
- SE1-0024 clean-Down proof cleared by activation: needs a "never
started since activation" proof in the writer-safety checks; analysed in
the ticket, not attempted.
- SE1-0031 remainder: verified-bundle hash cache, narrower Host
execution gate. SE1-0034 remainder: scheduled backups for a 24/7
cluster.
- SE1-0026 remainder: long operations still run on the HTTP request
token. SE1-0035 remainder: no UI button for cancel.
- SE1-0040 install-root Host/Port ignored: not reproducible at this
head; no change.
- New, open: goal Off does not stop the Host respawning a Gateway that
never started; a fix is proposed in the ticket but contradicts an
existing safety test, so it is not in this PR (SE1-0051).
- New, open: guided setup accepts a world without a static grid and the
converter then fails after the cluster ID is bound (SE1-0047).
- Not started: SE1-0025, SE1-0028, SE1-0029, SE1-0030, SE1-0032
(identity half done with SE1-0021), SE1-0033.
@viktor-ferenczi
viktor-ferenczi merged commit 9155abb into main Sep 21, 2026
4 checks passed
@viktor-ferenczi
viktor-ferenczi deleted the fix/prepare-managed-missing-metadata branch September 21, 2026 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant