Files
zeekay 724e24dd80 cli: decomplect — pkg/network + Profile/Spec/Env types + lux info verb
Sweep "brand" out of the API entirely. Package and types now match the
domain vocabulary the user sees in chain.yaml + CLI output:

  pkg/brand → pkg/network         (one package per concept)
  Brand     → Spec                (parsed chain.yaml)
  NetCfg    → Env                 (per-env block)
  RuntimeProfile → Profile
  RuntimeProfile.Brand → Profile.Name
  ChainLock → Lock
  $LUX_BRAND_PATH → $LUX_NETWORK_PATH

Surface BOTH identifiers distinctly — they are deliberately different:
  NetworkID         uint32  — validator wire, --network-id, info.getNetworkID
  PrimaryEvmChainID uint64  — C-chain EIP-155, eth_chainId, MetaMask

Lux brand keeps them apart (NID 1 / EVM 96369). Sovereign-L1 brand
forks (Zoo, Hanzo, Pars, Osage, Liquidity) collapse them by convention
(Zoo NID == EVM == 200200). The validator no longer enforces equality;
both are surfaced in lux ps and lux info.

New verb:
  lux info <name>/<env>   full Profile dump (both IDs, ports, paths,
                          snapshot name, service label, RPC endpoints,
                          live probe state)

lux ps output now shows NETWORK_ID and EVM_CHAIN_ID as separate columns.

chain.lock manifest now records both IDs so booting against a foreign
EVM-chain-ID dataDir is also rejected (previously only checked
networkID).
2026-06-16 21:03:14 -07:00

95 lines
2.3 KiB
Go

package network
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
)
// EnvVar is the colon-separated path of universe directories scanned
// for chain.yaml — same shape as $PATH.
const EnvVar = "LUX_NETWORK_PATH"
// DefaultPath is the workspace convention: ~/work/<name>/universe.
func DefaultPath() string {
home, _ := os.UserHomeDir()
return strings.Join([]string{
filepath.Join(home, "work", "lux", "universe"),
filepath.Join(home, "work", "zoo", "universe"),
filepath.Join(home, "work", "hanzo", "universe"),
filepath.Join(home, "work", "pars", "universe"),
filepath.Join(home, "work", "osage", "universe"),
filepath.Join(home, "work", "adnexus", "universe"),
}, ":")
}
// Registry holds networks discovered from $LUX_NETWORK_PATH.
type Registry struct {
specs map[string]*Spec // slug → Spec
}
// Discover scans $LUX_NETWORK_PATH for chain.yaml files. Networks
// later in the path do NOT override earlier ones — first-wins, like
// $PATH. Unreadable / malformed files are skipped with a warning.
func Discover() (*Registry, error) {
pathStr := os.Getenv(EnvVar)
if pathStr == "" {
pathStr = DefaultPath()
}
r := &Registry{specs: map[string]*Spec{}}
for _, dir := range strings.Split(pathStr, ":") {
if dir == "" {
continue
}
yaml := filepath.Join(dir, "chain.yaml")
st, err := os.Stat(yaml)
if err != nil || st.IsDir() {
continue
}
s, err := ParseFile(yaml)
if err != nil {
fmt.Fprintf(os.Stderr, "warn: %s: %v\n", yaml, err)
continue
}
name := s.Network.Slug
if _, dup := r.specs[name]; !dup {
r.specs[name] = s
}
}
return r, nil
}
// Lookup returns the network spec by name slug.
func (r *Registry) Lookup(name string) (*Spec, error) {
s, ok := r.specs[name]
if !ok {
known := r.Names()
return nil, fmt.Errorf("network %q not discovered under $%s (known: %v)", name, EnvVar, known)
}
return s, nil
}
// Names returns all discovered network slugs, sorted.
func (r *Registry) Names() []string {
out := make([]string, 0, len(r.specs))
for n := range r.specs {
out = append(out, n)
}
sort.Strings(out)
return out
}
// Refs returns all (name/env) pairs in the registry, sorted.
func (r *Registry) Refs() []string {
var out []string
for _, s := range r.specs {
for env := range s.Networks {
out = append(out, s.Network.Slug+"/"+env)
}
}
sort.Strings(out)
return out
}