-
released this
2026-10-13 06:00:24 +00:00 | 0 commits to main since this releaseRelease v3.7.1
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)
-
released this
2026-10-13 05:45:50 +00:00 | 2 commits to main since this releaseRelease v3.6.4
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)
-
released this
2026-10-13 05:42:25 +00:00 | 3 commits to main since this releaseRelease v3.6.3
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)
-
released this
2026-10-12 09:56:00 +00:00 | 4 commits to main since this releaseRelease v3.6.2
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)
-
released this
2026-10-12 09:35:07 +00:00 | 5 commits to main since this releaseRelease v3.6.1
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)
-
released this
2026-10-12 09:18:40 +00:00 | 6 commits to main since this releaseRelease v3.6.0
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)
-
released this
2026-10-12 09:08:23 +00:00 | 7 commits to main since this releaseAdded
-
scripts/set-endpoint.mjs— set the AI endpoint, model and key from the command
line, without a browser. Useful on a headless machine and essential when you carry a
database to another network, because the AI address lives in the database, not in
.env:.envonly seeds it the first time the app runs against an empty database.npm run endpoint # show the current setting node scripts/set-endpoint.mjs http://10.0.0.5:8200/v1 MyModel node scripts/set-endpoint.mjs --list --test # verify it works node scripts/set-endpoint.mjs --clear-key # local service, no keyIt rejects anything that is not an http(s) URL, and
--list/--testconfirm the
endpoint before you rely on it. Shipped inside the offline bundle.
Documentation
docs/OFFLINE.mdnow explains the AI-address behaviour explicitly, and answers the
folder question: nothing has to be created by hand — the app creates its data
directory (0700) and database (0600) itself, and the bundle already contains
every other path it reads. The only directory you place is the extractedapp/.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
-
released this
2026-10-12 09:05:48 +00:00 | 8 commits to main since this releaseAdded
- Offline bundle for isolated networks.
scripts/offline-bundle.shcollects the
application, the checksum-verified SimpleAuth binary and (optionally) the official
Node.js runtime into one tarball, because the target machine cannot download
anything.scripts/offline-install.shverifies the runtime version, writes.env
from the example and prints the run command plus a systemd unit — without touching
the network. See docs/OFFLINE.md. --no-nodeto skip the ~28 MB Node tarball when the target already has Node >= 22.5,
andARCH=for a different CPU.- The guide also covers carrying existing data across (copying
data/app.dbor using
the built-in backup) and the one thing that genuinely cannot work offline: building
the Docker images, which must be saved withdocker saveand loaded with
docker load.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Offline bundle for isolated networks.
-
released this
2026-10-12 09:01:34 +00:00 | 9 commits to main since this releaseSecond audit pass: the items deliberately left unfixed in 3.2.0.
Fixed
- An archived topic could lose its archive flag. Trashing an archived topic and then
unarchiving it clearedarchived_at, so restoring it dropped the topic into the main
list instead of the archive. Unarchiving a trashed topic is now refused with
409 topic_trashed, and restoring returns it to the archive. - Wrong method returned 404.
POST /api/health(and every other known path with an
unsupported verb) looked like a missing route. Known paths now answer405with a
properAllowheader; unknown paths stay404. - Sign-in failures reached the browser as English prose. They now travel as stable
codes (signin_expired,state_mismatch,provider_rejected,account_disabled,
signin_failed,sauth_unconfigured) and are translated like every other error. - Markdown: emphasis ran across code spans, producing crossed
<em>/<code>tags
(_ab_cd_). Code spans are now extracted first and re-inserted last. - Markdown: a link whose URL contains parentheses was cut at the first
), leaving a
stray bracket and a truncatedhref. Balanced parentheses are now accepted. - The topic list was mouse-only. Cards are
<article tabindex="0" role="button">
with anaria-label, and Enter/Space opens them. - Escape did nothing. It now closes the confirm dialog (as "cancel") and the
create-topic modal; the confirm dialog remembers and restores focus, and focuses the
safe button first so the destructive one needs a deliberate move. - English content rendered inside the RTL document without bidi isolation. Update
summaries, raw text, topic summaries and reports now carrydir="auto", and long
unbroken tokens wrap instead of overflowing. - Removed 34 dead translation entries in both dictionaries (and their orphaned CSS).
Tests
- 2 more regression tests (140 total) covering the 405/Allow behaviour and the
archive-while-trashed guard.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- An archived topic could lose its archive flag. Trashing an archived topic and then
-
released this
2026-10-12 08:52:13 +00:00 | 10 commits to main since this releaseFull audit pass (security, API edge cases, frontend, data integrity, load) with fixes.
Fixed — data safety
purgecould destroy a live topic.deleteTopic()now only ever deletes a topic
that is already in the trash, so a strayDELETEcan never wipe live data.- Editing an update discarded the edit when the AI was unreachable (the request
returned 500 and the new text was lost). The edit is now saved first and the analysis
is marked failed, exactly like creating an update. - An update inside a trashed topic could be deleted while the topic was still
recoverable; that path is now refused. /api/restoreaccepted anything and replaced the whole database with it (empty
titles, unknown statuses, non-timestamp dates, orphan updates, duplicate ids). Backup
files are now validated before a single row is touched, and failures return 400 with
invalid_backupinstead of a raw SQLite 500.- NUL characters silently truncated text on write (SQLite stops at
\0). They are
now rejected withinvalid_characters. ownerwas accepted when creating a topic and then silently dropped.
Fixed — security
- Stored XSS:
formatDate()returned non-timestamp input verbatim intoinnerHTML
in three places, andstatusBadge()injected a crafted status unescaped. Both are now
escaped/whitelisted, and restore validation rejects the payload that carried them. /api/modelswas not admin-gated and honoured a caller-supplied?base=, which
forwarded the stored AI key to any host (SSRF + key exfiltration). It is now
administrator-only, together with/api/backupand/api/restore.- Open redirect:
?next=//evil.compassed thestartsWith('/')check. Only
same-origin absolute paths are accepted now. - No session expiry server-side — a 400-day-old session still authenticated. Idle
(7 days) and absolute (30 days) limits are enforced on every request. - Malformed cookies (
%zz) and a JSONnullbody produced 500s; both are handled. - Security headers were missing entirely. Every response now carries a CSP
(frame-ancestors 'none', no inline scripts),nosniff,DENYandReferrer-Policy. - Session cookies get
Securewhen the app is served over HTTPS. - The data directory is
0700and the database0600(they hold session tokens and keys). /api/healthno longer discloses the AI endpoint or model to unauthenticated callers.- Saving an unreachable SimpleAuth URL is refused, so it can no longer lock every
administrator out with no way back through the UI. - Removed the dead
sauth_admin_keysetting.
Fixed — robustness
- No outbound timeouts: a hung AI or identity provider held a request open for
300 s and leaked the socket. All outbound calls now time out (AI 180 s, models 20 s,
SimpleAuth 15 s). - The advertised
413is now delivered instead of resetting the connection. - Field length caps (title 500, description 20 000, owner/author 200, update 50 000) so
one request can no longer store megabytes. ai_baseset to an empty string no longer silently reverts to the DeepSeek default,
and a JSONnullno longer becomes the literal string "null".- AI failures now return
502 ai_unreachableinstead of a generic 500. GET /api/topics/:id/updateswithout a cursor returns the most recent page instead of
an empty list.
Fixed — interface
- "Add update" was broken whenever sign-in was disabled: the client stopped sending
an author but the server still required one. The composer now shows a name field in
that mode only. .hiddenhad no CSS rule, so the admin-only Settings entry stayed visible to members.
Added
- 13 regression tests covering every fix above, plus migration tests for the two older
database schemas (v1 and 2.6-era) proving that an existing installation upgrades with
no data loss.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads