docs: repoint migrated issue refs to spirekeeper numbers

Follow-up to the satmachineadmin->spirekeeper issue migration. The 20
open issues were recreated on aiolabs/spirekeeper with reassigned
numbers; this repoints in-repo references to the migrated issues at
their new spirekeeper numbers (#3->#1, #4->#2, #8->#4, #9->#5, #10->#6,
#17->#11, #21->#12, #28->#16, #44->#20). References to closed/non-
migrated satmachineadmin issues (#20/#22/#26/#29/#32/#37/#38/#39) stay
pointing at the original repo where they were resolved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-06-16 17:42:56 +02:00
commit 439c47ceae
8 changed files with 14 additions and 14 deletions

2
.gitignore vendored
View file

@ -10,5 +10,5 @@ data/
# uv lockfile — pyproject.toml still uses [tool.poetry] syntax, so uv lock
# produces a header-only file that pins nothing. Ignore until the
# PEP 621 migration lands (aiolabs/satmachineadmin#28).
# PEP 621 migration lands (aiolabs/spirekeeper#16).
uv.lock

View file

@ -163,7 +163,7 @@ def _build_partial_dispense_memo(
async def settle_lp_balance(
client: DcaClient, machine: Machine, data: SettleBalanceData
) -> DcaPayment:
"""Operator UX action — closes satmachineadmin#4.
"""Operator UX action — closes spirekeeper#2.
Settle an LP's remaining fiat balance from the operator's chosen funding
wallet at the rate the operator specified. Records a leg_type='settlement'
@ -271,7 +271,7 @@ async def settle_lp_balance(
async def apply_partial_dispense_and_redistribute(
settlement_id: str, data: PartialDispenseData
) -> DcaSettlement:
"""Operator UX action — closes satmachineadmin#3.
"""Operator UX action — closes spirekeeper#1.
When a bitSpire dispense fails mid-transaction (e.g., dispenser jam after
6 of 10 bills), the operator confirms the actual amount dispensed and we
@ -619,7 +619,7 @@ async def _pay_one_dca_leg(
errors=errors,
)
# Best-effort auto-forward to LP's external LN address (closes
# satmachineadmin#8). Skip if the DCA leg failed (nothing to forward).
# spirekeeper#4). Skip if the DCA leg failed (nothing to forward).
# If autoforward fails, sats stay in the LP's LNbits wallet — the
# explicit safety constraint.
if (
@ -643,7 +643,7 @@ async def _attempt_autoforward(
settlement: DcaSettlement,
amount_sats: int,
) -> None:
"""LP auto-forward (best-effort) — closes satmachineadmin#8.
"""LP auto-forward (best-effort) — closes spirekeeper#4.
Resolves the LP's configured LN address, requests a bolt11 invoice for
the DCA leg's sat amount, and pays it from the LP's LNbits wallet. Each

View file

@ -303,7 +303,7 @@ None of those need to change. The new layers slot in *above* them.
| **S0 — SeedURL pairing + ATM keypair separation** | Provisioning script generates a fresh `nsec` for the ATM (already does — we just stop overwriting it with the operator's). Operator pastes a oneshot QR/seed URL containing `{atm_npub, operator_npub, relay_list, signed_delegation_token}` at ATM first boot. | G3 (most of it), G9 | 1 week | None — purely on our side. Use existing NIP26 spec. |
| **S1 — NIP40 expiration on all kind21000** | Every RPC carries `["expiration", now+5min]`. Handler refuses pastexpiration. ATM clock check on boot (warn if drift > 60s). | G4 | 12 days | Relay must support NIP40 (most do). |
| **S2 — NIP26 delegation enforcement in nostrtransport** | Handler parses `delegation` tag, validates sig over conditions, checks conditions match the event, looks up operator pubkey in roster. Reject events without a valid delegation. | G3 (rest), G7 (partially) | 12 weeks | LNbits PR upstream (or vendored fork on `aiolabs/lnbits` branch `nostr-transport-nip26`). |
| **S3 — NIP57style settlement receipts** | After LNbits internal payment legs complete, publish a signed receipt event per settlement (and per leg if we want leglevel audit). ATM subscribes; operator dashboard renders receipts sidebyside with `dca_settlements`. | G2, G7 | 12 weeks | **Kind allocation — DO NOT USE `kind:21001`.** That kind is claimed by CLINK (Offers) — collision caught during the 20260602 CLINK primer review. Rotation off 21001 is tracked at `aiolabs/satmachineadmin#44`; target is the aiolabs reserved band **`2200022099`** per the workspace rule in `~/dev/CLAUDE.md` (§ "Nostr kind allocations — avoid the CLINK band"). The earlier 21001 lock across `aiolabs/lnbits#22`, `aiolabs/satmachineadmin#17`, and the satmachine ATM is **SUPERSEDED** — pick the new kind before any of those land. Reusing `kind:9735` (zap receipt) is also off the table: NIP57 semantics don't apply to bitSpire cashout settlements. |
| **S3 — NIP57style settlement receipts** | After LNbits internal payment legs complete, publish a signed receipt event per settlement (and per leg if we want leglevel audit). ATM subscribes; operator dashboard renders receipts sidebyside with `dca_settlements`. | G2, G7 | 12 weeks | **Kind allocation — DO NOT USE `kind:21001`.** That kind is claimed by CLINK (Offers) — collision caught during the 20260602 CLINK primer review. Rotation off 21001 is tracked at `aiolabs/spirekeeper#20`; target is the aiolabs reserved band **`2200022099`** per the workspace rule in `~/dev/CLAUDE.md` (§ "Nostr kind allocations — avoid the CLINK band"). The earlier 21001 lock across `aiolabs/lnbits#22`, `aiolabs/spirekeeper#11`, and the satmachine ATM is **SUPERSEDED** — pick the new kind before any of those land. Reusing `kind:9735` (zap receipt) is also off the table: NIP57 semantics don't apply to bitSpire cashout settlements. |
| **S4 — NIP78 permachine config + fleet roster** | Operator publishes `kind:30078` config + `kind:30000` fleet list. Handler crosschecks ATM npub ∈ fleet; reads maxwithdraw/fee policy from config. | G1, G9 | 1 week | Define config schema; backwardscompat path for preNIP78 machines. |
| **S5 — `sender_pubkey` persistence + signed metadata in Payment.extra** | When the dispatcher writes a Payment row, it stamps `Payment.extra.sender_pubkey`, `delegation_root`, and an HMAC over the key fields keyed by the LNbits server's own secret. Mutation postwrite breaks the HMAC. | G2 (DBside), G5, G6 | 35 days | LNbits PR — fairly localised. |
| **S6 — Rate limiting + rostergated autoaccount** | Autoaccountfromnpub only fires if the npub appears in some operator's NIP78 fleet OR if an explicit "open enrollment" flag is set. Relay/handlerlevel rate limit per pubkey. | G8, G9 | 1 week | LNbits PR. |
@ -374,7 +374,7 @@ For an auditor or new contributor doing a walkthrough:
| `~/dev/nostr-protocol/nips/57.md` | Lightning zaps & signed receipts. | Pattern source for S3. |
| `~/dev/nostr-protocol/nips/78.md` | Appspecific replaceable events. | Source for S4. |
Existing Forgejo issues this report supersedes/consolidates: `aiolabs/satmachineadmin#9` (v2 epic), `#11` (security audit findings), `#12` (ATM pairing + bunker deepdive), `aiolabs/lamassu-next#44` (Payment.extra split). This document is the design that closes the securityrelevant subset of those.
Existing Forgejo issues this report supersedes/consolidates: `aiolabs/spirekeeper#5` (v2 epic), `#11` (security audit findings), `#12` (ATM pairing + bunker deepdive), `aiolabs/lamassu-next#44` (Payment.extra split). This document is the design that closes the securityrelevant subset of those.
---

View file

@ -45,7 +45,7 @@ async def m001_satmachine_v2_initial(db):
concurrent process_settlement invocations.
* notes on dca_settlements append-only audit memo for partial-
dispense recompute + operator-authored notes (see
aiolabs/satmachineadmin#10 for the future structured audit table).
aiolabs/spirekeeper#6 for the future structured audit table).
"""
# 1. Drop legacy v1 tables. IF EXISTS handles both fresh-install
# paths (no-op) and migration from a v1 schema (cleans up).

View file

@ -478,7 +478,7 @@ class UpdateSuperConfigData(BaseModel):
class PartialDispenseData(BaseModel):
"""Resolves satmachineadmin#3 — operator confirms actual bills dispensed
"""Resolves spirekeeper#1 — operator confirms actual bills dispensed
when bitSpire reports an error mid-dispense.
Either `dispensed_fraction` (0..1) for ratio-based recompute, or
@ -550,7 +550,7 @@ class AppendSettlementNoteData(BaseModel):
class SettleBalanceData(BaseModel):
"""Resolves satmachineadmin#4 — operator settles small remaining LP balance
"""Resolves spirekeeper#2 — operator settles small remaining LP balance
from their own wallet at a specified exchange rate.
Use case: an LP has a small remaining fiat balance (e.g. 47 GTQ) that

View file

@ -18,7 +18,7 @@ back to the helpers in this module against the stored `account.prvkey`.
This module's runtime export footprint is therefore:
- `encrypt_for` / `decrypt_from` called by the LocalSigner fallback in
`cassette_transport` until every operator on the instance is bunker-backed
(S7 / aiolabs/satmachineadmin#21). Then those calls disappear too.
(S7 / aiolabs/spirekeeper#12). Then those calls disappear too.
- Everything else (encrypt_with_conversation_key, decrypt_with_conversation_key,
get_conversation_key, padding helpers, error classes) is **test-only**:
referenced by `tests/test_nip44_v2.py` to validate the wire format against

View file

@ -1427,7 +1427,7 @@
</q-dialog>
<!-- =============================================================== -->
<!-- SETTLE BALANCE DIALOG (closes satmachineadmin#4) -->
<!-- SETTLE BALANCE DIALOG (closes spirekeeper#2) -->
<!-- =============================================================== -->
<q-dialog v-model="settleBalanceDialog.show" persistent>
<q-card :style="{minWidth: '480px', maxWidth: '95vw'}">

View file

@ -458,7 +458,7 @@ async def api_settle_client_balance(
data: SettleBalanceData,
user: User = Depends(check_user_exists),
) -> DcaPayment:
"""Operator UX — closes satmachineadmin#4.
"""Operator UX — closes spirekeeper#2.
Settle an LP's remaining fiat balance from the operator's chosen funding
wallet at the specified exchange rate. The amount_fiat is capped at the
@ -692,7 +692,7 @@ async def api_partial_dispense(
data: PartialDispenseData,
user: User = Depends(check_user_exists),
) -> DcaSettlement:
"""Operator UX — resolves satmachineadmin#3.
"""Operator UX — resolves spirekeeper#1.
Recompute the split for a settlement that didn't dispense the full
amount (jam, mid-tx error). Provide one of dispensed_fraction (0..1)