Building "zero-gap" secrets for a UGC platform
by Braden-dev | 2026-02-07 00:55:21+00:00
Most platforms that let users deploy code handle secrets the same way: stuff them in an env var and wish everyone luck. I'm building a small social platform for publishing and remixing runnable web apps (Vibecodr), and the moment people could deploy server-side code, the first request was predictable: "let me call an external API with my secret."
The env var approach has always bugged me. The instant you hand plaintext to user code, it lives in memory where it can be logged, accidentally echoed in a response, or quietly exfiltrated through some dependency you didn't audit. When something goes wrong, good luck figuring out where the secret leaked.
So I tried to design around a stricter invariant: the plaintext secret should never exist inside the user's worker memory. Not as an env var, not as a return value from a helper, not in logs. Ideally, not even for a millisecond.
Here's how it works.
The dispatch layer
A platform-controlled proxy sits between user code and the outside world. User code never gets secrets directly. Instead it calls a wrapper like fetch-with-secret, passing:

which secret to use (a key name, not the value)
the outbound request details
where to inject the secret (header, query param, body)

The dispatch layer does the sensitive work: decrypts the secret server-side, validates the outbound URL (HTTPS-only, optional per-secret allowlist, infrastructure host blocking, DNS-based SSRF checks), makes the upstream request with strict timeouts, manually handles redirects with re-validation at every hop, redacts the secret from text responses, and enforces quotas.
The opaque token mode
The explicit fetch-with-secret API is secure but sometimes awkward—developers like composing requests normally. So there's a second mode: user code requests a short-lived opaque token for a given secret key (still never seeing the actual value). If that token appears in a fetch URL, header, or body, the wrapper intercepts it and routes the request through dispatch for real injection. Tokens are per-request, short-lived, and only resolve if they were minted in the same request context. Anything else fails closed.
Key management
I version the encrypted secret payload format so future migrations and key rotations aren't ambiguous. Multiple candidate decryption keys are supported simultaneously—rotate without breaking existing encrypted values, and keep the crypto logic shared across components so implementations don't drift apart.
The footguns I actually hit
Redirects are a security trap. Most HTTP clients follow redirects by default, which can silently turn an "allowed host" into a redirect to an internal IP. Manual redirect handling with re-validation at every hop was non-negotiable. This one would've bitten me badly if I'd shipped the naive version.
Redaction is trickier than it sounds. Secrets can appear raw, URL-encoded, or base64-encoded in responses. You want defense-in-depth without turning every response into garbage. Short secrets create false-positive redactions—I chose to redact anyway and warn rather than skip them, because "oops, leaked" is worse than "oops, mangled."
You need hard caps on everything. Request body size, response body size for scanning, timeout ceilings. Without them, every "helpful feature" becomes a DoS vector.
Where I'd love pushback
I'm posting this because I want critique from people who've built similar systems or poked holes in them:

What's your preferred strategy for handling binary responses safely when secrets have been injected into the request?
Have you seen failure modes around DNS-based SSRF validation in edge/serverless environments specifically?
Any strong opinions on redaction vs. just blocking the response entirely if it might contain the secret?

If you want to see this in a real product context, the project is https://Vibecodr.space —but I'm here for feedback on the architecture and threat model, not to do a launch post.