# REP — Runtime Environment Protocol: Complete Documentation The full rep-protocol.dev documentation set, concatenated. Generated at build time. Individual pages are available as Markdown at https://rep-protocol.dev/.md — see https://rep-protocol.dev/llms.txt for the index. --- # Quick Start — Runtime Environment Variables in 5 Minutes > Get runtime environment variables in your frontend app in under 5 minutes. Install the SDK, rename your env vars, and run the gateway. No manifest required. Source: https://rep-protocol.dev/quick-start/ The `.rep.yaml` manifest is entirely optional. The gateway works with just environment variables — the naming convention **is** the configuration. 1. **Rename your environment variables** Add the `REP_PUBLIC_`, `REP_SENSITIVE_`, or `REP_SERVER_` prefix to classify each variable: ```bash # Before (Vite) → After (REP) VITE_API_URL → REP_PUBLIC_API_URL VITE_FEATURE_FLAGS → REP_PUBLIC_FEATURE_FLAGS # Before (CRA) → After (REP) REACT_APP_API_URL → REP_PUBLIC_API_URL # Should be encrypted in the browser REACT_APP_ANALYTICS_KEY → REP_SENSITIVE_ANALYTICS_KEY # Should never reach the browser DB_PASSWORD → REP_SERVER_DB_PASSWORD ``` 2. **Install the SDK and update your code** ```bash npm install @rep-protocol/sdk ``` Replace your `import.meta.env.*` / `process.env.*` reads: ```typescript import { rep } from '@rep-protocol/sdk'; // Was: import.meta.env.VITE_API_URL const apiUrl = rep.get('API_URL'); // With a default value for local development const apiUrl = rep.get('API_URL', 'http://localhost:3001'); // Sensitive variable — encrypted, decrypted on demand const key = await rep.getSecure('ANALYTICS_KEY'); ``` `rep.get()` is **synchronous** — the SDK reads the payload from the DOM on import, before your first component renders. No loading state needed. 3. **Build your app (nothing changes)** ```bash npm run build ``` The output is now environment-agnostic. The same `dist/` folder goes to every environment. 4. **Run the gateway** **Binary** ```bash REP_PUBLIC_API_URL=https://api.example.com \ REP_PUBLIC_FEATURE_FLAGS=dark-mode,new-checkout \ REP_SENSITIVE_ANALYTICS_KEY=ak_live_abc123 \ ./rep-gateway --mode embedded --static-dir ./dist ``` **Docker** ```bash docker run --rm -p 8080:8080 \ -e REP_PUBLIC_API_URL=https://api.example.com \ -e REP_PUBLIC_FEATURE_FLAGS=dark-mode,new-checkout \ -e REP_SENSITIVE_ANALYTICS_KEY=ak_live_abc123 \ -v "$(pwd)/dist:/static:ro" \ ghcr.io/ruachtech/rep/gateway:latest \ --mode embedded --static-dir /static ``` Open `http://localhost:8080` — the gateway injected your variables into every HTML response. ## Changing config without rebuilding Stop the gateway. Restart with different values. Same `dist/` folder, different runtime config: ```bash REP_PUBLIC_API_URL=https://api.prod.example.com \ REP_PUBLIC_FEATURE_FLAGS=dark-mode \ REP_SENSITIVE_ANALYTICS_KEY=ak_live_prod_xyz \ ./rep-gateway --mode embedded --static-dir ./dist ``` No rebuild. No new container image. This is the core proposition. ## Proxy mode If you already have nginx, Caddy, or another static file server, run the gateway in front of it instead: ```bash REP_PUBLIC_API_URL=https://api.example.com \ ./rep-gateway --mode proxy --upstream localhost:80 ``` The gateway intercepts `text/html` responses, injects the ` ``` This gives you the full SDK experience (including `rep.verify()` and `rep.meta()`) without the gateway. > **Caution** > > Don't forget to remove this tag from your production HTML. In production, the gateway injects it automatically. ## Option D: CLI dev server (full fidelity) The `rep dev` command runs a local gateway that reads your `.env` file and proxies to your dev server: **With Vite** ```bash # Terminal 1: Start Vite dev server npm run dev # → Vite running at http://localhost:5173 # Terminal 2: Start REP gateway proxy rep dev --env .env.local --proxy http://localhost:5173 # → Gateway at http://localhost:8080 → proxies to Vite ``` Open `http://localhost:8080` (the gateway port), not `:5173`. The gateway injects variables into Vite's HTML responses. **With static files** ```bash # Build first npm run build # Serve from dist/ with REP injection rep dev --env .env.local --static ./dist ``` ### `.env.local` example ```bash REP_PUBLIC_API_URL=http://localhost:3000 REP_PUBLIC_FEATURE_FLAGS=dark-mode,debug REP_SENSITIVE_ANALYTICS_KEY=test-key-123 ``` ### CLI dev server options | Flag | Default | Description | |---|---|---| | `-e, --env ` | `.env.local` | Path to .env file | | `-p, --port ` | `8080` | Gateway port | | `--proxy ` | — | Upstream proxy URL | | `--static ` | — | Serve static files (embedded mode) | | `--hot-reload` | `false` | Enable hot reload | | `--gateway-bin ` | auto-detected | Path to gateway binary | > **Note** > > When using proxy mode with Vite, access the site through the gateway port (8080), not Vite's port (5173). Vite's HMR WebSocket continues to work through the proxy for CSS and module updates. ## Using all CLI commands in development The CLI provides a complete development workflow: ```bash # 1. Validate your manifest rep validate --manifest .rep.yaml # ✓ Manifest is valid — 6 variables, 4 settings # 2. Generate TypeScript types rep typegen --manifest .rep.yaml --output src/rep.d.ts # ✓ Generated types for 3 public + 2 sensitive variables # 3. Start the dev server rep dev --env .env.local --proxy http://localhost:5173 # 4. Before deployment: scan bundles for leaked secrets rep lint --dir ./dist # ✓ No secrets detected in 12 files ``` --- # REP for AI Agents — Integration Playbook > A single-page, machine-readable playbook for AI coding agents integrating REP. Decision tree, install matrix, copy-paste recipes for SDK, Docker, Kubernetes, and every framework, plus the mistakes that break a REP setup. Source: https://rep-protocol.dev/agents/ This page is written for AI coding agents. It is dense on purpose: everything needed to integrate REP into an application correctly, without reading the rest of the site. Every page on this site is also available as plain-text Markdown by appending `.md` to its path — for example [`/reference/sdk.md`](/reference/sdk.md). A machine-readable index of all pages lives at [`/llms.txt`](/llms.txt), and the entire documentation set concatenated into one file at [`/llms-full.txt`](/llms-full.txt). ## What REP is REP (Runtime Environment Protocol) moves frontend environment variables from **build time** to **container runtime**. Instead of `import.meta.env.VITE_API_URL` being replaced with a literal string during `npm run build`, a small Go gateway reads `REP_*` environment variables when the container starts and injects them into every HTML response as an inert `' # 3. No secret-shaped literal survived in the bundle npx @rep-protocol/cli lint --dir ./dist --strict # 4. The manifest, if present, is valid npx @rep-protocol/cli validate ``` 5. In the browser console, `rep.verify()` returns `true` and `rep.meta()` reports the expected `publicCount`. ## Source of truth When this page is not enough, these are the pages to read next — each is available as `.md`: - [Quick Start](/quick-start.md) — the five-minute path - [How REP Works](/concepts/how-it-works.md) — startup sequence and injection mechanics - [Variable Classification](/concepts/variable-classification.md) — tier rules and guardrail internals - [Security Model](/concepts/security-model.md) — threat analyses and hardening - [Wire Format](/concepts/wire-format.md) — payload JSON, encrypted blob layout, HMAC - [SDK API](/reference/sdk.md) — full client reference - [Gateway Flags](/reference/gateway-flags.md) and [Endpoints](/reference/gateway-endpoints.md) - [REP-RFC-0001](/spec/rfc-0001.md) — the normative specification - [Conformance](/spec/conformance.md) — what an alternative implementation must satisfy Machine-readable indexes: [`/llms.txt`](/llms.txt) · [`/llms-full.txt`](/llms-full.txt) · [payload schema](/schema/rep-payload.schema.json) · [manifest schema](/schema/rep-manifest.schema.json) --- # How REP Works — Gateway Architecture and HTML Injection > How the REP gateway injects environment variables into HTML at runtime. Proxy and embedded modes, startup sequence, script tag injection, and SDK initialization. Source: https://rep-protocol.dev/concepts/how-it-works/ ## Architecture REP introduces a lightweight gateway process between the browser and your static file server. The gateway reads `REP_*` environment variables at startup, classifies them, and injects a ` ``` **Injection rules:** 1. Insert before `` (preferred) 2. If no ``, insert after `` 3. If neither exists, prepend to the response body > **Caution** > > `type="application/json"` is critical — it prevents the browser from executing the tag. This is inert data, not executable JavaScript. Never change this to `text/javascript`. ## SDK initialization The SDK reads the injected payload **synchronously** on import: 1. Locate ` ``` ### Fields | Field | Type | Required | Description | |---|---|---|---| | `public` | `object` | Yes | Key-value map of PUBLIC tier variables (strings only) | | `sensitive` | `string` | No | Base64-encoded AES-256-GCM encrypted blob | | `_meta` | `object` | Yes | Payload metadata | ### `_meta` fields | Field | Type | Required | Description | |---|---|---|---| | `version` | `string` | Yes | Protocol version (semver) | | `injected_at` | `string` | Yes | RFC 3339 timestamp of injection | | `integrity` | `string` | Yes | `hmac-sha256:{base64_signature}` | | `key_endpoint` | `string` | No | Path to session key endpoint (`/rep/session-key`) | | `hot_reload` | `string` | No | Path to SSE endpoint (`/rep/changes`) | | `ttl` | `integer` | No | Cache TTL in seconds (0 = no cache) | ### SRI attribute The `data-rep-integrity` attribute contains a SHA-256 hash of the raw JSON content: ``` data-rep-integrity="sha256-{base64_of_sha256_digest}" ``` The SDK verifies this using the Web Crypto API, comparing the hash against the actual `textContent` of the ` ``` ## `useRep(key, defaultValue?)` Reads a **PUBLIC** tier variable as a reactive `Ref`. ```typescript const value: Ref = useRep('API_URL'); const value: Ref = useRep('API_URL', 'fallback'); ``` - Returns a `Ref` set immediately from the injected payload - Automatically updates when the variable changes via hot reload - Unsubscribes via `onUnmounted` — must be called inside `setup()` ## `useRepSecure(key)` Reads a **SENSITIVE** tier variable as a reactive `Ref`. ```typescript const analyticsKey: Ref = useRepSecure('ANALYTICS_KEY'); ``` - Starts as `null` - Resolves once the session key fetch and decryption complete - Errors are swallowed (the SDK logs them); the ref stays `null` - The decrypted value is cached for the page lifetime > **Caution** > > `useRepSecure` does **not** subscribe to hot reload. Sensitive variable changes require a page reload to obtain a new session key. ## Hot reload `useRep` subscribes to the SSE stream on mount and updates the ref automatically. The subscription is cleaned up when the component unmounts. ## Development mode **Default values** ```typescript const apiUrl = useRep('API_URL', 'http://localhost:3000'); ``` **Mock payload** Add to your `index.html`: ```html ``` ## Requirements - Vue >= 3.0 - `@rep-protocol/sdk` as a peer dependency - Composables must be called from `setup()` for proper `onUnmounted` cleanup For the full API reference, see [Vue Adapter Reference](/reference/adapters/vue/). --- # Svelte Runtime Environment Variables — repStore() > Access runtime environment variables in Svelte with repStore() and repSecureStore(). Native readable stores with lazy SSE hot reload subscriptions. Source: https://rep-protocol.dev/frameworks/svelte/ ## Installation ```bash npm install @rep-protocol/svelte @rep-protocol/sdk ``` ## Basic usage ```svelte

API: {$apiUrl}

Analytics: {$analyticsKey ?? 'loading...'}

``` ## `repStore(key, defaultValue?)` Reads a **PUBLIC** tier variable as a Svelte `Readable`. ```typescript const value: Readable = repStore('API_URL'); const value: Readable = repStore('API_URL', 'fallback'); ``` - Synchronous initial value from the REP payload - Automatically updates when the variable changes via hot reload - **Lazy SSE:** the connection is established only when there is at least one subscriber, and closed when all subscribers unsubscribe ## `repSecureStore(key)` Reads a **SENSITIVE** tier variable as a `Readable`. ```typescript const value: Readable = repSecureStore('ANALYTICS_KEY'); ``` - Starts as `null` - Resolves to the decrypted value once the session key fetch completes - Errors are swallowed (the SDK logs them); the store stays `null` > **Caution** > > `repSecureStore` does **not** support hot reload. Changes to sensitive variables require a page reload. ## Hot reload `repStore` subscribes to the SSE stream lazily — only when there's at least one subscriber. The store value updates automatically. When the last subscriber unsubscribes, the SSE connection is closed. ## Development mode **Default values** ```typescript const apiUrl = repStore('API_URL', 'http://localhost:3000'); ``` **Mock payload** Add to your `index.html`: ```html ``` ## Requirements - Svelte >= 4 or Svelte 5 - `@rep-protocol/sdk` as a peer dependency For the full API reference, see [Svelte Adapter Reference](/reference/adapters/svelte/). --- # Angular Runtime Environment Variables — RepService > Access runtime environment variables in Angular with an injectable RepService. Observable support for reactive config updates without page reload. Source: https://rep-protocol.dev/frameworks/angular/ > **Note** > > There is no published `@rep-protocol/angular` package. Angular integration uses the core SDK directly via a service pattern shown below. ## Installation ```bash npm install @rep-protocol/sdk ``` ## Create a RepService ```typescript // src/app/services/rep.service.ts import { Injectable } from '@angular/core'; import { rep } from '@rep-protocol/sdk'; import { BehaviorSubject, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RepService { get(key: string, defaultValue?: string): string | undefined { return rep.get(key, defaultValue); } getSecure(key: string): Promise { return rep.getSecure(key); } watch(key: string): Observable { const subject = new BehaviorSubject(rep.get(key)); rep.onChange(key, (newVal) => subject.next(newVal)); return subject.asObservable(); } } ``` ## Usage in components ```typescript // src/app/components/config-display.component.ts import { Component, OnInit } from '@angular/core'; import { RepService } from '../services/rep.service'; import { Observable } from 'rxjs'; @Component({ selector: 'app-config-display', template: `

API: {{ apiUrl }}

Flags: {{ flags$ | async }}

Analytics: {{ analyticsKey ?? 'loading...' }}

`, }) export class ConfigDisplayComponent implements OnInit { apiUrl: string | undefined; flags$: Observable; analyticsKey: string | null = null; constructor(private rep: RepService) { // Synchronous — available immediately this.apiUrl = this.rep.get('API_URL', 'http://localhost:3000'); // Observable — updates on hot reload this.flags$ = this.rep.watch('FEATURE_FLAGS'); } async ngOnInit() { // Async — encrypted, decrypted on demand this.analyticsKey = await this.rep.getSecure('ANALYTICS_KEY'); } } ``` ## How it works - `get()` calls `rep.get()` from the core SDK — synchronous, no loading state - `getSecure()` calls `rep.getSecure()` — fetches a session key, decrypts, caches - `watch()` wraps `rep.onChange()` into a `BehaviorSubject` for Angular's `async` pipe ## Development mode Without the gateway, `rep.get()` returns `undefined`. Use default values: ```typescript this.apiUrl = this.rep.get('API_URL', 'http://localhost:3000'); ``` Or add a mock payload to your `index.html` during development: ```html ``` --- # Vanilla JavaScript Runtime Environment Variables > Access runtime environment variables in plain JavaScript with ESM imports. No framework, no build tool — works in any browser app that produces HTML. Source: https://rep-protocol.dev/frameworks/vanilla/ ## Installation ```bash npm install @rep-protocol/sdk ``` Or use directly from your bundled assets without npm. ## Basic usage ```html ``` ## With hot reload ```html ``` ## Integrity verification ```html ``` ## Development mode Without the gateway, `rep.get()` returns `undefined`. Two options: **Option A: Check and use fallbacks** ```javascript const apiUrl = rep.get('API_URL') ?? 'http://localhost:3000'; ``` **Option B: Mock payload in HTML** ```html ``` ## No build tool required REP works without any build tool. If you have a static HTML site with inline ` ``` The gateway injects the ` ↓ serves index.html ``` The HTML file imports the SDK as an ES module from esm.sh — no npm install, no bundler: ```html ``` > **Tip** > > The `__rep__` script tag is injected **before** the browser parses `` breakout from untrusted env values. The tag still carries `type="application/json"`, so the browser never executes it. - **Ephemeral keys are process-scoped**, held in a module-level singleton (`getOrCreateKeys()`) so the same key survives across route handler invocations within one `next dev` process. - **Byte-identical payload** to the Go gateway and the Vite plugin (sorted keys, same HMAC + SRI format) — `rep.verify()` and `rep.meta()` behave the same regardless of which one produced the payload. ## App Router only `RepScript` is a Server Component and requires the App Router (`app/`). The Pages Router (`pages/`) doesn't support React Server Components — use the [CLI dev server](/guides/development/#option-d-cli-dev-server-full-fidelity) or [mock payload](/guides/development/#option-c-mock-payload-in-html) approach instead. ## See also - [Local Development guide](/guides/development/) — where this fits alongside the CLI dev server and mock-payload approaches - [Next.js — Proxy Mode example](/examples/nextjs-proxy/) — production deployment behind the gateway - [Vite plugin reference](/reference/plugins/vite/) — the equivalent for Vite --- # Codemod Reference — Automated Migration Tool > Automated codemod to migrate from Vite import.meta.env, CRA process.env.REACT_APP_*, and Next.js NEXT_PUBLIC_* to REP runtime environment variables. Idempotent and non-destructive. Source: https://rep-protocol.dev/reference/codemod/ ```bash npm install -D @rep-protocol/codemod # or npx @rep-protocol/codemod [options] [files...] ``` ## Usage ```bash rep-codemod [options] [files or directories...] ``` ## Options | Flag | Default | Description | |---|---|---| | `-f, --framework ` | `vite` | Framework preset: `vite`, `cra`, `next` | | `--dry-run` | `false` | Preview changes without writing files | | `--extensions ` | `ts,tsx,js,jsx` | Comma-separated file extensions to process | ## Framework presets ### `--framework vite` Transforms `import.meta.env.VITE_*` to `rep.get('*')`: ```typescript // Before const apiUrl = import.meta.env.VITE_API_URL; // After import { rep } from '@rep-protocol/sdk'; const apiUrl = rep.get('API_URL'); ``` Vite built-ins (`MODE`, `DEV`, `PROD`, `SSR`, `BASE_URL`) are left untouched. ### `--framework cra` Transforms `process.env.REACT_APP_*` to `rep.get('*')`: ```typescript // Before const apiUrl = process.env.REACT_APP_API_URL; // After import { rep } from '@rep-protocol/sdk'; const apiUrl = rep.get('API_URL'); ``` ### `--framework next` Transforms `process.env.NEXT_PUBLIC_*` to `rep.get('*')`: ```typescript // Before const apiUrl = process.env.NEXT_PUBLIC_API_URL; // After import { rep } from '@rep-protocol/sdk'; const apiUrl = rep.get('API_URL'); ``` ## Examples ```bash # Transform Vite project rep-codemod --framework vite src/ # Dry run — preview changes rep-codemod --framework cra --dry-run src/components/ # Specific files rep-codemod --framework next src/app/page.tsx src/lib/api.ts # JavaScript files only rep-codemod --framework vite --extensions js,jsx src/ ``` ## Behavior - **Idempotent** — running twice produces the same result - **Import management** — adds `import { rep } from '@rep-protocol/sdk'` if absent - **Non-destructive** — only prefixed variables are transformed - **Format-preserving** — original formatting and comments are preserved (via jscodeshift/recast) ## Post-migration steps 1. Remove framework-specific type augmentations (e.g., `vite-env.d.ts`) 2. Run `rep typegen` to generate typed overloads 3. Update container config to set `REP_PUBLIC_*` environment variables --- # Specification Overview — REP Protocol Documents > Formal specification documents for the Runtime Environment Protocol. RFC-0001 core protocol, security model threat analysis, and conformance requirements for implementations. Source: https://rep-protocol.dev/spec/ REP is defined by three specification documents. They are the authoritative reference for the protocol, its security properties, and its conformance requirements. ## Documents | Document | Status | Version | Description | |---|---|---|---| | [REP-RFC-0001](/spec/rfc-0001/) | Active | 0.1.0 | Core protocol specification — variable classification, gateway architecture, SDK API, wire format, deployment patterns | | [Security Model](/spec/security-model/) | Active | 0.1.0 | Threat model, 7 threat analyses, hardening recommendations, known limitations | | [Conformance](/spec/conformance/) | Active | 0.1.0 | Requirements for conformant gateway and SDK implementations | ## Versioning policy The specification uses semantic versioning: - **Patch** (0.1.x): Clarifications, typo fixes, non-normative additions - **Minor** (0.x.0): New optional features, backwards-compatible extensions - **Major** (x.0.0): Breaking changes to the wire format, API surface, or security model The current version (0.1.0) indicates the specification is active and subject to refinement based on implementation experience. Breaking changes are possible before 1.0. ## License Specification documents are licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Reference implementations (gateway, SDK, CLI, adapters) are licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). ## JSON schemas Machine-readable schemas are published for integration and validation: - [Payload schema](/schema/rep-payload.schema.json) — validates the injected `