Commit graph

5 commits

Author SHA1 Message Date
b193f6262d docs(pairing): TTL + token-revoke now enforced post-bind (nsecbunkerd#27)
Some checks failed
ci.yml / docs(pairing): TTL + token-revoke now enforced post-bind (nsecbunkerd#27) (pull_request) Failing after 0s
nsecbunkerd#27 (deployed 2026-06-19) reverses the #24 finding: the
sign-time ACL now evaluates token lifecycle live on every request
(checkIfPubkeyAllowed step 4 joins through a liveWhere filter;
applyToken stopped photocopying grants into SigningConditions). So:

- duration_hours / token expiresAt now bounds an ESTABLISHED binding —
  an expired token stops signing post-bind, not just at connect. The
  prior docstring (connect-window-only, pointing at the now-closed
  nsecbunkerd#24) is corrected.
- Token-revoke is no longer a post-redeem no-op (closes the #22
  mechanism bunker-side). revoke_spire keeps using revoke_key_user
  because that's the subject-level ban cutting the whole binding, not
  just one token's grant — rationale updated, behavior unchanged.

Doc/comment only; 20 pairing tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 23:23:11 +02:00
22678dfb4f feat(pairing): authorize kind-22242 (NIP-42 AUTH) in spire policy (#52)
Some checks failed
ci.yml / feat(pairing): authorize kind-22242 (NIP-42 AUTH) in spire policy (#52) (pull_request) Failing after 0s
ci.yml / feat(pairing): authorize kind-22242 (NIP-42 AUTH) in spire policy (#52) (push) Failing after 0s
bitspire#52 consumer review (2026-06-18) enumerated the kinds the spire
signs as its OWN identity and found NIP-42 relay AUTH (kind 22242) missing
from SPIRE_POLICY_RULES — a silent bunker reject the moment a relay
challenges with AUTH. It must be bunker-signed (AUTH proves control of
spire_pubkey, which only the bunker holds; can't use the local client_nsec).

Adds 22242. Records the confirmed set in the policy comment: live = 21000 +
30078 + 22242; CLINK 21001-21003 dormant but kept; nip04 unused (v1 path is
dead code). New test locks the required-kinds contract so 22242 can't
silently regress.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 17:29:43 +00:00
a5efdf22a1 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
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>
2026-06-18 18:51:54 +02:00
9c5f07c72e refactor(pairing): use lnbits' public ensure_policy, drop fork duplicate (#9)
Some checks failed
ci.yml / refactor(pairing): use lnbits' public ensure_policy, drop fork duplicate (#9) (pull_request) Failing after 0s
Adopts aiolabs/lnbits#55 (merged b5fba561): pair_spire now calls the
public ensure_policy(client, name='spirekeeper-spire', rules=...,
methods_no_kind=...) instead of spirekeeper's cache-free
_ensure_spire_policy copy. #55 re-keyed _POLICY_ID_CACHE on
(admin_pubkey, policy_name), so the shared helper no longer returns the
wrong (lnbits-default) id for a non-default policy name — the exact
reason the duplicate existed. Net -45 LOC, one less fork-divergent
reimplementation to keep in sync.

Requires lnbits >= the #55 merge (ensure_policy importable) — already
true on dev/demo.

Tests: FakeBunker gains admin_pubkey; an autouse fixture clears lnbits'
_POLICY_ID_CACHE between tests (the shared helper caches, unlike the old
local one). 203 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 18:31:21 +02:00
a77f5bcb5c feat(pairing): bunker pairing service — mint per-spire key + seed URL (#9)
Operator-side producer for seed-URL pairing (S0/#9, model A1). pair_spire()
orchestrates the nsecbunkerd admin chain via lnbits' NsecBunkerAdminClient:
  create_new_key(spire-<id>) -> _ensure_spire_policy -> create_new_token ->
  get_key_tokens -> package the <npub>#secret token into a bunker:// URL +
  base64url seed URL {spire_npub, spire_pubkey, bunker_url, relays}.

The spire later self-signs all its events as that bunker-held key; lnbits'
path-B roster maps the npub to the operator wallet — no nsec on the spire.
spirekeeper does steps 1-4 only; the NIP-46 connect/bind happens spire-side
(bitspire#52) with the spire's own client keypair.

Scoped policy 'spirekeeper-spire': sign_event 21000/21001-3/30078 + nip44
(kind-less via add_policy_rule). Local _ensure_spire_policy (no cache)
avoids lnbits' admin-pubkey-keyed _ensure_policy cache (policy-name-blind).

9 unit tests with a fake bunker (orchestration, policy reconcile, seed/
bunker:// wire shape, error paths); npub<->hex via lnbits' real helpers.
200 tests green.

Known gaps (lnbits NsecBunkerAdminClient): no token-expiry param, no revoke
RPC — re-pair works; 'revoke spire access' deferred to a bunker follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 23:17:48 +02:00