Skip to content

feat(gateway): unblock Twitter/X event ingestion#125

Merged
yordis merged 3 commits intomainfrom
yordis/feat-twitter-webhook
Apr 17, 2026
Merged

feat(gateway): unblock Twitter/X event ingestion#125
yordis merged 3 commits intomainfrom
yordis/feat-twitter-webhook

Conversation

@yordis
Copy link
Copy Markdown
Member

@yordis yordis commented Apr 16, 2026

  • Unblock Twitter/X activity from entering the same gateway path the rest of our inbound sources already use.
  • Keep gateway growth predictable by giving X its own source boundary instead of pushing provider-specific behavior into shared ingress code.

@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 16, 2026

PR Summary

Medium Risk
Adds a new externally-facing webhook endpoint with HMAC signature validation and new JetStream subjects/streams; main risk is incorrect verification/routing or publishing behavior impacting ingestion and NATS load.

Overview
Adds first-class Twitter/X webhook ingestion to trogon-gateway, including new config/env/TOML wiring, stream provisioning, and mounting a /twitter/webhook route when TROGON_SOURCE_TWITTER_CONSUMER_SECRET is set.

Introduces a new trogon-source-twitter crate that handles X’s CRC challenge (GET) and signed deliveries (POST via x-twitter-webhooks-signature), classifies payloads into NATS subjects, publishes verified events to JetStream (with an unroutable fallback and diagnostic headers), and updates docs/examples/tests accordingly.

Reviewed by Cursor Bugbot for commit be68155. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

Warning

Rate limit exceeded

@yordis has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 50 minutes and 29 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 50 minutes and 29 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ed46c69f-f642-482a-b1df-52fcc3d26e46

📥 Commits

Reviewing files that changed from the base of the PR and between add74f7 and be68155.

📒 Files selected for processing (1)
  • rsworkspace/crates/trogon-source-twitter/src/server.rs

Walkthrough

This PR introduces Twitter/X webhook source support to the Trogon system. It creates a new trogon-source-twitter crate with signature verification and JetStream publishing, integrates it into the gateway via configuration resolution, HTTP routing, and stream provisioning, and updates environment and gateway documentation with webhook setup instructions.

Changes

Cohort / File(s) Summary
Environment & Gateway Documentation
devops/docker/compose/.env.example, devops/docker/compose/services/trogon-gateway/README.md, rsworkspace/crates/trogon-gateway/README.md
Added Twitter/X webhook configuration environment variables and detailed webhook routing documentation including CRC challenge handling and signature verification on GET and POST endpoints.
Workspace Dependency Configuration
rsworkspace/Cargo.toml, rsworkspace/crates/trogon-gateway/Cargo.toml
Added trogon-source-twitter workspace dependency references to enable gateway integration.
Gateway Configuration Integration
rsworkspace/crates/trogon-gateway/src/config.rs
Added TwitterConfig struct to ResolvedConfig, implemented resolve_twitter with consumer secret validation, NATS token validation, and timeout/stream-age conversion; extended unit tests for Twitter-specific validation scenarios.
Gateway HTTP & Stream Provisioning
rsworkspace/crates/trogon-gateway/src/http.rs, rsworkspace/crates/trogon-gateway/src/streams.rs
Conditionally mount /twitter sub-router when Twitter config is present; provision Twitter JetStream stream alongside other sources; updated test fixtures and stream count assertions.
New Twitter Source Crate — Configuration & Constants
rsworkspace/crates/trogon-source-twitter/Cargo.toml, rsworkspace/crates/trogon-source-twitter/src/config.rs, rsworkspace/crates/trogon-source-twitter/src/constants.rs, rsworkspace/crates/trogon-source-twitter/src/lib.rs
Created new crate manifest with workspace and crypto dependencies; defined TwitterConsumerSecret wrapper with debug redaction, TwitterConfig struct aggregating JetStream parameters, HTTP/header constants, and public API surface.
Twitter Webhook Server & Signature Verification
rsworkspace/crates/trogon-source-twitter/src/server.rs, rsworkspace/crates/trogon-source-twitter/src/signature.rs
Implemented Axum HTTP endpoints for CRC validation (GET /webhook) and signed webhook delivery (POST /webhook) with HMAC-SHA256 verification, JSON classification, JetStream publishing, and comprehensive error handling and test coverage.

Sequence Diagram(s)

sequenceDiagram
    participant Client as HTTP Client
    participant Gateway as Trogon Gateway<br/>/twitter/webhook
    participant Sig as Signature Module
    participant Parser as JSON Parser
    participant JS as JetStream
    
    Client->>Gateway: POST /webhook with body<br/>(x-twitter-webhooks-signature header)
    Gateway->>Sig: verify(consumer_secret, body, header)
    alt Signature Valid
        Sig-->>Gateway: Ok(())
        Gateway->>Parser: parse body & classify<br/>event_type, payload_kind
        alt Classification Succeeds
            Parser-->>Gateway: DottedNatsToken, PayloadKind
            Gateway->>JS: publish(subject, body)<br/>with NATS headers
            JS-->>Gateway: publish result
            alt Publish Success
                Gateway-->>Client: 200 OK
            else Publish Fails
                Gateway-->>Client: 500 Internal Server Error
            end
        else Classification Fails
            Parser-->>Gateway: unroutable payload
            Gateway->>JS: publish(unroutable subject)<br/>with reject reason header
            JS-->>Gateway: publish result
            alt Unroutable Publish Success
                Gateway-->>Client: 200 OK
            else Unroutable Publish Fails
                Gateway-->>Client: 500 Internal Server Error
            end
        end
    else Signature Invalid
        Sig-->>Gateway: SignatureError
        Gateway-->>Client: 401 Unauthorized
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

Suggested labels

feature:webhook-source, crate:trogon-source-twitter, gateway:source-integration

Poem

🐰 A Twitter stream hops into place,
With signatures signed and verified grace,
JetStream channels dance in a row,
Let the webhooks flow, let the data go! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(gateway): unblock Twitter/X event ingestion' accurately summarizes the main change: adding Twitter/X webhook support as a new gateway source.
Description check ✅ Passed The description clearly relates to the changeset, explaining the goal of enabling Twitter/X event ingestion through the gateway with proper source boundaries.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch yordis/feat-twitter-webhook

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread rsworkspace/crates/trogon-gateway/src/config.rs
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rsworkspace/crates/trogon-gateway/README.md (1)

34-55: ⚠️ Potential issue | 🟡 Minor

Gateway README is missing Sentry rows.

The webhook routes table (Lines 34-41) and the source-enablement table (Lines 47-55) document Twitter/X but not Sentry, even though this PR also adds /sentry/webhook and TROGON_SOURCE_SENTRY_CLIENT_SECRET (see devops/docker/compose/services/trogon-gateway/README.md and http.rs). Please add Sentry rows for consistency. Same for incident.io/Notion if they were similarly omitted previously.

📝 Proposed addition
 | Twitter/X | `/twitter/webhook` |
 | GitLab | `/gitlab/webhook` |
 | Linear | `/linear/webhook` |
+| Sentry | `/sentry/webhook` |
 | Twitter/X | `TROGON_SOURCE_TWITTER_CONSUMER_SECRET` |
 | GitLab | `TROGON_SOURCE_GITLAB_WEBHOOK_SECRET` |
 | Linear | `TROGON_SOURCE_LINEAR_WEBHOOK_SECRET` |
+| Sentry | `TROGON_SOURCE_SENTRY_CLIENT_SECRET` |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-gateway/README.md` around lines 34 - 55, Update the
two README tables to include Sentry: add a row in the webhook routes table
mapping Sentry to `/sentry/webhook` and add a row in the source enablement table
with Sentry and the env var `TROGON_SOURCE_SENTRY_CLIENT_SECRET`; also check and
add similar rows for incident.io and Notion if those sources were added
elsewhere (refer to the new route `/sentry/webhook` and the env var symbol
`TROGON_SOURCE_SENTRY_CLIENT_SECRET` and any incident.io/Notion env var names
present in the repo) so the documentation matches the code changes.
🧹 Nitpick comments (6)
rsworkspace/crates/trogon-source-twitter/Cargo.toml (1)

18-18: Consider dropping tokio "full" in normal deps.

Sibling source crates (e.g. sentry) don't enable tokio in [dependencies] at all — the runtime comes from the gateway binary. features = ["full"] pulls in a lot of unused surface (signal, process, fs, net...) into a library crate. If #[tokio::main] or specific runtime primitives aren't actually used in the library code, remove it; otherwise scope it to the minimum required features (e.g. sync, time).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-source-twitter/Cargo.toml` at line 18, The
Cargo.toml currently enables tokio with features = ["full"] which pulls
unnecessary runtime components into this library crate; update the dependency
for the trogon-source-twitter crate to only request the minimal tokio features
actually used (or remove tokio from [dependencies] entirely if the runtime is
provided by the gateway binary), e.g., replace features = ["full"] with the
specific features such as ["sync","time"] or remove the dependency from
Cargo.toml and rely on the application binary to supply the runtime; check
usages in this crate for #[tokio::main], tokio::spawn, time or sync primitives
to determine the exact features required.
rsworkspace/crates/trogon-source-twitter/src/config.rs (1)

26-32: Consider deriving Debug on TwitterConfig (same for SentryConfig).

TwitterConsumerSecret's redacting Debug means a derived Debug on TwitterConfig is safe and useful for diagnostics/tracing of the resolved gateway config. Without it, callers can't {:?}-log the config. Same applies to SentryConfig in rsworkspace/crates/trogon-source-sentry/src/config.rs.

Proposed change
+#[derive(Debug)]
 pub struct TwitterConfig {
     pub consumer_secret: TwitterConsumerSecret,
     pub subject_prefix: NatsToken,
     pub stream_name: NatsToken,
     pub stream_max_age: StreamMaxAge,
     pub nats_ack_timeout: NonZeroDuration,
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-source-twitter/src/config.rs` around lines 26 - 32,
Add a derived Debug impl for the TwitterConfig struct (and likewise for
SentryConfig) so callers can {:?}-log resolved gateway configs; since
TwitterConsumerSecret already provides a redacting Debug, simply update the
struct definitions (TwitterConfig and SentryConfig) to include #[derive(Debug)]
(or add Debug to their existing derive lists) to enable safe diagnostic printing
while preserving secret redaction.
rsworkspace/crates/trogon-gateway/src/config.rs (2)

1238-1265: Note the asymmetric validation for client_secret — consider a comment.

When status == "enabled" and client_secret is missing, a validation error is raised. When status is unset (default-enabled) and client_secret is missing, the source is silently disabled. This is tested (sentry_missing_client_secret_returns_none_when_status_unspecified vs sentry_enabled_without_client_secret_is_invalid) and matches the "opt-in with explicit validation" pattern, but the behavior is subtle. A short comment explaining the intent would help future readers avoid unifying the branches.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-gateway/src/config.rs` around lines 1238 - 1265,
The code intentionally treats a missing client_secret differently depending on
explicit status: when explicitly_enabled (computed from section.status) we
validate and push ConfigValidationError::missing for "sentry"/"client_secret",
but when status is unset we silently disable the source (after
resolve_source_status) — add a short clarifying comment near the
explicitly_enabled calculation and the client_secret match (referencing
explicitly_enabled, resolve_source_status("sentry", ...), section.client_secret
and SentryClientSecret::new) that explains this "opt-in with explicit
validation" behavior and references the corresponding tests
(sentry_missing_client_secret_returns_none_when_status_unspecified vs
sentry_enabled_without_client_secret_is_invalid) so future readers do not
collapse the branches.

1291-1309: Optional: reorder max-timeout check before NonZeroDuration::from_secs.

nats_ack_timeout is constructed first and then potentially discarded when the max-value check fails. Swapping the order avoids the throwaway binding and yields a cleaner flow:

♻️ Proposed reorder
+    if section.nats_ack_timeout_secs > SENTRY_MAX_ACK_TIMEOUT_SECS {
+        errors.push(ConfigValidationError::invalid(
+            "sentry",
+            "nats_ack_timeout_secs",
+            DurationTooLong::new(SENTRY_MAX_ACK_TIMEOUT_SECS),
+        ));
+        return None;
+    }
+
     let nats_ack_timeout = match NonZeroDuration::from_secs(section.nats_ack_timeout_secs) {
         Ok(duration) => duration,
         Err(error) => {
             errors.push(ConfigValidationError::invalid(
                 "sentry",
                 "nats_ack_timeout_secs",
                 error,
             ));
             return None;
         }
     };
-    if section.nats_ack_timeout_secs > SENTRY_MAX_ACK_TIMEOUT_SECS {
-        errors.push(ConfigValidationError::invalid(
-            "sentry",
-            "nats_ack_timeout_secs",
-            DurationTooLong::new(SENTRY_MAX_ACK_TIMEOUT_SECS),
-        ));
-        return None;
-    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-gateway/src/config.rs` around lines 1291 - 1309,
The current flow constructs nats_ack_timeout with NonZeroDuration::from_secs and
then rejects it if section.nats_ack_timeout_secs exceeds
SENTRY_MAX_ACK_TIMEOUT_SECS, creating a throwaway binding; instead, first check
if section.nats_ack_timeout_secs > SENTRY_MAX_ACK_TIMEOUT_SECS and, if so, push
ConfigValidationError::invalid (using
DurationTooLong::new(SENTRY_MAX_ACK_TIMEOUT_SECS)) and return None, then call
NonZeroDuration::from_secs to create nats_ack_timeout and handle its Err by
pushing ConfigValidationError::invalid and returning None.
rsworkspace/crates/trogon-source-sentry/src/server.rs (1)

186-199: Minor: log uses the pre-shadowed &str for resource, which is fine — just confirming intent.

Line 189 references resource while it still holds the trimmed &str value (from Line 164), and then it's shadowed with the SentryResource enum on Line 186's match binding. Works correctly, but the shadowing is easy to miss during later edits; consider renaming the enum binding to parsed_resource for clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-source-sentry/src/server.rs` around lines 186 -
199, The match currently shadows the original &str `resource` with the parsed
SentryResource; rename the enum binding to parsed_resource (e.g., let
parsed_resource = match resource.parse::<SentryResource>() { ... }) to avoid
subtle shadowing, update subsequent uses that expect the enum to use
parsed_resource (and keep the original `resource` string in the warn! call that
logs the raw value), and ensure any later code that relied on the previous
shadowed name now references parsed_resource or resource appropriately.
rsworkspace/crates/trogon-source-twitter/src/server.rs (1)

290-309: Consider: multi-event payloads fall back to account_activity, but the message id extraction iterates in declaration order.

When a single payload contains multiple known account-activity event types, the subject is account_activity (correct fallback), but extract_message_id on lines 322–330 uses find_map over ACCOUNT_ACTIVITY_EVENT_TYPES and picks the first matching array/object id. That id may not correspond to any specific semantics the consumer expects, and when arrays have >1 items no id is produced (by design on line 326). Whether this matters depends on how downstream dedup is supposed to behave for these rare composite payloads — worth documenting the chosen semantics or deliberately skipping NATS_MESSAGE_ID in the fallback case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-source-twitter/src/server.rs` around lines 290 -
309, The code falls back to the generic subject "account_activity" when multiple
ACCOUNT_ACTIVITY_EVENT_TYPES are present, but extract_message_id currently picks
an id by iterating ACCOUNT_ACTIVITY_EVENT_TYPES in declaration order which can
be misleading; update the logic in extract_message_id (the function that
find_maps over ACCOUNT_ACTIVITY_EVENT_TYPES and reads NATS_MESSAGE_ID) to detect
the multi-event fallback (i.e., when the earlier account_activity_types
collection has len() > 1 or when event_type == "account_activity") and return
None (skip setting NATS_MESSAGE_ID) for that case, or alternatively add explicit
documentation and a comment in extract_message_id explaining the chosen
semantics if you decide to keep the current behavior. Ensure you reference
ACCOUNT_ACTIVITY_EVENT_TYPES and the fallback "account_activity" subject when
making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rsworkspace/crates/trogon-source-sentry/src/server.rs`:
- Around line 169-176: The Sentry timestamp is read into the variable
`timestamp` via `header_value(&headers, HEADER_TIMESTAMP)` but never validated
for clock skew; add a configurable max-drift check (e.g.,
`timestamp_max_drift_secs` on your existing config) and reject requests whose
parsed `Sentry-Hook-Timestamp` differs from now by more than that threshold. In
`server.rs` where `HEADER_TIMESTAMP`, `header_value`, and `timestamp` are used,
parse the header into an integer/float (handle parse errors by returning
`StatusCode::BAD_REQUEST`), compute the absolute difference against the current
system time, and if it exceeds the configured max drift return
`StatusCode::BAD_REQUEST` (and log a warning) before proceeding to forward
headers (including `HEADER_REQUEST_ID` and NATS publishing).

---

Outside diff comments:
In `@rsworkspace/crates/trogon-gateway/README.md`:
- Around line 34-55: Update the two README tables to include Sentry: add a row
in the webhook routes table mapping Sentry to `/sentry/webhook` and add a row in
the source enablement table with Sentry and the env var
`TROGON_SOURCE_SENTRY_CLIENT_SECRET`; also check and add similar rows for
incident.io and Notion if those sources were added elsewhere (refer to the new
route `/sentry/webhook` and the env var symbol
`TROGON_SOURCE_SENTRY_CLIENT_SECRET` and any incident.io/Notion env var names
present in the repo) so the documentation matches the code changes.

---

Nitpick comments:
In `@rsworkspace/crates/trogon-gateway/src/config.rs`:
- Around line 1238-1265: The code intentionally treats a missing client_secret
differently depending on explicit status: when explicitly_enabled (computed from
section.status) we validate and push ConfigValidationError::missing for
"sentry"/"client_secret", but when status is unset we silently disable the
source (after resolve_source_status) — add a short clarifying comment near the
explicitly_enabled calculation and the client_secret match (referencing
explicitly_enabled, resolve_source_status("sentry", ...), section.client_secret
and SentryClientSecret::new) that explains this "opt-in with explicit
validation" behavior and references the corresponding tests
(sentry_missing_client_secret_returns_none_when_status_unspecified vs
sentry_enabled_without_client_secret_is_invalid) so future readers do not
collapse the branches.
- Around line 1291-1309: The current flow constructs nats_ack_timeout with
NonZeroDuration::from_secs and then rejects it if section.nats_ack_timeout_secs
exceeds SENTRY_MAX_ACK_TIMEOUT_SECS, creating a throwaway binding; instead,
first check if section.nats_ack_timeout_secs > SENTRY_MAX_ACK_TIMEOUT_SECS and,
if so, push ConfigValidationError::invalid (using
DurationTooLong::new(SENTRY_MAX_ACK_TIMEOUT_SECS)) and return None, then call
NonZeroDuration::from_secs to create nats_ack_timeout and handle its Err by
pushing ConfigValidationError::invalid and returning None.

In `@rsworkspace/crates/trogon-source-sentry/src/server.rs`:
- Around line 186-199: The match currently shadows the original &str `resource`
with the parsed SentryResource; rename the enum binding to parsed_resource
(e.g., let parsed_resource = match resource.parse::<SentryResource>() { ... })
to avoid subtle shadowing, update subsequent uses that expect the enum to use
parsed_resource (and keep the original `resource` string in the warn! call that
logs the raw value), and ensure any later code that relied on the previous
shadowed name now references parsed_resource or resource appropriately.

In `@rsworkspace/crates/trogon-source-twitter/Cargo.toml`:
- Line 18: The Cargo.toml currently enables tokio with features = ["full"] which
pulls unnecessary runtime components into this library crate; update the
dependency for the trogon-source-twitter crate to only request the minimal tokio
features actually used (or remove tokio from [dependencies] entirely if the
runtime is provided by the gateway binary), e.g., replace features = ["full"]
with the specific features such as ["sync","time"] or remove the dependency from
Cargo.toml and rely on the application binary to supply the runtime; check
usages in this crate for #[tokio::main], tokio::spawn, time or sync primitives
to determine the exact features required.

In `@rsworkspace/crates/trogon-source-twitter/src/config.rs`:
- Around line 26-32: Add a derived Debug impl for the TwitterConfig struct (and
likewise for SentryConfig) so callers can {:?}-log resolved gateway configs;
since TwitterConsumerSecret already provides a redacting Debug, simply update
the struct definitions (TwitterConfig and SentryConfig) to include
#[derive(Debug)] (or add Debug to their existing derive lists) to enable safe
diagnostic printing while preserving secret redaction.

In `@rsworkspace/crates/trogon-source-twitter/src/server.rs`:
- Around line 290-309: The code falls back to the generic subject
"account_activity" when multiple ACCOUNT_ACTIVITY_EVENT_TYPES are present, but
extract_message_id currently picks an id by iterating
ACCOUNT_ACTIVITY_EVENT_TYPES in declaration order which can be misleading;
update the logic in extract_message_id (the function that find_maps over
ACCOUNT_ACTIVITY_EVENT_TYPES and reads NATS_MESSAGE_ID) to detect the
multi-event fallback (i.e., when the earlier account_activity_types collection
has len() > 1 or when event_type == "account_activity") and return None (skip
setting NATS_MESSAGE_ID) for that case, or alternatively add explicit
documentation and a comment in extract_message_id explaining the chosen
semantics if you decide to keep the current behavior. Ensure you reference
ACCOUNT_ACTIVITY_EVENT_TYPES and the fallback "account_activity" subject when
making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 47cf8c07-5a2a-463a-9cbf-6c84267921aa

📥 Commits

Reviewing files that changed from the base of the PR and between 38090c0 and 622b406.

⛔ Files ignored due to path filters (1)
  • rsworkspace/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • devops/docker/compose/.env.example
  • devops/docker/compose/services/trogon-gateway/README.md
  • rsworkspace/Cargo.toml
  • rsworkspace/crates/trogon-gateway/Cargo.toml
  • rsworkspace/crates/trogon-gateway/README.md
  • rsworkspace/crates/trogon-gateway/src/config.rs
  • rsworkspace/crates/trogon-gateway/src/http.rs
  • rsworkspace/crates/trogon-gateway/src/streams.rs
  • rsworkspace/crates/trogon-source-sentry/Cargo.toml
  • rsworkspace/crates/trogon-source-sentry/src/config.rs
  • rsworkspace/crates/trogon-source-sentry/src/constants.rs
  • rsworkspace/crates/trogon-source-sentry/src/lib.rs
  • rsworkspace/crates/trogon-source-sentry/src/sentry_client_secret.rs
  • rsworkspace/crates/trogon-source-sentry/src/server.rs
  • rsworkspace/crates/trogon-source-sentry/src/signature.rs
  • rsworkspace/crates/trogon-source-twitter/Cargo.toml
  • rsworkspace/crates/trogon-source-twitter/src/config.rs
  • rsworkspace/crates/trogon-source-twitter/src/constants.rs
  • rsworkspace/crates/trogon-source-twitter/src/lib.rs
  • rsworkspace/crates/trogon-source-twitter/src/server.rs
  • rsworkspace/crates/trogon-source-twitter/src/signature.rs

Comment thread rsworkspace/crates/trogon-source-sentry/src/server.rs
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
@yordis yordis force-pushed the yordis/feat-twitter-webhook branch from 622b406 to 057bbc9 Compare April 16, 2026 23:30
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 16, 2026

badge

Code Coverage Summary

Details
Filename                                                                      Stmts    Miss  Cover    Missing
--------------------------------------------------------------------------  -------  ------  -------  ---------------------------------------------------------------------------------------------
crates/acp-nats/src/session_id.rs                                                72       0  100.00%
crates/acp-nats/src/lib.rs                                                       73       0  100.00%
crates/acp-nats/src/error.rs                                                     84       0  100.00%
crates/acp-nats/src/client_proxy.rs                                             200       0  100.00%
crates/acp-nats/src/pending_prompt_waiters.rs                                   141       0  100.00%
crates/acp-nats/src/req_id.rs                                                    39       0  100.00%
crates/acp-nats/src/acp_prefix.rs                                                51       0  100.00%
crates/acp-nats/src/jsonrpc.rs                                                    6       0  100.00%
crates/acp-nats/src/ext_method_name.rs                                           70       0  100.00%
crates/acp-nats/src/config.rs                                                   204       0  100.00%
crates/acp-nats/src/in_flight_slot_guard.rs                                      32       0  100.00%
crates/acp-nats/src/agent/prompt.rs                                             633       0  100.00%
crates/acp-nats/src/agent/fork_session.rs                                       106       0  100.00%
crates/acp-nats/src/agent/initialize.rs                                          83       0  100.00%
crates/acp-nats/src/agent/load_session.rs                                       101       0  100.00%
crates/acp-nats/src/agent/bridge.rs                                             123       4  96.75%   109-112
crates/acp-nats/src/agent/mod.rs                                                 65       0  100.00%
crates/acp-nats/src/agent/new_session.rs                                         91       0  100.00%
crates/acp-nats/src/agent/logout.rs                                              49       0  100.00%
crates/acp-nats/src/agent/authenticate.rs                                        52       0  100.00%
crates/acp-nats/src/agent/cancel.rs                                             105       0  100.00%
crates/acp-nats/src/agent/ext_notification.rs                                    88       0  100.00%
crates/acp-nats/src/agent/list_sessions.rs                                       50       0  100.00%
crates/acp-nats/src/agent/js_request.rs                                         304       0  100.00%
crates/acp-nats/src/agent/close_session.rs                                       67       0  100.00%
crates/acp-nats/src/agent/set_session_config_option.rs                           71       0  100.00%
crates/acp-nats/src/agent/set_session_model.rs                                   71       0  100.00%
crates/acp-nats/src/agent/resume_session.rs                                     102       0  100.00%
crates/acp-nats/src/agent/ext_method.rs                                          92       0  100.00%
crates/acp-nats/src/agent/test_support.rs                                       299       0  100.00%
crates/acp-nats/src/agent/set_session_mode.rs                                    71       0  100.00%
crates/acp-nats/src/nats/parsing.rs                                             285       1  99.65%   153
crates/acp-nats/src/nats/extensions.rs                                            3       0  100.00%
crates/acp-nats/src/nats/mod.rs                                                  23       0  100.00%
crates/acp-nats/src/nats/subjects/mod.rs                                        380       0  100.00%
crates/acp-nats/src/nats/subjects/stream.rs                                      58       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_client.rs                    18       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_session.rs                   11       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/prompt_wildcard.rs               11       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/global_all.rs                    11       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_session.rs                   18       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_agent.rs                     18       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_agent_ext.rs                 11       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_agent.rs                     11       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_client.rs                    11       0  100.00%
crates/trogon-gateway/src/http.rs                                               132       0  100.00%
crates/trogon-gateway/src/main.rs                                                 4       0  100.00%
crates/trogon-gateway/src/source_status.rs                                       32       0  100.00%
crates/trogon-gateway/src/config.rs                                            1682       0  100.00%
crates/trogon-gateway/src/streams.rs                                            148       0  100.00%
crates/trogon-source-github/src/config.rs                                        17       0  100.00%
crates/trogon-source-github/src/signature.rs                                     64       0  100.00%
crates/trogon-source-github/src/server.rs                                       351       0  100.00%
crates/trogon-source-gitlab/src/config.rs                                        17       0  100.00%
crates/trogon-source-gitlab/src/signature.rs                                     30       0  100.00%
crates/trogon-source-gitlab/src/server.rs                                       431       0  100.00%
crates/trogon-std/src/dirs/fixed.rs                                              84       0  100.00%
crates/trogon-std/src/dirs/system.rs                                             76       0  100.00%
crates/trogon-std/src/fs/mem.rs                                                 220      10  95.45%   61-63, 77-79, 133-135, 158
crates/trogon-std/src/fs/system.rs                                              102       0  100.00%
crates/acp-nats/src/client/session_update.rs                                     55       0  100.00%
crates/acp-nats/src/client/mod.rs                                              2987       0  100.00%
crates/acp-nats/src/client/rpc_reply.rs                                          71       0  100.00%
crates/acp-nats/src/client/terminal_create.rs                                   294       0  100.00%
crates/acp-nats/src/client/ext.rs                                               365       8  97.81%   193-204, 229-240
crates/acp-nats/src/client/fs_write_text_file.rs                                451       0  100.00%
crates/acp-nats/src/client/request_permission.rs                                338       0  100.00%
crates/acp-nats/src/client/fs_read_text_file.rs                                 384       0  100.00%
crates/acp-nats/src/client/ext_session_prompt_response.rs                       157       0  100.00%
crates/acp-nats/src/client/terminal_kill.rs                                     309       0  100.00%
crates/acp-nats/src/client/terminal_release.rs                                  357       0  100.00%
crates/acp-nats/src/client/terminal_output.rs                                   223       0  100.00%
crates/acp-nats/src/client/terminal_wait_for_exit.rs                            396       0  100.00%
crates/acp-nats-stdio/src/config.rs                                              72       0  100.00%
crates/acp-nats-stdio/src/main.rs                                               141      27  80.85%   64, 116-123, 129-131, 148, 179-200
crates/trogon-std/src/json.rs                                                    30       0  100.00%
crates/trogon-std/src/http.rs                                                    19       0  100.00%
crates/trogon-std/src/secret_string.rs                                           35       0  100.00%
crates/trogon-std/src/duration.rs                                                45       0  100.00%
crates/trogon-std/src/args.rs                                                    10       0  100.00%
crates/trogon-nats/src/jetstream/traits.rs                                       43      43  0.00%    140-208
crates/trogon-nats/src/jetstream/claim_check.rs                                 372       0  100.00%
crates/trogon-nats/src/jetstream/publish.rs                                      64       0  100.00%
crates/trogon-nats/src/jetstream/mocks.rs                                       740      44  94.05%   368-382, 388-396, 411-422, 436-446, 514-516
crates/trogon-nats/src/jetstream/stream_max_age.rs                               18       0  100.00%
crates/acp-nats/src/nats/subjects/responses/update.rs                            27       0  100.00%
crates/acp-nats/src/nats/subjects/responses/prompt_response.rs                   27       0  100.00%
crates/acp-nats/src/nats/subjects/responses/response.rs                          20       0  100.00%
crates/acp-nats/src/nats/subjects/responses/cancelled.rs                         18       0  100.00%
crates/acp-nats/src/nats/subjects/responses/ext_ready.rs                         15       0  100.00%
crates/trogon-std/src/env/system.rs                                              17       0  100.00%
crates/trogon-std/src/env/in_memory.rs                                           81       0  100.00%
crates/acp-nats/src/nats/subjects/global/session_new.rs                           8       0  100.00%
crates/acp-nats/src/nats/subjects/global/logout.rs                                8       0  100.00%
crates/acp-nats/src/nats/subjects/global/initialize.rs                            8       0  100.00%
crates/acp-nats/src/nats/subjects/global/ext_notify.rs                           12       0  100.00%
crates/acp-nats/src/nats/subjects/global/authenticate.rs                          8       0  100.00%
crates/acp-nats/src/nats/subjects/global/ext.rs                                  12       0  100.00%
crates/acp-nats/src/nats/subjects/global/session_list.rs                          8       0  100.00%
crates/trogon-nats/src/lease/provision.rs                                       210      10  95.24%   84-94
crates/trogon-nats/src/lease/nats_kv_lease_config.rs                             30       0  100.00%
crates/trogon-nats/src/lease/lease_bucket.rs                                     19       0  100.00%
crates/trogon-nats/src/lease/mod.rs                                             603      13  97.84%   182-195
crates/trogon-nats/src/lease/lease_config_error.rs                               13       0  100.00%
crates/trogon-nats/src/lease/lease_timing.rs                                     21       0  100.00%
crates/trogon-nats/src/lease/release.rs                                           5       5  0.00%    8-12
crates/trogon-nats/src/lease/acquire.rs                                           8       8  0.00%    9-18
crates/trogon-nats/src/lease/ttl.rs                                              76       0  100.00%
crates/trogon-nats/src/lease/lease_key.rs                                        19       0  100.00%
crates/trogon-nats/src/lease/renew.rs                                           263      19  92.78%   21-27, 46-57
crates/trogon-nats/src/lease/renew_interval.rs                                   61       0  100.00%
crates/trogon-std/src/telemetry/http.rs                                         239       0  100.00%
crates/acp-nats-agent/src/connection.rs                                        1434       1  99.93%   686
crates/acp-nats/src/nats/subjects/client_ops/fs_write_text_file.rs               15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/session_request_permission.rs       15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_create.rs                  15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_release.rs                 15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_wait_for_exit.rs           15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_output.rs                  15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_kill.rs                    15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/fs_read_text_file.rs                15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/session_update.rs                   15       0  100.00%
crates/trogon-source-incidentio/src/config.rs                                    16       0  100.00%
crates/trogon-source-incidentio/src/incidentio_event_type.rs                     67       0  100.00%
crates/trogon-source-incidentio/src/incidentio_signing_secret.rs                 67       0  100.00%
crates/trogon-source-incidentio/src/server.rs                                   365       0  100.00%
crates/trogon-source-incidentio/src/signature.rs                                455       0  100.00%
crates/acp-nats-ws/src/upgrade.rs                                                57       2  96.49%   59, 90
crates/acp-nats-ws/src/main.rs                                                  189      18  90.48%   89, 209-230, 308
crates/acp-nats-ws/src/config.rs                                                 83       0  100.00%
crates/acp-nats-ws/src/connection.rs                                            166      35  78.92%   75-82, 87-98, 114, 116-117, 122, 133-135, 142, 146, 150, 153-161, 172, 176, 179, 182-186, 220
crates/acp-nats/src/telemetry/metrics.rs                                         65       0  100.00%
crates/acp-telemetry/src/signal.rs                                                7       1  85.71%   43
crates/acp-telemetry/src/service_name.rs                                         49       0  100.00%
crates/acp-telemetry/src/log.rs                                                  71       1  98.59%   43
crates/acp-telemetry/src/lib.rs                                                 169      32  81.07%   28-34, 56-63, 98, 103, 108, 122-137, 174, 177, 180, 186
crates/acp-telemetry/src/trace.rs                                                32       3  90.62%   23-24, 32
crates/acp-telemetry/src/metric.rs                                               35       3  91.43%   30-31, 39
crates/trogon-source-slack/src/config.rs                                         17       0  100.00%
crates/trogon-source-slack/src/signature.rs                                      80       0  100.00%
crates/trogon-source-slack/src/server.rs                                        954       0  100.00%
crates/trogon-nats/src/telemetry/messaging.rs                                   102       0  100.00%
crates/trogon-service-config/src/lib.rs                                          98       0  100.00%
crates/trogon-nats/src/mocks.rs                                                 304       0  100.00%
crates/trogon-nats/src/nats_token.rs                                            161       0  100.00%
crates/trogon-nats/src/client.rs                                                 25      25  0.00%    50-89
crates/trogon-nats/src/connect.rs                                               105      11  89.52%   22-24, 37, 49, 68-73
crates/trogon-nats/src/messaging.rs                                             598       2  99.67%   153, 163
crates/trogon-nats/src/token.rs                                                   8       0  100.00%
crates/trogon-nats/src/auth.rs                                                  119       0  100.00%
crates/trogon-source-discord/src/config.rs                                      109       0  100.00%
crates/trogon-source-discord/src/gateway.rs                                     491       1  99.80%   157
crates/trogon-source-notion/src/server.rs                                       351       8  97.72%   100-104, 137-138, 162-163
crates/trogon-source-notion/src/notion_event_type.rs                             48       3  93.75%   49-51
crates/trogon-source-notion/src/notion_verification_token.rs                     17       0  100.00%
crates/trogon-source-notion/src/signature.rs                                     63       1  98.41%   32
crates/trogon-source-telegram/src/config.rs                                      17       0  100.00%
crates/trogon-source-telegram/src/server.rs                                     387       0  100.00%
crates/trogon-source-telegram/src/signature.rs                                   38       0  100.00%
crates/acp-nats/src/jetstream/ext_policy.rs                                      26       0  100.00%
crates/acp-nats/src/jetstream/consumers.rs                                       99       0  100.00%
crates/acp-nats/src/jetstream/provision.rs                                       61       0  100.00%
crates/acp-nats/src/jetstream/streams.rs                                        194       4  97.94%   254-256, 266
crates/trogon-std/src/time/mock.rs                                              129       0  100.00%
crates/trogon-std/src/time/system.rs                                             35       0  100.00%
crates/trogon-source-twitter/src/server.rs                                      567       0  100.00%
crates/trogon-source-twitter/src/signature.rs                                    77       0  100.00%
crates/trogon-source-twitter/src/config.rs                                       17       0  100.00%
crates/trogon-source-linear/src/server.rs                                       392       0  100.00%
crates/trogon-source-linear/src/signature.rs                                     54       1  98.15%   16
crates/trogon-source-linear/src/config.rs                                        17       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_model.rs                          18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/fork.rs                               18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/close.rs                              18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/resume.rs                             18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/prompt.rs                             18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/cancel.rs                             18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_mode.rs                           18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_config_option.rs                  18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/load.rs                               18       0  100.00%
crates/trogon-source-sentry/src/sentry_client_secret.rs                          17       0  100.00%
crates/trogon-source-sentry/src/server.rs                                       359       0  100.00%
crates/trogon-source-sentry/src/signature.rs                                     56       0  100.00%
TOTAL                                                                         27482     344  98.75%

Diff against main

Filename                                         Stmts    Miss  Cover
---------------------------------------------  -------  ------  --------
crates/trogon-gateway/src/http.rs                   +9       0  +100.00%
crates/trogon-gateway/src/config.rs               +121       0  +100.00%
crates/trogon-gateway/src/streams.rs               +10       0  +100.00%
crates/trogon-source-twitter/src/server.rs        +567       0  +100.00%
crates/trogon-source-twitter/src/signature.rs      +77       0  +100.00%
crates/trogon-source-twitter/src/config.rs         +17       0  +100.00%
TOTAL                                             +801       0  +0.04%

Results for commit: be68155

Minimum allowed coverage is 95%

♻️ This comment has been updated with latest results

Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit add74f7. Configure here.

Comment thread devops/docker/compose/.env.example
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
rsworkspace/crates/trogon-source-twitter/src/server.rs (2)

269-284: Classification precedence is data.event_typematching_rules → account-activity keys — worth a brief comment.

Today Twitter's filtered-stream payloads don't carry data.event_type, so this ordering is safe, but the precedence is load-bearing and easy to break silently when new payload shapes are added. A one-line comment documenting the intentional order (and the assumption that X Activity and filtered-stream shapes are disjoint on data.event_type) would reduce the odds of a future regression.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-source-twitter/src/server.rs` around lines 269 -
284, Add a one-line comment above the classification block that checks
payload.get("data").and_then(... event_type) and the subsequent
payload.get("matching_rules") branch to document the intentional precedence:
"classification precedence is data.event_type → matching_rules →
account-activity keys" and note the assumption that X Activity and
filtered-stream shapes are disjoint on data.event_type (so filtered-stream
payloads lack data.event_type). Place the comment near the
DottedNatsToken::new(...) / PayloadKind::XActivity and the matching_rules check
to make the reasoning for choosing DottedNatsToken and PayloadKind explicit for
future maintainers.

198-204: Missing-signature-header path is uncovered by tests.

invalid_signature_returns_unauthorized exercises the signature::verify failure path, but the earlier headers.get(HEADER_SIGNATURE) absent/non-UTF8 branch has no direct test. A small test posting webhook_request(body, None) asserting 401 UNAUTHORIZED would lock in the behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-source-twitter/src/server.rs` around lines 198 -
204, Add a unit/integration test that posts a webhook with no signature header
to exercise the branch where headers.get(HEADER_SIGNATURE) returns None: create
a test similar to invalid_signature_returns_unauthorized but call
webhook_request(body, None) (or otherwise omit the HEADER_SIGNATURE) and assert
the handler returns StatusCode::UNAUTHORIZED; place the test near the existing
invalid_signature_returns_unauthorized test so it references the same setup and
verifies the missing-signature path covered by the signature extraction in
server.rs.
rsworkspace/crates/trogon-source-twitter/src/signature.rs (1)

37-62: Consider accepting &TwitterConsumerSecret rather than &str.

Both crc_response_token and verify are public and currently re-admit a primitive at the boundary (consumer_secret: &str), which means callers outside server.rs can bypass the TwitterConsumerSecret invariant. Taking &TwitterConsumerSecret (and internally calling .as_str()/.as_bytes()) keeps the domain guarantee intact at the module boundary.

As per coding guidelines: "Prefer domain-specific value objects over primitives (e.g., AcpPrefix not String). Each type's factory must guarantee correctness at construction—invalid instances should be unrepresentable."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-source-twitter/src/signature.rs` around lines 37 -
62, Change the public APIs crc_response_token and verify to accept
&TwitterConsumerSecret instead of &str so the consumer-secret invariant is
preserved at the module boundary; update the function signatures
(crc_response_token(consumer_secret: &TwitterConsumerSecret, ...) and
verify(consumer_secret: &TwitterConsumerSecret, ...)), use
consumer_secret.as_bytes() / consumer_secret.as_str() where you currently call
.as_bytes(), and update all callers/tests to pass &TwitterConsumerSecret (and
import the type) so construction always enforces the domain invariant.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rsworkspace/crates/trogon-source-twitter/src/server.rs`:
- Around line 300-332: The extracted message id can come from a different
account-activity sub-event than the routed subject; update extract_message_id to
avoid cross-bucket collisions by namespacing the dedup key with the routed event
type (e.g. return format!("{event_type}:{id}")), or else return None for the
generic "account_activity" bucket; concretely, change extract_message_id to
accept the resolved event_type (the same value passed into DottedNatsToken::new
/ the logic around account_activity_types / PayloadKind::AccountActivity) and
when it finds an id from ACCOUNT_ACTIVITY_EVENT_TYPES, prefix it with the routed
event_type (or skip returning any id if event_type == "account_activity"), so
Nats-Msg-Id is either namespaced or omitted when using the generic bucket.

---

Nitpick comments:
In `@rsworkspace/crates/trogon-source-twitter/src/server.rs`:
- Around line 269-284: Add a one-line comment above the classification block
that checks payload.get("data").and_then(... event_type) and the subsequent
payload.get("matching_rules") branch to document the intentional precedence:
"classification precedence is data.event_type → matching_rules →
account-activity keys" and note the assumption that X Activity and
filtered-stream shapes are disjoint on data.event_type (so filtered-stream
payloads lack data.event_type). Place the comment near the
DottedNatsToken::new(...) / PayloadKind::XActivity and the matching_rules check
to make the reasoning for choosing DottedNatsToken and PayloadKind explicit for
future maintainers.
- Around line 198-204: Add a unit/integration test that posts a webhook with no
signature header to exercise the branch where headers.get(HEADER_SIGNATURE)
returns None: create a test similar to invalid_signature_returns_unauthorized
but call webhook_request(body, None) (or otherwise omit the HEADER_SIGNATURE)
and assert the handler returns StatusCode::UNAUTHORIZED; place the test near the
existing invalid_signature_returns_unauthorized test so it references the same
setup and verifies the missing-signature path covered by the signature
extraction in server.rs.

In `@rsworkspace/crates/trogon-source-twitter/src/signature.rs`:
- Around line 37-62: Change the public APIs crc_response_token and verify to
accept &TwitterConsumerSecret instead of &str so the consumer-secret invariant
is preserved at the module boundary; update the function signatures
(crc_response_token(consumer_secret: &TwitterConsumerSecret, ...) and
verify(consumer_secret: &TwitterConsumerSecret, ...)), use
consumer_secret.as_bytes() / consumer_secret.as_str() where you currently call
.as_bytes(), and update all callers/tests to pass &TwitterConsumerSecret (and
import the type) so construction always enforces the domain invariant.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 69808b43-4b84-4860-8bb9-102d477c7ef3

📥 Commits

Reviewing files that changed from the base of the PR and between 622b406 and add74f7.

⛔ Files ignored due to path filters (1)
  • rsworkspace/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • devops/docker/compose/.env.example
  • devops/docker/compose/services/trogon-gateway/README.md
  • rsworkspace/Cargo.toml
  • rsworkspace/crates/trogon-gateway/Cargo.toml
  • rsworkspace/crates/trogon-gateway/README.md
  • rsworkspace/crates/trogon-gateway/src/config.rs
  • rsworkspace/crates/trogon-gateway/src/http.rs
  • rsworkspace/crates/trogon-gateway/src/streams.rs
  • rsworkspace/crates/trogon-source-twitter/Cargo.toml
  • rsworkspace/crates/trogon-source-twitter/src/config.rs
  • rsworkspace/crates/trogon-source-twitter/src/constants.rs
  • rsworkspace/crates/trogon-source-twitter/src/lib.rs
  • rsworkspace/crates/trogon-source-twitter/src/server.rs
  • rsworkspace/crates/trogon-source-twitter/src/signature.rs
✅ Files skipped from review due to trivial changes (8)
  • rsworkspace/Cargo.toml
  • rsworkspace/crates/trogon-gateway/Cargo.toml
  • devops/docker/compose/.env.example
  • rsworkspace/crates/trogon-gateway/README.md
  • rsworkspace/crates/trogon-source-twitter/Cargo.toml
  • rsworkspace/crates/trogon-source-twitter/src/lib.rs
  • rsworkspace/crates/trogon-source-twitter/src/config.rs
  • rsworkspace/crates/trogon-source-twitter/src/constants.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • rsworkspace/crates/trogon-gateway/src/http.rs
  • devops/docker/compose/services/trogon-gateway/README.md

Comment thread rsworkspace/crates/trogon-source-twitter/src/server.rs
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
@yordis yordis merged commit cb7673a into main Apr 17, 2026
7 checks passed
@yordis yordis deleted the yordis/feat-twitter-webhook branch April 17, 2026 01:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant