Skip to content

fix: persist Daytona authentication failures - #519

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

fix: persist Daytona authentication failures#519
Elioooon wants to merge 4 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 tenant sandbox-provider persistence and runtime error handling on credential failures; optimistic manifest checks could leave status stale if misused, but scope is limited to Daytona auth paths.

Overview
When Daytona returns 401/403 (including wrapped Error.cause chains), the sandbox provider row is updated to failed with stable status_reason text instead of surfacing opaque internal settings errors or losing the signal when exec turns errors into { success: false }.

DaytonaSandboxProvider gains an optional onError hook invoked from createSandbox (before rethrow) and exec (before returning a failed result). Session resolution wires that hook to recordDaytonaAccessFailure, and snapshot refresh (checkSnapshotStatus) uses the same path on build/status errors.

updateSandboxStatus now accepts optional expected_manifest so status writes are skipped if credentials/manifest changed concurrently (Postgres + SQLite). Successful refresh updates also pass the current manifest for the same reason.

Reviewed by Cursor Bugbot for commit d24d771. 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: d24d771

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.

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.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit abb256d. Configure here.

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.

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