{"id":19352182,"url":"https://github.com/callebtc/electronwall","last_synced_at":"2025-12-16T00:15:59.971Z","repository":{"id":39707557,"uuid":"497001034","full_name":"callebtc/electronwall","owner":"callebtc","description":"A tiny firewall for LND that allows or denies channel openings and payment routings.","archived":false,"fork":false,"pushed_at":"2024-09-13T17:08:23.000Z","size":1511,"stargazers_count":42,"open_issues_count":3,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-02T09:51:15.613Z","etag":null,"topics":["bitcoin","lightning","security"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/callebtc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-27T13:09:07.000Z","updated_at":"2024-12-21T16:43:44.000Z","dependencies_parsed_at":"2024-11-10T04:38:30.998Z","dependency_job_id":"92633b3c-9116-4596-80a5-fa4b15745ec7","html_url":"https://github.com/callebtc/electronwall","commit_stats":{"total_commits":90,"total_committers":2,"mean_commits":45.0,"dds":"0.022222222222222254","last_synced_commit":"929fae4719daf75364597d6566986d5b94496e67"},"previous_names":["callebtc/lnd_whitelist"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/callebtc%2Felectronwall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/callebtc%2Felectronwall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/callebtc%2Felectronwall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/callebtc%2Felectronwall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/callebtc","download_url":"https://codeload.github.com/callebtc/electronwall/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250391191,"owners_count":21422855,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitcoin","lightning","security"],"created_at":"2024-11-10T04:38:25.826Z","updated_at":"2025-12-16T00:15:59.916Z","avatar_url":"https://github.com/callebtc.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \t\u003cimg width=\"300\" alt=\"Logo of electronwall.\" src=\"https://github.com/callebtc/electronwall/raw/main/resources/logo.png\" \u003e\n\u003c/p\u003e \n\nA tiny firewall for LND that can filter Lightning **channel opening requests** and **HTLC forwards** based on your custom rules. \n\nelectronwall uses filter lists that either allow (allowlist) or reject (denylist) events from a list of node public keys for channel openings, or channel IDs and channel pairs for payment routings.\n\nYou can also write [custom rules](#programmable-rules) using a builtin Javascript engine. \n\n*Note: electronwall is in early development and highly experimental software. Use at your own risk. Please report issues.*\n\n![electronwall0 4](https://user-images.githubusercontent.com/93376500/187682152-add9b2ee-7d84-4582-b5fd-3eb1a0fc7767.jpg)\n\n## Install\n\n### From source\nBuild from source (you may need to install go for this):\n\n```bash\ngit clone https://github.com/callebtc/electronwall.git\ncd electronwall\ngo build .\n```\n\n### Binaries\n\nYou can download a binary for your system [here](https://github.com/callebtc/electronwall/releases). You'll still need a [config file](https://github.com/callebtc/electronwall/blob/main/config.yaml.example).\n\n## Config\nEdit `config.yaml.example` and rename to `config.yaml`. \n\n## Run\n\n```bash\n./electronwall\n```\n\n# Rules\n\n## Passthrough\n\nThe `passthrough` option is a mode for both `ChannelMode` and `ForwardMode` in the `config.yaml` file. When set to `passthrough`, electronwall will not apply any allowlist, denylist, or programmable rules to channel open requests or HTLC forwards. Instead, it will simply pass through all requests without any checks.\n\n## Allowlist and denylist\n\nAllowlist and denylist rules are set in `config.yaml` under the appropriate keys. See the [example](config.yaml.example) config. \n\n## Programmable rules\n\nelectronwall has a Javascript engine called [goja](https://github.com/dop251/goja) that allows you to set custom rules. Note that you can only use pure Javascript (ECMAScript), you can't import a ton of other dependcies like with web applications.\n\nRules are saved in the `rules/` directory. There are two files, one for channel open requests `ChannelAccept.js` and one for HTLC forwards `HtlcForward.js`.\n\nelectronwall passes [contextual information](#contextual-information) to the Javascript engine that you can use to create rich rules. See below for a list of objects that are currently supported.\n\n Here is one rather complex rule for channel accept decisions in `ChannelAccept.js` for demonstration purposes:\n\n ```javascript\n // only channels \u003e 0.75 Msat\nChannelAccept.Event.FundingAmt \u003e= 750000 \u0026\u0026 \n// nodes with high 1ML availability score\nChannelAccept.OneMl.Noderank.Availability \u003e 100 \u0026\u0026\n// nodes with a low enough 1ML age rank\nChannelAccept.OneMl.Noderank.Age \u003c 10000 \u0026\u0026\n( \n    // only nodes with Amboss contact data\n    ChannelAccept.Amboss.Socials.Info.Email ||\n    ChannelAccept.Amboss.Socials.Info.Twitter ||\n    ChannelAccept.Amboss.Socials.Info.Telegram \n) \u0026\u0026\n(\n    // elitist: either nodes with amboss prime\n    ChannelAccept.Amboss.Amboss.IsPrime ||\n    // or nodes with high-ranking capacity\n    ChannelAccept.Amboss.GraphInfo.Metrics.CapacityRank \u003c 1000 ||\n    // or nodes with high-ranking channel count\n    ChannelAccept.Amboss.GraphInfo.Metrics.ChannelsRank \u003c 1000\n)\n ```\n\n Here is an example `HtlcForward.js` for deciding on HTLC forwards:\n ```javascript\n if (\n    // only forward amounts larger than 100 sat\n    HtlcForward.Event.OutgoingAmountMsat \u003e= 100000\n) { true } else { false }\n ```\n\n### Contextual information\nHere is a list of all objects that are passed to the Javascript engine. You need to look at the structure of these objects in order to use them in a custom rule like the example above. \n\n#### LND: ChannelAcceptRequest `*.Event`\n```go\ntype ChannelAcceptRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The pubkey of the node that wishes to open an inbound channel.\n\tNodePubkey []byte `protobuf:\"bytes,1,opt,name=node_pubkey,json=nodePubkey,proto3\" json:\"node_pubkey,omitempty\"`\n\t// The hash of the genesis block that the proposed channel resides in.\n\tChainHash []byte `protobuf:\"bytes,2,opt,name=chain_hash,json=chainHash,proto3\" json:\"chain_hash,omitempty\"`\n\t// The pending channel id.\n\tPendingChanId []byte `protobuf:\"bytes,3,opt,name=pending_chan_id,json=pendingChanId,proto3\" json:\"pending_chan_id,omitempty\"`\n\t// The funding amount in satoshis that initiator wishes to use in the\n\t// channel.\n\tFundingAmt uint64 `protobuf:\"varint,4,opt,name=funding_amt,json=fundingAmt,proto3\" json:\"funding_amt,omitempty\"`\n\t// The push amount of the proposed channel in millisatoshis.\n\tPushAmt uint64 `protobuf:\"varint,5,opt,name=push_amt,json=pushAmt,proto3\" json:\"push_amt,omitempty\"`\n\t// The dust limit of the initiator's commitment tx.\n\tDustLimit uint64 `protobuf:\"varint,6,opt,name=dust_limit,json=dustLimit,proto3\" json:\"dust_limit,omitempty\"`\n\t// The maximum amount of coins in millisatoshis that can be pending in this\n\t// channel.\n\tMaxValueInFlight uint64 `protobuf:\"varint,7,opt,name=max_value_in_flight,json=maxValueInFlight,proto3\" json:\"max_value_in_flight,omitempty\"`\n\t// The minimum amount of satoshis the initiator requires us to have at all\n\t// times.\n\tChannelReserve uint64 `protobuf:\"varint,8,opt,name=channel_reserve,json=channelReserve,proto3\" json:\"channel_reserve,omitempty\"`\n\t// The smallest HTLC in millisatoshis that the initiator will accept.\n\tMinHtlc uint64 `protobuf:\"varint,9,opt,name=min_htlc,json=minHtlc,proto3\" json:\"min_htlc,omitempty\"`\n\t// The initial fee rate that the initiator suggests for both commitment\n\t// transactions.\n\tFeePerKw uint64 `protobuf:\"varint,10,opt,name=fee_per_kw,json=feePerKw,proto3\" json:\"fee_per_kw,omitempty\"`\n\t//\n\t//The number of blocks to use for the relative time lock in the pay-to-self\n\t//output of both commitment transactions.\n\tCsvDelay uint32 `protobuf:\"varint,11,opt,name=csv_delay,json=csvDelay,proto3\" json:\"csv_delay,omitempty\"`\n\t// The total number of incoming HTLC's that the initiator will accept.\n\tMaxAcceptedHtlcs uint32 `protobuf:\"varint,12,opt,name=max_accepted_htlcs,json=maxAcceptedHtlcs,proto3\" json:\"max_accepted_htlcs,omitempty\"`\n\t// A bit-field which the initiator uses to specify proposed channel\n\t// behavior.\n\tChannelFlags uint32 `protobuf:\"varint,13,opt,name=channel_flags,json=channelFlags,proto3\" json:\"channel_flags,omitempty\"`\n\t// The commitment type the initiator wishes to use for the proposed channel.\n\tCommitmentType CommitmentType `protobuf:\"varint,14,opt,name=commitment_type,json=commitmentType,proto3,enum=lnrpc.CommitmentType\" json:\"commitment_type,omitempty\"`\n}\n\n```\n\n#### 1ML node information `*.OneMl`\n```go\ntype OneML_NodeInfoResponse struct {\n\tLastUpdate int    `json:\"last_update\"`\n\tPubKey     string `json:\"pub_key\"`\n\tAlias      string `json:\"alias\"`\n\tAddresses  []struct {\n\t\tNetwork string `json:\"network\"`\n\t\tAddr    string `json:\"addr\"`\n\t} `json:\"addresses\"`\n\tColor        string `json:\"color\"`\n\tCapacity     int    `json:\"capacity\"`\n\tChannelcount int    `json:\"channelcount\"`\n\tNoderank     struct {\n\t\tCapacity     int `json:\"capacity\"`\n\t\tChannelcount int `json:\"channelcount\"`\n\t\tAge          int `json:\"age\"`\n\t\tGrowth       int `json:\"growth\"`\n\t\tAvailability int `json:\"availability\"`\n\t} `json:\"noderank\"`\n}\n```\n#### Amboss node information `*.Amboss`\n```go\ntype Amboss_NodeInfoResponse struct {\n\tSocials struct {\n\t\tInfo struct {\n\t\t\tEmail            string      `json:\"email\"`\n\t\t\tTelegram         string      `json:\"telegram\"`\n\t\t\tTwitter          string      `json:\"twitter\"`\n\t\t\tLightningAddress string      `json:\"lightning_address\"`\n\t\t\tWebsite          string      `json:\"website\"`\n\t\t\tPubkey           string      `json:\"pubkey\"`\n\t\t\tMinChannelSize   interface{} `json:\"minChannelSize\"`\n\t\t\tMessage          string      `json:\"message\"`\n\t\t\tTwitterVerified  bool        `json:\"twitter_verified\"`\n\t\t\tUpdated          time.Time   `json:\"updated\"`\n\t\t} `json:\"info\"`\n\t} `json:\"socials\"`\n\tGraphInfo struct {\n\t\tLastUpdate time.Time `json:\"last_update\"`\n\t\tMetrics    struct {\n\t\t\tCapacity     string `json:\"capacity\"`\n\t\t\tCapacityRank int    `json:\"capacity_rank\"`\n\t\t\tChannels     int    `json:\"channels\"`\n\t\t\tChannelsRank int    `json:\"channels_rank\"`\n\t\t} `json:\"metrics\"`\n\t\tNode struct {\n\t\t\tAddresses []struct {\n\t\t\t\tAddr   string `json:\"addr\"`\n\t\t\t\tIPInfo struct {\n\t\t\t\t\tCity        string `json:\"city\"`\n\t\t\t\t\tCountry     string `json:\"country\"`\n\t\t\t\t\tCountryCode string `json:\"country_code\"`\n\t\t\t\t} `json:\"ip_info\"`\n\t\t\t\tNetwork string `json:\"network\"`\n\t\t\t} `json:\"addresses\"`\n\t\t\tLastUpdate int    `json:\"last_update\"`\n\t\t\tColor      string `json:\"color\"`\n\t\t\tFeatures   []struct {\n\t\t\t\tFeatureID  string `json:\"feature_id\"`\n\t\t\t\tIsKnown    bool   `json:\"is_known\"`\n\t\t\t\tIsRequired bool   `json:\"is_required\"`\n\t\t\t\tName       string `json:\"name\"`\n\t\t\t} `json:\"features\"`\n\t\t} `json:\"node\"`\n\t} `json:\"graph_info\"`\n\tAmboss struct {\n\t\tIsFavorite            bool `json:\"is_favorite\"`\n\t\tIsPrime               bool `json:\"is_prime\"`\n\t\tNumberFavorites       int  `json:\"number_favorites\"`\n\t\tNewChannelGossipDelta struct {\n\t\t\tMean string `json:\"mean\"`\n\t\t\tSd   string `json:\"sd\"`\n\t\t} `json:\"new_channel_gossip_delta\"`\n\t\tNotifications struct {\n\t\t\tNumberSubscribers int `json:\"number_subscribers\"`\n\t\t} `json:\"notifications\"`\n\t} `json:\"amboss\"`\n}\n```\n\n#### Network information `*.Network`\n*TBD*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcallebtc%2Felectronwall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcallebtc%2Felectronwall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcallebtc%2Felectronwall/lists"}