mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-09-18 15:12:20 +02:00
Review follow-up: - Drop the Next.js route. CARTO_API_KEY is now a regular backend registry key (env, .env, or the API Keys panel) served by public GET /api/basemap-config. Every frontend mode already proxies /api/* to the backend (Next.js proxy in web mode, companion server in packaged desktop), so this covers web and desktop with one mechanism and leaves the static export untouched. Also removes the invalid non-handler export from the route module by removing the module. - useBasemapConfig: fail open to the unkeyed style after 3 s, abort the request at 15 s, apply a late key when it arrives, cache successes per page and retry failures on the next mount. - Declare OSM/CARTO attribution on the raster source (same markup as the viewer's existing AttributionControl so MapLibre de-duplicates it). - Tests: backend endpoint (unset / set+trimmed / persisted operator key / registry), hook behaviour (success, non-OK, network error, soft timeout then late key, hard abort, shared request and retry), attribution and gating source checks. - CARTO_API_KEY moves to the backend service in docker-compose.yml; docs updated accordingly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
457 lines
23 KiB
Python
457 lines
23 KiB
Python
"""Typed configuration via pydantic-settings."""
|
|
|
|
from functools import lru_cache
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
# Admin/security
|
|
ADMIN_KEY: str = ""
|
|
ALLOW_INSECURE_ADMIN: bool = False
|
|
PUBLIC_API_KEY: str = ""
|
|
|
|
# OpenClaw agent connectivity
|
|
OPENCLAW_HMAC_SECRET: str = "" # HMAC shared secret for direct mode (auto-generated if empty)
|
|
OPENCLAW_ACCESS_TIER: str = "restricted" # "full" or "restricted"
|
|
|
|
# Data sources
|
|
AIS_API_KEY: str = ""
|
|
AISHUB_USERNAME: str = "" # Optional AISHub REST backup when AISStream is silent
|
|
OPENSKY_CLIENT_ID: str = ""
|
|
OPENSKY_CLIENT_SECRET: str = ""
|
|
LTA_ACCOUNT_KEY: str = ""
|
|
CARTO_API_KEY: str = "" # Basemap tiles; served to the browser via /api/basemap-config
|
|
|
|
# Runtime
|
|
CORS_ORIGINS: str = ""
|
|
FETCH_SLOW_THRESHOLD_S: float = 5.0
|
|
MESH_STRICT_SIGNATURES: bool = True
|
|
MESH_DEBUG_MODE: bool = False
|
|
MESH_MQTT_EXTRA_ROOTS: str = ""
|
|
MESH_MQTT_EXTRA_TOPICS: str = ""
|
|
MESH_MQTT_INCLUDE_DEFAULT_ROOTS: bool = True
|
|
MESH_RNS_ENABLED: bool = False
|
|
MESH_ARTI_ENABLED: bool = False
|
|
# When true, trust wormhole_status.json ready bit if the child process is
|
|
# alive — avoids transport-tier flapping when /api/health probes time out
|
|
# under Tor load (common during live DM E2E).
|
|
MESH_WORMHOLE_TRUST_FILE_READY: bool = False
|
|
MESH_ARTI_SOCKS_PORT: int = 9050
|
|
MESH_RELAY_PEERS: str = ""
|
|
MESH_PUBLIC_PEER_URL: str = ""
|
|
# Bootstrap seeds are discovery hints, not authoritative network roots.
|
|
# Nodes promote healthy discovered peers from the store/manifest over time.
|
|
MESH_BOOTSTRAP_SEED_PEERS: str = ""
|
|
# Optional comma-separated override for the public fleet's bundled onion seeds.
|
|
# This lets seed rotation/addition ship as env/config without weakening private sync.
|
|
MESH_FLEET_SEED_PEERS: str = ""
|
|
# Legacy name kept for older compose/.env files.
|
|
MESH_DEFAULT_SYNC_PEERS: str = ""
|
|
# Infonet/Wormhole must fail closed to private transports by default.
|
|
# Set true only for local relay development or explicitly public testnets.
|
|
MESH_INFONET_ALLOW_CLEARNET_SYNC: bool = False
|
|
MESH_BOOTSTRAP_DISABLED: bool = False
|
|
MESH_BOOTSTRAP_MANIFEST_PATH: str = "data/bootstrap_peers.json"
|
|
# Public sb-testnet-0 fleet signer (participants). Seed operator holds the private key.
|
|
MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY: str = (
|
|
"ul1d0kj/ODPIp0OhHzX8eLAVXzJ3CVvzW1vn2IC6q3I="
|
|
)
|
|
MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY: str = ""
|
|
# When true, empty MESH_PEER_PUSH_SECRET uses the public fleet HMAC for seed join/announce.
|
|
MESH_INFONET_FLEET_JOIN: bool = True
|
|
MESH_INFONET_FLEET_JOIN_DISABLED: bool = False
|
|
# Headless relay/seed compose: auto-enable Tor wormhole on startup so
|
|
# docker compose redeploys keep the fleet onion reachable.
|
|
MESH_INFONET_RELAY_AUTO_WORMHOLE: bool = False
|
|
MESH_INFONET_RELAY_AUTO_WORMHOLE_DISABLED: bool = False
|
|
MESH_BOOTSTRAP_SIGNER_ID: str = ""
|
|
MESH_PEER_REGISTRY_ENABLED: bool = False
|
|
MESH_PEER_REGISTRY_DISABLED: bool = False
|
|
MESH_PEER_REGISTRY_STALE_S: int = 604800
|
|
MESH_SWARM_MANIFEST_TTL_S: int = 14400
|
|
MESH_SWARM_MANIFEST_PULL_INTERVAL_S: int = 300
|
|
MESH_NODE_MODE: str = "participant"
|
|
MESH_SYNC_INTERVAL_S: int = 300
|
|
MESH_SYNC_FAILURE_BACKOFF_S: int = 60
|
|
MESH_SYNC_TIMEOUT_S: int = 5
|
|
MESH_SYNC_MAX_PEERS_PER_CYCLE: int = 3
|
|
MESH_RELAY_PUSH_TIMEOUT_S: int = 10
|
|
MESH_RELAY_MAX_FAILURES: int = 3
|
|
MESH_RELAY_FAILURE_COOLDOWN_S: int = 120
|
|
MESH_BOOTSTRAP_SEED_FAILURE_COOLDOWN_S: int = 15
|
|
MESH_PEER_PUSH_SECRET: str = ""
|
|
# Issue #256 (tg12): optional per-peer HMAC secret map. Comma-separated
|
|
# `url=secret` pairs. When a peer URL appears here, only that per-peer
|
|
# secret is accepted for it — the global MESH_PEER_PUSH_SECRET above is
|
|
# ignored for that specific URL. Single-peer installs and unmigrated
|
|
# multi-peer installs leave this empty and behavior is unchanged.
|
|
MESH_PEER_SECRETS: str = ""
|
|
MESH_RNS_APP_NAME: str = "shadowbroker"
|
|
MESH_RNS_ASPECT: str = "infonet"
|
|
MESH_RNS_IDENTITY_PATH: str = ""
|
|
MESH_RNS_PEERS: str = ""
|
|
MESH_RNS_DANDELION_HOPS: int = 2
|
|
MESH_RNS_DANDELION_DELAY_MS: int = 400
|
|
MESH_RNS_CHURN_INTERVAL_S: int = 300
|
|
MESH_RNS_MAX_PEERS: int = 32
|
|
MESH_RNS_MAX_PAYLOAD: int = 8192
|
|
MESH_RNS_PEER_BUCKET_PREFIX: int = 4
|
|
MESH_RNS_MAX_PEERS_PER_BUCKET: int = 4
|
|
MESH_RNS_PEER_FAIL_THRESHOLD: int = 3
|
|
MESH_RNS_PEER_COOLDOWN_S: int = 300
|
|
MESH_RNS_SHARD_ENABLED: bool = False
|
|
MESH_RNS_SHARD_DATA_SHARDS: int = 3
|
|
MESH_RNS_SHARD_PARITY_SHARDS: int = 1
|
|
MESH_RNS_SHARD_TTL_S: int = 30
|
|
MESH_RNS_FEC_CODEC: str = "xor" # xor | rs
|
|
MESH_RNS_BATCH_MS: int = 200
|
|
# Keep a low background cadence on private RNS links so quiet nodes are less
|
|
# trivially fingerprintable by silence alone. Set to 0 to disable explicitly.
|
|
MESH_RNS_COVER_INTERVAL_S: int = 30
|
|
MESH_RNS_COVER_SIZE: int = 512
|
|
MESH_DM_MAILBOX_TTL_S: int = 900
|
|
MESH_RNS_IBF_WINDOW: int = 256
|
|
MESH_RNS_IBF_TABLE_SIZE: int = 64
|
|
MESH_RNS_IBF_MINHASH_SIZE: int = 16
|
|
MESH_RNS_IBF_MINHASH_THRESHOLD: float = 0.25
|
|
MESH_RNS_IBF_WINDOW_JITTER: int = 32
|
|
MESH_RNS_IBF_INTERVAL_S: int = 120
|
|
MESH_RNS_IBF_SYNC_PEERS: int = 3
|
|
MESH_RNS_IBF_QUORUM_TIMEOUT_S: int = 6
|
|
MESH_RNS_IBF_MAX_REQUEST_IDS: int = 64
|
|
MESH_RNS_IBF_MAX_EVENTS: int = 64
|
|
MESH_RNS_SESSION_ROTATE_S: int = 1800
|
|
MESH_RNS_IBF_FAIL_THRESHOLD: int = 3
|
|
MESH_RNS_IBF_COOLDOWN_S: int = 120
|
|
MESH_VERIFY_INTERVAL_S: int = 600
|
|
# MESH_VERIFY_SIGNATURES is intentionally removed — the audit loop in main.py
|
|
# always calls validate_chain_incremental(verify_signatures=True). Any value
|
|
# set in the environment is ignored.
|
|
MESH_DM_SECURE_MODE: bool = True
|
|
MESH_DM_TOKEN_PEPPER: str = ""
|
|
MESH_ALLOW_LEGACY_DM1_UNTIL: str = ""
|
|
MESH_ALLOW_LEGACY_DM_GET_UNTIL: str = ""
|
|
MESH_ALLOW_LEGACY_DM_SIGNATURE_COMPAT_UNTIL: str = ""
|
|
MESH_DM_PERSIST_SPOOL: bool = False
|
|
MESH_DM_RELAY_FILE_PATH: str = ""
|
|
MESH_DM_RELAY_AUTO_RELOAD: bool = False
|
|
MESH_DM_REQUIRE_SENDER_SEAL_SHARED: bool = True
|
|
MESH_DM_NONCE_TTL_S: int = 300
|
|
MESH_DM_NONCE_CACHE_MAX: int = 4096
|
|
MESH_DM_NONCE_PER_AGENT_MAX: int = 256
|
|
MESH_DM_REQUEST_MAX_AGE_S: int = 300
|
|
MESH_DM_REQUEST_MAILBOX_LIMIT: int = 12
|
|
MESH_DM_SHARED_MAILBOX_LIMIT: int = 48
|
|
MESH_DM_SELF_MAILBOX_LIMIT: int = 12
|
|
# Anti-spam: cap on distinct UNACKED messages a single sender can have
|
|
# parked in a single recipient's mailbox at any one time. Once the
|
|
# recipient pulls (acks) a message, the sender's quota for that pair
|
|
# frees up. Default 2 — a sender who wants to deliver more must wait
|
|
# for the recipient to actually read the prior messages.
|
|
#
|
|
# This cap is enforced TWICE: once on the local deposit path (the
|
|
# sender's own node refuses to spool the 3rd message) AND once on
|
|
# the replication-acceptance path (honest peer relays refuse to
|
|
# accept inbound replicas that would put them over the cap). The
|
|
# double enforcement makes the rule a NETWORK rule — patching out
|
|
# the local check on a hostile sender's relay doesn't let extras
|
|
# propagate, because every honest peer enforces the same cap on
|
|
# inbound replication.
|
|
MESH_DM_PENDING_PER_SENDER_LIMIT: int = 2
|
|
MESH_BLOCK_LEGACY_AGENT_ID_LOOKUP: bool = True
|
|
MESH_ALLOW_COMPAT_DM_INVITE_IMPORT: bool = False
|
|
MESH_ALLOW_COMPAT_DM_INVITE_IMPORT_UNTIL: str = ""
|
|
MESH_ALLOW_LEGACY_NODE_ID_COMPAT_UNTIL: str = ""
|
|
# Rotate voter-blinding salts on a rolling cadence so new reputation
|
|
# events do not reuse one forever-stable blinded identity.
|
|
MESH_VOTER_BLIND_SALT_ROTATE_DAYS: int = 30
|
|
# Keep historical salts long enough to cover live vote records, so
|
|
# duplicate-vote detection and wallet-cost accounting survive rotation.
|
|
MESH_VOTER_BLIND_SALT_GRACE_DAYS: int = 30
|
|
MESH_DM_MAX_MSG_BYTES: int = 8192
|
|
MESH_DM_ALLOW_SENDER_SEAL: bool = False
|
|
# TTL for DH key and prekey bundle registrations — stale entries are pruned.
|
|
MESH_DM_KEY_TTL_DAYS: int = 30
|
|
# TTL for invite-scoped prekey lookup aliases; shorter windows reduce
|
|
# long-lived relay linkage between opaque lookup handles and agent IDs.
|
|
MESH_DM_PREKEY_LOOKUP_ALIAS_TTL_DAYS: int = 14
|
|
# TTL for relay witness history; keep continuity metadata bounded instead
|
|
# of relying on a hidden hardcoded retention window.
|
|
MESH_DM_WITNESS_TTL_DAYS: int = 14
|
|
# TTL for mailbox binding metadata — shorter = smaller metadata footprint on disk.
|
|
MESH_DM_BINDING_TTL_DAYS: int = 3
|
|
# When False, mailbox bindings are memory-only (agents re-register on restart).
|
|
# Enable explicitly only if restart continuity is worth persisting DM graph metadata.
|
|
MESH_DM_METADATA_PERSIST: bool = False
|
|
# Second explicit opt-in for at-rest DM metadata persistence. This keeps a
|
|
# single boolean flip from silently writing mailbox graph metadata to disk.
|
|
MESH_DM_METADATA_PERSIST_ACKNOWLEDGE: bool = False
|
|
# Optional import path for externally managed root witness material packages.
|
|
# Relative paths resolve from the backend directory.
|
|
MESH_DM_ROOT_EXTERNAL_WITNESS_IMPORT_PATH: str = ""
|
|
# Optional URI for externally managed root witness material packages.
|
|
# Supports file:// and http(s):// sources; when set it overrides the local path.
|
|
MESH_DM_ROOT_EXTERNAL_WITNESS_IMPORT_URI: str = ""
|
|
# Maximum acceptable age for externally sourced root witness packages.
|
|
# Strong DM trust fails closed when the imported package exported_at is older than this.
|
|
MESH_DM_ROOT_EXTERNAL_WITNESS_MAX_AGE_S: int = 3600
|
|
# Warning threshold for externally sourced root witness packages.
|
|
# When current external witness material reaches this age, operator health degrades to warning
|
|
# before the strong path eventually fails closed at MAX_AGE.
|
|
MESH_DM_ROOT_EXTERNAL_WITNESS_WARN_AGE_S: int = 2700
|
|
# Optional export path for the append-only stable-root transparency ledger.
|
|
# Relative paths resolve from the backend directory.
|
|
MESH_DM_ROOT_TRANSPARENCY_LEDGER_EXPORT_PATH: str = ""
|
|
# Optional URI used to read back and verify published transparency ledgers.
|
|
# Supports file:// and http(s):// sources.
|
|
MESH_DM_ROOT_TRANSPARENCY_LEDGER_READBACK_URI: str = ""
|
|
# Maximum acceptable age for externally read transparency ledgers.
|
|
# Strong DM trust fails closed when exported_at is older than this.
|
|
MESH_DM_ROOT_TRANSPARENCY_LEDGER_MAX_AGE_S: int = 3600
|
|
# Warning threshold for externally read transparency ledgers.
|
|
# When current external transparency readback reaches this age, operator health degrades to warning
|
|
# before the strong path eventually fails closed at MAX_AGE.
|
|
MESH_DM_ROOT_TRANSPARENCY_LEDGER_WARN_AGE_S: int = 2700
|
|
MESH_SCOPED_TOKENS: str = ""
|
|
# Deprecated legacy env vars kept for backward config compatibility only.
|
|
# Ordinary shipped gate flows keep MLS decrypt local; backend decrypt is
|
|
# reserved for explicit recovery reads.
|
|
MESH_GATE_BACKEND_DECRYPT_COMPAT: bool = False
|
|
MESH_GATE_BACKEND_DECRYPT_COMPAT_ACKNOWLEDGE: bool = False
|
|
MESH_BACKEND_GATE_DECRYPT_COMPAT: bool = False
|
|
# Deprecated legacy env vars kept for backward config compatibility only.
|
|
# Ordinary shipped gate flows keep compose/post local and submit encrypted
|
|
# payloads to the backend for sign/post only.
|
|
MESH_GATE_BACKEND_PLAINTEXT_COMPAT: bool = False
|
|
MESH_GATE_BACKEND_PLAINTEXT_COMPAT_ACKNOWLEDGE: bool = False
|
|
MESH_BACKEND_GATE_PLAINTEXT_COMPAT: bool = False
|
|
# Runtime gate for recovery envelopes. When off, per-gate
|
|
# envelope_recovery / envelope_always policies fail closed to
|
|
# envelope_disabled. Default True so the Reddit-like durable history
|
|
# model works out of the box: any member with the gate_secret can
|
|
# decrypt every envelope encrypted from the moment they had that key.
|
|
# Set MESH_GATE_RECOVERY_ENVELOPE_ENABLE=false to revert to MLS-only
|
|
# forward-secret behavior (your own history becomes unreadable after
|
|
# the sending ratchet advances).
|
|
MESH_GATE_RECOVERY_ENVELOPE_ENABLE: bool = True
|
|
MESH_GATE_RECOVERY_ENVELOPE_ENABLE_ACKNOWLEDGE: bool = True
|
|
# Durable gate plaintext retention is disabled by default. Enable only
|
|
# when the operator explicitly accepts the at-rest privacy tradeoff.
|
|
MESH_GATE_PLAINTEXT_PERSIST: bool = False
|
|
MESH_GATE_PLAINTEXT_PERSIST_ACKNOWLEDGE: bool = False
|
|
MESH_GATE_SESSION_ROTATE_MSGS: int = 50
|
|
MESH_GATE_SESSION_ROTATE_S: int = 3600
|
|
MESH_GATE_LEGACY_ENVELOPE_FALLBACK_MAX_DAYS: int = 30
|
|
# Add a randomized grace window before anonymous gate-session auto-rotation
|
|
# so threshold-triggered identity swaps are less trivially correlated.
|
|
MESH_GATE_SESSION_ROTATE_JITTER_S: int = 180
|
|
# Gate persona (named identity) rotation thresholds. Rotating the signing
|
|
# key limits the linkability window. Zero = disabled.
|
|
MESH_GATE_PERSONA_ROTATE_MSGS: int = 200
|
|
MESH_GATE_PERSONA_ROTATE_S: int = 604800 # 7 days
|
|
MESH_GATE_PERSONA_ROTATE_JITTER_S: int = 600
|
|
# Feature-flagged session stream for multiplexed gate room updates.
|
|
# Disabled by default so rollout stays explicit while stream-first rooms bake.
|
|
MESH_GATE_SESSION_STREAM_ENABLED: bool = False
|
|
MESH_GATE_SESSION_STREAM_HEARTBEAT_S: int = 20
|
|
MESH_GATE_SESSION_STREAM_BATCH_MS: int = 1500
|
|
MESH_GATE_SESSION_STREAM_MAX_GATES: int = 16
|
|
# Private gate APIs expose a backward-jittered timestamp view so observers
|
|
# cannot trivially align exact send times from response metadata alone.
|
|
MESH_GATE_TIMESTAMP_JITTER_S: int = 60
|
|
# Ban/kick gate-secret rotation is on by default (hardening Rec #10): the
|
|
# invariant has baked and a ban that does not rotate is effectively a
|
|
# display-only removal. Set MESH_GATE_BAN_KICK_ROTATION_ENABLE=false to
|
|
# revert to observe-only during incident triage.
|
|
MESH_GATE_BAN_KICK_ROTATION_ENABLE: bool = True
|
|
MESH_BLOCK_LEGACY_NODE_ID_COMPAT: bool = True
|
|
MESH_ALLOW_RAW_SECURE_STORAGE_FALLBACK: bool = False
|
|
MESH_ACK_RAW_FALLBACK_AT_OWN_RISK: bool = False
|
|
MESH_SECURE_STORAGE_SECRET: str = ""
|
|
MESH_SECURE_STORAGE_SECRET_FILE: str = ""
|
|
MESH_PRIVATE_LOG_TTL_S: int = 900
|
|
# Sprint 1 rollout: restored DM boot probes stay disabled by default until
|
|
# the architect reviews false positives from the observe-only path.
|
|
MESH_DM_RESTORED_SESSION_BOOT_PROBE_ENABLE: bool = False
|
|
# Queued DM release requires explicit per-item approval before any weaker
|
|
# relay fallback. Silent fallback is not a safe private-mode default.
|
|
MESH_PRIVATE_RELEASE_APPROVAL_ENABLE: bool = True
|
|
# Expiry for user-approved scoped private relay fallback policy. The policy
|
|
# is still bounded by hidden-transport checks before it can auto-release.
|
|
MESH_PRIVATE_RELAY_POLICY_TTL_S: int = 3600
|
|
# Background privacy prewarm prepares keys/aliases/transport readiness
|
|
# before send-time. Anonymous mode uses a cadence gate so user clicks do
|
|
# not directly create hidden-transport activity.
|
|
MESH_PRIVACY_PREWARM_ENABLE: bool = True
|
|
MESH_PRIVACY_PREWARM_INTERVAL_S: int = 300
|
|
MESH_PRIVACY_PREWARM_ANON_CADENCE_S: int = 300
|
|
# Sprint 4 rollout: authenticated RNS cover markers remain disabled until
|
|
# the observer-equivalence and receive-path DoS tests are green.
|
|
MESH_RNS_COVER_AUTH_MARKER_ENABLE: bool = False
|
|
# Signed-write revocation lookups use a short local TTL; stale entries force
|
|
# a local rebuild before honor. Offline/local-refresh failures remain
|
|
# observe-only until the later enforcement sprint.
|
|
MESH_SIGNED_REVOCATION_CACHE_TTL_S: int = 300
|
|
MESH_SIGNED_REVOCATION_CACHE_ENFORCE: bool = True
|
|
MESH_SIGNED_WRITE_CONTEXT_REQUIRED: bool = True
|
|
# Sprint 5 rollout: when enabled, root witness finality requires
|
|
# independent quorum for threshold>1 witnessed roots before they count as
|
|
# verified first-contact provenance.
|
|
WORMHOLE_ROOT_WITNESS_FINALITY_ENFORCE: bool = False
|
|
# Optional JSON artifact generated by CI/release workflow for the Sprint 8
|
|
# release gate. Relative paths resolve from the backend directory.
|
|
# dev = permissive local/dev behavior; testnet-private = strict private
|
|
# defaults; release-candidate = no compatibility/debug escape hatches.
|
|
MESH_RELEASE_PROFILE: str = "dev"
|
|
MESH_RELEASE_ATTESTATION_PATH: str = ""
|
|
# Operator release attestation for the Sprint 8 release gate. This does
|
|
# not change runtime behavior; it only records that the DM relay security
|
|
# suite was run and passed for the release candidate.
|
|
MESH_RELEASE_DM_RELAY_SECURITY_SUITE_GREEN: bool = False
|
|
PRIVACY_CORE_MIN_VERSION: str = "0.1.0"
|
|
PRIVACY_CORE_ALLOWED_SHA256: str = ""
|
|
PRIVACY_CORE_DEV_OVERRIDE: bool = False
|
|
# Sprint 4 rollout: fail fast when the loaded privacy-core artifact is
|
|
# missing required FFI symbols expected by the current Python bridge.
|
|
PRIVACY_CORE_EXPORT_SET_AUDIT_ENABLE: bool = True
|
|
# Clearnet fallback policy for private-tier messages.
|
|
# "block" (default) = refuse to send private messages over clearnet.
|
|
# "allow" = fall back to clearnet when Tor/RNS is unavailable (weaker privacy).
|
|
MESH_PRIVATE_CLEARNET_FALLBACK: str = "block"
|
|
# Second explicit opt-in for private-tier clearnet fallback. Without this
|
|
# acknowledgement, "allow" remains requested but not effective.
|
|
MESH_PRIVATE_CLEARNET_FALLBACK_ACKNOWLEDGE: bool = False
|
|
# Meshtastic MQTT bridge — disabled by default to avoid hammering the
|
|
# public broker. Users opt in explicitly.
|
|
MESH_MQTT_ENABLED: bool = False
|
|
# Meshtastic MQTT broker credentials (defaults match public firmware).
|
|
MESH_MQTT_BROKER: str = "mqtt.meshtastic.org"
|
|
MESH_MQTT_PORT: int = 1883
|
|
MESH_MQTT_USER: str = "meshdev"
|
|
MESH_MQTT_PASS: str = "large4cats"
|
|
# Hex-encoded PSK — empty string means use the default LongFast key.
|
|
# Must decode to exactly 16 or 32 bytes when set.
|
|
MESH_MQTT_PSK: str = ""
|
|
# Optional operator-provided Meshtastic node ID (e.g. "!abcd1234") included
|
|
# in the User-Agent when fetching from meshtastic.liamcottle.net so the
|
|
# service operator can identify per-install traffic instead of a generic
|
|
# "ShadowBroker" aggregate.
|
|
MESHTASTIC_OPERATOR_CALLSIGN: str = ""
|
|
# Per-install operator handle used in the User-Agent for EVERY third-party
|
|
# API the backend calls (Wikipedia, Wikidata, Nominatim, GDELT, OpenMHz,
|
|
# Broadcastify, weather.gov, NUFORC, etc.). The default is empty, in which
|
|
# case backend/services/network_utils.py auto-generates a stable
|
|
# pseudonymous handle like "operator-7f3a92" on first use and caches it.
|
|
# Operators who want to identify themselves with a real handle can set
|
|
# this; operators who want to stay pseudonymous can leave it empty.
|
|
#
|
|
# The handle is sent ONLY to public third-party APIs. It is NEVER mixed
|
|
# into mesh / Wormhole / Infonet identity (those have their own crypto
|
|
# identity layer; conflating the two would leak public attribution into
|
|
# private mesh state).
|
|
OPERATOR_HANDLE: str = ""
|
|
|
|
# SAR (Synthetic Aperture Radar) data layer
|
|
# Mode A — free catalog metadata, no account, default-on
|
|
MESH_SAR_CATALOG_ENABLED: bool = True
|
|
# Mode B — free pre-processed anomalies (OPERA / EGMS / GFM / EMS / UNOSAT)
|
|
# Two-step opt-in: must be "allow" AND _ACKNOWLEDGE must be true
|
|
MESH_SAR_PRODUCTS_FETCH: str = "block"
|
|
MESH_SAR_PRODUCTS_FETCH_ACKNOWLEDGE: bool = False
|
|
# NASA Earthdata Login (free) — required for OPERA products
|
|
MESH_SAR_EARTHDATA_USER: str = ""
|
|
MESH_SAR_EARTHDATA_TOKEN: str = ""
|
|
# Copernicus Data Space (free) — required for EGMS / EMS products
|
|
MESH_SAR_COPERNICUS_USER: str = ""
|
|
MESH_SAR_COPERNICUS_TOKEN: str = ""
|
|
# Whether OpenClaw agents may read/act on the SAR layer
|
|
MESH_SAR_OPENCLAW_ENABLED: bool = True
|
|
# Require private-tier transport before signing/broadcasting SAR anomalies
|
|
MESH_SAR_REQUIRE_PRIVATE_TIER: bool = True
|
|
|
|
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
|
|
|
|
|
@lru_cache
|
|
def get_settings() -> Settings:
|
|
try:
|
|
from services.api_settings import (
|
|
load_persisted_api_keys_into_environ,
|
|
load_persisted_openclaw_into_environ,
|
|
)
|
|
load_persisted_api_keys_into_environ()
|
|
load_persisted_openclaw_into_environ()
|
|
except Exception:
|
|
pass
|
|
return Settings()
|
|
|
|
|
|
def private_clearnet_fallback_requested(settings: Settings | None = None) -> str:
|
|
snapshot = settings or get_settings()
|
|
policy = str(getattr(snapshot, "MESH_PRIVATE_CLEARNET_FALLBACK", "block") or "block").strip().lower()
|
|
return "allow" if policy == "allow" else "block"
|
|
|
|
|
|
def private_clearnet_fallback_effective(settings: Settings | None = None) -> str:
|
|
snapshot = settings or get_settings()
|
|
requested = private_clearnet_fallback_requested(snapshot)
|
|
acknowledged = bool(getattr(snapshot, "MESH_PRIVATE_CLEARNET_FALLBACK_ACKNOWLEDGE", False))
|
|
if requested == "allow" and acknowledged:
|
|
return "allow"
|
|
return "block"
|
|
|
|
|
|
def backend_gate_decrypt_compat_effective(settings: Settings | None = None) -> bool:
|
|
snapshot = settings or get_settings()
|
|
return bool(
|
|
getattr(snapshot, "MESH_BACKEND_GATE_DECRYPT_COMPAT", False)
|
|
or getattr(snapshot, "MESH_GATE_BACKEND_DECRYPT_COMPAT", False)
|
|
)
|
|
|
|
|
|
def backend_gate_plaintext_compat_effective(settings: Settings | None = None) -> bool:
|
|
snapshot = settings or get_settings()
|
|
return bool(
|
|
getattr(snapshot, "MESH_BACKEND_GATE_PLAINTEXT_COMPAT", False)
|
|
or getattr(snapshot, "MESH_GATE_BACKEND_PLAINTEXT_COMPAT", False)
|
|
)
|
|
|
|
|
|
def gate_recovery_envelope_effective(settings: Settings | None = None) -> bool:
|
|
snapshot = settings or get_settings()
|
|
requested = bool(getattr(snapshot, "MESH_GATE_RECOVERY_ENVELOPE_ENABLE", False))
|
|
acknowledged = bool(getattr(snapshot, "MESH_GATE_RECOVERY_ENVELOPE_ENABLE_ACKNOWLEDGE", False))
|
|
return requested and acknowledged
|
|
|
|
|
|
def gate_plaintext_persist_effective(settings: Settings | None = None) -> bool:
|
|
snapshot = settings or get_settings()
|
|
requested = bool(getattr(snapshot, "MESH_GATE_PLAINTEXT_PERSIST", False))
|
|
acknowledged = bool(getattr(snapshot, "MESH_GATE_PLAINTEXT_PERSIST_ACKNOWLEDGE", False))
|
|
return requested and acknowledged
|
|
|
|
|
|
def gate_ban_kick_rotation_enabled(settings: Settings | None = None) -> bool:
|
|
snapshot = settings or get_settings()
|
|
return bool(getattr(snapshot, "MESH_GATE_BAN_KICK_ROTATION_ENABLE", False))
|
|
|
|
|
|
def dm_restored_session_boot_probe_enabled(settings: Settings | None = None) -> bool:
|
|
snapshot = settings or get_settings()
|
|
return bool(getattr(snapshot, "MESH_DM_RESTORED_SESSION_BOOT_PROBE_ENABLE", False))
|
|
|
|
|
|
def signed_revocation_cache_ttl_s(settings: Settings | None = None) -> int:
|
|
snapshot = settings or get_settings()
|
|
return max(0, int(getattr(snapshot, "MESH_SIGNED_REVOCATION_CACHE_TTL_S", 300) or 0))
|
|
|
|
|
|
def signed_revocation_cache_enforce(settings: Settings | None = None) -> bool:
|
|
snapshot = settings or get_settings()
|
|
return bool(getattr(snapshot, "MESH_SIGNED_REVOCATION_CACHE_ENFORCE", False))
|
|
|
|
|
|
def wormhole_root_witness_finality_enforce(settings: Settings | None = None) -> bool:
|
|
snapshot = settings or get_settings()
|
|
return bool(getattr(snapshot, "WORMHOLE_ROOT_WITNESS_FINALITY_ENFORCE", False))
|