"""Which settings the central overlay may touch, and which take effect live.
Two lists, and the asymmetry between them is deliberate.
``DENIED_SETTINGS`` are fields a hive document can never change. They are not
a matter of taste: the bootstrap ones (``HIVE_URL``, the key path, the cache
path) are needed *before* the overlay can be fetched, so an overlay that set
them could never describe the channel it arrives through. The rest are the
ways a config can make every machine unreachable or silently point it at the
wrong database — a valid value that is merely wrong is the one failure class
validation cannot catch.
``HOT_SETTINGS`` are fields read from the singleton at call time, so writing a
new value into the live object takes effect immediately. Everything else
changes something built at startup (CORS middleware, backends, the MCP pool,
the push service) and is reported as ``restart_required`` instead of being
pretended into effect.
"""
from __future__ import annotations
# Never overlaid. Unknown to the hive's validator? No — known, and refused.
DENIED_SETTINGS: frozenset[str] = frozenset(
{
# Bootstrap: needed before the overlay exists.
"hive_url",
"swarm_key_file",
"instance_file",
"hive_config_enabled",
"hive_config_cache_file",
"hive_config_timeout_sec",
# Reachability and data: a wrong-but-valid value is unrecoverable
# without a local edit (or, for the DB, looks like lost history).
"database_url",
"navi_host",
"navi_port",
"navi_ui_mcp_host",
"navi_ui_mcp_port",
"navi_auth_enabled",
}
)
# Applied to the live settings object on refresh; everything else is staged
# until the next start. Only fields verified to be read at call time belong
# here — a wrong entry would claim an effect that never happens.
HOT_SETTINGS: frozenset[str] = frozenset(
{
# gnexus-auth: read per request / per login. The cached default OAuth
# client is reset by an apply hook (navi/auth/client.py).
"gnauth_base_url",
"gnauth_client_id",
"gnauth_client_secret",
"gnauth_redirect_uri",
"gnauth_admin_role_slug",
"gnauth_user_role_slug",
"gnauth_profile_path",
"gnauth_webhook_secret",
# Synapse outbound: a client is built per emit.
"synapse_source_url",
"synapse_source_api_key",
"synapse_source_name",
# Read at call time.
"public_url",
"navi_default_profile_id",
"navi_push_cooldown_sec",
"peer_ask_timeout_sec",
"session_files_max_size_mb",
"share_file_max_size_mb",
"terminal_allowed_commands",
"terminal_user_allowed_commands",
"fs_allowed_paths",
"backgroundable_tools",
"parallel_tool_calls",
"message_queue_max",
"tasks_max_per_session",
"tasks_max_global",
"tasks_max_spawn",
"tasks_ttl_sec",
"tasks_event_buffer_size",
"tasks_rate_limit",
"task_notes_max_pending",
"task_notes_per_turn",
# LLM timeouts / retries.
"llm_complete_timeout",
"planning_llm_timeout_sec",
"llm_stream_first_chunk_timeout",
"llm_stream_chunk_timeout",
"llm_complete_retries",
"llm_retry_backoff_sec",
# Sub-agent guards.
"subagent_thinking_stall_seconds",
"subagent_thinking_stall_chars",
"subagent_timeout_seconds",
# Context compression.
"context_compression_enabled",
"context_compression_threshold",
"context_compression_target",
"context_keep_recent",
"context_summary_temperature",
"context_summary_max_tokens",
"output_reserve_tokens",
"context_message_token_budget",
}
)
KNOWN_SECTIONS: frozenset[str] = frozenset(
{"settings", "profiles", "llm", "mcp_servers"}
)