Skip to content

feat: verify telegram owner during hot activation#1157

Merged
henrypark133 merged 8 commits intostagingfrom
codex/telegram-hot-activation-tests
Mar 16, 2026
Merged

feat: verify telegram owner during hot activation#1157
henrypark133 merged 8 commits intostagingfrom
codex/telegram-hot-activation-tests

Conversation

@henrypark133
Copy link
Copy Markdown
Collaborator

@henrypark133 henrypark133 commented Mar 13, 2026

Summary

This PR fixes Telegram hot activation so setup can establish the Telegram owner
binding before the channel starts, instead of hot-activating into a
owner_id = null state that falls back to pairing.

It also adds end-to-end regression coverage around the Telegram setup flow,
owner-aware activation status, and Telegram authorization behavior.

Problem

In v0.18.0, Telegram setup could successfully:

  • install the Telegram WASM channel
  • save telegram_bot_token
  • hot-activate the channel
  • start polling Telegram

but still come up with:

  • owner_id = null
  • dm_policy = pairing
  • bot_username = null

That meant "hot activation worked" at the transport/runtime level, but the
intended owner-verified Telegram experience did not. The first DM to the bot
fell back into pairing behavior instead of using a configured owner binding.

What Changed

Telegram setup now verifies and persists the owner binding

When configuring the Telegram channel, the extension manager now:

  • validates the bot token with getMe
  • reuses an existing persisted Telegram owner when one is already configured
  • otherwise polls getUpdates to capture the first qualifying private user DM
  • persists channels.wasm_channel_owner_ids.telegram
  • persists the resolved bot_username
  • injects that runtime config into the hot-activated Telegram channel

After activation succeeds for a newly verified Telegram owner, IronClaw also
sends a one-time confirmation DM to that owner so the binding result is visible
inside Telegram.

Telegram startup/hot-activation config is now consistent

The host now restores/passes Telegram-specific runtime config more consistently:

  • owner_id is injected during hot activation and refresh flows
  • persisted bot_username is restored on startup as well, not only on the hot path

This keeps post-restart Telegram behavior aligned with hot activation.

Web/UI status handling is shared and typed

The Extensions UI logic was tightened so owner-bound Telegram channels show as
active instead of incorrectly presenting as still awaiting pairing.

This PR also refactors activation-status classification away from duplicated
stringly-typed logic into a shared typed helper used by the web handlers.

Telegram setup UI now explains the owner-verification step

The web configure modal now tells the user that, after saving the bot token,
they need to send a private message to the bot so IronClaw can verify them as
the owner. The modal also shows a waiting state while setup is in progress.

The agent-thread auth/setup prompt for Telegram now carries the same guidance.

E2E fixture isolation and worktree reliability

The E2E harness now:

  • runs IronClaw with a temp HOME / IRONCLAW_BASE_DIR
  • avoids touching the developer’s real pairing/allowFrom state
  • rebuilds the embedded gateway binary when source files are newer than the built binary
  • keeps worktree-based artifact discovery working for Telegram auth integration tests

Files of Note

  • src/extensions/manager.rs
    • Telegram owner verification during setup
    • hot-activation config injection
    • owner confirmation DM
    • new manager-level regression coverage
  • channels-src/telegram/src/lib.rs
    • existing runtime owner enforcement remains the source of truth
  • src/channels/web/types.rs
    • typed ExtensionActivationStatus
    • shared WASM channel activation classifier
  • src/channels/web/server.rs
  • src/channels/web/handlers/extensions.rs
    • owner-bound channel status classification
  • src/channels/web/static/app.js
  • src/channels/web/static/i18n/en.js
  • src/channels/web/static/style.css
    • Telegram setup guidance/waiting-state UX
  • src/channels/wasm/setup.rs
  • src/channels/wasm/telegram_host_config.rs
    • shared Telegram host-config helpers and startup restoration
  • tests/e2e/conftest.py
    • temp HOME isolation and smarter rebuild behavior
  • tests/e2e/scenarios/test_telegram_hot_activation.py
    • browser coverage for the Telegram setup/activation flow
  • tests/telegram_auth_integration.rs
    • real Telegram authorization enforcement coverage

Test Coverage

Rust manager/unit coverage

  • test_telegram_hot_activation_runtime_config_includes_owner_id
  • test_telegram_hot_activation_configure_uses_mock_loader_and_persists_state
  • test_notify_telegram_owner_verified_sends_confirmation_for_new_binding
  • test_notify_telegram_owner_verified_skips_existing_binding
  • test_telegram_auth_instructions_include_owner_verification_guidance
  • test_wasm_channel_activation_status_owner_bound_counts_as_active

Telegram auth integration coverage

tests/telegram_auth_integration.rs continues to verify the message-level
authorization behavior:

  • non-owner messages are rejected when owner_id is set
  • allowlist and pairing fallback behavior still works when owner_id is null

Playwright coverage

tests/e2e/scenarios/test_telegram_hot_activation.py covers:

  • Telegram setup modal rendering
  • bot token submission payload
  • owner-verification guidance in the UI
  • hot-activation success path into the correct post-setup state

Verification

  • cargo test --lib test_telegram_hot_activation -- --nocapture
  • cargo test --lib test_notify_telegram_owner_verified -- --nocapture
  • cargo test --lib test_telegram_auth_instructions_include_owner_verification_guidance -- --nocapture
  • cargo test --lib test_wasm_channel_activation_status_owner_bound_counts_as_active -- --nocapture
  • cargo test --test telegram_auth_integration -- --nocapture
  • /Users/henry/near_ai/near_claw/ironclaw/tests/e2e/.venv/bin/pytest tests/e2e/scenarios/test_telegram_hot_activation.py -q

Feature Parity

Updated FEATURE_PARITY.md to note Telegram setup-time owner verification.

Copilot AI review requested due to automatic review settings March 13, 2026 23:50
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@github-actions github-actions bot added scope: channel/web Web gateway channel scope: channel/wasm WASM channel runtime scope: extensions Extension management scope: docs Documentation size: XL 500+ changed lines risk: medium Business logic, config, or moderate-risk modules contributor: core 20+ merged PRs labels Mar 13, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Telegram WASM channel “hot activation” improvements by verifying channel ownership during setup (via a Telegram DM), persisting owner/bot metadata, and reflecting the new activation lifecycle in the web UI and tests.

Changes:

  • Implement setup-time Telegram owner verification and persist owner_id + bot_username for runtime config injection.
  • Introduce a typed activation_status for extensions and update web handlers/UI to treat owner-bound channels as “Active”.
  • Expand test coverage: new Playwright scenario, Rust unit tests, and improved E2E isolation/build behavior.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/telegram_auth_integration.rs Locate Telegram channel WASM across git worktrees for integration tests.
tests/e2e/scenarios/test_telegram_hot_activation.py New E2E coverage for Telegram setup modal hint + hot-activation flow.
tests/e2e/conftest.py Isolate HOME/base dir for E2E; add binary rebuild detection logic.
src/extensions/manager.rs Core Telegram owner-verification flow, persisted binding state, runtime config updates, and new unit tests.
src/channels/web/types.rs Add ExtensionActivationStatus enum + classifier for consistent activation state reporting.
src/channels/web/static/style.css Add styling for a configure modal hint panel.
src/channels/web/static/i18n/en.js Add Telegram-specific setup hint + “waiting” button text.
src/channels/web/static/app.js Show Telegram owner-verification hint; update button text while waiting; restore on failure.
src/channels/web/server.rs Use activation classifier and count owner-bound channels as “Active”; add test.
src/channels/web/handlers/extensions.rs Same activation classification update for the handlers module.
src/channels/wasm/telegram_host_config.rs Add Telegram name constant + settings key helper for bot username persistence.
src/channels/wasm/setup.rs Inject persisted Telegram bot_username into runtime config on startup.
src/channels/wasm/mod.rs Wire in and re-export Telegram host config helpers.
FEATURE_PARITY.md Document setup-time owner verification as part of Telegram parity.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

let get_me_url = format!("https://api.telegram.org/bot{bot_token}/getMe");
let get_me_resp =
client.get(&get_me_url).send().await.map_err(|e| {
ExtensionError::Other(format!("Telegram getMe request failed: {e}"))

let delete_webhook_url = format!("https://api.telegram.org/bot{bot_token}/deleteWebhook");
let delete_webhook_resp = client.post(&delete_webhook_url).send().await.map_err(|e| {
ExtensionError::Other(format!("Telegram deleteWebhook request failed: {e}"))
Comment on lines +4060 to +4061
.map_err(|e| {
ExtensionError::Other(format!("Telegram getUpdates request failed: {e}"))
Comment on lines +78 to +82
ROOT / "providers.json",
ROOT / "src",
ROOT / "channels-src",
]
return any(_latest_mtime(path) > binary_mtime for path in inputs)
#[serde(skip_serializing_if = "Option::is_none")]
pub activation_status: Option<String>,
pub activation_status: Option<ExtensionActivationStatus>,
/// Human-readable error when activation_status is "failed".
Copy link
Copy Markdown
Collaborator

@zmanian zmanian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

The core verification logic is well-structured and the security model is reasonable for single-owner private bot setup. Three blocking issues.

Blocking

1. Bug: server.rs lost the ChannelRelay activation status branch

The refactoring replaced the per-kind if/else if chain in server.rs with a blanket call to classify_wasm_channel_activation() for ALL extensions. That function returns None for non-WasmChannel kinds. The original code had a separate else if ext.kind == ChannelRelay branch returning Active/Configured/Installed.

handlers/extensions.rs correctly preserves this branch, but server.rs does not. ChannelRelay extensions (e.g., Signal) will report activation_status: null instead of their correct status.

2. Clippy failure -- let...else should use ? operator at manager.rs:446:

// Current:
let Some(store) = self.store.as_ref() else { return None; };
// Should be:
let store = self.store.as_ref()?;

3. No-panics CI check flagging test code -- .expect()/.unwrap() in #[cfg(test)] blocks are being flagged. Needs resolution (either fix CI check to exclude test blocks, or move test code to a location the check ignores).

Non-blocking

  • Code duplication in #[cfg(test)]/#[cfg(not(test))] loader blocks (~50 lines duplicated). Extract common construction into a helper.
  • Bot token in Telegram API URLs could leak to trace logs. Consider sanitizing before logging.
  • deleteWebhook during verification leaves channel broken if verification times out (120s). Any previously configured webhook is destroyed but channel isn't activated. Consider documenting this or restoring webhook on timeout.
  • First-user-wins during 120s window -- first non-bot private DM sender becomes owner. Low risk for newly created bots during setup, but worth noting for public bots.

Security

  • Verification flow is sound: getMe validates token, getUpdates long-polls for owner DM, binding is persisted and injected into WASM runtime.
  • After initial binding, WASM channel runtime rejects non-owner messages (unchanged enforcement).
  • No race conditions in the activation flow -- RwLock is not held across the long-polling await.

Copilot AI review requested due to automatic review settings March 14, 2026 04:25
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds “setup-time owner verification” for the Telegram WASM channel (deriving/storing an owner_id by polling Telegram updates), along with UI affordances and test coverage to support hot-activation and clearer activation states in the web Extensions tab.

Changes:

  • Implement Telegram owner verification during configure() (polls Telegram Bot API, persists owner_id + bot_username, updates runtime config, and optionally sends a confirmation DM).
  • Improve Extensions UI/JSON activation-state reporting (typed activation status + Telegram-specific setup hint/waiting state) and add E2E coverage.
  • Harden tests/dev ergonomics (worktree-aware Telegram WASM path in integration test; E2E runs with isolated HOME; conditional gateway binary rebuild).

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/telegram_auth_integration.rs Locates Telegram channel WASM artifact across git worktrees.
tests/e2e/scenarios/test_telegram_hot_activation.py New Playwright coverage for Telegram setup modal + hot-activation UI flow.
tests/e2e/conftest.py Isolates HOME for E2E + adds “rebuild binary if sources changed” heuristic.
src/extensions/manager.rs Implements Telegram owner verification, stores owner/bot username, and injects config overrides during activation/refresh.
src/channels/web/types.rs Introduces typed ExtensionActivationStatus + classification helper for WASM channels.
src/channels/web/static/style.css Adds styling for a configure-modal hint block.
src/channels/web/static/i18n/en.js Adds Telegram-specific configure hint/waiting strings.
src/channels/web/static/app.js Shows Telegram hint in configure modal; changes submit button text while waiting on verification.
src/channels/web/server.rs Uses activation-status classifier and treats owner-bound channels as “active”; adds tests.
src/channels/web/handlers/extensions.rs Mirrors server-side activation-status classification logic.
src/channels/wasm/telegram_host_config.rs Centralizes Telegram channel name + settings key helper for bot username persistence.
src/channels/wasm/setup.rs Injects persisted Telegram bot_username into runtime config during startup channel registration.
src/channels/wasm/mod.rs Wires new Telegram host-config module exports.
FEATURE_PARITY.md Notes Telegram “setup-time owner verification” parity.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +4022 to +4026
let delete_webhook_url = format!("https://api.telegram.org/bot{bot_token}/deleteWebhook");
let delete_webhook_resp = client.post(&delete_webhook_url).send().await.map_err(|e| {
ExtensionError::Other(format!("Telegram deleteWebhook request failed: {e}"))
})?;
if !delete_webhook_resp.status().is_success() {
let deadline = std::time::Instant::now()
+ std::time::Duration::from_secs(TELEGRAM_OWNER_BIND_TIMEOUT_SECS);
let mut offset = 0_i64;

Comment on lines +80 to +81
ROOT / "channels-src",
]
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2be585a. _latest_mtime() now walks the tree with os.walk() and prunes target/ directories before checking mtimes, so local channels-src/*/target artifacts no longer force unnecessary gateway rebuilds during E2E runs.

Comment on lines +3988 to +3989
client.get(&get_me_url).send().await.map_err(|e| {
ExtensionError::Other(format!("Telegram getMe request failed: {e}"))
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2be585a. I replaced the raw reqwest::Error formatting with the typed telegram_request_error() / telegram_response_parse_error() helpers, so getMe/deleteWebhook/getUpdates failures no longer surface token-bearing URLs in logs or API errors.

Comment on lines +4056 to +4060
.send()
.await
.map_err(|e| {
ExtensionError::Other(format!("Telegram getUpdates request failed: {e}"))
})?;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2be585a. The same sanitizing helper now covers the getUpdates path as well, so we no longer interpolate a raw reqwest::Error that could include the token-bearing Telegram URL.


let delete_webhook_url = format!("https://api.telegram.org/bot{bot_token}/deleteWebhook");
let delete_webhook_resp = client.post(&delete_webhook_url).send().await.map_err(|e| {
ExtensionError::Other(format!("Telegram deleteWebhook request failed: {e}"))
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2be585a. deleteWebhook now goes through the same sanitized Telegram request-error helper, so setup failures no longer echo a raw URL that embeds the bot token.

@henrypark133
Copy link
Copy Markdown
Collaborator Author

Addressed the blocking review items on the latest branch head.

  • ChannelRelay activation status is restored in \ instead of routing every extension through the WASM-channel classifier. I also added a regression test for that path in commit .
  • The clippy \ warning in \ is fixed in commit \ by switching to the \ operator.
  • The no-panics CI issue is also fixed in : the new in-file tests were rewritten to avoid \ patterns that the repo guard flags in changed production files.

Re-verified on current head:

  • \
  • \
  • \

  • running 1 test
    test channels::web::server::tests::test_wasm_channel_activation_status_owner_bound_counts_as_active ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 3042 filtered out; finished in 0.01s


  • running 1 test
    test channels::web::server::tests::test_channel_relay_activation_status_is_preserved ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 3042 filtered out; finished in 0.00s

@henrypark133
Copy link
Copy Markdown
Collaborator Author

Addressed the blocking review items on the latest branch head.

  • ChannelRelay activation status is restored in server.rs instead of routing every extension through the WASM-channel classifier. I also added a regression test for that path in commit c3a406d.
  • The clippy let...else warning in manager.rs is fixed in commit e50ea46 by switching to the ? operator.
  • The no-panics CI issue is also fixed in e50ea46: the new in-file tests were rewritten to avoid assert!/expect/unwrap patterns that the repo guard flags in changed production files.

Re-verified on current head:

  • cargo fmt --check
  • cargo clippy --all-targets -- -D warnings
  • cargo clippy --all-features --all-targets -- -D warnings
  • cargo test --lib test_wasm_channel_activation_status_owner_bound_counts_as_active -- --nocapture
  • cargo test --lib test_channel_relay_activation_status_is_preserved -- --nocapture

zmanian
zmanian previously approved these changes Mar 14, 2026
Copy link
Copy Markdown
Collaborator

@zmanian zmanian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review: All blocking items resolved

All 3 blocking items from my previous review have been addressed:

  1. ChannelRelay activation status restored -- Fixed in commit c3a406d. The server.rs extensions_list_handler now has proper if ext.kind == WasmChannel / else if ChannelRelay / else None branching. New test test_channel_relay_activation_status_is_preserved covers this.
  2. Clippy let...else -> ? operator -- Fixed in commit e50ea46. manager.rs:464 now uses let store = self.store.as_ref()?.
  3. No-panics CI check on test code -- Fixed in commit e50ea46. Tests converted to -> Result<(), String> with require/require_eq helpers instead of assert!/.expect().

Also addressed from non-blocking:

  • Bot token sanitization -- Fixed in commit 2be585a. New telegram_request_error/telegram_response_parse_error helpers log structured fields (action, status, timeout flags) without exposing the raw error message that might contain the token URL.
  • E2E conftest -- _latest_mtime now skips target/ directory during mtime scan.

Full CI is green (all Clippy variants, Formatting, E2E, regression test enforcement).

LGTM.

…-activation-tests

# Conflicts:
#	src/extensions/manager.rs
Copilot AI review requested due to automatic review settings March 14, 2026 23:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Telegram “hot activation” with setup-time owner verification, updates the web UI/API to reflect richer WASM-channel activation states, and expands test coverage (Rust unit tests + Playwright E2E) to cover the new flow.

Changes:

  • Implement Telegram setup-time owner binding + runtime config injection (owner_id / bot_username) during activation and reconfiguration.
  • Replace stringly-typed WASM channel activation status with a typed enum and shared classification logic for the web API.
  • Add/adjust integration + E2E tests (including worktree-friendly WASM/binary handling).

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/extensions/manager.rs Core implementation: Telegram owner verification via Bot API polling, persisted owner/bot-username settings, hot-activation refinements, and extensive new tests.
src/channels/wasm/telegram_host_config.rs Centralizes Telegram channel name constant + bot-username settings key helper.
src/channels/wasm/mod.rs Exposes Telegram host config helpers within the wasm channel module.
src/channels/wasm/setup.rs Injects persisted bot_username into Telegram channel runtime config at startup.
src/channels/web/types.rs Introduces ExtensionActivationStatus enum + classify_wasm_channel_activation() helper; updates ExtensionInfo accordingly.
src/channels/web/server.rs Uses the shared activation classification and incorporates “owner-bound counts as active”; adds tests.
src/channels/web/handlers/extensions.rs Mirrors the server handler changes for extension listing / activation status classification.
src/channels/web/static/app.js Adds Telegram-specific setup hint and “waiting for owner verification” button state in the configure modal.
src/channels/web/static/i18n/en.js Adds English strings for the Telegram owner-verification hint/waiting UI.
src/channels/web/static/style.css Styles the new configure hint block.
tests/e2e/conftest.py Makes E2E runs more hermetic (temp HOME / base dir) and rebuilds the binary when inputs change.
tests/e2e/scenarios/test_telegram_hot_activation.py New Playwright test covering Telegram setup modal + hot-activation UI transitions.
tests/telegram_auth_integration.rs Makes Telegram WASM path resolution work in git worktree setups.
FEATURE_PARITY.md Documents Telegram’s new “setup-time owner verification” capability.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

zmanian
zmanian previously approved these changes Mar 15, 2026
Copy link
Copy Markdown
Collaborator

@zmanian zmanian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review: All prior items resolved + new verification code mechanism

The 4 original commits are unchanged from my prior review (all 3 blocking items resolved). Two new commits: a staging merge and a significant security improvement.

Prior blocking items (still resolved)

  1. ChannelRelay activation status -- restored in server.rs with test
  2. Clippy let...else -- fixed to ?-operator
  3. No-panics CI -- tests converted to Result<(), String>

New: Verification code replaces first-DM-wins (commit 7373178)

This directly addresses the "first-user-wins during 120s window" concern from my earlier review. Previously, the FIRST private DM sender became the owner. Now:

  1. System generates an 8-character random code (rand::thread_rng(), alphanumeric, ~2.8 trillion combinations)
  2. User sees the code + instructions + deep link (https://t.me/bot?start=CODE) in the configure modal
  3. Polling loop validates the received message matches the code (handles exact match, /start CODE, /start prefix)
  4. Challenge has 300s TTL, auto-cleared on expiry
  5. deleteWebhook called with drop_pending_updates=true to ensure bot only receives messages after code issuance

The flow change touches all web handler paths (chat auth token, setup submit, ws) consistently -- result.verification.is_some() broadcasts AuthRequired instead of AuthCompleted, keeping auth mode active until verification completes.

Security assessment

  • Code entropy is adequate for a short-lived (5 min) verification token
  • No code leakage -- displayed to authenticated user only, sent over HTTPS deep link
  • textContent used for code display in UI (no XSS)
  • Expired challenges are auto-pruned on access

CI

Full CI green (all Clippy, Formatting, E2E core/extensions/features, regression check).

Title check: "feat: verify telegram owner during hot activation" -- accurate. The verification code is a natural evolution of the verification mechanism described in the title.

LGTM.

…-activation-tests

# Conflicts:
#	src/channels/wasm/setup.rs
#	src/channels/web/server.rs
#	src/extensions/manager.rs
Copilot AI review requested due to automatic review settings March 16, 2026 14:58
@henrypark133
Copy link
Copy Markdown
Collaborator Author

bypassing and merging. Already got approvals -> just rebasing/merge conflict resolution.

@henrypark133 henrypark133 merged commit 63a2355 into staging Mar 16, 2026
23 checks passed
@henrypark133 henrypark133 deleted the codex/telegram-hot-activation-tests branch March 16, 2026 15:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Telegram “hot activation” support by introducing a setup-time owner-verification challenge, wiring it through the extension manager, web API/SSE, and the web UI, with new unit + E2E coverage.

Changes:

  • Introduce verification challenges in extension configure results and surface them via web handlers and UI.
  • Implement Telegram owner binding (challenge issuance + long-poll verification) and persist owner/bot-username state into settings/runtime config.
  • Add E2E scenario coverage and improve E2E isolation/rebuild behavior.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/telegram_auth_integration.rs Make WASM test artifact discovery more robust (local path + git worktrees).
tests/e2e/scenarios/test_telegram_hot_activation.py New Playwright scenario covering Telegram setup modal + verify-owner flow.
tests/e2e/conftest.py Isolate HOME/base dir for E2E and rebuild the binary when inputs change.
src/extensions/mod.rs Add VerificationChallenge and extend ConfigureResult with verification.
src/extensions/manager.rs Implement Telegram owner verification/binding + runtime config updates + tests.
src/channels/web/ws.rs Treat verification as “still auth required” in WS auth-token flow.
src/channels/web/types.rs Add typed ExtensionActivationStatus + verification field on ActionResponse.
src/channels/web/static/style.css Add styles for Telegram verification panel in configure modal.
src/channels/web/static/i18n/en.js Add Telegram verification UI strings.
src/channels/web/static/app.js Render Telegram verification hint/panel; update configure submit UX.
src/channels/web/server.rs Surface verification through REST handlers + SSE (AuthRequired/AuthCompleted).
src/channels/web/handlers/extensions.rs Use new activation-status classifier + consider owner binding as “active”.
src/channels/web/handlers/chat.rs Update chat auth-token handler to broadcast AuthRequired when verification pending.
src/channels/wasm/telegram_host_config.rs Define Telegram constants + settings key helper for bot username.
src/channels/wasm/setup.rs Inject persisted Telegram bot username into runtime config on startup.
src/channels/wasm/mod.rs Export Telegram host-config helpers for internal use.
FEATURE_PARITY.md Document “setup-time owner verification” for Telegram parity notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1166 to +1188
Ok(result) => {
let mut resp = if result.verification.is_some() || result.activated {
ActionResponse::ok(result.message.clone())
} else {
ActionResponse::fail(result.message.clone())
};
resp.activated = Some(result.activated);
resp.auth_url = result.auth_url.clone();
resp.verification = result.verification.clone();
resp.instructions = result.verification.as_ref().map(|v| v.instructions.clone());

state.sse.broadcast(SseEvent::AuthCompleted {
extension_name: req.extension_name.clone(),
success: true,
message: result.message.clone(),
});
if result.verification.is_some() {
state.sse.broadcast(SseEvent::AuthRequired {
extension_name: req.extension_name.clone(),
instructions: Some(result.message),
auth_url: None,
setup_url: None,
});
} else if result.activated {
// Clear auth mode on the active thread
clear_auth_mode(&state).await;

state.sse.broadcast(SseEvent::AuthCompleted {
Comment on lines 164 to +181
Ok(result) => {
clear_auth_mode(&state).await;
let mut resp = ActionResponse::ok(result.message.clone());
resp.activated = Some(result.activated);
resp.auth_url = result.auth_url.clone();
resp.verification = result.verification.clone();
resp.instructions = result.verification.as_ref().map(|v| v.instructions.clone());

state.sse.broadcast(SseEvent::AuthCompleted {
extension_name: req.extension_name.clone(),
success: true,
message: result.message.clone(),
});
if result.verification.is_some() {
state.sse.broadcast(SseEvent::AuthRequired {
extension_name: req.extension_name.clone(),
instructions: Some(result.message),
auth_url: None,
setup_url: None,
});
} else {
clear_auth_mode(&state).await;

state.sse.broadcast(SseEvent::AuthCompleted {
Comment on lines 265 to +286
if let Some(ref ext_mgr) = state.extension_manager {
match ext_mgr.configure_token(&extension_name, &token).await {
Ok(result) => {
crate::channels::web::server::clear_auth_mode(state).await;
state
.sse
.broadcast(crate::channels::web::types::SseEvent::AuthCompleted {
extension_name,
success: true,
message: result.message,
});
if result.verification.is_some() {
state.sse.broadcast(
crate::channels::web::types::SseEvent::AuthRequired {
extension_name: extension_name.clone(),
instructions: Some(result.message),
auth_url: None,
setup_url: None,
},
);
} else {
crate::channels::web::server::clear_auth_mode(state).await;
state.sse.broadcast(
crate::channels::web::types::SseEvent::AuthCompleted {
extension_name,
success: true,
message: result.message,
},
);
}
Comment on lines +639 to +651
let delete_webhook_url = format!("https://api.telegram.org/bot{bot_token}/deleteWebhook");
let delete_webhook_resp = client
.post(&delete_webhook_url)
.query(&[("drop_pending_updates", "true")])
.send()
.await
.map_err(|e| telegram_request_error("deleteWebhook", &e))?;
if !delete_webhook_resp.status().is_success() {
return Err(ExtensionError::Other(format!(
"Telegram deleteWebhook failed (HTTP {})",
delete_webhook_resp.status()
)));
}
@ironclaw-ci ironclaw-ci bot mentioned this pull request Mar 17, 2026
bkutasi pushed a commit to bkutasi/ironclaw that referenced this pull request Mar 28, 2026
* feat(telegram): verify owner during hot activation

* fix(ci): satisfy no-panics and clippy checks

* fix(web): preserve relay activation status

* fix(telegram): redact setup errors

* fix(telegram): require owner verification code

* fix(telegram): allow code in conversational dm
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: medium Business logic, config, or moderate-risk modules scope: channel/wasm WASM channel runtime scope: channel/web Web gateway channel scope: docs Documentation scope: extensions Extension management size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants