republish_operator_configs helper for LocalSigner→RemoteBunkerSigner migration cascade #41

Closed
opened 2026-06-01 07:22:48 +00:00 by padreug · 1 comment
Owner

Motivation

When an operator migrates from LocalSignerRemoteBunkerSigner (per the in-flight bunker integration), their account.pubkey changes. All previously-published kind-30078 docs under the old pubkey become orphaned:

  • Bitspire ATMs subscribe to authors:[<operator_pubkey>] for their operator-pushed config.
  • After migration, authors:[<old_pubkey>] shows no new events.
  • authors:[<new_pubkey>] shows no history.
  • Net effect: the ATM stops receiving config updates until each kind-30078 doc is hand-republished under the new pubkey.

Today this affects:

  • bitspire-cassettes:<atm_pubkey> (operator → ATM cassette inventory, #29)

Once #39 lands, it'll also affect:

  • bitspire-fees:<atm_pubkey> (operator → ATM fee config, #39)

Future operator-pushed configs (rate limits, dispense schedules, operator hours, …) extend the surface further. As doc-count grows, ad-hoc re-publishing per migration becomes increasingly error-prone.

Surfaced by

lnbits session advisory in ~/dev/coordination/log.md §2026-06-01T07:00Z. Greg's actual migration worked because cassette config was hand-republished — but that's a one-off shape that won't scale.

Proposed shape

Single entry point in cassette_transport.py (or wherever the operator-config publish primitives live by then):

async def republish_operator_configs(operator_user_id: str) -> dict[str, list[str]]:
    """Republish every operator-owned kind-30078 doc under the operator's
    current signer identity. Used by the LocalSigner→RemoteBunkerSigner
    migration cascade.

    Returns {doc_type: [event_ids_published]} for audit.
    """
    # 1. Load operator's known machines via dca_machines WHERE operator_user_id = ?
    # 2. For each machine, for each known doc type (cassettes, fees, ...):
    #    a. Load latest local state from DB
    #    b. Re-encrypt + re-sign under operator's current signer
    #    c. Publish via existing nostrclient relay path
    # 3. Return per-doc-type event-id list for the migration script audit log

Migration script becomes a one-liner per operator:

events = await republish_operator_configs(operator.id)
logger.info("re-published operator configs: %s", events)

Linked

  • lnbits memory project_local_to_remotebunker_migration — add a step pointing at this helper once it lands.
  • coord-log §2026-06-01T07:00Z (lnbits surfaces the gap), §2026-06-01T13:30Z (satmachineadmin adopting as follow-up)
  • Parent: #37 (fee architecture tracker — this is a follow-up that grows out of the multi-doc kind-30078 pattern fee config introduces)
  • Touches: #29 (cassette config, first kind-30078 operator doc), #39 (fee config, second)

Not in scope here

  • Detecting that a migration just happened (the migration script triggers this; we don't auto-detect)
  • Cleaning up the old orphaned events on the relay (relays handle replaceable-event semantics; orphans are inert, not harmful)
  • Republishing on every operator login or signer-switch (only the explicit migration cascade)

Sequencing

Not blocking #38 / #39 / #57. Lands cleanly after #39 ships (when there's >1 doc type to factor through the helper). Single-doc version (republish_cassette_configs) could land before #39 if a Greg-style migration recurs in the meantime.

## Motivation When an operator migrates from `LocalSigner` → `RemoteBunkerSigner` (per the in-flight bunker integration), their `account.pubkey` changes. All previously-published kind-30078 docs under the old pubkey become **orphaned**: - Bitspire ATMs subscribe to `authors:[<operator_pubkey>]` for their operator-pushed config. - After migration, `authors:[<old_pubkey>]` shows no new events. - `authors:[<new_pubkey>]` shows no history. - Net effect: the ATM stops receiving config updates until each kind-30078 doc is hand-republished under the new pubkey. Today this affects: - `bitspire-cassettes:<atm_pubkey>` (operator → ATM cassette inventory, #29) Once #39 lands, it'll also affect: - `bitspire-fees:<atm_pubkey>` (operator → ATM fee config, #39) Future operator-pushed configs (rate limits, dispense schedules, operator hours, …) extend the surface further. As doc-count grows, ad-hoc re-publishing per migration becomes increasingly error-prone. ## Surfaced by lnbits session advisory in `~/dev/coordination/log.md` §`2026-06-01T07:00Z`. Greg's actual migration worked because cassette config was hand-republished — but that's a one-off shape that won't scale. ## Proposed shape Single entry point in `cassette_transport.py` (or wherever the operator-config publish primitives live by then): ```python async def republish_operator_configs(operator_user_id: str) -> dict[str, list[str]]: """Republish every operator-owned kind-30078 doc under the operator's current signer identity. Used by the LocalSigner→RemoteBunkerSigner migration cascade. Returns {doc_type: [event_ids_published]} for audit. """ # 1. Load operator's known machines via dca_machines WHERE operator_user_id = ? # 2. For each machine, for each known doc type (cassettes, fees, ...): # a. Load latest local state from DB # b. Re-encrypt + re-sign under operator's current signer # c. Publish via existing nostrclient relay path # 3. Return per-doc-type event-id list for the migration script audit log ``` Migration script becomes a one-liner per operator: ```python events = await republish_operator_configs(operator.id) logger.info("re-published operator configs: %s", events) ``` ## Linked - lnbits memory `project_local_to_remotebunker_migration` — add a step pointing at this helper once it lands. - coord-log §`2026-06-01T07:00Z` (lnbits surfaces the gap), §`2026-06-01T13:30Z` (satmachineadmin adopting as follow-up) - Parent: #37 (fee architecture tracker — this is a follow-up that grows out of the multi-doc kind-30078 pattern fee config introduces) - Touches: #29 (cassette config, first kind-30078 operator doc), #39 (fee config, second) ## Not in scope here - Detecting that a migration just happened (the migration script triggers this; we don't auto-detect) - Cleaning up the old orphaned events on the relay (relays handle replaceable-event semantics; orphans are inert, not harmful) - Republishing on every operator login or signer-switch (only the explicit migration cascade) ## Sequencing Not blocking #38 / #39 / #57. Lands cleanly after #39 ships (when there's >1 doc type to factor through the helper). Single-doc version (`republish_cassette_configs`) could land before #39 if a Greg-style migration recurs in the meantime.
Author
Owner

➡️ Migrated to aiolabs/spirekeeper#19 (aiolabs/spirekeeper#19).

The v2-bitspire line of this extension now lives in its own repo, aiolabs/spirekeeper. Tracking for this issue continues there; closing here. (Issue numbers were reassigned in the new repo.)

➡️ **Migrated to https://git.atitlan.io/aiolabs/spirekeeper/issues/19 (aiolabs/spirekeeper#19).** The v2-bitspire line of this extension now lives in its own repo, `aiolabs/spirekeeper`. Tracking for this issue continues there; closing here. (Issue numbers were reassigned in the new repo.)
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/satmachineadmin#41
No description provided.