One build per environment
Your staging bundle can’t be promoted to production. You rebuild for every environment, breaking the container promise of “build once, run anywhere.”
Hot reload and feature flags — no rebuild, no redeploy.
Every frontend framework resolves environment variables at build time — process.env.API_URL becomes a hardcoded string in your JavaScript bundle. This creates problems that compound in production:
One build per environment
Your staging bundle can’t be promoted to production. You rebuild for every environment, breaking the container promise of “build once, run anywhere.”
No security model
All variables are treated equally. API keys, analytics tokens, and public URLs all end up as plaintext strings in your bundle — visible to anyone who opens View Source.
Config changes require redeployment
Need to rotate an API key or flip a feature flag? Rebuild, re-test, redeploy. Even if your backend can hot-swap config, your frontend can’t.
Secrets leak into build logs
CI/CD pipelines pass secrets as build args. They appear in Docker layer history, build logs, and cached artifacts — creating supply chain risk.
REP is an open protocol and reference implementation that decouples environment variables from the build process entirely. A lightweight gateway reads variables at startup, classifies them by security tier, encrypts sensitive values, and injects them into HTML responses — whether you’re running containers, static builds, or local development.
Three Security Tiers
PUBLIC — plaintext, synchronous access. SENSITIVE — AES-256-GCM encrypted, decrypted on demand. SERVER — never sent to the browser. Classification by naming convention eliminates ambiguity.
Encryption + Integrity
Sensitive variables are encrypted with ephemeral keys generated at startup. Every payload carries an HMAC-SHA256 integrity token and SRI hash to detect tampering in transit.
Secret Detection Guardrails
Entropy analysis and known-format matching (AWS keys, JWTs, Stripe keys, GitHub tokens) catch misclassified secrets before they reach the browser. Strict mode makes warnings into hard failures.
Hot Reload via SSE
Config changes push to every connected browser via Server-Sent Events. No page reload, no redeployment. Edit an env file or rotate a ConfigMap — the UI updates live.
REP operates at the infrastructure layer — no build-tool plugins, no framework coupling. It works with any frontend stack that produces HTML.
Containers
Reverse proxy mode sits in front of nginx, caddy, or any upstream. Embedded mode serves static files directly from a FROM scratch image (~3MB).
Static Builds
Build your app once with npm run build. The gateway injects config into the static HTML at serve time — same artifact for every environment.
Local Development
The CLI wraps the gateway for local dev. Proxy your Vite/webpack dev server with hot reload, or serve a production build with live config changes.
Any Framework
First-party adapters for React, Vue, and Svelte. A zero-dependency TypeScript SDK works with anything — Angular, vanilla JS, or your custom setup.
| Approach | Security Tiers | Encrypted Vars | Integrity Check | Hot Reload | Standalone Binary |
|---|---|---|---|---|---|
envsubst / sed on bundles | — | — | — | — | — |
Fetch /config.json at runtime | — | — | — | — | — |
window.__ENV__ via shell script | — | — | — | — | — |
| Build-tool plugins (dotenv, etc.) | — | — | — | — | — |
| REP | 3-tier | AES-256-GCM | HMAC + SRI | SSE | ~3MB Go |
import { rep } from '@rep-protocol/sdk';
// Synchronous — no loading state neededconst apiUrl = rep.get('API_URL');const flags = rep.get('FEATURE_FLAGS');
// Encrypted — decrypted on demand via session keyconst key = await rep.getSecure('ANALYTICS_KEY');
// Hot reload — react to config changes liverep.onChange('FEATURE_FLAGS', (newValue) => { console.log('Flags updated:', newValue);});# Same image, different config — true build-once, deploy-anywhereservices: frontend-staging: image: myapp:latest environment: REP_PUBLIC_API_URL: "https://api.staging.example.com" REP_SENSITIVE_ANALYTICS_KEY: "UA-XXXXX-staging" REP_SERVER_INTERNAL_SECRET: "never-reaches-browser"
frontend-prod: image: myapp:latest # SAME IMAGE environment: REP_PUBLIC_API_URL: "https://api.example.com" REP_SENSITIVE_ANALYTICS_KEY: "UA-XXXXX-prod" REP_SERVER_INTERNAL_SECRET: "also-never-reaches-browser"# PUBLIC — plaintext in page source, rep.get()REP_PUBLIC_API_URL=https://api.example.com
# SENSITIVE — AES-256-GCM encrypted, await rep.getSecure()REP_SENSITIVE_ANALYTICS_KEY=UA-12345-1
# SERVER — never leaves the gateway processREP_SERVER_DB_PASSWORD=never-reaches-browserGateway
Go binary (~3MB). Zero dependencies. FROM scratch compatible. Proxy or embedded mode.
SDK
TypeScript. Zero runtime deps. ~1.5KB gzipped. Synchronous get(), async getSecure().
CLI
rep validate, rep typegen, rep lint, rep dev. Full development workflow.
Adapters
First-party React, Vue, and Svelte adapters with hot-reload-aware hooks and stores.
| Document | Status | Version |
|---|---|---|
| REP-RFC-0001 | Active | 0.1.0 |
| Security Model | Active | 0.1.0 |
| Conformance | Active | 0.1.0 |
Specification documents are licensed under CC BY 4.0. Code is licensed under Apache 2.0.