Skip to content

fix: render the Electrum address as ssl://; 0.11.1:18 → 0.11.1:19 - #83

Merged
MattDHill merged 1 commit into
masterfrom
fix/electrum-address-shows-ssl-scheme
Aug 9, 2026
Merged

fix: render the Electrum address as ssl://; 0.11.1:18 → 0.11.1:19#83
MattDHill merged 1 commit into
masterfrom
fix/electrum-address-shows-ssl-scheme

Conversation

@helix-nine

@helix-nine helix-nine commented Aug 9, 2026

Copy link
Copy Markdown

The report

A user who had just migrated from 0.3.5.1 found Electrs running, fully synced, and reporting "Electrum Server is ready and accepting connections". The Interfaces page showed the Main IPv4 endpoint as 192.168.x.x:50001. Sparrow could reach neither that nor the onion, returning only "Retries exhausted".

The endpoint was fine. nc reached 50001, and openssl s_client against it came back with the StartOS certificate chain. Setting Private Electrum → port 50001 → Use SSL ON connected immediately, to electrs/0.11.1, protocol 1.4.

Their words: "the current Electrs Interfaces page shows :50001, but it isn't obvious from that screen that Sparrow needs SSL enabled. The Electrs Instructions also appear to reference SSL on port 50002, which made troubleshooting more confusing."

Both halves of that are our bug.

Nothing said the endpoint was TLS

MultiHost.bindPort with protocol: null returns new Origin(this, internalPort, null, null)scheme and sslScheme are both null, and this package passed schemeOverride: null on top. addressHostToUrl omits the :// prefix entirely when the effective scheme is null, so StartOS printed a bare host:port. The only SSL signal on the row is the certificate column reading "Root CA", which is easy to miss, and the interface was called "Main".

schemeOverride: { ssl: 'ssl', noSsl: 'tcp' } makes every address render ssl://host:port — the form Electrum wallets expect to be handed. This is well-worn in the fleet: coturn uses turns/turn, owntracks mqtts/mqtt, lnd lndconnect, simplex smp.

The interface is renamed Main → Electrum (SSL). That name is accurate rather than aspirational: the plaintext leg's forward is gated on !reqs.secure && !info.secure() (net/forward.rs:705), and info.secure() holds only for Loopback | Bridge, so no LAN or WAN gateway ever gets a rule for it. TLS is the only way in from off the box.

The plaintext port is not unbound, though — it is reachable at the bridge IP from the host and from other services over lxcbr0, source-filtered to that subnet. That is what getBridgeAddress(…, { ssl: false }) returns, and what replaced the retired electrs.startos DNS name.

The docs named a port StartOS never promised

instructions.md named 50002 three times. preferredExternalPort is a preference: BindInfo::new tries it and otherwise falls back to alloc(), and an assigned binding then never changes its external port — integrations depend on it, so only uninstall and reinstall reassigns. The number is per-server and fixed for the life of the install.

Which is exactly why this user was on 50001. Their 0.3.x manifest bound one port and had lan-config commented out:

interfaces:
  electrum:
    tor-config:
      port-mapping:
        50001: "50001"
    # lan-config:
    #   443:
    #     ssl: true
    #     internal: 50001

The v1 compat shim maps that to {preferredExternalPort: 50001, addSsl: null} (addSsl comes only from lan-config), giving assignedPort: 50001, assignedSslPort: None. The 0.4 package reuses the same host id (electrum) and internal port, so installing it hit BindInfo::update rather than new, where the since-removed carried handed that lone number to the TLS leg. Start9Labs/start-technologies#3638 stops that happening again; it does not and should not move servers already assigned.

So instructions.md no longer names a port. It points at the Interfaces page, and names the toggle — Private Electrum → Use SSL in Sparrow, the s suffix in Electrum — since the failure mode is a generic timeout that mentions neither TLS nor the certificate.

A wrong claim in the developer docs

README.md and AGENTS.md both said no plain-TCP port is exposed externally. One is allocated and reachable over the bridge, as above. Corrected in both, and AGENTS.md keeps one bullet on it — the interface is TLS-only from off the box, the plaintext port is the bridge address dependents resolve, and no literal external port belongs in docs.

Version

0.11.1:18 → 0.11.1:19. Interface metadata and docs only, so no migration — but :18 carried the clearTask migration for bitcoind's action renames, so it is frozen into startos/versions/v0.11.1_18.ts and added to the graph to keep it reachable from :17 and below.

Verified with npm run check and prettier. Not installed on a box — the change is exportServiceInterface metadata, which setupInterfaces re-emits on update.

A user who migrated from 0.3.5.1 could not connect Sparrow to either the
LAN or the onion endpoint, getting only "Retries exhausted". The endpoint
was fine: openssl s_client against it returned the StartOS chain, and
turning Sparrow's SSL toggle on connected immediately.

The Interfaces page gave no sign it had to be on. bindPort with
protocol: null returns an origin with no scheme, and StartOS renders a
scheme-less address as a bare host:port — so the page showed
192.168.x.x:50001 and nothing else. The only SSL marker on the row is the
certificate column reading "Root CA".

Sets schemeOverride: { ssl: 'ssl', noSsl: 'tcp' } so every address renders
ssl://host:port, the form Electrum wallets expect to be handed, and renames
the interface Main → Electrum (SSL) to match Fulcrum. The name is accurate:
no LAN or WAN gateway gets a forward for the plaintext port, so TLS is the
only way in from off the box.

The docs also named 50002 as if it were guaranteed. It is a preference, and
whatever StartOS assigns is permanent — an existing binding never changes
its external port, so the number is per-server for the life of the install.
That is why the reporter's server serves TLS on 50001 and will keep doing
so. instructions.md now points at the Interfaces page instead of naming a
port, and tells the user which toggle to look for.

And they claimed no plaintext port is exposed externally. One is allocated
and reachable at the bridge IP from the host and other services over
lxcbr0 — it is what getBridgeAddress hands to mempool, specter and canary,
and what replaced the retired electrs.startos name — it is just never
reachable from another machine. Corrected in README.md and AGENTS.md.

Freezes 0.11.1:18 into its own version file to keep its clearTask migration
reachable from :17 and below.
@helix-nine
helix-nine force-pushed the fix/electrum-address-shows-ssl-scheme branch from 113b527 to 64b42c1 Compare August 9, 2026 16:08
MattDHill added a commit to Start9Labs/fulcrum-startos that referenced this pull request Aug 9, 2026
Folded into 2.1.1:13. Same defect reported against electrs
(Start9-Community/electrs-startos#83) and present here identically: bindPort
with protocol: null returns an origin with no scheme, and StartOS renders a
scheme-less address as a bare host:port. The Interfaces page showed no sign
the endpoint was TLS, and a wallet configured from it failed with a generic
timeout — Sparrow reports only "Retries exhausted", naming neither SSL nor
the certificate.

Fulcrum was partly shielded by already naming its interface Electrum (SSL),
which is why this surfaced on electrs first, but the address itself was just
as bare.

Sets schemeOverride: { ssl: 'ssl', noSsl: 'tcp' } so every address renders
ssl://host:port, the form Electrum wallets expect to be handed.

instructions.md now tells the user which toggle to look for and to take the
port from the address rather than assuming one: preferredExternalPort is a
preference, and whatever StartOS assigns is permanent, so the number is
per-server for the life of the install.

README.md records that, and pins down the plaintext leg: no LAN or WAN
gateway gets a forward for it, but it is reachable at the bridge IP from the
host and other services over lxcbr0 — that is what getBridgeAddress returns
and what replaced the retired fulcrum.startos name.

Release notes gain a paragraph; no separate version, since 2.1.1:13 is
unreleased.
@MattDHill
MattDHill merged commit 2084047 into master Aug 9, 2026
3 checks passed
@MattDHill
MattDHill deleted the fix/electrum-address-shows-ssl-scheme branch August 9, 2026 16:13
MattDHill added a commit to Start9Labs/fulcrum-startos that referenced this pull request Aug 9, 2026
…ss as ssl:// (#16)

* feat(2.1.1:13): size db_mem to the box and reclaim it after sync

Fulcrum's own db_mem default is a flat 2048 MiB regardless of box size,
tuned for a dedicated Electrum server. On StartOS it is never alone — the
package has a hard dependency on Bitcoin — and the package wrote no db_mem
at all, so every install inherited that 2048 on top of Bitcoin's own cache.
A 4 GB box got a RocksDB budget over half its RAM, which upstream
explicitly advises against.

Seed db_mem at install to a quarter of installed RAM, capped at upstream's
2048, then drop it to 512 once the address index is built (upstream: "even
256 works well on an SSD"). Only the value we seeded is ever lowered, so
anything set through Configure is left alone. The migration backfills
existing installs according to their sync state.

main.ts now consts the whole fulcrum.conf, registered after the bitcoind
bridge-address merge so that write is already part of the captured value —
consting first would make the service restart itself once on every start.
Fulcrum reads its config only at startup, so this is also what makes a
Configure save take effect: previously the action wrote the file and
nothing restarted, leaving db_mem, worker_threads and bitcoind_clients
changes silently inert until something else restarted the service.

The post-sync write is the package's sole allowWriteAfterConst. It is
self-extinguishing twice over: syncNotified is persisted before it, and
merge skips writing byte-identical output.

Also folds in the abandoned bitcoind:other-config replay key that TODO.md
had been holding for the next shipping version.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: render the Electrum address as ssl://

Folded into 2.1.1:13. Same defect reported against electrs
(Start9-Community/electrs-startos#83) and present here identically: bindPort
with protocol: null returns an origin with no scheme, and StartOS renders a
scheme-less address as a bare host:port. The Interfaces page showed no sign
the endpoint was TLS, and a wallet configured from it failed with a generic
timeout — Sparrow reports only "Retries exhausted", naming neither SSL nor
the certificate.

Fulcrum was partly shielded by already naming its interface Electrum (SSL),
which is why this surfaced on electrs first, but the address itself was just
as bare.

Sets schemeOverride: { ssl: 'ssl', noSsl: 'tcp' } so every address renders
ssl://host:port, the form Electrum wallets expect to be handed.

instructions.md now tells the user which toggle to look for and to take the
port from the address rather than assuming one: preferredExternalPort is a
preference, and whatever StartOS assigns is permanent, so the number is
per-server for the life of the install.

README.md records that, and pins down the plaintext leg: no LAN or WAN
gateway gets a forward for it, but it is reachable at the bridge IP from the
host and other services over lxcbr0 — that is what getBridgeAddress returns
and what replaced the retired fulcrum.startos name.

Release notes gain a paragraph; no separate version, since 2.1.1:13 is
unreleased.

---------

Co-authored-by: Matt Hill <9935159+MattDHill@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants