Skip to content

fix(admin): env filter and honest count on DLQ replay#80

Closed
ravidsrk wants to merge 2 commits into
dodopayments:mainfrom
ravidsrk:ravidsrk/cs-56-dlq-env-filter
Closed

fix(admin): env filter and honest count on DLQ replay#80
ravidsrk wants to merge 2 commits into
dodopayments:mainfrom
ravidsrk:ravidsrk/cs-56-dlq-env-filter

Conversation

@ravidsrk

Copy link
Copy Markdown

This PR adds an optional environment filter to the admin DLQ replay endpoint and makes the replayed count honest.

POST /admin/api/dlq/{job_id}/replay called dlq::replay(pool, id, None), matching the bare job id across environments. Because (environment_id, id) is the key, the same job id parked in two environments would replay both while the response reported replayed: 1. Not attacker-reachable (job ids are server-minted UUIDv7), but the count lied and there was no way to pin the replay.

Changes:

  • dlq::replay returns the moved-row count instead of a bool, so both the CLI and the endpoint report the real number.
  • The endpoint accepts ?environment=<slug>, mirroring the CLI's --env, resolved via dlq::environment_by_slug. An unknown slug is a 404.
  • OpenAPI annotations updated and the generated client types plus docs spec regenerated.
  • redteam_dlq_env_scope covers the new surface: a filtered replay moves only the pinned environment's row, an unfiltered replay reports replayed: 2 for a job id parked in two environments, and an unknown slug 404s.

Fixes #56

ravidsrk added 2 commits July 16, 2026 00:13
dlq::replay collapsed the moved-row count to a bool, hiding how many
rows an unscoped id match actually moved. Callers now see the count.
POST /admin/api/dlq/{job_id}/replay accepts an optional
?environment=<slug> filter mirroring the CLI's --env. The same job id
can be parked in more than one environment under the (environment_id,
id) key, and the unscoped handler replayed every copy while reporting
a hardcoded replayed: 1. Unfiltered replay keeps the documented
cross-environment reach but now reports the real moved count. Unknown
slugs 404 before any row moves.

Closes dodopayments#56
@ravidsrk

Copy link
Copy Markdown
Author

@claude review this PR. @codex review this PR.

@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes two related issues with the admin DLQ replay endpoint: the replayed count was always hardcoded to 1 even when a job id was parked across multiple environments, and there was no way to pin a replay to a specific environment.

  • dlq::replay now returns u64 (rows affected) instead of bool, making both the CLI and the HTTP endpoint report the true moved-row count.
  • POST /admin/api/dlq/{job_id}/replay accepts an optional ?environment=<slug> query parameter; an unknown slug returns 404 before touching any rows.
  • Three new integration tests cover the filtered path, the cross-environment unfiltered count, and the unknown-slug 404.

Confidence Score: 4/5

Safe to merge. The core replay logic is unchanged; the new query param and count fix are well-tested and the generated artifacts are in sync.

The u64 as i64 cast in the handler is technically lossy — a count above i64::MAX wraps to a negative replayed value in the response. The bound on rows affected is the number of environments a single job can be parked in, so this will never fire in practice, but the cast is still imprecise. The 404 OpenAPI example also only documents one of two distinct messages consumers might receive.

server/src/api/admin.rs — the as i64 cast and the single 404 example are the two spots worth a quick look.

Important Files Changed

Filename Overview
server/src/dlq.rs Return type of replay changed from bool to u64 (rows affected); logic unchanged. Clean and correct.
server/src/api/admin.rs Adds AdminReplayFilter query param and slug resolution; replayed count is now honest. Minor: u64 as i64 cast is lossy in theory; 404 example only covers one of two messages.
server/src/main.rs CLI call site updated to > 0 comparison following the bool → u64 return-type change.
server/tests/redteam_dlq_env_scope.rs Three new integration tests covering filtered replay, cross-env count accuracy, and unknown-slug 404 — solid coverage of the new surface.
docs/openapi/chimely.yaml Generated spec regenerated correctly with new environment query parameter and updated 404 description.
packages/client/src/generated/api.d.ts Generated TypeScript types updated in sync with the spec — environment query parameter added to adminReplayDeadLetter.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant C as Caller
    participant H as replay_dead_letter
    participant D as dlq::environment_by_slug
    participant R as dlq::replay

    C->>H: "POST /admin/api/dlq/{job_id}/replay?environment=slug"
    H->>H: parse_typeid(job_id)
    alt invalid job_id
        H-->>C: 404 no such parked job
    end
    alt environment param present
        H->>D: environment_by_slug(pool, slug)
        D-->>H: Some(uuid) or None
        alt slug not found
            H-->>C: 404 no such environment
        end
    end
    H->>R: replay(pool, id, env_uuid)
    R-->>H: rows_affected u64
    alt "rows == 0"
        H-->>C: 404 no such parked job
    else rows greater than 0
        H-->>C: 200 replayed N
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant C as Caller
    participant H as replay_dead_letter
    participant D as dlq::environment_by_slug
    participant R as dlq::replay

    C->>H: "POST /admin/api/dlq/{job_id}/replay?environment=slug"
    H->>H: parse_typeid(job_id)
    alt invalid job_id
        H-->>C: 404 no such parked job
    end
    alt environment param present
        H->>D: environment_by_slug(pool, slug)
        D-->>H: Some(uuid) or None
        alt slug not found
            H-->>C: 404 no such environment
        end
    end
    H->>R: replay(pool, id, env_uuid)
    R-->>H: rows_affected u64
    alt "rows == 0"
        H-->>C: 404 no such parked job
    else rows greater than 0
        H-->>C: 200 replayed N
    end
Loading

Fix All in Claude Code

Reviews (1): Last reviewed commit: "feat(admin): env filter on DLQ replay en..." | Re-trigger Greptile

Comment thread server/src/api/admin.rs
.await
.map_err(ApiError::from)?;
if !replayed {
.map_err(ApiError::from)? as i64;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Lossy u64 → i64 cast

rows_affected() returns u64, and the bare as i64 cast silently wraps to a negative value if the count ever exceeds i64::MAX. In DLQ replay that number is bounded by the number of environments a single job can be parked in, so it is practically zero risk — but a prefer-safe path would be u64::try_into().unwrap_or(i64::MAX) or simply changing AdminReplayResult.replayed to u64 (the OpenAPI schema renders it as an integer either way).

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

Comment thread server/src/api/admin.rs
Comment on lines 1296 to 1302
),
(
status = 404,
description = "No such parked job.",
description = "No such parked job, or no such environment.",
body = crate::api::contract::Error,
example = json!({"error": {"code": "not_found", "message": "no such parked job"}}),
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 404 example only covers one of two distinct cases

The utoipa example on the 404 response hard-codes "no such parked job", but this status is also returned when the environment slug is unknown (with "no such environment"). An API consumer looking at the generated docs has no way to distinguish the two. Adding a second named example (utoipa supports examples with keys) would let callers tell apart a bad slug from a missing job.

Fix in Claude Code

@ravidsrk

Copy link
Copy Markdown
Author

Closing as a duplicate of #76, which was opened first and covers the same issue (#56). Apologies for the noise — will follow up there if our independent implementation surfaced anything #76 doesn't cover.

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.

Admin HTTP DLQ replay lacks an environment filter

1 participant