Merge branch 'fix/g07' into fix/integrated

# Conflicts:
#	internal/ipc/api.go
#	internal/ipc/client.go
#	internal/llm/client.go
This commit is contained in:
kami
2026-08-01 14:36:48 +04:00
39 changed files with 1913 additions and 215 deletions
+70 -11
View File
@@ -65,7 +65,13 @@ What it does and does not do:
response at 2 MiB and redirects at 3, and makes at most one request per host
per second. See `internal/webfetch`;
- how far each feed was read is stored as a config fact `rss:latest:<name>`, so
a restart does not re-note yesterday's headlines.
a restart does not re-note yesterday's headlines. A feed whose items carry no
dates gets the same mark, and the first poll after a restart takes those items
as already read rather than writing them all again;
- `max_items` paces, it does not drop: a burst larger than the cap arrives over
the following polls, oldest first;
- feed notes are **not** part of recall. "что я говорил про X" searches what he
said; headlines are read back only by asking about the feeds.
### Reading a page (`crawl`, also off by default)
@@ -89,13 +95,23 @@ switched:
a fallback and not a habit;
- `watches` re-reads a fixed list on its interval and writes a note when the
text changed. Like the feeds, it announces nothing;
- the answer path sits **last** in the query chain, behind his memory, his notes
and (once wired) the local Kiwix ZIMs. A local read costs nothing;
- the answer path sits behind his memory and his notes, and ahead of the model
answering from what it remembers. Kiwix is not wired into the chain yet. A
local read costs nothing, so anything local goes first;
- `robots.txt` is fetched first and obeyed with no override; a `Disallow` is a
refusal she says out loud. `Crawl-delay` is honoured;
refusal she says out loud. `Crawl-delay` is waited out before the page is
fetched, and a delay longer than the turn fails the read instead of hanging
it. A `robots.txt` that answers 5xx refuses the crawl — a broken server is
not permission;
- `allow_hosts` limits on-demand reading to those hosts and nothing else.
Watched pages' hosts are reachable by the scheduled crawler whether listed or
not, but a watch does **not** widen what he may ask her to read;
- same guarded fetcher as the feeds: allowlist/denylist, no private addresses,
size cap, redirect cap, timeout, one request per host per second;
- dedup state is the config fact `crawl:hash:<name>`.
- dedup state is the config fact `crawl:hash:<name>`;
- like feed notes, watch notes are kept out of recall (`store.ReadSourcePrefixes`).
Text from someone else's page is not something he said, so it must not come
back as an answer to a question about him. Watch notes are visible on `/dash`.
## Not yet verified / host-dependent
@@ -132,18 +148,61 @@ it), with paths as they exist **on the host**, not inside a container:
"source_dir": "/home/kami/apps/Maven",
"install_dir": "/home/kami/apps/Maven",
"snapshot_dir": "/var/lib/maven-snapshots",
"source_rollback": "git",
"binaries": ["mavend", "mavweb", "mavsttd", "mavttsd", "mavwaked",
"mavenclient", "mavpoll", "mavcaldav", "mavmaild"],
"mavenclient", "mavpoll", "mavcaldav", "mavmaild", "mavupdate"],
"config_files": ["deploy/mavend.json"],
"restart_cmd": ["docker", "compose", "up", "-d", "--build"],
"health_socket": "/var/lib/docker/volumes/maven_sockets/_data/mavend.sock",
"restart_cmd": ["docker", "compose", "up", "-d", "--build", "mavend"],
"health_socket": "/run/maven-host/mavend.sock",
"health_timeout_sec": 120
}
```
`snapshot_dir` must be outside `install_dir` (a restore must not read from what
the install writes) and `health_socket` is required: an update that cannot check
its own result cannot roll itself back, so the config is refused without one.
`snapshot_dir` must be outside both `install_dir` and `source_dir` (a restore
must not read from what the install writes, and a snapshot dir inside the tree
lands in the docker build context). `health_socket` is required: an update that
cannot check its own result cannot roll itself back, so the config is refused
without one.
**`source_rollback` is what makes a rollback real on this deployment.** Compose
builds the image from the tree — the Dockerfile copies `cmd/` and `internal/`
and runs the build in the builder stage, and `.dockerignore` keeps the host
binaries out — so `install_dir` is the tree, `install` is a no-op, and putting
the old binaries back puts back bytes nothing reads. A rollback that only did
that would rebuild the same bad image and burn a second health timeout proving
it. With `"source_rollback": "git"` the commit is recorded before the update and
checked back out before the restart, so the restore is of the thing that
actually gets deployed. It requires a clean tree: `apply` refuses to start with
uncommitted changes, because the recorded commit would not describe what is
deployed and the forced checkout on the way back would delete the work. It also
means a rollback moves every tracked file, `deploy/mavend.json` included, so on
this deployment a config edit belongs in a commit.
Leaving `source_rollback` out is only valid when `install_dir` holds what
actually runs. `Validate` refuses the combination of "same dir" and "no way to
put the source back" at startup rather than at the one rollback that mattered.
**The socket has to be one the account running `mavupdate` can open.** The
compose stack keeps IPC in a named volume, whose host path
(`/var/lib/docker/volumes/maven_sockets/_data`) is under a `drwx--x--- root
root` directory, and the socket itself is 0600 owned by the container's uid
10001. A non-root `mavupdate` gets EACCES on the dial, which reports as
`update: cannot open the health socket` rather than as a daemon that will not
answer. Bind-mount the socket dir to a host path he owns and run the daemon
under his uid instead:
```yaml
mavend:
user: "1000:1000"
volumes:
- /run/maven-host:/run/maven
```
Do **not** work around it with `sudo mavupdate apply`. `verify` runs `make
build` and `make test` in `source_dir`, and as root that leaves root-owned
binaries, object files and a build cache in the working tree, so the next
ordinary `make` fails. `Verify` refuses to run as root over a tree owned by
someone else for exactly that reason.
Then: