feat(config,caddy): single gateway port and site.caddy per the komodo convention
This commit is contained in:
+1
-5
@@ -78,10 +78,6 @@ KOMODO_SECRET=
|
|||||||
|
|
||||||
### Порты хоста и внешний адрес ###############################################
|
### Порты хоста и внешний адрес ###############################################
|
||||||
|
|
||||||
PORT_MESSAGES=62990
|
PORT_GATEWAY=62990
|
||||||
PORT_MCP=62991
|
|
||||||
PORT_ADMIN=62992
|
|
||||||
PORT_MARKDOWN=62993
|
|
||||||
PORT_API=62994
|
|
||||||
# Публичный URL за reverse proxy (см. ./caddy). Пусто для localhost/dev
|
# Публичный URL за reverse proxy (см. ./caddy). Пусто для localhost/dev
|
||||||
PUBLIC_BASE_URL=
|
PUBLIC_BASE_URL=
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ with a whitelisted environment; the vault is read-only for it except
|
|||||||
|
|
||||||
### 4. Mint a token
|
### 4. Mint a token
|
||||||
|
|
||||||
Open admin at `http://localhost:62992` (or `https://<DOMAIN>/admin/` if Caddy), sign in with `ADMIN_USER` / `ADMIN_PASS`, go to **Tokens → Create**, scope `*` for first run.
|
Open admin at `http://localhost:62990/` (or `https://<DOMAIN>/` if Caddy), sign in with `ADMIN_USER` / `ADMIN_PASS`, go to **Tokens → Create**, scope `*` for first run.
|
||||||
|
|
||||||
### 5. Smoke test
|
### 5. Smoke test
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ Open admin at `http://localhost:62992` (or `https://<DOMAIN>/admin/` if Caddy),
|
|||||||
docker exec beaver-gateway ls /vault | head
|
docker exec beaver-gateway ls /vault | head
|
||||||
docker logs beaver-gateway | grep -i "agent registered"
|
docker logs beaver-gateway | grep -i "agent registered"
|
||||||
|
|
||||||
curl http://localhost:62990/v1/models \
|
curl http://localhost:62990/anthropic/v1/models \
|
||||||
-H "Authorization: Bearer <YOUR_TOKEN>"
|
-H "Authorization: Bearer <YOUR_TOKEN>"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -84,24 +84,25 @@ curl http://localhost:62990/v1/models \
|
|||||||
|
|
||||||
The admin dashboard renders ready-to-copy URLs and snippets for each of these
|
The admin dashboard renders ready-to-copy URLs and snippets for each of these
|
||||||
|
|
||||||
- any Anthropic client: `http://localhost:62990` or `https://<DOMAIN>/anthropic`, model = agent name (`beaver-opus-high` etc)
|
Everything is on one port under its own path; Caddy forwards the whole domain as is.
|
||||||
- MCP clients (Claude Desktop, Raycast extension): just find in admin
|
|
||||||
- Obsidian companion plugin: paste the dashboard's **plugin base** (`http://localhost:62993` raw, or `https://<DOMAIN>/md` behind Caddy) into the plugin's "Base URL" setting
|
- any Anthropic client: `http://localhost:62990/anthropic` or `https://<DOMAIN>/anthropic`, model = agent name (`beaver-opus-high` etc)
|
||||||
- **Admin UI:** `http://localhost:62992` or `https://<DOMAIN>/admin`, login from `ADMIN_USER` / `ADMIN_PASS`
|
- MCP clients (Claude Desktop, Raycast extension): `/mcp/<name>/`, discovery page at `/mcp/`
|
||||||
|
- Obsidian companion plugin: `/md` as the plugin's "Base URL", `/api` for the panel
|
||||||
|
- **Admin UI:** `http://localhost:62990/` or `https://<DOMAIN>/`, login from `ADMIN_USER` / `ADMIN_PASS`
|
||||||
|
|
||||||
## Exposing to the internet
|
## Exposing to the internet
|
||||||
|
|
||||||
Reference config in `caddy`. Designed for: gateway on rpi, Caddy on a server with a public IP, wired through tailscale.
|
`caddy/site.caddy` holds the routes (one `reverse_proxy`, domain from `BEAVER_DOMAIN`); on a shared server it is bind-mounted into that server's Caddy as `projects.d/beaver-agent/`, on its own box `caddy/docker-compose.yml` runs a Caddy around it.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd caddy
|
cd caddy
|
||||||
cp Caddyfile.example Caddyfile
|
cp Caddyfile.example Caddyfile
|
||||||
cp .env.example .env # only for cloudflare
|
cp .env.example .env # BEAVER_DOMAIN, BEAVER_UPSTREAM, Cloudflare token if any
|
||||||
# replace <DOMAIN>; point upstreams at wherever the gateway is reachable
|
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
Set `PUBLIC_BASE_URL=https://<DOMAIN>` in beaver-agent's `.env`. For per-subdomain layout, rewrite the Caddyfile and pass full `public_base_url=...` per frontend in `config.py`.
|
Set `PUBLIC_BASE_URL=https://<DOMAIN>` in beaver-agent's `.env` so advertised endpoints use the outside origin.
|
||||||
|
|
||||||
## Editing config.py on the mac
|
## Editing config.py on the mac
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
|
BEAVER_DOMAIN=beaver.localhost
|
||||||
|
# куда ходить за gateway; из контейнера caddy хост - host.docker.internal
|
||||||
|
BEAVER_UPSTREAM=host.docker.internal:62990
|
||||||
|
# только с acme_dns в Caddyfile
|
||||||
CLOUDFLARE_API_TOKEN=
|
CLOUDFLARE_API_TOKEN=
|
||||||
|
|||||||
+13
-29
@@ -1,40 +1,24 @@
|
|||||||
|
# Скелет для одиночного сервера: глобальный блок и import роутов проекта.
|
||||||
|
# Роуты - в site.caddy, домен и апстрим - в .env.
|
||||||
{
|
{
|
||||||
admin off
|
admin off
|
||||||
|
# Локально - самоподписанные сертификаты. На сервере с Cloudflare
|
||||||
|
# закомментировать local_certs, раскомментировать acme_dns.
|
||||||
|
local_certs
|
||||||
# acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
|
# acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
|
||||||
|
|
||||||
log {
|
log {
|
||||||
format console
|
format console
|
||||||
}
|
}
|
||||||
|
|
||||||
servers {
|
# servers {
|
||||||
trusted_proxies cloudflare
|
# trusted_proxies cloudflare
|
||||||
client_ip_headers Cf-Connecting-Ip
|
# client_ip_headers Cf-Connecting-Ip
|
||||||
}
|
# }
|
||||||
}
|
}
|
||||||
|
|
||||||
<DOMAIN> {
|
(compress) {
|
||||||
redir /anthropic /anthropic/ 308
|
encode zstd gzip
|
||||||
redir /mcp /mcp/ 308
|
|
||||||
redir /admin /admin/ 308
|
|
||||||
redir /md /md/ 308
|
|
||||||
|
|
||||||
handle_path /anthropic/* {
|
|
||||||
reverse_proxy host.docker.internal:62990
|
|
||||||
}
|
|
||||||
handle_path /mcp/* {
|
|
||||||
reverse_proxy host.docker.internal:62991
|
|
||||||
}
|
|
||||||
handle /admin/* {
|
|
||||||
reverse_proxy host.docker.internal:62992
|
|
||||||
}
|
|
||||||
handle_path /md/* {
|
|
||||||
reverse_proxy host.docker.internal:62993
|
|
||||||
}
|
|
||||||
# API разговоров + SSE для панели плагина: handle, не handle_path -
|
|
||||||
# приложение само живёт под /api/…
|
|
||||||
handle /api/* {
|
|
||||||
reverse_proxy host.docker.internal:62994 {
|
|
||||||
flush_interval -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import /etc/caddy/projects.d/*/site.caddy
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ services:
|
|||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
volumes:
|
volumes:
|
||||||
- ./Caddyfile:/etc/caddy/Caddyfile
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
|
# каталог, не файл: git меняет inode, одиночный bind-mount залипает
|
||||||
|
- ./:/etc/caddy/projects.d/beaver-agent:ro
|
||||||
- caddy_data:/data
|
- caddy_data:/data
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Роуты beaver. Один апстрим, префиксы не срезаются: gateway сам живёт под
|
||||||
|
# /anthropic, /mcp, /md, /api, /admin, а / ведёт в админку.
|
||||||
|
# Домен и апстрим - из env сервера (domains.env / .env), tls - забота
|
||||||
|
# серверного Caddyfile.
|
||||||
|
{$BEAVER_DOMAIN} {
|
||||||
|
import compress
|
||||||
|
reverse_proxy {$BEAVER_UPSTREAM:localhost:62990} {
|
||||||
|
# SSE и стримы ответов - без буферизации
|
||||||
|
flush_interval -1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -212,11 +212,6 @@ agents: list[BaseAgent] = [
|
|||||||
|
|
||||||
PUBLIC_BASE_URL = os.environ.get("PUBLIC_BASE_URL", "").rstrip("/")
|
PUBLIC_BASE_URL = os.environ.get("PUBLIC_BASE_URL", "").rstrip("/")
|
||||||
|
|
||||||
|
|
||||||
def _public(suffix: str) -> str | None:
|
|
||||||
return f"{PUBLIC_BASE_URL}{suffix}" if PUBLIC_BASE_URL else None
|
|
||||||
|
|
||||||
|
|
||||||
# §3.8: личка с ботом - домашний фронтенд мастера и веток (General и топики).
|
# §3.8: личка с ботом - домашний фронтенд мастера и веток (General и топики).
|
||||||
# Токен и id - секреты gateway, в env процесса модели они не попадают.
|
# Токен и id - секреты gateway, в env процесса модели они не попадают.
|
||||||
TELEGRAM = (
|
TELEGRAM = (
|
||||||
@@ -230,31 +225,25 @@ TELEGRAM = (
|
|||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Один порт: каждый HTTP-фронтенд живёт под своим путём (/anthropic, /mcp,
|
||||||
|
# /admin, /api, /md), `/` ведёт в админку. Caddy ничего не срезает.
|
||||||
frontends: list[Frontend] = [
|
frontends: list[Frontend] = [
|
||||||
*([TELEGRAM] if TELEGRAM is not None else []),
|
*([TELEGRAM] if TELEGRAM is not None else []),
|
||||||
AnthropicMessagesFrontend(
|
AnthropicMessagesFrontend(),
|
||||||
host="0.0.0.0", port=62990, public_base_url=_public("/anthropic")
|
McpServerFrontend(),
|
||||||
),
|
AdminFrontend(),
|
||||||
McpServerFrontend(host="0.0.0.0", port=62991, public_base_url=_public("/mcp")),
|
|
||||||
AdminFrontend(host="0.0.0.0", port=62992, public_base_url=_public("/admin")),
|
|
||||||
# §3.9: /api показывает всё; дефолт master/branch здесь - запасной на случай
|
# §3.9: /api показывает всё; дефолт master/branch здесь - запасной на случай
|
||||||
# gateway без телеграма, deep - у markdown.
|
# gateway без телеграма, deep - у markdown.
|
||||||
ApiFrontend(
|
ApiFrontend(
|
||||||
host="0.0.0.0",
|
|
||||||
port=62994,
|
|
||||||
public_base_url=_public("/api"),
|
|
||||||
master_agent="beaver-dispatcher",
|
master_agent="beaver-dispatcher",
|
||||||
branch_agent="beaver-dispatcher",
|
branch_agent="beaver-dispatcher",
|
||||||
memory_root=BEAVER,
|
memory_root=BEAVER,
|
||||||
),
|
),
|
||||||
MarkdownFrontend(
|
MarkdownFrontend(
|
||||||
host="0.0.0.0",
|
|
||||||
port=62993,
|
|
||||||
vault_path=CHATS_DIR,
|
vault_path=CHATS_DIR,
|
||||||
default_agent="beaver-opus-high",
|
default_agent="beaver-opus-high",
|
||||||
log_all_chats=True,
|
log_all_chats=True,
|
||||||
chat_path=chat_path,
|
chat_path=chat_path,
|
||||||
public_base_url=_public("/md"),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -277,4 +266,11 @@ texts = ConversationTexts(
|
|||||||
seed=seed_body,
|
seed=seed_body,
|
||||||
)
|
)
|
||||||
|
|
||||||
gateway = Gateway(agents=agents, mcps=mcps, frontends=frontends, texts=texts)
|
gateway = Gateway(
|
||||||
|
agents=agents,
|
||||||
|
mcps=mcps,
|
||||||
|
frontends=frontends,
|
||||||
|
texts=texts,
|
||||||
|
port=62990,
|
||||||
|
public_url=PUBLIC_BASE_URL or None,
|
||||||
|
)
|
||||||
|
|||||||
+2
-5
@@ -48,11 +48,8 @@ services:
|
|||||||
CLAUDE_RUNNER_USER: beaver-runner
|
CLAUDE_RUNNER_USER: beaver-runner
|
||||||
CLAUDE_HOME: /home/beaver-runner
|
CLAUDE_HOME: /home/beaver-runner
|
||||||
ports:
|
ports:
|
||||||
- "${PORT_MESSAGES:-62990}:62990" # anthropic
|
# один порт: /anthropic, /mcp, /admin, /api, /md, / -> админка
|
||||||
- "${PORT_MCP:-62991}:62991" # mcp
|
- "${PORT_GATEWAY:-62990}:62990"
|
||||||
- "${PORT_ADMIN:-62992}:62992" # admin
|
|
||||||
- "${PORT_MARKDOWN:-62993}:62993" # obsidian companion
|
|
||||||
- "${PORT_API:-62994}:62994" # conversations api + sse
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./config.py:/config/config.py:ro
|
- ./config.py:/config/config.py:ro
|
||||||
- ./config.json:/config/config.json:ro
|
- ./config.json:/config/config.json:ro
|
||||||
|
|||||||
Reference in New Issue
Block a user