Compare commits
No commits in common. "main" and "v0.1.2" have entirely different histories.
2 changed files with 22 additions and 32 deletions
32
pairing.py
32
pairing.py
|
|
@ -179,20 +179,26 @@ async def pair_spire(
|
||||||
connection lifecycle out of the orchestration so this is unit-testable
|
connection lifecycle out of the orchestration so this is unit-testable
|
||||||
with a fake client.
|
with a fake client.
|
||||||
|
|
||||||
`relays` are the relays the spire uses for its *own* events
|
`bunker_relay` / `keystore_passphrase` default to the lnbits bunker
|
||||||
(kind-21000/30078) — typically the operator's public nostrrelay; supplied by
|
settings; injectable for tests. `relays` are the relays the spire will
|
||||||
the API layer. `bunker_relay` (the relay baked into `bunker_url`, where the
|
use for its *own* events (kind-21000/30078) — typically the operator's
|
||||||
spire reaches the bunker) defaults to `relays[0]`; `keystore_passphrase`
|
nostrrelay; supplied by the API layer.
|
||||||
defaults to the lnbits bunker setting. Both injectable for tests.
|
|
||||||
|
|
||||||
Raises PairingError on any bunker failure; no state is persisted here
|
Raises PairingError on any bunker failure; no state is persisted here
|
||||||
(the API layer persists on success).
|
(the API layer persists on success).
|
||||||
"""
|
"""
|
||||||
|
relay = (
|
||||||
|
bunker_relay if bunker_relay is not None else settings.lnbits_nsec_bunker_url
|
||||||
|
)
|
||||||
passphrase = (
|
passphrase = (
|
||||||
keystore_passphrase
|
keystore_passphrase
|
||||||
if keystore_passphrase is not None
|
if keystore_passphrase is not None
|
||||||
else settings.lnbits_nsec_bunker_keystore_passphrase
|
else settings.lnbits_nsec_bunker_keystore_passphrase
|
||||||
)
|
)
|
||||||
|
if not relay:
|
||||||
|
raise PairingError(
|
||||||
|
"LNBITS_NSEC_BUNKER_URL is not set — cannot build a spire bunker connection"
|
||||||
|
)
|
||||||
if not passphrase:
|
if not passphrase:
|
||||||
raise PairingError(
|
raise PairingError(
|
||||||
"LNBITS_NSEC_BUNKER_KEYSTORE_PASSPHRASE is not set — "
|
"LNBITS_NSEC_BUNKER_KEYSTORE_PASSPHRASE is not set — "
|
||||||
|
|
@ -200,14 +206,6 @@ async def pair_spire(
|
||||||
)
|
)
|
||||||
if not relays:
|
if not relays:
|
||||||
raise PairingError("at least one relay is required for the seed URL")
|
raise PairingError("at least one relay is required for the seed URL")
|
||||||
# The relay baked into `bunker_url` is where the *spire* (the remote ATM)
|
|
||||||
# reaches the bunker, so it must be a machine-reachable public URL — NOT
|
|
||||||
# `settings.lnbits_nsec_bunker_url`, which is how the co-located lnbits
|
|
||||||
# reaches the bunker (typically ws://127.0.0.1, unreachable from the ATM —
|
|
||||||
# the localhost-relay /pair gotcha bitspire flagged). Default to the spire's
|
|
||||||
# own event relay (the bunker lives on the same operator relay the spire
|
|
||||||
# publishes to); an explicit `bunker_relay` overrides for split-relay deploys.
|
|
||||||
relay = bunker_relay if bunker_relay else relays[0]
|
|
||||||
|
|
||||||
key_name = spire_key_name(machine.id)
|
key_name = spire_key_name(machine.id)
|
||||||
client_name = f"spire-client-{machine.id}"
|
client_name = f"spire-client-{machine.id}"
|
||||||
|
|
@ -252,7 +250,9 @@ async def pair_spire(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def revoke_spire(machine: Machine, *, admin_client: NsecBunkerAdminClient) -> int:
|
async def revoke_spire(
|
||||||
|
machine: Machine, *, admin_client: NsecBunkerAdminClient
|
||||||
|
) -> int:
|
||||||
"""Revoke a spire's bunker access (the "Revoke spire access" UX,
|
"""Revoke a spire's bunker access (the "Revoke spire access" UX,
|
||||||
aiolabs/spirekeeper#9/#12).
|
aiolabs/spirekeeper#9/#12).
|
||||||
|
|
||||||
|
|
@ -274,4 +274,6 @@ async def revoke_spire(machine: Machine, *, admin_client: NsecBunkerAdminClient)
|
||||||
except NsecBunkerNotConfiguredError as exc:
|
except NsecBunkerNotConfiguredError as exc:
|
||||||
raise PairingError(f"nsecbunkerd is not configured: {exc}") from exc
|
raise PairingError(f"nsecbunkerd is not configured: {exc}") from exc
|
||||||
except NsecBunkerError as exc:
|
except NsecBunkerError as exc:
|
||||||
raise PairingError(f"bunker admin RPC failed during revoke: {exc}") from exc
|
raise PairingError(
|
||||||
|
f"bunker admin RPC failed during revoke: {exc}"
|
||||||
|
) from exc
|
||||||
|
|
|
||||||
|
|
@ -218,29 +218,17 @@ def test_malformed_token_raises():
|
||||||
_pair(bunker)
|
_pair(bunker)
|
||||||
|
|
||||||
|
|
||||||
def test_bunker_relay_defaults_to_spire_event_relay():
|
def test_missing_relay_or_passphrase_raises():
|
||||||
"""No explicit bunker_relay -> the relay baked into bunker_url is the spire's
|
with pytest.raises(PairingError, match="LNBITS_NSEC_BUNKER_URL"):
|
||||||
own public event relay (relays[0]), NOT lnbits's internal bunker URL. This
|
asyncio.run(
|
||||||
is the localhost-relay /pair gotcha: a UI-minted seed (the form has no
|
|
||||||
bunker_relay field) must embed a machine-reachable relay, not ws://127.0.0.1.
|
|
||||||
An empty bunker_relay falls back to the same default."""
|
|
||||||
from urllib.parse import quote
|
|
||||||
|
|
||||||
for empty in (None, ""):
|
|
||||||
result = asyncio.run(
|
|
||||||
pair_spire(
|
pair_spire(
|
||||||
_machine(),
|
_machine(),
|
||||||
relays=_RELAYS,
|
relays=_RELAYS,
|
||||||
admin_client=FakeBunker(token_secret="s"), # pragma: allowlist secret
|
admin_client=FakeBunker(),
|
||||||
bunker_relay=empty,
|
bunker_relay="",
|
||||||
keystore_passphrase=_PASSPHRASE,
|
keystore_passphrase=_PASSPHRASE,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert f"relay={quote(_RELAYS[0], safe='')}" in result.bunker_url
|
|
||||||
assert "127.0.0.1" not in result.bunker_url
|
|
||||||
|
|
||||||
|
|
||||||
def test_missing_relay_or_passphrase_raises():
|
|
||||||
with pytest.raises(PairingError, match="PASSPHRASE"):
|
with pytest.raises(PairingError, match="PASSPHRASE"):
|
||||||
asyncio.run(
|
asyncio.run(
|
||||||
pair_spire(
|
pair_spire(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue