Skip to content

Add outflow monitor design document - #665

Open
RodriFS wants to merge 1 commit into
mainfrom
outflow-monitor-design
Open

RodriFS wants to merge 1 commit into
mainfrom
outflow-monitor-design

Conversation

@RodriFS

@RodriFS RodriFS commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Today, NodeGuard does not alert when funds leave a NodeGuard wallet without a matching request, or with a request that no legitimate user or API token created. This PR adds a design document for an outflow monitor that detects these cases and alerts through Grafana. The design also adds HMAC signatures on request and template PSBT rows, so a direct database write cannot fake a justified outflow. This PR contains only the design; implementation follows in three PRs.

Scope

  • Documentation only. No code, schema, or configuration changes.
  • The document proposes the delivery plan: PR 1 closes audit gaps, PR 2 adds the monitor, PR 3 adds signed records and the checks before signing. A KMS signer comes later.

Validation

No build or tests ran, because this PR changes only Markdown.

Suggested review order

  1. Goal, scope and threat model (§1–§3). Confirm what counts as a justified outflow and which attacks are in and out of scope.
  2. Detection and matching (§4). Check the NBXplorer subscription, the startup scan that replaces persisted state, and the grace period for the TxId race.
  3. AuditLog justification (§5). Check the user and API paths, and the audit gaps that PR 1 must close.
  4. Signed records (§6). Check the signer interface, the four request columns, the template PSBT signature, and the one-off signing of existing rows.
  5. Alerting and delivery (§7–§9). Check the PR split and the test cases.
  6. Checks before signing (§11). Check how withdrawals and channel opens are blocked before the internal wallet signs, including the SIGHASH_NONE window on channel opens.

@RodriFS
RodriFS force-pushed the outflow-monitor-design branch from 86748d0 to 6c1f21a Compare November 4, 2026 15:59

@imclvr imclvr Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember we discussed to have this monitoring outside NG. Also, in https://github.com/Lunar-Rails/ep-backend/pull/2338/changes#r4142040308 you mentioned ep-recheck (I would name it differently btw like ep-audit or whatever)

My point here (a bit of nitpick) is, this document currently belongs to NodeGuard. Perhaps either we create such repo apart of NG. Either on Notion, in ep-backend or even creating the new component agreeing first to have it. As you can see, there is more than one feature to place on it so probably it will be a good thing if we all agree on that.

WDYT?

@daliclovr pinging you as well as we need consensus on: "Should we create an ep-audit/ep-recheck/ep-whatever to hold accounting tasks"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that we discussed to have this monitor outside ep-backend, not outside nodeguard. Specifically because onchain outflows belong to nodeguard

Comment on lines +13 to +14
2. **Unjustified** — a matching request exists, but it was not created by a logged-in user (UI) or by
the backend through the authenticated gRPC API.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Killer feature 🎯


Raise an alert when money leaves a NodeGuard wallet and either:

1. **Unmatched** — the transaction does not correspond to any request in the NodeGuard database, or

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about unsolicited bumps, that is, fee bumps done outside NG? That's a risk as well because we would lose the track of the tx

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea!

Comment on lines +41 to +50
| Keys used outside NodeGuard (e.g. signers co-sign a tx directly) | §4 matching — no request |
| Request row inserted/edited directly in the DB | §5 audit cross-check, §6 signatures |
| Request `TxId` edited to point at a rogue tx | §4 output check, §6 TxId signature |
| Request row *and* audit row forged directly in the DB | §6 signatures only |
| Request backdated to fall under an exemption | No date-based exemptions exist (§5, §6.5) |
| Withdrawal template PSBT outputs edited before anyone signs | §6 template signature; blocked before signing by §11 |
| Change shrunk to inflate the fee | Withdrawals: §6 template signature, §11. Channel opens: §11 fee rate limit |
| Channel-open outputs edited after human approval (SIGHASH_NONE) | §11 checks before the internal wallet signs |
| Stolen API token used to request withdrawals | **Not covered** — the request is created through the legitimate gRPC path and counts as justified |
| Attacker with code execution on the app host | **Not covered** — they can create legitimate-looking requests and get them signed. KMS (§6.6) only stops them from keeping the key after they lose access |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about this scenario that comes to my mind?

No activity on NG's withdrawal request, but we catch a tx with different hash, recently mined with same inputs than a NG withdrawal request?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The job monitors the mempool and the blockchain, not the withdrawal requests. So as soon an input from one of our tracked wallets is in the mempool, nbxplorer will launch an event and we'll do all the needed checks, so that's also contemplated in this feature.

A long-running job, `WalletOutflowSubscriptorJob`, started at boot like `NodeSubscriptorJob`, using
`IDbContextFactory<ApplicationDbContext>`.

- **Startup scan.** For every NodeGuard wallet, `GetTransactionsAsync` from NBXplorer and evaluate

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to https://github.com/Elenpay/NodeGuard/pull/665/changes#r4478512055 , I think it's also worthy to scan every tx on mined blocks.

Not a blocker for initial release OFC but something to be added to the checklist.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment this feature trusts nbxplorer to tell us with GetTransactionsAsync, which are the outgoing transactions in each wallet. But I believe you are right, we should't trust nbxplorer and we should scan all transactions. Will add it as a next steps thing

This branch has not been deployed

No deployments
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