auto: docs sync 2026-07-12T00:00:23+00:00
Files changed: ", c.get(k))\nPY\n\\\"\n\"" engine/config.yaml engine/lib/lint.py engine/lib/vocab_gen.py engine/lint-report.md engine/sweep.sh vault/.obsidian/workspace.json vault/archive/projects/meshai-native-fire-severity-audit-cc-handoff.md vault/archive/projects/vaultwarden-plan.md vault/docs/matrix/matrix_host.md vault/docs/matrix/synapse.md vault/docs/services/services.md vault/docs/software/authentik.md vault/docs/software/caddy.md vault/docs/software/dns.md vault/docs/software/recon.md vault/docs/software/searxng.md vault/glossary.md vault/notes/echo6-landing-page-data-export.md vault/projects/matrix-synapse-deployment.md vault/projects/meshai.md vault/projects/meshtastic-headscale-runbook.md vault/projects/mmud-project.md vault/runbooks/add-peertube-channel.md vault/runbooks/authentik-access-groups.md vault/runbooks/authentik-create-invitation.md vault/runbooks/authentik-oidc-application.md vault/runbooks/authentik-upgrade.md vault/runbooks/expose-service-contabo.md vault/runbooks/lxc-service-migration.md vault/runbooks/mailcow-create-mailbox.md vault/runbooks/meshtastic-sidecar-node.md vault/runbooks/meshtasticd-sim-nodes-runbook.md vault/runbooks/proxmox-create-ubuntu-vm.md vault/runbooks/recon-operations.md vault/runbooks/recon-service-integration.md vault/runbooks/syncthing-add-node.md
This commit is contained in:
parent
4a7a41df93
commit
cce29c595d
37 changed files with 336 additions and 262 deletions
|
|
@ -10,12 +10,12 @@ related:
|
|||
- [[mautrix_signal]]
|
||||
- [[caddy]]
|
||||
- [[lxc-service-migration]]
|
||||
updated: 2026-06-18
|
||||
updated: 2026-07-11
|
||||
---
|
||||
# Matrix Synapse Deployment
|
||||
|
||||
**Status:** Deployed 2026-02-15, migrated to Contabo 2026-02-15
|
||||
**Target:** Contabo VPS (5.189.158.149 / 100.64.0.1)
|
||||
**Status:** Deployed 2026-02-15, migrated to Contabo 2026-02-15. Migrated to edge2 CT 106 2026-06-19.
|
||||
**Target (historical, at time of deployment):** Contabo VPS (5.189.158.149 / 100.64.0.1)
|
||||
**URLs:** https://matrix.echo6.co ([[synapse]]), https://element.echo6.co (Element Web)
|
||||
**Server Name:** echo6.co (federated identity: @user:echo6.co)
|
||||
|
||||
|
|
@ -25,9 +25,11 @@ updated: 2026-06-18
|
|||
|
||||
| Component | Detail |
|
||||
|-----------|--------|
|
||||
| Host | Contabo VPS (5.189.158.149 / 100.64.0.1) |
|
||||
| Host (historical, at deployment time) | Contabo VPS (5.189.158.149 / 100.64.0.1) |
|
||||
| Host (current) | edge2 CT 106 (100.64.0.37) — migrated 2026-06-18 |
|
||||
| Docker [[services]] | Synapse (127.0.0.1:8008), Element Web (127.0.0.1:8088), PostgreSQL 16 |
|
||||
| Reverse proxy | Contabo [[caddy]] (auto ACME certs) |
|
||||
| Reverse proxy (historical) | Contabo [[caddy]] (auto ACME certs) |
|
||||
| Reverse proxy (current) | edge2 host Caddy |
|
||||
| SSO | [[authentik]] OIDC → communication-users group |
|
||||
| Federation | Well-known delegation on echo6.co base domain (served by utility Caddy) |
|
||||
| Compose path | `/opt/matrix/docker-compose.yml` |
|
||||
|
|
@ -297,7 +299,7 @@ If `echo6.co` doesn't have a cert yet, issue one via acme.sh following the same
|
|||
|
||||
### dnsmasq split DNS
|
||||
|
||||
Add to `/etc/dnsmasq.d/tailscale-dns.conf` on Contabo:
|
||||
**Historical (Contabo, pre-2026-06-19 migration).** Was added to `/etc/dnsmasq.d/tailscale-dns.conf` on Contabo:
|
||||
|
||||
```
|
||||
address=/matrix.echo6.co/100.64.0.8
|
||||
|
|
@ -306,12 +308,14 @@ address=/element.echo6.co/100.64.0.8
|
|||
|
||||
Both point to the Utility Caddy Tailscale IP (100.64.0.8), which proxies to CT 108.
|
||||
|
||||
Restart dnsmasq:
|
||||
Restart dnsmasq (historical — ran on Contabo):
|
||||
|
||||
```bash
|
||||
ssh root@100.64.0.1 "systemctl restart dnsmasq"
|
||||
```
|
||||
|
||||
**Current (post-migration):** Matrix and Element now run on edge2 CT 106 (100.64.0.37), fronted by edge2's own host Caddy — see [[services]] and [[matrix_host]]. dnsmasq split-DNS on the old Contabo host is no longer part of this path.
|
||||
|
||||
---
|
||||
|
||||
## Step 8: Configure Authentik SSO
|
||||
|
|
|
|||
|
|
@ -1,260 +0,0 @@
|
|||
# MeshAI Audit Handoff — Native Fire Severity Regression
|
||||
|
||||
**For:** Claude Code (CC), operating from cortex against the MeshAI LXC (192.168.1.144)
|
||||
**Repo:** zvx-echo6/meshai (deployed via docker compose on the LXC)
|
||||
**Date of audit:** 2026-07-08, against HEAD `aef9877` (#95)
|
||||
**Scope:** One minimal backend fix in `work/meshai/env/store.py`, plus a mandatory DB pre-seed. Nothing else. No drive-by refactors.
|
||||
|
||||
---
|
||||
|
||||
## 0. Standing operational rules (non-negotiable)
|
||||
|
||||
1. **SSH prereq gate.** Before ANY work: verify SSH from cortex to the MeshAI LXC (192.168.1.144) using the configured method in `~/.ssh/config`. If auth fails, STOP and report. Never assume SSH works.
|
||||
2. **Never use `docker compose run`.** Ever. Use `docker exec` against the running container, or host tooling.
|
||||
3. **Verbatim diffs only.** Apply the diff in §10 exactly. If the anchor lines don't match, STOP and report — do not improvise.
|
||||
4. **Plain-English explanation before every commit.** Matt's understanding is the gate. Present the summary, wait for his confirmation, then commit.
|
||||
5. **Every status report back to Matt ends with `sudo docker ps`.**
|
||||
6. **Rebuild/deploy is ONLY:** `sudo docker compose build meshai && sudo docker compose up -d`.
|
||||
7. **Mesh-spam stop gate** — see §1. Binding.
|
||||
|
||||
---
|
||||
|
||||
## 1. ⚠️ MESH-SPAM STOP GATE — BINDING
|
||||
|
||||
Deploying the fix in this document **will release a burst of fire broadcasts** unless the pre-seed in §9 is completed and verified first.
|
||||
|
||||
Mechanics: every fire currently blocked by this bug retries its "New" broadcast **on every poll** and dies silently at the dispatcher severity floor. Because delivery never succeeds, the decider's commit never runs, so its `fires` row keeps `last_broadcast_at = NULL`. The first poll after the fixed code starts will pass **every one of those fires at once** — and there is no pacer on the native path (`FirePacer` is wired to the Central consumer only, `main.py:128–136`; its own docstring says it serves the Central reconnect/drain path).
|
||||
|
||||
**Rule:** Phase −1 (§7) and Phase 0 (§8) are read-only and may run immediately. **Do not execute anything in §9 onward until Matt replies verbatim: `I know and I accept`.** When you reach that point, report the burst size from Phase 0 step D and ask for the ack explicitly. Even with the pre-seed done, note to Matt that legitimate post-fix fire traffic will begin (that's the point), and multiple genuinely-new fires arriving in one poll batch will emit together.
|
||||
|
||||
---
|
||||
|
||||
## 2. Symptom
|
||||
|
||||
Since the central→native swap (Jul 4–6), the mesh carries only WZDx, NWS weather alerts, satpass, and band conditions. Fire alerts — which should be active in southern Idaho in July — are silent. (Avalanche silence is seasonal and expected; it is NOT part of this problem.)
|
||||
|
||||
## 3. Root cause — plain English
|
||||
|
||||
The central→native swap dropped the decider's severity promotion, and native fire events now die at the dispatcher's `min_severity` floor — silently, with no log line and no drop counter.
|
||||
|
||||
Evidence chain (all verified by reading the code at HEAD):
|
||||
|
||||
1. **Central path (old behavior):** `central/wfigs_handler.py:238, 348` stamped `data["_severity_override"] = "priority"` on every fire New/Update. `central/consumer.py:649–654` promoted it onto the Event: `severity=sev_override or map_severity(...)`. Every fire reached the dispatcher as **priority**.
|
||||
2. **Native adapter severity:** `env/fires.py` (~lines 139–148) assigns severity by anchor proximity — `"priority"` only within 25 km of a region anchor, **`"routine"` otherwise**. `to_event` (line 333+) passes it through unchanged.
|
||||
3. **The decider still stamps the override:** `notifications/gating/fire.py::_broadcast_patch` sets `"_severity_override": "priority"` on every New and Update GateResult. The intent is unchanged from Central.
|
||||
4. **The promotion is lost:** `env/store.py::_emit_event` (line 678+) builds the Event from `adapter.to_event()` FIRST, then merges the decider patch into **`event.data` only** (`event.data.update(gate.data_patch)`, ~line 731). Nothing anywhere in the native delivery path promotes `_severity_override` onto `event.severity` — verified by grep across dispatcher, composer, and events modules: zero consumers.
|
||||
5. **The floor:** `notifications/pipeline/dispatcher.py` Section 2 (~line 558): `if event_rank < SEVERITY_RANK[tog.min_severity]: return` — a bare return. **No log, no counter** (the four persisted counters cover cold-start/stale/cooldown/dedup, not the severity floor). The matrix path (Section 1.5) has an equivalent per-cell floor with an "authoritative no-send" return.
|
||||
6. **The default floor is "priority":** `config.py:641` (`NotificationToggle.min_severity: str = "priority"`) and again at the factory default, `config.py:681`.
|
||||
|
||||
Net: routine-severity fire → silent drop, every poll, forever. Fire is forced through the decider regardless of cutover state (`NATIVE_ALWAYS_DECIDE`, `notifications/cutover.py:44`), so every native fire New/Update is affected.
|
||||
|
||||
**Timeline:** WFIGS→decider migration `8bc9b14` (Jul 4, 22:51), received-delta gate `0c76a80` (Jul 5), native WFIGS routed through decider `8d61b16` (Jul 6, 15:35). Fire silence began at the native flip — matches the operator's report exactly.
|
||||
|
||||
## 4. Why the surviving feeds survive
|
||||
|
||||
They handle severity in-adapter, so they never depended on the lost promotion:
|
||||
- `env/satpass.py:250` — reads `severity=data.get("_severity_override", "routine")` itself. **This is the correct pattern the fix replicates at the store level.**
|
||||
- `env/nws.py:39` — derives severity in-adapter via `_map_nws_severity` from CAP severity; warnings clear the floor.
|
||||
- `env/wzdx.py:365` — full closures are `"priority"`.
|
||||
- Band conditions — scheduled broadcaster (`BandConditionsScheduler`) that calls the dispatcher directly.
|
||||
|
||||
Of the six deciders that emit `_severity_override` (fire, nws, quake, swpc, avalanche, firms), only **firms** (`env/firms.py:456`) and **satpass** consume it in-adapter.
|
||||
|
||||
## 5. Ruled out during the audit
|
||||
|
||||
- Avalanche: out of season; expected silence. Not a bug.
|
||||
- Received-delta gate (`_delta_emit`, commit `0c76a80`): fires deliberately bypass it (`store.py::_ingest`, `nifc` branch) — the decider is the fire gate. Not the drop point.
|
||||
- FirePacer black hole: pacer is Central-consumer-only; native fires go straight to the bus. Not a factor.
|
||||
- Toggle mapping: `wildfire_declared`/`wildfire_incident` both map to the `fire` toggle (`notifications/categories.py:~380–390`). Not a factor.
|
||||
- Coverage filter: fail-open for non-weather categories; fires carry centroids. Not the primary drop.
|
||||
- ToggleFilter/inhibitor/grouper: all log their drops; fire family is presumed enabled (verify in Phase 0).
|
||||
|
||||
## 6. Secondary findings — explicitly OUT OF SCOPE for this change (backlog)
|
||||
|
||||
1. **Restart-window ignition swallowing.** `_fires_seeded` is per-process (`store.py::_ingest_fires`); every restart's first poll silent-seeds any fire that first appeared while the container was down — it is never announced as New. Deliberate anti-backlog design, but during dense deploy periods in fire season it eats real ignitions. Needs a design decision (e.g., persisted seen-baseline), not a hotfix.
|
||||
2. **Zero-acre New fires deferred.** `env/fires.py::to_event` returns None when `not acres`; fresh WFIGS ignitions often report 0 acres initially. Self-heals when acreage posts (row exists with NULL `last_broadcast_at` → decider fires "New"), but delays first announcements.
|
||||
3. **Same bug class armed for quake/swpc/avalanche/nws.** Their deciders emit `_severity_override` with no native consumer; currently masked behind `MESHAI_CUTOVER_CATEGORIES` shadow gating. The §10 fix resolves this at the shared choke point for all cut-over categories too.
|
||||
4. **No native fire pacer.** Multiple legit new fires in one poll batch will emit back-to-back. Consider extending FirePacer to the native emit path later.
|
||||
|
||||
---
|
||||
|
||||
## 7. Phase −1 — SSH prereq gate (mandatory first step)
|
||||
|
||||
From cortex:
|
||||
|
||||
```bash
|
||||
ssh <configured-alias-or-user>@192.168.1.144 'echo SSH_OK && hostname'
|
||||
```
|
||||
|
||||
If this fails for any reason: STOP. Report to Matt. Do not proceed.
|
||||
|
||||
Then locate the deployment on the LXC (do not guess paths):
|
||||
|
||||
```bash
|
||||
sudo docker ps --format '{{.Names}}\t{{.Image}}' | grep -i meshai
|
||||
find /opt /root /home -maxdepth 3 -name docker-compose.yml 2>/dev/null | xargs grep -l meshai 2>/dev/null
|
||||
```
|
||||
|
||||
Record the compose project directory and the container name (assumed `meshai` below — substitute if different). Confirm `git status` in the repo checkout is clean before any edit; if dirty, STOP and report.
|
||||
|
||||
## 8. Phase 0 — read-only diagnostics (safe to run now)
|
||||
|
||||
Run all of these and report results to Matt before anything else. Determine the DB path first:
|
||||
|
||||
```bash
|
||||
sudo docker exec meshai ls /app/data
|
||||
```
|
||||
|
||||
**A. Are fire events reaching the bus and dying downstream?** (Diagnosis predicts: repeatedly nonzero.)
|
||||
|
||||
```bash
|
||||
sudo docker logs meshai --since 24h 2>&1 | grep -c "Emitted nifc"
|
||||
sudo docker logs meshai --since 24h 2>&1 | grep "Emitted nifc" | tail -5
|
||||
```
|
||||
|
||||
**B. Live fire family config — the floor:**
|
||||
|
||||
```bash
|
||||
sudo docker exec meshai grep -B2 -A8 -i "fire" /app/config/config.yaml | grep -i -B1 -A4 "min_severity\|enabled\|region"
|
||||
```
|
||||
|
||||
**C. Cutover state (determines blast radius of the bug class):**
|
||||
|
||||
```bash
|
||||
sudo docker exec meshai printenv MESHAI_CUTOVER_CATEGORIES
|
||||
```
|
||||
|
||||
**D. Burst size if fixed today** — this number goes in the stop-gate report to Matt:
|
||||
|
||||
```bash
|
||||
sudo docker exec meshai sqlite3 /app/data/<DBFILE> \
|
||||
"SELECT COUNT(*) FROM fires WHERE last_broadcast_at IS NULL;"
|
||||
sudo docker exec meshai sqlite3 /app/data/<DBFILE> \
|
||||
"SELECT irwin_id, incident_name, current_acres, current_contained_pct, last_broadcast_at
|
||||
FROM fires ORDER BY last_event_at DESC LIMIT 20;"
|
||||
```
|
||||
|
||||
**E. Dispatcher drop counters** (severity-floor drops are NOT counted — expected to look quiet; that's part of the finding):
|
||||
|
||||
```bash
|
||||
sudo docker exec meshai sqlite3 /app/data/<DBFILE> "SELECT * FROM dispatcher_state;"
|
||||
```
|
||||
|
||||
**Decision matrix:**
|
||||
- A nonzero AND B shows fire `min_severity: priority` (or matrix fire cells with priority floors) → **diagnosis confirmed.** Proceed to the stop gate.
|
||||
- A is ZERO → fires aren't reaching the bus at all; the diagnosis does NOT hold as-is. STOP. Report findings; do not apply the fix. (Check adapter enablement, NIFC fetch errors: `sudo docker logs meshai --since 24h 2>&1 | grep -i "nifc\|wfigs" | tail -20`.)
|
||||
- A nonzero but B shows fire floor already `routine` → drop is elsewhere (matrix cells or region scope). STOP. Report B plus the matrix config; do not apply the fix blind.
|
||||
|
||||
End the Phase 0 report with `sudo docker ps`.
|
||||
|
||||
---
|
||||
|
||||
## ⛔ EVERYTHING BELOW REQUIRES MATT'S VERBATIM ACK: `I know and I accept`
|
||||
|
||||
Report the Phase 0 results including the §8-D burst count, restate the risk (§1), and wait.
|
||||
|
||||
## 9. Phase 1 — pre-seed the fires table (mandatory BEFORE the fixed code ever starts)
|
||||
|
||||
Run against the **still-running old container** (old code keeps suppressing at the floor, so this changes nothing live; after pre-seed the decider simply returns cooldown for those rows):
|
||||
|
||||
```bash
|
||||
sudo docker exec meshai sqlite3 /app/data/<DBFILE> "
|
||||
UPDATE fires
|
||||
SET last_broadcast_at = strftime('%s','now'),
|
||||
first_broadcast_at = COALESCE(first_broadcast_at, strftime('%s','now')),
|
||||
last_broadcast_acres = current_acres,
|
||||
last_broadcast_contained = current_contained_pct
|
||||
WHERE last_broadcast_at IS NULL;"
|
||||
```
|
||||
|
||||
Verify — MUST return 0 before proceeding:
|
||||
|
||||
```bash
|
||||
sudo docker exec meshai sqlite3 /app/data/<DBFILE> \
|
||||
"SELECT COUNT(*) FROM fires WHERE last_broadcast_at IS NULL;"
|
||||
```
|
||||
|
||||
If `sqlite3` is not present in the image: stop the container, run the same SQL with host `sqlite3` against the bind-mounted data directory (check the volume mapping in docker-compose.yml), verify count 0, and only then continue. Do NOT start the new image before the verify shows 0.
|
||||
|
||||
Consequence to state to Matt: currently-active fires will not get a retroactive "New" announcement; they will broadcast on their next real growth/containment change. Only genuinely new post-deploy ignitions announce as New.
|
||||
|
||||
## 10. Phase 2 — the fix (verbatim diff)
|
||||
|
||||
Target: `work/meshai/env/store.py`, inside `_emit_event` (function starts line 678). Anchor by grepping `event.data.update(gate.data_patch)` — it occurs once. Verify both anchor lines exist verbatim, then apply exactly:
|
||||
|
||||
```diff
|
||||
--- a/work/meshai/env/store.py
|
||||
+++ b/work/meshai/env/store.py
|
||||
@@ def _emit_event(self, adapter, raw_evt: dict):
|
||||
# Apply data_patch into event.data
|
||||
event.data.update(gate.data_patch)
|
||||
+ # Promote decider overrides onto the Event itself, mirroring
|
||||
+ # the Central path (central/consumer.py:649-653). The gating
|
||||
+ # deciders stamp _severity_override (fire: "priority" on every
|
||||
+ # New/Update) and, for fire New/tombstone, a category
|
||||
+ # override. Merging them into event.data alone leaves
|
||||
+ # event.severity at the adapter's value ("routine" for fires
|
||||
+ # >=25 km from an anchor), which silently fails the
|
||||
+ # toggle/matrix min_severity floors (default "priority",
|
||||
+ # config.py:641). Native and Central must share identical
|
||||
+ # broadcast decisions.
|
||||
+ _sev_override = gate.data_patch.get("_severity_override")
|
||||
+ if _sev_override:
|
||||
+ event.severity = _sev_override
|
||||
+ _cat_override = gate.data_patch.get("category")
|
||||
+ if _cat_override:
|
||||
+ event.category = _cat_override
|
||||
if gate.commit is not None:
|
||||
event.data["_on_broadcast_committed"] = gate.commit
|
||||
```
|
||||
|
||||
Notes for review:
|
||||
- `Event` is a plain mutable `@dataclass` (`notifications/events.py:38`); attribute assignment is valid.
|
||||
- Category promotion is intentional and safe: `wildfire_declared` maps to the `fire` toggle (`categories.py:~387`); formatter branch selection already falls back to `data["category"]`; cooldown keys become category-scoped, matching Central behavior (New vs Update cooldowns separate).
|
||||
- This is the shared choke point: any category later added to `MESHAI_CUTOVER_CATEGORIES` (quake, swpc, avalanche, nws) gets the correct promotion automatically.
|
||||
|
||||
**Tests:** if a pytest environment exists on the LXC checkout, run the targeted subsets and compare against the known 10-failure baseline (per commit `0c76a80`, 1697 passed):
|
||||
|
||||
```bash
|
||||
python3 -m pytest work/tests -k "fire or store or dispatch or gating" -q
|
||||
```
|
||||
|
||||
If no local pytest env exists, note that in the report and proceed — the change is 16 lines with fully-traced semantics. Do NOT use `docker compose run` to run tests.
|
||||
|
||||
## 11. Phase 3 — commit and deploy
|
||||
|
||||
1. Present Matt a plain-English summary of the change (what/why in 3–5 sentences, drawn from §3). Wait for his confirmation. His understanding is the gate.
|
||||
2. Commit with a message in the repo's convention, e.g.:
|
||||
`fix(native): promote decider _severity_override/category onto Event — restores Central-parity fire severity (silent min_severity floor drop)`
|
||||
3. Deploy exactly:
|
||||
|
||||
```bash
|
||||
sudo docker compose build meshai && sudo docker compose up -d
|
||||
```
|
||||
|
||||
## 12. Phase 4 — post-deploy verification
|
||||
|
||||
Watch the first 2–3 NIFC poll cycles (read the poll interval from the wfigs/nifc adapter config during Phase 0):
|
||||
|
||||
```bash
|
||||
sudo docker logs -f meshai 2>&1 | grep -Ei "Emitted nifc|matrix: dispatched|dispatched|SUPPRESSED|cold-start"
|
||||
```
|
||||
|
||||
Expected: NO burst at startup (pre-seed verified 0); cold-start seed lines for any brand-new first-sight fires; subsequent genuine New/growth events showing `matrix: dispatched` or toggle-path delivery lines. Confirm on-air on the mesh when the first legit event lands.
|
||||
|
||||
Then confirm the counters are stable and report:
|
||||
|
||||
```bash
|
||||
sudo docker exec meshai sqlite3 /app/data/<DBFILE> "SELECT * FROM dispatcher_state;"
|
||||
sudo docker ps
|
||||
```
|
||||
|
||||
Every report back to Matt ends with `sudo docker ps`.
|
||||
|
||||
## 13. Backlog (do NOT do now — list for future sessions)
|
||||
|
||||
1. Persist the fire seen-baseline across restarts to close the restart-window ignition gap (§6.1).
|
||||
2. Decide policy for zero-acre first sightings (§6.2).
|
||||
3. Extend pacing to the native emit path for multi-fire poll batches (§6.4).
|
||||
4. Before cutting over quake/swpc/avalanche/nws via `MESHAI_CUTOVER_CATEGORIES`, re-verify override promotion end-to-end (covered by this fix, but confirm with shadow logs).
|
||||
5. Add a drop counter + log line for the dispatcher severity-floor return (it is currently the only fully silent drop in the pipeline).
|
||||
|
|
@ -11,7 +11,7 @@ related:
|
|||
- [[services]]
|
||||
- [[meshcore-transport]]
|
||||
- [[central]]
|
||||
updated: 2026-07-02
|
||||
updated: 2026-07-11
|
||||
---
|
||||
|
||||
# meshai
|
||||
|
|
@ -25,7 +25,7 @@ meshai (bot name **AIDA**) attaches to the mesh as a physical node and does two
|
|||
- **Outbound alerts.** It consumes real-world situational-awareness feeds (weather, wildfire, traffic/511, space weather, earthquakes, water, satellite passes, …), normalizes them into events, and broadcasts short, chunked alerts to the mesh.
|
||||
- **Interactive assistant.** Mesh users message it directly and get data-driven LLM answers over LoRa — mesh-health questions ("how's the mesh?"), knowledge/weather queries, and command handlers (`!health`, `!region`, `!neighbors`, `!fires`, `!quakes`, `!space`, `!water`, subscriptions, etc.).
|
||||
|
||||
The LLM backend is Gemini 2.5 Flash with Google Search grounding (multi-backend capable — Gemini / OpenAI / Anthropic / local via LiteLLM). Conversation memory is a rolling window persisted to SQLite.
|
||||
The LLM backend is gemini-3.1-flash-lite with Google Search grounding (multi-backend capable — Gemini / OpenAI / Anthropic / local via LiteLLM). Conversation memory is a rolling window persisted to SQLite.
|
||||
|
||||
## Where it runs
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ related:
|
|||
- [[meshtastic-sidecar-node]]
|
||||
- [[headscale-onboard-node]]
|
||||
- [[caddy]]
|
||||
updated: 2026-06-21
|
||||
updated: 2026-07-11
|
||||
---
|
||||
# IdahoMesh Tailnet Runbook
|
||||
|
||||
|
|
@ -369,9 +369,9 @@ tailscale --socket=/var/run/tailscale-meshtastic/tailscaled.sock up \
|
|||
After joining, approve the advertised route on Echo6 Headscale only:
|
||||
|
||||
```bash
|
||||
# On Echo6 Headscale (Contabo) — enable the 100.100.0.0/16 route
|
||||
docker exec headscale-vanilla headscale routes list
|
||||
docker exec headscale-vanilla headscale routes enable -r <route-id>
|
||||
# On Echo6 Headscale (edge2 CT 107) — enable the 100.100.0.0/16 route
|
||||
ssh edge2 "sudo pct exec 107 -- docker exec headscale headscale routes list"
|
||||
ssh edge2 "sudo pct exec 107 -- docker exec headscale headscale routes enable -r <route-id>"
|
||||
|
||||
# NO route approval needed on IdahoMesh Headscale — nothing is advertised
|
||||
```
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ related:
|
|||
- [[ip-allocation]]
|
||||
- [[services]]
|
||||
- [[meshtastic-headscale-runbook]]
|
||||
updated: 2026-06-18
|
||||
updated: 2026-07-11
|
||||
---
|
||||
# MMUD — Mesh Multi-User Dungeon
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ Text-based multiplayer dungeon crawler for Meshtastic LoRa mesh networks. BBS do
|
|||
|
||||
## Status
|
||||
|
||||
**Phase:** Deployed and running — all 6 phases implemented, NPC conversation system live with Gemini 2.5 Flash.
|
||||
**Phase:** Deployed and running — all 6 phases implemented, NPC conversation system live with gemini-3.1-flash-lite.
|
||||
|
||||
## Deployment
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ Text-based multiplayer dungeon crawler for Meshtastic LoRa mesh networks. BBS do
|
|||
- MRN (CT 114) — Maren healer NPC
|
||||
- TRVL (CT 115) — Torval merchant NPC
|
||||
- WSPR (CT 116) — Whisper sage NPC
|
||||
- **LLM Backend:** Gemini 2.5 Flash via Google genai SDK (configured in DB `llm_config` table)
|
||||
- **LLM Backend:** gemini-3.1-flash-lite via Google genai SDK (configured in DB `llm_config` table)
|
||||
- **Compose:** `/opt/mmud/docker-compose.yml` on CT 109
|
||||
- **Admin:** https://mmud.echo6.co/admin (session auth, password in docker env)
|
||||
|
||||
|
|
@ -75,9 +75,9 @@ NPCs use runtime LLM calls (the one exception to the "no runtime LLM" rule). Key
|
|||
## LLM Configuration
|
||||
|
||||
- Model configured via `llm_config` table in SQLite DB (not env vars)
|
||||
- Currently: Gemini 2.5 Flash (`gemini-2.5-flash`)
|
||||
- Currently: gemini-3.1-flash-lite
|
||||
- API key stored in DB, manageable via admin panel at /admin/llm
|
||||
- **No `max_output_tokens` restrictions** — Gemini 2.5 Flash thinking tokens consume the budget, causing truncation. All backends have token limits removed.
|
||||
- **No `max_output_tokens` restrictions** — gemini-3.1-flash-lite thinking tokens consume the budget, causing truncation. All backends have token limits removed.
|
||||
- Supports: Google (Gemini), Anthropic (Claude), OpenAI-compatible backends
|
||||
|
||||
## Development Phases (All Complete)
|
||||
|
|
@ -91,7 +91,7 @@ NPCs use runtime LLM calls (the one exception to the "no runtime LLM" rule). Key
|
|||
|
||||
## Gotchas
|
||||
|
||||
- Gemini 2.5 Flash thinking tokens count against `max_output_tokens` — never set token limits
|
||||
- gemini-3.1-flash-lite thinking tokens count against `max_output_tokens` — never set token limits
|
||||
- NPC greeting path uses `complete()`, conversation path uses `chat()` — different code paths
|
||||
- `npc_memory` table stores `turn_count` for interaction depth tracking
|
||||
- Death log table (`death_log`) tracks monster kills for Maren's memory feature
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue