Skip to content

feat(convex): webhook-driven sandbox state sync - #83

Merged
mislavivanda merged 4 commits into
mainfrom
feat/convex-webhooks
Oct 9, 2026
Merged

mislavivanda merged 4 commits into
mainfrom
feat/convex-webhooks

Conversation

@mislavivanda

@mislavivanda mislavivanda commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by cubic

Adds webhook-driven sandbox state sync so records update in real time when Daytona changes sandboxes on its own (auto-stop, auto-archive, auto-delete), instead of lagging until refreshSandbox is called. The feature is opt-in and off by default.

  • Routes Daytona's sandbox.state.updated deliveries to the component's webhook route (mounted via httpPrefix) and applies them to tracked sandbox records.
  • Verifies every delivery's HMAC signature using the Svix/Standard Webhooks scheme, rejects timestamps older than 5 minutes to block replays, fails closed when DAYTONA_WEBHOOK_SECRET isn't passed down, and returns 400 on signed-but-malformed payloads instead of crashing.
  • Dedupes duplicate and out-of-order deliveries, and discards late events, via a new remoteUpdatedAt timestamp — which API-observed writes also stamp, so a webhook can't overwrite newer state; events for untracked sandboxes and non-state events are acknowledged and ignored.
  • Setup: set DAYTONA_WEBHOOK_SECRET on your deployment, pass it down in the app config, and point a Daytona webhook endpoint (in the same organization as your DAYTONA_API_KEY) at https://<deployment>.convex.site/daytona/webhook.

Written for commit b545691. Summary will update on new commits.

View guided diff Turn on auto-fix

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
@mislavivanda
mislavivanda deployed to integration-tests October 2, 2026 16:20 — with GitHub Actions Active

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 12 files

This PR changes authentication, authorization, or input validation. Ultrareviews find 2.4x more serious bugs than standard reviews. Comment @cubic-dev-ai ultrareview to run one.

Fix all with cubic | Re-trigger cubic

Comment thread packages/convex/src/component/http.ts
Comment thread packages/convex/src/component/webhooks.ts
Comment thread packages/convex/scripts/live.mjs Outdated
…writes

- validate the parsed payload is an object and that id, newState and the
  timestamp are non-empty strings; signed-but-malformed deliveries get 400
  instead of crashing the action or failing argument validation
- every API-observed state write (upsertSandbox, setSandboxError) stamps
  remoteUpdatedAt, so a late webhook event older than state already
  pulled from the API is discarded rather than overwriting it
- live script asserts CONVEX_SITE_URL is present instead of failing
  with an opaque URL parse error

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 4 files (changes from recent commits).

Requires human review: Auto-approval blocked because this review re-detected 1 unresolved issue already reported by Cubic.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/convex/src/component/sandboxes.ts
@mislavivanda

Copy link
Copy Markdown
Collaborator Author

Verified end-to-end with real Daytona webhook deliveries (in addition to unit tests and the live suite):

  • Cloud Convex dev deployment with the component mounted at https://<deployment>.convex.site/daytona/webhook; endpoint created in the Daytona dashboard subscribed to sandbox.state.updated; its signing secret (whsec_…, shown on the endpoint's details page) set as DAYTONA_WEBHOOK_SECRET.
  • Created a sandbox through the component (record: started), then stopped it from the Daytona dashboard, with no component calls in between. The record flipped to stopped from the delivery alone, and remoteUpdatedAt advanced to Daytona's event updatedAt (2026-10-08T21:26:05.070Z), confirming signature verification against a real dashboard-issued secret and the ordering guard on real payloads.
  • Before the secret was set, the public route refused deliveries (fail-closed); afterwards, unsigned requests got 401.

Also confirmed along the way: webhook events are per-organization, so the endpoint must live in the same org as the API key the component uses (worth knowing when debugging "no events arrive").

…et is shown

Both confirmed during the real-delivery test: the endpoint must live in
the API key's organization, and the signing secret is on the endpoint's
details page in the Webhooks table.

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
@mislavivanda
mislavivanda deployed to integration-tests October 9, 2026 16:39 — with GitHub Actions Active
@mislavivanda
mislavivanda merged commit cb0d4e8 into main Oct 9, 2026
38 checks passed
@mislavivanda
mislavivanda deleted the feat/convex-webhooks branch October 9, 2026 16:44
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 9, 2026

This branch was successfully deployed

1 active deployment
integration-tests — b545691f Deployed Oct 9, 2026 by mislavivanda via convex #196
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant