From 7a5c62e062e0cca20798be73e9a71af4200b40ea Mon Sep 17 00:00:00 2001 From: Shiv Rossi Date: Fri, 4 Sep 2026 22:44:12 -0600 Subject: [PATCH] fix: preserve legacy Telegram token upgrades --- README.md | 13 ++-- crates/iris-providers/src/config.rs | 108 ++++++++++++++++++++++++++++ deploy/docker-compose.yml | 3 +- 3 files changed, 119 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 85a1069..09fc5e8 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ docker run --rm \ --publish 127.0.0.1:9876:9876 \ --volume iris-data:/data \ --env IRIS_ENABLED_PROVIDERS=telegram \ - --env IRIS_TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN}" \ + --env IRIS_TELEGRAM_BOT_TOKEN="${IRIS_TELEGRAM_BOT_TOKEN}" \ ghcr.io/techgodhq/iris:latest ``` @@ -61,7 +61,7 @@ variables. To keep the full-fidelity TOML path, mount a file and set For a reference Iris + Rite deployment, use [`deploy/docker-compose.yml`](deploy/docker-compose.yml). Set -`TELEGRAM_BOT_TOKEN` and `RITE_GITHUB_WEBHOOK_SECRET` in its environment before +`IRIS_TELEGRAM_BOT_TOKEN` and `RITE_GITHUB_WEBHOOK_SECRET` in its environment before running `docker compose -f deploy/docker-compose.yml up -d`. ## Configuration @@ -77,7 +77,10 @@ For file-free configuration, set `IRIS_ENABLED_PROVIDERS` and matching `IRIS_EMAIL_IMAP_PORT`, `IRIS_EMAIL_SMTP_HOST`, `IRIS_EMAIL_SMTP_PORT`, `IRIS_EMAIL_USERNAME`, `IRIS_EMAIL_PASSWORD`, and optional `IRIS_EMAIL_MAILBOX`, `IRIS_EMAIL_FROM`, `IRIS_EMAIL_PAGE_SIZE`, and `IRIS_EMAIL_MAX_MESSAGES`. -Enabled providers validate required credentials at startup. +Enabled providers validate required credentials at startup. For upgrade compatibility, +Telegram also accepts the legacy `TELEGRAM_BOT_TOKEN` only when +`IRIS_TELEGRAM_BOT_TOKEN` is absent; migrate to the Iris-prefixed name because the +canonical variable always takes precedence and the legacy fallback is deprecated. ```toml [providers.mock] @@ -95,7 +98,9 @@ enabled = true [providers.telegram.credentials] # Telegram Bot API token. `token` is also accepted as an alias. -bot_token = { env = "TELEGRAM_BOT_TOKEN" } +# Prefer the canonical Iris-prefixed variable. Legacy `TELEGRAM_BOT_TOKEN` +# remains a deprecated fallback only when this variable is absent. +bot_token = { env = "IRIS_TELEGRAM_BOT_TOKEN" } ``` The Telegram provider uses the Bot API. It can list and normalize messages that diff --git a/crates/iris-providers/src/config.rs b/crates/iris-providers/src/config.rs index 4994a59..648ef40 100644 --- a/crates/iris-providers/src/config.rs +++ b/crates/iris-providers/src/config.rs @@ -334,6 +334,9 @@ fn resolve_provider( }) } +const LEGACY_TELEGRAM_BOT_TOKEN_ENV: &str = "TELEGRAM_BOT_TOKEN"; +const CANONICAL_TELEGRAM_BOT_TOKEN_ENV: &str = "IRIS_TELEGRAM_BOT_TOKEN"; + fn apply_provider_env(config: &mut ProviderConfig, provider_type: &str, instance: Option<&str>) { let fields: &[(&str, &str)] = provider_env_fields() .iter() @@ -357,6 +360,26 @@ fn apply_provider_env(config: &mut ProviderConfig, provider_type: &str, instance .insert((*field).to_owned(), SecretValue::FromEnv { env: variable }); } } + + // v0.1.0 documented this unprefixed variable. Retain it only for the + // default Telegram instance so existing deployments can upgrade in place. + if provider_type == "telegram" + && instance.is_none() + && env::var_os(CANONICAL_TELEGRAM_BOT_TOKEN_ENV).is_none() + && env::var_os(LEGACY_TELEGRAM_BOT_TOKEN_ENV).is_some() + { + tracing::warn!( + legacy_env = LEGACY_TELEGRAM_BOT_TOKEN_ENV, + canonical_env = CANONICAL_TELEGRAM_BOT_TOKEN_ENV, + "deprecated Telegram credential environment variable selected; the canonical variable takes precedence when both are set" + ); + config.credentials.insert( + "bot_token".to_owned(), + SecretValue::FromEnv { + env: LEGACY_TELEGRAM_BOT_TOKEN_ENV.to_owned(), + }, + ); + } } impl SecretValue { @@ -709,6 +732,91 @@ bot_token = "123:abc" assert_eq!(providers[0].id(), "telegram"); } + #[test] + fn legacy_telegram_token_env_builds_default_provider() { + let _guard = env_lock().lock().expect("environment lock"); + temp_env::with_vars( + [ + (ENABLED_PROVIDERS_ENV, Some("telegram")), + (LEGACY_TELEGRAM_BOT_TOKEN_ENV, Some("legacy-token")), + (CANONICAL_TELEGRAM_BOT_TOKEN_ENV, None), + ], + || { + let mut config = IrisConfig::default(); + config.apply_env_overrides().expect("env applies"); + let resolved = config + .resolved_enabled_providers() + .expect("provider resolves"); + assert_eq!(resolved[0].credentials["bot_token"], "legacy-token"); + let providers = providers_from_config(&config, &test_store(), &test_audit()) + .expect("legacy token constructs Telegram provider"); + assert_eq!(providers[0].id(), "telegram"); + }, + ); + } + + #[test] + fn canonical_telegram_token_env_builds_default_provider() { + let _guard = env_lock().lock().expect("environment lock"); + temp_env::with_vars( + [ + (ENABLED_PROVIDERS_ENV, Some("telegram")), + (LEGACY_TELEGRAM_BOT_TOKEN_ENV, None), + (CANONICAL_TELEGRAM_BOT_TOKEN_ENV, Some("canonical-token")), + ], + || { + let mut config = IrisConfig::default(); + config.apply_env_overrides().expect("env applies"); + let resolved = config + .resolved_enabled_providers() + .expect("provider resolves"); + assert_eq!(resolved[0].credentials["bot_token"], "canonical-token"); + }, + ); + } + + #[test] + fn canonical_telegram_token_env_wins_over_legacy() { + let _guard = env_lock().lock().expect("environment lock"); + temp_env::with_vars( + [ + (ENABLED_PROVIDERS_ENV, Some("telegram")), + (LEGACY_TELEGRAM_BOT_TOKEN_ENV, Some("legacy-token")), + (CANONICAL_TELEGRAM_BOT_TOKEN_ENV, Some("canonical-token")), + ], + || { + let mut config = IrisConfig::default(); + config.apply_env_overrides().expect("env applies"); + let resolved = config + .resolved_enabled_providers() + .expect("provider resolves"); + assert_eq!(resolved[0].credentials["bot_token"], "canonical-token"); + }, + ); + } + + #[test] + fn legacy_telegram_token_env_does_not_overlay_unrelated_provider() { + let _guard = env_lock().lock().expect("environment lock"); + temp_env::with_vars( + [ + (ENABLED_PROVIDERS_ENV, Some("mock")), + (LEGACY_TELEGRAM_BOT_TOKEN_ENV, Some("legacy-token")), + (CANONICAL_TELEGRAM_BOT_TOKEN_ENV, None), + ], + || { + let mut config = IrisConfig::default(); + config.apply_env_overrides().expect("env applies"); + let resolved = config + .resolved_enabled_providers() + .expect("provider resolves"); + assert_eq!(resolved.len(), 1); + assert_eq!(resolved[0].id, "mock"); + assert!(resolved[0].credentials.is_empty()); + }, + ); + } + #[test] fn builds_email_provider_from_credentials() { let config = IrisConfig::from_toml( diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 77d95f8..abcfd5e 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -6,7 +6,8 @@ services: restart: unless-stopped environment: IRIS_ENABLED_PROVIDERS: ${IRIS_ENABLED_PROVIDERS:-telegram} - TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN:?set TELEGRAM_BOT_TOKEN} + # `TELEGRAM_BOT_TOKEN` is a deprecated fallback for upgrades only. + IRIS_TELEGRAM_BOT_TOKEN: ${IRIS_TELEGRAM_BOT_TOKEN:?set IRIS_TELEGRAM_BOT_TOKEN} volumes: - iris-data:/data ports: