Matrix logo

Config

The config system loads daemon settings from a .kvx file with environment variable precedence. It supports two wallet modes, capability policy profiles, and custom chain registr...

Source files: internal/config/config.go, internal/config/kvx.go

The config system loads daemon settings from a .kvx file with environment variable precedence. It supports two wallet modes, capability policy profiles, and custom chain registrations.


Design decisions

.kvx format, not TOML/YAML/JSON

The config uses a custom sectioned key/value format (.kvx) that is zero-dependency, deterministic, and matches the Matrix .mtx convention:

[section]
key = "string"            # double-quoted strings
num = 31337               # bare ints/bools
list = ["a", "b"]         # bracketed, comma-separated, quoted items
[section.sub]
ref = "${ENV_VAR}"        # env interpolation

This avoids pulling in TOML/YAML parsers and keeps the config file human-readable and comment-friendly.

Precedence: environment > kvx > defaults

Every config value is resolved via pick(envKey, kvxVal, fallback):

  1. Environment variable (if set and non-empty)
  2. .kvx file value (if present)
  3. Hardcoded default

This lets operators override any setting via environment without editing the file.

.env file loading

A .env file in the working directory is loaded when present (KEY=VALUE lines only, no export prefix needed). This is loaded before .kvx parsing, so .kvx ${ENV_VAR} references resolve values from .env.

Wallet mode auto-detection

If wallet.mode is not explicitly set, the config loader auto-detects from environment:

  • TACHYON_ALLOW_DEV_SIGNER=true or TACHYON_DEV_PRIVATE_KEY set → self_hosted
  • PAXEER_WALLET_TOKEN or MATRIX_EXECUTOR_KEY set → embedded

This backwards-compatibility shim supports the original dev/Paxeer signer environment variables.

Foundry root discovery

If project_root is . (default), the config loader walks up to 12 parent directories looking for foundry.toml. This means the daemon can be started from any subdirectory of a Foundry project.


Config structure

type Config struct {
    APIAddr      string
    ProjectRoot  string
    ArtifactsDir string
    RegistryPath string
    SolcPath     string
    ForgePath    string
    AuthToken    string
    Wallet       WalletConfig
    Policies     map[string]PolicyProfile
    Chains       []ChainConfig
}

type WalletConfig struct {
    Mode             string // "" | self_hosted | embedded
    Signer           string // raw | keystore | env
    PrivateKey       string
    KeystorePath     string
    KeystorePassword string
    Keyfile          string // ed25519 seed path
    Label            string // DID label
    API              string // embedded wallet base URL
}

type PolicyProfile struct {
    Name        string
    SpendCapWei string
    Allow       []string
    Chains      []string
}

type ChainConfig struct {
    ID       string
    Name     string
    RPCURL   string
    ChainID  uint64
    Explorer string
}

KVX parser

type kvxDoc struct {
    sections map[string]map[string]string
    order    []string
}

Typed accessors

func (d *kvxDoc) str(section, key string) string      // interpolated string
func (d *kvxDoc) list(section, key string) []string   // bracketed list
func (d *kvxDoc) uint64Or(section, key string, fallback uint64) uint64
func (d *kvxDoc) sectionsWithPrefix(prefix string) []string

Interpolation

${ENV_VAR} is replaced with os.Getenv("ENV_VAR") at parse time. This happens in interpolate(), called by str() and list().


Example config

[server]
addr       = "127.0.0.1:8645"
auth_token = "${TACHYON_AUTH_TOKEN}"

[wallet]
mode = "self_hosted"

[wallet.self_hosted]
signer      = "env"
private_key = "${DEPLOYER_PK}"

[wallet.embedded]
keyfile = "/data/.matrix/executor.key"
label   = "executor"
api     = "https://connect.paxportwallet.com"

[policy.default]
spend_cap_wei = "100000000000000000"
allow         = []
chains        = ["paxeer-mainnet"]

[chains.anvil]
name     = "Local Anvil"
rpc_url  = "http://127.0.0.1:8545"
chain_id = 31337

Modifying the config

What to changeWhere
Add config fieldinternal/config/config.goConfig struct + Load
Add kvx accessorinternal/config/kvx.go — new typed method
Change precedenceinternal/config/config.gopick function
Add env shiminternal/config/config.goloadWallet