Skip to content

feat(snapshots): expose imageUri end-to-end (spec, server, all SDKs)#1272

Open
ferponse wants to merge 6 commits into
opensandbox-group:mainfrom
ferponse:feat/snapshot-imageuri-kotlin
Open

feat(snapshots): expose imageUri end-to-end (spec, server, all SDKs)#1272
ferponse wants to merge 6 commits into
opensandbox-group:mainfrom
ferponse:feat/snapshot-imageuri-kotlin

Conversation

@ferponse

@ferponse ferponse commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #1271.

In our organization we integrate with OpenSandbox through the official SDKs rather than calling the REST API directly — the typed client is our integration contract. When a field is missing from the SDK we are forced to drop down to raw HTTP, which adds complexity and couples our code to the wire protocol, hurting maintainability.

A Ready snapshot already produces a portable OCI restore image internally, but it is never surfaced on the public snapshot contract, so callers cannot restore a sandbox from that image (e.g. to move a sandbox across clusters) through the SDK without bypassing it. This exposes it end-to-end as imageUri, across the spec, the server, and all SDKs.

Per the Codex review on the earlier revision (which flagged that only the server + Kotlin were updated, leaving the other SDK snapshot models inconsistent), this now regenerates/updates every SDK. Commits are split by concern — one for spec + server, then one per SDK.

Spec

  • Add the optional imageUri to the Snapshot schema in specs/sandbox-lifecycle.yml.

Server

  • Add image_uri/imageUri to the Snapshot response model and populate it from the record's restore image in _to_snapshot_response, only once the snapshot is Ready.

Generated clients (regenerated from the spec, not hand-edited)

  • python: regenerated the lifecycle Snapshot model with scripts/generate_api.py (openapi-python-client); mapped onto the domain SnapshotInfo in the converter.
  • javascript: regenerated api/lifecycle.ts with pnpm gen:api (openapi-typescript); added imageUri to the domain snapshot model.
  • kotlin: the lifecycle client regenerates from the spec at build time (sandbox-api OpenAPI generator); added imageUri to the domain SnapshotInfo and mapped it in SandboxModelConverter.

Hand-written clients (no spec codegen — field added by hand)

  • go: the lifecycle types are hand-written, so the field was added directly to types.go (ImageURI string with json:"imageUri,omitempty").
  • csharp: the snapshot model is hand-written, so Models/Sandboxes.cs gets string? ImageUri and Adapters/SandboxesAdapter.cs parses imageUri from the response JSON.

imageUri is optional and only populated once the snapshot is Ready (null/omitted otherwise), so the change is additive and backward compatible.

Commits

  1. feat(snapshots): expose snapshot imageUri in spec and server
  2. feat(python): expose snapshot imageUri in the Python SDK
  3. feat(javascript): expose snapshot imageUri in the JavaScript SDK
  4. feat(kotlin): expose snapshot imageUri in the Kotlin SDK
  5. feat(go): expose snapshot imageUri in the Go SDK
  6. feat(csharp): expose snapshot imageUri in the C# SDK

Testing

  • Not run (explain why)

  • Unit tests

  • Integration tests

  • e2e / manual verification

  • server: uv run pytest tests/test_snapshot_service.py — asserts the response exposes imageUri only when Ready. Green.

  • kotlin: ./gradlew spotlessCheck :sandbox:testSandboxesAdapterTest.getSnapshot parses imageUri; the lifecycle client regenerated from the spec exposes the field. Green.

  • csharp: dotnet build + dotnet test — 132 passed.

  • javascript: pnpm run typecheck green; api/lifecycle.ts regenerated from the spec.

  • python: ruff check clean; lifecycle Snapshot regenerated via scripts/generate_api.py.

  • go: hand-written field; the SDK has no codegen step.

Breaking Changes

  • None
  • Yes (describe impact and migration path)

Additive optional field; null/omitted unless the snapshot is Ready.

Checklist

  • Linked Issue or clearly described motivation — Expose the snapshot restore image (imageUri) across all SDKs #1271
  • Added/updated tests (if needed)
  • Security impact considered — exposes the OCI restore image reference the caller needs to restore; gated on Ready, no new privileged data
  • Backward compatibility considered — additive optional field

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6057c898f7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread specs/sandbox-lifecycle.yml
@ferponse
ferponse force-pushed the feat/snapshot-imageuri-kotlin branch from 6057c89 to 074d8d2 Compare July 10, 2026 15:13

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 074d8d2d32

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Ferran Pons Serra added 3 commits July 10, 2026 17:30
Add the optional imageUri to the Snapshot schema and populate it from the record restore image in the server response, only once the snapshot is Ready (null/omitted otherwise).
Regenerate the lifecycle Snapshot model from the spec and map imageUri onto the domain SnapshotInfo in the converter.
Regenerate the lifecycle API types from the spec and add imageUri to the domain SnapshotInfo model.
@ferponse
ferponse force-pushed the feat/snapshot-imageuri-kotlin branch from 074d8d2 to dc6cf5d Compare July 10, 2026 15:31
@ferponse ferponse changed the title feat(snapshots): expose snapshot imageUri (spec, server, Kotlin SDK) feat(snapshots): expose imageUri end-to-end (spec, server, all SDKs) Jul 10, 2026
Ferran Pons Serra added 3 commits July 10, 2026 17:40
Add imageUri to the domain SnapshotInfo and map it in SandboxModelConverter; the lifecycle client regenerates from the spec at build time. SnapshotInfo keeps a binary-compatibility constructor (with a default on name) so the pre-imageUri JVM constructor and its $default synthetic are preserved for already-compiled callers.
Add the ImageURI field to the hand-written SnapshotInfo type.
Add ImageUri to the SnapshotInfo model and parse imageUri from the response in SandboxesAdapter.
@ferponse
ferponse force-pushed the feat/snapshot-imageuri-kotlin branch from dc6cf5d to a1efdd1 Compare July 10, 2026 15:40
@ferponse

Copy link
Copy Markdown
Contributor Author

@Pangjiping could you review this PR when you have a moment? 🙏

Both Codex review comments from the earlier revision are addressed:

  1. Regenerate all SDK snapshot models for imageUri — done. imageUri is now surfaced in every SDK, not just server + Kotlin. The spec-driven clients were regenerated from the updated spec (Python via scripts/generate_api.py, JavaScript via pnpm gen:api, Kotlin at build time) and the hand-written ones (Go, C#) were updated by hand. Commits are split by concern: one for spec + server, then one per SDK.

  2. Preserve the old Kotlin default-argument constructor — done. The SnapshotInfo binary-compatibility constructor now keeps a default on name, which makes the compiler re-emit the pre-imageUri synthetic $default constructor. Verified against the compiled class with javap:

    SnapshotInfo(String, String, String, SnapshotStatus, OffsetDateTime)                                   // 5-arg (Java)
    SnapshotInfo(String, String, String, SnapshotStatus, OffsetDateTime, int, DefaultConstructorMarker)     // old $default (restored)
    SnapshotInfo(String, String, String, SnapshotStatus, OffsetDateTime, String)                            // new primary
    SnapshotInfo(String, String, String, SnapshotStatus, OffsetDateTime, String, int, DefaultConstructorMarker) // new $default
    

    So Kotlin call sites compiled against the previous SnapshotInfo that omitted name keep resolving instead of hitting NoSuchMethodError. I also confirmed there's no new overload-resolution ambiguity for callers that omit name/imageUri (compiled a probe covering those call shapes).

Validation: server pytest green; Kotlin spotlessCheck + :sandbox:test green; C# dotnet build/test green (132); JS typecheck green; Python ruff clean with the lifecycle client regenerated from the spec.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose the snapshot restore image (imageUri) across all SDKs

1 participant