Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/workflows/release-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,13 @@ jobs:
["adapters/react"]="react-v"
["adapters/vue"]="vue-v"
["adapters/svelte"]="svelte-v"
["plugins/vite"]="vite-v"
["plugins/next"]="next-v"
)

TAGGED=0

for pkg_path in "sdk" "cli" "codemod" "adapters/react" "adapters/vue" "adapters/svelte"; do
for pkg_path in "sdk" "cli" "codemod" "adapters/react" "adapters/vue" "adapters/svelte" "plugins/vite" "plugins/next"; do
manifest_ver=$(jq -r --arg p "$pkg_path" '.[$p]' .release-please-manifest.json)
pkg_ver=$(jq -r '.version' "${pkg_path}/package.json")
prefix="${TAG_PREFIX[$pkg_path]}"
Expand Down Expand Up @@ -284,6 +286,24 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Publish vite plugin
working-directory: plugins/vite
run: |
V=$(node -p "require('./package.json').version")
PUBLISHED=$(npm view @rep-protocol/vite@${V} version 2>/dev/null || true)
if [ "$PUBLISHED" = "$V" ]; then echo "vite@${V} already published, skipping."; else pnpm publish --provenance --access public --no-git-checks; fi
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Publish next plugin
working-directory: plugins/next
run: |
V=$(node -p "require('./package.json').version")
PUBLISHED=$(npm view @rep-protocol/next@${V} version 2>/dev/null || true)
if [ "$PUBLISHED" = "$V" ]; then echo "next@${V} already published, skipping."; else pnpm publish --provenance --access public --no-git-checks; fi
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

release-gateway:
name: GoReleaser
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
- 'cli/**'
- 'codemod/**'
- 'adapters/**'
- 'plugins/**'
- 'pnpm-workspace.yaml'
- '.github/workflows/sdk.yml'
pull_request:
Expand All @@ -31,6 +32,7 @@ jobs:
- 'cli/**'
- 'codemod/**'
- 'adapters/**'
- 'plugins/**'
- 'pnpm-workspace.yaml'
- '.github/workflows/sdk.yml'

Expand Down
2 changes: 2 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@
"adapters/react": "0.1.11",
"adapters/vue": "0.1.11",
"adapters/svelte": "0.1.11",
"plugins/vite": "0.1.11",
"plugins/next": "0.1.11",
Comment on lines +8 to +9
"gateway": "0.1.5"
}
43 changes: 42 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ rep/
│ └── release-sdk.yml # Release workflow (npm + GoReleaser + Docker)
├── package.json # Monorepo root (pnpm 9.0.0, private)
├── pnpm-workspace.yaml # Workspace: sdk, cli, adapters/*, codemod, examples/*
├── pnpm-workspace.yaml # Workspace: sdk, cli, adapters/*, plugins/*, codemod, docs
├── pnpm-lock.yaml
├── release-please-config.json # Release-please config for all packages
├── .release-please-manifest.json # Per-package version tracker
Expand Down Expand Up @@ -124,6 +124,30 @@ rep/
│ ├── vue/ # @rep-protocol/vue — useRep() composable
│ └── svelte/ # @rep-protocol/svelte — repStore()
├── plugins/
│ ├── vite/ # @rep-protocol/vite — Vite dev server plugin
│ │ ├── package.json # v0.1.10
│ │ ├── src/
│ │ │ ├── index.ts # repPlugin() — Vite plugin entry
│ │ │ ├── payload.ts # Payload builder (mirrors Go payload.go)
│ │ │ ├── crypto.ts # AES-256-GCM, HMAC-SHA256, SRI, HKDF (Node crypto)
│ │ │ ├── env.ts # Read env file + classify variables by prefix
│ │ │ ├── guardrails.ts # Shannon entropy + known format checks (mirrors cli/)
│ │ │ └── __tests__/ # 49 tests
│ │ └── vitest.config.ts
│ └── next/ # @rep-protocol/next — Next.js dev integration
│ ├── package.json # v0.1.10; exports "." (RepScript) + "./session-key" (route handler)
│ ├── src/
│ │ ├── index.ts # RepScript RSC — renders <script> in dev, null in prod
│ │ ├── session-key.ts # GET handler for app/api/rep/session-key/route.ts
│ │ ├── keys.ts # Module-scoped singleton for ephemeral keys
│ │ ├── payload.ts # Payload builder (shared with vite plugin)
│ │ ├── crypto.ts # AES-256-GCM, HMAC-SHA256, SRI, HKDF (shared with vite plugin)
│ │ ├── env.ts # Read env file + classify variables (shared with vite plugin)
│ │ ├── guardrails.ts # Shannon entropy + known format checks (shared with vite plugin)
│ │ └── __tests__/ # 13 tests
│ └── vitest.config.ts
├── codemod/ # @rep-protocol/codemod
│ └── src/transforms/ # CRA, Next.js, Vite transforms
Expand Down Expand Up @@ -280,6 +304,15 @@ Full threat analysis in `spec/SECURITY-MODEL.md`.
- **Synchronous init, lazy async.** SDK reads the DOM synchronously on import. SSE connects lazily on first `onChange()` call.
- **Named export + default namespace.** Both `import { get } from '@rep-protocol/sdk'` and `import { rep } from '@rep-protocol/sdk'` work.

### TypeScript (Vite & Next.js Plugins)

- **Crypto, payload, env, and guardrails modules mirror gateway Go logic.** Changes to `gateway/internal/crypto/crypto.go`, `gateway/pkg/payload/payload.go`, `gateway/internal/config/classify.go`, or `cli/src/utils/guardrails.ts` must be reflected in **both** `plugins/vite/src/` and `plugins/next/src/`. All three implementations (Go gateway, Vite plugin, Next.js plugin) must produce byte-identical JSON (sorted keys, Go HTML escaping of `<>&`).
- **`plugins/next/src/` shares `crypto.ts`, `env.ts`, `guardrails.ts`, `payload.ts` with `plugins/vite/src/`.** These files are currently copies. When modifying shared logic, update both locations.
- **`dotenv` is the only runtime dependency** for both plugins. Everything else uses `node:crypto` and `node:fs`.
- **Session key endpoints are simplified for dev.** No rate limiting or single-use enforcement — these are dev-only tools, not production gateways.
- **Next.js plugin: `RepScript` returns `null` in production.** The gateway handles injection in prod. `RepScript` only renders during `next dev` to avoid baking env vars into static HTML at build time.
- **Next.js plugin: session-key route handler returns 404 in production.** Prevents accidental exposure of an unhardened key endpoint alongside the gateway's rate-limited one.

### TypeScript (Testing)

- **Vitest + jsdom** across all TS packages.
Expand Down Expand Up @@ -316,6 +349,14 @@ pnpm build && pnpm test

# Adapters (from adapters/react/, adapters/vue/, adapters/svelte/)
pnpm build && pnpm test

# Vite Plugin (from plugins/vite/)
pnpm build # Build CJS + ESM + types → dist/
pnpm test # Run vitest (49 tests)

# Next.js Plugin (from plugins/next/)
pnpm build # Build CJS + ESM + types → dist/ (two entry points)
pnpm test # Run vitest (13 tests)
```

---
Expand Down
23 changes: 20 additions & 3 deletions docs/src/content/docs/examples/nextjs-proxy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ The Next.js server is never exposed directly. All traffic flows through the gate
- examples/nextjs-proxy/
- src/
- app/
- layout.tsx
- layout.tsx Includes RepScript for dev-time injection
- page.tsx Server component shell
- api/rep/session-key/
- route.ts Dev-only session key endpoint
- components/
- EnvDisplay.tsx Client component — reads REP vars
- IntegrityBanner.tsx Verifies payload was not modified in transit
Expand Down Expand Up @@ -192,14 +194,29 @@ See [`examples/nextjs-csr-embedded/k8s/`](https://github.com/ruachtech/rep/tree/
</TabItem>
</Tabs>

## CLI dev mode (no Docker)
## Local development

### With `@rep-protocol/next` (recommended)

The project includes `RepScript` in `app/layout.tsx` and a session-key route handler. No gateway needed for development:

```bash
cp .env.example .env.local # if you haven't already
npm run dev
```

Open `http://localhost:3000`. `RepScript` injects the REP payload during `next dev` and returns `null` in production.

### With the CLI gateway

If you need full gateway fidelity:

```bash
# Terminal 1 — run Next.js dev server
npm run dev # starts on :3000

# Terminal 2 — run REP gateway in proxy mode
npx @rep-protocol/cli dev --proxy http://localhost:3000 --env-file .env.local
npx @rep-protocol/cli dev --proxy http://localhost:3000 --env .env.local
```

Open `http://localhost:8080`.
26 changes: 15 additions & 11 deletions docs/src/content/docs/examples/todo-react.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,31 @@ A complete React todo app demonstrating all REP features. Source code at [`examp

## Running locally

### With the CLI
### With the Vite plugin (recommended)

The project includes `@rep-protocol/vite` in `vite.config.ts`. No gateway needed for development:

```bash
# From the repo root
cd examples/todo-react
pnpm install
pnpm dev
```

# Validate the manifest
cd examples/todo-react
npx @rep-protocol/cli validate
Open `http://localhost:5173`. The Vite plugin reads `.env.local` and injects the REP payload automatically.

### With the CLI gateway

# Generate TypeScript types
npx @rep-protocol/cli typegen
If you need full gateway fidelity (rate-limited session keys, CORS origin checking):

# Start Vite dev server
```bash
# Terminal 1: Start Vite dev server
pnpm dev

# In another terminal: start REP gateway proxy
npx @rep-protocol/cli dev --proxy http://localhost:5173
# Terminal 2: Start REP gateway proxy
pnpm dev:gateway
```

Open `http://localhost:8080` to see the app with REP-injected variables.
Open `http://localhost:3000` (the gateway port).

### With Docker

Expand Down
28 changes: 19 additions & 9 deletions docs/src/content/docs/frameworks/react.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -99,22 +99,32 @@ Both hooks subscribe to the gateway's SSE stream when `--hot-reload` is enabled.

## Development mode

Without the gateway running, `useRep()` returns `undefined`. Two approaches:
Without the gateway running, `useRep()` returns `undefined`. Three approaches:

<Tabs>
<TabItem label="Vite plugin (recommended)">
```bash
npm install -D @rep-protocol/vite
```

```ts
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { repPlugin } from '@rep-protocol/vite';

export default defineConfig({
plugins: [react(), repPlugin()],
});
```
Comment on lines +110 to +119

The plugin reads `.env.local` and injects the REP payload during `vite dev` — no gateway needed. Just run `npm run dev` as normal.
</TabItem>
<TabItem label="Default values">
```tsx
const apiUrl = useRep('API_URL', 'http://localhost:3000');
```
</TabItem>
<TabItem label="Mock payload">
Add to your `index.html` during development:
```html
<script id="__rep__" type="application/json">
{"public":{"API_URL":"http://localhost:3000","FEATURE_FLAGS":"dark-mode"},"_meta":{"version":"0.1.0","injected_at":"2026-01-01T00:00:00Z","integrity":"hmac-sha256:dev","ttl":0}}
</script>
```
</TabItem>
<TabItem label="CLI dev server">
```bash
# Terminal 1: Vite dev server
Expand Down
71 changes: 68 additions & 3 deletions docs/src/content/docs/guides/development.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,72 @@ import { Tabs, TabItem, Aside } from '@astrojs/starlight/components';

During local development you typically don't have the REP gateway running. The SDK handles this gracefully — `rep.get()` returns `undefined` when no payload is present.

## Option A: Default values (simplest)
## Option A: Build-tool plugin (recommended)

The easiest way to get full REP support in development. The plugin injects the same `<script id="__rep__">` tag that the gateway produces, so the SDK works identically.

<Tabs>
<TabItem label="Vite">
```bash
npm install -D @rep-protocol/vite
```

```ts
// vite.config.ts
import { defineConfig } from 'vite';
import { repPlugin } from '@rep-protocol/vite';

export default defineConfig({
plugins: [repPlugin()],
});
Comment on lines +20 to +27
```

The plugin reads `.env.local` (configurable via `env` option), classifies variables, encrypts sensitive vars, and injects the payload into your HTML. Changes to `.env.local` trigger a full page reload.

```bash
# Just run Vite as normal — no second terminal needed
npm run dev
```
</TabItem>
<TabItem label="Next.js">
```bash
npm install -D @rep-protocol/next
```

```tsx
// app/layout.tsx
import type { ReactNode } from 'react';
import { RepScript } from '@rep-protocol/next';

export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html>
<head><RepScript /></head>
<body>{children}</body>
</html>
);
}
```

```ts
// app/api/rep/session-key/route.ts (only needed for SENSITIVE vars)
export { GET } from '@rep-protocol/next/session-key';
```

`RepScript` is a React Server Component that renders the REP payload during `next dev`. It returns `null` in production — the gateway handles injection in prod. No build-time env vars are baked into the output.

```bash
# Just run Next.js as normal — no second terminal needed
npm run dev
```
</TabItem>
</Tabs>

<Aside>
Both plugins run guardrails on PUBLIC vars and warn if they look like secrets. Use the `strict` option to make warnings into errors.
</Aside>

## Option B: Default values (simplest)

Use the second argument to `rep.get()`:

Expand All @@ -19,7 +84,7 @@ const apiUrl = rep.get('API_URL', 'http://localhost:3000');

This is the simplest approach and works with any framework. No extra configuration needed.

## Option B: Mock payload in HTML
## Option C: Mock payload in HTML

Add a `<script id="__rep__">` tag to your `index.html`:

Expand Down Expand Up @@ -47,7 +112,7 @@ This gives you the full SDK experience (including `rep.verify()` and `rep.meta()
Don't forget to remove this tag from your production HTML. In production, the gateway injects it automatically.
</Aside>

## Option C: CLI dev server (full fidelity)
## 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:

Expand Down
Loading
Loading