# Improved Sentinel Features - Enable Discovery & StaticNodes variable in config.json

**URL:** https://forum.hypercore.one/t/improved-sentinel-features-enable-discovery-staticnodes-variable-in-config-json/594
**Category:** Development
**Created:** [April 5, 2025, 11:10am UTC](https://forum.hypercore.one/t/improved-sentinel-features-enable-discovery-staticnodes-variable-in-config-json/594 "2025-04-05T11:10:18Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![0x3639](https://forum.hypercore.one/user_avatar/forum.hypercore.one/0x3639/32/33_2.png) [@0x3639](https://forum.hypercore.one/u/0x3639)
#### Post date: [April 5, 2025, 11:10am UTC](https://forum.hypercore.one/t/improved-sentinel-features-enable-discovery-staticnodes-variable-in-config-json/594/1 "2025-04-05T11:10:18Z")

</div>

I’m looking at ways to improve the sentinel / pillar setup where a user runs their own hardware / firewall and has more control over their network topology. Specifically, I would like to make the `Discovery` and `StaticNodes` variables configurable in the `config.json` file. The goal is to allow a user to setup a sentinel and pillar where the pillar sits in a VPC and it only connects to the sentinel for syncing. That way a pillar can be 100% isolated from the internet. Today, the Sentrify setup from Moon simply blocks access to all endpoints, but for an approved endpoint, at the local firewall level. This method works, but I suspect it would be more efficient to take advantage of the `StaticNodes` variable.

With the help of Cursor, I’ve laid out the next steps to implement this. @georgezgeorgez @vilkris do these steps look appropriate and are there any downstream issues you see with implementing this?

* * *

# Making Discovery and StaticNodes Configurable via config.json

Currently, the `discovery` and `staticNodes` settings in the Zenon node are hardcoded and not configurable through `config.json`. This post explains the changes needed to make these settings configurable.

## Current Implementation

Currently, these settings are hardcoded in `node/node.go`:

```go
node.server = &p2p.Server{
    // ...
    Discovery: true, // Always enabled
    StaticNodes: nil, // Always empty
    // ...
}

```

The codebase already has robust node parsing functionality in `p2p/discover/node.go` that can parse node URLs in the format:

```auto
enode://<node-id>@<ip>:<port>

```

This is demonstrated by the existing `Nodes()` method in `p2p/config.go` which parses seeders:

```go
func (c *Net) Nodes() ([]*discover.Node, error) {
    var err error
    nodes := make([]*discover.Node, len(c.Seeders))
    for index, nodeAddress := range c.Seeders {
        nodes[index], err = discover.ParseNode(nodeAddress)
        if err != nil {
            return nil, err
        }
    }
    return nodes, nil
}

```

## Required Changes

### 1. Update the NetConfig Structure

First, we need to add the new fields to the `NetConfig` structure in `node/config.go`:

```go
type NetConfig struct {
    ListenHost string
    ListenPort int

    MinPeers int
    MinConnectedPeers int
    MaxPeers int
    MaxPendingPeers int

    Discovery bool // Add this field
    StaticNodes []string // Add this field
    Seeders []string
}

```

### 2. Update the Server Configuration

Modify the `makeNetConfig` function in `node/config.go` to include the new fields:

```go
func (c *Config) makeNetConfig() *p2p.Net {
    networkDataDir := filepath.Join(c.DataPath, p2p.DefaultNetDirName)
    privateKeyFile := filepath.Join(c.DataPath, p2p.DefaultNetPrivateKeyFile)

    return &p2p.Net{
        PrivateKeyFile: privateKeyFile,
        MaxPeers: c.Net.MaxPeers,
        MaxPendingPeers: c.Net.MaxPendingPeers,
        MinConnectedPeers: c.Net.MinConnectedPeers,
        Name: fmt.Sprintf("%v %v", metadata.Version, c.Name),
        Discovery: c.Net.Discovery, // Add this line
        StaticNodes: c.Net.StaticNodes, // Add this line
        Seeders: c.Net.Seeders,
        NodeDatabase: networkDataDir,
        ListenAddr: c.Net.ListenHost,
        ListenPort: c.Net.ListenPort,
    }
}

```

### 3. Update Node Initialization

Modify the node initialization in `node/node.go` to use the config values and parse static nodes:

```go
func NewNode(conf *Config) (*Node, error) {
    // ... existing code ...

    netConfig := conf.makeNetConfig()
    nodes, err := netConfig.Nodes()
    if err != nil {
        return nil, errors.Errorf("Unable to parse seeders. Reason: %v", err)
    }

    // Parse static nodes using the existing ParseNode functionality
    var staticNodes []*discover.Node
    if len(netConfig.StaticNodes) > 0 {
        staticNodes = make([]*discover.Node, len(netConfig.StaticNodes))
        for i, nodeStr := range netConfig.StaticNodes {
            staticNodes[i], err = discover.ParseNode(nodeStr)
            if err != nil {
                return nil, errors.Errorf("Unable to parse static node %s. Reason: %v", nodeStr, err)
            }
        }
    }

    node.server = &p2p.Server{
        PrivateKey: netConfig.PrivateKey(),
        Name: netConfig.Name,
        MaxPeers: netConfig.MaxPeers,
        MinConnectedPeers: netConfig.MinConnectedPeers,
        MaxPendingPeers: netConfig.MaxPendingPeers,
        Discovery: netConfig.Discovery, // Use config value
        NoDial: false,
        StaticNodes: staticNodes, // Use parsed static nodes
        BootstrapNodes: nodes,
        TrustedNodes: nil,
        NodeDatabase: netConfig.NodeDatabase,
        ListenAddr: fmt.Sprintf("%v:%v", netConfig.ListenAddr, netConfig.ListenPort),
        Protocols: node.z.Protocol().SubProtocols,
    }
    return node, nil
}

```

## Example config.json

After these changes, you can configure discovery and static nodes in your `config.json` like this:

```json
{
    "Net": {
        "ListenHost": "0.0.0.0",
        "ListenPort": 35995,
        "MaxPeers": 60,
        "MinConnectedPeers": 16,
        "MaxPendingPeers": 10,
        "Discovery": true,
        "StaticNodes": [
            "enode://<node-id>@<ip>:<port>",
            "enode://<node-id>@<ip>:<port>"
        ],
        "Seeders": [
            "enode://<node-id>@<ip>:<port>"
        ]
    }
}

```

## Benefits

1. **Flexibility** : Users can now configure whether to enable or disable peer discovery
2. **Control** : Users can specify static nodes that should always be maintained
3. **Customization** : Different nodes can have different discovery and connection strategies
4. **Reuse** : Leverages existing node parsing functionality that’s already proven to work with seeders

## Considerations

1. When `Discovery` is set to `false`, make sure to provide enough `StaticNodes` to maintain network connectivity
2. The `StaticNodes` list should contain valid enode URLs in the format: `enode://<node-id>@<ip>:<port>`
3. Changes to these settings require a node restart to take effect
4. The node parsing functionality is already well-tested through its use with seeders

---

<div class="post-metadata">

### Author: ![0x3639](https://forum.hypercore.one/user_avatar/forum.hypercore.one/0x3639/32/33_2.png) [@0x3639](https://forum.hypercore.one/u/0x3639)
#### Post date: [April 5, 2025, 11:16am UTC](https://forum.hypercore.one/t/improved-sentinel-features-enable-discovery-staticnodes-variable-in-config-json/594/2 "2025-04-05T11:16:17Z")

</div>

If these changes make sense, I would like to propose a ZIP based on the discussion here.

---

<div class="post-metadata">

### Author: ![0x3639](https://forum.hypercore.one/user_avatar/forum.hypercore.one/0x3639/32/33_2.png) [@0x3639](https://forum.hypercore.one/u/0x3639)
#### Post date: [April 5, 2025, 12:20pm UTC](https://forum.hypercore.one/t/improved-sentinel-features-enable-discovery-staticnodes-variable-in-config-json/594/3 "2025-04-05T12:20:41Z")

</div>

Looking at the code a little more, if we are going to consider these changes, should we also look at activating `TrustedNodes`. Currently the value is `nil`

Purpose: The purpose of trusted nodes is to maintain connections with specific, highly trusted peers regardless of normal connection limits. This can be useful for:

- Maintaining connections with critical infrastructure nodes
- Ensuring connectivity with specific nodes even under high network load
- Creating a trusted backbone network

Difference from Static Nodes: While static nodes are always reconnected when disconnected, trusted nodes have the additional privilege of bypassing the maximum peer limit.

---

<div class="post-metadata">

### Author: ![0x3639](https://forum.hypercore.one/user_avatar/forum.hypercore.one/0x3639/32/33_2.png) [@0x3639](https://forum.hypercore.one/u/0x3639)
#### Post date: [April 6, 2025, 6:44pm UTC](https://forum.hypercore.one/t/improved-sentinel-features-enable-discovery-staticnodes-variable-in-config-json/594/4 "2025-04-06T18:44:41Z")

</div>

Guys - I am utterly shocked. I was actually able to get this code to work. I have a test pillar syncing with a sentinel in a private subnet. Let’s see how fast it can sync from scratch.

> **[GitHub - 0x3639/go-zenon at feature/configurable-discovery](https://github.com/0x3639/go-zenon/tree/feature/configurable-discovery)**
>
> feature/configurable-discovery

Here is my test `config.json`

```auto
{
    "Net": {
        "MinPeers": 1,
        "MinConnectedPeers": 1,
        "MaxPeers": 1,
        "MaxPendingPeers": 1,
        "StaticNodes": [
            "enode://e422cec51b5440d1fabc3ea565666d8bda3525a809a7f21ff03e5af80e539e953fd80493ea69fbedee879f5ad728287276b6189f45604a919878f8c4c51dc3da@PRIVATE_IP:35995"]
    }
}

```
