Skip to content

fix: persist Daytona authentication failures - #519

Draft
Elioooon wants to merge 8 commits into
truefoundry:mainfrom
Elioooon:fix/daytona-auth-failure-status
Draft

Elioooon wants to merge 8 commits into
truefoundry:mainfrom
Elioooon:fix/daytona-auth-failure-status

Conversation

@Elioooon

@Elioooon Elioooon commented Aug 31, 2026

Copy link
Copy Markdown

Summary

Persist Daytona 401/403 failures as a failed sandbox-provider status instead of returning an internal settings error or losing the typed error when sandbox execution converts it to a failed result.

Closes #516

Changes

  • map Daytona authorization errors, including wrapped causes, to stable status_reason values
  • persist the failure from snapshot refresh and runtime sandbox create/exec paths
  • add focused coverage for settings refresh, sandbox creation, and existing-sandbox execution
  • add patch changesets for trueforge and trueforge-core

How was this tested?

  • pnpm --filter @truefoundry/trueforge-core test -- --runInBand tests/core/sandbox/daytonaSnapshotRegistration.test.ts
  • pnpm --filter @truefoundry/trueforge test -- --runInBand tests/unit/sandbox/providerUtils.test.ts
  • pnpm --filter @truefoundry/trueforge test:store:sqlite -- --runInBand tests/db/sqlite/sandbox-provider-store/contract.test.ts
  • pnpm --filter @truefoundry/trueforge-core typecheck
  • pnpm --filter @truefoundry/trueforge typecheck
  • pnpm exec eslint packages/trueforge-core/src/core/sandbox/provider/DaytonaProvider.ts packages/trueforge/src/runtime/sessionResources.ts packages/trueforge/src/sandbox/providerUtils.ts
  • git diff --check

Validation is intentionally scoped to the two changed packages and the Daytona failure paths; full workspace build/test and live Daytona credential revocation were not run locally. The callback is optional and database write failures are logged without replacing the original provider result. The SQLite store contract was run locally; the equivalent Postgres contract was not run because no local Postgres service was started.

Checklist

  • I have read the contributing guidelines
  • pnpm build, pnpm test, pnpm typecheck, pnpm lint:ci, and pnpm format:check pass locally
  • Tests added/updated where it makes sense
  • No hand-edits to generated code (packages/trueforge-sdk, .github/fern/openapi/openapi.json, docs/openapi.json) — fork PRs omit SDK regen; maintainers regenerate after merge
  • Docs / .env.example updated if configuration or behavior changed (not applicable: no configuration or documented API change)

Note

Medium Risk
Changes sandbox credential handling, in-memory caching, and conditional DB status updates on auth failures; incorrect optimistic locking could leave status stale, but scope is limited to Daytona failure paths.

Overview
When Daytona returns 401/403 (including wrapped causes), the sandbox provider row is updated to failed with a stable status_reason instead of surfacing opaque settings errors or losing the signal when exec paths return { success: false }.

Runtime wiring: DaytonaSandboxProvider accepts an optional onError hook invoked from create, exec, and file/preview operations. Session resolution passes recordDaytonaAccessFailure, which writes the failure only for auth/permission errors.

Cache safety: Process-wide sandbox caching keys now include a hash of tenant, API URL, and API key so rotated credentials do not reuse clients authenticated with the old key; failed operations evict the matching cache entry.

Concurrency: updateSandboxStatus can require matching expected_manifest and expected_status so stale snapshot refreshes do not overwrite a concurrent auth failure (Postgres and SQLite).

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

@changeset-bot

changeset-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e76785c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@truefoundry/trueforge Patch
@truefoundry/trueforge-core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment thread packages/trueforge/src/runtime/sessionResources.ts

@cursor cursor 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.

Stale Bugbot comment from a previous run.

@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch from 290e8b8 to a1379ae Compare August 31, 2026 11:13
@Elioooon

Copy link
Copy Markdown
Author

Rebased onto the latest main (6848d71) and force-pushed. Revalidation after the rebase passed: Daytona provider tests (6/6), SQLite sandbox provider contract (7/7), provider utility unit test (1/1), and both @truefoundry/trueforge-core and @truefoundry/trueforge typechecks. The Postgres contract remains the documented local-environment boundary.

@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch from a1379ae to f056581 Compare September 3, 2026 07:33

Elioooon commented Sep 3, 2026

Copy link
Copy Markdown
Author

Rebased onto current main (d269b01) and resolved the sessionResources.ts conflict by preserving the upstream ReasoningEffort integration alongside this PR's Daytona failure callback. Force-pushed as f056581.

Post-rebase validation:

  • pnpm --filter @truefoundry/trueforge-core test -- --runInBand tests/core/sandbox/daytonaSnapshotRegistration.test.ts — 6 passed
  • pnpm --filter @truefoundry/trueforge test -- --runInBand tests/unit/sandbox/providerUtils.test.ts — 1 passed
  • pnpm --filter @truefoundry/trueforge-core typecheck — passed
  • pnpm --filter @truefoundry/trueforge typecheck — passed, including DB/unit test projects
  • touched-file ESLint and Prettier checks — passed
  • git diff --check — passed

The SQLite sandbox-provider contract could not execute on this host because the better-sqlite3@13.0.3 prebuild requires GLIBC 2.29 while the runner has an older glibc. Rebuilding locally is also blocked by the host's Python 3.7 being too old for node-gyp 11; this is an environment/toolchain boundary, not a test assertion failure. The same 7-test contract passed before the rebase, and CI remains the authoritative post-rebase SQLite run.

@Elioooon

Copy link
Copy Markdown
Author

Rebased onto current main (e097f21d) and force-pushed as abb256d3, resolving the merge conflict.

The upstream manifest model now includes a non-Daytona stored variant. I preserved that boundary by capturing the already-narrowed Daytona manifest before the async error callback, then using that snapshot for the conditional status update; the store-level expected-manifest comparison accepts the persisted union. This keeps the stale-write guard without treating a non-Daytona record as a Daytona configuration.

Validated after the rebase:

  • pnpm --filter @truefoundry/trueforge-core test -- --runInBand tests/core/sandbox/daytonaSnapshotRegistration.test.ts — 6 passed
  • pnpm --filter @truefoundry/trueforge test -- --runInBand tests/unit/sandbox/providerUtils.test.ts — 1 passed
  • pnpm --filter @truefoundry/trueforge-core typecheck — passed
  • pnpm --filter @truefoundry/trueforge typecheck — passed (including DB and unit test projects)
  • git diff --check upstream/main...HEAD — passed

The existing SQLite/Postgres contract is unchanged by the rebase; the latter remains an environment-dependent integration boundary noted earlier.

@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch from f056581 to abb256d Compare September 12, 2026 01:42

@cursor cursor 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.

Stale Bugbot comment from a previous run.

Comment thread packages/trueforge/src/db/sandboxProviderStore.ts Outdated
@Elioooon

Copy link
Copy Markdown
Author

Follow-up on the latest store-input review: addressed in 1ce678d7 by making expected_manifest explicit (StoredSandboxProviderManifest | undefined) and updating every contract/store call site to pass the stale-write guard deliberately.

Revalidated on the current head:

  • pnpm --filter @truefoundry/trueforge test -- --runInBand tests/unit/truefoundry/TrueFoundrySandboxProviderStore.test.ts — 4 passed
  • pnpm --filter @truefoundry/trueforge typecheck — passed
  • git diff --check upstream/main...HEAD — passed

This does not change the concurrency behavior; it makes omission of the conditional-write guard a type-level error.

@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch from 1ce678d to e6ac0ed Compare September 12, 2026 11:34
@Elioooon

Copy link
Copy Markdown
Author

Rebased onto current main and force-pushed (e6ac0ed2).

The stale-write findings are covered by the manifest compare-and-set path: runtime and snapshot callbacks pass the manifest used to create the client; updateSandboxStatus updates only when it still matches, otherwise it returns the current row. This prevents an in-flight revoked-key error from overwriting a configuration rotation, without relying on timestamp precision.

Validated locally:

  • pnpm --filter @truefoundry/trueforge test -- --runInBand tests/unit/sandbox/providerUtils.test.ts
  • pnpm --filter @truefoundry/trueforge typecheck
  • pnpm exec eslint over changed production files (no errors)

I also attempted pnpm test:store:sqlite; it is blocked before test execution because the prebuilt better-sqlite3 binary requires GLIBC_2.29, unavailable in this runner. CI remains the environment for the SQLite contract suite.

@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch 2 times, most recently from 29f28b5 to 7297779 Compare September 12, 2026 13:02
@Elioooon

Copy link
Copy Markdown
Author

Rebased onto current main (24d2bfa9) and force-pushed the unchanged patch series (7297779f).

Validation after the rebase:

  • pnpm --filter @truefoundry/trueforge typecheck
  • pnpm --filter @truefoundry/trueforge exec jest --config jest.unit.config.cjs tests/unit/sandbox/providerUtils.test.ts tests/unit/truefoundry/TrueFoundrySandboxProviderStore.test.ts --runInBand — 2 suites / 5 tests passed
  • git diff --check upstream/main...HEAD

I also attempted the SQLite contract test that covers the stale-manifest guard. This runner cannot load its better-sqlite3 prebuild because the system glibc is older than GLIBC_2.29; that is an environment compatibility gate, not a test assertion failure. CI remains the path for that adapter-level coverage.

@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch from 7297779 to d24d771 Compare September 12, 2026 21:04
@Elioooon

Copy link
Copy Markdown
Author

Rebased the PR onto current main (6c12e594) and force-pushed the replayed branch at d24d7710; the rebase was conflict-free.

Revalidated the changed failure-propagation paths:

pnpm --filter @truefoundry/trueforge-core exec jest --config jest.config.cjs tests/core/sandbox/daytonaSnapshotRegistration.test.ts --runInBand
# 6 passed
pnpm --filter @truefoundry/trueforge exec jest --config jest.unit.config.cjs tests/unit/sandbox/providerUtils.test.ts tests/unit/truefoundry/TrueFoundrySandboxProviderStore.test.ts --runInBand
# 5 passed
git diff --check upstream/main...HEAD

I did not run the Postgres/SQLite contract suite locally because it requires those backing services; the patch still includes its contract coverage and CI remains the integration check.

@Elioooon

Copy link
Copy Markdown
Author

Addressed in d24d771: expected_manifest is now required (while still allowing an explicit undefined), and all store contract/adapter callers pass the guard explicitly. This keeps the stale-write precondition visible at every status update call site.

@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch from d24d771 to aedce08 Compare September 13, 2026 08:33
@Elioooon

Copy link
Copy Markdown
Author

The store-input concern is addressed in the current remote head aedce08: expected_manifest is required as StoredSandboxProviderManifest | undefined, making every caller choose the stale-write guard explicitly. The related contract-suite and TrueFoundry store call sites pass undefined where that is intentional.

Verification: pnpm --filter @truefoundry/trueforge typecheck passed. pnpm test:store:sqlite is blocked in this runner before tests execute because the installed better-sqlite3 prebuild requires GLIBC_2.29; upstream CI is needed for that runtime suite.

@Elioooon

Copy link
Copy Markdown
Author

The three Cursor findings are addressed in the follow-up commits already on this branch:

  • Runtime access failures now carry the exact manifest and build metadata snapshot into a conditional status update, so a later settings update is not overwritten.
  • Both Postgres and SQLite guard on manifest rather than a JavaScript-millisecond timestamp; this avoids the Postgres precision mismatch.
  • The expected manifest guard is explicit on the store input, including undefined for unconditional updates.

I re-ran the failure-path unit test and the SQLite store contract (including the stale-manifest case), then package typecheck. All passed.

@Elioooon

Copy link
Copy Markdown
Author

Correction to my previous validation note: the focused providerUtils failure-path unit test passed. The SQLite store contract could not start on this host because the installed better-sqlite3 native prebuild requires glibc 2.29+, while the host has an older glibc; this is an environment loader failure before any test executes. Consequently, package typecheck did not run in that chained local command. The PR’s existing contract test still covers the stale-manifest guard, but I am not claiming a local SQLite pass.

@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch from aedce08 to 53d7503 Compare September 13, 2026 22:32
@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch from 87d8a39 to 8961521 Compare September 16, 2026 18:02

@cursor cursor 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.

Stale Bugbot comment from a previous run.

@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch 4 times, most recently from 1d3f864 to 4f67d02 Compare September 17, 2026 07:04

@cursor cursor 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.

Stale Bugbot comment from a previous run.

@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch 3 times, most recently from 5631bae to 3aead05 Compare September 17, 2026 10:31

@cursor cursor 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.

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 3aead05. Configure here.

Comment thread packages/trueforge/src/sandbox/providerUtils.ts
@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch 6 times, most recently from a2fcd0e to 6818942 Compare September 17, 2026 21:32
@chiragjn
chiragjn marked this pull request as draft September 18, 2026 03:41
@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch 3 times, most recently from 5bf9a09 to 2399ba4 Compare September 18, 2026 17:04
@Elioooon
Elioooon force-pushed the fix/daytona-auth-failure-status branch from 2399ba4 to e76785c Compare September 18, 2026 18:03
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.

Daytona API key expiry is unhandled in few of the places

1 participant