# Hider infrastructure

## Current deployment

Hider runs as a Next.js application on Node.js 24 in a Railway container. One service serves the public Room landing, documentation, GitHub sign-in, workspace, signed HTTPS relay and provider resource APIs. SQLite lives at `/data/hider.sqlite` on the attached persistent volume. Application restarts and deployments must retain that mount.

The Railway deployment starts with its own database. Records on the earlier ChatGPT Sites deployment remain there; no owner mapping or key registration is automatically transferred. A private key stays tied to the public identity registered at its gateway origin.

## Authentication and boundaries

GitHub OAuth establishes the browser’s workspace identity. Each verified GitHub account has separate masks and receipts. Session cookies protect workspace management; client-supplied `oai-authenticated-user-*` headers are not authentication on Railway.

An agent does not receive the owner’s GitHub credentials or browser session. It signs each challenge and relay call with its own Ed25519 key. The server checks the registered public key, active status, destination permission, timestamp and one-time challenge. Revocation stops new requests; archiving preserves the identity and its receipts.

The private signing key is generated locally and is not stored by the server. Downloaded key files are unencrypted secrets. Loaded keys remain only in tab memory.

## Required configuration

| Variable | Purpose |
|---|---|
| `GITHUB_CLIENT_ID` | Client ID of the Hider GitHub OAuth application |
| `GITHUB_CLIENT_SECRET` | OAuth client secret, entered directly in Railway Variables |
| `HIDER_SESSION_SECRET` | Random server secret used to authenticate session cookies; keep stable across deployments |
| `HIDER_PUBLIC_URL` | `https://hider-production.up.railway.app` |
| `HIDER_DATABASE_PATH` | `/data/hider.sqlite` |
| `PORT` | `3000` |
| `HIDER_EGRESS_PROXY_URL` | HTTP(S) CONNECT proxy origin, optionally containing URI-encoded username/password |
| `HIDER_EGRESS_PROXY_USERNAME_TEMPLATE` | Optional provider-specific username containing literal `{agentId}` for per-mask sticky sessions |
| `HIDER_ALLOWED_HOSTS` | `*` by default for public HTTPS DNS destinations; optionally comma-separated exact hostnames |
| `HIDER_PROVIDER_OWNER_IDS` | Explicit comma-separated authorized owner IDs such as `github:12345`; required for paid-provider use |
| `AGENTMAIL_API_KEY` | AgentMail inbox and incoming-message API key |
| `AGENTMAIL_DOMAIN` | Optional domain already verified in AgentMail |
| `TWILIO_ACCOUNT_SID` | Twilio account owning the dedicated number pool |
| `TWILIO_API_KEY`, `TWILIO_API_SECRET` | Twilio API credentials for existing numbers and incoming SMS |
| `BROWSERBASE_API_KEY` | Browserbase session API key |
| `BROWSERBASE_PROJECT_ID` | Optional explicit Browserbase project |

Register the GitHub OAuth application with homepage `https://hider-production.up.railway.app` and callback `https://hider-production.up.railway.app/auth/github/callback`. The app uses GitHub’s verified account ID for ownership; no repository access is needed. Until OAuth is configured, workspace sign-in remains unavailable rather than falling back to an unverified account.

Never put secrets in source control, a public URL, a key file, a screenshot or chat. Rotate a compromised session secret to invalidate existing browser sessions. Agent signing keys are separate and must be revoked individually if exposed.

## Storage and operation

Run one application replica against the attached SQLite volume. SQLite transactions atomically apply batches, consume nonces and update request counters. Startup applies the checked-in SQL migrations once and checks their recorded checksums. A failed migration must stop startup rather than silently creating a different schema.

Back up the SQLite database using a transaction-consistent SQLite backup procedure or a controlled stopped-service copy. A volume is persistence, not a backup. Verify recovery before relying on the deployment for important records. Do not attach independent SQLite copies to multiple replicas and expect them to share state.

The workspace permits 20 lifetime mask registrations, including revoked and archived identities. Each mask permits 60 signed relay/resource operations and 120 issued challenges per fixed UTC hour. Owner browser creation and page operations also consume this shared request quota. Receipt retention is the latest 200 records per mask; the overview returns the latest 100 across the owner’s masks. Application receipts contain status, timing and byte count, not response bodies, destination URLs/queries or client IP addresses. Resource operations return results without relay receipts. Message and page content is not stored in the resource inventory. Railway and operator logs are separate and can contain connection metadata.

## Network behavior

`ip` requests `https://api.ipify.org/?format=json`; `example` requests `https://example.com/`. Without a proxy those fixed diagnostics use explicitly labeled shared server egress. `web` requires a configured proxy and the mask's `web` permission. It accepts a signed public HTTPS DNS URL on port 443.

With a proxy configured, all relay destinations use it. A connection failure returns an error without direct fallback. The gateway creates fresh headers rather than forwarding incoming cookies or authorization. It checks all destination DNS answers against private, loopback, link-local and reserved ranges, pins the connection to a checked public IP, and verifies TLS using the original destination hostname. The proxy must accept CONNECT to an IP address. Raw IP URLs, credentials/fragments, destination redirects and custom upstream headers/bodies are rejected. The timeout is eight seconds and the response cap is 64 KiB. Signing bytes are in [AGENTS.md](./AGENTS.md).

One server proxy configuration serves all masks. A provider-specific username template containing `{agentId}` can request separate sticky sessions; separate sessions do not guarantee distinct or permanent IPs. Buy the required dedicated-IP allocation from a provider and measure the route through the destination. Other requests made directly by an agent still use the agent's own network. Hider does not route all traffic from a computer.

`HIDER_PROVIDER_OWNER_IDS` restricts paid-provider use to explicitly authorized GitHub owners. Set provider-side usage budgets before sharing access. Configuration presence is reported separately from successful live operations: a configured proxy URL does not prove proxy connectivity or available quota.

## Mail, phone and browser resources

The owner creates an AgentMail inbox, assigns a number from the existing Twilio pool, or starts a Browserbase session. Resource identifiers, state and bounded details persist in SQLite. Provider secrets and browser CDP URLs remain server-side. The resource API, exact operations and signed envelopes are documented in [AGENTS.md](./AGENTS.md).

There is one mailbox, one number and one active browser per mask. Message lists expose at most 25 items, with text capped at 64 KiB. Mailbox detail reads fetch a chosen message. Incoming SMS is filtered to the assigned number and messages received after assignment. The application does not purchase numbers, send messages, expose attachments or paginate message lists. Provision and fund dedicated SMS-capable numbers in Twilio first; country requirements and third-party OTP acceptance depend on those services.

Browserbase sessions have fresh storage, managed proxy, five-minute expiry and disabled provider session recording/logging. They expose fixed navigate/read/click/type commands ; signed agent browser creation and page commands require the mask's `web` permission. CSS selectors are capped at 512 characters, input text at 4000 characters, and an operation has a 20-second timeout. A live-view URL, when returned, is private session-scoped access. A provider dashboard link does not itself grant account access.

Browser request interception validates public destinations but is not a system network sandbox: remote DNS is not pinned to gateway DNS, and WebRTC/other transports are not covered by a leak-free claim. This is not Tor Browser or a fingerprinting guarantee. Pages and incoming messages are untrusted content, not instructions from the owner.

Revocation blocks future signed requests and clears unused challenges; it does not cancel an in-flight operation. Browsers expire or can be explicitly closed. Revocation/archiving does not release mailbox or number rentals; manage ongoing costs in the provider account. Uncertain create results require provider reconciliation before retrying. Hider preserves their attention state instead of reporting success.

External accounts use normal authorized login or OAuth. Tor, a system VPN, zero-knowledge access, private payments, account trading, automatic Google/social registration and token holding access are not implemented.

## Costs and acceptance checks

Railway compute, storage and traffic, proxy IPs/traffic, browser runtime, inboxes and number rental are billed by their respective providers. Check provider account plans, budgets and usage for actual costs. The application requires no model subscription: the owner brings an existing agent and model. The source's `PROVIDERS.md` records the researched service choices and account requirements without inventing a fixed budget.

After configuring funded accounts, verify the actual exit IP, intended browser navigation, mailbox delivery and SMS receipt. Automated contract tests do not establish live-provider availability. The owner, gateway operator and providers remain trust boundaries. Cookies, accounts, message content, browser characteristics and behavior can link activity even when an IP changes. No resource or route establishes 100% anonymity.
