Esposter

Configuration

The one committed on-disk artifact virrun reads in a consuming repo: virrun.config.{ts,mts,js,mjs,json} at the repo root, selecting which backend a sandboxed command runs through. It lives in version control so the backend choice is reviewable and revertible like any change. It does not decide whether a command is sandboxed — that is the prefix's job, per command (adoption).

The file

// virrun.config.ts — defineConfig is a typed identity from the tiny `virrun/config` subpath entry (never import the
// `virrun` barrel here: jiti transpiles a config file's imports on every `virrun -- <cmd>`, ~11 s vs ~0.4 s measured)
import { defineConfig } from "virrun/config";

export default defineConfig({
  backend: process.platform === "win32" ? "os" : "native", // BackendType: auto | native | vfs | os
  environment: "nuxt", // Environment?: nuxt (omit the key entirely for no preset — there is no "none" value)
});
  • Resolution — walk up from cwd to the first virrun.config.*, delegated to unconfig's loadConfigSync: nearest directory wins, and within a directory the candidate order is ts > mts > js > mjs > json. Absent file = the backend defaults to auto (native today). No config is a valid, fully-functional state.
  • Every field is optional in the file: parseVirrunConfiguration defaults backend → auto and leaves environment → undefined (no preset), so a minimal {} — or no file at all — is valid. Unknown keys throw (a typo fails loud, not silent), and the resolved VirrunConfiguration stays fully populated — optionality is the input surface, not the internal contract.
  • Strict JSON, not jsonc — the JSON variant is parsed with strict JSON.parse, so it carries no comments. Its field docs come from the shipped schema.json (referenced via $schema — the oxlint pattern), whose enums mirror BackendType/Environment in code. The TS/JS forms get their editor intelligence from types instead.
  • environment selects a framework preset whose generated artifacts (Nuxt's .nuxt) the sandbox regenerates into a source-keyed prepare layer (snapshot and fork), so type-aware tooling reads a platform-correct, fresh copy instead of the host's (os backend only). Preset-driven — no overrides; nuxt runs nuxt prepare for the git-detected nuxt package.
  • No allowlist, no on/off flag — there is no per-command matching; the prefix's presence is the sole opt-in, and the config applies the same backend to every command that carries it. Auto-fallback always degrades to native (the only universally-available backend); the degrade target is fixed, not a knob — a configurable target could itself be unsupported, reintroducing the very problem.

This repo's config

The committed root virrun.config.ts branches on process.platform: win32 → os (the platform whose host-generated .nuxt misfires Linux type-aware tooling, so sandboxed commands read the Linux-generated prepare layer) with the nuxt environment; everything else → native (Linux/CI generates platform-correct artifacts in place, so the sandbox would only add overhead). os degrades to native on hosts without bubblewrap or a WSL Linux Node.js, so the config is a safe no-op anywhere.

Key files

Paths relative to packages/virrun/src/.

FileRole
services/configuration/resolveVirrunConfiguration.tsdiscovery + loading via unconfig's loadConfigSync (jiti for TS/JS, strict JSON parse)
services/configuration/parseVirrunConfiguration.tsone zod schema validating + defaulting every format
services/configuration/resolvePrepareStep.tsresolve the environment preset to a { command, outputs } prepare step
virrun/config subpath entry (defineConfig)the ~1 kB typed author-facing helper — the only safe config-file import
schema.json (package files)editor hover docs for the JSON variant; enums mirror BackendType/Environment in code

Notes

  • Config is committed and reviewable; the cache is gitignored and disposable (cache) — the split is deliberate. The backend choice is code; materialized bytes are not.
  • unconfig is the package's one runtime dependency, externalized from the bundle because its sync TS loading resolves jiti relative to its own installed file.