Skip to content

agents: let trigger rotate_secret revoke the outgoing webhook secret (MARSOHS-1078) - #714

Merged
SSharma-10 merged 2 commits into
digitalocean:OHS_endpointsfrom
sdharavath:sdharavath/MARSOHS-1078/trigger-revoke-previous
Aug 26, 2026
Merged

agents: let trigger rotate_secret revoke the outgoing webhook secret (MARSOHS-1078)#714
SSharma-10 merged 2 commits into
digitalocean:OHS_endpointsfrom
sdharavath:sdharavath/MARSOHS-1078/trigger-revoke-previous

Conversation

@sdharavath

Copy link
Copy Markdown

Summary

triggers.rotate_secret() can only ask for the default rotation, and its docstring says the old secret "stays valid briefly" — which is exactly the unactionable wording MARSOHS-1078 was filed about. Someone responding to a leaked secret can neither kill the old value nor tell from the response whether it is already dead.

This adds revoke_previous to the sync and async clients. The default is unchanged.

# routine rotation: grace window, unchanged behaviour
resp = client.agents.triggers.rotate_secret("trig-abc123")
resp.previous_secret_expires_at   # when the old secret stops verifying

# breach response: retire the outgoing secret on this call
resp = client.agents.triggers.rotate_secret("trig-abc123", revoke_previous=True)
resp.previous_secret_revoked      # True; no expiry is reported

Why grace is still the default: the provider side holds one secret at a time (a GitHub webhook has a single Secret field), so it keeps signing with the old value until a human pastes the new one in. Revoking on every rotate would turn routine rotation into a delivery outage lasting as long as that handoff. revoke_previous=True accepts that gap deliberately, because a leaked secret is the worse risk.

The response now always states the outcome — exactly one of previous_secret_expires_at and previous_secret_revoked is present, so a caller never infers it from a missing field. The docstrings were rewritten to say so.

Notes for review

Related

Test plan

  • pytest tests/agents/ — 127 passed, 1 skipped (pre-existing)
  • New: test_rotate_secret_revoke_previous and test_async_rotate_secret_revoke_previous assert revoke_previous=true reaches the wire and the revoked flag parses
  • Existing test_rotate_secret extended to assert the default sends no revoke_previous and parses the expiry
  • black --check clean

Made with Cursor

…(MARSOHS-1078)

Rotation defaults to a grace window so in-flight deliveries keep verifying
while the new secret is pasted into the provider. That is right for routine
rotation and wrong for a leaked secret, where the point is to kill the old
value now. Add revoke_previous=True to opt into that on both the sync and
async clients.

Also replaces the "stays valid briefly" docstring, which is the vague wording
the ticket was filed about: the response now says which happened, carrying
either previous_secret_expires_at or previous_secret_revoked.

Co-authored-by: Cursor <cursoragent@cursor.com>
…all by URL

Co-authored-by: Cursor <cursoragent@cursor.com>
@sdharavath

Copy link
Copy Markdown
Author

@SSharma-10 review request when you have time — pydo half of MARSOHS-1078 (P1, webhook secret rotation).

Touches only the hand-authored triggers modules: rotate_secret gains a keyword-only revoke_previous=False, sync and async, with the docstring explaining that the response carries either previous_secret_expires_at or previous_secret_revoked and never both. Nothing generated changes.

I can't add reviewers on this repo, so mentioning instead.

@SSharma-10 SSharma-10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@SSharma-10
SSharma-10 merged commit f6ff34f into digitalocean:OHS_endpoints Aug 26, 2026
6 checks passed
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.

2 participants