# How to Handle Environment Variables in Pi-Web: Configuration Patterns Explained

> Master Pi-Web environment variable handling. Learn configuration patterns using process env, nullish coalescing, and error throwing for secure and robust applications.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: how-to-guide
- Published: 2026-08-09

---

**Pi-Web reads configuration values from the Node.js process environment using `process.env`, applying nullish coalescing fallbacks for optional settings and throwing explicit errors for required secrets.**

Pi-Web is a Next.js-based application that relies on environment variables for runtime configuration, security policies, and external service integration. Understanding how to handle environment variables in pi-web ensures your deployment remains secure, portable, and maintainable across development, staging, and production environments.

## Where Pi-Web Accesses Environment Variables

The codebase accesses `process.env` across several critical modules to configure authentication, security policies, and external API endpoints.

### Authentication and Host Security

The authentication layer reads sensitive credentials directly from the environment. In [`lib/web-auth.ts`](https://github.com/agegr/pi-web/blob/main/lib/web-auth.ts), the application retrieves the password protection setting:

```typescript
// lib/web-auth.ts
export const getPassword = (): string => {
  const pwd = process.env.PI_WEB_PASSWORD;
  if (!pwd) {
    throw new Error("Missing required env var PI_WEB_PASSWORD");
  }
  return pwd;
};

```

Host restrictions are configured in [`lib/request-security.ts`](https://github.com/agegr/pi-web/blob/main/lib/request-security.ts), which builds an allow-list from two environment variables:

```typescript
// lib/request-security.ts
export const allowedHosts = [
  process.env.PI_WEB_HOSTNAME,
  ...(process.env.PI_WEB_ALLOWED_HOSTS?.split(",") ?? []),
].filter(Boolean);

```

### External API Configuration

The skills integration layer uses `SKILLS_API_URL` to configure backend communication. In [`lib/skill-updates.ts`](https://github.com/agegr/pi-web/blob/main/lib/skill-updates.ts) and [`app/api/skills/search/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/skills/search/route.ts), the code provides a production-ready fallback:

```typescript
// lib/skill-updates.ts
const DEFAULT_SKILLS_API_BASE = process.env.SKILLS_API_URL ?? "https://skills.sh";

```

### Build-Time Public Variables

For client-side exposure, Pi-Web uses the `NEXT_PUBLIC_` prefix convention. The [`app/api/app-update/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/app-update/route.ts) file exposes the application version to the browser:

```typescript
// app/api/app-update/route.ts
const version = process.env.NEXT_PUBLIC_APP_VERSION;

```

### Git Operations Environment

When spawning git subprocesses in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) and [`lib/git-changes.ts`](https://github.com/agegr/pi-web/blob/main/lib/git-changes.ts), Pi-Web preserves the parent environment while injecting locale settings for consistent output parsing:

```typescript
// lib/worktree.ts
await exec("git", ["worktree", "add", /* ... */], {
  env: { ...process.env, LC_ALL: "C" },
});

```

## Configuration Patterns and Implementation

Pi-Web follows consistent patterns for environment variable handling that balance flexibility with reliability.

### Validating Required Variables

Critical configuration values trigger immediate failures with descriptive messages rather than silent undefined behavior. The `PI_WEB_PASSWORD` validation in [`lib/web-auth.ts`](https://github.com/agegr/pi-web/blob/main/lib/web-auth.ts) demonstrates this defensive approach, ensuring the application never starts in an insecure state.

### Providing Sensible Defaults

Optional configuration uses the nullish coalescing operator (`??`) to supply defaults. This pattern appears in the skills API configuration, where missing values default to the production service URL rather than causing runtime errors.

### Parsing Complex Values

Multi-value configuration leverages optional chaining and array spreading. The `PI_WEB_ALLOWED_HOSTS` variable accepts comma-separated hostnames, which [`lib/request-security.ts`](https://github.com/agegr/pi-web/blob/main/lib/request-security.ts) splits and filters to build a clean whitelist array.

### Scoped Environment Inheritance

Child processes receive explicitly scoped environments rather than blanket inheritance. The git execution helpers spread `process.env` only when necessary, then override specific keys like `LC_ALL` to ensure deterministic command output while maintaining access to user PATH and other essential variables.

## Security and Portability Benefits

Handling configuration through environment variables keeps sensitive values like `PI_WEB_PASSWORD` outside the source tree and version control. This approach enables the same Docker image or deployment artifact to run across different machines, CI pipelines, and cloud providers without code changes.

Feature toggles such as `NODE_ENV` guard development-only behaviors, as seen in [`lib/i18n/format.ts`](https://github.com/agegr/pi-web/blob/main/lib/i18n/format.ts) where console warnings appear only during development builds. This conditional logic prevents performance overhead and information leakage in production.

## Summary

- **Access Pattern**: Pi-Web reads variables via `process.env` throughout [`lib/web-auth.ts`](https://github.com/agegr/pi-web/blob/main/lib/web-auth.ts), [`lib/request-security.ts`](https://github.com/agegr/pi-web/blob/main/lib/request-security.ts), and API routes.
- **Validation Strategy**: Required variables throw explicit errors during startup; optional variables use nullish coalescing (`??`) for defaults.
- **Security Practice**: Sensitive values remain out of source code, while public values use the `NEXT_PUBLIC_` prefix for browser exposure.
- **Subprocess Handling**: Git commands in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) inherit the parent environment selectively using `{ ...process.env, LC_ALL: "C" }`.
- **Configuration Format**: Comma-separated lists in `PI_WEB_ALLOWED_HOSTS` are parsed into arrays for host whitelist validation.

## Frequently Asked Questions

### What happens if PI_WEB_PASSWORD is not configured?

The application throws a clear startup error. In [`lib/web-auth.ts`](https://github.com/agegr/pi-web/blob/main/lib/web-auth.ts), the code explicitly checks for the variable's presence and throws `new Error("Missing required env var PI_WEB_PASSWORD")` if undefined, preventing the server from running in an unprotected state.

### How do I allow multiple hostnames in pi-web?

Set the `PI_WEB_ALLOWED_HOSTS` environment variable to a comma-separated list of domains. The [`lib/request-security.ts`](https://github.com/agegr/pi-web/blob/main/lib/request-security.ts) module splits this string using `?.split(",")` and combines it with `PI_WEB_HOSTNAME` to build the complete whitelist.

### How are environment variables exposed to the browser?

Only variables prefixed with `NEXT_PUBLIC_` are sent to the client. For example, `NEXT_PUBLIC_APP_VERSION` is accessed in [`app/api/app-update/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/app-update/route.ts) to communicate build information to the frontend, while secrets like `PI_WEB_PASSWORD` remain server-side only.

### Why does pi-web spread `process.env` when running git commands?

The spread operator in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) (`env: { ...process.env, LC_ALL: "C" }`) preserves the user's PATH and other essential environment variables while overriding the locale to ensure consistent, parseable git output across different system configurations.