Make the Cloudinary cloud name a constant, not a secret - #191
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The cloud name is the first path segment of every image URL the app serves. It is public by construction — anyone who has loaded a single photo already has it — and storing it in Secret Manager protected nothing. What it did instead was invent a failure mode. Because it was a secret param, every callable that validates a media URL had to remember `secrets: [CLOUDINARY_CLOUD_NAME]`, and one that forgot would pass CI and throw in production on the first upload. CI cannot catch that: the emulator drives handlers through .run(), which bypasses secret mounting entirely, and setup.ts set the variable directly, so the tests saw a value the deployed function would not have had. #185's own description flagged this and had to derive the list of eleven bindings by grepping call paths rather than by testing it. All eleven bindings are gone. The value lives in platform.ts next to CLOUDINARY_FOLDER, and shared.ts reads it directly instead of reaching into process.env — which also removes the "misconfigured" branch that existed only to handle a caller arriving without the binding. There is no longer a way to arrive without it. CLOUDINARY_API_KEY and CLOUDINARY_API_SECRET stay in Secret Manager. Those are the credentials, and media.ts keeps binding them. The truthiness check there now covers only those two, since the cloud name cannot be empty. setup.ts stops setting CLOUDINARY_CLOUD_NAME, and the tests compare against the constant. That is the part that matters beyond tidiness: a test can no longer pass because the environment supplied something production would not have. Functions 80/80, build / lint / typecheck:test clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
renrenmimi
force-pushed
the
chore/cloud-name-not-a-secret
branch
from
September 7, 2026 01:53
627fbfd to
239349a
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.
The cloud name is the first path segment of every image URL the app serves. It is public by construction — anyone who has loaded a single photo already has it — and storing it in Secret Manager protected nothing.
What it did instead
It invented a failure mode. Because it was a secret param, every callable that validates a media URL had to remember
secrets: [CLOUDINARY_CLOUD_NAME], and one that forgot would pass CI and throw in production on the first upload.CI structurally cannot catch that: the emulator drives handlers through
.run(), which bypasses secret mounting entirely, andsetup.tsset the variable directly — so the tests saw a value the deployed function would not have had. #185's own description flagged this and had to derive the list of eleven bindings by grepping call paths rather than by testing.The change
All eleven bindings gone. The value lives in
platform.tsnext toCLOUDINARY_FOLDER, andshared.tsreads it directly instead of reaching intoprocess.env— which also removes the"misconfigured"branch that existed purely to handle a caller arriving without the binding. There is no longer a way to arrive without it.CLOUDINARY_API_KEYandCLOUDINARY_API_SECRETstay in Secret Manager — those are the credentials, andmedia.tskeeps binding them. The truthiness check there now covers only those two.The part that matters beyond tidiness
setup.tsstops settingCLOUDINARY_CLOUD_NAME, and the tests compare against the constant. A test can no longer pass because the environment supplied something production would not have. That is the same shape as the bug in #187, where a test recomputed the implementation's own wrong assumption and passed while every upload failed.Functions 80/80,
build/lint/typecheck:testclean.Deploy note
This changes the deploy config of eleven functions (removing a secret binding), so they will genuinely redeploy rather than report "no changes detected".
🤖 Generated with Claude Code