Commit Graph

680 Commits

Author SHA1 Message Date
chriscrosstalk 5dee9428b7
feat(benchmark): live telemetry during benchmark runs (#1082) (#1084)
Phase 1 of the live benchmark-run experience. Replaces the opaque (and
in sync mode, simulated) progress bar with a real-time run view driven by
actual host telemetry.

- Async run path: the UI now dispatches to the queue worker and keys off
  SSE instead of faking stage progress with client-side timers.
- New BenchmarkTelemetrySampler broadcasts per-core CPU load, CPU temp
  (best-effort, hidden when unavailable), and disk MB/s at 1 Hz over a new
  benchmark-telemetry SSE channel. Runs in the orchestration process, never
  the sysbench container, so it cannot affect scores.
- BenchmarkProgress carries the ordered stage plan + index so the frontend
  renders a live stage rail.
- AI benchmark streams /api/generate for live tokens/sec and true TTFT; the
  scored numbers still come from Ollama's authoritative final eval fields.
- Frontend: useBenchmarkRun hook owns both subscriptions; self-contained SVG
  components (StageRail, CoreGrid, Sparkline, LiveReadout) + BenchmarkRunView,
  styled in the desert palette. No chart library added.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 23:28:07 -07:00
jakeaturner eea7b04b3e
chore(deps): bump tar, vite, and dockerode in admin 2026-07-20 05:56:41 +00:00
chriscrosstalk 97d70a493d
fix(content): refresh installed ZIMs when a download completes to prune ghost entries (#1099)
The Content Explorer remote list already filters out installed ZIMs client-side
(flatData excludes localNames), but the installed-files query has
refetchOnWindowFocus disabled and was never invalidated on download completion.
So when a ZIM finished downloading it dropped off the active-downloads list
(isDownloading -> false) while localFiles stayed stale (isPresent -> false),
letting the just-installed ZIM reappear as a ghost entry until the page remounted.

Add an effect that invalidates the ['zim-files'] query whenever a job drops off
the polled downloads list, so the completed install is picked up and pruned.

Fix originally diagnosed by @johno10661 in #771; reimplemented focused onto
current dev (the accumulated-page/filter groundwork already landed via #731).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 22:40:06 -07:00
chriscrosstalk 59332c6c47
fix(downloads): send a descriptive User-Agent so Wikimedia mirrors don't 403 (#1114)
download.kiwix.org routes the large Wikimedia-family ZIMs (Wikipedia,
Wikiversity, Wikibooks — including the flagship full Wikipedia) to
dumps.wikimedia.org, which enforces a User-Agent policy and returns HTTP
403 for requests with a missing or generic (axios/x) User-Agent. Because
doResumableDownload sent no User-Agent, every Wikimedia-hosted ZIM failed
to download while Kiwix-mirror-hosted ZIMs succeeded — so a curated set or
Easy Setup run would silently stall on exactly the highest-value content.

Add a descriptive User-Agent to the HEAD and GET requests. Verified
against the live mirror: default/empty UA -> 403, ProjectNOMAD UA -> 200/206.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 22:37:57 -07:00
chriscrosstalk 3735a4cb56
feat(dashboard): add dismissable "What's new" banner for v1.34 (#1112)
Surface post-upgrade release highlights on the Command Center: a
dismissable banner keyed to the running build's release line (major.minor).
It appears when a user upgrades into the line and every patch within it,
then disappears on the next minor. Dismissal is remembered per-line in
localStorage, so dismissing v1.34 won't suppress a future v1.35 note.

Reuses the existing dismissable Alert component and the dashboard's top
banner slot; reads the running version from the appVersion shared prop.
No schema or migration needed — to surface a new release's highlights,
bump WHATS_NEW.version and replace its highlights.

Initial content: Creator Packs and the offline Medication Reference.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 22:36:09 -07:00
chriscrosstalk e1d7da0379
fix(easy-setup): streamline wizard + robust model recommendations (#1110)
Step 1: remove the "Additional Tools" (Notes/Data Tools) section from
onboarding and point users to Supply Depot, the browsable app catalog,
for everything beyond the three core capabilities.

Step 2: add a note that individual countries and a full global map can
be installed any time from the Maps Manager.

Step 4: default the KB auto-index policy to "Ask me first" (Manual)
instead of "Yes, always" — auto-indexing has cost/resource implications
a non-technical user won't anticipate from the toggle alone.

ollama_service: the recommended-models fallback only fired on a null
result, so a successful-but-empty upstream response (models: []) showed
"No recommended AI models available" and poisoned the 24h cache. Now the
fallback fires on empty too, empty results are never cached, empty caches
are ignored, and the upstream request has a 10s timeout.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 22:34:16 -07:00
chriscrosstalk a0d4a45745
docs(install): make storage-relocation guidance accurate and consistent (#1103)
The two compose comments contradicted each other on NOMAD_STORAGE_PATH (one
called it optional/"explicit", the other "MUST match") and neither warned about
the failure modes that actually break relocation. A user hit exactly this on
#1050: a case-mismatched path silently produced an empty Kiwix library.

- Frame the admin /app/storage host path as the single source of truth; the
  admin auto-detects it (#938) and child apps follow, so no per-service edits.
- NOMAD_STORAGE_PATH is a fallback that should be kept in sync, not a hard
  requirement (reconciles the contradiction).
- Add the real gotchas: move existing data first (keep zim/models subfolders),
  paths are case-sensitive, and update the disk-collector volume too or host
  disk stats point at the wrong place.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 22:27:26 -07:00
chriscrosstalk 05af8476b5
feat(debug-info): add storage, docker, GPU health, and auto-update diagnostics (#1102)
The Debug Info bundle carried no storage-path info, which stalled diagnosis of
relocation issues like #1050 (moved data not seen by the admin). Add the fields
support actually asks for, all best-effort so one failure never blanks the bundle:

- Storage: resolved host storage root (#938), container path, whether
  NOMAD_STORAGE_PATH is set, and the Kiwix library book count (0 books is the
  tell for an empty/wrong-path library).
- Docker Engine version (reporters currently paste it by hand; needed for
  container/updater issues).
- GPU passthrough health (gpuHealth.status + detected gpu.type) for the
  passthrough-lost-after-update class (#755/#878).
- Auto-update status for core/apps/content plus any auto-disabled reason,
  since the auto-update trilogy shipped.

Adds a public DockerService.getHostStorageRoot() wrapper over the existing
#938 resolver.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 22:25:53 -07:00
chriscrosstalk 2a11a81eb4
fix(updater): prune superseded images after update to reclaim disk (#1101)
* fix(updater): prune superseded images after update to reclaim disk (#858)

The sidecar updater pulled new image versions on every update but never
removed the old ones, so /var/lib/containerd grew unbounded across
releases (50+ GB of orphaned layers observed on long-running installs).

After a confirmed-successful recreate, prune (1) dangling layers left by
re-pulled moving tags and (2) superseded tags of the core services this
updater manages (the images in compose.yml), keeping the refs now in use.

Deliberately avoids `docker system/image prune -a`: that would delete
images for installed-but-stopped Supply Depot / curated services and
force a re-pull that fails on an offline box. Scoped strictly to
compose-managed repositories; optional/offline images are never touched.
Uses `docker rmi` without -f so anything still referenced by a container
is refused rather than force-removed. Best-effort; never fails the update.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(updater): scope image pruning to those directly used by NOMAD

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: jakeaturner <jturner@cosmistack.com>
2026-07-19 22:22:45 -07:00
chriscrosstalk 5c05e12eb8
fix(install): define missing header_red + colors in uninstall/update scripts (#1098)
The uninstall and update scripts called header_red() and referenced color
variables (GREEN/RED/RESET) that were never defined, so running them printed
"header_red: command not found" instead of the section banners. Also corrects
the update script's success message (it said "installation completed" and
pointed at an undefined ${nomad_dir} path) and fixes two spelling typos.

- uninstall_nomad.sh: add Color Codes block + header_red()
- update_nomad.sh: add header_red(); fix success wording and start_nomad.sh path
- CONTRIBUTING.md, admin/constants/ollama.ts: spelling fixes

Reported by @fix2015 in #1058; reimplemented in-house.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 21:37:37 -07:00
chriscrosstalk cb66faacbc
docs(contributing): add UI Consistency section (#1080)
Capture the frontend conventions so new UI (including community PRs) stays
visually and behaviorally uniform with the rest of the GUI: reuse the shared
component library (Switch, InfoTooltip, StyledModal, Input), match sibling
label punctuation and typography tokens, use theme tokens for dark mode, keep
tooltips from clipping, and test UI changes in a real browser before
submitting. Notes when a raw control (checkbox, radio, native select) is still
the right call.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 10:34:48 -07:00
chriscrosstalk 166247c284
fix(AI): stream thinking from /v1 reasoning field + abort on client disconnect (#1078)
Two defects made chat hang forever with thinking-capable models on the OpenAI-compat
(/v1) path, which NOMAD uses for both local and remote Ollama:

1. Field mismatch. chatStream()/chat() read `delta.thinking` / `message.thinking`,
   but Ollama's /v1 endpoint emits thinking tokens as `reasoning`. All thinking output
   was silently dropped, so the SSE stream was nothing but empty content+thinking chunks
   and never reached done. Now read `thinking ?? reasoning` in both paths (the inline
   <think>-tag parser for other backends is unchanged).

2. No abort on client disconnect. When the user gave up and closed the chat, the
   upstream generation kept decoding server-side. With Ollama's default
   OLLAMA_NUM_PARALLEL=1 that abandoned request occupied the only slot, so every later
   chat/RAG request queued behind it and the whole assistant appeared dead. The
   controller now wires an AbortController to the response 'close' event and threads the
   signal into the OpenAI SDK request, so a disconnect aborts the upstream generation.

Verified on NOMAD2 (qwen3:0.6b, which reports the `thinking` capability and emits
`reasoning` on /v1): before, the stream was endless empty chunks; after, thinking streams
visibly and reaches done. On disconnect, Ollama's decode counter freezes and the server
logs `cancel task` / `slot release`, freeing the slot for the next request.

Note: thinking is still force-on for capable models here; a user-facing per-model
thinking toggle (default off) is a planned follow-up.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 10:33:06 -07:00
chriscrosstalk 39507d60a1
fix(supply-depot): generate Homebox API key pepper so it stops crash-looping (#1077)
Homebox >= 0.26 (ghcr.io/sysadminsmedia/homebox:0.26.2) panics at boot unless
HBOX_AUTH_API_KEY_PEPPER is set to a >= 32-byte value. The seeder ships no Env
block, so every clean install crash-loops immediately.

Generate a per-install pepper, persist it in the KV store
(apps.homebox.apiKeyPepper), and inject it as HBOX_AUTH_API_KEY_PEPPER at every
container-create path. The pepper is generated once and reused: rotating it would
invalidate every API key a user has issued from Homebox.

Inject at all three create paths so no lifecycle action drops it:
- _createContainer (install / force-reinstall)
- the service update path (heals a pre-fix container on update)
- recreateCustomAppContainer (the Edit / reconfigure rebuild — the in-app docs
  tell users to add HBOX_OPTIONS_ALLOW_REGISTRATION=false via Manage > Edit,
  which rebuilds Env from container_config alone and would otherwise drop the
  pepper and re-trigger the crash loop)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 10:21:14 -07:00
chriscrosstalk 6b294657ac
fix(AI): stop forcing HSA_OVERRIDE=11.0.0 on natively-supported AMD iGPUs (#1076)
The current ollama:rocm image bundles ROCm 7.2, which natively supports the
RDNA 3/3.5 mobile parts (780M gfx1103, 890M gfx1150, Strix Halo gfx1151).
Forcing HSA_OVERRIDE_GFX_VERSION=11.0.0 coerces them onto gfx1100's kernels:
unnecessary on the 890M and a source of faults on the 780M, which lacks the
gfx1100 WMMA instructions those kernels use.

- Map gfx1103/gfx1150/gfx1151 to no override (was 11.0.0) so ROCm discovers
  them natively.
- Change the no-marker default from 11.0.0 to null. A hardcoded default gets
  more wrong as ROCm adds native targets; native discovery is the safer
  forward-looking default. Hardware that still needs coercion (e.g. RDNA 2
  iGPUs on an install without the gfx marker) can force a value via the
  ai.amdHsaOverride KV.
- Keep gfx1031..gfx1036 (RDNA 2 iGPUs like the 680M) on 10.3.0 — still not
  natively supported.

Verified on NOMAD2 (Radeon 890M): after a Force Reinstall the container has
no HSA_OVERRIDE, Ollama discovers the GPU as its native gfx1150 with the
rocm_v7_2 libraries, and a model loads at 100% GPU with no faults.

Depends on #1056 context; see companion issue for the 780M follow-up.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 10:12:19 -07:00
chriscrosstalk 60fffdab82
fix(AI): set OLLAMA_IGPU_ENABLE on AMD provisioning so iGPUs are used (#1074)
Ollama's scheduler drops integrated GPUs unless OLLAMA_IGPU_ENABLE=1 is set.
NOMAD sets HSA_OVERRIDE_GFX_VERSION for AMD but never this flag, so AMD APUs
(780M/890M/8060S) silently fell back to CPU-only inference despite correct
/dev/kfd and /dev/dri passthrough.

Set OLLAMA_IGPU_ENABLE=1 whenever AMD acceleration is configured, on both the
install and update provisioning paths. The flag is a no-op on discrete AMD
cards, so it's safe to set unconditionally within the AMD branch. On the update
path we also strip any prior value so containers provisioned before this change
pick up the flag on their next update.

Verified on NOMAD2 (Ryzen AI 9 HX 370 / Radeon 890M): before, Ollama logged
"dropping integrated GPU" and ran on CPU; after, it reports the 890M as an
iGPU ROCm inference device and models load at 100% GPU.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 10:02:58 -07:00
Chris Sherwood fca1a48c85 feat(brand): add ™ to Project NOMAD wordmark on prominent surfaces
Wordmark application filed (USPTO serial 99912179). Mark the
persistent Command Center footer wordmark (both the AppLayout footer
and the settings/docs sidebar footer) and the first mention on the
Legal Notices page. Browser-tab titles and body copy left unmarked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 09:56:54 -07:00
Chris Sherwood 9feffeaad4 chore: standardize brand name to Project NOMAD, retire backronym
Retire the dotted "N.O.M.A.D." styling everywhere in favor of
"Project NOMAD" (no periods) ahead of the trademark filing, and
remove the "Node for Offline Media, Archives, and Data" backronym
from all copy except a single origin-story line in the About page
(corrected to "Maps" rather than "Media").

Scope is display strings only: docs, admin UI labels/titles, install
script output, Dockerfile labels, package.json description, and issue
templates. Code identifiers, container/service names, env vars, CSS
tokens, URLs, and the project-nomad slug are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 09:56:54 -07:00
jarvisxyz 9482f78486
feat: Expandable rows in Kiwix Library browser (#1060)
* feat: expandable rows in Kiwix Library browser (rebased onto dev)

Click a row in the Content Explorer's 'Browse the Kiwix Library' table
to expand it and reveal the full description (no longer truncated) along
with additional metadata: author, publisher, language, category,
article count, media count, issue date, file size, tags, and file name.

Changes:
- Extend RemoteZimFileEntry type with optional metadata fields
  (language, publisher, category, tags, article_count, media_count, issued)
- Update zim_service.ts listRemote() to map these fields from the raw
  Kiwix API response in the paginated accumulator loop
- Remove @tanstack/react-virtual virtualization from remote-explorer.tsx
  (12 items per page — virtualization not needed and incompatible with
  variable-height expanded rows)
- Use StyledTable's built-in expandable prop with expandedRowRender
  to show full details when a row is clicked
- Preserve upstream's custom libraries source selector and directory browser

* fix(RemoteExplorer): guard against invalid dates being rendered

---------

Co-authored-by: eizus <hello@cdr.xyz>
Co-authored-by: jakeaturner <jturner@cosmistack.com>
2026-07-18 09:55:46 -07:00
jarvisxyz d48dffc802
fix(downloads): add retry button and resource download link for failed downloads (#1059)
Previously, failed downloads showed only an alert icon and a dismiss (X)
button — the user had no way to retry or reach the resource page without
manually re-adding the download and finding the source URL elsewhere.

This commit adds:
- POST /api/downloads/jobs/:jobId/retry endpoint (controller + service)
- retryDownloadJob() API method on the frontend
- Failed-state UI in ActiveDownloads.tsx now shows:
  - Retry button (re-dispatches the original download job)
  - 'Download page' external link (when the download URL is an HTTP(S) URL)
- Loading state on the retry button while the request is in-flight
- Screenshots documenting before/after UI

Co-authored-by: eizus <hello@cdr.xyz>
2026-07-18 09:25:42 -07:00
chriscrosstalk 3ab916bf75
chore(catalog): sunset orphaned Meshtastic Daemon card (#1049)
Meshtastic Daemon was pulled from DEFAULT_SERVICES because it can't work
without hands-on setup (radio MAC address, etc.). The seeder never deletes,
so every early-access deployment keeps an orphaned nomad_meshtasticd row and
still shows the broken card.

Migration mirrors the legacy-Kolibri sunset: drop the row where installed=0,
flag is_deprecated where installed=1 (keeps it manageable, hides from catalog).
Runs automatically on each box's next update.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 09:06:57 -07:00
chriscrosstalk 8f56d76fe7
chore(KB): filter non-content sections + render tables in ZIM extraction (#1044)
Closes #902.

Two gaps in the structured ZIM extraction path:

1. NON_CONTENT_HEADING_PATTERNS was only used by the structure heuristic to
   count meaningful sections, never at section-emit time. Sections under
   "See also" / "References" / "External links" / etc. were still chunked and
   embedded. They're now flagged when the heading opens and dropped.

2. <table> elements were run through cheerio's `.text()`, concatenating every
   cell with no separators ("AgeDoseAdult500mg") into unsearchable word salad.
   New tableToText() joins cells with " | " and rows with newlines so
   row/column structure survives into the chunk.

Refactor: moved extractStructuredContent out of ZIMExtractionService into a
pure, cheerio-only util (app/utils/zim_html.ts) so it can be unit-tested
without the native @openzim/libzim binding. Service delegates to it; behavior
is otherwise unchanged. Adds tests/unit/zim_html.spec.ts (6 tests).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 09:03:53 -07:00
chriscrosstalk 59c2fdbb32
feat(maps): add notes input to map pin placement popup (#926)
The map_markers backend has accepted a `notes` column since PR #770 and
the popup display path was wired up to render it (commit 6328256), but
the placement UI never got an input. Result: notes are stored,
displayed when present, and impossible to actually enter via the UI.

Add a notes textarea below the name input in the placement popup,
thread the value through `addMarker` and `createMapMarker`, and trim +
null-coalesce on save. Notes display in the marker popup on click is
unchanged and now actually reachable.

- admin/inertia/lib/api.ts: extend createMapMarker request type with
  optional notes
- admin/inertia/hooks/useMapMarkers.ts: addMarker accepts and forwards
  notes (response already populated notes into local state, so no
  display-side change needed)
- admin/inertia/components/maps/MapComponent.tsx: markerNotes state,
  textarea after name input, threaded into handleSaveMarker

Edit-mode for existing markers (so users can backfill notes on
already-placed pins) is intentionally out of scope here - selected-marker
popup is still read-only. That's a follow-up PR if there's demand.
2026-07-18 08:59:35 -07:00
cosmistack-bot 6a4f02dd46 docs(release): finalize v1.33.0 release notes [skip ci] 2026-06-23 17:52:28 +00:00
cosmistack-bot cf294bf908 chore(release): 1.33.0 [skip ci] 2026-06-23 17:51:38 +00:00
jakeaturner c794d5e4b0
Revert "chore(release): 1.33.0 [skip ci]"
This reverts commit d8cca183f9.
2026-06-23 17:49:54 +00:00
jakeaturner 587600ba78
Revert "docs(release): finalize v1.33.0 release notes [skip ci]"
This reverts commit c9a86790de.
2026-06-23 17:49:36 +00:00
jakeaturner e82a2934c7
Revert "chore(release): 1.33.1 [skip ci]"
This reverts commit 0ee5dda534.
2026-06-23 17:49:17 +00:00
cosmistack-bot 0ee5dda534 chore(release): 1.33.1 [skip ci] 2026-06-23 17:41:25 +00:00
jakeaturner f4af81ef02
fix: lazy-connect + retry for ioredis to avoid blocked startup 2026-06-23 17:40:10 +00:00
cosmistack-bot c9a86790de docs(release): finalize v1.33.0 release notes [skip ci] 2026-06-23 04:49:49 +00:00
cosmistack-bot d8cca183f9 chore(release): 1.33.0 [skip ci] 2026-06-23 04:48:59 +00:00
jakeaturner 76ce725340
chore(release): set version to 1.33.0-rc.1 2026-06-23 04:47:13 +00:00
jakeaturner 9609edc281
chore(docs): update release notes 2026-06-23 04:47:12 +00:00
Jake Turner 0143afe7cc
fix(supply-depot): bump default Ollama and CyberChef image versions (#1036) 2026-06-23 04:47:12 +00:00
Jake Turner 5af27e9904
fix(supply-depot): ensure all curated images pinned to specific versions (#1033) 2026-06-23 04:47:12 +00:00
Jake Turner 88ac4d5ec4
feat(RAG): adds the ability to cancel all embedding jobs (#1034) 2026-06-23 04:47:11 +00:00
jakeaturner f0142b67f8
chore(docs): update release notes 2026-06-23 04:47:11 +00:00
jakeaturner 4a795df793
feat: configurable internet test url override in new Advanced Settings page 2026-06-23 04:47:10 +00:00
Jake Turner 02c9f72bf0
fix(UI): unifies Supply Depot icon and improves loading UX (#1022) 2026-06-23 04:47:10 +00:00
Benjamin Smith 5181637926
feat(KnowledgeBase): add document viewer, download, metadata, and sorting (#721)
Rebuilt on top of dev's RFC #883 state-machine UI rather than the now-defunct
StoredFile shape:

- Extend StoredFileInfo with fileName/size/uploadedAt/isUserUpload
- Populate metadata from on-disk stats in RagService.getStoredFiles
- Add fileSourceSchema validator + getFileContent/downloadFile endpoints
  scoped to the uploads directory only (tighter than the original PR — matches
  docs_service traversal pattern)
- KnowledgeBaseModal: sortable Size and Uploaded columns; View/Download
  buttons on upload-bucket rows; new FileViewerModal for in-browser text
  preview. Bucket grouping preserved — sort applies within each bucket.
- Use formatBytes from ~/lib/util rather than redefining
2026-06-23 04:47:09 +00:00
jakeaturner fad9e30ddc
fix(zim): stabilize uploader state to avoid cancel on tab refocus
Also improves surfacing of upload error messages
2026-06-23 04:47:09 +00:00
Henry Estela a7d05859d4
feat(zim): add zim uploader in content manager
adds a collapsible file uploader to accept zim file uploads into kiwix.
2026-06-23 04:47:08 +00:00
Lorenzo Galassi fe6735fdcb
fix(queue): share one ioredis connection across BullMQ queues and workers (#1009)
BullMQ instantiates a fresh ioredis client per Queue/Worker when handed a
plain {host, port} config object, and under sustained ZIM ingestion the
embed pipeline leaked ~1 client/sec until Redis maxclients was exhausted.
Pass a single shared ioredis instance (maxRetriesPerRequest: null, as
required by BullMQ) so all queues and workers reuse one client pool.
Workers still duplicate the connection once for their blocking client,
which is expected and bounded.

Closes #885
2026-06-23 04:47:08 +00:00
Jake Turner b507b8bd4e
chore(deps): bump mysql2 in admin (#1021) 2026-06-23 04:47:07 +00:00
Jake Turner 8982d93a31
feat: replace legacy Kolibri image default with latest v19 image (#1019)
* feat: replace legacy Kolibri image default with latest v19 image
* feat(supply-depot): add content migration instructions for Edu Platform Gen 1 to 2
2026-06-23 04:47:07 +00:00
jakeaturner 1b040c8b9a
fix: pin default meshcore web image 2026-06-23 04:47:07 +00:00
jakeaturner 475781ffa2
build: ensure openssl installed in admin container 2026-06-23 04:47:06 +00:00
Chris Sherwood bd65c885be
feat(supply-depot): add MeshCore Web with self-signed HTTPS
Adds the MeshCore web client to the Supply Depot catalog (host port 8500),
alongside the existing Meshtastic apps. Uses aXistem's prebuilt image of Liam
Cottle's MeshCore client (MeshCore is a sibling LoRa mesh project to Meshtastic).

The image is stock nginx serving a static Flutter build over HTTP, but the
client reaches radios via Web Bluetooth / Web Serial, which browsers only allow
from a secure (HTTPS) context. So we serve it over HTTPS: a new preinstall hook
generates a self-signed cert + a small SSL nginx config into storage/meshcore-web,
both bind-mounted into the container (the config over the image's default.conf),
publishing 443. Same one-time browser-warning approach as Vaultwarden, whose
openssl cert generation is refactored into a shared _ensureSelfSignedCert helper.

Also adds a NOMAD-specific docs section + Manage>Docs anchor, and registers the
IconAntenna icon. Meshtastic Web left unchanged.

Validated on NOMAD3 (v1.33.0-rc.1): the image + SSL config + self-signed cert
serves the MeshCore Flutter app over HTTPS 200 with working SPA fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 04:47:06 +00:00
johno10661 83576ec33d
feat(supply-depot): add uninstall for curated apps (#1006)
Curated catalog apps could be installed, stopped, and force-reinstalled,
but never removed — the only path off a device was manual docker + DB
surgery. Custom apps already had delete; this adds the equivalent for
curated apps.

POST /api/system/services/uninstall stops and removes the app's
container (optionally its image, same best-effort semantics as custom
app delete) and flips the record back to not-installed so the card
returns to the available catalog. Host bind-mount data is deliberately
left on disk, so a later reinstall picks the app back up where it left
off — unlike force-reinstall, which clears volumes.

Guards: custom apps are rejected (use delete), dependency services are
rejected, and uninstalling a not-installed app is a 409.

UI: installed curated cards get an Uninstall action in the card menu,
with a confirm modal that explains data is preserved and offers the
same remove-image checkbox as custom app delete.
2026-06-23 04:47:05 +00:00
Chris Sherwood e2beb7ebeb
docs: document Supply Depot + auto-updates in README
Bring the GitHub-facing README in line with the v1.33 feature set:

- Add Supply Depot (one-click app catalog + bring-your-own custom
  Docker containers) and Automatic Updates to the "Built-in
  capabilities" list and the "What's Included" table.
- Fix the Offline Maps row, which claimed "navigation" — NOMAD maps
  are download/view only, with no routing. Reword to "offline viewing
  and search."

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 04:47:05 +00:00