Skip to content

feat: sweep webhooks, balance sync, sweep API improvements, server update webhook (v0.1.6.0-v0.1.8.0) - #34

Open
i2dor wants to merge 7 commits into
sethforprivacy:mainfrom
i2dor:main
Open

feat: sweep webhooks, balance sync, sweep API improvements, server update webhook (v0.1.6.0-v0.1.8.0)#34
i2dor wants to merge 7 commits into
sethforprivacy:mainfrom
i2dor:main

Conversation

@i2dor

@i2dor i2dor commented Aug 23, 2026

Copy link
Copy Markdown

This PR adds several features built on top of v0.1.5.4, across four releases.


v0.1.6.0 — Balance sync, sweep webhook, config warnings, setup scripts

  • POST .../spark/sync — forces a wallet sync and returns the current balance (walletRunning, balanceSats, syncedAt). Bypasses the 20-second SDK cache. Requires canModifyStoreSettings.
  • Sweep webhooksweepWebhookUrl in sweep settings causes Flint to POST a JSON payload after each successful sweep: storeId, idempotencyKey, txId, amountSats, feeSats, destination, destinationMode, trigger, completedAt.
  • Sweep configuration warningsGET .../spark/sweep now includes a warnings array. On mainnet, entries appear when thresholds fall below the recommended defaults calibrated on regtest.
  • scripts/setup-stores.sh — headless multi-store provisioning via Greenfield API with optional GPG seed encryption.
  • scripts/flint-logrotate.confcopytruncate-based logrotate config for sdk.log (Rust SDK holds the file handle open).
  • docs/railway.md — deployment guide: persistent volumes, environment variables, log rotation, health check.

v0.1.6.1 — Sweep webhook retry

  • Webhook delivery retries up to 3 times with exponential backoff (2 s, 4 s, 8 s). 5xx and network errors are retried; 4xx are not.

v0.1.6.2 — Webhook on failed sweeps

  • event: "sweep.failed" payload fired when a sweep attempt fails. Carries storeId, trigger, reason, and (when a record was created before the failure) idempotencyKey, amountSats, destination, destinationMode.
  • Success payload gains event: "sweep.swept" so receivers can route both event types on the same endpoint.

v0.1.7.0 — Sweep API improvements

  • GET .../spark/sweep/{idempotencyKey} — retrieve a specific sweep record by idempotency key. Requires canViewStoreSettings. Returns 404 with sweep-record-not-found when no record matches.
  • destinationAddress in POST .../spark/sweep — overrides the store's configured sweep destination for this one sweep. Validated against the server's chain. Not supported for EVM cross-chain sweep mode (returns 422).
  • force flag in POST .../spark/sweep** — bypasses the store's configured minimum sweep amount. The absolute on-chain protocol minimum (Constants.MinimumOnchainSendSats) still applies.

v0.1.8.0 — Server-level plugin update webhook

  • GET/PUT /api/v1/server/spark (requires canModifyServerSettings) — read or set updateWebhookUrl.
  • A daily background task compares the installed version against the BTCPay plugin registry. When a newer version is available, Flint POSTs { event: "plugin.update-available", pluginIdentifier, installedVersion, availableVersion } to the configured URL. Each version is notified at most once.

All endpoints are documented in swagger.json. All new behaviour is covered by the unit test suite (1109 passing, 86 skipped — env-gated Postgres/regtest tests).

@sethforprivacy

Copy link
Copy Markdown
Owner

Thanks so much for the PR! Will get this reviewed and think through it tomorrow.

@i2dor

i2dor commented Aug 25, 2026

Copy link
Copy Markdown
Author

Thanks a lot!

i2dor added 7 commits August 25, 2026 10:07
…s, scripts

- POST /api/v1/stores/{storeId}/spark/sync: forces wallet sync, returns
  balanceSats/walletRunning/syncedAt; bypasses the 20-second status cache
- Sweep webhook: SweepWebhookUrl field in SweepSettings + SweepSettingsInput;
  SparkSweepWebhookNotifier fires POST after SweepOutcomeKind.Swept with
  storeId, txId, amountSats, feeSats, destination, trigger, completedAt
- GET .../spark/sweep warnings array: mainnet advisory when thresholds fall
  below recommended defaults calibrated on regtest
- scripts/setup-stores.sh: headless multi-store provisioning via Greenfield
  API with optional GPG seed encryption
- scripts/flint-logrotate.conf: copytruncate rotation for sdk.log (Rust SDK
  holds the file open)
- docs/railway.md: persistent volumes, env vars, log rotation, health check

Tests: SyncBalance added to GreenfieldSparkStoreScopeTests; SparkBalanceSyncData
added to SparkApiContractTests.DocumentedModels; swagger.json updated with new
path and schema; Clone_copies_every_property guarded by SweepWebhookUrl in Clone()
SparkSweepWebhookNotifier now retries up to 3 times with 2s/4s/8s
exponential backoff when the webhook endpoint returns a 5xx response
or a network error. 4xx responses are not retried (permanent rejection).

- MaxAttempts = 4 (1 initial + 3 retries), delays injectable for testing
- StubHttpMessageHandler gains Sequence() and FailOnceThenOK() helpers
- SparkSweepWebhookNotifierTests: 6 tests covering all retry paths
SparkSweepWebhookNotifier now also fires when a sweep attempt fails.
The failure payload carries event: "sweep.failed", storeId, trigger,
reason, and optional record fields (idempotencyKey, amountSats,
destination) when a record was written before the failure occurred.

Success payload gains event: "sweep.swept" so receivers can route
both notification types on the same endpoint.

- Shared PostWithRetryAsync helper eliminates duplication
- StubHttpMessageHandler.Capture() for request-body inspection in tests
- 4 new tests covering failure delivery, payload, retry, and URL guard
…e flag (0.1.7.0)

- GET /api/v1/stores/{storeId}/spark/sweep/{idempotencyKey}: retrieve a
  specific sweep record by the idempotency key the engine assigned it.
  Requires canViewStoreSettings. Returns 404 with sweep-record-not-found
  when no record matches.

- destinationAddress in POST sweep request body: overrides the store's
  configured sweep destination for this sweep. Validated against the
  server's chain. Refused with 422 for EVM cross-chain sweep stores.

- force flag in POST sweep request body: bypasses the configured minimum
  sweep amount. The absolute on-chain protocol minimum
  (Constants.MinimumOnchainSendSats) still applies unconditionally.

All three fields documented in swagger.json and covered by the existing
contract test suite.
Adds a server-level webhook that fires once per day when a newer Flint
version is available on the BTCPay plugin registry.

New:
- SparkServerSettings: UpdateWebhookUrl + LastNotifiedUpdateVersion
- SparkPluginUpdateChecker (IPeriodicTask, daily): compares installed vs
  remote version via PluginService.GetRemotePlugins; POSTs
  'plugin.update-available' payload; records notified version to avoid
  repeat notifications

Endpoints (CanModifyServerSettings):
  GET  /api/v1/server/spark  - read current settings
  PUT  /api/v1/server/spark  - set/clear updateWebhookUrl

Webhook payload:
  { event, pluginIdentifier, installedVersion, availableVersion }

Swagger updated; store-scope harness passes null for server settings;
API contract tests updated to allow CanModifyServerSettings on /server/
paths; store-scope route count test narrowed to {storeId} routes.
@i2dor i2dor changed the title feat: balance sync endpoint, sweep webhook, config warnings, setup scripts (v0.1.6.0) feat: sweep webhooks, balance sync, sweep API improvements, server update webhook (v0.1.6.0-v0.1.8.0) Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants