diff --git a/.vscode/launch.json b/.vscode/launch.json
index 757d8ee..d8729d5 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -29,6 +29,18 @@
"~/.ma/pong.yaml"
],
"console": "integratedTerminal"
+ },
+ {
+ "name": "Show config",
+ "type": "go",
+ "request": "launch",
+ "mode": "auto",
+ "program": "${fileDirname}",
+ "args": [
+ "--show_config",
+ ],
+ "console": "integratedTerminal"
}
+
]
}
\ No newline at end of file
diff --git a/README.md b/README.md
index 4fb5e58..c26c772 100644
--- a/README.md
+++ b/README.md
@@ -2,11 +2,24 @@
This is go-ma-actor based on [an example from go-libp2p][src].
-Now you can either run with `go run`, or build and run the binary:
+## Requirements
+
+This is a distributed app that relies heavily on the [libp2p](https://libp2p.io/) stack
+and [IPFS][ipfs] in particular. It's unusable unless you have a running IPFS node.
+
+I suggest using [Brave Browser][brave] or [IPFS Desktop][desktop] to run and IPFS node.
+
+*By using Brave browser your can run an IPFS node without installing anything.
+And you can investigate the IPFS network with the built-in IPFS node.
+It provides The ability to browse IPFS properly, and to pin files and directories.*
+
+## TL;DR
```bash
-# Generate persistent environment variables of *SECRET* keysets
-eval $(go run . -genenv -forcePublish | tee .env)
+
+# Generate persistent config file with *SECRETS*
+# It needs to be published to the IPFS network to be useful
+./go-ma-actor --generate --nick "asj" --publish > actor.yaml
./go-ma-actor # Share and enjoy!
```
@@ -16,30 +29,17 @@ type `./go-ma-actor -help`. Most config settings can be set with environment var
```bash
export GO_MA_LOG_LEVEL="error"
-export GO_MA_DISCOVERY_TIMEOUT="300"
-export GO_MA_KEYSET="myBase58EncodedPrivkeyGeneratedByGenerate"
+export GO_MA_LIBP2P_DISCOVERY_TIMEOUT="300"
+export GO_MA_ACTOR_IDENTITY="myBase58EncodedPrivkeyGeneratedByGenerate"
```
## Identity
-A `-generate` or `genenv` parameter to generate a text version of a secret key.
-The key is text formatted privKey for your node.
-
-This key can and should be kept safely on a PostIt note on your monitor :-)
-Just don't store somewhere insecure. It's your future identity.
-
-```bash
-unset HISTFILE
- export GO_MA_ACTOR_KEYSET=FooBarABCDEFbase58
-```
-
-or specified on the command line:
-
-```bash
-./go-ma-actor -keyset FooBarABCDEFbase58
-```
+A `-generate` flag is available to generate a new identity.
+It uses defaults, BUT it generates a new random identity.
-The first is the best. (Noticed that in most shells the empty space before the command, means that the line isn't saved in history.)
+You can use the output as your future identity, but keep it secret.
+Those identities are used to sign messages, and to encrypt and decrypt private messages.
## Usage
@@ -49,8 +49,14 @@ To quit, hit `Ctrl-C`, or type `/quit` into the input field.
- /status [sub|topic|host]
- /discover
+- /alias [node|entity] set [DID|NAME] NAME
+- /aliases
+- /whereis [DID|NAME]
+- /msg Name Message
- /enter room
-- /nick Name
- /refresh
[src]: https://github.com/libp2p/go-libp2p/tree/master/examples/pubsub/chat
+[brave]: (Recommended Browser for 間)
+[desktop]: (IPFS Desktop)
+[ipfs]: (IPFS)
diff --git a/alias/alias.go b/alias/alias.go
index 8888ea3..e61ac9c 100644
--- a/alias/alias.go
+++ b/alias/alias.go
@@ -10,22 +10,19 @@ import (
"github.com/libp2p/go-libp2p/core/peer"
_ "github.com/mattn/go-sqlite3"
log "github.com/sirupsen/logrus"
- "github.com/spf13/pflag"
- "github.com/spf13/viper"
)
const (
- defaultAliasFile = "~/.ma/aliases.db"
defaultAliasLength = 8
- SELECT_ENTITY_NICK = "SELECT nick FROM entities WHERE did = ?"
- SELECT_ENTITY_DID = "SELECT did FROM entities WHERE nick = ?"
- SELECT_NODE_NICK = "SELECT nick FROM nodes WHERE id = ?"
- SELECT_NODE_ID = "SELECT id FROM nodes WHERE nick = ?"
- UPSERT_ENTITY = "INSERT INTO entities (did, nick) VALUES (?, ?) ON CONFLICT(did) DO UPDATE SET nick = ?"
- UPSERT_NODE = "INSERT INTO nodes (id, nick) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET nick = ?"
- DELETE_ENTITY = "DELETE FROM entities WHERE did = ?"
- DELETE_NODE = "DELETE FROM nodes WHERE id = ?"
+ _SELECT_ENTITY_NICK = "SELECT nick FROM entities WHERE did = ?"
+ _SELECT_ENTITY_DID = "SELECT did FROM entities WHERE nick = ?"
+ _SELECT_NODE_NICK = "SELECT nick FROM nodes WHERE id = ?"
+ _SELECT_NODE_ID = "SELECT id FROM nodes WHERE nick = ?"
+ _UPSERT_ENTITY = "INSERT INTO entities (did, nick) VALUES (?, ?) ON CONFLICT(did) DO UPDATE SET nick = ?"
+ _UPSERT_NODE = "INSERT INTO nodes (id, nick) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET nick = ?"
+ _DELETE_ENTITY = "DELETE FROM entities WHERE did = ?"
+ _DELETE_NODE = "DELETE FROM nodes WHERE id = ?"
)
var (
@@ -33,13 +30,6 @@ var (
db *sql.DB
)
-func init() {
-
- pflag.String("aliases", defaultAliasFile, "File to *write* node aliases to. If the file does not exist, it will be created.")
- viper.BindPFlag("aliases", pflag.Lookup("aliases"))
- viper.SetDefault("aliases", defaultAliasFile)
-}
-
// Initiates the database connection and creates the tables if they do not exist
func GetDB() (*sql.DB, error) {
@@ -89,7 +79,7 @@ func GetEntityAlias(id string) (string, error) {
var a string
- err = db.QueryRow(SELECT_ENTITY_NICK, id).Scan(&a)
+ err = db.QueryRow(_SELECT_ENTITY_NICK, id).Scan(&a)
if err != nil {
return "", err
}
@@ -112,7 +102,7 @@ func GetEntityDID(nick string) (string, error) {
var id string
- err = db.QueryRow(SELECT_ENTITY_DID, nick).Scan(&id)
+ err = db.QueryRow(_SELECT_ENTITY_DID, nick).Scan(&id)
if err != nil {
return "", err
}
@@ -136,7 +126,7 @@ func GetNodeAlias(id string) (string, error) {
var a string
- err = db.QueryRow(SELECT_NODE_NICK, id).Scan(&a)
+ err = db.QueryRow(_SELECT_NODE_NICK, id).Scan(&a)
if err != nil {
return "", err
}
@@ -159,7 +149,7 @@ func GetNodeID(nick string) (string, error) {
var id string
- err = db.QueryRow(SELECT_NODE_ID, nick).Scan(&id)
+ err = db.QueryRow(_SELECT_NODE_ID, nick).Scan(&id)
if err != nil {
return "", err
}
@@ -180,7 +170,7 @@ func SetEntityAlias(id string, nick string) error {
return err
}
- _, err = db.Exec(UPSERT_ENTITY, id, nick, nick)
+ _, err = db.Exec(_UPSERT_ENTITY, id, nick, nick)
if err != nil {
return err
}
@@ -202,7 +192,7 @@ func SetNodeAlias(id string, nick string) error {
return err
}
- _, err = db.Exec(UPSERT_NODE, id, nick, nick)
+ _, err = db.Exec(_UPSERT_NODE, id, nick, nick)
if err != nil {
return err
}
@@ -222,7 +212,7 @@ func RemoveEntityAlias(id string) error {
return err
}
- _, err = db.Exec(DELETE_ENTITY, id)
+ _, err = db.Exec(_DELETE_ENTITY, id)
if err != nil {
return err
}
diff --git a/config/actor.go b/config/actor.go
index e80e190..6fdd333 100644
--- a/config/actor.go
+++ b/config/actor.go
@@ -66,15 +66,25 @@ func InitActor() {
}
-func handleGenerateOrExit() {
+// Genreates a libp2p and actor identity and returns the keyset and the actor identity
+// These are imperative, so failure to generate them is a fatal error.
+func handleGenerateOrExit() (string, string) {
+
// Generate a new keysets if requested
+ nick := viper.GetString("actor.nick")
- keyset_string, err := generateAndPrintActorIdentity()
+ keyset_string, err := generateKeysetString(nick)
if err != nil {
log.Errorf("config.initIdentity: Failed to generate keyset: %v", err)
os.Exit(70) // EX_SOFTWARE
}
+ ni, err := generateNodeIdentity()
+ if err != nil {
+ log.Errorf("config.initIdentity: Failed to generate node identity: %v", err)
+ os.Exit(70) // EX_SOFTWARE
+ }
+
if viper.GetBool("publish") {
err = publishActorIdentityFromString(keyset_string)
if err != nil {
@@ -83,26 +93,7 @@ func handleGenerateOrExit() {
}
}
- err = generateAndPrintNodeIdentity()
- if err != nil {
- log.Errorf("config.initIdentity: Failed to generate node identity: %v", err)
- os.Exit(70) // EX_SOFTWARE
- }
-
-}
-
-func generateAndPrintActorIdentity() (string, error) {
-
- nick := viper.GetString("actor.nick")
-
- keyset_string, err := generateKeyset(nick)
- if err != nil {
- return "", fmt.Errorf("config.initIdentity: Failed to generate keyset: %v", err)
- }
-
- fmt.Println(ENV_PREFIX + "_ACTOR_IDENTITY=" + keyset_string)
-
- return keyset_string, nil
+ return keyset_string, ni
}
func publishActorIdentityFromString(keyset_string string) error {
@@ -120,7 +111,8 @@ func publishActorIdentityFromString(keyset_string string) error {
return nil
}
-func generateKeyset(nick string) (string, error) {
+// Generates a new keyset and returns the keyset as a string
+func generateKeysetString(nick string) (string, error) {
ks, err := set.GetOrCreate(nick)
if err != nil {
diff --git a/config/alias.go b/config/alias.go
index 8498c0a..1d9a204 100644
--- a/config/alias.go
+++ b/config/alias.go
@@ -2,9 +2,19 @@ package config
import (
"github.com/mitchellh/go-homedir"
+ "github.com/spf13/pflag"
"github.com/spf13/viper"
)
+const defaultAliases = "~/.ma/aliases.db"
+
+func init() {
+
+ pflag.String("aliases", defaultAliases, "File to *write* node aliases to. If the file does not exist, it will be created.")
+ viper.BindPFlag("aliases", pflag.Lookup("aliases"))
+ viper.SetDefault("aliases", defaultAliases)
+}
+
// Returns expanded path to the aliases file
// If the expansion fails it returns an empty string
func GetAliases() string {
diff --git a/config/config.go b/config/config.go
index 329ff54..d3d217a 100644
--- a/config/config.go
+++ b/config/config.go
@@ -7,6 +7,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/pflag"
"github.com/spf13/viper"
+ "gopkg.in/yaml.v2"
)
const (
@@ -32,6 +33,10 @@ func init() {
// Allow to set config file via command line flag.
pflag.StringVarP(&configFile, "config", "c", "", "Config file to use.")
+ pflag.Bool("show-config", false, "Whether to print the config.")
+ viper.BindPFlag("show-config", pflag.Lookup("show-config"))
+ pflag.Bool("show-defaults", false, "Whether to print the config.")
+ viper.BindPFlag("show-defaults", pflag.Lookup("show-defaults"))
pflag.BoolP("version", "v", false, "Print version and exit.")
viper.BindPFlag("version", pflag.Lookup("version"))
@@ -62,7 +67,27 @@ func Init(configName string) error {
// This will exit when done. It will also publish if applicable.
if viper.GetBool("generate") {
log.Info("Generating new keyset and node identity")
- handleGenerateOrExit()
+ actor, node := handleGenerateOrExit()
+ generateConfigFile(actor, node)
+ os.Exit(0)
+ }
+
+ if viper.GetBool("show-config") {
+ configMap := viper.AllSettings()
+ configYAML, err := yaml.Marshal(configMap)
+ if err != nil {
+ log.Fatalf("error: %v", err)
+ }
+
+ // Print the YAML to stdout or write it to a template file
+ fmt.Println(string(configYAML))
+ os.Exit(0)
+ }
+
+ if viper.GetBool("show-defaults") {
+
+ // Print the YAML to stdout or write it to a template file
+ generateConfigFile("zNO_DEFAULT_ACTOR_IDENITY", "zNO_DEFAULT_NODE_IDENITY")
os.Exit(0)
}
diff --git a/config/generate.go b/config/generate.go
new file mode 100644
index 0000000..9c56d60
--- /dev/null
+++ b/config/generate.go
@@ -0,0 +1,63 @@
+package config
+
+import (
+ "fmt"
+
+ "github.com/bahner/go-ma"
+ "github.com/bahner/go-ma/key/set"
+ log "github.com/sirupsen/logrus"
+ "gopkg.in/yaml.v2"
+)
+
+// Well, actually find a nice one for this!
+const defaultHome = "did:ma:k2k4r8kzkhamrqz9m5yy0tihj1fso3t6znnuidu00dbtnh3plazatrfw#pong"
+
+func generateConfigFile(actor string, node string) {
+
+ ks, err := set.Unpack(actor)
+ if err != nil {
+ log.Fatalf("error: %v", err)
+ }
+
+ // Get the default settings as a map
+ // Note: Viper does not have a built-in way to directly extract only the defaults
+ // so we manually recreate the structure based on the defaults we have set.
+ defaults := map[string]interface{}{
+ "actor": map[string]interface{}{
+ "identity": actor,
+ "home": defaultHome,
+ },
+ "aliases": defaultAliases,
+ "log": map[string]interface{}{
+ "level": defaultLogLevel,
+ "file": defaultLogfile,
+ },
+ "api": map[string]interface{}{
+ "maddr": ma.DEFAULT_IPFS_API_MULTIADDR,
+ },
+ "http": map[string]interface{}{
+ "socket": defaultHttpSocket,
+ },
+ "libp2p": map[string]interface{}{
+ "identity": node,
+ "port": defaultListenPort,
+ "connmgr": map[string]interface{}{
+ "low-watermark": defaultConnmgrLowWatermark,
+ "high-watermark": defaultConnmgrHighWatermark,
+ "grace-period": defaultConnmgrGracePeriod,
+ },
+ "discovery-retry": defaultDiscoveryRetryInterval,
+ "discovery-timeout": defaultDiscoveryTimeout,
+ },
+ }
+
+ // Convert the map of defaults to YAML
+ defaultsYAML, err := yaml.Marshal(defaults)
+ if err != nil {
+ log.Fatalf("error: %v", err)
+ }
+
+ // Print the YAML defaults
+ fmt.Println("# " + ks.DID.String())
+ fmt.Println(string(defaultsYAML))
+}
diff --git a/config/logging.go b/config/logging.go
index e5e07df..6839c8e 100644
--- a/config/logging.go
+++ b/config/logging.go
@@ -17,13 +17,13 @@ const (
func init() {
- pflag.String("loglevel", defaultLogLevel, "Loglevel to use for application.")
+ pflag.String("log-level", defaultLogLevel, "Loglevel to use for application.")
viper.SetDefault("log.level", defaultLogLevel)
- viper.BindPFlag("log.level", pflag.Lookup("loglevel"))
+ viper.BindPFlag("log.level", pflag.Lookup("log-level"))
- pflag.String("logfile", defaultLogfile, "Logfile to use for application. Accepts 'STDERR' and 'STDOUT' as such.")
+ pflag.String("log-file", defaultLogfile, "Logfile to use for application. Accepts 'STDERR' and 'STDOUT' as such.")
viper.SetDefault("log.file", defaultLogfile)
- viper.BindPFlag("log.file", pflag.Lookup("logfile"))
+ viper.BindPFlag("log.file", pflag.Lookup("log-file"))
}
func InitLogging() {
diff --git a/config/p2p.go b/config/p2p.go
index 8ee381f..a047dad 100644
--- a/config/p2p.go
+++ b/config/p2p.go
@@ -14,45 +14,41 @@ import (
)
const (
- defaultLowWaterMark int = 10
- defaultHighWaterMark int = 30
- defaultListenPort int = 0
+ defaultConnmgrLowWatermark int = 10
+ defaultConnmgrHighWatermark int = 30
+ defaultListenPort int = 0
defaultDiscoveryTimeout time.Duration = time.Second * 20
- defaultConnMgrGrace time.Duration = time.Minute * 1
+ defaultConnmgrGracePeriod time.Duration = time.Minute * 1
defaultDiscoveryRetryInterval time.Duration = time.Second * 300
)
func init() {
// P2P Settings
- pflag.Int("low_watermark", defaultLowWaterMark, "Low watermark for peer discovery.")
- viper.SetDefault("libp2p.connmgr.low_watermark", defaultLowWaterMark)
- viper.BindPFlag("libp2p.connmgr.low_watermark", pflag.Lookup("low_watermark"))
+ pflag.Int("low-watermark", defaultConnmgrLowWatermark, "Low watermark for peer discovery.")
+ viper.SetDefault("libp2p.connmgr.low-watermark", defaultConnmgrLowWatermark)
+ viper.BindPFlag("libp2p.connmgr.low-watermark", pflag.Lookup("low-watermark"))
- pflag.Int("high_watermark", defaultHighWaterMark, "High watermark for peer discovery.")
- viper.SetDefault("libp2p.connmgr.high_watermark", defaultHighWaterMark)
- viper.BindPFlag("libp2p.connmgr.high_watermark", pflag.Lookup("high_watermark"))
+ pflag.Int("high-watermark", defaultConnmgrHighWatermark, "High watermark for peer discovery.")
+ viper.SetDefault("libp2p.connmgr.high-watermark", defaultConnmgrHighWatermark)
+ viper.BindPFlag("libp2p.connmgr.high-watermark", pflag.Lookup("high-watermark"))
- // pflag.Int("desired_peers", defaultDesiredPeers, "Desired number of peers to connect to.")
- // viper.SetDefault("libp2p.connmgr.desired_peers", defaultDesiredPeers)
- // viper.BindPFlag("libp2p.connmgr.desired_peers", pflag.Lookup("desired_peers"))
+ pflag.Duration("grace-period", defaultConnmgrGracePeriod, "Grace period for connection manager.")
+ viper.SetDefault("libp2p.connmgr.grace-period", defaultConnmgrGracePeriod)
+ viper.BindPFlag("libp2p.connmgr.grace-period", pflag.Lookup("grace-period"))
- pflag.Duration("grace_period", defaultConnMgrGrace, "Grace period for connection manager.")
- viper.SetDefault("libp2p.connmgr.grace_period", defaultConnMgrGrace)
- viper.BindPFlag("libp2p.connmgr.grace_period", pflag.Lookup("grace_period"))
+ pflag.Duration("discovery-retry", defaultDiscoveryRetryInterval, "Retry interval for peer discovery.")
+ viper.SetDefault("libp2p.discovery-retry", defaultDiscoveryRetryInterval)
+ viper.BindPFlag("libp2p.discovery-retry", pflag.Lookup("discovery-retryl"))
- pflag.Duration("discovery_retry", defaultDiscoveryRetryInterval, "Retry interval for peer discovery.")
- viper.SetDefault("libp2p.discovery_retry", defaultDiscoveryRetryInterval)
- viper.BindPFlag("libp2p.discovery_retry", pflag.Lookup("discovery_retryl"))
+ pflag.Duration("discovery-timeout", defaultDiscoveryTimeout, "Timeout for peer discovery.")
+ viper.SetDefault("libp2p.discovery-timeout", defaultDiscoveryTimeout)
+ viper.BindPFlag("libp2p.discovery-timeout", pflag.Lookup("discoveryTimeout"))
- pflag.Duration("discovery_timeout", defaultDiscoveryTimeout, "Timeout for peer discovery.")
- viper.SetDefault("libp2p.discovery_timeout", defaultDiscoveryTimeout)
- viper.BindPFlag("libp2p.connmgr.discovery_timeout", pflag.Lookup("discoveryTimeout"))
-
- pflag.Int("listen_port", defaultListenPort, "Port for libp2p node to listen on.")
+ pflag.Int("listen-port", defaultListenPort, "Port for libp2p node to listen on.")
viper.SetDefault("libp2p.port", defaultListenPort)
- viper.BindPFlag("libp2p.port", pflag.Lookup("listen_port"))
+ viper.BindPFlag("libp2p.port", pflag.Lookup("listen-port"))
}
// P2P Node identity
@@ -94,18 +90,6 @@ func GetNodeIdentity() crypto.PrivKey {
}
-func generateAndPrintNodeIdentity() error {
-
- p2pPrivKey, err := generateNodeIdentity()
- if err != nil {
- return fmt.Errorf("config.initIdentity: Failed to generate node identity: %v", err)
- }
-
- fmt.Println(ENV_PREFIX + "_LIBP2P_IDENTITY=" + p2pPrivKey)
-
- return nil
-}
-
func generateNodeIdentity() (string, error) {
pk, _, err := crypto.GenerateKeyPair(crypto.Ed25519, -1)
if err != nil {
@@ -141,7 +125,7 @@ func GetDiscoveryContext() (context.Context, func()) {
}
func GetDiscoveryTimeout() time.Duration {
- return time.Duration(viper.GetDuration("libp2p.discovery_timeout"))
+ return time.Duration(viper.GetDuration("libp2p.discovery-timeout"))
}
func GetDiscoveryTimeoutString() string {
@@ -149,7 +133,7 @@ func GetDiscoveryTimeoutString() string {
}
func GetLowWatermark() int {
- return viper.GetInt("libp2p.connmgr.low_watermark")
+ return viper.GetInt("libp2p.connmgr.low-watermark")
}
func GetLowWatermarkString() string {
@@ -157,7 +141,7 @@ func GetLowWatermarkString() string {
}
func GetHighWatermark() int {
- return viper.GetInt("libp2p.connmgr.high_watermark")
+ return viper.GetInt("libp2p.connmgr.high-watermark")
}
func GetHighWatermarkString() string {
@@ -165,7 +149,7 @@ func GetHighWatermarkString() string {
}
func GetConnMgrGracePeriod() time.Duration {
- return viper.GetDuration("libp2p.connmgr.grace_period")
+ return viper.GetDuration("libp2p.connmgr.grace-period")
}
func GetConnMgrGraceString() string {
@@ -181,7 +165,7 @@ func GetListenPortString() string {
}
func GetDiscoveryRetryInterval() time.Duration {
- return viper.GetDuration("libp2p.discovery_retry")
+ return viper.GetDuration("libp2p.discovery-retry")
}
func GetDiscoveryRetryIntervalString() string {
diff --git a/config/web.go b/config/web.go
index 7b9c4b3..26b5a85 100644
--- a/config/web.go
+++ b/config/web.go
@@ -13,8 +13,8 @@ func init() {
// Flags - user configurations
- pflag.String("http_socket", defaultHttpSocket, "Address for webserver to listen on")
- viper.BindPFlag("http.socket", pflag.Lookup("socket"))
+ pflag.String("http-socket", defaultHttpSocket, "Address for webserver to listen on")
+ viper.BindPFlag("http.socket", pflag.Lookup("http-socket"))
}
diff --git a/go.mod b/go.mod
index ac3e329..f916f90 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/bahner/go-ma-actor
go 1.21
require (
- github.com/bahner/go-ma v0.4.0
+ github.com/bahner/go-ma v0.4.1-0.20240206135113-165e47a9b140
github.com/fxamacker/cbor/v2 v2.5.0
github.com/gdamore/tcell/v2 v2.7.0
github.com/libp2p/go-libp2p v0.32.2
@@ -18,6 +18,7 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.18.2
+ gopkg.in/yaml.v2 v2.4.0
)
require (
diff --git a/go.sum b/go.sum
index b881336..e8c8478 100644
--- a/go.sum
+++ b/go.sum
@@ -53,8 +53,8 @@ github.com/alibabacloud-go/tea-utils v1.3.5/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQ
github.com/aliyun/credentials-go v1.1.0/go.mod h1:ZXrrxv386Mj6z8NpihLKpexQE550m7j3LlyCvYub9aE=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
-github.com/bahner/go-ma v0.4.0 h1:JKHdjwzFwHakwe521ozJhLelCUgTqcuKtXAMnM2n7C0=
-github.com/bahner/go-ma v0.4.0/go.mod h1:WM8wAs1tYjIuQLnM5kGkOo177N0dRQ4bXO4p/t63w+E=
+github.com/bahner/go-ma v0.4.1-0.20240206135113-165e47a9b140 h1:SUosauh4xglni948TM2j+FJd0ze8sJ1uXpiqT91luLk=
+github.com/bahner/go-ma v0.4.1-0.20240206135113-165e47a9b140/go.mod h1:WM8wAs1tYjIuQLnM5kGkOo177N0dRQ4bXO4p/t63w+E=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=