Skip to main content

Deployment paths: Browser UI vs. GitHub Actions

Relay Button ships two independent ways to provision an Aleph relay VM. Both share the low-level building blocks in @le-space/core (instance message creation, CRN scoring/notify, runtime inspection, FORGET/erase), but the orchestration differs substantially — which explains why the Actions path has historically been more reliable than the browser path.

AspectBrowser (@le-space/ui controller + @le-space/browser)GitHub Actions (@le-space/node deploy-executor)
SigningMetaMask personal_sign per message (user interaction)ALEPH_PRIVATE_KEY (non-interactive)
CRN selectionfilterDeployableCrns (score-sorted crns-list snapshot), preferred CRN first, up to 5 candidatesSame scoring plus geo preference, manual/scheduler placement strategies, vm_max_crn_attempts
CRN sourcecrns-list, falling back to the corechannel aggregate; aggregate candidates are probed at deploy time, treating a blocked request as "unknown" rather than deadSame fallback; aggregate candidates are probed while ranking. Both: GET /about/executions/list — see CRN discovery
Allocation notifyResult was discarded until 0.6.32; now an unconfirmed notify fails the attempt and triggers CRN failoverResult still discarded (.catch(() => null)) — compensated by the runtime wait below
Runtime validationwaitForVmRuntime polls CRN for IPv4/portsSame, but failure cleanly advances to the next CRN candidate
Guest configurationwaitForSetupEndpoint (hard gate) then HTTP POST VM:80/configure — impossible from an HTTPS origin, see Guest configuration handoff; images declaring supportsBootstrapConfigAggregate use the pull handoff insteadwaitForSetupEndpoint (hard gate) then configureOrbitdbRelaySetup; failure fails the attempt
Bootstrap registrationWaits for the guest; on delay publishes a browser fallback registration and then reports "Relay ready" — a dead guest looks greenReads real metadata (peer id, multiaddrs) from the configured VM, publishes an owner-authorized registration, verifies visibility on Aleph
Reachability validationNone — no dial test before successRequired relay probes (tcp, proxy-wss; best-effort webrtc-direct) — success means the relay was actually dialed
Cleanup of failed attemptsErase + FORGET + verify (since 0.6.30/0.6.32 fixes)Erase + FORGET + verify, plus retention of the last N successful deployments

Sequence: Browser path (Sponsor Relay UI)​

Sequence: GitHub Actions path (deploy-executor)​

Why the Actions path "just works" more often​

  1. No success lie. The Actions path derives the bootstrap registration from metadata served by the configured VM itself and then dials the relay (tcp, proxy-wss required). The browser path can publish a predicted fallback registration for a VM that never booted and still report "Relay ready" (observed in simple-todo E2E runs #37–#42).
  2. Hard gates instead of warnings. waitForSetupEndpoint and a failed /configure abort the attempt in the Actions path; the browser treats the equivalent situations as recoverable warnings.
  3. Failover maturity. vm_max_crn_attempts existed in the Actions path long before the browser gained working failover in 0.6.32 (the browser discarded the allocation-notify result until then, so a full CRN — NodeCity3 at capacity, HTTP 503 — silently produced ghost deployments).

A/B evidence: same image, same CRN, two outcomes (2026-07-19)​

orbitdb-relay run 29696456560 deployed the exact rootfs (orbitdb-relay-v0.9.7, f50d5005…) that the browser E2E had been failing with — after 3 CRN attempts it landed on Free To Link Moonlight5 (host 62.141.40.252, the same CRN/host where the browser-path guest had appeared dead in simple-todo runs #37–#42) and every probe passed. The infrastructure is fine; the difference is who publishes the bootstrap registration:

Conclusion: the guest self-registration services that the 0.6.30+ browser controller waits for (deregister service, reworked refresh) exist in the git tree but were never built into a published rootfs image — the deployed v0.9.7 image predates them. The Actions path never depended on guest self-registration (the deployer publishes from real VM metadata), which is why it keeps working. Two remedies, not mutually exclusive:

  1. Short term: make the browser controller publish the final registration from real VM metadata after a confirmed /configure (same as the Actions path) instead of treating that as a degraded "fallback".
  2. Long term: build + publish a new rootfs image containing the guest self-registration services, then update consumer manifests.

Known gaps (both paths)​

  • notifyCrnAllocationWithRetry results are still ignored in deploy-executor (.catch(() => null)); the runtime wait compensates, but adopting the 0.6.32 browser behavior (fail the attempt on an unconfirmed notify) would save one full runtime-wait cycle per rejected CRN.
  • The browser fallback registration should never satisfy the "Relay ready" condition; consumers (e.g. the simple-todo E2E guard) currently have to detect it themselves via the publisher address.
  • Neither path health-checks a CRN before selecting it; a CRN with broken Aleph endpoints (e.g. responses referencing retired official.aleph.cloud hosts) is only discovered after a deploy attempt.