feat(pairing): optional token TTL + revoke endpoint (#9/#12, #22)
Some checks failed
ci.yml / feat(pairing): optional token TTL + revoke endpoint (#9/#12, #22) (pull_request) Failing after 0s
Some checks failed
ci.yml / feat(pairing): optional token TTL + revoke endpoint (#9/#12, #22) (pull_request) Failing after 0s
Builds on the seed-URL pairing in #21 (stacked). (b) TTL — PairMachineData.duration_hours (validated > 0) threads through pair_spire -> create_new_token (lnbits#55). None = non-expiring. (c) Revoke — POST /machines/{id}/revoke -> revoke_spire -> admin_client.revoke_key_user(spire-<id>). Per spirekeeper#22, revoke MUST go through KeyUser.revokedAt (revoke_key_user), NOT token revoke: lnbits eager-binds (redeems) the connect token at provision, so nsecbunkerd has materialised the policy into per-KeyUser grants its ACL checks BEFORE the Token.revokedAt filter -> token revoke is a silent no-op. Returns RevokeResult{revoked_count}: >=1 = cut, 0 = never bound. set_machine_unpaired clears paired_at (keeps npub + bunker_spire_key_name for audit / re-pair). 7 new tests (duration threading + default-None; revoke routes to revoke_key_user and never token-revoke + error mapping; endpoint wiring revoke happy/zero/502). 210 green; new code black/ruff-clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9c5f07c72e
commit
a5efdf22a1
6 changed files with 223 additions and 10 deletions
18
crud.py
18
crud.py
|
|
@ -233,6 +233,24 @@ async def set_machine_pairing(
|
|||
return await get_machine(machine_id)
|
||||
|
||||
|
||||
async def set_machine_unpaired(machine_id: str) -> Machine | None:
|
||||
"""Mark a machine unpaired after revoking its spire's bunker access
|
||||
(POST /revoke). Clears `paired_at`; keeps `machine_npub` +
|
||||
`bunker_spire_key_name` for audit / re-pair. The bunker-side
|
||||
`KeyUser.revokedAt` (set by `revoke_spire`) is what actually stops the
|
||||
spire signing — this just records the operator-visible state."""
|
||||
await db.execute(
|
||||
"""
|
||||
UPDATE spirekeeper.dca_machines
|
||||
SET paired_at = NULL,
|
||||
updated_at = :updated_at
|
||||
WHERE id = :id
|
||||
""",
|
||||
{"updated_at": datetime.now(), "id": machine_id},
|
||||
)
|
||||
return await get_machine(machine_id)
|
||||
|
||||
|
||||
async def delete_machine(machine_id: str) -> None:
|
||||
await db.execute(
|
||||
"DELETE FROM spirekeeper.dca_machines WHERE id = :id",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue