Skip to content

Add redacted environment config loader for server and CLI #58

Description

@Justinabox

Motivation

Callstack is becoming a long-running Pi PBX/SMS service, but deployment configuration is still scattered: server.py has hard-coded host/port defaults, ModemConfig is constructed manually, and the CLI only supports flags plus --sim-pin-env. A small redacted environment/config loader would make installs repeatable without encouraging users to paste SIM PINs, API keys, or modem details into code or logs.

This is intentionally narrower than dashboard/realtime work: provide one safe configuration path that server.py and CLI commands can share.

User journey

A user creates a local environment file or service manager environment:

CALLSTACK_AT_PORT=/dev/ttyUSB2
CALLSTACK_AUDIO_PORT=/dev/ttyUSB4
CALLSTACK_BAUDRATE=115200
CALLSTACK_SMS_DB_PATH=/var/lib/callstack/sms.sqlite3
CALLSTACK_HTTP_HOST=127.0.0.1
CALLSTACK_HTTP_PORT=8080
CALLSTACK_API_KEYS_FILE=/etc/callstack/api-keys
CALLSTACK_SIM_PIN_ENV=CALLSTACK_SIM_PIN

Then they can run the same configuration through the server and CLI:

python server.py
callstack status
callstack send --to '<redacted test recipient>' --body 'test'

Invalid values fail before opening serial ports, and any diagnostic output redacts secrets.

API / UX sketch

  • Add a documented helper such as ModemConfig.from_env(prefix="CALLSTACK_") or load_modem_config_from_env(os.environ).
  • Add a small HTTP/server config helper for host, port, API-key source, and rate-limit settings if that does not fit ModemConfig.
  • CLI flags continue to override env-derived defaults.
  • API keys should be loaded from a file path or secret env var without ever printing the key values. Prefer CALLSTACK_API_KEYS_FILE; if CALLSTACK_API_KEYS is supported, docs must warn that process environments can be visible to local admins/tools.
  • CALLSTACK_SIM_PIN_ENV names the variable holding the PIN; do not print the value.

Technical approach

  • Keep ModemConfig focused on modem settings; if HTTP settings are needed, add a tiny dataclass near server.py or in a new module such as callstack/server_config.py.
  • Parse and validate numeric values (baudrate, command_timeout, reconnect_interval, HTTP port, rate limit) before constructing runtime services.
  • Make the loader pure/testable by passing a mapping rather than reading globals directly in tests.
  • Update server.py to use the loader instead of hard-coded ModemConfig() and constants where practical.
  • Update callstack/cli.py so env-derived defaults are used, with explicit flags still taking precedence.
  • Ensure repr()/errors/logging never include SIM PIN values, API keys, SMS bodies, phone numbers, or full webhook URLs.

Affected modules

  • callstack/config.py
  • callstack/cli.py
  • server.py
  • Optional new module: callstack/server_config.py
  • Tests: tests/test_config.py, tests/test_cli.py, tests/test_api_auth.py or a new tests/test_server_config.py
  • Docs: README configuration / HTTP server sections

Hardware / modem caveats

  • Defaults should remain compatible with the current SIMCOM-oriented /dev/ttyUSB2 and /dev/ttyUSB4 examples.
  • Env loading must not mask the need for safe discovery/capability work; it should only configure explicit ports or documented defaults.
  • SIM PIN/PUK handling is sensitive. This issue should support reading an existing SIM PIN secret by env var name, but it must not expose public PUK recovery or credential-bearing commands.

Acceptance criteria

  • A pure config loader maps documented CALLSTACK_* values to ModemConfig and server settings.
  • CLI commands use env-derived defaults, while explicit CLI flags still win.
  • server.py uses env/server config for host, port, API keys, and modem settings instead of relying solely on hard-coded constants.
  • Invalid numeric values fail with clear, non-secret errors before opening serial ports or starting the HTTP server.
  • API keys and SIM PIN values are never printed, logged, returned in JSON, or included in exception messages.
  • README documents a minimal systemd/container-friendly env example with redacted placeholders.
  • Existing tests for auth, CLI, and config continue to pass without real hardware.

Exact gates

git diff --check
PYTHONPATH=. uv run --no-project --with pytest --with pytest-asyncio --with pytest-aiohttp --with pyserial-asyncio --with aiosqlite pytest tests/test_config.py tests/test_cli.py tests/test_api_auth.py -q
PYTHONPATH=. uv run --no-project --with pytest --with pytest-asyncio --with pytest-aiohttp --with pyserial-asyncio --with aiosqlite pytest tests/ -q

Non-goals

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions