Skip to main content

Build Roadmap โ€” Sentinel service node in Syrius

Ordered, scoped work items distilled from the Specification and verified against the Investigation. Each task lists where the work lives, the code anchors to start from, acceptance criteria, and whether it needs a protocol change.

Legend: ๐ŸŸข off-chain (no consensus change) ยท ๐Ÿ”ด requires spork/governance ยท โš ๏ธ work outside Syrius-Dart (libznn/go-zenon).


Phase 0 โ€” Verify assumptions (no code)โ€‹

TaskDetailDone when
0.1Confirm go-zenon's embedded RPC config: does libznn already expose a public-capable RPC server, and on what interface?The bind/port/TLS defaults are documented from go-zenon source.
0.2Confirm the Sentinel reward logic in go-zenon (is "uptime = registration duration" true?).Specification ยง2.1 is marked verified or corrected.

Phase 0 needs the go-zenon source, which is not in this repo. Resolve before estimating Phase A.


Phase A โ€” Make Syrius serve as a Sentinel โš ๏ธ๐ŸŸขโ€‹

The load-bearing, under-stated phase. The embedded node already runs and serves WS on 127.0.0.1:35998; the work is to let it serve publicly and stay up.

A.1 โ€” Configurable embedded node โš ๏ธโ€‹

  • Where: libznn/go-zenon FFI and syrius/lib/embedded_node/embedded_node.dart.
  • Anchors: FFI is arg-less RunNode()/StopNode() (embedded_node.dart:15-21,96); start path node_utils.dart:243.
  • Do: extend the FFI to accept config (bind address, port, enable public RPC, TLS), or have Syrius write a go-zenon config.json the node reads on RunNode().
  • Acceptance: Syrius can start the embedded node bound to a chosen interface/port with wss/TLS, controlled from Dart; default remains localhost-only.

A.2 โ€” "Run as Sentinel / serve publicly" mode ๐ŸŸขโ€‹

  • Where: Syrius app (settings + lifecycle).
  • Anchors: wake-lock handling node_utils.dart:238; node management node_management_screen.dart.
  • Do: add an explicit opt-in "serve publicly" toggle, a keep-alive/headless or tray-resident lifecycle, and a status surface (sync height, reachability, public endpoint).
  • Acceptance: with the toggle on, the node keeps serving when the main window is closed; off by default; clear security warning shown.

A.3 โ€” Public-serving security hardening ๐ŸŸขโ€‹

  • Where: Syrius + node config.
  • Do: read-scope the public RPC (no wallet/signing endpoints), add rate limiting/connection caps, enforce wss for non-localhost. See Specification ยง9.
  • Acceptance: public endpoint exposes only read/subscribe RPC; abuse controls in place.

Phase B โ€” Discovery, health & failover ๐ŸŸขโ€‹

Build the layer that finds Sentinel endpoints and routes clients to healthy ones. Reuses existing Syrius scaffolding.

B.1 โ€” Signed service recordsโ€‹

  • Where: Syrius + operator tooling (znn-controller).
  • Spec: ยง5. Do: define + verify the signed {owner, endpoint, capabilities, version, issuedAt, expiresAt, nonce, signature} record; verify signature, expiry, and that owner is in embedded.sentinel.getAllActive() / getByOwner().
  • Acceptance: an invalid/expired/non-active record is rejected before probing.

B.2 โ€” Populate the discovery slotโ€‹

  • Where: syrius/lib/main.dart, assets/community-nodes.json.
  • Anchors: loader _loadDefaultCommunityNodes (main.dart:180-192), validator InputValidators.node, shuffle (node_management_screen.dart:43).
  • Do: add a "Sentinel Nodes" category sourced from signed records (curated list first, feed later) instead of the empty [].
  • Acceptance: verified Sentinel endpoints appear as a selectable node tier.

B.3 โ€” Health probing + scoringโ€‹

  • Where: Syrius (node_utils.dart).
  • Anchors: establishConnectionToNode (node_utils.dart:20), getNodeChainIdentifier (:28); currently used only for the active node (:92,236), not as a pre-filter.
  • Spec: ยง7โ€“ยง8 (gating vs. scoring). Do: probe reachability + chain ID (gates) then score sync freshness, RPC correctness, latency, subscriptions, peers; rank; re-probe periodically.
  • Acceptance: unhealthy/wrong-chain nodes are excluded; survivors are ranked and cached.

B.4 โ€” RPC failoverโ€‹

  • Where: Syrius (NodeUtils).
  • Anchors: single zenon!.wsClient (node_utils.dart:79-110).
  • Do: on active-node failure, fail over to the next-best ranked node.
  • Acceptance: dropping the active node transparently reconnects to the next healthy one.

Phase C โ€” Trustless on-chain discovery ๐Ÿ”ดโ€‹

Requires governance; out of MVP scope. Tracked so it isn't mistaken for buildable now.

TaskDetail
C.1Add an endpoint (and optional capability/service-key) field to Sentinel Register + SentinelInfo + SDK. Blocker today: SentinelInfo has only owner/registrationTimestamp/isRevocable/revokeCooldown/active; register() encodes ('Register', []) (api/embedded/sentinel.dart:51).
C.2Move reward weighting from registration-duration to service quality: reward_weight = stake_valid ร— reachability ร— sync_freshness ร— service_correctness ร— uptime.

Dependency orderโ€‹

Phase 0 โ”€โ”€โ–บ A.1 โ”€โ”€โ–บ A.2 โ”€โ”€โ–บ A.3
โ”‚
B.1 โ”€โ”€โ–บ B.2 โ”€โ”€โ–บ B.3 โ”€โ”€โ–บ B.4 (B.1โ€“B.3 can start in parallel with A; B.2 needs B.1)
โ”‚
โ–ผ
Phase C (governance, later)

The smallest useful release = A.1 + A.2 + B.1 + B.2 + B.3 (operators serve publicly; Syrius discovers, verifies, and ranks them). No consensus change.