Skip to content

fix: resolve dependency addresses from the bridge address, not assignedPort - #74

Merged
MattDHill merged 1 commit into
masterfrom
fix/bridge-address-resolution
Jul 25, 2026
Merged

fix: resolve dependency addresses from the bridge address, not assignedPort#74
MattDHill merged 1 commit into
masterfrom
fix/bridge-address-resolution

Conversation

@helix-nine

@helix-nine helix-nine commented Jul 25, 2026

Copy link
Copy Markdown

Why

This package resolved its dependencies' addresses by reading bindings[<port>].net.assignedPort. That field is raw metadata, and which port field is populated depends on how the dependency bound the port — a binding with addSsl frees assignedPort entirely and carries only assignedSslPort.

LND hit exactly this when it moved REST behind the OS reverse proxy (Start9Labs/lnd-startos#171): every dependent silently resolved null.

What changed

Adopts sdk.host.getBridgeAddress, added in start-sdk 2.0.9 (Start9Labs/start-technologies#3560, #3561), and deletes the local copy of the helper this package was carrying — one of 44 near-identical copies across the fleet.

The helper resolves the binding's own derived bridge address, which is correct whether the dependency terminates its own TLS or hands the port to the OS proxy. It is computed per binding rather than per exported interface, so it also resolves bridge-only bindings such as tor's SOCKS proxy.

ssl: is passed only where the target binding publishes two bridge addresses — protocol: 'http'/'ws', or secure: null with addSsl. bitcoind's RPC is the clearest case (10.0.3.1:8332 ssl=false alongside 10.0.3.1:54404 ssl=true), so an undiscriminated lookup there is order-dependent. Where a binding publishes one address no discriminator is passed — pinning one would assert a fact about how the dependency binds, the coupling this change removes.

On the lockfile diff

Larger than expected, and expected to stay that way for now. This package pins git dependencies that track #next branches, which still resolve start-sdk 2.0.7, so npm nests a second SDK copy rather than hoisting one. It collapses once those next branches carry 2.0.9. The lockfile has to be committed regardless — s9pk.mk and the reusable CI both run npm ci, which fails on a lockfile out of sync with package.json. Functionally inert: the git deps are imported for types, and the s9pk build tree-shakes.

Verification

tsc and prettier clean against the published start-sdk 2.0.9. The resolution logic was verified live on a StartOS 0.4.0-beta.10 box: with LND on addSsl, net.assignedPort reads null while the binding's bridge entry resolves to 10.0.3.1:8080; LNbits connected through it (✔️ Backend LndRestWallet connected), and Fulcrum picked bitcoind's plaintext :8332 leg over the TLS :54404 one.

Not exercised against a running instance of this service — compile-checked only.

Test plan

  1. Install this build alongside its dependencies.
  2. Confirm it connects to each one and its health checks go green.
  3. Restart a dependency and confirm the address re-resolves without a restart loop — .const() should settle on one value and stay there.

@helix-nine
helix-nine force-pushed the fix/bridge-address-resolution branch from 68c8c40 to be6e323 Compare July 25, 2026 01:22
`net.assignedPort` and `net.assignedSslPort` are raw metadata, and
which of them is populated depends on how the dependency bound the port: a
binding with addSsl frees `assignedPort` entirely. Reading either field
directly breaks the moment a dependency changes its TLS arrangement, as
LND just did (Start9Labs/lnd-startos#171).

start-sdk 2.0.9 adds `sdk.host.getBridgeAddress`, which resolves the
binding's own derived address — correct under either arrangement, and
computed per binding so it also covers bridge-only bindings such as tor's
SOCKS proxy. Adopt it and delete the local copy of the helper this
package was carrying.

`ssl` is passed only where the target publishes both a plaintext and a
TLS address; elsewhere pinning it would assert a fact about how the
dependency binds.
@helix-nine
helix-nine force-pushed the fix/bridge-address-resolution branch from be6e323 to 1aa5e42 Compare July 25, 2026 02:31

@MattDHill MattDHill left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed: resolves dependency addresses via sdk.host.getBridgeAddress rather than the raw net.assignedPort / assignedSslPort fields, which are only populated depending on how the dependency bound the port. Verified live on a 0.4.0-beta.10 box across lnd, cln, fulcrum and lnbits.

@MattDHill
MattDHill merged commit 9f0d956 into master Jul 25, 2026
3 checks passed
@MattDHill
MattDHill deleted the fix/bridge-address-resolution branch July 25, 2026 03:47
@helix-nine

Copy link
Copy Markdown
Author

Merged and published — v0.11.1_12, Publish job green on both arches.

Two follow-ups since, for anyone reading this thread later:

The oversized lockfile I flagged above is gone — 2574ec6 collapsed the nested SDK copy with an overrides self-reference (−3060 lines) and shipped it as :13, rather than waiting on the #next branches to carry 2.0.9.

The docs half is still not right, so I opened #76. #75 swapped the helper name in AGENTS.md but kept the clause describing the mechanism this PR removed — sdk.getOsIp plus the binding's assigned external port. getOsIp is no longer called anywhere in startos/, and that file is auto-loaded by every agent in this repo, so it was pointing future work back at the coupling we just deleted.

Writing that fix also turned up something worth correcting in my description above. I justified omitting ssl: on the P2P lookup as "pinning it would re-assert the very coupling the change removes" — that reasoning holds, but the SDK docstring's stronger claim, that a discriminator there "would select nothing," does not: the peer binding's address carries ssl: false, and the filter is ssl === undefined || a.ssl === ssl, so ssl: false would match. Only ssl: true selects nothing. Relatedly, assignedPort and assignedSslPort are not mutually exclusive — BindInfo::new drops assignedPort only when secure.ssl == true and addSsl is set, so bitcoind's protocol: 'http' RPC binding carries both. That is exactly why the RPC lookup needs ssl: false and an undiscriminated one would be order-dependent.

Neither correction changes the code here — both calls are right as merged — but the first framing would have misled anyone debugging a null address into thinking assignedPort was empty when it isn't.

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.

2 participants