mirror of
https://github.com/volcengine/OpenViking.git
synced 2026-09-28 19:53:23 +08:00
docs: add public access guide + default port 1934 aggregated proxy
- Add Caddyfile with :1934 HTTP aggregated proxy (merges 1933+8020)
- Enable Caddy service by default in docker-compose.yml on port 1934
- Add docs/{en,zh}/guides/12-public-access.md with full HTTPS setup guide
- Simplify 11-oauth.md: replace inline reverse proxy config with refs to 12
- Add HTTPS requirement callout to OAuth recommended setup
- Update 03-deployment.md to mention port 1934 as recommended entry point
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# OpenViking aggregated reverse proxy
|
||||
#
|
||||
# Port 1934 merges the API server (1933) and Console (8020) under one origin.
|
||||
# This block always serves plain HTTP — suitable for local dev, internal
|
||||
# networks, and as an upstream behind your own TLS-terminating proxy.
|
||||
#
|
||||
# To add public HTTPS, append a domain block below (Caddy auto-provisions
|
||||
# Let's Encrypt certs):
|
||||
#
|
||||
# {$OPENVIKING_PUBLIC_BASE_URL} {
|
||||
# @console path /console /console/*
|
||||
# handle @console {
|
||||
# reverse_proxy openviking:8020
|
||||
# }
|
||||
# handle {
|
||||
# reverse_proxy openviking:1933
|
||||
# }
|
||||
# # Optional: pin ACME email
|
||||
# # tls {$OV_ACME_EMAIL}
|
||||
# }
|
||||
#
|
||||
# Then expose ports 80/443 in docker-compose.yml and set
|
||||
# OPENVIKING_PUBLIC_BASE_URL=https://your-domain.com in .env.
|
||||
|
||||
:1934 {
|
||||
@console path /console /console/*
|
||||
handle @console {
|
||||
reverse_proxy openviking:8020
|
||||
}
|
||||
handle {
|
||||
reverse_proxy openviking:1933
|
||||
}
|
||||
}
|
||||
+35
-46
@@ -1,34 +1,30 @@
|
||||
version: "3.8"
|
||||
|
||||
# Set OPENVIKING_PUBLIC_BASE_URL in a `.env` file next to this compose file —
|
||||
# both services read the same variable, so the domain is configured once.
|
||||
# Set OPENVIKING_PUBLIC_BASE_URL in a `.env` file next to this compose file
|
||||
# when you need public HTTPS (OAuth, MCP clients on the internet).
|
||||
#
|
||||
# .env example for local-only on 127.0.0.1
|
||||
# (leave the variable empty or unset)
|
||||
# .env example for local-only (leave unset or empty):
|
||||
# # no .env needed — just `docker compose up -d`
|
||||
#
|
||||
# .env example for public HTTPS deployment with Caddy
|
||||
# .env example for public HTTPS:
|
||||
# OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com
|
||||
# OV_ACME_EMAIL=admin@your-domain.com # optional, for Let's Encrypt
|
||||
# OV_ACME_EMAIL=admin@your-domain.com
|
||||
#
|
||||
# When OPENVIKING_PUBLIC_BASE_URL is unset, the server falls back to
|
||||
# request-header inference (works for direct localhost connections).
|
||||
# See docs/en/guides/12-public-access.md for the full walkthrough.
|
||||
|
||||
services:
|
||||
openviking:
|
||||
image: ghcr.io/volcengine/openviking:latest
|
||||
container_name: openviking
|
||||
# Direct access to individual services (optional — caddy:1934 is the
|
||||
# recommended single entry point). Comment these out once Caddy is your
|
||||
# only ingress.
|
||||
ports:
|
||||
- "1933:1933"
|
||||
- "8020:8020"
|
||||
volumes:
|
||||
# All persistent state (ov.conf, ovcli.conf, workspace) lives here.
|
||||
- ~/.openviking:/app/.openviking
|
||||
environment:
|
||||
# Public-facing URL the server publishes to MCP clients (issuer in
|
||||
# OAuth metadata, host in WWW-Authenticate hints, base URL the
|
||||
# `add_resource` tool tells agents to upload to). Behind a reverse
|
||||
# proxy you should set this explicitly — request-header inference
|
||||
# only works if the proxy chain forwards X-Forwarded-Host correctly.
|
||||
OPENVIKING_PUBLIC_BASE_URL: ${OPENVIKING_PUBLIC_BASE_URL:-}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:1933/health || exit 1"]
|
||||
@@ -37,41 +33,34 @@ services:
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
restart: unless-stopped
|
||||
# If you need to override the default command (which runs openviking-server),
|
||||
# you can do so here. For example, to run the CLI:
|
||||
# command: ["openviking", "--help"]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Optional Caddy reverse proxy with automatic HTTPS via Let's Encrypt.
|
||||
# Aggregated reverse proxy — merges API (1933) + Console (8020) on port
|
||||
# 1934 under one origin. Always enabled, plain HTTP by default.
|
||||
#
|
||||
# Required for OAuth-only MCP clients (Claude.ai, Claude Desktop, ChatGPT,
|
||||
# Cursor, …) which only accept https:// MCP server URLs. To enable:
|
||||
#
|
||||
# 1. Set OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com in `.env`.
|
||||
# 2. Drop a Caddyfile next to this compose file (template in
|
||||
# docs/en/guides/11-mcp-oauth.md). Caddy reads the URL from the
|
||||
# OPENVIKING_PUBLIC_BASE_URL env var via {$OPENVIKING_PUBLIC_BASE_URL}.
|
||||
# 3. Point your DNS A/AAAA record at this host's public IP.
|
||||
# 4. Uncomment the caddy service + the `volumes:` block at the bottom.
|
||||
# 5. `docker compose up -d`. ACME issuance happens on the first request.
|
||||
# ---------------------------------------------------------------------------
|
||||
# caddy:
|
||||
# image: caddy:2
|
||||
# container_name: openviking-caddy
|
||||
# restart: unless-stopped
|
||||
# ports:
|
||||
# - "80:80"
|
||||
# - "443:443"
|
||||
# volumes:
|
||||
# - ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
# - caddy_data:/data
|
||||
# - caddy_config:/config
|
||||
# environment:
|
||||
# OPENVIKING_PUBLIC_BASE_URL: ${OPENVIKING_PUBLIC_BASE_URL}
|
||||
# OV_ACME_EMAIL: ${OV_ACME_EMAIL:-}
|
||||
# depends_on:
|
||||
# - openviking
|
||||
# For public HTTPS: edit the Caddyfile to add a domain block, set
|
||||
# OPENVIKING_PUBLIC_BASE_URL in .env, uncomment the 80/443 port lines
|
||||
# below, and add the caddy volumes at the bottom.
|
||||
caddy:
|
||||
image: caddy:2
|
||||
container_name: openviking-caddy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "1934:1934"
|
||||
# Uncomment for public HTTPS (after adding a domain block to Caddyfile):
|
||||
# - "80:80"
|
||||
# - "443:443"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
# Uncomment for public HTTPS (Caddy stores certs here):
|
||||
# - caddy_data:/data
|
||||
# - caddy_config:/config
|
||||
environment:
|
||||
OPENVIKING_PUBLIC_BASE_URL: ${OPENVIKING_PUBLIC_BASE_URL:-}
|
||||
OV_ACME_EMAIL: ${OV_ACME_EMAIL:-}
|
||||
depends_on:
|
||||
- openviking
|
||||
|
||||
# Uncomment for public HTTPS:
|
||||
# volumes:
|
||||
# caddy_data:
|
||||
# caddy_config:
|
||||
|
||||
@@ -276,8 +276,11 @@ docker compose up -d
|
||||
```
|
||||
|
||||
After startup, you can access:
|
||||
- API service: `http://localhost:1933`
|
||||
- Console UI: `http://localhost:8020`
|
||||
- Aggregated entry point: `http://localhost:1934` (Caddy merges API + Console)
|
||||
- API service (direct): `http://localhost:1933`
|
||||
- Console UI (direct): `http://localhost:8020`
|
||||
|
||||
For public HTTPS access, see the [Public Access Guide](12-public-access.md).
|
||||
|
||||
To build the image yourself, pass an explicit OpenViking version:
|
||||
`docker build --build-arg OPENVIKING_VERSION=0.3.12 -t openviking:latest .`
|
||||
@@ -314,6 +317,8 @@ Use `/health` for Kubernetes liveness probes and `/ready` for readiness probes.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Public Access & Reverse Proxy](12-public-access.md) - HTTPS, Caddy, nginx
|
||||
- [Authentication](04-authentication.md) - API key setup
|
||||
- [OAuth Guide](11-oauth.md) - OAuth 2.1 for MCP clients
|
||||
- [Observability & Diagnostics](05-observability.md) - Health checks, tracing, and debugging
|
||||
- [API Overview](../api/01-overview.md) - Complete API reference
|
||||
|
||||
+39
-202
@@ -10,46 +10,24 @@ work just as well.
|
||||
|
||||
## Recommended setup
|
||||
|
||||
This is the minimum config that works end to end with Claude.ai and similar
|
||||
public clients. You'll need a public domain, ports 80/443 reachable, and
|
||||
docker compose.
|
||||
> **Prerequisite**: public HTTPS. OAuth 2.1 (and the MCP SDK) **requires
|
||||
> HTTPS** for any non-localhost issuer. See the
|
||||
> [Public Access Guide](12-public-access.md) for how to set up HTTPS with
|
||||
> Caddy or nginx.
|
||||
|
||||
1. **Create `.env` next to `docker-compose.yml`:**
|
||||
1. **Set up HTTPS** — follow [Public Access Guide](12-public-access.md) to
|
||||
get `https://ov.your-domain.com` working (Caddy + `.env` +
|
||||
`docker compose up`).
|
||||
|
||||
```dotenv
|
||||
OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com
|
||||
OV_ACME_EMAIL=admin@your-domain.com # optional; recommended for Let's Encrypt
|
||||
```
|
||||
|
||||
2. **Drop a `Caddyfile` next to `docker-compose.yml`:**
|
||||
|
||||
```caddyfile
|
||||
{$OPENVIKING_PUBLIC_BASE_URL} {
|
||||
@console path /console /console/*
|
||||
handle @console {
|
||||
reverse_proxy openviking:8020
|
||||
}
|
||||
handle {
|
||||
reverse_proxy openviking:1933
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Enable OAuth in `~/.openviking/ov.conf`** (the server otherwise ignores
|
||||
the env var and the `/oauth/*` routes don't mount):
|
||||
2. **Enable OAuth in `~/.openviking/ov.conf`:**
|
||||
|
||||
```json
|
||||
{ "oauth": { "enabled": true } }
|
||||
```
|
||||
|
||||
4. **Uncomment the `caddy:` service and the `volumes:` block** at the bottom
|
||||
of `docker-compose.yml`, point DNS at this host, then:
|
||||
3. **Restart** (`docker compose restart openviking`).
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
5. **Connect a client.** In Claude.ai → Connectors → Add, enter
|
||||
4. **Connect a client.** In Claude.ai → Connectors → Add, enter
|
||||
`https://ov.your-domain.com/mcp`. The browser will open an authorize page
|
||||
that displays a 6-character code; open
|
||||
`https://ov.your-domain.com/console`, sign in with your API key, paste
|
||||
@@ -57,8 +35,8 @@ docker compose.
|
||||
browser flips back to Claude.ai and the connector is live.
|
||||
|
||||
That's the whole production path. The rest of this guide explains why each
|
||||
piece exists, how to do it without docker compose, and how to verify with
|
||||
curl when something doesn't work.
|
||||
piece exists, how to test locally without HTTPS, and how to verify with curl
|
||||
when something doesn't work.
|
||||
|
||||
---
|
||||
|
||||
@@ -133,7 +111,13 @@ endpoints, so this mode is only useful for local testing with tools like
|
||||
}
|
||||
```
|
||||
|
||||
2. **Start the API server (port 1933) and the console (port 8020):**
|
||||
2. **Start the services:**
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Or without Docker:
|
||||
|
||||
```bash
|
||||
openviking-server
|
||||
@@ -141,182 +125,35 @@ endpoints, so this mode is only useful for local testing with tools like
|
||||
python -m openviking.console.bootstrap --write-enabled
|
||||
```
|
||||
|
||||
3. **Sign in to the console** at <http://127.0.0.1:8020/console>, paste your
|
||||
API key into Settings, click Save. The "Authorize an MCP client" form is
|
||||
now available.
|
||||
3. **Sign in to the console** at <http://127.0.0.1:1934/console> (or
|
||||
`:8020/console` if running without the aggregated proxy), paste your API
|
||||
key into Settings, click Save. The "Authorize an MCP client" form is now
|
||||
available.
|
||||
|
||||
4. **Connect a local MCP client** (e.g. MCP Inspector) to
|
||||
`http://127.0.0.1:1933/mcp`. The client will hit the OAuth flow above; copy
|
||||
the 6-character code from the authorize page into the console form, click
|
||||
Authorize, and the client will receive a token.
|
||||
`http://127.0.0.1:1934/mcp` (or `:1933/mcp`). The client will hit the
|
||||
OAuth flow above; copy the 6-character code from the authorize page into
|
||||
the console form, click Authorize, and the client will receive a token.
|
||||
|
||||
For Claude.ai / Claude Desktop, continue to the production deployment below.
|
||||
For Claude.ai / Claude Desktop on the public internet, see the
|
||||
[Public Access Guide](12-public-access.md).
|
||||
|
||||
---
|
||||
|
||||
## Production deployment (HTTPS)
|
||||
|
||||
For OAuth clients on the public internet you need:
|
||||
OAuth 2.1 **requires HTTPS** for any non-localhost issuer. The
|
||||
[Public Access Guide](12-public-access.md) covers the full setup — Caddy,
|
||||
nginx, docker compose, CDN — in detail. The short version:
|
||||
|
||||
1. A public domain pointing at your server (`my.ov` in the examples below)
|
||||
2. TLS termination at a reverse proxy (Caddy or nginx)
|
||||
3. Both `openviking-server` (1933) and the console (8020) running locally
|
||||
4. The reverse proxy fronting them on the same domain so the OAuth page and
|
||||
the console can share a session origin
|
||||
1. Follow [Public Access Guide § Adding HTTPS](12-public-access.md#adding-https-for-public-access)
|
||||
to get `https://your-domain.com` serving port 1934 over TLS.
|
||||
2. Enable OAuth: `{ "oauth": { "enabled": true } }` in `ov.conf`.
|
||||
3. Restart: `docker compose restart openviking`.
|
||||
4. Set `OPENVIKING_PUBLIC_BASE_URL=https://your-domain.com` in `.env` (the
|
||||
server uses this as the issuer in OAuth metadata and `WWW-Authenticate`).
|
||||
|
||||
### Why a reverse proxy
|
||||
|
||||
The MCP and OAuth endpoints **must live at the public domain root**:
|
||||
|
||||
| Path | Required by |
|
||||
|---|---|
|
||||
| `/.well-known/oauth-authorization-server` | RFC 8414 — clients append this path to the issuer URL |
|
||||
| `/.well-known/oauth-protected-resource` | RFC 9728 — discovered via the 401 `WWW-Authenticate` hint |
|
||||
| `/register`, `/authorize`, `/token` | MCP SDK, mounted at root |
|
||||
| `/mcp` | Default MCP convention |
|
||||
|
||||
This means **`openviking-server` (1933) is the "root" service**, and the
|
||||
console (8020) lives at the `/console/...` sub-path (it was designed this way:
|
||||
all of its static asset URLs are hard-coded to `/console/styles.css`,
|
||||
`/console/app.js`, etc.).
|
||||
|
||||
### Tell the server its public origin
|
||||
|
||||
The OAuth subsystem publishes URLs that contain the public host name (issuer,
|
||||
PRM, `WWW-Authenticate`). Resolution order, highest-priority first:
|
||||
|
||||
1. `OPENVIKING_PUBLIC_BASE_URL` environment variable
|
||||
2. `oauth.issuer` in `ov.conf`
|
||||
3. `X-Forwarded-Proto` + `X-Forwarded-Host` request headers
|
||||
4. The request `Host` header
|
||||
|
||||
Set option 1 or 2 explicitly when you're behind a reverse proxy. Either:
|
||||
|
||||
```bash
|
||||
# Process env (systemd, docker, …)
|
||||
export OPENVIKING_PUBLIC_BASE_URL="https://my.ov"
|
||||
```
|
||||
|
||||
```jsonc
|
||||
// ov.conf
|
||||
{
|
||||
"oauth": {
|
||||
"enabled": true,
|
||||
"issuer": "https://my.ov"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Reverse proxy config — Caddy (recommended)
|
||||
|
||||
Caddy obtains and renews Let's Encrypt certificates automatically. The
|
||||
console expects to live under `/console/...` (its HTML hard-codes
|
||||
`/console/styles.css`, `/console/app.js`, etc.) — use `handle` rather than
|
||||
`handle_path` so the prefix is **not** stripped before forwarding.
|
||||
|
||||
`/etc/caddy/Caddyfile`:
|
||||
|
||||
```caddyfile
|
||||
my.ov {
|
||||
@console path /console /console/*
|
||||
handle @console {
|
||||
reverse_proxy 127.0.0.1:8020
|
||||
}
|
||||
handle {
|
||||
# Everything else: MCP, OAuth, REST, .well-known
|
||||
reverse_proxy 127.0.0.1:1933
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After `caddy reload`, browse to <https://my.ov/console>, sign in with your
|
||||
API key, and the OAuth flow is ready. Caddy adds `X-Forwarded-Proto` and
|
||||
`X-Forwarded-Host` automatically.
|
||||
|
||||
### Reverse proxy config — nginx
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name my.ov;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/my.ov/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/my.ov/privkey.pem;
|
||||
|
||||
# 8020 console at /console/...
|
||||
location /console {
|
||||
proxy_pass http://127.0.0.1:8020;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
|
||||
# Everything else → 1933 (MCP, OAuth, REST, .well-known)
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:1933;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTP → HTTPS redirect
|
||||
server {
|
||||
listen 80;
|
||||
server_name my.ov;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
You don't need `proxy_pass_header Authorization` or `proxy_pass_header
|
||||
X-Api-Key` — those directives forward upstream **response** headers, while
|
||||
client request headers are forwarded to the upstream by default.
|
||||
|
||||
### Docker Compose
|
||||
|
||||
The shipped `docker-compose.yml` includes a commented-out Caddy service. To
|
||||
go from "local 1933/8020" to "public HTTPS at `https://my.ov`":
|
||||
|
||||
1. **Create a `.env`** next to `docker-compose.yml`. The same
|
||||
`OPENVIKING_PUBLIC_BASE_URL` is read by both the OpenViking container and
|
||||
Caddy — set it once:
|
||||
|
||||
```dotenv
|
||||
OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com
|
||||
OV_ACME_EMAIL=admin@your-domain.com # optional; recommended for Let's Encrypt
|
||||
```
|
||||
|
||||
2. **Drop a `Caddyfile`** next to `docker-compose.yml`. Caddy accepts a full
|
||||
URL as the site address (the `https://` prefix tells it to enable HTTPS):
|
||||
|
||||
```caddyfile
|
||||
{$OPENVIKING_PUBLIC_BASE_URL} {
|
||||
@console path /console /console/*
|
||||
handle @console {
|
||||
reverse_proxy openviking:8020
|
||||
}
|
||||
handle {
|
||||
reverse_proxy openviking:1933
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To pin Let's Encrypt registration to a specific email, add `tls
|
||||
{$OV_ACME_EMAIL}` inside the site block.
|
||||
|
||||
3. **Uncomment the `caddy:` service and the `volumes:` block** at the bottom
|
||||
of `docker-compose.yml`.
|
||||
|
||||
4. **Point DNS** at this host's public IP and ensure ports 80/443 are
|
||||
reachable.
|
||||
|
||||
5. `docker compose up -d`. The first HTTPS request triggers ACME issuance;
|
||||
subsequent requests are cached.
|
||||
|
||||
> The Caddy service uses the compose network's container DNS, so
|
||||
> `reverse_proxy openviking:8020` works without exposing 8020 publicly. You
|
||||
> can drop the host-side port mappings (`"8020:8020"`, `"1933:1933"`) once
|
||||
> Caddy is the public entry point.
|
||||
Once HTTPS + OAuth are both up, connect clients as described below.
|
||||
|
||||
---
|
||||
|
||||
@@ -501,10 +338,10 @@ curl -i https://my.ov/mcp -d '{}' -H 'Content-Type: application/json' | grep -i
|
||||
|
||||
## References
|
||||
|
||||
- [Public Access & Reverse Proxy Guide](12-public-access.md) — HTTPS, Caddy, nginx, docker compose
|
||||
- [MCP Specification — Authorization](https://modelcontextprotocol.io/specification/2025-03-26/server/authorization)
|
||||
- [RFC 8414 — OAuth 2.0 Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414)
|
||||
- [RFC 9728 — OAuth 2.0 Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728)
|
||||
- [RFC 7591 — Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591)
|
||||
- [RFC 7636 — PKCE](https://datatracker.ietf.org/doc/html/rfc7636)
|
||||
- [Caddyfile syntax](https://caddyserver.com/docs/caddyfile)
|
||||
- [OpenViking MCP Integration Guide](06-mcp-integration.md)
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
# Public Access & Reverse Proxy
|
||||
|
||||
OpenViking runs two internal services:
|
||||
|
||||
| Port | Service | Handles |
|
||||
|------|---------|---------|
|
||||
| 1933 | API server | REST API, MCP, OAuth, `.well-known/*` |
|
||||
| 8020 | Console | Web UI at `/console/...` |
|
||||
|
||||
The bundled **Caddy reverse proxy** merges them into a single port — **1934** — so
|
||||
clients only need one URL. This works out of the box with `docker compose up`.
|
||||
|
||||
## Port overview
|
||||
|
||||
```
|
||||
┌────────────────────────┐
|
||||
Internet / LAN ──► │ Caddy :1934 (HTTP) │
|
||||
│ │
|
||||
│ /console/* → :8020 │
|
||||
│ /* → :1933 │
|
||||
└────────────────────────┘
|
||||
```
|
||||
|
||||
Port 1934 is plain HTTP — fine for local development, internal networks, and
|
||||
as an upstream target behind your own TLS-terminating proxy or CDN.
|
||||
|
||||
For **public HTTPS** (required for OAuth with MCP clients like Claude.ai,
|
||||
ChatGPT, Cursor), Caddy can also serve port 443 with automatic Let's Encrypt
|
||||
certificates. See [Adding HTTPS](#adding-https-for-public-access) below.
|
||||
|
||||
## Quick start (local / internal)
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Both services are now reachable on one port:
|
||||
|
||||
```bash
|
||||
curl http://localhost:1934/health # API server health
|
||||
curl http://localhost:1934/api/v1/system/status \
|
||||
-H "X-Api-Key: YOUR_KEY" # REST API
|
||||
open http://localhost:1934/console # Web console
|
||||
```
|
||||
|
||||
You can still access 1933 and 8020 directly — those port mappings are left in
|
||||
`docker-compose.yml` for debugging. Once you're confident everything works
|
||||
through 1934, feel free to comment them out.
|
||||
|
||||
## Adding HTTPS for public access
|
||||
|
||||
You need: a public domain, ports 80 + 443 reachable, DNS pointing here.
|
||||
|
||||
### 1. Create `.env`
|
||||
|
||||
```dotenv
|
||||
OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com
|
||||
OV_ACME_EMAIL=admin@your-domain.com # optional; recommended for Let's Encrypt
|
||||
```
|
||||
|
||||
`OPENVIKING_PUBLIC_BASE_URL` is read by both the OpenViking container (it
|
||||
publishes this URL in OAuth metadata and `WWW-Authenticate` headers) and by
|
||||
Caddy (as the site address for the HTTPS block).
|
||||
|
||||
### 2. Add a domain block to `Caddyfile`
|
||||
|
||||
Append this below the existing `:1934` block:
|
||||
|
||||
```caddyfile
|
||||
{$OPENVIKING_PUBLIC_BASE_URL} {
|
||||
@console path /console /console/*
|
||||
handle @console {
|
||||
reverse_proxy openviking:8020
|
||||
}
|
||||
handle {
|
||||
reverse_proxy openviking:1933
|
||||
}
|
||||
# Pin ACME registration email (optional):
|
||||
# tls {$OV_ACME_EMAIL}
|
||||
}
|
||||
```
|
||||
|
||||
The `:1934` block stays — it continues to serve HTTP for local/internal
|
||||
access. The new block serves HTTPS on 443 for the public domain.
|
||||
|
||||
### 3. Uncomment HTTPS lines in `docker-compose.yml`
|
||||
|
||||
Three places:
|
||||
|
||||
```yaml
|
||||
# In caddy.ports — uncomment:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
|
||||
# In caddy.volumes — uncomment:
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
|
||||
# At the bottom — uncomment:
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
```
|
||||
|
||||
### 4. Launch
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
The first HTTPS request triggers ACME certificate issuance. Subsequent
|
||||
requests use the cached cert. Caddy handles renewal automatically.
|
||||
|
||||
### 5. Verify
|
||||
|
||||
```bash
|
||||
curl https://ov.your-domain.com/health
|
||||
# {"status": "ok"}
|
||||
|
||||
# OAuth metadata (if oauth.enabled = true):
|
||||
curl https://ov.your-domain.com/.well-known/oauth-authorization-server
|
||||
```
|
||||
|
||||
## Using your own reverse proxy
|
||||
|
||||
If you already have nginx, Traefik, or another proxy handling TLS, point it
|
||||
at port 1934 instead of juggling 1933 + 8020 separately. Port 1934 already
|
||||
does the path-based routing internally.
|
||||
|
||||
### nginx (TLS termination at nginx, plain HTTP to 1934)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name ov.your-domain.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/ov.your-domain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/ov.your-domain.com/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:1934;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name ov.your-domain.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
With this setup, remove the Caddy HTTPS block from the Caddyfile — keep only
|
||||
the `:1934` block.
|
||||
|
||||
### Caddy (external, without docker-compose Caddy)
|
||||
|
||||
If you run Caddy on the host rather than inside compose:
|
||||
|
||||
```caddyfile
|
||||
ov.your-domain.com {
|
||||
reverse_proxy 127.0.0.1:1934
|
||||
}
|
||||
```
|
||||
|
||||
### Cloudflare / CDN
|
||||
|
||||
Point the CDN origin at `http://your-server-ip:1934`. Set
|
||||
`OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com` so the server knows
|
||||
its public-facing origin. Ensure the CDN forwards `Host`,
|
||||
`X-Forwarded-Proto`, and `X-Forwarded-Host` headers.
|
||||
|
||||
## Tell the server its public URL
|
||||
|
||||
OAuth metadata, `WWW-Authenticate` headers, and resource URLs all need to
|
||||
contain the public-facing origin. Resolution order (highest priority first):
|
||||
|
||||
1. `OPENVIKING_PUBLIC_BASE_URL` environment variable
|
||||
2. `oauth.issuer` in `ov.conf`
|
||||
3. `X-Forwarded-Proto` + `X-Forwarded-Host` request headers
|
||||
4. The request `Host` header
|
||||
|
||||
Behind any reverse proxy, set option 1 explicitly:
|
||||
|
||||
```bash
|
||||
export OPENVIKING_PUBLIC_BASE_URL="https://ov.your-domain.com"
|
||||
```
|
||||
|
||||
Or in `ov.conf`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"oauth": {
|
||||
"enabled": true,
|
||||
"issuer": "https://ov.your-domain.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## HTTPS and OAuth
|
||||
|
||||
OAuth 2.1 (and the MCP SDK) **requires HTTPS** for any non-localhost issuer.
|
||||
If the server's published origin is `http://` on a non-loopback address, MCP
|
||||
clients will refuse to connect with an "Issuer URL must be HTTPS" error.
|
||||
|
||||
This is a protocol-level requirement, not an OpenViking limitation. For local
|
||||
testing, `http://127.0.0.1:1934` works without HTTPS. For anything else, set
|
||||
up TLS as described above.
|
||||
|
||||
Non-OAuth API access (API key auth) works fine over HTTP if you accept the
|
||||
risk — the protocol doesn't enforce TLS for bearer tokens.
|
||||
|
||||
## Without Docker
|
||||
|
||||
If you run `openviking-server` and the console directly (systemd, bare
|
||||
metal, etc.), install Caddy on the host and use the same Caddyfile pattern
|
||||
with `127.0.0.1` upstreams:
|
||||
|
||||
```caddyfile
|
||||
:1934 {
|
||||
@console path /console /console/*
|
||||
handle @console {
|
||||
reverse_proxy 127.0.0.1:8020
|
||||
}
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:1933
|
||||
}
|
||||
}
|
||||
|
||||
# Add domain block for HTTPS if needed:
|
||||
# ov.your-domain.com { ... }
|
||||
```
|
||||
|
||||
Or with nginx:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 1934;
|
||||
|
||||
location /console {
|
||||
proxy_pass http://127.0.0.1:8020;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:1933;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [Deployment Guide](03-deployment.md) — Docker, systemd, Kubernetes
|
||||
- [OAuth Guide](11-oauth.md) — OAuth 2.1 configuration and client setup
|
||||
- [Authentication](04-authentication.md) — API key management
|
||||
+35
-189
@@ -8,52 +8,29 @@ token、metadata)由官方 `mcp.server.auth` SDK 提供,整体遵循 OAuth 2
|
||||
|
||||
## 推荐配置
|
||||
|
||||
下面是能让 Claude.ai 等公网客户端跑通的最小配置。前提:你有一个公网域名,
|
||||
80/443 端口可达,并安装了 docker compose。
|
||||
> **前提**:公网 HTTPS。OAuth 2.1(以及 MCP SDK)对非 localhost 的 issuer
|
||||
> **强制要求 HTTPS**。请参阅[公网访问指南](12-public-access.md)了解如何配置
|
||||
> Caddy 或 nginx 的 HTTPS。
|
||||
|
||||
1. **在 `docker-compose.yml` 旁创建 `.env`:**
|
||||
1. **配置 HTTPS** — 按[公网访问指南](12-public-access.md)设置好
|
||||
`https://ov.your-domain.com`(Caddy + `.env` + `docker compose up`)。
|
||||
|
||||
```dotenv
|
||||
OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com
|
||||
OV_ACME_EMAIL=admin@your-domain.com # 可选;推荐用于 Let's Encrypt
|
||||
```
|
||||
|
||||
2. **在 `docker-compose.yml` 旁放一份 `Caddyfile`:**
|
||||
|
||||
```caddyfile
|
||||
{$OPENVIKING_PUBLIC_BASE_URL} {
|
||||
@console path /console /console/*
|
||||
handle @console {
|
||||
reverse_proxy openviking:8020
|
||||
}
|
||||
handle {
|
||||
reverse_proxy openviking:1933
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **在 `~/.openviking/ov.conf` 启用 OAuth**(不开的话服务端会忽略 env
|
||||
变量,`/oauth/*` 路由也不会挂载):
|
||||
2. **在 `~/.openviking/ov.conf` 启用 OAuth:**
|
||||
|
||||
```json
|
||||
{ "oauth": { "enabled": true } }
|
||||
```
|
||||
|
||||
4. **取消 `docker-compose.yml` 末尾的 `caddy:` service 和 `volumes:` 段的
|
||||
注释**,DNS 指向本机,然后:
|
||||
3. **重启**(`docker compose restart openviking`)。
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
5. **接入客户端。** Claude.ai → Connectors → Add → 输入
|
||||
4. **接入客户端。** Claude.ai → Connectors → Add → 输入
|
||||
`https://ov.your-domain.com/mcp`。浏览器打开授权页,显示一个 6 字符码;
|
||||
再打开 `https://ov.your-domain.com/console`,用 API Key 登录,把码粘到
|
||||
Settings → "Authorize an MCP client" → 点 Authorize。浏览器自动跳回
|
||||
Claude.ai,连接器就位。
|
||||
|
||||
线上路径就这五步。后续章节解释每一块为什么这样设计、不用 docker compose
|
||||
怎么做、出问题时怎么用 curl 排查。
|
||||
线上路径就这四步。后续章节解释每一块为什么这样设计、本地怎么不走 HTTPS 做
|
||||
联调、出问题时怎么用 curl 排查。
|
||||
|
||||
---
|
||||
|
||||
@@ -125,7 +102,13 @@ Claude.ai / Claude Desktop 等线上客户端**只接受公网 HTTPS**,所以
|
||||
}
|
||||
```
|
||||
|
||||
2. **同时启动 API server (1933) 和 console (8020):**
|
||||
2. **启动:**
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
或不用 Docker:
|
||||
|
||||
```bash
|
||||
openviking-server
|
||||
@@ -133,170 +116,33 @@ Claude.ai / Claude Desktop 等线上客户端**只接受公网 HTTPS**,所以
|
||||
python -m openviking.console.bootstrap --write-enabled
|
||||
```
|
||||
|
||||
3. **登录 console**:访问 <http://127.0.0.1:8020/console>,把 API Key 粘到
|
||||
Settings → 点 Save。"Authorize an MCP client" 表单现在可用。
|
||||
3. **登录 console**:访问 <http://127.0.0.1:1934/console>(不走聚合代理时用
|
||||
`:8020/console`),把 API Key 粘到 Settings → 点 Save。"Authorize an MCP
|
||||
client" 表单现在可用。
|
||||
|
||||
4. **接一个本地 MCP 客户端**(例如 MCP Inspector)到
|
||||
`http://127.0.0.1:1933/mcp`。客户端会走上面那套流程;把 authorize 页显示
|
||||
的 6 字符码复制到 console 表单 → 点 Authorize → 客户端拿到 token。
|
||||
`http://127.0.0.1:1934/mcp`(或 `:1933/mcp`)。客户端会走上面那套流程;把
|
||||
authorize 页显示的 6 字符码复制到 console 表单 → 点 Authorize → 客户端拿到
|
||||
token。
|
||||
|
||||
线上接 Claude.ai / Claude Desktop 走下面的 HTTPS 部署。
|
||||
线上接 Claude.ai / Claude Desktop 走[公网访问指南](12-public-access.md)。
|
||||
|
||||
---
|
||||
|
||||
## 生产部署(HTTPS)
|
||||
|
||||
线上 OAuth 客户端需要:
|
||||
OAuth 2.1 对非 localhost 的 issuer **强制要求 HTTPS**。
|
||||
[公网访问指南](12-public-access.md)详细介绍了 Caddy、nginx、docker compose、
|
||||
CDN 的配置方法。简要步骤:
|
||||
|
||||
1. 一个公网域名指向你的服务器(下面例子里用 `my.ov`)
|
||||
2. 在反代层做 TLS 终止(Caddy 或 nginx)
|
||||
3. 本机同时跑 `openviking-server` (1933) 和 console (8020)
|
||||
4. 反代把两者放到**同一域名**下,OAuth 页才能与 console 同源(quick-authorize 才能用)
|
||||
1. 按[公网访问指南 § 添加 HTTPS](12-public-access.md#添加-https公网访问)
|
||||
配置好 `https://your-domain.com`,使 1934 端口走 TLS。
|
||||
2. 启用 OAuth:`ov.conf` 里 `{ "oauth": { "enabled": true } }`。
|
||||
3. 重启:`docker compose restart openviking`。
|
||||
4. 在 `.env` 设置 `OPENVIKING_PUBLIC_BASE_URL=https://your-domain.com`
|
||||
(服务端用它作为 OAuth 元数据和 `WWW-Authenticate` 的 issuer)。
|
||||
|
||||
### 为什么需要反代
|
||||
|
||||
MCP 与 OAuth 端点**必须挂在公网域名根下**:
|
||||
|
||||
| 路径 | 来源 |
|
||||
|---|---|
|
||||
| `/.well-known/oauth-authorization-server` | RFC 8414 — 客户端拼 issuer URL + 此路径 |
|
||||
| `/.well-known/oauth-protected-resource` | RFC 9728 — 通过 401 `WWW-Authenticate` 头发现 |
|
||||
| `/register`、`/authorize`、`/token` | MCP SDK 默认挂在根 |
|
||||
| `/mcp` | MCP 默认惯例 |
|
||||
|
||||
所以 **`openviking-server` (1933) 是"根域服务"**,console (8020) 住在
|
||||
`/console/...` 子路径下(这是 console 现状:HTML 里硬编码了
|
||||
`/console/styles.css`、`/console/app.js` 等等)。
|
||||
|
||||
### 告诉服务端自己的公网地址
|
||||
|
||||
OAuth 子系统会在多处发布带域名的 URL(issuer、PRM、`WWW-Authenticate`)。
|
||||
解析顺序,**优先级从高到低**:
|
||||
|
||||
1. `OPENVIKING_PUBLIC_BASE_URL` 环境变量
|
||||
2. `ov.conf` 里的 `oauth.issuer`
|
||||
3. `X-Forwarded-Proto` + `X-Forwarded-Host` 请求头
|
||||
4. 请求的 `Host` 头
|
||||
|
||||
反代后强烈建议显式设置 1 或 2,二选一:
|
||||
|
||||
```bash
|
||||
# 进程环境(systemd / docker / …)
|
||||
export OPENVIKING_PUBLIC_BASE_URL="https://my.ov"
|
||||
```
|
||||
|
||||
```jsonc
|
||||
// ov.conf
|
||||
{
|
||||
"oauth": {
|
||||
"enabled": true,
|
||||
"issuer": "https://my.ov"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 反代配置 — Caddy(推荐)
|
||||
|
||||
Caddy 自动签发并续期 Let's Encrypt 证书。console 自己住在 `/console/...` 下
|
||||
(HTML 里硬编码),所以用 `handle` 而不是 `handle_path`,**不**剥前缀。
|
||||
|
||||
`/etc/caddy/Caddyfile`:
|
||||
|
||||
```caddyfile
|
||||
my.ov {
|
||||
@console path /console /console/*
|
||||
handle @console {
|
||||
reverse_proxy 127.0.0.1:8020
|
||||
}
|
||||
handle {
|
||||
# 其他都走 1933:MCP、OAuth、REST、.well-known
|
||||
reverse_proxy 127.0.0.1:1933
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`caddy reload` 后访问 <https://my.ov/console>,登录 API Key,OAuth 流程
|
||||
就绪。Caddy 自动加 `X-Forwarded-Proto` 与 `X-Forwarded-Host`。
|
||||
|
||||
### 反代配置 — nginx
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name my.ov;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/my.ov/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/my.ov/privkey.pem;
|
||||
|
||||
# 8020 console 在 /console/... 下
|
||||
location /console {
|
||||
proxy_pass http://127.0.0.1:8020;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
|
||||
# 其他都走 1933(MCP、OAuth、REST、.well-known)
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:1933;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTP → HTTPS 跳转
|
||||
server {
|
||||
listen 80;
|
||||
server_name my.ov;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
不需要 `proxy_pass_header Authorization` 或 `proxy_pass_header X-Api-Key` —
|
||||
那些指令是用来透传**上游响应**头的;客户端请求头(Authorization、X-Api-Key)
|
||||
默认就会被原样转发到上游。
|
||||
|
||||
### Docker Compose
|
||||
|
||||
仓库里的 `docker-compose.yml` 已带一份注释掉的 Caddy service。把
|
||||
"本地 1933/8020" 升到 "公网 HTTPS at `https://my.ov`" 的步骤:
|
||||
|
||||
1. **在 `docker-compose.yml` 旁创建 `.env`**。`OPENVIKING_PUBLIC_BASE_URL`
|
||||
是公网地址的唯一来源,OpenViking 容器和 Caddy 都读它,只需设置一次:
|
||||
|
||||
```dotenv
|
||||
OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com
|
||||
OV_ACME_EMAIL=admin@your-domain.com # 可选;推荐用于 Let's Encrypt
|
||||
```
|
||||
|
||||
2. **在 `docker-compose.yml` 旁放一份 `Caddyfile`**。Caddy 接受完整 URL 作
|
||||
为 site 地址(`https://` 前缀会启用自动 HTTPS):
|
||||
|
||||
```caddyfile
|
||||
{$OPENVIKING_PUBLIC_BASE_URL} {
|
||||
@console path /console /console/*
|
||||
handle @console {
|
||||
reverse_proxy openviking:8020
|
||||
}
|
||||
handle {
|
||||
reverse_proxy openviking:1933
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
想把 Let's Encrypt 注册绑定到特定邮箱,可以在 site 块里加
|
||||
`tls {$OV_ACME_EMAIL}`。
|
||||
|
||||
3. **取消 `docker-compose.yml` 末尾的 `caddy:` service 和 `volumes:` 段的注释。**
|
||||
|
||||
4. **DNS** 指向本机公网 IP,确保 80/443 端口能进。
|
||||
|
||||
5. `docker compose up -d`。首次 HTTPS 请求会触发 ACME 签发,之后会缓存。
|
||||
|
||||
> Caddy service 通过 compose 的容器 DNS 直连,`reverse_proxy openviking:8020`
|
||||
> 不需要把 8020 暴露到 host。Caddy 接管公网入口后,可以删掉
|
||||
> `"8020:8020"` 与 `"1933:1933"` 这两条 host 端口映射。
|
||||
HTTPS + OAuth 就绪后,按下面的方式接入客户端。
|
||||
|
||||
---
|
||||
|
||||
@@ -470,10 +316,10 @@ curl -i https://my.ov/mcp -d '{}' -H 'Content-Type: application/json' | grep -i
|
||||
|
||||
## 参考
|
||||
|
||||
- [公网访问与反向代理指南](12-public-access.md) — HTTPS、Caddy、nginx、docker compose
|
||||
- [MCP 规范 — Authorization](https://modelcontextprotocol.io/specification/2025-03-26/server/authorization)
|
||||
- [RFC 8414 — OAuth 2.0 Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414)
|
||||
- [RFC 9728 — OAuth 2.0 Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728)
|
||||
- [RFC 7591 — Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591)
|
||||
- [RFC 7636 — PKCE](https://datatracker.ietf.org/doc/html/rfc7636)
|
||||
- [Caddyfile 语法](https://caddyserver.com/docs/caddyfile)
|
||||
- [OpenViking MCP 集成指南](06-mcp-integration.md)
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
# 公网访问与反向代理
|
||||
|
||||
OpenViking 运行两个内部服务:
|
||||
|
||||
| 端口 | 服务 | 处理内容 |
|
||||
|------|------|---------|
|
||||
| 1933 | API 服务 | REST API、MCP、OAuth、`.well-known/*` |
|
||||
| 8020 | Console | Web 管理界面,路径前缀 `/console/...` |
|
||||
|
||||
自带的 **Caddy 反向代理**将两者合并到一个端口 — **1934** — 客户端只需一个
|
||||
URL。`docker compose up` 即可开箱使用。
|
||||
|
||||
## 端口总览
|
||||
|
||||
```
|
||||
┌────────────────────────┐
|
||||
Internet / LAN ──► │ Caddy :1934 (HTTP) │
|
||||
│ │
|
||||
│ /console/* → :8020 │
|
||||
│ /* → :1933 │
|
||||
└────────────────────────┘
|
||||
```
|
||||
|
||||
1934 端口是纯 HTTP — 适用于本地开发、内网环境,也可以作为外部 TLS 终止代理
|
||||
或 CDN 的上游。
|
||||
|
||||
如果需要**公网 HTTPS**(Claude.ai、ChatGPT、Cursor 等 MCP 客户端走 OAuth 时
|
||||
必须),Caddy 还能在 443 端口自动签发 Let's Encrypt 证书。见下方
|
||||
[添加 HTTPS](#添加-https-公网访问)。
|
||||
|
||||
## 快速开始(本地 / 内网)
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
两个服务在一个端口上可达:
|
||||
|
||||
```bash
|
||||
curl http://localhost:1934/health # API 健康检查
|
||||
curl http://localhost:1934/api/v1/system/status \
|
||||
-H "X-Api-Key: YOUR_KEY" # REST API
|
||||
open http://localhost:1934/console # Web Console
|
||||
```
|
||||
|
||||
1933 和 8020 仍可直接访问 — `docker-compose.yml` 里保留了端口映射方便调试。
|
||||
确认 1934 一切正常后,可以把那两行注释掉。
|
||||
|
||||
## 添加 HTTPS(公网访问)
|
||||
|
||||
前提:有公网域名、80 + 443 端口可达、DNS 已指向。
|
||||
|
||||
### 1. 创建 `.env`
|
||||
|
||||
```dotenv
|
||||
OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com
|
||||
OV_ACME_EMAIL=admin@your-domain.com # 可选;推荐用于 Let's Encrypt
|
||||
```
|
||||
|
||||
`OPENVIKING_PUBLIC_BASE_URL` 同时被 OpenViking 容器(发布在 OAuth 元数据和
|
||||
`WWW-Authenticate` 头中)和 Caddy(作为 HTTPS 站点地址)读取。
|
||||
|
||||
### 2. 在 `Caddyfile` 追加域名块
|
||||
|
||||
在已有的 `:1934` 块下面添加:
|
||||
|
||||
```caddyfile
|
||||
{$OPENVIKING_PUBLIC_BASE_URL} {
|
||||
@console path /console /console/*
|
||||
handle @console {
|
||||
reverse_proxy openviking:8020
|
||||
}
|
||||
handle {
|
||||
reverse_proxy openviking:1933
|
||||
}
|
||||
# 绑定 ACME 注册邮箱(可选):
|
||||
# tls {$OV_ACME_EMAIL}
|
||||
}
|
||||
```
|
||||
|
||||
`:1934` 块保留 — 继续为本地/内网提供 HTTP 访问。新块在 443 端口为公网域名
|
||||
提供 HTTPS。
|
||||
|
||||
### 3. 取消 `docker-compose.yml` 中的 HTTPS 注释
|
||||
|
||||
三处:
|
||||
|
||||
```yaml
|
||||
# caddy.ports 里取消注释:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
|
||||
# caddy.volumes 里取消注释:
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
|
||||
# 文件末尾取消注释:
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
```
|
||||
|
||||
### 4. 启动
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
首次 HTTPS 请求触发 ACME 证书签发,后续使用缓存。Caddy 自动续期。
|
||||
|
||||
### 5. 验证
|
||||
|
||||
```bash
|
||||
curl https://ov.your-domain.com/health
|
||||
# {"status": "ok"}
|
||||
|
||||
# OAuth 元数据(如果 oauth.enabled = true):
|
||||
curl https://ov.your-domain.com/.well-known/oauth-authorization-server
|
||||
```
|
||||
|
||||
## 使用自己的反向代理
|
||||
|
||||
如果你已有 nginx、Traefik 或其他 TLS 终止代理,直接指向 1934 端口,不用分别
|
||||
处理 1933 + 8020。1934 内部已经做好了路径路由。
|
||||
|
||||
### nginx(TLS 在 nginx 终止,HTTP 到 1934)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name ov.your-domain.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/ov.your-domain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/ov.your-domain.com/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:1934;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name ov.your-domain.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
这种方案下,Caddyfile 只保留 `:1934` 块,不加域名块。
|
||||
|
||||
### Caddy(宿主机运行,不走 compose)
|
||||
|
||||
```caddyfile
|
||||
ov.your-domain.com {
|
||||
reverse_proxy 127.0.0.1:1934
|
||||
}
|
||||
```
|
||||
|
||||
### Cloudflare / CDN
|
||||
|
||||
CDN 源站指向 `http://your-server-ip:1934`。设置
|
||||
`OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com` 让服务端知道自己的公网
|
||||
地址。确保 CDN 转发 `Host`、`X-Forwarded-Proto`、`X-Forwarded-Host` 头。
|
||||
|
||||
## 告诉服务端公网 URL
|
||||
|
||||
OAuth 元数据、`WWW-Authenticate` 头、资源 URL 都需要包含公网 origin。
|
||||
解析顺序(**优先级从高到低**):
|
||||
|
||||
1. `OPENVIKING_PUBLIC_BASE_URL` 环境变量
|
||||
2. `ov.conf` 里的 `oauth.issuer`
|
||||
3. `X-Forwarded-Proto` + `X-Forwarded-Host` 请求头
|
||||
4. 请求的 `Host` 头
|
||||
|
||||
在反代后面,务必显式设置选项 1:
|
||||
|
||||
```bash
|
||||
export OPENVIKING_PUBLIC_BASE_URL="https://ov.your-domain.com"
|
||||
```
|
||||
|
||||
或者 `ov.conf`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"oauth": {
|
||||
"enabled": true,
|
||||
"issuer": "https://ov.your-domain.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## HTTPS 与 OAuth
|
||||
|
||||
OAuth 2.1(以及 MCP SDK)对非 localhost 的 issuer **强制要求 HTTPS**。
|
||||
如果服务端发布的 origin 是非回环地址的 `http://`,MCP 客户端会拒绝连接并报
|
||||
"Issuer URL must be HTTPS"。
|
||||
|
||||
这是协议层面的要求,不是 OpenViking 的限制。本地测试时
|
||||
`http://127.0.0.1:1934` 无需 HTTPS。其他场景请按上面的方式配置 TLS。
|
||||
|
||||
非 OAuth 的 API 访问(API Key 认证)在 HTTP 下也能正常工作 — 协议本身不强制
|
||||
bearer token 走 TLS,用户自行评估风险。
|
||||
|
||||
## 不用 Docker
|
||||
|
||||
如果直接运行 `openviking-server` 和 console(systemd、裸机等),在宿主机装
|
||||
Caddy 并使用相同的 Caddyfile 模式,上游改为 `127.0.0.1`:
|
||||
|
||||
```caddyfile
|
||||
:1934 {
|
||||
@console path /console /console/*
|
||||
handle @console {
|
||||
reverse_proxy 127.0.0.1:8020
|
||||
}
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:1933
|
||||
}
|
||||
}
|
||||
|
||||
# 需要 HTTPS 时追加域名块:
|
||||
# ov.your-domain.com { ... }
|
||||
```
|
||||
|
||||
或者 nginx:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 1934;
|
||||
|
||||
location /console {
|
||||
proxy_pass http://127.0.0.1:8020;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:1933;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [部署指南](03-deployment.md) — Docker、systemd、Kubernetes
|
||||
- [OAuth 指南](11-oauth.md) — OAuth 2.1 配置与客户端接入
|
||||
- [认证](04-authentication.md) — API Key 管理
|
||||
Reference in New Issue
Block a user