What changed, and why it matters
This commit adds a new command-line tool called `deletefwdhistory` to LND's `lncli` utility. It lets node operators permanently delete old payment forwarding history records from their own node database, either by age (e.g., older than 30 days) or by a specific Unix timestamp. The command asks for confirmation unless the user passes `--force`. It is a privacy/data-retention feature, not a network protocol change, and does not by itself create a vulnerability. The main risk is accidental or malicious local data loss if an attacker already has access to run `lncli` commands.
No immediate security patch is required. Operators should protect `lncli` access (macaroons, RPC TLS, OS permissions) because any party that can run `lncli deletefwdhistory --force` can irreversibly delete local forwarding records. Consider logging or auditing use of destructive commands and ensuring backups/compaction policies are in place.
Security signals we found
Destructive local operation gated by interactive confirmation and --force flag
Wraps pre-existing DeleteForwardingHistory RPC; no new RPC authorization logic shown
No input validation or sanitization visible in the CLI layer beyond requiring exactly one of --age/--before
Potential for accidental/malicious data loss if an attacker can execute lncli with --force
Evidence from the diff
The patch adds deletefwdhistory to cmd/commands/cmd_payments.go and registers it in cmd/commands/main.go. It wraps the existing routerrpc.DeleteForwardingHistory RPC, accepting --age (Go/custom duration string) or --before (uint64 Unix seconds), with a --force flag to bypass an interactive confirmation. The CLI builds a DeleteForwardingHistoryRequest, sends it to the router RPC, and prints the JSON response. There is no new server-side logic, privilege model, or network exposure in this diff; it is purely a client command exposing an already-present RPC.
Changed components
lncli command-line clientcmd/commands/cmd_payments.gocmd/commands/main.gorouterrpc.DeleteForwardingHistory RPC client wrapperInspect captured patch +121 / −0
diff --git a/cmd/commands/cmd_payments.go b/cmd/commands/cmd_payments.go
index 9e46d36..be7dc3d 100644
--- a/cmd/commands/cmd_payments.go
+++ b/cmd/commands/cmd_payments.go
@@ -1942,6 +1942,126 @@ func deletePayments(ctx *cli.Context) error {
return nil
}
+var deleteFwdHistoryCommand = cli.Command{
+ Name: "deletefwdhistory",
+ Category: "Payments",
+ Usage: "Delete old forwarding history for privacy.",
+ ArgsUsage: "age | before",
+ Description: `
+ Deletes all forwarding history events with a timestamp at or before a
+ specified time. This is useful for implementing data retention policies
+ for privacy purposes. The command permanently removes old forwarding
+ events from the database and returns statistics about the deletion
+ including total fees earned.
+
+ Time can be specified in two ways:
+ 1. Relative age (standard Go or custom units): e.g., "-1w", "-24h",
+ "-1M"
+ 2. Absolute Unix timestamp: e.g., "1640995200"
+
+ Supported relative time units:
+ - Standard Go: ns, us/µs, ms, s, m, h (e.g., "-24h", "-1.5h")
+ - Custom units: d (days), w (weeks), M (months=30.44d),
+ y (years=365.25d)
+
+ Examples:
+ # Delete events from ~1 month ago and earlier:
+ lncli deletefwdhistory --age="-1M"
+
+ # Delete events from ~1 month ago and earlier
+ lncli deletefwdhistory --age="-720h"
+
+ # Delete events at or before Jan 1, 2022:
+ lncli deletefwdhistory --before=1640995200
+
+ NOTE: As with deletepayments, removing events from the database frees up
+ disk space within bbolt, but that space is only reclaimed after
+ compacting the database. Consider enabling auto-compaction
+ (db.bolt.auto-compact=true).
+
+ WARNING: This operation is irreversible. Deleted forwarding history
+ cannot be recovered. A minimum age validation is enforced to prevent
+ accidental deletion of very recent data.
+ `,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "age",
+ Usage: "delete events at or before this age in the " +
+ "past " +
+ `(e.g., "-1w", "-1M", "-24h", "-720h")`,
+ },
+ cli.Uint64Flag{
+ Name: "before",
+ Usage: "delete events at or before this Unix " +
+ "timestamp (seconds)",
+ },
+ cli.BoolFlag{
+ Name: "force, f",
+ Usage: "skip the confirmation prompt, useful for " +
+ "scripts",
+ },
+ },
+ Action: actionDecorator(deleteFwdHistory),
+}
+
+func deleteFwdHistory(ctx *cli.Context) error {
+ ctxc := getContext()
+ conn := getClientConn(ctx, false)
+ defer conn.Close()
+
+ client := routerrpc.NewRouterClient(conn)
+
+ // Show command help if no arguments or flags are provided.
+ if ctx.NArg() > 0 || (!ctx.IsSet("age") && !ctx.IsSet("before")) {
+ _ = cli.ShowCommandHelp(ctx, "deletefwdhistory")
+ return nil
+ }
+
+ // User must specify exactly one of age or until.
+ if ctx.IsSet("age") && ctx.IsSet("before") {
+ return fmt.Errorf("cannot use both --age and --before; " +
+ "specify one time parameter")
+ }
+
+ req := &routerrpc.DeleteForwardingHistoryRequest{}
+
+ //nolint:ll
+ switch {
+ case ctx.IsSet("age"):
+ req.TimeSpec = &routerrpc.DeleteForwardingHistoryRequest_DeleteBeforeDuration{
+ DeleteBeforeDuration: ctx.String("age"),
+ }
+
+ case ctx.IsSet("before"):
+ req.TimeSpec = &routerrpc.DeleteForwardingHistoryRequest_DeleteBeforeTime{
+ DeleteBeforeTime: ctx.Uint64("before"),
+ }
+ }
+
+ if !ctx.Bool("force") {
+ if !promptForConfirmation("WARNING: This operation is " +
+ "irreversible and will permanently delete forwarding " +
+ "history.\nProceed? (yes/no): ") {
+
+ fmt.Println("Operation cancelled.")
+ return nil
+ }
+ }
+
+ fmt.Println("Deleting forwarding history, this may take a while...")
+
+ resp, err := client.DeleteForwardingHistory(ctxc, req)
+ if err != nil {
+ return fmt.Errorf(
+ "failed to delete forwarding history: %w", err,
+ )
+ }
+
+ printJSON(resp)
+
+ return nil
+}
+
var estimateRouteFeeCommand = cli.Command{
Name: "estimateroutefee",
Category: "Payments",
diff --git a/cmd/commands/main.go b/cmd/commands/main.go
index a11b63b..dc9b993 100644
--- a/cmd/commands/main.go
+++ b/cmd/commands/main.go
@@ -497,6 +497,7 @@ func Main() {
feeReportCommand,
updateChannelPolicyCommand,
forwardingHistoryCommand,
+ deleteFwdHistoryCommand,
exportChanBackupCommand,
verifyChanBackupCommand,
restoreChanBackupCommand,
Why this scored 23/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.