feat(v2): operator fee-config Nostr publisher (closes #39) #43
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/fee-transport"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Layer 2 of the operator-configurable fee architecture (parent: #37). Publishes per-machine fee config to bitspire ATMs via NIP-44-v2-encrypted kind-30078 events, pairing with the consumer at
aiolabs/lamassu-next#57.Wire format locked at coord-log §
2026-06-01T14:25Z:Commits
aeaee1f— Refactor: extract shared kind-30078 publish primitives fromcassette_transport.pyinto a newnostr_publish.pymodule. Two consumers now (cassette + fee), so ~100 lines of duplicate was about to land. Generic helpers (resolve_operator_signer,sign_as_operator,nip44_encrypt_via_signer,nip44_decrypt_via_signer,publish_signed_event) + a high-levelpublish_encrypted_kind_30078wrapper.CassetteTransportErrorbecomes a subclass of the newNostrPublishError; existing catches still work via re-export. Behavior unchanged; 164/164 green.12f3922—FeeConfigPayload+FeePayloadComponentsPydantic models (Pydantic validators enforce the locked invariants: cap ≤ 0.15 per direction,|total - components_sum| < 1e-6consistency assert, schema_version ≥ 1).fee_transport.pypublisher withbuild_fee_payload(super_config, machine)andpublish_fee_config(machine, super_config, operator_user_id). Soft-fail discipline: transport errors log WARN + return None; cap violations hard-raise (indicates an API-guard bypass, not a transient).794d7e5— Wirepublish_fee_configinto the three trigger endpoints per the #39 spec. All three soft-fail on transport errors — underlying CRUD always succeeds.Triggers
api_create_machineapi_update_machineoperator_cash_*_fee_fractionin patchapi_update_super_configsuper_cash_*_fee_fractionchanged → fleet-wide republish, each machine signed by its own operatorsuper_fee_wallet_idchangedTest plan
test_fee_transport.py— 9 FeeConfigPayload validator cases (well-formed, wire round-trip, cap violations per direction, exact-cap acceptance, sum/components mismatch per direction, schema_version ≥ 1, zero-zero free-charge ATM), 4 build_fee_payload composition cases, 5 publish_fee_config soft-fail discipline cases (relay/signer/operator-identity-missing soft-fail, success returns signed event with correct d-tag + recipient + payload shape, cap violation raises before reaching publish)test_fee_publish_triggers.py— 9 trigger-integration cases covering all three endpoints (publish-on-create with default + nonzero fees, no-super-config short-circuit, publish-on-cash_in-change + publish-on-cash_out-change for update, skip-on-name-only + skip-on-is_active-only for update, fleet-wide publish-per-active-machine on super fraction change with per-machine operator signing, skip-on-wallet-id-only with assertion that list_all_active_machines is never called)cassette_transport.publish_to_atmis now a thin wrapper over the shared helperaiolabs/lamassu-next#57deploy gaps from coord-log §2026-06-01T18:30Z(relay.aiolabs.devhardcoded default points at nothing; deadVITE_LNBITS_HTTP_URLecho; operator-fees subscriber doesn't run duringawaiting-feesmaintenance state). Layer 2 is fully autonomous from bitspire on the publish side — events land on the relay with the correct shape; consumer side fixes are bitspire's lane.Strict-from-the-start
Per workspace CLAUDE.md "Backwards-compatibility on pre-public-launch code": no compat shims. The cassette_transport refactor removes 5 internal underscore-prefixed helpers in favor of shared public ones (one external caller in
tasks.pymigrated to import fromnostr_publishdirectly).Sequencing — what now
enforce_fee_matchPhase 2 (settlement-reject on out-of-tolerance) lands as a follow-up after observability data justifies the tighter posture.republish_operator_configshelper (#41) becomes implementable on top of the fee_transport + cassette_transport primitives — same shape for both doc types.Out of scope (follow-ups)
#38follow-up#41republish_operator_configs migration-cascade helperaiolabs/lamassu-next#57🤖 Generated with Claude Code