| 2026-09-26 |

PWA: installable webclient, offline shell, web push
...
Installability:
- public/manifest.webmanifest (standalone, theme #16161E) + PNG icons
generated from logo.svg (regular + maskable, served via /images mount)
- index.html: manifest link, theme-color, apple-touch-icon
Offline shell:
- hand-rolled sw.js (no workbox): navigation = network-first (3s race)
with cached-shell fallback + background refresh (a stale cached shell
would 404 on entry chunks after a deploy); /assets/* cache-first
(content-hashed); /images/* cache-first capped; /api,/ws,/auth,/push,
/content pass-through
- vite closeBundle plugin stamps __NAVI_BUILD_VERSION__ (digest of
index.html + asset names) into dist/sw.js; sw.js served no-store so
every deploy reactivates the SW and activation evicts old caches
- SW registration in main.js, PROD only (dev HMR untouched)
- OfflineBanner (useOnline composable) over the app shell
Web push (VAPID, pywebpush):
- navi/push/ package: push_subscriptions table (postgres, boot-time DDL),
PushSubscriptionStore, PushService (async fan-out, to_thread sends,
404/410 prunes dead endpoints, per-session cooldown)
- routes: GET /push/vapid-key, POST/DELETE /push/subscribe (auth-gated)
- trigger in orchestrator run_agent + run_recall: push on StreamEnd when
no WebSocket client watches the session; fire-and-forget, never
disturbs the run; anonymous fallback only when auth is off
- client: usePush composable + Notifications settings panel (enable/
disable via PushManager.subscribe with the server VAPID key)
- notification click focuses the app at /#<session_id> (hash routing
opens the right chat); payload body is a markdown-stripped <=140-char
preview
NAVIVAPID keys empty = push fully disabled (graceful, like other optional
integrations). dist/ artifacts committed per repo convention.
Tests: pytest push store/service/routes/trigger (+23), vitest usePush
(83 webclient tests green). Full suite 1143 passed.
Eugene Sukhodolskiy
committed
10 hours ago
|
| 2026-09-10 |
deploy: NAVI_HOST=0.0.0.0 — fresh installs join the swarm peer channel
...
Fresh deployments were 'visible but deaf': announcements to the hive are
outbound and worked, but inbound /peer/* connections hit the localhost-only
bind and failed with ConnectError. 0.0.0.0 makes the peer channel work;
127.0.0.1 stays documented for machines outside the swarm.
Eugene Sukhodolskiy
committed
16 days ago
|
swarm stage 2: peer-to-peer channel — /peer endpoints + peer tool
...
Server side (navi/api/routes/peer.py, port 8099):
- GET /peer/hello — open discovery ping (name, uuid prefix, version)
- GET /peer/status — PSK; identity, uptime, machine facts, hive view
- POST /peer/ask — PSK; one-shot agent run under PEER_ASK_PROFILE
(default server_admin) answers, one concurrent ask at a time
- loop guard: answering agent runs without the peer tool (deterministic
recursion cut) + own-uuid asks refused with 409
- verify_swarm_key in navi/swarm.py accepts .swarm-key.previous
(rotation window, constant-time)
Client side (navi/tools/peer.py):
- peer tool: list (hive book, stale cache fallback marked), status,
ask by swarm name; asks go direct peer-to-peer, hive never on the
message path
- audit events on both sides (peer.ask_sent/received/answered/failed)
peer tool added to server_admin, navi_code, developer profiles.
Eugene Sukhodolskiy
committed
17 days ago
|
| 2026-09-09 |
swarm stage 1: instance identity, PSK, hive registry, announce loop
...
- navi/identity.py: adjective-animal names + uuid in instance.json
(generated at install, renameable by hand)
- navi/swarm.py: HiveAnnouncer - periodic POST /announce to the hive
with non-blocking reachability tracking (transitional logs, /health
export, hive_status context provider reports outages to the agent)
- hive/: standalone FastAPI address book (SQLite, port 8087, PSK via
X-Swarm-Key with .swarm-key.previous rotation window, TTL online
status, host from client IP, port from payload). Not installed or
started by default - run manually on the main server.
- ports moved to 8099 (API) / 8098 (UI MCP)
- install.sh: PYTHONIOENCODING=utf-8 in the systemd unit, generates
instance.json and .swarm-key on fresh installs
Eugene Sukhodolskiy
committed
17 days ago
|
port: 8000/8001 -> 8099/8098 everywhere
...
navi runs on shared servers where 8000/8001 are usually taken. New
defaults: API 8099, navi_ui MCP 8098. Touched: config defaults
(navi_port, navi_ui_mcp_port, public_url, gnauth_redirect_uri),
navi-server launcher docs, env.template/.env.example, install.sh
health-check fallback, terminal client base_url, webclient dev configs
(useWebSocket, contentLinks, vite proxy), android url hint, docs.
Also made the navi_ui FastMCP constructor port settings-driven instead
of a hardcoded 8001 (it was overridden at start anyway).
Eugene Sukhodolskiy
committed
17 days ago
|
fix: ASCII-safe startup log messages + force UTF-8 stdout in the systemd unit
...
A unit on an old distro without a UTF-8 locale gets latin-1/ascii
stdout; structlog's print of the em-dash in the webclient-disabled
message raised UnicodeEncodeError and killed the app at startup
(Application startup failed, crash-loop restart counter 28). The unit
now sets PYTHONIOENCODING=utf-8 so any future non-ASCII log line is
safe too.
Eugene Sukhodolskiy
committed
17 days ago
|
deploy: auto-install docker compose v2 plugin when the host lacks it
...
Ubuntu 18.04's docker packages predate compose v2. The plugin is a
single static binary that runs on any distro with docker CLI >= 18.09,
so the script downloads it into /usr/local/lib/docker/cli-plugins
(system-wide — sudo docker compose sees it too) when docker compose is
unavailable, with the same offline pattern as the python tarball: a
pre-downloaded binary next to install.sh wins. If the CLI is too old
to support plugins at all, the error asks for docker --version.
Eugene Sukhodolskiy
committed
17 days ago
|
deploy: standalone CPython fallback for old distros
...
venv cannot be shipped in the repo — it embeds absolute paths and
distro-specific compiled wheels (asyncpg, uvloop, cryptography), so it
dies on any other machine. Instead, when the host has no python >= 3.11
(Ubuntu 18.04: 3.6, 22.04: 3.10; system python must not be replaced),
install.sh fetches a standalone astral python-build-standalone CPython
3.12 into .python/ — self-contained, needs only glibc >= 2.17, works
on any distro. The tarball is cached next to the repo and reused; on
offline servers a pre-downloaded tarball next to install.sh wins.
Verified locally: tarball layout, venv creation, pip install.
Eugene Sukhodolskiy
committed
17 days ago
|
deploy: pick the newest python >= 3.11 instead of hardcoding python3
...
Ubuntu 22.04's system python3 is 3.10 and must not be replaced (apt
depends on it) — a deadsnakes install lives under python3.11/3.12.
The install now tries those names before plain python3 and builds
the venv with whichever qualifies, with a hint in the error path.
Eugene Sukhodolskiy
committed
17 days ago
|
deploy: README opens with the full install breakdown
Eugene Sukhodolskiy
committed
17 days ago
|
deploy: regenerate placeholder secrets from the deploy branch's .env on first install
Eugene Sukhodolskiy
committed
17 days ago
|
deploy: one-command server bundle (dockerized postgres + systemd)
...
deploy/install.sh — everything in one command: prerequisite checks,
.env provisioning from env.template (generated DB password), dockerized
PostgreSQL (pgvector image, restart: always, bound to 127.0.0.1, vector
+ pg_trgm extensions), venv + pip install, a systemd unit with
Restart=always (the server lives from installation, per the roadmap
where these modules become each other's subagents), health check, and
navi-code/navi-server symlinks into PATH. Idempotent — safe to re-run.
Default deployed shape: web UI off, auth off, terminal client only.
Re-enabling the web panel later is editing .env + restart — nothing is
removed from the tree.
The deploy branch (next commit set) differs from master only by a
filled-in .env.
Eugene Sukhodolskiy
committed
17 days ago
|