| 1 | package config |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "net" |
| 6 | "time" |
| 7 | ) |
| 8 | |
| 9 | // Transformer is a function which takes configuration and applies some filter to it. |
| 10 | type Transformer func(c *Config) error |
| 11 | |
| 12 | // Profile contains the profile transformer the description of the profile. |
| 13 | type Profile struct { |
| 14 | // Description briefly describes the functionality of the profile. |
| 15 | Description string |
| 16 | |
| 17 | // Transform takes ipfs configuration and applies the profile to it. |
| 18 | Transform Transformer |
| 19 | |
| 20 | // InitOnly specifies that this profile can only be applied on init. |
| 21 | InitOnly bool |
| 22 | } |
| 23 | |
| 24 | // defaultServerFilters lists IPv4 and IPv6 prefixes that are private, |
| 25 | // local-only, or otherwise not "Globally Reachable" per the IANA |
| 26 | // Special-Purpose Address Registries (RFC 6890): |
| 27 | // |
| 28 | // https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml |
| 29 | // https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml |
| 30 | // |
| 31 | // The `server` profile appends this list to both `Addresses.NoAnnounce` |
| 32 | // (strip from self-announce / identify / DHT self-record) and |
| 33 | // `Swarm.AddrFilters` (refuse libp2p dial/accept involving these ranges). |
| 34 | // See docs/config.md under "`server` profile" for the rendered table with |
| 35 | // per-entry RFC references and guidance on optional entries (for example |
| 36 | // loopback or IPv6 outside `2000::/3`) that operators may add manually. |
| 37 | // |
| 38 | // Keep this list stable; changes here affect every `server`-profile user. |
| 39 | var defaultServerFilters = []string{ |
| 40 | "/ip4/10.0.0.0/ipcidr/8", // RFC 1918: private-use |
| 41 | "/ip4/100.64.0.0/ipcidr/10", // RFC 6598: shared address space (CGNAT) |
| 42 | "/ip4/127.0.0.0/ipcidr/8", // RFC 1122: IPv4 loopback |
| 43 | "/ip4/169.254.0.0/ipcidr/16", // RFC 3927: link-local |
| 44 | "/ip4/172.16.0.0/ipcidr/12", // RFC 1918: private-use |
| 45 | "/ip4/192.0.0.0/ipcidr/24", // RFC 6890: IETF protocol assignments |
| 46 | "/ip4/192.0.2.0/ipcidr/24", // RFC 5737: TEST-NET-1 (documentation) |
| 47 | "/ip4/192.168.0.0/ipcidr/16", // RFC 1918: private-use |
| 48 | "/ip4/198.18.0.0/ipcidr/15", // RFC 2544: benchmarking |
| 49 | "/ip4/198.51.100.0/ipcidr/24", // RFC 5737: TEST-NET-2 (documentation) |
| 50 | "/ip4/203.0.113.0/ipcidr/24", // RFC 5737: TEST-NET-3 (documentation) |
| 51 | "/ip4/240.0.0.0/ipcidr/4", // RFC 1112: reserved (covers broadcast 255.255.255.255) |
| 52 | "/ip6/::/ipcidr/3", // RFC 4291 §2.4: IANA-reserved 0000::/3 block (unspecified, loopback, IPv4-mapped, NAT64, and unallocated space where 1e::/16 leaks) |
| 53 | "/ip6/::1/ipcidr/128", // RFC 4291 §2.4: IPv6 loopback (subset of `::/3` above; kept for documentation) |
| 54 | "/ip6/100::/ipcidr/64", // RFC 6666: discard-only (subset of `::/3` above; kept for documentation) |
| 55 | "/ip6/2001:2::/ipcidr/48", // RFC 5180: BMWG benchmarking |
| 56 | "/ip6/2001:db8::/ipcidr/32", // RFC 3849: documentation |
| 57 | "/ip6/fc00::/ipcidr/7", // RFC 4193: unique local addresses (ULA) |
| 58 | "/ip6/fe80::/ipcidr/10", // RFC 4291: link-local unicast |
| 59 | } |
| 60 | |
| 61 | // Profiles is a map holding configuration transformers. Docs are in docs/config.md. |
| 62 | var Profiles = map[string]Profile{ |
| 63 | "server": { |
| 64 | Description: `Disables local host discovery, recommended when |
| 65 | running IPFS on machines with public IPv4 addresses.`, |
| 66 | |
| 67 | Transform: func(c *Config) error { |
| 68 | c.Addresses.NoAnnounce = appendSingle(c.Addresses.NoAnnounce, defaultServerFilters) |
| 69 | c.Swarm.AddrFilters = appendSingle(c.Swarm.AddrFilters, defaultServerFilters) |
| 70 | c.Discovery.MDNS.Enabled = false |
| 71 | c.Swarm.DisableNatPortMap = true |
| 72 | return nil |
| 73 | }, |
| 74 | }, |
| 75 | |
| 76 | "local-discovery": { |
| 77 | Description: `Sets default values to fields affected by the server |
| 78 | profile, enables discovery in local networks.`, |
| 79 | |
| 80 | Transform: func(c *Config) error { |
| 81 | c.Addresses.NoAnnounce = deleteEntries(c.Addresses.NoAnnounce, defaultServerFilters) |
| 82 | c.Swarm.AddrFilters = deleteEntries(c.Swarm.AddrFilters, defaultServerFilters) |
| 83 | c.Discovery.MDNS.Enabled = true |
| 84 | c.Swarm.DisableNatPortMap = false |
| 85 | return nil |
| 86 | }, |
| 87 | }, |
| 88 | "test": { |
| 89 | Description: `Reduces external interference of IPFS daemon, this |
| 90 | is useful when using the daemon in test environments.`, |
| 91 | |
| 92 | Transform: func(c *Config) error { |
| 93 | c.Addresses.API = Strings{"/ip4/127.0.0.1/tcp/0"} |
| 94 | c.Addresses.Gateway = Strings{"/ip4/127.0.0.1/tcp/0"} |
| 95 | c.Addresses.Swarm = []string{ |
| 96 | "/ip4/127.0.0.1/tcp/0", |
| 97 | } |
| 98 | |
| 99 | c.Swarm.DisableNatPortMap = true |
| 100 | c.Routing.LoopbackAddressesOnLanDHT = True |
| 101 | |
| 102 | c.Bootstrap = []string{} |
| 103 | c.Discovery.MDNS.Enabled = false |
| 104 | c.AutoTLS.Enabled = False |
| 105 | c.AutoConf.Enabled = False |
| 106 | |
| 107 | // Explicitly set autoconf-controlled fields to empty when autoconf is disabled |
| 108 | c.DNS.Resolvers = map[string]string{} |
| 109 | c.Routing.DelegatedRouters = []string{} |
| 110 | c.Ipns.DelegatedPublishers = []string{} |
| 111 | return nil |
| 112 | }, |
| 113 | }, |
| 114 | "default-networking": { |
| 115 | Description: `Restores default network settings. |
| 116 | Inverse profile of the test profile.`, |
| 117 | |
| 118 | Transform: func(c *Config) error { |
| 119 | c.Addresses = addressesConfig() |
| 120 | |
| 121 | // Use AutoConf system for bootstrap peers |
| 122 | c.Bootstrap = []string{AutoPlaceholder} |
| 123 | c.AutoConf.Enabled = Default |
| 124 | c.AutoConf.URL = nil // Clear URL to use implicit default |
| 125 | |
| 126 | c.Swarm.DisableNatPortMap = false |
| 127 | c.Discovery.MDNS.Enabled = true |
| 128 | c.AutoTLS.Enabled = Default |
| 129 | return nil |
| 130 | }, |
| 131 | }, |
| 132 | "default-datastore": { |
| 133 | Description: `Configures the node to use the default datastore (flatfs). |
| 134 | |
| 135 | Read the "flatfs" profile description for more information on this datastore. |
| 136 | |
| 137 | This profile may only be applied when first initializing the node. |
| 138 | `, |
| 139 | |
| 140 | InitOnly: true, |
| 141 | Transform: func(c *Config) error { |
| 142 | c.Datastore.Spec = flatfsSpec() |
| 143 | return nil |
| 144 | }, |
| 145 | }, |
| 146 | "flatfs": { |
| 147 | Description: `Configures the node to use the flatfs datastore. |
| 148 | |
| 149 | This is the most battle-tested and reliable datastore. |
| 150 | You should use this datastore if: |
| 151 | |
| 152 | * You need a very simple and very reliable datastore, and you trust your |
| 153 | filesystem. This datastore stores each block as a separate file in the |
| 154 | underlying filesystem so it's unlikely to loose data unless there's an issue |
| 155 | with the underlying file system. |
| 156 | * You need to run garbage collection in a way that reclaims free space as soon as possible. |
| 157 | * You want to minimize memory usage. |
| 158 | * You are ok with the default speed of data import, or prefer to use --nocopy. |
| 159 | |
| 160 | See configuration documentation at: |
| 161 | https://github.com/ipfs/kubo/blob/master/docs/datastores.md#flatfs |
| 162 | |
| 163 | NOTE: This profile may only be applied when first initializing node at IPFS_PATH |
| 164 | via 'ipfs init --profile flatfs' |
| 165 | `, |
| 166 | |
| 167 | InitOnly: true, |
| 168 | Transform: func(c *Config) error { |
| 169 | c.Datastore.Spec = flatfsSpec() |
| 170 | return nil |
| 171 | }, |
| 172 | }, |
| 173 | "flatfs-measure": { |
| 174 | Description: `Configures the node to use the flatfs datastore with metrics tracking wrapper. |
| 175 | Additional '*_datastore_*' metrics will be exposed on /debug/metrics/prometheus |
| 176 | |
| 177 | NOTE: This profile may only be applied when first initializing node at IPFS_PATH |
| 178 | via 'ipfs init --profile flatfs-measure' |
| 179 | `, |
| 180 | |
| 181 | InitOnly: true, |
| 182 | Transform: func(c *Config) error { |
| 183 | c.Datastore.Spec = flatfsSpecMeasure() |
| 184 | return nil |
| 185 | }, |
| 186 | }, |
| 187 | "pebbleds": { |
| 188 | Description: `Configures the node to use the pebble high-performance datastore. |
| 189 | |
| 190 | Pebble is a LevelDB/RocksDB inspired key-value store focused on performance |
| 191 | and internal usage by CockroachDB. |
| 192 | You should use this datastore if: |
| 193 | |
| 194 | - You need a datastore that is focused on performance. |
| 195 | - You need reliability by default, but may choose to disable WAL for maximum performance when reliability is not critical. |
| 196 | - This datastore is good for multi-terabyte data sets. |
| 197 | - May benefit from tuning depending on read/write patterns and throughput. |
| 198 | - Performance is helped significantly by running on a system with plenty of memory. |
| 199 | |
| 200 | See configuration documentation at: |
| 201 | https://github.com/ipfs/kubo/blob/master/docs/datastores.md#pebbleds |
| 202 | |
| 203 | NOTE: This profile may only be applied when first initializing node at IPFS_PATH |
| 204 | via 'ipfs init --profile pebbleds' |
| 205 | `, |
| 206 | |
| 207 | InitOnly: true, |
| 208 | Transform: func(c *Config) error { |
| 209 | c.Datastore.Spec = pebbleSpec() |
| 210 | return nil |
| 211 | }, |
| 212 | }, |
| 213 | "pebbleds-measure": { |
| 214 | Description: `Configures the node to use the pebble datastore with metrics tracking wrapper. |
| 215 | Additional '*_datastore_*' metrics will be exposed on /debug/metrics/prometheus |
| 216 | |
| 217 | NOTE: This profile may only be applied when first initializing node at IPFS_PATH |
| 218 | via 'ipfs init --profile pebbleds-measure' |
| 219 | `, |
| 220 | |
| 221 | InitOnly: true, |
| 222 | Transform: func(c *Config) error { |
| 223 | c.Datastore.Spec = pebbleSpecMeasure() |
| 224 | return nil |
| 225 | }, |
| 226 | }, |
| 227 | "badgerds": { |
| 228 | Description: `DEPRECATED: Configures the node to use the legacy badgerv1 datastore. |
| 229 | This profile will be removed in a future Kubo release. |
| 230 | New deployments should use 'flatfs' or 'pebbleds' instead. |
| 231 | |
| 232 | NOTE: this is badger 1.x, which has known bugs and is no longer supported by the upstream team. |
| 233 | It is provided here only for pre-existing users, allowing them to migrate away to more modern datastore. |
| 234 | |
| 235 | Other caveats: |
| 236 | |
| 237 | * This datastore will not properly reclaim space when your datastore is |
| 238 | smaller than several gigabytes. If you run IPFS with --enable-gc, you plan |
| 239 | on storing very little data in your IPFS node, and disk usage is more |
| 240 | critical than performance, consider using flatfs. |
| 241 | * This datastore uses up to several gigabytes of memory. |
| 242 | * Good for medium-size datastores, but may run into performance issues |
| 243 | if your dataset is bigger than a terabyte. |
| 244 | |
| 245 | To migrate: create a new IPFS_PATH with 'ipfs init --profile=flatfs', |
| 246 | move pinned data via 'ipfs dag export/import' or 'ipfs pin ls -t recursive|add', |
| 247 | and decommission the old badger-based node. |
| 248 | When it comes to block storage, use experimental 'pebbleds' only if you are sure |
| 249 | modern 'flatfs' does not serve your use case (most users will be perfectly fine |
| 250 | with flatfs, it is also possible to keep flatfs for blocks and replace leveldb |
| 251 | with pebble if preferred over leveldb). |
| 252 | |
| 253 | See configuration documentation at: |
| 254 | https://github.com/ipfs/kubo/blob/master/docs/datastores.md#badgerds |
| 255 | |
| 256 | NOTE: This profile may only be applied when first initializing node at IPFS_PATH |
| 257 | via 'ipfs init --profile badgerds' |
| 258 | `, |
| 259 | |
| 260 | InitOnly: true, |
| 261 | Transform: func(c *Config) error { |
| 262 | c.Datastore.Spec = badgerSpec() |
| 263 | return nil |
| 264 | }, |
| 265 | }, |
| 266 | "badgerds-measure": { |
| 267 | Description: `DEPRECATED: Configures the node to use the legacy badgerv1 datastore with metrics wrapper. |
| 268 | This profile will be removed in a future Kubo release. |
| 269 | New deployments should use 'flatfs' or 'pebbleds' instead. |
| 270 | |
| 271 | NOTE: This profile may only be applied when first initializing node at IPFS_PATH |
| 272 | via 'ipfs init --profile badgerds-measure' |
| 273 | `, |
| 274 | |
| 275 | InitOnly: true, |
| 276 | Transform: func(c *Config) error { |
| 277 | c.Datastore.Spec = badgerSpecMeasure() |
| 278 | return nil |
| 279 | }, |
| 280 | }, |
| 281 | "lowpower": { |
| 282 | Description: `Reduces daemon overhead on the system. May affect node |
| 283 | functionality - performance of content discovery and data |
| 284 | fetching may be degraded. |
| 285 | `, |
| 286 | Transform: func(c *Config) error { |
| 287 | // Disable "server" services (dht, autonat, limited relay) |
| 288 | c.Routing.Type = NewOptionalString("autoclient") |
| 289 | c.AutoNAT.ServiceMode = AutoNATServiceDisabled |
| 290 | c.Swarm.RelayService.Enabled = False |
| 291 | |
| 292 | // Keep bare minimum connections around |
| 293 | lowWater := int64(20) |
| 294 | highWater := int64(40) |
| 295 | gracePeriod := time.Minute |
| 296 | c.Swarm.ConnMgr.Type = NewOptionalString("basic") |
| 297 | c.Swarm.ConnMgr.LowWater = &OptionalInteger{value: &lowWater} |
| 298 | c.Swarm.ConnMgr.HighWater = &OptionalInteger{value: &highWater} |
| 299 | c.Swarm.ConnMgr.GracePeriod = &OptionalDuration{&gracePeriod} |
| 300 | return nil |
| 301 | }, |
| 302 | }, |
| 303 | "announce-off": { |
| 304 | Description: `Disables Provide system (announcing to Amino DHT). |
| 305 | |
| 306 | USE WITH CAUTION: |
| 307 | The main use case for this is setups with manual Peering.Peers config. |
| 308 | Data from this node will not be announced on the DHT. This will make |
| 309 | DHT-based routing and data retrieval impossible if this node is the only |
| 310 | one hosting it, and other peers are not already connected to it. |
| 311 | `, |
| 312 | Transform: func(c *Config) error { |
| 313 | c.Provide.Enabled = False |
| 314 | c.Provide.DHT.Interval = NewOptionalDuration(0) // 0 disables periodic reprovide |
| 315 | return nil |
| 316 | }, |
| 317 | }, |
| 318 | "announce-on": { |
| 319 | Description: `Re-enables Provide system (reverts announce-off profile).`, |
| 320 | Transform: func(c *Config) error { |
| 321 | c.Provide.Enabled = True |
| 322 | c.Provide.DHT.Interval = NewOptionalDuration(DefaultProvideDHTInterval) // have to apply explicit default because nil would be ignored |
| 323 | return nil |
| 324 | }, |
| 325 | }, |
| 326 | "randomports": { |
| 327 | Description: `Use a random port number for swarm.`, |
| 328 | |
| 329 | Transform: func(c *Config) error { |
| 330 | port, err := getAvailablePort() |
| 331 | if err != nil { |
| 332 | return err |
| 333 | } |
| 334 | c.Addresses.Swarm = []string{ |
| 335 | fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port), |
| 336 | fmt.Sprintf("/ip6/::/tcp/%d", port), |
| 337 | } |
| 338 | return nil |
| 339 | }, |
| 340 | }, |
| 341 | "unixfs-v0-2015": { |
| 342 | Description: `Legacy UnixFS import profile for backward-compatible CID generation. |
| 343 | Produces CIDv0 with no raw leaves, sha2-256, 256 KiB chunks, and |
| 344 | link-based HAMT size estimation. Use only when legacy CIDs are required. |
| 345 | See https://specs.ipfs.tech/ipips/ipip-0499/. Alias: legacy-cid-v0`, |
| 346 | Transform: applyUnixFSv02015, |
| 347 | }, |
| 348 | "legacy-cid-v0": { |
| 349 | Description: `Alias for unixfs-v0-2015 profile.`, |
| 350 | Transform: applyUnixFSv02015, |
| 351 | }, |
| 352 | "unixfs-v1-2025": { |
| 353 | Description: `Recommended UnixFS import profile for cross-implementation CID determinism. |
| 354 | Uses CIDv1, raw leaves, sha2-256, 1 MiB chunks, 1024 links per file node, |
| 355 | 256 HAMT fanout, and block-based size estimation for HAMT threshold. |
| 356 | See https://specs.ipfs.tech/ipips/ipip-0499/`, |
| 357 | Transform: func(c *Config) error { |
| 358 | c.Import.CidVersion = *NewOptionalInteger(1) |
| 359 | c.Import.UnixFSRawLeaves = True |
| 360 | c.Import.UnixFSChunker = *NewOptionalString("size-1048576") // 1 MiB |
| 361 | c.Import.HashFunction = *NewOptionalString("sha2-256") |
| 362 | c.Import.UnixFSFileMaxLinks = *NewOptionalInteger(1024) |
| 363 | c.Import.UnixFSDirectoryMaxLinks = *NewOptionalInteger(0) |
| 364 | c.Import.UnixFSHAMTDirectoryMaxFanout = *NewOptionalInteger(256) |
| 365 | c.Import.UnixFSHAMTDirectorySizeThreshold = *NewOptionalBytes("256KiB") |
| 366 | c.Import.UnixFSHAMTDirectorySizeEstimation = *NewOptionalString(HAMTSizeEstimationBlock) |
| 367 | c.Import.UnixFSDAGLayout = *NewOptionalString(DAGLayoutBalanced) |
| 368 | return nil |
| 369 | }, |
| 370 | }, |
| 371 | "autoconf-on": { |
| 372 | Description: `Sets configuration to use implicit defaults from remote autoconf service. |
| 373 | Bootstrap peers, DNS resolvers, delegated routers, and IPNS delegated publishers are set to "auto". |
| 374 | This profile requires AutoConf to be enabled and configured.`, |
| 375 | |
| 376 | Transform: func(c *Config) error { |
| 377 | c.Bootstrap = []string{AutoPlaceholder} |
| 378 | c.DNS.Resolvers = map[string]string{ |
| 379 | ".": AutoPlaceholder, |
| 380 | } |
| 381 | c.Routing.DelegatedRouters = []string{AutoPlaceholder} |
| 382 | c.Ipns.DelegatedPublishers = []string{AutoPlaceholder} |
| 383 | c.AutoConf.Enabled = True |
| 384 | if c.AutoConf.URL == nil { |
| 385 | c.AutoConf.URL = NewOptionalString(DefaultAutoConfURL) |
| 386 | } |
| 387 | return nil |
| 388 | }, |
| 389 | }, |
| 390 | "autoconf-off": { |
| 391 | Description: `Disables AutoConf and sets networking fields to empty for manual configuration. |
| 392 | Bootstrap peers, DNS resolvers, delegated routers, and IPNS delegated publishers are set to empty. |
| 393 | Use this when you want normal networking but prefer manual control over all endpoints.`, |
| 394 | |
| 395 | Transform: func(c *Config) error { |
| 396 | c.Bootstrap = nil |
| 397 | c.DNS.Resolvers = nil |
| 398 | c.Routing.DelegatedRouters = nil |
| 399 | c.Ipns.DelegatedPublishers = nil |
| 400 | c.AutoConf.Enabled = False |
| 401 | return nil |
| 402 | }, |
| 403 | }, |
| 404 | } |
| 405 | |
| 406 | func getAvailablePort() (port int, err error) { |
| 407 | ln, err := net.Listen("tcp", "[::]:0") |
| 408 | if err != nil { |
| 409 | return 0, err |
| 410 | } |
| 411 | defer ln.Close() |
| 412 | port = ln.Addr().(*net.TCPAddr).Port |
| 413 | return port, nil |
| 414 | } |
| 415 | |
| 416 | func appendSingle(a []string, b []string) []string { |
| 417 | out := make([]string, 0, len(a)+len(b)) |
| 418 | m := map[string]bool{} |
| 419 | for _, f := range a { |
| 420 | if !m[f] { |
| 421 | out = append(out, f) |
| 422 | } |
| 423 | m[f] = true |
| 424 | } |
| 425 | for _, f := range b { |
| 426 | if !m[f] { |
| 427 | out = append(out, f) |
| 428 | } |
| 429 | m[f] = true |
| 430 | } |
| 431 | return out |
| 432 | } |
| 433 | |
| 434 | func deleteEntries(arr []string, del []string) []string { |
| 435 | m := map[string]struct{}{} |
| 436 | for _, f := range arr { |
| 437 | m[f] = struct{}{} |
| 438 | } |
| 439 | for _, f := range del { |
| 440 | delete(m, f) |
| 441 | } |
| 442 | return mapKeys(m) |
| 443 | } |
| 444 | |
| 445 | func mapKeys(m map[string]struct{}) []string { |
| 446 | out := make([]string, 0, len(m)) |
| 447 | for f := range m { |
| 448 | out = append(out, f) |
| 449 | } |
| 450 | return out |
| 451 | } |
| 452 | |
| 453 | // applyUnixFSv02015 applies the legacy UnixFS v0 (2015) import settings. |
| 454 | func applyUnixFSv02015(c *Config) error { |
| 455 | c.Import.CidVersion = *NewOptionalInteger(0) |
| 456 | c.Import.UnixFSRawLeaves = False |
| 457 | c.Import.UnixFSChunker = *NewOptionalString("size-262144") // 256 KiB |
| 458 | c.Import.HashFunction = *NewOptionalString("sha2-256") |
| 459 | c.Import.UnixFSFileMaxLinks = *NewOptionalInteger(174) |
| 460 | c.Import.UnixFSDirectoryMaxLinks = *NewOptionalInteger(0) |
| 461 | c.Import.UnixFSHAMTDirectoryMaxFanout = *NewOptionalInteger(256) |
| 462 | c.Import.UnixFSHAMTDirectorySizeThreshold = *NewOptionalBytes("256KiB") |
| 463 | c.Import.UnixFSHAMTDirectorySizeEstimation = *NewOptionalString(HAMTSizeEstimationLinks) |
| 464 | c.Import.UnixFSDAGLayout = *NewOptionalString(DAGLayoutBalanced) |
| 465 | return nil |
| 466 | } |