Pi-Web HTTP Basic Auth Security Model: How Remote Access Protection Works

Pi-Web uses a two-layer security model that combines host-trust validation with optional HTTP Basic Authentication, where the username is hard-coded to "pi" and the password is set via the PI_WEB_PASSWORD environment variable.

The pi-web repository (agegr/pi-web) implements a lightweight but robust security layer for all incoming HTTP requests. Whether accessing the web UI or calling API endpoints, remote clients must pass both host-based trust checks and—when configured—HTTP Basic Auth validation. This article breaks down the complete security flow implemented in the source code.

Layer 1: Host-Trust Validation

Before any authentication occurs, the proxy middleware validates whether the requesting host is trusted. This happens in [lib/request-security.ts](https://github.com/agegr/pi-web/blob/main/lib/request-security.ts).

The trust logic splits traffic into two categories:

  • API routes: Allowed only from hosts passing isApiRequestAllowed
  • Non-API routes (UI pages): Allowed only if the host satisfies isApiRequestHostAllowed

Untrusted requests receive an immediate 403 Forbidden response. This host-gating prevents unauthorized network scanning from reaching the authentication layer entirely.

Layer 2: Password Protection via HTTP Basic Auth

When PI_WEB_PASSWORD is defined and non-empty, pi-web enables password protection. The password gate is controlled by isWebPasswordEnabled in [lib/web-auth.ts](https://github.com/agegr/pi-web/blob/main/lib/web-auth.ts).

Authentication Requirements

Every request must include a valid Authorization: Basic … header with these constraints:

Component Value
Username Hard-coded to "pi" (constant PI_WEB_AUTH_USERNAME)
Password Must exactly match PI_WEB_PASSWORD environment variable
Header format Basic <base64-encoded-credentials>

Secure Validation with Timing-Safe Comparison

The isValidBasicAuthorization function in web-auth.ts performs validation:

// Pseudocode based on source implementation
function isValidBasicAuthorization(header: string): boolean {
  // 1. Parse "Basic <base64>" format
  // 2. Safely decode Base64 payload
  // 3. Split into username:password
  // 4. Compare using crypto.timingSafeEqual on SHA-256 hashes
}

Malformed payloads are rejected. Valid credentials are compared using crypto.timingSafeEqual on SHA-256 hashes, eliminating timing-attack vulnerabilities.

Authentication Failure Responses

Pi-web returns distinct HTTP status codes for different failure modes:

  • 403 Forbidden: Host failed trust validation (blocked before auth)
  • 401 Unauthorized: Password required but missing or invalid, with header:
    
    WWW-Authenticate: Basic realm="Pi Web", charset="UTF-8"
    

This header prompts browsers to display a native login dialog.

Enabling and Using HTTP Basic Auth

Configure Password Protection


# In your environment or .env file

export PI_WEB_PASSWORD="s3cr3tP@ss"

# Start the server

npm run dev

When PI_WEB_PASSWORD is unset or empty, pi-web runs without authentication—useful for local development.

Authenticated Requests with curl

Access the UI:

curl -u pi:s3cr3tP@ss http://localhost:30141/

Call an API endpoint:

curl -u pi:s3cr3tP@ss http://localhost:30141/api/sessions

The -u flag automatically encodes credentials into the required Authorization: Basic … header.

Key Source Files

File Security Role
[lib/web-auth.ts](https://github.com/agegr/pi-web/blob/main/lib/web-auth.ts) Username/password constants, password-enabled check, timing-safe validation
[proxy.ts](https://github.com/agegr/pi-web/blob/main/proxy.ts) Middleware orchestrating trust checks, password gate, and 401/403 responses
[lib/request-security.ts](https://github.com/agegr/pi-web/blob/main/lib/request-security.ts) Host-trust definitions: isApiRequestAllowed, isApiRequestHostAllowed

Summary

  • Pi-web HTTP Basic Auth security model combines host-trust validation with optional password protection
  • Host trust is enforced first; untrusted clients receive 403 before reaching authentication
  • Password protection activates when PI_WEB_PASSWORD is set; username is always "pi"
  • Credentials are validated with crypto.timingSafeEqual on SHA-256 hashes to prevent timing attacks
  • Failed authentication returns 401 with WWW-Authenticate header prompting credential entry
  • Three files implement the complete security stack: request-security.ts, web-auth.ts, and proxy.ts

Frequently Asked Questions

What happens if I don't set PI_WEB_PASSWORD?

Pi-web runs without authentication. All requests from trusted hosts proceed directly to the application. This mode is intended for local development only.

Can I change the username from "pi" to something else?

No. The username "pi" is hard-coded as the constant PI_WEB_AUTH_USERNAME in lib/web-auth.ts. Only the password is configurable.

Why does pi-web use SHA-256 with timingSafeEqual instead of direct string comparison?

Direct string comparison (===) short-circuits on first mismatch, leaking timing information that attackers can exploit. SHA-256 hashing ensures both values have identical length, and crypto.timingSafeEqual performs a constant-time comparison regardless of where differences occur.

How do I troubleshoot 403 vs 401 errors?

A 403 Forbidden indicates host-trust failure—check lib/request-security.ts for your client's IP or origin. A 401 Unauthorized means you reached the authentication layer but provided missing or invalid credentials; verify your PI_WEB_PASSWORD value and header encoding.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →