Make delivery and integration failures explicit
Persist reminder presentations and retry state, atomically complete collapsed deliveries, fall back across away reaches, and block permanent failures visibly (V-715, V-678). Fail closed when enabled integrations lack credentials and keep remote arms explicitly dark (V-691). Give mavweb one sanitized, request-correlated error contract (V-689). Owner explicitly requested direct commits to master.
This commit is contained in:
@@ -22,27 +22,26 @@ caveat is the pointer between them plus the trigger.
|
||||
|
||||
Every entry below came from the 2026-08-10 deep audit
|
||||
(`docs/evals/2026-08-10-repo-audit.md`), except the last, which came from wiring
|
||||
the gate the audit asked for. Nine of the twenty findings are fixed and have no
|
||||
the gate the audit asked for. Twelve of the twenty findings are fixed and have no
|
||||
entry. The unauthenticated mavgpud proxy was V-673. The 20 reachable advisories
|
||||
in the toolchain and `x/text` were V-682. The missing analyzers were V-694, and
|
||||
what they now report is the baseline entry under V-701. The invalid STT and
|
||||
weather responses were V-675/V-676, the stuck TCP close was V-679, and the
|
||||
unbounded PTT path was V-688. The two unguarded
|
||||
invariants were V-692 and V-693, and their guards are described in
|
||||
`docs/routing.md`.
|
||||
`docs/routing.md`. The complete secret inventory and fail-closed integration
|
||||
validation were V-691. The atomic reminder transition was V-678. The traceable,
|
||||
sanitized mavweb problem contract was V-689.
|
||||
|
||||
| limit | severity |
|
||||
| --- | --- |
|
||||
| [Anyone past the proxy can enroll a passkey](security.md#enrollment) | high |
|
||||
| [Passkey credentials are rewritten in place](security.md#credentials) | medium |
|
||||
| [Dialogue persistence errors are swallowed](storage.md#dialogue) | medium |
|
||||
| [The reminder transition is a lost update](storage.md#reminders) | medium |
|
||||
| [A recall miss scans two whole tables](storage.md#recall) | medium |
|
||||
| [mavweb errors cannot be traced](transport.md#errors) | medium |
|
||||
| [Fact enrichment is a 20-call serial waterfall](workers.md#enrichment) | medium |
|
||||
| [A suppressed nudge is phrased anyway](workers.md#nudges) | medium |
|
||||
| [Committed absolute paths pin the build to this box](config.md#paths) | medium |
|
||||
| [The env example omits deployed variables](config.md#secrets) | medium |
|
||||
| [The analyzers pass against a baseline, not zero](dependencies.md#baseline) | medium |
|
||||
| [Domain packages depend on store and IPC types](layering.md#dtos) | low |
|
||||
| [Eleven symbols are unreachable](layering.md#deadcode) | low |
|
||||
|
||||
@@ -8,14 +8,3 @@ workstation model and Python paths. Vendoring hides the `go.mod` problem for an
|
||||
ordinary build. `-mod=mod`, `go mod tidy` and a fresh checkout all fail.
|
||||
Revisit when: anyone clones this repo elsewhere, or a `tidy` is needed.
|
||||
Workaround: build only from this checkout, with the vendor directory.
|
||||
|
||||
## The env example omits deployed variables [#691] {#secrets}
|
||||
|
||||
Costs: a fresh deploy can lose remote speech-to-text or ambient authentication
|
||||
and run on fallback behaviour with an apparently valid config. Three variables
|
||||
are referenced and undocumented: `MAVEN_STT_TOKEN`, `MAVEN_AMBIENT_TOKEN` and
|
||||
`CW2_TOKEN`. V-673 added `MAVEN_GPU_TOKEN` to the example.
|
||||
It is not silent. The loader logs which variables were unset and says whatever
|
||||
they configure is off. What is missing is a startup failure.
|
||||
Revisit when: the box is redeployed from scratch, or a new secret is added.
|
||||
Workaround: read that log line at startup.
|
||||
|
||||
@@ -11,14 +11,6 @@ Revisit when: a restart is reported as losing context. Sooner if a turn starts
|
||||
reading dialogue rows back to him.
|
||||
Workaround: none. The failure is invisible from outside.
|
||||
|
||||
## The reminder transition is a lost update [#678] {#reminders}
|
||||
|
||||
Costs: a concurrent fire and cancel both succeed and the last writer wins.
|
||||
Medium today because cancellation has no surface. High the moment V-622 adds
|
||||
one, and V-622 does not describe this invariant.
|
||||
Revisit when: V-622 starts, whichever comes first.
|
||||
Workaround: none, but the window is small while nothing can cancel.
|
||||
|
||||
## A recall miss scans two whole tables [#681] {#recall}
|
||||
|
||||
Costs: every missed recall reads all of `memory_vectors` and then decodes and
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# Transport
|
||||
|
||||
The HTTP and socket seams. What a client can do to them, and what a shutdown
|
||||
can do to us.
|
||||
|
||||
## mavweb errors cannot be traced [#689] {#errors}
|
||||
|
||||
Costs: some handlers return the raw internal error, which discloses internals.
|
||||
Others return a generic one with no identifier, which cannot be joined to its
|
||||
log line. There is no request-id middleware to join them.
|
||||
Revisit when: a reported UI failure cannot be found in the log.
|
||||
Workaround: read the log by timestamp.
|
||||
+16
-1
@@ -78,7 +78,10 @@ vocabulary size. CW2's 51897 tokens shift seven special token ids. So CW2 is its
|
||||
own transformers service on port 8081 (`deploy/cw2/serve.py`).
|
||||
`stt.HTTPTranscriber` posts raw PCM to it with a bearer token, because audio is
|
||||
the most sensitive thing that crosses this seam. The switch is `workstation.stt`
|
||||
in `deploy/mavend.json`, and deleting the block sends every utterance to mavsttd.
|
||||
in `deploy/mavend.json`. A LAN URL with an empty token fails config validation;
|
||||
`workstation.stt.disabled: true` is the STT-only dark state;
|
||||
`workstation.disabled: true` disables both remote arms. The model has its own
|
||||
`model_disabled` switch because CW2 can remain live without the model proxy.
|
||||
|
||||
**mavgpud runs that service as a second child.** This is not an optimisation.
|
||||
CW2 is a ROCm process on the same card, so it registers on the KFD like any
|
||||
@@ -208,6 +211,18 @@ rule.
|
||||
|
||||
## Web UI conventions
|
||||
|
||||
Every HTTP response carries a server-generated `X-Request-ID`. Error responses
|
||||
use `application/problem+json` with three fields: a sanitized `error`, a stable
|
||||
low-cardinality `code`, and that `request_id`. `writeProblem` is the only error
|
||||
writer: it records the full wrapped error beside the same ID in the mavweb log,
|
||||
while implementation paths, transport messages and credentials never reach the
|
||||
browser. A page that can usefully degrade instead of failing keeps HTTP 200 and
|
||||
renders the same code/request reference beside its sanitized panel error.
|
||||
|
||||
The `/ecosystem` panel uses its HTTP request ID as `X-Correlation-ID` on its
|
||||
direct Nexus, Praxis and Hexis reads. Calls routed through mavend retain
|
||||
mavend's action-scoped correlation contract instead.
|
||||
|
||||
Server-rendered pages share `cmd/mavweb/static/ui.css` (served at `/ui.css`) and
|
||||
the shell partial in `cmd/mavweb/shell.html`. A page opens with
|
||||
`{{template "shellTop" "<page-key>"}}` and closes with `{{template "shellBottom"}}`,
|
||||
|
||||
+5
-2
@@ -113,8 +113,11 @@ read `/slots`, which returns other callers' prompts. mavgpud now reads
|
||||
network without one. Downgrading to loopback instead would look safe and take
|
||||
the model arm down. Maven sends the same token from `workstation.token`, on the
|
||||
completion and on the `/health` probe alike. An unsigned probe answers 401,
|
||||
which Pair reads as a busy card, so a missing token degrades to the resident
|
||||
model rather than breaking a turn. The proxy also
|
||||
which Pair reads as a busy card, but Maven now rejects a live LAN workstation
|
||||
block with a missing token at startup: fallback must describe a temporarily
|
||||
unavailable machine, not hide a broken secret source. `model_disabled: true`
|
||||
is the explicit resident-only state and does not take CW2 down with it. The
|
||||
proxy also
|
||||
allowlists the five paths Maven calls, so a leaked token buys the model API and
|
||||
not llama-server's admin surface.
|
||||
|
||||
|
||||
+18
-2
@@ -67,14 +67,30 @@ Config path: `~/.config/maven/mavend.json`. Full example with all options.
|
||||
|
||||
Omit the `embedder` block entirely to use the deterministic HashEmbedder floor (no ML, no ONNX runtime dependency). Useful for testing or low-resource setups.
|
||||
|
||||
`${NTFY_TOKEN}` and the `${TELEGRAM_*}` vars are expanded from `deploy/telegram.env`, which is gitignored. Copy `deploy/telegram.env.example` and fill it in. Mint a scoped token rather than reusing an admin one. It needs write access to the `maven` topic and nothing else:
|
||||
`deploy/telegram.env.example` is the canonical inventory for every deployed
|
||||
secret, including values whose destination is the root `.env`, the workpc CW2
|
||||
environment file, or `deploy/db_key.env`. Copy values only to the destination
|
||||
named beside them; never commit the populated files. Maven expands the homesrv
|
||||
sink, workstation, and Home Assistant variables from `deploy/telegram.env`.
|
||||
|
||||
Every written integration block is either explicitly disabled or live. A live
|
||||
Telegram, ntfy, LAN workstation model/STT, Home Assistant, ambient, CW2, or
|
||||
encrypted-database configuration with an empty credential fails startup. This
|
||||
keeps a missing env file from quietly becoming fallback behavior. The deployed
|
||||
ntfy is currently `disabled: true`, and the workstation model arm is
|
||||
`model_disabled: true`; the separately credentialed CW2 STT arm remains live.
|
||||
Remove a dark-state flag only after provisioning that arm's credential.
|
||||
|
||||
Mint a scoped ntfy token rather than reusing an admin one. It needs write access
|
||||
to the `maven` topic and nothing else:
|
||||
|
||||
```sh
|
||||
ntfy access maven maven write-only
|
||||
ntfy token add --expires=never maven
|
||||
```
|
||||
|
||||
Deleting the `ntfy` block turns the reach off, and that is not a no-op. The routing table sends sev3-away nudges and away reminders to ntfy and nowhere else. With no sink wired they hit a nil and vanish, leaving no log line and no `delivery_attempts` row (V-649).
|
||||
`disabled: true` keeps a documented ntfy block dark. When enabled, reminders
|
||||
try ntfy and fall through to Telegram; delivery attempts record each reach.
|
||||
|
||||
## mavsttd — STT worker (optional, remote whisper.cpp)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user