feat(pairing): m010 schema — bunker pairing columns on dca_machines
Schema checkpoint for seed-URL pairing (S0 / #9; spire-side bitspire#52), model A1 — the spire's signing key lives in the operator's nsecbunkerd, not on the spire's disk. dca_machines gains: - bunker_spire_key_name — the spire's key name in the bunker (spire-<machine_id>); used to re-issue connect tokens on re-pair. - paired_at — last successful pair; NULL = never paired. Both nullable, idempotent column-probe add (m009 pattern). Machine model gains the matching optional fields. Validated on the regtest dev db (columns present, migrations clean); 191 tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c651d53419
commit
bb473f5385
2 changed files with 44 additions and 0 deletions
|
|
@ -735,3 +735,44 @@ async def m009_split_fee_fractions_by_direction(db):
|
|||
await db.execute(
|
||||
"ALTER TABLE spirekeeper.super_config DROP COLUMN super_fee_fraction"
|
||||
)
|
||||
|
||||
|
||||
async def m010_add_machine_bunker_pairing(db):
|
||||
"""Add NIP-46 bunker-pairing columns to dca_machines for seed-URL
|
||||
pairing (S0 / aiolabs/spirekeeper#9; spire-side aiolabs/bitspire#52).
|
||||
|
||||
Under the chosen model (A1, decided 2026-06-16), the spire's signing
|
||||
key lives inside the operator's nsecbunkerd rather than on the spire's
|
||||
disk. `pair_machine` mints a per-spire key in the bunker, issues a
|
||||
scoped NIP-46 connect token, and hands the spire a one-shot seed URL
|
||||
embedding a `bunker://` connection. The spire then self-signs all its
|
||||
events (kind-21000 RPC, kind-30078 beacon/cassette-state) as its own
|
||||
bunker-held key; lnbits' path-B roster routes that npub to the
|
||||
operator's wallet.
|
||||
|
||||
("spire" = a bitSpire machine; the legacy Lamassu term was "ATM".)
|
||||
|
||||
- bunker_spire_key_name — the spire's key name inside the bunker
|
||||
(`spire-<machine_id>`). Used to re-issue a connect token on
|
||||
re-pair and (once the admin client grows a revoke RPC) to revoke
|
||||
spire access.
|
||||
- paired_at — timestamp of the last successful pair. NULL = the
|
||||
machine row exists but no bunker key has been minted yet.
|
||||
|
||||
Both nullable: machines created before this migration, and registered
|
||||
-but-never-paired machines, carry NULL until first pair. Idempotent
|
||||
column-probe pattern (same shape as m009).
|
||||
"""
|
||||
additions = [
|
||||
("dca_machines", "bunker_spire_key_name", "TEXT"),
|
||||
("dca_machines", "paired_at", "TIMESTAMP"),
|
||||
]
|
||||
for table, col, coltype in additions:
|
||||
try:
|
||||
await db.fetchone(f"SELECT {col} FROM spirekeeper.{table} LIMIT 1")
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
await db.execute(
|
||||
f"ALTER TABLE spirekeeper.{table} ADD COLUMN {col} {coltype}"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ class Machine(BaseModel):
|
|||
is_active: bool
|
||||
operator_cash_in_fee_fraction: float = 0.0
|
||||
operator_cash_out_fee_fraction: float = 0.0
|
||||
# NIP-46 bunker pairing (S0 / #9). NULL until the spire is first paired.
|
||||
bunker_spire_key_name: str | None = None
|
||||
paired_at: datetime | None = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue