return_to query parameters are now parsed and only http and https URLs
are used for redirects, both in the frontend and when the createApp API
adds the connection parameters to the URL.
The production frontend build now also includes the same
Content-Security-Policy meta tag that is served as a header in http
mode, so the policy also applies where no HTTP headers are set, e.g. in
the desktop app.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The mock event consumer waited a fixed 10ms before returning consumed
events, which was not always enough on slow CI runners and caused flaky
failures (e.g. TestMarkSettled_App_BudgetWarning missing its
nwc_budget_warning event). It also appended to the events slice from
concurrent goroutines without synchronization, a data race that could
drop events.
- guard the consumed events slice with a mutex and return copies
- add WaitForConsumedEvents which polls until the expected number of
events arrived (up to 5s) instead of relying on a fixed sleep
- use it in tests that assert on consumed events; tests asserting that
no event was published keep the short grace period
- normalize event order in the keysend self-payment test, matching the
existing approach in the self-payment test, since async publishing
does not guarantee ordering
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Archive entry names come from the uploaded backup and were joined to the
restore directory without validation, so an entry name containing ".."
segments could resolve to a path outside it. Reject entries whose name is
absolute or escapes the restore directory, and confirm the cleaned
destination path stays within it before writing.
Add a test covering rejection of an entry that points outside the restore
directory.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix: keep showing migration success page after creating migration file
After creating a node migration file the hub is halted and the Alby
OAuth token is intentionally removed, so visiting the homepage sent the
user through /start into the Alby OAuth flow. Track the halted state
in memory and redirect back to the migration success page instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: synchronize migration flag access and propagate zip close error
Make nodeMigrationFileCreated an atomic.Bool since it is written by
CreateBackup and read by GetInfo on concurrent HTTP handler goroutines,
and finalize the migration archive explicitly so a failed zip close
returns an error instead of reporting a corrupt backup as success.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: return minimal info response after migration file is created
Once a migration file is created the hub is halted and the database is
closed. GetInfo previously only worked because every config key it reads
happened to be served from the config cache; any cache miss on an
error-propagating read would fail /api/info. Return early with a minimal
response instead so the migration success page does not depend on cache
state.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Allows users running Alby Hub on postgres (e.g. Alby Cloud) to create a
migration file from Settings -> Migrate Alby Hub. The contents of the
postgres database are copied into a temporary local sqlite database
which is included in the migration file, so it can be imported into a
fresh sqlite-based hub.
- extract the db_migrate CLI copy logic into a shared db.MigrateDB
- also copy the swaps and forwards tables (previously silently dropped)
- only require VSS in the source when migrating to postgres
- show a hint on the migrate page when running on postgres
- show database storage type and VSS status on the about page
- don't log an error when removing non-existent db files before restore
Closes#2500
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix: update bark onboarding and backup messaging for seed-based recovery
Since bark 0.6.0, offchain funds are recoverable from the mnemonic
alone via the seed-derived recovery mailbox. Remove the outdated
warnings that the recovery phrase is not sufficient, show the standard
recovery guidance for bark during onboarding, and expose the
seed-recovery scan result as a 'recoveryreport' custom node command so
users migrating to a new device can verify their funds were restored.
Closes#2512
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: settle bark lightning receives in the new delivering state
bark 0.6.0 added a 'delivering' receive state between preimage reveal
and settlement. The receive claim handler only treated
'preimage-revealed' and 'settled' as paid, so claimed receives were
published without a preimage and the transactions service rejected the
settlement ('no preimage in payment'), leaving paid invoices pending
forever.
Recognize all states at or past preimage reveal via a receiveIsPaid
helper (a positive allowlist, so an unknown future state degrades to
pending rather than falsely settled), only mark the transaction settled
when the preimage is present, and prefer bark's own settled_at
timestamp when available.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: replace import channels checkbox with LDK-specific warning
The 'I don't have another Alby Hub to migrate or open channels'
checkbox on the import recovery phrase screen only applied to LDK but
was required for every backend, and its claim that channel funds are
always lost is wrong when dynamic channel backups (VSS) are enabled.
Remove the checkbox and the channels bullet from the import screen and
show the caveat on the Security & Recovery page instead, only when a
mnemonic was imported and the LDK backend was chosen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat: enable typing card name when choosing other card
Closes#2457
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: reset connect-card dialog form on open and show empty name validation error
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: use shadcn Button for other-card option in connect dialog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix: publish transaction events only after the database transaction commits
markTransactionSettled and markPaymentFailed published nwc_payment_sent /
nwc_payment_received / nwc_payment_failed (and checkBudgetUsage published
nwc_budget_warning) while still inside the caller's database transaction, so
connected apps and the Alby API could be notified of a payment whose row was
never committed, and subscribers reading the database in response to an event
could race with the commit.
Every function that writes transaction state now owns its own database
transaction and publishes its events only after the commit succeeds:
- markTransactionSettled and markPaymentFailed open their own transaction;
callers no longer wrap them in db.Transaction
- new createSettledTransactionFromNotification inserts transactions reported
by LNClient notifications for payments the hub has no record of (external
payments, received keysends) directly in their settled state, removing the
transient PENDING row and the zombie row left behind on duplicate events
- markPaymentFailed now refuses to mark a settled transaction as failed,
replacing CancelHoldInvoice's in-transaction ACCEPTED re-check and also
protecting the SendPaymentSync error path from a racing settle
- checkBudgetUsage returns the budget warning event instead of publishing it
Closes#2506
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: serialize payment failure with settlement and propagate lock errors
Address review findings on the previous commit:
- markPaymentFailed now takes the same payment-hash row lock as settlement
(postgres), so the settled-state guard cannot be bypassed by a concurrent
settle between the state check and the update; it also returns not-found
instead of publishing an event when the transaction row no longer exists,
and reports whether this call transitioned the row so CancelHoldInvoice
only publishes nwc_hold_invoice_canceled when it performed the cancellation
- findSettledTransaction propagates errors from the lock query and the
settled-transaction lookup instead of treating a failed lookup as
"no settled transaction exists", which could defeat the dedup guard
- TestMarkSettled_Twice no longer shares one transaction struct between
concurrent goroutines and collects errors instead of asserting inside them
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: mark failed keysend payments via markPaymentFailed
The SendKeysend failure path updated the transaction directly, which never
zeroed the fee reserve, recorded no failure reason, published no
nwc_payment_failed event, and had no guard against overwriting a
concurrently settled payment. Route it through markPaymentFailed like
SendPaymentSync, and allow MockLn keysends to fail so the path is testable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix: use scope constant in get_budget permission query
The get_budget controller filtered the app_permissions scope column with
models.PAY_INVOICE_METHOD, which only matched because the method and
scope constants share the same string value. Use
constants.PAY_INVOICE_SCOPE like every other scope lookup, and document
why the unchecked First result is safe.
Fixes#2503
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: return error from get_budget on unexpected permission query failure
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The avatar overlay made the QR code hard to scan, especially for
short lightning addresses. Without center content the QR also drops
back to a lower error correction level, improving scannability.
Fixes#2507
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat: filter transactions
* fix: harden transaction filters
* refactor: use explicit nullable transaction filters with HideFailed polarity
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat: set transaction filters in a dialog from wallet actions menu
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat: filter transactions by search term and type
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: reject invalid transaction filters and reset page synchronously
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: parse complete minimum amount value in transactions filter dialog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Roland Bewick <roland.bewick@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix: fallback to outgoing payments in Phoenixd LookupInvoice
LookupInvoice only queried /payments/incoming/{hash}, returning 404 for
outgoing payments. This caused all outgoing Lightning payments to remain
permanently stuck as PENDING in Alby Hub.
The fix tries incoming first (preserving existing behavior), then falls
back to listing outgoing payments and matching by paymentHash.
Fixes#2442
* fix: amount and fees in phoenix payment to transaction
---------
Co-authored-by: Roland Bewick <roland.bewick@gmail.com>
chore: update bark bindings to v0.15.0
Moves from bark 0.4.0 to 0.6.0. The Ark server now requires protocol version 5
(hashlock clauses) to start a lightning receive, which only bark 0.6.0 sends, so
older clients are refused outright and cannot generate invoices.
No client changes needed: v0.15.0 only adds to the surface we use.
Creating a developer token in the Wails build failed with a confusing
"Unhandled route: POST /api/unlock" error, because the desktop app does
not expose an HTTP API for the token to be used against. Hide the token
creation form in the desktop build and show an explanatory message
instead.
Fixes#2471
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Picks up getAlby/go-nostr#6, which shares relay connections in
SimplePool when dials fail, closes relay websockets on pool close,
and closes previous subscriptions before re-subscribing on CLOSED.
The shared per-relay-URL connect backoff is now enabled by default
in the fork, so no hub-side pool option is needed
(nostr.WithPenaltyBox is deprecated).
Fixes#2481
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Switch from polling node.NextEvent() every second to node.NextEventAsync(),
which parks the goroutine until an event arrives without blocking an OS
thread or an LDK thread, as LDK is migrating to async event handling.
Guard event handling with a mutex held by Shutdown() so in-flight handlers
finish before the node is stopped and destroyed, and drop events that
arrive after shutdown starts (LDK redelivers unhandled events on startup).
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat: refresh payment QR and status components
* fix: align payment success button spacing
* fix: invert payment QR colors in dark mode
* fix: address payment QR review feedback
* fix: flatten nested cards in payment review FixedFloat tiles
* chore: remove internal payment component review screen
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: keep QR codes dark-on-light in dark mode
Inverted QR codes are unreadable by many scanner apps (e.g. Phoenix).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: René Aaron <rene@getalby.com>
Co-authored-by: Roland Bewick <roland.bewick@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Moves from bark 0.2.3 to 0.4.0, which changed the FFI surface:
- WalletOpen takes the network and a WalletOpenArgs, replacing WalletCreate
and the separate RunDaemon call.
- Bolt11Invoice takes an optional anti-DoS token, unused here.
- LightningReceiveStatus is now LightningReceiveState, reporting progress
via State rather than a PreimageRevealed bool.
Movements expose PaymentHash and sends expose a typed terminal state, so both
are read from those instead of the movement metadata JSON. A send movement that
is neither pending nor successful now resolves the SendPaymentSync waiter
instead of being ignored.
the mnemonic could be considered a non read-only route because the
mnemonic could be used.
This moves this route to the full access group to require a full access
api key.