Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions apps/docs/deployment/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,42 @@ description: "Reference for Harly environment variables: required secrets, stora
`https://hiring.example.com`. Every secret must be independent — do not reuse
values between variables.

## `HARLY_URL` validation

Harly derives every public URL from `HARLY_URL` rather than the incoming
request origin. That includes upload presign URLs, authenticated file URLs,
OAuth callbacks, redirects, page metadata, outbound webhook payloads,
candidate notifications, and integration callbacks. A misconfigured value
therefore leaks into links your candidates and integrations receive, so Harly
validates it at startup and refuses to boot when it is unsafe.

In production (`NODE_ENV=production`), `HARLY_URL` must:

- Be an absolute URL with an `https://` scheme. Plain `http://` is rejected.
- Resolve to a routable public hostname. Harly rejects `localhost`, any
`*.localhost` subdomain, the IPv4 loopback range `127.0.0.0/8`, the IPv6
loopback `::1` (and `[::1]`), and the unspecified bind addresses `0.0.0.0`,
`::`, and `[::]`.
- Contain no userinfo, query string, or fragment. Only scheme, host, and
optional port are accepted.
Comment on lines +26 to +27

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Startup rejection is overstated

The startup configuration validator does not reject userinfo, query strings, or fragments as documented here; that stricter check occurs later in getHarlyPublicOrigin(), so an invalid value can pass startup configuration validation and fail only when an affected application path loads.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/docs/deployment/configuration.mdx
Line: 26-27

Comment:
**Startup rejection is overstated**

The startup configuration validator does not reject userinfo, query strings, or fragments as documented here; that stricter check occurs later in `getHarlyPublicOrigin()`, so an invalid value can pass startup configuration validation and fail only when an affected application path loads.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex


Outside production, `http://` is allowed so local development against
`http://localhost:3000` continues to work.

If validation fails, Harly throws at startup with a message such as
`HARLY_URL must use HTTPS in production.` or `HARLY_URL must use a reachable
public hostname, not a local or bind address.`. Fix the value in `.env` (or
your platform's secret manager) and restart the app.

<Warning>
Behind a reverse proxy, set `HARLY_URL` to the public hostname candidates
and integrations see — never the container's internal bind address. Values
like `http://0.0.0.0:3000` or `http://127.0.0.1:3000` are rejected in
production, and even when accepted they would produce broken links in
emails, webhooks, and OAuth callbacks. See
[proxy modes](/self-hosting/proxy-modes) for reverse-proxy setup.
</Warning>

## Required runtime values

```dotenv
Expand Down
10 changes: 10 additions & 0 deletions apps/docs/self-hosting/proxy-modes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ Point your existing certificate management (Certbot, an ACME client, or a load b
The Nginx block above is a standard reverse-proxy pattern, not a file shipped by Harly. Adapt it to your existing proxy configuration.
</Note>

### Set `HARLY_URL` to the public hostname

Harly builds upload presign URLs, authenticated file URLs, OAuth callbacks, redirects, page metadata, outbound webhooks, and integration callbacks from `HARLY_URL`, not from the incoming request. Set it to the public origin that candidates and integrations reach:

```dotenv
HARLY_URL=https://careers.example.com
```

Never point `HARLY_URL` at the container's internal bind address. In production, Harly rejects `HARLY_URL` values that use `http://` or resolve to `localhost`, the IPv4 loopback range `127.0.0.0/8`, `::1`, `0.0.0.0`, or `::`, and refuses to start. See [`HARLY_URL` validation](/deployment/configuration#harly-url-validation) for the full list of rules.

### Trust your reverse proxy for client IPs

By default, Harly reads the client IP from the rightmost hop in `X-Forwarded-For`. In `external` mode that hop is your reverse proxy, not the end user. Workspace IP allowlists, per-IP rate limits, and audit log `ipAddress` fields will all see the proxy's address instead of the real client until you tell Harly which peer to trust.
Expand Down
Loading