API Server

Run Harper with an HTTP API for session access, review workflows, and local authenticated sign-in.

Config

Enable the server in your config.

[server]
enabled = true
host = "127.0.0.1"
port = 8081

Execution approval, execution strategy, and sandbox behavior are configured under [exec_policy]. You can edit them directly in config or select them from Settings -> Execution Policy in the TUI, including the command allow/block lists.

[exec_policy]
approval_profile = "allow_listed"   # strict | allow_listed | allow_all
execution_strategy = "auto"         # auto | grounded | deterministic | model
sandbox_profile = "disabled"        # disabled | workspace | networked_workspace
retry_max_attempts = 1

[ui]
header_widgets = ["model", "cwd", "strategy", "update"]

allow_listed preserves the existing default behavior for safe commands, but Harper still asks for approval when the command declares network access or writes outside the configured writable roots. strict requires approval for every command. allow_all skips approval checks. deterministic prefers direct grounded tool execution for supported intents, grounded prefers deterministic grounding first for routable repo questions and then allows model synthesis when needed, auto remains tool-assisted and can still fall back to deterministic handling for supported prompts, and model disables deterministic shortcuts. workspace enables sandboxing with workspace access and network disabled. networked_workspace enables the same workspace sandbox but allows network access.

header_widgets controls the persistent chat header. In Settings -> Execution Policy, Harper now exposes that field as both a checklist and a synchronized comma-separated text field, while saving still writes the selected widgets back to config/local.toml. That same screen also provides a direct Updates action, which refreshes the release manifest and the update widget in place. Supported values are session, plan, agents, web, auth, focus, model, cwd, strategy, approval, update, and activity. Direct updater installs are verified against both release checksums and detached signatures.

retry_max_attempts controls bounded automatic retries for retry-safe commands. You can also tune which command classes qualify:

[exec_policy]
retry_network_commands = ["curl", "wget"]
retry_write_commands = ["mkdir", "touch"]

Retries remain conservative: Harper still relies on declared command intent plus configured command classes, and it does not blindly retry networked or mutating commands outside those bounds.

For finer sandbox boundaries, add an explicit sandbox subsection:

[exec_policy.sandbox]
allowed_dirs = ["."]
writable_dirs = ["./tmp", "./build"]

allowed_dirs are readable roots. writable_dirs are writable roots. This lets you keep most of the workspace readable while only allowing writes in specific subpaths.

Deterministic repo routes

Harper now uses an explicit control path for repo work:

  • deterministic prefers direct grounded tool execution for supported intents.
  • grounded prefers deterministic grounding first for routable repo questions, then allows model synthesis when needed.
  • auto remains tool-assisted and can still degrade to deterministic fallback for supported prompts.
  • model disables deterministic shortcuts.

Examples of grounded routes include git status, git diff, repo identity, branch lookup, direct file reads, command follow-ups such as run that, and simple create/write prompts. Codebase prompts such as where is X used, what calls X, and where is X defined use a structured search helper with intent-aware ranking. Open-ended authoring prompts build authoring context, require plan/inspection before edits, and use deterministic fallback only when the model fails or is unavailable.

This keeps the execution path explicit and safe, while avoiding the older failure mode where a weaker local model would answer with generic prose instead of using the repo tools. If the model backend is unavailable and there is no deterministic fallback, Harper now returns a clear assistant reply instead of a raw API error.

Run Harper with the server enabled

Start Harper normally after enabling the server in config. If you only want the TUI, disable server startup with the flag below.

# TUI + server
cargo harper

# TUI only
cargo run -p harper-ui --bin harper -- --no-server

The short form cargo harper is now the normal local entry point. It starts the TUI and the local API server together, so auth commands and local API routes work without extra flags. If it fails with Address already in use, stop the old Harper process on port 8081 or change the local server port in config/local.toml.

Endpoints

  • GET /health - health check
  • GET /api/sessions - list sessions
  • GET /api/sessions/{id} - get session
  • GET /api/sessions/{id}/plan - fetch the current planner/runtime state only
  • GET /api/sessions/{id}/plan/stream - stream planner/runtime SSE updates
  • DELETE /api/sessions/{id} - delete
  • POST /api/chat - send message
  • POST /api/review - inline code review findings and fix suggestions
  • GET /auth/login/{provider} - browser auth entry point
  • GET /auth/tui/start/{provider} - start a TUI-owned auth flow
  • GET /auth/tui/flow/{flow_id} - poll TUI auth completion
  • GET /auth/callback - OAuth callback target
  • GET /auth/me - resolve the current signed-in user
  • GET /auth/status - browser status page backed by /auth/me
  • GET /auth/logout - clear Harper auth cookies

Test the server

# Health check
curl http://127.0.0.1:8081/health
# List sessions
curl http://127.0.0.1:8081/api/sessions

Health response

{"status":"ok","version":"0.12.0"}

Sessions response

[
  {
    "id": "9b548837-495e-4051-a11e-0a6f24d1a64f",
    "created_at": "2026-04-26 19:23:32",
    "updated_at": "2026-04-26 19:23:40",
    "title": "fix the tui spinner visibility"
  }
]

Chat example

curl -X POST http://127.0.0.1:8081/api/chat \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Review the latest session state",
    "session_id": "demo-session"
  }'

The session endpoint returns messages plus any active plan and resolved AGENTS context for that session. Session list items also include a human-readable title derived from the first user message when available. When Supabase auth is enabled, session list, load, delete, export, and statistics operations are scoped to the signed-in user.

Planner runtime API

Harper exposes planner/runtime state through a pull endpoint and a server-sent events stream. Use the pull endpoint for snapshots and the SSE endpoint when a client needs to follow running jobs incrementally.

# Planner/runtime snapshot
curl http://127.0.0.1:8081/api/sessions/demo-session/plan

# Planner/runtime SSE stream
curl -N http://127.0.0.1:8081/api/sessions/demo-session/plan/stream

The stream sends an initial plan event immediately, then emits new plan events whenever persisted planner state changes.

Delivery model:

  • Same-process updates are pushed through an in-memory broadcast channel for low-latency delivery.
  • Cross-process updates are observed through a persisted SQLite plan event journal, so separate Harper server processes sharing the same database still converge on the same stream state.

This is the current architectural boundary: cross-process delivery is journal-backed rather than powered by a separate external event bus.

Auth

Harper can use Supabase Auth for local browser or TUI sign-in. The current supported local pattern is GitHub-backed login through Supabase, with the authenticated session then available to the local API server.

[auth]
enabled = true

Keep the secret values in .env, not tracked config.

SUPABASE_URL=https://your-project-ref.supabase.co
SUPABASE_ANON_KEY=your-supabase-anon-key
SUPABASE_JWT_SECRET=your-supabase-jwt-secret
SUPABASE_REDIRECT_URL=http://127.0.0.1:8081/auth/callback
SUPABASE_ALLOWED_PROVIDERS=github

For browser auth, send the user to /auth/login/github. For the TUI flow, Harper uses /auth/tui/start/github and then polls /auth/tui/flow/{flow_id} until the session is ready.

# Current signed-in user
curl http://127.0.0.1:8081/auth/me

# Browser auth entry point
open http://127.0.0.1:8081/auth/login/github

/auth/me response

{
  "authenticated": true,
  "user": {
    "user_id": "00000000-0000-0000-0000-000000000000",
    "email": "user@example.com",
    "display_name": "Example User",
    "provider": "github"
  }
}

TUI auth commands

The TUI does not require a separate auth menu. Start a normal conversation and type one of the slash commands below into the message input.

/agents
/agents status
/agents on
/agents off
/auth login github
/auth status
/auth logout

Typing / opens the full slash-command list in the TUI. Use / or Tab to move through suggestions before submitting the command from chat.

/agents reports AGENTS context status for the current TUI session. /agents on and /agents off enable or disable AGENTS context resolution in the UI, and /agents status reports the current setting.

/auth login github starts a local flow, opens the browser, and then stores the resulting TUI session once Harper sees the completed callback. /auth status reports the local TUI auth state. /auth logout clears the local stored TUI session.

Successful TUI sign-in stores the local auth session in the system credential store (for example macOS Keychain) rather than a plain JSON token file. If you launch Harper with --no-server, these TUI auth commands are unavailable because the local auth callback and polling endpoints are disabled.

The same TUI session is also visible from Settings -> Profile, which shows the signed-in account and offers Logout and Refresh Session actions.

When a remote session request returns 401 Unauthorized, Harper automatically attempts one Supabase refresh-token exchange through the local server and retries the request once before surfacing the error.

Signed-in users see account-scoped data in History, Export, and Statistics. Press D or Delete in the TUI session lists to remove the selected session.

Review example

curl -X POST http://127.0.0.1:8081/api/review \
  -H "Content-Type: application/json" \
  -d '{
    "file_path": "src/main.rs",
    "language": "rust",
    "max_findings": 5,
    "instructions": "Focus on correctness and concrete fixes.",
    "content": "fn main() { let x = 1; let x = 2; }"
  }'

Response

{"summary":"Revised code to eliminate redundant variable declaration","findings":[
  {
    "title":"Redundant Variable Declaration",
    "severity":"error",
    "message":"The second assignment to `x` is unnecessary...",
    "range":{"start_line":1,"start_column":13,"end_line":1,"end_column":24},
    "suggestion":{
      "description":"Remove the redundant `let x = 2;` line",
      "replacement":"fn main() { let x = 2; }"
    }
  }
],"model":"llama3"}

Editor integration

Harper includes a VS Code extension scaffold that consumes /api/review and turns findings into diagnostics and quick fixes.

Run the review with Cmd+Shift+P → "Harper: Review Current File", or bind a custom shortcut:

{
  "key": "cmd+shift+r",
  "command": "harperReview.reviewCurrentFile"
}

View findings in Cmd+Shift+M (Problems view).

Verify diagnostics appear

After running "Harper: Review Current File", look for:

  • Status bar - Bottom-right corner of VS Code. You should see either:
    • $(warning) Harper N - warning icon with N findings the LLM found in this file
    • $(check) Harper Clean - green check if no issues found
    • $(error) Harper Review - red X if error occurred

    The number (N) changes for each file - it's the actual count of issues detected, not a fixed value.

  • Problems view - Press Cmd+Shift+M. You should see entries with:
    • Source: "Harper Review"
    • Error/Warning/Info icons based on severity
    • File path and line number
  • Quick fixes - In the editor:
    • Red/yellow/blue squiggly underlines on code with issues
    • Hover to see the finding message
    • Click the lightbulb icon or press Cmd+. to see fix options

Example findings include: unused variables, missing error handling, inefficient patterns, security issues, and more.

Rebuild VSIX

cd extensions/harper-review-vscode
vsce package --out harper-review.vsix

Then reinstall: Extensions...Install from VSIX...

Troubleshooting

Server not running

curl http://127.0.0.1:8081/health
# {"status":"ok","version":"0.12.0"}

If it fails, start the server: cargo run -p harper-ui --bin harper

Extension shows "fetch failed"

  • Check server is running: curl http://127.0.0.1:8081/health
  • Reload VS Code: Cmd+Shift+P → "Developer: Reload Window"
  • Check port in settings: Cmd+, → search "harperReview.serverUrl"

Port already in use

If you get "Address already in use", stop the existing process or pick a different port:

# Change the port in config/local.toml:
[server]
port = 8082

Change port

If you move the server off 8081, update the local config first, then make sure any editor extension or scripted client points at the same host and port.