Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
Fix DefaultSeedingPolicy JSON schema
Merged fintohaps opened 2 months ago
2 files changed +609 -0 6291cae5 6291cae5
modified crates/radicle-cli/examples/rad-config.md
@@ -57,6 +57,614 @@ $ rad config
}
```

+
The `rad config schema` command provides the JSON schema that can be used to
+
validate the JSON of the user configuration.
+

+
```
+
$ rad config schema
+
{
+
  "$schema": "https://json-schema.org/draft/2020-12/schema",
+
  "title": "Config",
+
  "description": "Local radicle configuration.",
+
  "type": "object",
+
  "properties": {
+
    "publicExplorer": {
+
      "description": "Public explorer. This is used for generating links.",
+
      "$ref": "#/$defs/Explorer",
+
      "default": "https://app.radicle.xyz/nodes/$host/$rid$path"
+
    },
+
    "preferredSeeds": {
+
      "description": "Preferred seeds. These seeds will be used for explorer links/nand in other situations when a seed needs to be chosen.",
+
      "type": "array",
+
      "items": {
+
        "$ref": "#/$defs/ConnectAddress"
+
      },
+
      "default": []
+
    },
+
    "web": {
+
      "description": "Web configuration.",
+
      "$ref": "#/$defs/WebConfig",
+
      "default": {
+
        "pinned": {
+
          "repositories": []
+
        }
+
      }
+
    },
+
    "cli": {
+
      "description": "CLI configuration.",
+
      "$ref": "#/$defs/CliConfig",
+
      "default": {
+
        "hints": true
+
      }
+
    },
+
    "node": {
+
      "description": "Node configuration.",
+
      "$ref": "#/$defs/NodeConfig"
+
    }
+
  },
+
  "required": [
+
    "node"
+
  ],
+
  "$defs": {
+
    "Explorer": {
+
      "description": "A public explorer.",
+
      "type": "string"
+
    },
+
    "ConnectAddress": {
+
      "description": "A node address to connect to. Format: An Ed25519 public key in multibase encoding, followed by the symbol '@', followed by an IP address, or a DNS name, or a Tor onion name, followed by the symbol ':', followed by a TCP port number.",
+
      "type": "string",
+
      "examples": [
+
        "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@rosa.radicle.xyz:8776",
+
        "z6MkvUJtYD9dHDJfpevWRT98mzDDpdAtmUjwyDSkyqksUr7C@xmrhfasfg5suueegrnc4gsgyi2tyclcy5oz7f5drnrodmdtob6t2ioyd.onion:8776",
+
        "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi@seed.example.com:8776",
+
        "z6MkkfM3tPXNPrPevKr3uSiQtHPuwnNhu2yUVjgd2jXVsVz5@192.0.2.0:31337"
+
      ],
+
      "pattern": "^.+@.+:((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4}))$"
+
    },
+
    "WebConfig": {
+
      "description": "Web configuration.",
+
      "type": "object",
+
      "properties": {
+
        "pinned": {
+
          "description": "Pinned content.",
+
          "$ref": "#/$defs/Pinned"
+
        },
+
        "bannerUrl": {
+
          "description": "URL pointing to an image used in the header of a node page.",
+
          "type": [
+
            "string",
+
            "null"
+
          ],
+
          "format": "uri"
+
        },
+
        "avatarUrl": {
+
          "description": "URL pointing to an image used as the node avatar.",
+
          "type": [
+
            "string",
+
            "null"
+
          ],
+
          "format": "uri"
+
        },
+
        "description": {
+
          "description": "Node description.",
+
          "type": [
+
            "string",
+
            "null"
+
          ],
+
          "format": "uri"
+
        }
+
      },
+
      "required": [
+
        "pinned"
+
      ]
+
    },
+
    "Pinned": {
+
      "description": "Pinned content. This can be used to pin certain content when/nlisting, e.g. pin repositories on a web client.",
+
      "type": "object",
+
      "properties": {
+
        "repositories": {
+
          "description": "Pinned repositories.",
+
          "type": "array",
+
          "uniqueItems": true,
+
          "items": {
+
            "$ref": "#/$defs/RepoId"
+
          }
+
        }
+
      },
+
      "required": [
+
        "repositories"
+
      ]
+
    },
+
    "RepoId": {
+
      "description": "A repository identifier.",
+
      "type": "string",
+
      "examples": [
+
        "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5"
+
      ],
+
      "minLength": 5,
+
      "pattern": "rad:z[1-9a-km-zA-HJ-NP-Z]+"
+
    },
+
    "CliConfig": {
+
      "description": "CLI configuration.",
+
      "type": "object",
+
      "properties": {
+
        "hints": {
+
          "description": "Whether to show hints or not in the CLI.",
+
          "type": "boolean",
+
          "default": false
+
        }
+
      }
+
    },
+
    "NodeConfig": {
+
      "description": "Service configuration.",
+
      "type": "object",
+
      "properties": {
+
        "alias": {
+
          "description": "Node alias.",
+
          "$ref": "#/$defs/Alias"
+
        },
+
        "listen": {
+
          "description": "Socket address (a combination of IPv4 or IPv6 address and TCP port) to listen on.",
+
          "type": "array",
+
          "items": {
+
            "type": "string"
+
          },
+
          "examples": [
+
            "127.0.0.1:8776"
+
          ],
+
          "default": []
+
        },
+
        "peers": {
+
          "description": "Peer configuration.",
+
          "$ref": "#/$defs/PeerConfig",
+
          "default": {
+
            "type": "dynamic"
+
          }
+
        },
+
        "connect": {
+
          "description": "Peers to connect to on startup./nConnections to these peers will be maintained.",
+
          "type": "array",
+
          "uniqueItems": true,
+
          "items": {
+
            "$ref": "#/$defs/ConnectAddress"
+
          },
+
          "default": []
+
        },
+
        "externalAddresses": {
+
          "description": "Specify the node's public addresses",
+
          "type": "array",
+
          "items": {
+
            "$ref": "#/$defs/Address"
+
          },
+
          "default": []
+
        },
+
        "proxy": {
+
          "description": "Global proxy.",
+
          "type": [
+
            "string",
+
            "null"
+
          ]
+
        },
+
        "onion": {
+
          "description": "Onion address config.",
+
          "anyOf": [
+
            {
+
              "$ref": "#/$defs/AddressConfig"
+
            },
+
            {
+
              "type": "null"
+
            }
+
          ]
+
        },
+
        "network": {
+
          "description": "Peer-to-peer network.",
+
          "$ref": "#/$defs/Network",
+
          "default": "main"
+
        },
+
        "log": {
+
          "description": "Log level.",
+
          "$ref": "#/$defs/LogLevel",
+
          "default": "INFO"
+
        },
+
        "relay": {
+
          "description": "Whether or not our node should relay messages.",
+
          "$ref": "#/$defs/Relay",
+
          "default": "auto"
+
        },
+
        "limits": {
+
          "description": "Configured service limits.",
+
          "$ref": "#/$defs/Limits",
+
          "default": {
+
            "routingMaxSize": 1000,
+
            "routingMaxAge": 604800,
+
            "gossipMaxAge": 1209600,
+
            "fetchConcurrency": 1,
+
            "maxOpenFiles": 4096,
+
            "rate": {
+
              "inbound": {
+
                "fillRate": 5.0,
+
                "capacity": 1024
+
              },
+
              "outbound": {
+
                "fillRate": 10.0,
+
                "capacity": 2048
+
              }
+
            },
+
            "connection": {
+
              "inbound": 128,
+
              "outbound": 16
+
            },
+
            "fetchPackReceive": "500.0 MiB"
+
          }
+
        },
+
        "workers": {
+
          "description": "Number of worker threads to spawn.",
+
          "type": "integer",
+
          "format": "uint",
+
          "minimum": 0,
+
          "default": 8
+
        },
+
        "seedingPolicy": {
+
          "description": "Default seeding policy.",
+
          "$ref": "#/$defs/DefaultSeedingPolicy",
+
          "default": {
+
            "default": "block"
+
          }
+
        },
+
        "secret": {
+
          "description": "Path to a file containing an Ed25519 secret key, in OpenSSH format, i.e./nwith the `-----BEGIN OPENSSH PRIVATE KEY-----` header. The corresponding/npublic key will be used as the Node ID./n/nA decryption password cannot be configured, but passed at runtime via/nthe environment variable `RAD_PASSPHRASE`.",
+
          "type": [
+
            "string",
+
            "null"
+
          ]
+
        }
+
      },
+
      "required": [
+
        "alias"
+
      ],
+
      "additionalProperties": true
+
    },
+
    "Alias": {
+
      "description": "Node alias, i.e. a short and memorable name for it.",
+
      "type": "string"
+
    },
+
    "PeerConfig": {
+
      "description": "Peer configuration.",
+
      "oneOf": [
+
        {
+
          "description": "Static peer set. Connect to the configured peers and maintain the connections.",
+
          "type": "object",
+
          "properties": {
+
            "type": {
+
              "type": "string",
+
              "const": "static"
+
            }
+
          },
+
          "required": [
+
            "type"
+
          ]
+
        },
+
        {
+
          "description": "Dynamic peer set.",
+
          "type": "object",
+
          "properties": {
+
            "type": {
+
              "type": "string",
+
              "const": "dynamic"
+
            }
+
          },
+
          "required": [
+
            "type"
+
          ]
+
        }
+
      ]
+
    },
+
    "Address": {
+
      "description": "An IP address, or a DNS name, or a Tor onion name, followed by the symbol ':', followed by a TCP port number.",
+
      "type": "string",
+
      "examples": [
+
        "xmrhfasfg5suueegrnc4gsgyi2tyclcy5oz7f5drnrodmdtob6t2ioyd.onion:8776",
+
        "seed.example.com:8776",
+
        "192.0.2.0:31337"
+
      ],
+
      "pattern": "^.+:((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4}))$"
+
    },
+
    "AddressConfig": {
+
      "description": "Proxy configuration.",
+
      "oneOf": [
+
        {
+
          "description": "Proxy connections to this address type.",
+
          "type": "object",
+
          "properties": {
+
            "address": {
+
              "description": "Proxy address.",
+
              "type": "string"
+
            },
+
            "mode": {
+
              "type": "string",
+
              "const": "proxy"
+
            }
+
          },
+
          "required": [
+
            "mode",
+
            "address"
+
          ]
+
        },
+
        {
+
          "description": "Forward address to the next layer. Either this is the global proxy,/nor the operating system, via DNS.",
+
          "type": "object",
+
          "properties": {
+
            "mode": {
+
              "type": "string",
+
              "const": "forward"
+
            }
+
          },
+
          "required": [
+
            "mode"
+
          ]
+
        }
+
      ]
+
    },
+
    "Network": {
+
      "description": "Peer-to-peer network.",
+
      "type": "string",
+
      "enum": [
+
        "main",
+
        "test"
+
      ]
+
    },
+
    "LogLevel": {
+
      "$ref": "#/$defs/Level"
+
    },
+
    "Level": {
+
      "description": "A log level.",
+
      "oneOf": [
+
        {
+
          "description": "Designates very serious errors.",
+
          "type": "string",
+
          "const": "ERROR"
+
        },
+
        {
+
          "description": "Designates hazardous situations.",
+
          "type": "string",
+
          "const": "WARN"
+
        },
+
        {
+
          "description": "Designates useful information.",
+
          "type": "string",
+
          "const": "INFO"
+
        },
+
        {
+
          "description": "Designates lower priority information.",
+
          "type": "string",
+
          "const": "DEBUG"
+
        },
+
        {
+
          "description": "Designates very low priority, often extremely verbose, information.",
+
          "type": "string",
+
          "const": "TRACE"
+
        }
+
      ]
+
    },
+
    "Relay": {
+
      "description": "Relay configuration.",
+
      "oneOf": [
+
        {
+
          "description": "Always relay messages.",
+
          "type": "string",
+
          "const": "always"
+
        },
+
        {
+
          "description": "Never relay messages.",
+
          "type": "string",
+
          "const": "never"
+
        },
+
        {
+
          "description": "Relay messages when applicable.",
+
          "type": "string",
+
          "const": "auto"
+
        }
+
      ]
+
    },
+
    "Limits": {
+
      "description": "Configuration parameters defining attributes of minima and maxima.",
+
      "type": "object",
+
      "properties": {
+
        "routingMaxSize": {
+
          "description": "Number of routing table entries before we start pruning.",
+
          "type": "integer",
+
          "format": "uint",
+
          "minimum": 0,
+
          "default": 1000
+
        },
+
        "routingMaxAge": {
+
          "description": "How long to keep a routing table entry before being pruned.",
+
          "$ref": "#/$defs/LocalDuration",
+
          "default": 604800
+
        },
+
        "gossipMaxAge": {
+
          "description": "How long to keep a gossip message entry before pruning it.",
+
          "$ref": "#/$defs/LocalDuration",
+
          "default": 1209600
+
        },
+
        "fetchConcurrency": {
+
          "description": "Maximum number of concurrent fetches per peer connection.",
+
          "type": "integer",
+
          "format": "uint",
+
          "minimum": 0,
+
          "default": 1
+
        },
+
        "maxOpenFiles": {
+
          "description": "Maximum number of open files.",
+
          "type": "integer",
+
          "format": "uint",
+
          "minimum": 0,
+
          "default": 4096
+
        },
+
        "rate": {
+
          "description": "Rate limiter settings.",
+
          "$ref": "#/$defs/RateLimits",
+
          "default": {
+
            "inbound": {
+
              "fillRate": 5.0,
+
              "capacity": 1024
+
            },
+
            "outbound": {
+
              "fillRate": 10.0,
+
              "capacity": 2048
+
            }
+
          }
+
        },
+
        "connection": {
+
          "description": "Connection limits.",
+
          "$ref": "#/$defs/ConnectionLimits",
+
          "default": {
+
            "inbound": 128,
+
            "outbound": 16
+
          }
+
        },
+
        "fetchPackReceive": {
+
          "description": "Channel limits.",
+
          "$ref": "#/$defs/FetchPackSizeLimit",
+
          "default": "500.0 MiB"
+
        }
+
      }
+
    },
+
    "LocalDuration": {
+
      "description": "A time duration measured locally in seconds.",
+
      "type": "integer",
+
      "format": "uint128",
+
      "minimum": 0
+
    },
+
    "RateLimits": {
+
      "description": "Rate limits for inbound and outbound connections.",
+
      "type": "object",
+
      "properties": {
+
        "inbound": {
+
          "$ref": "#/$defs/RateLimit"
+
        },
+
        "outbound": {
+
          "$ref": "#/$defs/RateLimit"
+
        }
+
      },
+
      "required": [
+
        "inbound",
+
        "outbound"
+
      ]
+
    },
+
    "RateLimit": {
+
      "description": "Rate limits for a single connection.",
+
      "type": "object",
+
      "properties": {
+
        "fillRate": {
+
          "type": "number",
+
          "format": "double"
+
        },
+
        "capacity": {
+
          "type": "integer",
+
          "format": "uint",
+
          "minimum": 0
+
        }
+
      },
+
      "required": [
+
        "fillRate",
+
        "capacity"
+
      ]
+
    },
+
    "ConnectionLimits": {
+
      "description": "Connection limits.",
+
      "type": "object",
+
      "properties": {
+
        "inbound": {
+
          "description": "Max inbound connections.",
+
          "type": "integer",
+
          "format": "uint",
+
          "minimum": 0,
+
          "default": 128
+
        },
+
        "outbound": {
+
          "description": "Max outbound connections. Note that this can be higher than the *target* number.",
+
          "type": "integer",
+
          "format": "uint",
+
          "minimum": 0,
+
          "default": 16
+
        }
+
      }
+
    },
+
    "FetchPackSizeLimit": {
+
      "description": "Limiter for byte streams./n/nDefault: 500MiB",
+
      "$ref": "#/$defs/ByteSize"
+
    },
+
    "ByteSize": {
+
      "description": "Byte quantities using unit prefixes according to SI or ISO/IEC 80000-13.",
+
      "type": "string",
+
      "pattern": "^//d+(//.//d+)? ((K|M|G|T|P)i?B?|B)$",
+
      "examples": [
+
        "7 G",
+
        "50.3 TiB",
+
        "200 B",
+
        "4 Ki",
+
        "10 MB"
+
      ]
+
    },
+
    "DefaultSeedingPolicy": {
+
      "description": "Default seeding policy. Applies when no repository policies for the given repo are found.",
+
      "oneOf": [
+
        {
+
          "description": "Allow seeding.",
+
          "type": "object",
+
          "properties": {
+
            "default": {
+
              "type": "string",
+
              "const": "allow"
+
            }
+
          },
+
          "anyOf": [
+
            {
+
              "$ref": "#/$defs/Scope"
+
            },
+
            {
+
              "type": "null"
+
            }
+
          ],
+
          "required": [
+
            "default"
+
          ]
+
        },
+
        {
+
          "description": "Block seeding.",
+
          "type": "object",
+
          "properties": {
+
            "default": {
+
              "type": "string",
+
              "const": "block"
+
            }
+
          },
+
          "required": [
+
            "default"
+
          ]
+
        }
+
      ]
+
    },
+
    "Scope": {
+
      "description": "Follow scope of a seeded repository.",
+
      "oneOf": [
+
        {
+
          "description": "Seed remotes that are explicitly followed.",
+
          "type": "string",
+
          "const": "followed"
+
        },
+
        {
+
          "description": "Seed all remotes.",
+
          "type": "string",
+
          "const": "all"
+
        }
+
      ]
+
    }
+
  }
+
}
+
```
+

You can also get any value in the configuration by path, eg.

```
modified crates/radicle/src/node/config.rs
@@ -373,6 +373,7 @@ pub enum DefaultSeedingPolicy {
    Allow {
        /// Seeding scope.
        #[serde(skip_serializing_if = "Scope::is_implicit")]
+
        #[cfg_attr(feature = "schemars", schemars(flatten))]
        scope: Scope,
    },
    /// Block seeding.