Skip to content
Merged
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
4 changes: 2 additions & 2 deletions docs/clients/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ <h2>Transport Choices</h2>
<li><strong>SSE (<code>/sse</code> + <code>/messages</code>)</strong> — legacy transport; needed for the claude.ai web connector and older MCP clients.</li>
</ul>

<p>Both transports share the same tools, sessions, and OAuth flow. Clients without a bearer token pass through; invalid bearers are rejected with 401.</p>
<p>Both transports share the same tools, sessions, and OAuth flow. Clients without a bearer token pass through; invalid bearers are rejected with 401. As of v1.15.1, the <code>WWW-Authenticate</code> challenge on a 401 includes RFC 9728 <code>resource_metadata="…/.well-known/oauth-protected-resource"</code> plus <code>error="invalid_token"</code> per RFC 6750 §3.1 — MCP clients can use the <code>resource_metadata</code> attribute to discover the authorization server.</p>

<h2>Client Configurations</h2>

Expand Down Expand Up @@ -295,7 +295,7 @@ <h3>Claude Code</h3>
<div class="client-card">
<h3>Claude.ai (Web Connector)</h3>
<div class="config-path">Settings &rarr; Connectors &rarr; Add custom connector</div>
<p>claude.ai uses the legacy SSE transport. Paste your Pathfinder URL into the custom connector dialog, then click <strong>Authenticate</strong> — the OAuth handshake runs automatically and the connector is approved anonymously.</p>
<p>claude.ai uses the legacy SSE transport. Paste your Pathfinder URL into the custom connector dialog, then click <strong>Authenticate</strong> — the OAuth handshake runs automatically. As of v1.15, the handshake shows a consent screen confirming the connector and its redirect target before issuing the auth code; the screen is single-click for the anonymous DCR flow.</p>
<div class="code-block"><span class="comment"># Streamable HTTP (newer clients)</span>
<span class="key">URL:</span> <span class="value">https://your-pathfinder-host/mcp</span>

Expand Down
14 changes: 14 additions & 0 deletions docs/config/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1615,6 +1615,20 @@ <h3 id="auth-endpoints">OAuth endpoints</h3>
</li>
</ul>

<h3 id="consent-screen">Consent screen and abuse defense (v1.15+)</h3>
<p>
<code>GET /authorize</code> renders a server-rendered consent screen instead of auto-approving. The screen carries an HMAC-bound nonce; the form posts back to <code>POST /authorize/consent</code>, which re-verifies every bound parameter (<code>client_id</code>, <code>redirect_uri</code>, <code>state</code>, <code>code_challenge</code>, <code>response_type</code>, <code>scope</code>, <code>resource</code>) before issuing an auth code. The final redirect URL is built from the nonce-bound <code>redirect_uri</code>, never the form body — this is the phishing-resistance invariant.
</p>
<p>
The page is locked down with <code>Content-Security-Policy: default-src 'none'; frame-ancestors 'none'</code>, <code>X-Frame-Options: DENY</code>, and <code>Referrer-Policy: no-referrer</code>. No JavaScript, no external resources.
</p>
<p>
<code>POST /register</code> applies a <strong>redirect_uri policy</strong>: <code>https</code>-only except for loopback (<code>localhost</code>, <code>127.0.0.0/8</code>, <code>[::1]</code>); rejects <code>0.0.0.0/8</code>, RFC 1918, link-local, ULA, IPv4-mapped private addresses, wildcards, userinfo, and fragments. Maximum 2048 chars per URI, 10 URIs per client.
</p>
<p>
Registered clients are capped at <strong>10,000 total / 100 per-IP</strong> with lazy TTL eviction (30 days unused or 7 days never-used-after-registration). Overflow returns <code>429</code> (per-IP) or <code>503</code> (total) with a <code>Retry-After</code> header.
</p>

<h3 id="auth-bearer">
Bearer auth on <code>/mcp</code> and <code>/sse</code>
</h3>
Expand Down
4 changes: 4 additions & 0 deletions docs/deploy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ <h2>Docker Compose (full stack)</h2>
<span class="key">GITHUB_TOKEN:</span> <span class="value">${GITHUB_TOKEN:-}</span>
<span class="key">GITHUB_WEBHOOK_SECRET:</span> <span class="value">${GITHUB_WEBHOOK_SECRET:-}</span>
<span class="key">MCP_JWT_SECRET:</span> <span class="value">${MCP_JWT_SECRET}</span>
<span class="key">PATHFINDER_CONSENT_HMAC_KEY:</span> <span class="value">${PATHFINDER_CONSENT_HMAC_KEY}</span>
<span class="key">PATHFINDER_CONFIG:</span> <span class="value">/app/pathfinder.yaml</span>
<span class="key">WORKSPACE_DIR:</span> <span class="value">/data/workspaces</span>
<span class="key">PORT:</span> <span class="value">${PORT:-3001}</span>
Expand Down Expand Up @@ -356,6 +357,8 @@ <h3>systemd service</h3>
Environment=PORT=3001
Environment=DATABASE_URL=postgresql://pathfinder:secret@localhost:5432/pathfinder
Environment=OPENAI_API_KEY=sk-...
Environment=MCP_JWT_SECRET=...
Environment=PATHFINDER_CONSENT_HMAC_KEY=...
Environment=PATHFINDER_CONFIG=/opt/pathfinder/pathfinder.yaml
Environment=WORKSPACE_DIR=/var/lib/pathfinder/workspaces
Restart=always
Expand Down Expand Up @@ -400,6 +403,7 @@ <h2>Environment Variables</h2>
<tbody>
<tr><td><code>DATABASE_URL</code></td><td>For search tools</td><td>-</td><td>PostgreSQL connection string (with pgvector)</td></tr>
<tr><td><code>MCP_JWT_SECRET</code></td><td><strong>Required in production</strong></td><td>-</td><td>HMAC secret for signing OAuth access/refresh tokens. Generate with <code>openssl rand -hex 32</code>. Rotating this invalidates all issued tokens — clients will re-authenticate transparently. In any non-production environment (any <code>NODE_ENV</code> other than <code>production</code>) a random secret is generated per process and logged as a warning.</td></tr>
<tr><td><code>PATHFINDER_CONSENT_HMAC_KEY</code></td><td><strong>Required in production</strong></td><td>-</td><td>HMAC secret for signing the consent-screen nonce on the OAuth <code>/authorize</code> → <code>/authorize/consent</code> flow. Generate with <code>openssl rand -hex 32</code>. Comma-separated values are accepted for rotation — the first key signs, all keys verify; rotate by prepending a new key. Rotating invalidates only in-flight consent nonces (10-minute TTL) — issued access/refresh tokens are unaffected. In any non-production environment (any <code>NODE_ENV</code> other than <code>production</code>) a random ephemeral key is generated per process and logged as a warning.</td></tr>
<tr><td><code>OPENAI_API_KEY</code></td><td>When embedding.provider is "openai" (default)</td><td>-</td><td>OpenAI API key for computing embeddings. Not needed for ollama or local providers.</td></tr>
<tr><td><code>GITHUB_TOKEN</code></td><td>For private repos</td><td>-</td><td>GitHub PAT for cloning private repositories</td></tr>
<tr><td><code>GITHUB_WEBHOOK_SECRET</code></td><td>For webhooks</td><td>-</td><td>Secret for validating GitHub webhook payloads</td></tr>
Expand Down