feat(api,proxy,daemon,sdk): pre-signed file download/upload URLs with key rotation across all SDKs#5099
Open
MDzaja wants to merge 1 commit into
Open
feat(api,proxy,daemon,sdk): pre-signed file download/upload URLs with key rotation across all SDKs#5099MDzaja wants to merge 1 commit into
MDzaja wants to merge 1 commit into
Conversation
|
View your CI Pipeline Execution ↗ for commit fd6d245
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗ ☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
21 issues found across 85 files
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
10cbec8 to
bb4b462
Compare
… key rotation across all SDKs Signed-off-by: MDzaja <mirkodzaja0@gmail.com>
bb4b462 to
fd6d245
Compare
2c15a7f to
b40f732
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds pre-signed file URLs so callers can download from / upload to a sandbox over plain HTTP without an auth token — the URL itself is the credential. URLs are HMAC-signed, optionally time-limited, and can be invalidated by rotating the sandbox's signing key. Ships end-to-end across the API, daemon, proxy, generated API clients, and all five SDKs (Python sync + async, TypeScript, Go, Ruby, Java).
Motivation
Today, every file transfer goes through the authenticated toolbox path. Pre-signed URLs let users hand a download/upload link to any HTTP client (browser,
curl, a third party) without exposing their API key.How it works
v1:files:{METHOD}:{path}:{expires}v1_{toolboxProxyUrl}/{sandboxId}/files/{download|upload-v2}?path=...&expires=...&signature=...expires=0(never expires)sandbox_metadatatable and caches it in Redis (300s).Changes by component
API (NestJS)
sandbox_metadatatable (sandboxIdPK,signingKey, FK →sandboxON DELETE CASCADE) via pre-deploy migration. Table + FK only — no backfill; rows are lazily created on first key read.SandboxMetadataentity + module registration.sandbox.service.ts:ensureMetadata()(lazy, race-safe),getSigningKey()(Redis cache 300s),rotateSigningKey()(regenerates key, write-through to Redis cache to prevent stale-key race on concurrent reads).ROTATE_SIGNING_KEY.Daemon (Go)
upload_file.go: dual-endpoint design for backward compatibility:POST /files/upload— legacy endpoint, kept alive but undocumented (no swagger annotations). Returns empty200. Old SDK clients continue working unchanged.POST /files/upload-v2— new documented endpoint. Returns a singleUploadedFileobject ({name, path, type}). Both new SDK clients and signed upload URLs use this endpoint.filefield) and rawapplication/octet-stream, with parent-dir creation (MkdirAll) and properClose()error checking.server.go: registerHEAD /files/download(proxy already permits HEAD for signed downloads; gin needs it declared explicitly).Proxy (Go)
signed_request_auth.go(new): generic signed-request core — verify request, compute/verify signature, parse expiry, strip signature params, signing-key cache + fetch + refetch-on-mismatch. Uses passed context (notcontext.Background()) for proper cancellation.file_url_auth.go(new): file-URL flavor —isSignedFileUrlRequest,fileUrlCanonical,verifySignedFileUrl.file_url_auth_test.go(new): cross-language signature vectors lock the wire format.get_sandbox_target.go: dispatch — signed file request → signature auth, else normal auth. Non-signed traffic is unaffected (theelse ifbranch is byte-identical to the previous condition).proxy.go: signing-key cache wiring.Generated API clients (all languages)
UploadedFilemodel for the upload response shape./files/upload-v2.SDKs (all five)
download_url/upload_url/rotate_signing_key(idiomatic naming per language) + a file-URL signing utility + 15s signing-key cache with mutex protection (Go).Number.isFiniteguard +Math.flooron TTL (TypeScript)._sync+_async), TypeScript, Go, Ruby, Java.Docs
Backward compatibility
POST /files/uploadstill returns empty200(the legacy endpoint is registered inserver.gobut has no swagger annotations, so it doesn't appear in regenerated clients). Verified live: old endpoint returns empty body, new/files/upload-v2returns theUploadedFileobject.download_url,upload_url,rotate_signing_key) naturally require the new daemon version. Standard version-skew behavior for new features — not a regression.signaturequery param). Non-signed traffic follows the identical auth path as before.signingKeyis not exposed on any DTO.Public SDK surface (example: Python)
Docstrings document the bounded-staleness contract: the key is cached for up to 15s, so a URL may be rejected for a few seconds if the key was rotated from another client.