# Vinext applications on Rogue

Use Vinext for a Next.js-compatible app that needs server rendering, API routes,
authentication, or SQL. Rogue runs the compiled app in its own **untrusted
Cloudflare Worker**, on Workers for Platforms. Source stays in the project's Git
repository; it can remain private while the app is public. Vinext is currently
beta: run its compatibility check and test the application's actual routes.

## Build locally

Follow the [Cloudflare Vinext guide](https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/).
For an existing Next.js project, review `npx vinext check` and `npx vinext init`.
Use Vinext with `@cloudflare/vite-plugin`; do not upload `.next` or a Node server.
A typical Vite configuration is:

```ts
import { defineConfig } from 'vite';
import vinext from 'vinext';
import { cloudflare } from '@cloudflare/vite-plugin';
export default defineConfig({ plugins: [vinext(), cloudflare({
  viteEnvironment: { name: 'rsc', childEnvironments: ['ssr'] }
})] });
```

Use `vinext build` to produce `dist/server/index.js`, its imported modules, and
`dist/client` assets. Run the result locally with Wrangler. Review dependencies
and scripts before executing a customer's source. Rogue does not run npm, build
source, accept arbitrary Wrangler bindings, or deploy on Git push.

## Optional project D1

Each project has zero or one D1 database. Create it only if the app needs SQL:

```sh
rog db create PROJECT_UUID --yes
rog db list PROJECT_UUID
rog db migrate PROJECT_UUID --remote --name 0001_initial --file migrations/0001_initial.sql --yes
```

Save the returned **Rogue database ID**, then bind it at app creation. Your Rogue
owner credential administers the database. The running app receives the `DB`
binding, with no owner token or Cloudflare management credential. Never put those
tokens into app environment variables. See [database administration](https://rogue.camp/guides/databases.md)
for imports, backups, local SQL, limits and the exact access model.

In server code, use `import { env } from 'cloudflare:workers'` and `env.DB`.
Drizzle's `drizzle-orm/d1` adapter works with this binding. Prisma's D1 adapter is
also supported: use the `prisma-client` generator with `runtime = "workerd"`,
`compilerBuild = "small"`, and `engineType = "client"`; instantiate
`new PrismaClient({adapter: new PrismaD1(env.DB)})` within the request. Keep the
emitted WebAssembly module in the build. Do not use Postgres drivers or share a
request's ORM/D1 instance globally. D1 is SQLite, so port PostgreSQL-specific SQL.

## Push, package and deploy

Install the official JavaScript client package from [Rogue clients](https://rogue.camp/clients)
to obtain `rog-vinext`. The native `rog` tool handles app and database operations.

```sh
# Once: create the app. Omit --database-id when SQL is not needed.
rog apps create PROJECT_UUID web --database-id DATABASE_UUID --yes

# Commit source, push to the project's returned Git clone URL, then build.
git add .
git commit -m 'Build Vinext application'
git push rogue HEAD:main
npm run build
rog-vinext pack --out .rogue/app.json
rog apps deploy web --file .rogue/app.json --yes
```

Ignore `.rogue/`, `.wrangler/`, `dist/`, generated ORM code and secret files in
Git. `pack` records a clean Git HEAD by default, packages existing output only,
and writes a new private file. The server verifies that commit in the project's
repository. The package contains ES modules, WebAssembly and static assets;
`.env`, tooling state, source maps, `_headers` and `_redirects` are excluded.
Express redirects, headers and middleware in the app itself. Configure external
server requests with a reviewed `egress_hosts` array in `rog apps create ...
--file app-config.json`; outbound networking is denied by default. Use the
secret-management tools for selected app secrets, never plaintext build files.

Deployment uploads an immutable revision, activates it after provider readback
and a sandboxed readiness probe, and publishes it. The response includes
`https://app-APP_UUID.rogue.camp`. This dedicated origin supports application
cookies, redirects, HTTP request bodies, and streamed SSR. The UUID URL
`https://rogue.camp/APP_UUID` redirects there. Pro custom domains attach to the
same **app UUID** through the [domain guide](https://rogue.camp/guides/custom-domains.md).
The publication MCP endpoint describes the app link; it does not invent tools
for the app's backend. An app can implement its own MCP route if needed.

After an uncertain upload or activation, inspect `rog apps versions web`; do
not repeat mutations automatically. To publish a retained version:

```sh
rog apps activate web 2 --yes
rog apps publish APP_UUID --yes
```

Activation and publication are separate. Code rollback preserves the database;
make migrations compatible with both versions or plan a separate data restore.
A readiness probe proves the Worker starts, not that every route works.

## Limits and verification

Read `lambda_limits` for current limits: HTTP apps currently have 1,000 ms CPU,
50 subrequests, 30 seconds wall time, 5 MiB request and 10 MiB response bodies.
Bundles are at most 25 MiB JSON, 512 modules and 2,000 assets; each file is at most
10 MiB. The provider's compressed Worker size/startup limits also apply. Ordinary
HTTP methods are supported; WebSocket upgrades and arbitrary long-running Node
processes are not. Every request that executes the app, including failures, counts
against a separate hosted-app allowance: 10,000 requests per Free owner per UTC
day, or 100,000 on Pro, shared by that owner’s apps. These are HTTP requests,
not visitors. Ordinary function calls have their own 100/1,000 allowance.
The platform admits up to 500,000 combined attempts per UTC day. These Rogue
budgets are separate from Cloudflare’s plan allowances. `rog bootstrap` reports
`quotas.invocations.http_apps` with used, limit, remaining and `resets_at`;
HTTP 429 includes `Retry-After`. D1 binding operations also consume
Cloudflare's SQL limits. A selected binding permits SQL in that database only.

Check GET responses and browser hydration, asset MIME types, deep-route reloads,
login/logout, sessions, redirects, API writes and D1 persistence after redeploy.
Use the app origin for authentication. Owner suspension, unpublishing, deleting
an app, or an invalid binding stops new requests at the trusted gateway. Keep
application passwords and application users inside the app's own authentication
system; Rogue's account is its deployment/database administrator.

## Static assets and response caches

New bundle uploads index each declared static file inside the original immutable
artifact. The gateway checks current publication, owner, project, domain and
maintenance state, then serves that file from an internal cache. On a miss it
reads only the indexed R2 byte range and verifies the decoded file's SHA-256.
No duplicate artifact object is stored. Range requests and ETags are supported.
These responses do not run the tenant Worker, load secrets or consume its app
execution allowance. Undeclared paths still execute the app and require atomic
quota admission; older revisions keep that path until a new bundle is uploaded.
Publication/revision changes select a different cache identity immediately.

The cache contains immutable bytes only. Every new request rechecks access, even
for HEAD and conditional requests. Responses stay `private, no-store`; a global
CDN cache must not bypass Rogue's revocation/maintenance gate. Previously
downloaded copies and in-flight responses cannot be recalled. Static responses
can remain available when the app's execution allowance is exhausted or its D1
binding is unavailable, because they execute no application or SQL code.

Apps may separately cache anonymous public HTML/JSON inside their isolated
Worker using a named Cache API cache. Keep cookies, authentication, RSC/action
variants and private routes out of shared output. Use a versioned cache key,
bounded response sizes, a finite TTL and explicit invalidation on mutations;
continue background synchronization on cache hits. Such requests still invoke
the Worker and consume the hosted-app allowance. App/revision/database checks
are joined, and admission plus durable invocation recording is one atomic
PostgreSQL call. Current bound secrets are still read before actual execution.
