Skip to content

Commit d6a5bc0

Browse files
authored
feat(AutoTLS): enabled by default with 1h RegistrationDelay (#10724)
Enables AutoTLS by default, but delays registration by 1h if user did not explicitly set `AutoTLS.Enabled` to `true`
1 parent 65a9b59 commit d6a5bc0

File tree

15 files changed

+118
-66
lines changed

15 files changed

+118
-66
lines changed

cmd/ipfs/kubo/daemon.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -421,9 +421,16 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
421421
// Private setups should not use public AutoTLS infrastructure
422422
// as it will leak their existence and PeerID identity to CA
423423
// and they will show up at https://crt.sh/?q=libp2p.direct
424-
// Below ensures we hard fail if someone tries to enable both
425-
if cfg.AutoTLS.Enabled.WithDefault(config.DefaultAutoTLSEnabled) {
426-
return errors.New("private networking (swarm.key / LIBP2P_FORCE_PNET) does not work with AutoTLS.Enabled=true, update config to remove this message")
424+
enableAutoTLS := cfg.AutoTLS.Enabled.WithDefault(config.DefaultAutoTLSEnabled)
425+
if enableAutoTLS {
426+
if cfg.AutoTLS.Enabled != config.Default {
427+
// hard fail if someone tries to explicitly enable both
428+
return errors.New("private networking (swarm.key / LIBP2P_FORCE_PNET) does not work with AutoTLS.Enabled=true, update config to remove this message")
429+
} else {
430+
// print error and disable autotls if user runs on default settings
431+
log.Error("private networking (swarm.key / LIBP2P_FORCE_PNET) is not compatible with AutoTLS. Set AutoTLS.Enabled=false in config to remove this message.")
432+
cfg.AutoTLS.Enabled = config.False
433+
}
427434
}
428435
}
429436

config/autotls.go

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package config
22

3-
import p2pforge "github.com/ipshipyard/p2p-forge/client"
3+
import (
4+
"time"
5+
6+
p2pforge "github.com/ipshipyard/p2p-forge/client"
7+
)
48

59
// AutoTLS includes optional configuration of p2p-forge client of service
610
// for obtaining a domain and TLS certificate to improve connectivity for web
@@ -21,6 +25,9 @@ type AutoTLS struct {
2125
// Optional Authorization token, used with private/test instances of p2p-forge
2226
RegistrationToken *OptionalString `json:",omitempty"`
2327

28+
// Optional registration delay used when AutoTLS.Enabled is not explicitly set to true in config
29+
RegistrationDelay *OptionalDuration `json:",omitempty"`
30+
2431
// Optional override of CA ACME API used by p2p-forge system
2532
CAEndpoint *OptionalString `json:",omitempty"`
2633

@@ -29,10 +36,11 @@ type AutoTLS struct {
2936
}
3037

3138
const (
32-
DefaultAutoTLSEnabled = false // experimental, opt-in for now (https://github.com/ipfs/kubo/pull/10521)
33-
DefaultDomainSuffix = p2pforge.DefaultForgeDomain
34-
DefaultRegistrationEndpoint = p2pforge.DefaultForgeEndpoint
35-
DefaultCAEndpoint = p2pforge.DefaultCAEndpoint
36-
DefaultAutoWSS = true // requires AutoTLS.Enabled
37-
DefaultAutoTLSShortAddrs = true // requires AutoTLS.Enabled
39+
DefaultAutoTLSEnabled = true // with DefaultAutoTLSRegistrationDelay, unless explicitly enabled in config
40+
DefaultDomainSuffix = p2pforge.DefaultForgeDomain
41+
DefaultRegistrationEndpoint = p2pforge.DefaultForgeEndpoint
42+
DefaultCAEndpoint = p2pforge.DefaultCAEndpoint
43+
DefaultAutoWSS = true // requires AutoTLS.Enabled
44+
DefaultAutoTLSShortAddrs = true // requires AutoTLS.Enabled
45+
DefaultAutoTLSRegistrationDelay = 1 * time.Hour
3846
)

config/profile.go

+2
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ is useful when using the daemon in test environments.`,
8686

8787
c.Bootstrap = []string{}
8888
c.Discovery.MDNS.Enabled = false
89+
c.AutoTLS.Enabled = False
8990
return nil
9091
},
9192
},
@@ -104,6 +105,7 @@ Inverse profile of the test profile.`,
104105

105106
c.Swarm.DisableNatPortMap = false
106107
c.Discovery.MDNS.Enabled = true
108+
c.AutoTLS.Enabled = Default
107109
return nil
108110
},
109111
},

core/coreapi/test/api_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ func (NodeProvider) MakeAPISwarm(t *testing.T, ctx context.Context, fullIdentity
6969
c.Addresses.Swarm = []string{fmt.Sprintf("/ip4/18.0.%d.1/tcp/4001", i)}
7070
c.Identity = ident
7171
c.Experimental.FilestoreEnabled = true
72+
c.AutoTLS.Enabled = config.False // disable so no /ws listener is added
7273

7374
ds := syncds.MutexWrap(datastore.NewMapDatastore())
7475
r := &repo.Mock{

core/node/groups.go

+14-10
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.Part
112112
autonat = fx.Provide(libp2p.AutoNATService(cfg.AutoNAT.Throttle, true))
113113
}
114114

115+
enableTCPTransport := cfg.Swarm.Transports.Network.TCP.WithDefault(true)
116+
enableWebsocketTransport := cfg.Swarm.Transports.Network.Websocket.WithDefault(true)
115117
enableRelayTransport := cfg.Swarm.Transports.Network.Relay.WithDefault(true) // nolint
116118
enableRelayService := cfg.Swarm.RelayService.Enabled.WithDefault(enableRelayTransport)
117119
enableRelayClient := cfg.Swarm.RelayClient.Enabled.WithDefault(enableRelayTransport)
@@ -129,14 +131,8 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.Part
129131
}
130132
}
131133

132-
if enableAutoTLS {
133-
if !cfg.Swarm.Transports.Network.TCP.WithDefault(true) {
134-
logger.Fatal("Invalid configuration: AutoTLS.Enabled=true requires Swarm.Transports.Network.TCP to be true as well.")
135-
}
136-
if !cfg.Swarm.Transports.Network.Websocket.WithDefault(true) {
137-
logger.Fatal("Invalid configuration: AutoTLS.Enabled=true requires Swarm.Transports.Network.Websocket to be true as well.")
138-
}
139-
134+
switch {
135+
case enableAutoTLS && enableTCPTransport && enableWebsocketTransport:
140136
// AutoTLS for Secure WebSockets: ensure WSS listeners are in place (manual or automatic)
141137
wssWildcard := fmt.Sprintf("/tls/sni/*.%s/ws", cfg.AutoTLS.DomainSuffix.WithDefault(config.DefaultDomainSuffix))
142138
wssWildcardPresent := false
@@ -170,7 +166,8 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.Part
170166
// if no manual /ws listener was set by the user
171167
if enableAutoWSS && !wssWildcardPresent && !customWsPresent {
172168
if len(tcpListeners) == 0 {
173-
logger.Fatal("Invalid configuration: AutoTLS.AutoWSS=true requires at least one /tcp listener present in Addresses.Swarm, see https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls")
169+
logger.Error("Invalid configuration, AutoTLS will be disabled: AutoTLS.AutoWSS=true requires at least one /tcp listener present in Addresses.Swarm, see https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls")
170+
enableAutoTLS = false
174171
}
175172
for _, tcpListener := range tcpListeners {
176173
wssListener := tcpListener + wssWildcard
@@ -180,8 +177,15 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.Part
180177
}
181178

182179
if !wssWildcardPresent && !enableAutoWSS {
183-
logger.Fatal(fmt.Sprintf("Invalid configuration: AutoTLS.Enabled=true requires a /tcp listener ending with %q to be present in Addresses.Swarm or AutoTLS.AutoWSS=true, see https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls", wssWildcard))
180+
logger.Error(fmt.Sprintf("Invalid configuration, AutoTLS will be disabled: AutoTLS.Enabled=true requires a /tcp listener ending with %q to be present in Addresses.Swarm or AutoTLS.AutoWSS=true, see https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls", wssWildcard))
181+
enableAutoTLS = false
184182
}
183+
case enableAutoTLS && !enableTCPTransport:
184+
logger.Error("Invalid configuration: AutoTLS.Enabled=true requires Swarm.Transports.Network.TCP to be true as well. AutoTLS will be disabled.")
185+
enableAutoTLS = false
186+
case enableAutoTLS && !enableWebsocketTransport:
187+
logger.Error("Invalid configuration: AutoTLS.Enabled=true requires Swarm.Transports.Network.Websocket to be true as well. AutoTLS will be disabled.")
188+
enableAutoTLS = false
185189
}
186190

187191
// Gather all the options

core/node/libp2p/addrs.go

+9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"path/filepath"
8+
"time"
89

910
logging "github.com/ipfs/go-log"
1011
version "github.com/ipfs/kubo"
@@ -145,11 +146,19 @@ func P2PForgeCertMgr(repoPath string, cfg config.AutoTLS, atlsLog *logging.ZapEv
145146
certmagic.Default.Logger = rawLogger.Named("default_fixme")
146147
certmagic.DefaultACME.Logger = rawLogger.Named("default_acme_client_fixme")
147148

149+
registrationDelay := cfg.RegistrationDelay.WithDefault(config.DefaultAutoTLSRegistrationDelay)
150+
if cfg.Enabled == config.True && cfg.RegistrationDelay.IsDefault() {
151+
// Skip delay if user explicitly enabled AutoTLS.Enabled in config
152+
// and did not set custom AutoTLS.RegistrationDelay
153+
registrationDelay = 0 * time.Second
154+
}
155+
148156
certStorage := &certmagic.FileStorage{Path: storagePath}
149157
certMgr, err := p2pforge.NewP2PForgeCertMgr(
150158
p2pforge.WithLogger(rawLogger.Sugar()),
151159
p2pforge.WithForgeDomain(cfg.DomainSuffix.WithDefault(config.DefaultDomainSuffix)),
152160
p2pforge.WithForgeRegistrationEndpoint(cfg.RegistrationEndpoint.WithDefault(config.DefaultRegistrationEndpoint)),
161+
p2pforge.WithRegistrationDelay(registrationDelay),
153162
p2pforge.WithCAEndpoint(cfg.CAEndpoint.WithDefault(config.DefaultCAEndpoint)),
154163
p2pforge.WithForgeAuth(cfg.RegistrationToken.WithDefault(os.Getenv(p2pforge.ForgeAuthEnv))),
155164
p2pforge.WithUserAgent(version.GetUserAgentVersion()),

docs/changelogs/v0.34.md

+26-3
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,40 @@
66

77
- [Overview](#overview)
88
- [🔦 Highlights](#-highlights)
9+
- [AutoTLS now enabled by default for nodes with 1 hour uptime](#autotls-now-enabled-by-default-for-nodes-with-1-hour-uptime)
910
- [RPC and CLI command changes](#rpc-and-cli-command-changes)
1011
- [Bitswap improvements from Boxo](#bitswap-improvements-from-boxo)
11-
- [IPFS_LOG_LEVEL deprecated](#ipfs_log_level-deprecated)
12-
- [Pebble datastore format upgrade](#pebble_datastore_format_update)
13-
- [Badger datastore update](#badger_datastore_update)
12+
- [`IPFS_LOG_LEVEL` deprecated](#ipfs_log_level-deprecated)
13+
- [Pebble datastore format update](#pebble-datastore-format-update)
14+
- [Badger datastore update](#badger-datastore-update)
1415
- [👨‍👩‍👧‍👦 Contributors](#-contributors)
1516

1617
### Overview
1718

1819
### 🔦 Highlights
1920

21+
#### AutoTLS now enabled by default for nodes with 1 hour uptime
22+
23+
Starting now, any publicly dialable Kubo node with a `/tcp` listener that remains online for at least one hour will receive a TLS certificate through the [`AutoTLS`](https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls) feature.
24+
This occurs automatically, with no need for manual setup.
25+
26+
To bypass the 1-hour delay and enable AutoTLS immediately, users can explicitly opt-in by running the following commands:
27+
28+
```console
29+
$ ipfs config --json AutoTLS.Enabled true
30+
$ ipfs config --json AutoTLS.RegistrationDelay 0
31+
```
32+
33+
AutoTLS will remain disabled under the following conditions:
34+
35+
- The node already has a manually configured `/ws` (WebSocket) listener
36+
- A private network is in use with a `swarm.key`
37+
- TCP or WebSocket transports are disabled, or there is no `/tcp` listener
38+
39+
To troubleshoot, use `GOLOG_LOG_LEVEL="error,autotls=info`.
40+
41+
For more details, check out the [`AutoTLS` configuration documentation](https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls) or dive deeper with [AutoTLS libp2p blog post](https://blog.libp2p.io/autotls/).
42+
2043
#### RPC and CLI command changes
2144

2245
- `ipfs config` is now validating json fields ([#10679](https://github.com/ipfs/kubo/pull/10679)).

docs/config.md

+29-34
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ config file at runtime.
3434
- [`AutoTLS.DomainSuffix`](#autotlsdomainsuffix)
3535
- [`AutoTLS.RegistrationEndpoint`](#autotlsregistrationendpoint)
3636
- [`AutoTLS.RegistrationToken`](#autotlsregistrationtoken)
37+
- [`AutoTLS.RegistrationDelay`](#autotlsregistrationdelay)
3738
- [`AutoTLS.CAEndpoint`](#autotlscaendpoint)
3839
- [`Bootstrap`](#bootstrap)
3940
- [`Datastore`](#datastore)
@@ -463,12 +464,7 @@ Type: `duration` (when `0`/unset, the default value is used)
463464

464465
## `AutoTLS`
465466

466-
> [!CAUTION]
467-
> This is an **EXPERIMENTAL** opt-in feature and should not be used in production yet.
468-
> Feel free to enable it and [report issues](https://github.com/ipfs/kubo/issues/new/choose) if you want to help with testing.
469-
> Track progress in [kubo#10560](https://github.com/ipfs/kubo/issues/10560).
470-
471-
AutoTLS feature enables publicly reachable Kubo nodes (those dialable from the public
467+
The [AutoTLS](https://blog.libp2p.io/autotls/) feature enables publicly reachable Kubo nodes (those dialable from the public
472468
internet) to automatically obtain a wildcard TLS certificate for a DNS name
473469
unique to their PeerID at `*.[PeerID].libp2p.direct`. This enables direct
474470
libp2p connections and retrieval of IPFS content from browsers [Secure Context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts)
@@ -480,11 +476,11 @@ broker enabling peer to obtain a wildcard TLS certificate tied to public key of
480476

481477
By default, the certificates are requested from Let's Encrypt. Origin and rationale for this project can be found in [community.letsencrypt.org discussion](https://community.letsencrypt.org/t/feedback-on-raising-certificates-per-registered-domain-to-enable-peer-to-peer-networking/223003).
482478

479+
<a href="https://ipshipyard.com/"><img align="right" src="https://github.com/user-attachments/assets/39ed3504-bb71-47f6-9bf8-cb9a1698f272" /></a>
480+
483481
> [!NOTE]
484482
> Public good DNS and [p2p-forge] infrastructure at `libp2p.direct` is run by the team at [Interplanetary Shipyard](https://ipshipyard.com).
485483
>
486-
> <a href="https://ipshipyard.com/"><img src="https://github.com/user-attachments/assets/39ed3504-bb71-47f6-9bf8-cb9a1698f272" /></a>
487-
488484
[p2p-forge]: https://github.com/ipshipyard/p2p-forge
489485

490486
Default: `{}`
@@ -493,50 +489,38 @@ Type: `object`
493489

494490
### `AutoTLS.Enabled`
495491

496-
> [!CAUTION]
497-
> This is an **EXPERIMENTAL** opt-in feature and should not be used in production yet.
498-
> Feel free to enable it and [report issues](https://github.com/ipfs/kubo/issues/new/choose) if you want to help with testing.
499-
> Track progress in [kubo#10560](https://github.com/ipfs/kubo/issues/10560).
492+
Enables the AutoTLS feature to provide DNS and TLS support for [libp2p Secure WebSocket](https://github.com/libp2p/specs/blob/master/websockets/README.md) over a `/tcp` port,
493+
to allow JS clients running in web browser [Secure Context](https://w3c.github.io/webappsec-secure-contexts/) to connect to Kubo directly.
500494

501-
Enables AutoTLS feature to get DNS+TLS for [libp2p Secure WebSocket](https://github.com/libp2p/specs/blob/master/websockets/README.md) on `/tcp` port.
495+
When activated, together with [`AutoTLS.AutoWSS`](#autotlsautowss) (default) or manually including a `/tcp/{port}/tls/sni/*.libp2p.direct/ws` multiaddr in [`Addresses.Swarm`](#addressesswarm)
496+
(with SNI suffix matching [`AutoTLS.DomainSuffix`](#autotlsdomainsuffix)), Kubo retrieves a trusted PKI TLS certificate for `*.{peerid}.libp2p.direct` and configures the `/ws` listener to use it.
502497

503-
If `AutoTLS.AutoWSS` is `true`, or `/tcp/../tls/sni/*.libp2p.direct/ws` [multiaddr] is present in [`Addresses.Swarm`](#addressesswarm)
504-
with SNI segment ending with [`AutoTLS.DomainSuffix`](#autotlsdomainsuffix),
505-
Kubo will obtain and set up a trusted PKI TLS certificate for `*.peerid.libp2p.direct`, making it dialable from web browser's [Secure Contexts](https://w3c.github.io/webappsec-secure-contexts/).
498+
**Note:**
506499

507-
> [!TIP]
508-
> - Most users don't need custom `/ws` config in `Addresses.Swarm`. Try running this with `AutoTLS.AutoWSS=true`: it will reuse preexisting catch-all `/tcp` ports that were already forwarded/safelisted on your firewall.
509-
> - Debugging can be enabled by setting environment variable `GOLOG_LOG_LEVEL="error,autotls=debug,p2p-forge/client=debug"`. Less noisy `GOLOG_LOG_LEVEL="error,autotls=info` may be informative enough.
510-
> - Certificates are stored in `$IPFS_PATH/p2p-forge-certs`. Removing directory and restarting daemon will trigger certificate rotation.
511-
512-
> [!IMPORTANT]
513-
> Caveats:
514-
> - Requires your Kubo node to be publicly dialable.
515-
> - If you want to test this with a node that is behind a NAT and uses manual TCP port forwarding or UPnP (`Swarm.DisableNatPortMap=false`), use `AutoTLS.AutoWSS=true`, or manually
516-
> add catch-all `/ip4/0.0.0.0/tcp/4001/tls/sni/*.libp2p.direct/ws` and `/ip6/::/tcp/4001/tls/sni/*.libp2p.direct/ws` to [`Addresses.Swarm`](#addressesswarm)
517-
> and **wait 5-15 minutes** for libp2p node to set up and learn about own public addresses via [AutoNAT](#autonat).
518-
> - If your node is fresh and just started, the [p2p-forge] client may produce and log ERRORs during this time, but once a publicly dialable addresses are set up, a subsequent retry should be successful.
519-
> - The TLS certificate is used only for [libp2p WebSocket](https://github.com/libp2p/specs/blob/master/websockets/README.md) connections.
520-
> - Right now, this is NOT used for hosting a [Gateway](#gateway) over HTTPS (that use case still requires manual TLS setup on reverse proxy, and your own domain).
500+
- This feature requires a publicly reachable node. If behind NAT, manual port forwarding or UPnP (`Swarm.DisableNatPortMap=false`) is required.
501+
- The first time AutoTLS is used, it may take 5-15 minutes + [`AutoTLS.RegistrationDelay`](#autotlsregistrationdelay) before `/ws` listener is added. Be patient.
502+
- Avoid manual configuration. [`AutoTLS.AutoWSS=true`](#autotlsautowss) should automatically add `/ws` listener to existing, firewall-forwarded `/tcp` ports.
503+
- To troubleshoot, use `GOLOG_LOG_LEVEL="error,autotls=debug` for detailed logs, or `GOLOG_LOG_LEVEL="error,autotls=info` for quieter output.
504+
- Certificates are stored in `$IPFS_PATH/p2p-forge-certs`; deleting this directory and restarting the daemon forces a certificate rotation.
505+
- For now, the TLS cert applies solely to `/ws` libp2p WebSocket connections, not HTTP [`Gateway`](#gateway), which still need separate reverse proxy TLS setup with a custom domain.
521506

522-
Default: `false`
507+
Default: `true`
523508

524509
Type: `flag`
525510

526511
### `AutoTLS.AutoWSS`
527512

528513
Optional. Controls if Kubo should add `/tls/sni/*.libp2p.direct/ws` listener to every pre-existing `/tcp` port IFF no explicit `/ws` is defined in [`Addresses.Swarm`](#addressesswarm) already.
529514

530-
Default: `true` (active only if `AutoTLS.Enabled` is `true` as well)
515+
Default: `true` (if `AutoTLS.Enabled`)
531516

532517
Type: `flag`
533518

534519
### `AutoTLS.ShortAddrs`
535520

536521
Optional. Controls if final AutoTLS listeners are announced under shorter `/dnsX/A.B.C.D.peerid.libp2p.direct/tcp/4001/tls/ws` addresses instead of fully resolved `/ip4/A.B.C.D/tcp/4001/tls/sni/A-B-C-D.peerid.libp2p.direct/tls/ws`.
537522

538-
> [!TIP]
539-
> The main use for AutoTLS is allowing connectivity from Secure Context in a web browser, and DNS lookup needs to happen there anyway, making `/dnsX` a more compact, more interoperable option without obvious downside.
523+
The main use for AutoTLS is allowing connectivity from Secure Context in a web browser, and DNS lookup needs to happen there anyway, making `/dnsX` a more compact, more interoperable option without obvious downside.
540524

541525
Default: `true`
542526

@@ -574,6 +558,17 @@ Default: `""`
574558

575559
Type: `optionalString`
576560

561+
### `AutoTLS.RegistrationDelay`
562+
563+
An additional delay applied before sending a request to the `RegistrationEndpoint`.
564+
565+
The default delay is bypassed if the user explicitly set `AutoTLS.Enabled=true` in the JSON configuration file.
566+
This ensures that ephemeral nodes using the default configuration do not spam the`AutoTLS.CAEndpoint` with unnecessary ACME requests.
567+
568+
Default: `1h` (or `0` if explicit `AutoTLS.Enabled=true`)
569+
570+
Type: `optionalDuration`
571+
577572
### `AutoTLS.CAEndpoint`
578573

579574
Optional override of CA ACME API used by [p2p-forge] system.

docs/examples/kubo-as-a-library/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ require (
111111
github.com/ipld/go-car/v2 v2.14.2 // indirect
112112
github.com/ipld/go-codec-dagpb v1.6.0 // indirect
113113
github.com/ipld/go-ipld-prime v0.21.0 // indirect
114-
github.com/ipshipyard/p2p-forge v0.3.0 // indirect
114+
github.com/ipshipyard/p2p-forge v0.4.0 // indirect
115115
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
116116
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
117117
github.com/jbenet/goprocess v0.1.4 // indirect

0 commit comments

Comments
 (0)