Skip to content

Kontinuum Node — Configuration

TOML config schema для kontinuum-node-server и kontinuum-node-admin, env-vars override, secrets management. P0 prerequisite — определяет format перед началом кодинга config.rs.

Audience: node developers · ops · operator onboarding.

Связанные документы:

  • db-schemas.md — config указывает paths для DB
  • architecture.md — tier model, capabilities (формируют section'ы конфига)
  • operations.md — admin config интегрируется с billing, Tier 0 RPC

Обзор

Два разных config-файла per deployment:

ProcessConfig файлDefault location
kontinuum-node-servernode.toml/etc/kontinuum-node/node.toml (prod) или ./config/node.toml (dev)
kontinuum-node-adminadmin.toml/etc/kontinuum-node-admin/admin.toml

Загрузка config'a:

  1. Path указан через CLI flag (--config /path/to/node.toml) — primary.
  2. Иначе — default path для environment.
  3. Env vars override любое поле из TOML (см. §Env vars).
  4. Validation — fail-fast при startup; ошибки идут в stderr + лог.

TOML chosen because:

  • Human-readable, легче чем YAML для ops без YAML-traps (indentation, type ambiguity).
  • Native Rust support через config crate (уже в Cargo.toml как config = "0.14").
  • Comments allowed — important для operator handbook.

node.toml schema

Полный пример

toml
# Kontinuum Node — node.toml
# Reference: docs/node/configuration.md

[identity]
# Path to Ed25519 signing key (encrypted with master_key).
# Generated at first run, chmod 600.
key_path = "/var/lib/kontinuum-node/keys/node.key"

# Path to Tier 0-signed cert (None для Tier 2 pure-volunteer).
cert_path = "/var/lib/kontinuum-node/keys/node.cert"

# Path для BIP39 recovery phrase (write-only при onboarding, потом file removed).
# Stored encrypted с pin.
# recovery_phrase_path = "/var/lib/kontinuum-node/keys/recovery.enc"

[network]
# libp2p listen multiaddrs.
listen_addrs = [
    "/ip4/0.0.0.0/tcp/4001",
    "/ip4/0.0.0.0/udp/4001/quic-v1",
    "/ip6/::/tcp/4001",
]

# Optional advertised external addrs (если за NAT, прописать вручную).
external_addrs = []

# Tier 0 bootstrap peers — hardcoded в коде (см. bootstrap.md).
# Дополнительные bootstrap peers для test/dev environments — указать здесь.
extra_bootstrap_peers = []

# Connection limits.
max_connections = 1000
connection_idle_timeout_secs = 300

# Noise + Yamux config tuning — defaults usually fine.
# yamux_window_size = 16777216  # 16 MB
# noise_handshake_timeout_secs = 30

[storage]
# Base data directory. DB files, rustfs blob storage, transient cache.
data_dir = "/var/lib/kontinuum-node/data"

# DB file paths (relative to data_dir или absolute).
db_path = "node.db"

# Disk allocation (см. §7.2 — relay-node как coordination + free S3).
# total = system_reserved + dht_storage + cache + free_s3 (computed)
max_total_gb = 20

# Quotas для каждого components (хосткa нагрузки).
dht_storage_gb = 3
cache_storage_gb = 3
system_reserved_gb = 2
# free_s3_gb = max_total_gb - sum(above) = 12

[storage.rustfs]
# Embedded rustfs configuration.
# Backend: filesystem | memory (for tests).
backend = "filesystem"
backend_path = "rustfs/"   # relative to data_dir
listen_addr = "127.0.0.1:9000"
# Authentication is via Ed25519 shim, not SigV4. Internal port only.

[capabilities]
# Capabilities, declared by node в `node:{node_id}` записи.
# `dht_routing` всегда mandatory — не нужно перечислять явно.
# Допустимые значения: relay, storage, mailbox, s3_gateway, anti_entropy, rendezvous
enabled = ["relay", "storage", "mailbox", "s3_gateway", "anti_entropy"]
# rendezvous default OFF для Tier 2 byo (см. §6.6).

[tier]
# Tier and tenancy mode.
tier = 1                    # 0 | 1 | 2
tenancy = "single"          # "multi" | "single"
operator = "org"            # "org" | "byo:vps" | "byo:home"

# Для single-tenant — identity_id владельца (hex-encoded blake3 of pubkey).
# Для multi-tenant — оставьте пустым.
served_identity = "a3f9..."

[geo]
# Geographic zone — где physically running this node.
host_geo_zone = "eu-west"

# Default geo для own buckets (если не override'ится per-bucket).
bucket_default_geo_zone = "eu-west"

[observability]
# Prometheus metrics endpoint.
prometheus_listen = "127.0.0.1:9100"

# Logging.
log_level = "info"          # trace | debug | info | warn | error
log_format = "json"         # "json" | "text"

# OpenTelemetry tracing (optional).
# tracing_endpoint = "http://otel-collector:4317"

[tenants]
# Family-mode whitelist (для byo:* × multi). Empty для single-tenant.
# Каждый tenant с per-tenant квотами.
# Подписи квот сохраняются в DB (tenant_quotas), это просто onboarding source.
whitelist = []

# [[tenants.whitelist]]
# identity_id = "b7c2..."
# max_storage_bytes = 5000000000     # 5 GB
# max_mailbox_bytes = 100000000      # 100 MB
# max_mailbox_messages = 10000

[reencryption]
# Background re-encryption job parameters (§5.2.3 forward secrecy).
batch_size = 50            # blob/batch processed in one cycle
batch_interval_secs = 60
parallelism = 4

[anti_entropy]
# Merkle-tree gossip parameters (§11d).
gossip_interval_secs = 300        # 5 min
partition_count = 256
max_descend_depth = 16

[challenge]
# PoS challenge cadence (для Tier 2 verification).
issue_interval_secs = 3600        # 1h
sample_size_per_cycle = 10
score_threshold_eviction = -100

Required vs optional fields

Required (validation fails при отсутствии):

  • identity.key_path
  • network.listen_addrs (минимум 1)
  • storage.data_dir, storage.db_path
  • tier.tier, tier.tenancy, tier.operator
  • geo.host_geo_zone

Optional с defaults:

  • network.max_connections → 1000
  • network.connection_idle_timeout_secs → 300
  • storage.max_total_gb → 20
  • storage.dht_storage_gb → 3
  • storage.cache_storage_gb → 3
  • capabilities.enabled["relay", "storage", "mailbox"]
  • observability.log_level → "info"
  • observability.log_format → "json"
  • reencryption.batch_size → 50
  • anti_entropy.gossip_interval_secs → 300
  • challenge.issue_interval_secs → 3600

Cross-field validation:

  • tenancy = "single"served_identity обязателен.
  • tenancy = "multi"served_identity должен быть пустым.
  • operator = "org"tier ∈ {0, 1}.
  • operator = "byo:vps" | "byo:home"tier = 2.
  • tier = 0 → cert_path может быть пустым (Tier 0 self-signed).
  • tier ∈ {1, 2} → cert_path обязателен.
  • Sum(dht_storage_gb + cache_storage_gb + system_reserved_gb) < max_total_gb.

admin.toml schema

toml
# Kontinuum Node Admin — admin.toml
# Reference: docs/node/configuration.md

[admin]
# Admin process listens на REST API для org operators.
rest_listen = "127.0.0.1:8080"
db_path = "/var/lib/kontinuum-node-admin/data/admin.db"

[operator_bot]
# Platform-agnostic operator chat bot для quick ops. Implemented via
# operator_bot::BotBackend trait в admin crate.
enabled = true
# Backend identifier — "kontinuum" | "telegram" | "matrix" | "signal"
# | "discord" | "noop". At v0.1 only "noop" doesn't need credentials;
# concrete backends pull their own SDK and read credentials from
# `credentials_file`.
backend = "noop"
credentials_file = "/etc/kontinuum-node-admin/secrets/operator_bot_credentials"
# Identifier whitelist (backend-specific format — chat_id, JID, phone, etc.).
allowed_operators = []

[billing_integration]
# Kontinuum Billing webhook endpoint (inbound).
webhook_listen = "0.0.0.0:9090"
webhook_secret_file = "/etc/kontinuum-node-admin/secrets/billing_webhook_secret"

# Kontinuum Billing API (outbound, для cert issuance triggers).
billing_api_url = "https://billing.kontinuum.local/api/v1"
billing_api_key_file = "/etc/kontinuum-node-admin/secrets/billing_api_key"

[tier0]
# Tier 0 anchor endpoint (admin process сам Tier 0 nor talks to Tier 0).
# Для cert issuance flow — admin отправляет signing request на Tier 0 multi-sig group.
tier0_endpoint = "https://tier0.kontinuum.local/api/v1"
tier0_client_key_file = "/etc/kontinuum-node-admin/secrets/tier0_client.key"

[observability]
prometheus_listen = "127.0.0.1:9101"
log_level = "info"
log_format = "json"

[directus]
# Directus headless CMS, integrated для admin UI.
# Admin process exposes DB read-only через PostgreSQL bridge.
# Directus сам управляется отдельно (см. deploy/directus/).
directus_db_user = "directus_read"
directus_db_password_file = "/etc/kontinuum-node-admin/secrets/directus_db_pw"

Env vars override

Все TOML fields могут быть override'ны через env vars. Convention: KONTINUUM_NODE_<SECTION>_<FIELD>.

TOML pathEnv var
identity.key_pathKONTINUUM_NODE_IDENTITY_KEY_PATH
network.listen_addrs[0]KONTINUUM_NODE_NETWORK_LISTEN_ADDRS_0
storage.data_dirKONTINUUM_NODE_STORAGE_DATA_DIR
tier.tierKONTINUUM_NODE_TIER_TIER
observability.log_levelKONTINUUM_NODE_OBSERVABILITY_LOG_LEVEL
tenants.whitelistKONTINUUM_NODE_TENANTS_WHITELIST_JSON (JSON-encoded array)

Для admin process — prefix KONTINUUM_NODE_ADMIN_.

Precedence: env vars > TOML > defaults.

Useful overrides:

  • Tests: KONTINUUM_NODE_STORAGE_DATA_DIR=/tmp/test-node-$$
  • Container: bake env vars в Dockerfile или Helm values.
  • Secrets: НЕ через env vars (utilites like ps, /proc/<pid>/environ may leak); только через file-paths.

Secrets management

Принципы

  1. Никогда не хранить secrets в config файле напрямую (TOML текстовый).
  2. Всегда через file paths — config указывает где, файл сам chmod 600.
  3. Master_key — derived at startup из BIP39, держится в-memory только.
  4. Backup-копии secrets — encrypted (LUKS / GPG), хранятся offline.

Files convention

/var/lib/kontinuum-node/keys/
├── node.key                  # Ed25519 signing key (encrypted with master_key)  — chmod 600
├── node.cert                 # Tier 0-signed cert binary                          — chmod 644
└── recovery.enc              # encrypted BIP39 phrase (optional, removed после bootstrap) — chmod 600
/etc/kontinuum-node-admin/secrets/
├── operator_bot_credentials  # operator chat bot credentials (backend-specific format) — chmod 600
├── billing_webhook_secret    # HMAC secret для webhook verify      — chmod 600
├── billing_api_key           # API key для outbound calls          — chmod 600
├── tier0_client.key          # client cert для Tier 0 RPC          — chmod 600
└── directus_db_pw            # postgres password для Directus       — chmod 600

Owner / group:

  • Server process runs как kontinuum-node user, owner of /var/lib/kontinuum-node/.
  • Admin process runs как kontinuum-node-admin user, owner of /etc/kontinuum-node-admin/secrets/.
  • Никаких world-readable secrets.

Master key derivation

master_key = Argon2id(
    password = BIP39_phrase,
    salt = "kontinuum-node-master-v1",
    memory = 65536 KB,
    iterations = 3,
    parallelism = 4,
    output_len = 32 bytes,
)

Same algorithm как kontinuum-app::vault::recovery. Master key stays в-memory, NEVER persisted on disk plaintext. После derivation BIP39 phrase wiped из process memory.

Если перезапуск ноды — admin приходит, вводит BIP39 phrase через interactive prompt (или PIN unlock через pin_encryption::ValidatedPin, если ранее настроен PIN — analogous to kontinuum-app::vault::pin_encryption).


Per-environment configs

Convention для dev / staging / prod:

config/
├── node.toml.dev         # local development
├── node.toml.staging     # staging environment
└── node.toml.prod        # production

Startup flag: --config config/node.toml.prod.

Что отличается между environments:

Fielddevstagingprod
network.listen_addrs127.0.0.1:40010.0.0.0:40010.0.0.0:4001 + external_addrs
network.extra_bootstrap_peerstests's mock Tier 0staging Tier 0 clusterempty (uses hardcoded prod Tier 0)
storage.data_dir./data//var/lib/kontinuum-node/data//var/lib/kontinuum-node/data/
storage.max_total_gb1520
observability.log_leveldebuginfoinfo
observability.log_formattextjsonjson
tier.tier1 (mocked cert)11 (real cert from prod Tier 0)

Containerization (production)

Standard Docker image:

dockerfile
FROM kontinuum-node:v1.0
# Bake default config — env vars override at runtime.
COPY node.toml.prod /etc/kontinuum-node/node.toml
ENV KONTINUUM_NODE_STORAGE_DATA_DIR=/data
VOLUME ["/data", "/var/lib/kontinuum-node/keys"]
EXPOSE 4001 9100
ENTRYPOINT ["/usr/local/bin/kontinuum-node-server"]
CMD ["--config", "/etc/kontinuum-node/node.toml"]

Kubernetes: ConfigMap для node.toml, Secret для keys, PVC для data_dir.


Validation

At startup

rust
async fn load_and_validate(path: &Path) -> Result<NodeConfig> {
    let raw = std::fs::read_to_string(path)?;
    let mut cfg: NodeConfig = toml::from_str(&raw)?;

    // Override from env vars.
    cfg.apply_env_overrides()?;

    // Required-field check.
    cfg.validate_required()?;

    // Cross-field constraints.
    cfg.validate_consistency()?;

    // File-path existence (key, cert).
    cfg.validate_paths()?;

    // Permission check (chmod 600 для secrets).
    cfg.validate_permissions()?;

    Ok(cfg)
}

Error messages

User-friendly с clear hint к fix:

ERROR: tier.served_identity is required when tenancy="single"
  in /etc/kontinuum-node/node.toml line 24

Hint: add `served_identity = "<hex blake3 of owner pubkey>"` или change tenancy to "multi".
ERROR: identity.key_path "/var/lib/kontinuum-node/keys/node.key" не существует
  и has wrong permissions

Hint: первый запуск? Generate identity через:
  kontinuum-node-server bootstrap --output /var/lib/kontinuum-node/keys/node.key

Hot reload

Reloadable fields

При SIGHUP config перечитывается; следующие поля применяются без рестарта:

  • observability.log_level
  • observability.log_format
  • network.max_connections
  • network.connection_idle_timeout_secs
  • reencryption.batch_size / batch_interval_secs / parallelism
  • anti_entropy.gossip_interval_secs
  • challenge.issue_interval_secs
  • tenants.whitelist (если valid signatures)

Non-reloadable (требуют restart)

  • identity.* — signing key / cert changes
  • network.listen_addrs — bind addr changes
  • storage.* — DB path / quotas (избежать data loss)
  • tier.* — tier transitions through cert lifecycle, не config-level
  • geo.* — node geo identity, fixed
  • capabilities.enabled — restart safer для clean DHT membership change

При попытке reload non-reloadable field — log warning, ignore, продолжить со старым value.


CLI flags

kontinuum-node-server

kontinuum-node-server [OPTIONS]

OPTIONS:
    --config <PATH>           Path to node.toml (default: /etc/kontinuum-node/node.toml)
    --log-level <LEVEL>       Override log level (trace/debug/info/warn/error)
    --data-dir <PATH>         Override storage.data_dir
    --dry-run                 Validate config and exit без start'a network
    --version                 Print version and exit

SUBCOMMANDS:
    bootstrap                 Generate identity, vault, BIP39 phrase (first run)
    cert request              Request cert from Tier 0 (manual flow for testing)
    dump-state                Print DB state to stdout (debugging)

kontinuum-node-admin

kontinuum-node-admin [OPTIONS]

OPTIONS:
    --config <PATH>           Path to admin.toml
    --log-level <LEVEL>
    --version

SUBCOMMANDS:
    server                    Start REST API + operator chat bot + billing webhook listener
    cert issue <node_id>      Issue cert (calls Tier 0 multi-sig flow)
    cert revoke <node_id>     Add to CRL
    node drain <node_id>      Set drain mode
    audit dump                Export admin_audit_log

Implementation checklist

  • [ ] Добавить config = "0.14", toml = "0.8", dotenv = "0.15" (для test env vars) в Cargo.toml.
  • [ ] Implement NodeConfig struct в config.rs с serde::Deserialize.
  • [ ] Implement env-var override layer (config::Environment::with_prefix("KONTINUUM_NODE")).
  • [ ] Implement validation functions (required, consistency, paths, permissions).
  • [ ] Implement CLI args parsing через clap.
  • [ ] Implement SIGHUP handler для hot reload.
  • [ ] Sample config files в config/node.toml.{dev,staging,prod} (включая в repo как reference).
  • [ ] Tests: load valid config, reject invalid, env override works, permissions check fires.
  • [ ] Documentation in --help text (rustdoc + clap docs).

Open implementation questions

  1. Hot-reload SIGHUP vs config-watcher (inotify) — SIGHUP проще, но inotify более responsive. Решить при имплементации.
  2. config::Environment::with_prefix vs custom env-merge — config crate handles большинство случаев, но JSON-encoded arrays через env требуют custom logic.
  3. Schema validation tooling — JSON Schema + validator или Rust-only? JSON Schema удобнее для external tools (Directus form generation, etc.).
  4. Secrets management — Vault / age / pass / plain files — для v1.0 plain files chmod 600 OK; для multi-machine production может потребоваться HashiCorp Vault интеграция.
  5. Encrypted backup formatrage (age in Rust) vs GPG vs custom? rage minimal, modern crypto.