routerrpc: add clarifying docs for the intercepted forward
What changed, and why it matters
This commit only adds documentation comments to the router RPC API. It explains that HTLC interception requests may be replayed after reconnect and clarifies what happens when an HTLC moves on-chain. No code behavior was changed.
No action required. This is a documentation-only change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff is purely additive documentation in router.proto, the generated router.pb.go, and the generated router.swagger.json. It documents idempotency expectations for interceptor clients and the semantics of auto_fail_height and ForwardHtlcInterceptResponse actions for on-chain HTLCs. There are no logic, validation, or behavior changes.
Changed components
lnrpc/routerrpc/router.protolnrpc/routerrpc/router.pb.golnrpc/routerrpc/router.swagger.jsonInspect captured patch +32 / −6
diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go
index 3008177..78f21e2 100644
--- a/lnrpc/routerrpc/router.pb.go
+++ b/lnrpc/routerrpc/router.pb.go
@@ -2812,6 +2812,10 @@ type ForwardHtlcInterceptRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The key of this forwarded htlc. It defines the incoming channel id and
// the index in this channel.
+ //
+ // Interceptor clients should handle requests for the same circuit key
+ // idempotently. Requests may be replayed after reconnect, and an htlc that was
+ // previously offered off-chain may be offered again after it moves on-chain.
IncomingCircuitKey *CircuitKey `protobuf:"bytes,1,opt,name=incoming_circuit_key,json=incomingCircuitKey,proto3" json:"incoming_circuit_key,omitempty"`
// The incoming htlc amount.
IncomingAmountMsat uint64 `protobuf:"varint,5,opt,name=incoming_amount_msat,json=incomingAmountMsat,proto3" json:"incoming_amount_msat,omitempty"`
@@ -2834,7 +2838,8 @@ type ForwardHtlcInterceptRequest struct {
// The onion blob for the next hop
OnionBlob []byte `protobuf:"bytes,9,opt,name=onion_blob,json=onionBlob,proto3" json:"onion_blob,omitempty"`
// The block height at which this htlc will be auto-failed to prevent the
- // channel from force-closing.
+ // channel from force-closing. For on-chain htlcs, this field is the
+ // settlement deadline instead and no automatic fail-back is attempted.
AutoFailHeight int32 `protobuf:"varint,10,opt,name=auto_fail_height,json=autoFailHeight,proto3" json:"auto_fail_height,omitempty"`
// The custom records of the peer's incoming p2p wire message.
InWireCustomRecords map[uint64][]byte `protobuf:"bytes,11,rep,name=in_wire_custom_records,json=inWireCustomRecords,proto3" json:"in_wire_custom_records,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
@@ -2957,6 +2962,14 @@ func (x *ForwardHtlcInterceptRequest) GetInWireCustomRecords() map[uint64][]byte
// field modifications.
// - `Reject`: Fail the htlc backwards.
// - `Settle`: Settle this htlc with a given preimage.
+//
+// Once the incoming channel has force-closed and the HTLC is being resolved
+// on-chain (see auto_fail_height), only `Settle` has any effect. The HTLC can no
+// longer be resumed or failed back off-chain, so `Resume`, `ResumeModified`, and
+// `Fail` return a stream-terminating error. The HTLC stays held until it is
+// settled with a preimage, the on-chain resolver completes, or it expires
+// on-chain. Clients should reconnect to receive any held HTLCs that remain
+// unresolved.
type ForwardHtlcInterceptResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// *
diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto
index a8aebe9..7b422bd 100644
--- a/lnrpc/routerrpc/router.proto
+++ b/lnrpc/routerrpc/router.proto
@@ -910,6 +910,10 @@ message ForwardHtlcInterceptRequest {
/*
The key of this forwarded htlc. It defines the incoming channel id and
the index in this channel.
+
+ Interceptor clients should handle requests for the same circuit key
+ idempotently. Requests may be replayed after reconnect, and an htlc that was
+ previously offered off-chain may be offered again after it moves on-chain.
*/
CircuitKey incoming_circuit_key = 1;
@@ -944,7 +948,8 @@ message ForwardHtlcInterceptRequest {
bytes onion_blob = 9;
// The block height at which this htlc will be auto-failed to prevent the
- // channel from force-closing.
+ // channel from force-closing. For on-chain htlcs, this field is the
+ // settlement deadline instead and no automatic fail-back is attempted.
int32 auto_fail_height = 10;
// The custom records of the peer's incoming p2p wire message.
@@ -959,6 +964,14 @@ forward. The caller can choose either to:
field modifications.
- `Reject`: Fail the htlc backwards.
- `Settle`: Settle this htlc with a given preimage.
+
+Once the incoming channel has force-closed and the HTLC is being resolved
+on-chain (see auto_fail_height), only `Settle` has any effect. The HTLC can no
+longer be resumed or failed back off-chain, so `Resume`, `ResumeModified`, and
+`Fail` return a stream-terminating error. The HTLC stays held until it is
+settled with a preimage, the on-chain resolver completes, or it expires
+on-chain. Clients should reconnect to receive any held HTLCs that remain
+unresolved.
*/
message ForwardHtlcInterceptResponse {
/**
diff --git a/lnrpc/routerrpc/router.swagger.json b/lnrpc/routerrpc/router.swagger.json
index 121c50d..88b4761 100644
--- a/lnrpc/routerrpc/router.swagger.json
+++ b/lnrpc/routerrpc/router.swagger.json
@@ -111,7 +111,7 @@
"parameters": [
{
"name": "body",
- "description": "*\nForwardHtlcInterceptResponse enables the caller to resolve a previously hold\nforward. The caller can choose either to:\n- `Resume`: Execute the default behavior (usually forward).\n- `ResumeModified`: Execute the default behavior (usually forward) with HTLC\nfield modifications.\n- `Reject`: Fail the htlc backwards.\n- `Settle`: Settle this htlc with a given preimage. (streaming inputs)",
+ "description": "*\nForwardHtlcInterceptResponse enables the caller to resolve a previously hold\nforward. The caller can choose either to:\n- `Resume`: Execute the default behavior (usually forward).\n- `ResumeModified`: Execute the default behavior (usually forward) with HTLC\nfield modifications.\n- `Reject`: Fail the htlc backwards.\n- `Settle`: Settle this htlc with a given preimage.\n\nOnce the incoming channel has force-closed and the HTLC is being resolved\non-chain (see auto_fail_height), only `Settle` has any effect. The HTLC can no\nlonger be resumed or failed back off-chain, so `Resume`, `ResumeModified`, and\n`Fail` return a stream-terminating error. The HTLC stays held until it is\nsettled with a preimage, the on-chain resolver completes, or it expires\non-chain. Clients should reconnect to receive any held HTLCs that remain\nunresolved. (streaming inputs)",
"in": "body",
"required": true,
"schema": {
@@ -1552,7 +1552,7 @@
"properties": {
"incoming_circuit_key": {
"$ref": "#/definitions/routerrpcCircuitKey",
- "description": "The key of this forwarded htlc. It defines the incoming channel id and\nthe index in this channel."
+ "description": "The key of this forwarded htlc. It defines the incoming channel id and\nthe index in this channel.\n\nInterceptor clients should handle requests for the same circuit key\nidempotently. Requests may be replayed after reconnect, and an htlc that was\npreviously offered off-chain may be offered again after it moves on-chain."
},
"incoming_amount_msat": {
"type": "string",
@@ -1600,7 +1600,7 @@
"auto_fail_height": {
"type": "integer",
"format": "int32",
- "description": "The block height at which this htlc will be auto-failed to prevent the\nchannel from force-closing."
+ "description": "The block height at which this htlc will be auto-failed to prevent the\nchannel from force-closing. For on-chain htlcs, this field is the\nsettlement deadline instead and no automatic fail-back is attempted."
},
"in_wire_custom_records": {
"type": "object",
@@ -1656,7 +1656,7 @@
"description": "Any custom records that should be set on the p2p wire message message of\nthe resumed HTLC. This field is ignored if the action is not\nRESUME_MODIFIED.\n\nThis map will merge with the existing set of custom records (if any),\nreplacing any conflicting types. Note that there currently is no support\nfor deleting existing custom records (they can only be replaced)."
}
},
- "description": "*\nForwardHtlcInterceptResponse enables the caller to resolve a previously hold\nforward. The caller can choose either to:\n- `Resume`: Execute the default behavior (usually forward).\n- `ResumeModified`: Execute the default behavior (usually forward) with HTLC\nfield modifications.\n- `Reject`: Fail the htlc backwards.\n- `Settle`: Settle this htlc with a given preimage."
+ "description": "*\nForwardHtlcInterceptResponse enables the caller to resolve a previously hold\nforward. The caller can choose either to:\n- `Resume`: Execute the default behavior (usually forward).\n- `ResumeModified`: Execute the default behavior (usually forward) with HTLC\nfield modifications.\n- `Reject`: Fail the htlc backwards.\n- `Settle`: Settle this htlc with a given preimage.\n\nOnce the incoming channel has force-closed and the HTLC is being resolved\non-chain (see auto_fail_height), only `Settle` has any effect. The HTLC can no\nlonger be resumed or failed back off-chain, so `Resume`, `ResumeModified`, and\n`Fail` return a stream-terminating error. The HTLC stays held until it is\nsettled with a preimage, the on-chain resolver completes, or it expires\non-chain. Clients should reconnect to receive any held HTLCs that remain\nunresolved."
},
"routerrpcGetMissionControlConfigResponse": {
"type": "object",
Why this scored 15/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.