Files
Maven/deploy/ecosystem/nginx.conf
T
kami 7f42cc73be Address PR review comments on 50, 52, 53, 54, 59, 61
Seven fixes, each answering a line comment on the stack.

**Weather no longer invents Moscow** (PR 50). extractWeatherLocation returned
the string "Moscow" when he named no city and voice.weather.default_location
was unset — a made-up answer presented as fact, which is the one thing maven
must never do. It returns "" now and the query path says it does not know.

**Digest statuses are a defined type** (PR 50). DigestStatus string plus the
three constants, so a rule name cannot reach the status column.

**Quiet-mode negation is not adjacency** (PR 53). The OFF list carried
{"не","тих"}, an adjacency pattern, so "не надо тихий режим" missed OFF, hit
the ON pattern {"тих","режим"}, and asking for quiet mode to stop turned it
on. Negators are scanned over the whole utterance now, with the two ON phrases
that are themselves built on "не" excluded. "тихий режим выключи" works too,
which it did not before.

**Pattern stability uses a median band** (PR 54). max/min over the extremes
asked whether every gap resembles every other gap, so 7,7,7,7,20 — four clean
weeks and one holiday — was thrown away at a ratio of 2.9. Each interval is
now tested against the median and 70% must be in band, and the reported
interval is the median of the in-band ones, so a holiday no longer drags a
weekly habit to "every 9.6 days". The reviewer's 5,8,10,3 is still rejected.

**The weekday profile stops reciting everyday habits** (PR 59). "What do I do
on Saturdays?" answered "you drink water" — true, and useless, because it is
equally true of every other day. Activities that are habits on six or more
weekdays move to Profile.Everyday and are read back as daily habits instead of
as an answer about that day.

**Russian phrase tables move out of Go** (PR 59, PR 61). The behaviour glosses
and weekday names, and the task capture/urgency/list vocabulary, are now
behavior_ru.json and task_phrases.json, embedded with go:embed. Single-binary
deploy is unchanged; wording edits are no longer source diffs.

**nginx template stops taking nginx down** (PR 52). Two host-side failure
modes, both plausible causes of today's crash. The $connection_upgrade map is
fatal when duplicated, so it moved to its own nginx-upgrade-map.conf with a
grep-first note. And `listen 10.42.0.1:80` fails with EADDRNOTAVAIL when wg0
is not up yet, so nginx exits on a reboot that beats WireGuard — the header
now documents net.ipv4.ip_nonlocal_bind.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TrVSBKe3RFDF4fGYKWYQnX
2026-08-01 12:50:47 +04:00

127 lines
4.6 KiB
Nginx Configuration File

# Reverse-proxy Maven's own web UI plus the three sibling admin UIs. Drop into
# your nginx sites (or the nginx-panel app) and reload. Assumes the compose
# publishes each service on 127.0.0.1:<port>. Add TLS (certbot / your existing
# cert block) per server.
#
# NOTE: hexis.<domain> previously pointed at the MCP tool — repoint that
# elsewhere first (the app now owns hexis.*).
#
# 10.42.0.1 and 192.168.1.104 below are THIS BOX's WireGuard and LAN
# addresses (homesrv) — these admin UIs have no auth of their own, so the
# explicit bind + allow/deny below is what keeps them off the open internet.
# On a different box, replace both addresses with that box's wg and LAN IPs.
# Do NOT "fix" a failed bind by reverting to `listen 80` (all interfaces) —
# that removes the only access control these containers have.
#
# BEFORE YOU RELOAD — two ways this file takes nginx down, both host-side.
# These blocks are for the HOST nginx, not for anything inside the compose;
# the containers publish on 127.0.0.1 and have no nginx of their own.
#
# 1. Binding an address that does not exist yet. `listen 10.42.0.1:80` fails
# with EADDRNOTAVAIL if wg0 is down, and nginx exits rather than starting
# without it — so a reboot that brings nginx up before WireGuard leaves the
# box with no web at all. Allow the bind to succeed regardless:
# sysctl -w net.ipv4.ip_nonlocal_bind=1
# echo 'net.ipv4.ip_nonlocal_bind = 1' > /etc/sysctl.d/99-nginx-bind.conf
# Ordering nginx after the wg interface works too, but only until the next
# time the tunnel restarts.
#
# 2. A duplicate $connection_upgrade map. That is a fatal config error, so the
# map now lives in nginx-upgrade-map.conf and is installed separately —
# read the note at the top of that file first.
#
# `nginx -t` catches the second and not the first. Run it anyway, every time.
# maven.<domain> → mavweb (docker-compose.yml publishes it on 127.0.0.1:9201).
# Same bind + ACL as the siblings, and for a stronger reason: mavweb serves
# POST /tools, which defines argv that internal/tool EXECUTES, plus POST
# /routines, /api/revert and /api/chat (Vikunja #317). Without
# -webauthn-origin/-webauthn-rpid mavweb has no auth of its own, so this block
# is the auth. If you add TLS and a basic-auth/oauth2-proxy layer, keep the
# allow/deny anyway — belt and braces on an RCE surface.
#
# WebSocket upgrade matters here: /ws carries push-to-talk audio, so the
# Upgrade/Connection headers below are required, not decoration. They reference
# $connection_upgrade, which this file does NOT define — see
# nginx-upgrade-map.conf and point 2 above.
server {
listen 10.42.0.1:80;
listen 192.168.1.104:80;
server_name maven.kvmx.ru;
allow 10.42.0.0/24;
allow 192.168.1.0/24;
deny all;
# push-to-talk uploads raw PCM; the default 1m is enough for a short
# utterance but not for a long one.
client_max_body_size 32m;
location / {
proxy_pass http://127.0.0.1:9201;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s; # an LLM turn can take minutes on the iGPU
}
}
server {
listen 10.42.0.1:80;
listen 192.168.1.104:80;
server_name nexus.kvmx.ru;
allow 10.42.0.0/24;
allow 192.168.1.0/24;
deny all;
location / {
proxy_pass http://127.0.0.1:9740;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 10.42.0.1:80;
listen 192.168.1.104:80;
server_name praxis.kvmx.ru;
allow 10.42.0.0/24;
allow 192.168.1.0/24;
deny all;
location / {
proxy_pass http://127.0.0.1:8989;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 10.42.0.1:80;
listen 192.168.1.104:80;
server_name hexis.kvmx.ru;
allow 10.42.0.0/24;
allow 192.168.1.0/24;
deny all;
location / {
proxy_pass http://127.0.0.1:9741;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}