Skip to content

Plugin: derive operator daemon enablement from the configured account - #578

Merged
heifner merged 1 commit into
masterfrom
fix/drop-daemon-enable-flags
Aug 27, 2026
Merged

Plugin: derive operator daemon enablement from the configured account#578
heifner merged 1 commit into
masterfrom
fix/drop-daemon-enable-flags

Conversation

@heifner

@heifner heifner commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Running a batch operator or an underwriter took three settings that all had to agree: the plugin listed under plugin =, an enable flag, and the account name. The flag is redundant with the other two and fails badly when it disagrees with them — an operator who sets the account but not the flag gets a node that boots clean, logs one info line, and relays nothing, which looks healthy to a supervisor until the elected group misses an epoch.

batch-enabled and underwriter-enabled are removed. Each daemon now runs when its account is configured, matching how producer_plugin keys off producer-name. For batch_operator_plugin the account rather than plugin presence is the trigger because external_debugging_plugin declares the plugin as a dependency, and keying off presence would silently promote a debug node to an operator.

BREAKING: appbase rejects unknown options in config.ini and on the command line, so a node still setting either flag will refuse to start. The cluster-tool passes both and needs the matching change.

Also corrects the batch_operator README's option table, which documented a batch-outpost-poll-ms option that does not exist and defaults that no longer match the code.

Cross-repo

Lands as a set — nodeop rejects unknown options, so cluster-managed operators stay down until the cluster-tool change lands too.

Merge order: this and #579, then the sdk-core publish, then wire-tools-ts#77.

batch_operator_plugin and underwriter_plugin each carried an enable flag alongside the account name, so running either daemon took three settings: the plugin listed under `plugin =`, the enable flag, and the account. Setting the account but forgetting the flag left the node booting clean, logging one info line, and doing nothing — indistinguishable from a healthy node until the elected group missed an epoch.

Both flags are removed. Enablement now derives from the account being configured, the way producer_plugin keys off producer-name. Keying off the account rather than the plugin being listed matters for batch_operator_plugin because external_debugging_plugin declares it as a dependency; plugin presence alone would silently promote a debug node to a batch operator.

The skip-startup log lines name the missing option instead of saying "disabled", and both option-surface tests assert the removed spellings stay gone. The batch README's option table was stale independently of this change — it documented a batch-outpost-poll-ms option that does not exist and defaults that no longer match the code — and is corrected here.
@heifner
heifner requested review from a team and huangminghuang August 20, 2026 15:12

@huangminghuang huangminghuang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking compatibility issue: this PR removes --batch-enabled and --underwriter-enabled, but the canonical cluster tool still emits both for every operator daemon in packages/cluster-tool/src/tools/wire/OperatorDaemonTool.ts (batchOperatorArgs and underwriterArgs). Because nodeop rejects unknown options, cluster-tool-managed batch operators and underwriters will fail to start after this merges. Please land the matching cluster-tool change (and its test updates) before or alongside this PR, or retain backward compatibility until then.

@heifner

heifner commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Confirmed, and the matching cluster-tool change is up: Wire-Network/wire-tools-ts#77. It drops --batch-enabled and --underwriter-enabled from batchOperatorArgs / underwriterArgs along with their test assertions in OperatorDaemonTool.test.ts, NodeopProcess.test.ts, and NodeopProcessSteps.test.ts.

It also covers a second wave of the same problem, since the whole set has to land together: #579 moves each outpost's remote contract addresses onto its sysio.chains row and keys the RPC client by chain code, removing --batch-outpost, --batch-sol-client-id and both --underwriter-{eth,sol}-outpost. The cluster-tool PR strips those too.

Order is forced by @wireio/sdk-core being a published dependency in wire-tools-ts rather than a workspace link:

  1. this PR and Opp: source outpost identity from sysio.chains instead of operator config #579
  2. Chore: regenerate SysioContractTypes for sysio.chains outpost addrs wire-libraries-ts#72 — regenerated SysioContractTypes — then publish
  3. Fix: source outpost identity from sysio.chains wire-tools-ts#77

I did not add a backward-compatibility window. Accepting-and-ignoring a removed flag would leave batch-enabled = false silently disabling a node whose account is configured, which is the exact failure this PR removes.

@huangminghuang huangminghuang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. The cluster-tool compatibility change is landing with this PR.

@heifner

heifner commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

@jglanz — on why the daemons need a WIRE account and not just the signature provider. Short answer: a key is a credential, the account is the identity, and signing is only a small part of what the account is used for.

The provider (fc::crypto::signature_provider) carries target_chain, key_type, key_name and public_key — no account. The cluster-tool registers the WIRE provider with no name at all (unlike the ETH/SOL ones, which get eth-${account} / sol-${account}), and the plugins select it by chain kind and key type. So there is nothing on the provider to derive an account from, and key→account is one-to-many anyway.

Here is every place each daemon actually uses it.

Batch operator

Use What it does
Transaction auth permission_level{account, active} on msgch::deliver and msgch::chkcons
batch_op_name in the deliver payload The depot records WHICH operator delivered. This is how consensus is tallied across the 7 and how a wrong delivery is attributed for slashing
Group membership Scans sysio.epoch::batch_op_groups for its own name to set my_group and decide whether it is elected this epoch
Own status Finds its sysio.opreg::operators row and halts relaying on SLASHED / TERMINATED
Debug envelopes Stamps the emitting operator on DebugEnvelopeEvent

Underwriter

Use What it does
Transaction auth Same, on its sysio.uwrit actions
uic.uw_account Written into the signed UnderwriteIntentCommit protobuf. The contract verifies the UIC signature against this named account — it is the claim being signed
Collateral math Three account-filtered reads: raw balances from sysio.opreg::operators, minus active locks from sysio.uwrit::locks, minus pending withdraws from sysio.opreg::wtdwqueue
Request selection Skips uwreqs already assigned to a different underwriter
Commit dedup Scans commits_by on a request to see whether IT already committed
AuthX link coverage Preflight requires a sysio.authex::links row per outpost chain, keyed by username = the account
Own status Same SLASHED / TERMINATED halt

The short of it

Signing is one row in each table. Everything else is either identity written INTO on-chain data — batch_op_name on the delivery, uw_account in the signed UIC, both of which the contracts read and act on — or table lookups keyed by account: your collateral, your locks, your group slot, your commits, your status.

The collateral path is the clearest case that a key would not help: available balance is three separate account-keyed reads across two contracts, and the result decides whether the underwriter can cover a request at all.

There is also a practical reason not to make the key the identity: keys rotate, and the account is what holds the bond, the group slot and the slashing history. If identity were the key, a key rotation would make you a different operator.

Where the instinct is right

Configuring an account and a key that must agree, with nothing checking they agree, is a poor deployment story — today a mismatch starts cleanly and then fails every delivery on chain forever. The underwriter already guards this (provider_can_authorize_active_alone, requiring exactly one WIRE provider whose key alone satisfies the account's active permission); the batch operator does not. Happy to add the same check so the pairing is verified at startup rather than left to convention.

@heifner
heifner merged commit 796dd34 into master Aug 27, 2026
13 checks passed
@heifner
heifner deleted the fix/drop-daemon-enable-flags branch August 27, 2026 13:50
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.

3 participants