The `rad config` command is used to manage the local user configuration.
In its simplest form, `rad config` prints the current configuration.
```
$ rad config
{
"publicExplorer": "https://app.radicle.xyz/nodes/$host/$rid$path",
"preferredSeeds": [
"z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@iris.radicle.xyz:8776"
],
"web": {
"pinned": {
"repositories": []
}
},
"cli": {
"hints": true
},
"node": {
"alias": "alice",
"listen": [],
"peers": {
"type": "dynamic"
},
"connect": [],
"externalAddresses": [],
"network": "main",
"log": "INFO",
"relay": "auto",
"limits": {
"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": 8,
"seedingPolicy": {
"default": "block"
}
}
}
```
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"
}
},
"database": {
"description": "Database configuration.",
"$ref": "#/$defs/Config"
},
"fetch": {
"description": "Configuration for fetching from other nodes.",
"$ref": "#/$defs/Fetch"
},
"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"
}
]
},
"Config": {
"description": "Database configuration.",
"type": "object",
"properties": {
"sqlite": {
"description": "SQLite configuration.",
"$ref": "#/$defs/SqliteConfig"
}
},
"required": [
"sqlite"
]
},
"SqliteConfig": {
"description": "SQLite database configuration.",
"type": "object",
"properties": {
"pragma": {
"$ref": "#/$defs/Pragma"
}
}
},
"Pragma": {
"description": "Global SQLite pragma statements to make in order to configure SQLite itself,/nsee <https://sqlite.org/pragma.html>.",
"type": "object",
"properties": {
"journalMode": {
"$ref": "#/$defs/JournalMode"
},
"synchronous": {
"$ref": "#/$defs/Synchronous"
}
}
},
"JournalMode": {
"description": "Value for a `journal_mode` pragma statement./nFor a description of all variants please refer to/n<https://sqlite.org/pragma.html#pragma_journal_mode>./nNote that when SQLite documentation talks about /"the application/",/nthe application linked against this crate, e.g. Radicle Node, Radicle CLI,/nand others, is meant.",
"type": "string",
"enum": [
"DELETE",
"TRUNCATE",
"PERSIST",
"MEMORY",
"WAL",
"OFF"
]
},
"Synchronous": {
"description": "Value for a `synchronous` pragma statement./nFor a description of all variants please refer to/n<https://sqlite.org/pragma.html#pragma_synchronous>.",
"type": "string",
"enum": [
"EXTRA",
"FULL",
"NORMAL",
"OFF"
]
},
"Fetch": {
"description": "Configuration for fetching repositories from/nother nodes.",
"type": "object",
"properties": {
"signedReferences": {
"$ref": "#/$defs/SignedReferencesConfig"
}
}
},
"SignedReferencesConfig": {
"type": "object",
"properties": {
"featureLevel": {
"$ref": "#/$defs/FeatureLevelConfig"
}
}
},
"FeatureLevelConfig": {
"type": "object",
"properties": {
"minimum": {
"description": "The minimum feature level required to accept incoming/nreferences from other users. This value is compared/nagainst the feature level detected on refs as they are/nfetched./n/nNote that by increasing this value, security can be/ntraded for compatibility. The higher the value,/nthe less backward compatible, but the more secure, fetches will be.",
"$ref": "#/$defs/FeatureLevel"
}
}
},
"FeatureLevel": {
"description": "The Signed References feature has evolved over time./nThis enum captures the corresponding /"feature level/"./n/nFeature levels are monotonic, in the sense that a greater feature level/nencompasses all the features of smaller ones.",
"oneOf": [
{
"description": "The lowest feature level, with least security. It is vulnerable to/ngraft attacks and replay attacks.",
"type": "string",
"const": "none"
},
{
"description": "An intermediate feature level, which protects against graft attacks but is vulnerable to replay attacks. Introduced in Radicle 1.1.0, in commit `989edacd564fa658358f5ccfd08c243c5ebd8cda`.",
"type": "string",
"const": "root"
},
{
"description": "The highest feature level known, which protects against graft attacks and replay attacks. Introduced in Radicle 1.7.0, in commit `d3bc868e84c334f113806df1737f52cc57c5453d`.",
"type": "string",
"const": "parent"
}
]
}
}
}
```
You can also get any value in the configuration by path, eg.
```
$ rad config get node.alias
alice
$ rad config get preferredSeeds
z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@iris.radicle.xyz:8776
$ rad config get node.limits.routingMaxSize
1000
```
You can set scalar values by path.
```
$ rad config set node.alias bob
bob
$ rad config get node.alias
bob
```
You can push a value to a collection by path.
```
$ rad config push web.pinned.repositories rad:z3TajuiHXifEDEX4qbJxe8nXr9ufi
rad:z3TajuiHXifEDEX4qbJxe8nXr9ufi
$ rad config push web.pinned.repositories rad:z3trNYnLWS11cJWC6BbxDs5niGo82
rad:z3TajuiHXifEDEX4qbJxe8nXr9ufi
rad:z3trNYnLWS11cJWC6BbxDs5niGo82
```
You can remove a value from a collection by path.
```
$ rad config remove web.pinned.repositories rad:z3TajuiHXifEDEX4qbJxe8nXr9ufi
rad:z3trNYnLWS11cJWC6BbxDs5niGo82
```
Values that are not strictly required for a working configuration, such as
optional values or additional user-defined values, can be deleted.
```
$ rad config set web.name alice
alice
$ rad config unset web.name
```
``` (fail)
$ rad config get web.name
✗ Error: web.name does not exist in configuration found at "[..]/.radicle/config.json"
```
Values along the path will be created if necessary.
```
$ rad config set value.a.future.update.might.add.value 5
5
$ rad config push value.a.future.update.might.add.collection 1
1
```
```
$ rad config push node.array a
a
$ rad config push node.array b
a
b
```
Values that are required for a valid config can't be deleted.
``` (fail)
$ rad config unset node.alias
✗ Error: writing configuration to "[..]/.radicle/config.json" failed: validation failure due to missing field `alias`
```
Values for changes are being validated.
``` (fail)
$ rad config set web.pinned.repositories 5
✗ Error: writing configuration to "[..]/.radicle/config.json" failed: validation failure due to invalid type: integer `5`, expected a set
```
The type of the operation is validated.
``` (fail)
$ rad config push node.alias eve
✗ Error: failed to modify configuration found at "[..]/.radicle/config.json" due to the element at the path 'node.alias' is not a JSON array
```