# SDK-002 — Config System (`~/.hyperscaled/config.toml`)

**Status:** Complete
**Date:** 2026-03-04
**Branch:** `sdk-002/config-system` (off `main`)
**Location:** `hyperscaled-sdk/`

---

## Summary

Local configuration for wallet addresses, active miner selection, account state, and API base URL. Reads/writes `~/.hyperscaled/config.toml`, validates wallet address format, and supports environment variable fallbacks. No secrets stored — wallet signing keys come from environment variables or the user's local wallet.

---

## What Was Built

### New Files

```
hyperscaled-sdk/
├── hyperscaled/
│   ├── sdk/
│   │   └── config.py           # Config Pydantic model, TOML load/save, env var fallbacks
│   └── cli/
│       └── config.py           # CLI commands: set, show, path
└── tests/
    └── test_config.py          # 23 tests
```

### Modified Files

| File | Change |
|------|--------|
| `hyperscaled/cli/main.py` | Added `config` subcommand group (now 11 groups total) |
| `tests/test_imports.py` | Added `hyperscaled.sdk.config` and `hyperscaled.cli.config` to import checks |

### Config Schema

```toml
[wallet]
hl_address = ""            # Hyperliquid trading wallet (0x + 40 hex chars)
payout_address = ""        # Wallet for receiving payouts (same format)

[account]
entity_miner = ""          # Active entity miner slug (set during registration)
funded_account_id = ""     # Funded account ID (set after purchase)
kyc_status = "not_started" # "not_started" | "pending" | "verified"

[api]
hyperscaled_base_url = "https://api.hyperscaled.com"
```

### SDK Interface

```python
from hyperscaled.sdk.config import Config

config = Config.load()                # Reads ~/.hyperscaled/config.toml (auto-creates if missing)
config.wallet.hl_address = "0x..."
config.save()                         # Writes back

# Dotted-key setter with validation
config.set_value("wallet.hl_address", "0xAbC...")

# Env var fallbacks: HYPERSCALED_HL_ADDRESS, HYPERSCALED_PAYOUT_ADDRESS, HYPERSCALED_BASE_URL
```

### CLI Commands

```bash
hyperscaled config set wallet.hl_address 0xAbC...    # Set with validation
hyperscaled config set wallet.payout_address 0xDeF...
hyperscaled config show                               # Rich table of all config values
hyperscaled config path                                # Prints ~/.hyperscaled/config.toml
```

### Precedence (highest → lowest)

1. Values set directly on the model
2. Config file (`~/.hyperscaled/config.toml`)
3. Environment variables
4. Defaults

### Validation

Wallet addresses validated with the same regex the Chrome extension uses (`/^0x[a-fA-F0-9]{40}$/` — see `popup.js` line 242). Invalid addresses are rejected at `config set` time and on `Config.load()`.

---

## Acceptance Criteria — All Met

| Criterion | Status |
|-----------|--------|
| First run auto-creates `~/.hyperscaled/config.toml` with defaults | Pass |
| `config set` validates wallet address format before writing | Pass |
| `config show` displays all values in a formatted table | Pass |
| Env var overrides work: `HYPERSCALED_HL_ADDRESS=0x... hyperscaled config show` | Pass |
| Config is a Pydantic model so it validates on load | Pass |

### Verification Results

```
ruff check .          → All checks passed!
mypy hyperscaled      → Success: no issues found in 26 source files
pytest -v             → 27 passed in 0.13s
```

### Test Coverage

| Test Group | Count | What's Covered |
|------------|-------|----------------|
| Config model | 8 | Defaults, valid/invalid/empty addresses, invalid kyc_status, JSON roundtrip |
| Load/save | 7 | Auto-create on first load, TOML roundtrip, dotted-key set, validation errors for bad section/key/format |
| Env var fallbacks | 4 | HL address, payout address, base URL from env; file value takes precedence over env |
| CLI commands | 4 | `config show`, `config set` (valid + invalid), `config path` |

---

## What's Next

This unblocks **SDK-003** (HyperscaledClient core class), which depends on SDK-002 for config loading. SDK-004 (Pydantic models & exception hierarchy) can proceed in parallel — it depends only on SDK-001.

```
SDK-001 ──┬── SDK-002 ✓ ── SDK-003 ──┐
          │                          ├── SDK-005
          └── SDK-004 ───────────────┘
```
