Commit graph

62 commits

Author SHA1 Message Date
lichenblankie
5866b7b4d7 hide forum link from template when forum disabled: {{forum_link}} placeholder + DB migration 2026-06-05 05:06:11 +00:00
lichenblankie
a2d4719d74 forum README: auto-discovery note 2026-06-05 05:06:11 +00:00
lichenblankie
5af3f68130 forum plugin integration: dispatch, style toggle, nav link, URL prefill, SO_REUSEADDR 2026-06-05 05:06:11 +00:00
lichenblankie
173477e1b7 Distribute via git clone + docker compose, not registry pull 2026-06-05 05:06:11 +00:00
lichenblankie
38b5ac11d1 Fix container mode with Docker socket mount 2026-06-05 05:06:11 +00:00
lichenblankie
8610897673 Switch to host mode for Docker access 2026-06-05 05:06:11 +00:00
lichenblankie
bca5400c30 Fix workflow: install Docker in container 2026-06-05 05:06:11 +00:00
lichenblankie
ef4c134d06 Fix workflow: install jq for release upload 2026-06-05 05:06:11 +00:00
lichenblankie
c4d6c78e0c Fix workflow: use --break-system-packages for pip 2026-06-05 05:06:10 +00:00
lichenblankie
a1cdd8a0db Fix workflow: use apt-get for Python instead of setup-python action 2026-06-05 05:06:10 +00:00
lichenblankie
9f852c8ada Add pytest test suite
174 tests covering URL normalization, FTS5 query sanitization, SSRF/CSRF
guards, sharing-mode logic, DB schema and upsert paths, handler
end-to-end flows, and gateway body-size / mesh-whitelist guards. Each
recent bug-fix commit (6ffd38d, 1bc695f, 8dffd8c) has an explicit
regression test in test_regressions.py. One xfail documents a minor
latent bug in clean_url where port 80 is not stripped from upgraded
https URLs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
26fc0c81d7 Add data-loss guards and first-run empty state
- Bulk delete now routes through a server-rendered confirmation page
  listing the selected titles; a `confirmed=1` form field is required
  before pages are actually deleted. Mirrors the single-delete flow.
- Reset-template button gains a JS confirm() so stray clicks don't wipe
  the custom template.
- Homepage shows a short, neutral empty-state block when the index has
  zero pages and no query — just names what tinyweb is and links to
  /add, /style, and /subscriptions as equal options.
- /about gains a "your data" section explaining what lives in
  ~/.tinyweb/ (identity file, index.db), what losing each costs, and
  how /export differs from a full backup.
- README gains a "Backups" subsection mirroring the /about copy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
64b81c3957 Harden network and privacy defaults; fix several bugs
Security:
- Bind HTTP gateway to 127.0.0.1 by default; add --bind for LAN opt-in
- Restrict Reticulum mesh surface to GET /api/sites only (CSRF cannot
  authenticate mesh callers, so gate by whitelist)
- Cap request body size at 16 MiB to prevent memory DoS
- Redact /bookmark query strings from request logs so the bookmark token
  and URLs do not land in stdout / docker / journal logs
- Tighten FTS5 sanitizer: strip colon, drop AND/OR/NOT/NEAR operator words
- Expand .dockerignore; document trust model in README

Features:
- Add sharing mode toggle (share everything except private vs share only
  public-tagged) with /share/preview so users can see what subscribers
  would receive before enabling sharing

Bugs:
- handle_export() crashed on every call (missing query kwarg)
- Dead float16 decompression branch in embeddings.py silently corrupted
  the HNSW index when compress_embeddings was on
- GATEWAY_PORT staleness: --port and find_available_port had no effect
  on the actual bind
- semantic_search default mismatched between db.py ("1") and the rest of
  the app ("0"), causing embeddings to be generated when the UI said off
- Connection pool returned connections with uncommitted transactions to
  the next consumer
- Gateway POST body decode 502'd on non-UTF-8 input
- ensure_rns_config clobbered user-edited ~/.reticulum/config; now only
  rewrites files it authored (sentinel-tagged)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
af46ed66e8 Add LoRa support with background sync and settings UI
- Progressive retry in rns_client.py: fast timeout (15s) then slow (60s+)
  for LoRa/multi-hop links, with automatic fallback
- Background sync threads so subscriptions page returns immediately
  with syncing/error status indicators per subscription
- LoRa RNode configuration in settings page with serial port and
  expandable advanced radio settings (frequency, bandwidth, etc.)
- Internet transport now toggleable alongside LoRa — users can
  enable one, the other, or both
- Reticulum config auto-generated from settings on startup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
9885ea965d Fixed edge case domains 2026-06-05 05:06:10 +00:00
lichenblankie
4fd954238d Add public/private sites 2026-06-05 05:06:10 +00:00
lichenblankie
79f10a3088 Optimized storage and updated readme 2026-06-05 05:06:10 +00:00
lichenblankie
a554068bb6 Add Docker setup instructions 2026-06-05 05:06:10 +00:00
lichenblankie
14e201f427 Fixed workflow build 2026-06-05 05:06:10 +00:00
lichenblankie
036b40d012 Add bulk operations, select all, and orphaned tag cleanup
- Bulk delete and retag from browse page with checkboxes
- Select all / deselect all toggle
- Delete confirmation shows count of selected pages
- Auto-cleanup orphaned tags on delete, edit, and bulk actions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
a7d034e338 Privacy hardening: degoogle, security headers, referrer protection
- Replace Google Fonts with system font stacks across all themes
- Add Referrer-Policy, X-Content-Type-Options, X-Frame-Options, CSP headers
- Add rel="noreferrer noopener" on all outbound links
- Add no-referrer and dns-prefetch-control meta tags to all themes
- Clean tracking params on outbound links from trusted/remote sources
- Remove Google domains from CSP whitelists

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
a3c6068045 Add kodama2 theme with styles for new handler features
Adds pagination, meta, and success message styles, plus input
selectors for new form fields (edit page, manual entry, transport node).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
2f27ce5cf6 Disabled semantic search and reranker by default 2026-06-05 05:06:10 +00:00
lichenblankie
cd20e51c55 Add PyInstaller builds, AGPLv3 license, transport node selection, and rmap.world link
- Add pyinstaller.spec and GitHub/Forgejo CI workflows for cross-platform builds
- Add AGPLv3 license
- Move data storage to ~/.tinyweb/
- Add --version and --port CLI flags
- Add transport node selection in /style (smart regeneration preserves Reticulum config)
- Add discover more nodes link to rmap.world
2026-06-05 05:06:10 +00:00
lichenblankie
6b6045978d Fix gap in add form between URL and note fields 2026-06-05 05:06:10 +00:00
lichenblankie
759a3d8f18 Add radio toggle for URL vs Reticulum hash input in add page 2026-06-05 05:06:10 +00:00
lichenblankie
142deb6635 Add dropdown to switch between add site and subscribe in same input box 2026-06-05 05:06:10 +00:00
lichenblankie
0fa3f17a41 Add reticulum destination hash option to add URL page 2026-06-05 05:06:10 +00:00
lichenblankie
b8d76121db Added manual entry 2026-06-05 05:06:10 +00:00
lichenblankie
c30cb1ef46 Make semantic search and reranking optional, use site meta descriptions for snippets
- Add semantic_search setting to toggle AI-powered search on/off
- Skip embedding generation, hybrid search, and model preloading when disabled
- Use site owner's meta description as snippet instead of heuristic extraction
- Remove _generate_summary() and snippet() - no more generated snippets
- Show reranker/reindex controls grayed out when semantic search is off
- AI dependencies (onnxruntime, hnswlib, etc.) are now fully optional

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
8caa3d5b70 Improve snippet generation with heuristic extraction instead of AI
- Case-insensitive meta description extraction (fixes sites like Lemmy
  with capitalized "Description" meta name)
- Strip aside and noscript tags for cleaner body text
- Extract paragraph text separately for better sentence quality
- Prefer sentences mentioning the site name, then first quality
  paragraph, then title as fallback
- Skip meta descriptions under 20 chars (e.g. just "Lemmy")
- Remove embedding/centroid dependency from summary generation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
150c6faf04 Strip noscript tags when parsing pages to remove JS-disabled messages
Lemmy and other JS-heavy sites include noscript fallback text like
"Javascript is disabled" that pollutes the stored body text and
generated snippets/summaries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
ffaf272add Fix reindex to re-embed all pages and preserve existing summaries
Previously reindex skipped pages that already had chunks, leaving stale
embeddings in place. It also overwrote good meta description summaries
with auto-generated ones. Now it clears all chunks first so everything
is re-embedded, and only generates summaries for pages missing one.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
eb5c17ca2f Add junimo theme and increase browse page size to 50
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
7e70d1ac22 Add hybrid semantic search with optional cross-encoder reranking
Implements a three-stage search pipeline:
1. BM25 keyword search via FTS5 with column weights
2. Semantic search via Snowflake arctic-embed-s bi-encoder + HNSW index
3. Optional cross-encoder reranking (on by default, toggleable in settings)

Top 20 results are reranked for precision, next 10 appended from RRF
for coverage, giving 30 total results across 3 pages.

- New embeddings.py with ONNX Runtime inference, text chunking, HNSW
  index management, RRF fusion, and cross-encoder reranking
- Meta description extraction for authentic page snippets with centroid
  extractive fallback
- Stopword filtering in FTS5 queries to avoid overly strict matching
- /reindex page for batch embedding of existing pages
- Semantic embedding of remote pages during subscription sync
- ~125MB dependency footprint (onnxruntime, tokenizers, hnswlib, numpy)
- Models: 34MB bi-encoder + 22MB cross-encoder (downloaded on first use)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
e169acd152 Fix navbar disappearing when saving customize form
Browser textarea submissions convert \n to \r\n, causing the template
comparison against DEFAULT_TEMPLATE to always fail. This saved the bare
skeleton as a custom template, overriding the default navbar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
c4764934fe Redesign subscriptions page with card layout
Replace cramped table layout with card-based design that works
better in narrow viewports and across different themes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
69876f2275 Set share_instance = No for reliable mesh announces
With share_instance = Yes, announces weren't being sent over TCP
in Docker environments. Setting it to No ensures each TinyWeb
instance manages its own Reticulum interfaces directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
e7ffcc179e Add delay before announce to ensure TCP interface is ready
The announce was firing before the TCP transport connection was fully
established, causing Docker instances to never announce over the mesh.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
e4c754f234 Add default internet transport node for zero-config mesh connectivity
New TinyWeb instances now auto-connect to reticulum.derickphan.com:4242
so users get internet mesh connectivity out of the box without any
manual Reticulum configuration. Env var overrides still supported.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
170de2ae9b Add entrypoint script for configurable Reticulum networking in Docker
Replaces static CMD with an entrypoint that generates RNS config from
environment variables (RNS_TCP_HOST/PORT), enabling TCP transport for
environments without LAN auto-discovery (e.g. Docker on macOS).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
5369922001 Add Dockerfile and Docker Compose for one-command setup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
eda86d8551 Add WAL mode, connection pooling, pagination, and delta sync
WAL + pooling:
- Enable WAL journal mode for concurrent read/write support
- Add connection pool (size 4) with return_db() to reuse connections
  instead of opening/closing on every request

Pagination:
- Search results, /pages, and /tags/<name> now paginate at 50 per page
- Prev/next navigation links appear when results exceed one page

Delta sync:
- Pages table gains last_modified timestamp, set on insert/update
- /api/sites accepts ?since= param to return only changed pages
- Subscription sync uses last_sync timestamp for incremental fetches
- Remote pages upserted instead of delete-all/re-insert
- Full sync includes all_urls list for detecting remote deletions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
00d425bc43 Normalize URLs to prevent duplicate indexing
clean_url() now canonicalizes: http→https, strips www., removes
trailing slashes, drops default ports, and sorts query params.
Prevents the same page from being indexed multiple times under
different URL variations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
c7cc53783b Add README with setup, usage, architecture, and security docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
cc997375fd Fix index_url using wrong page_id after upsert
lastrowid returns 0 when ON CONFLICT DO UPDATE fires on an existing
row, causing links to not be cleaned up or associated correctly on
re-index. Now fetches the actual row ID with a SELECT after upsert.
Also adds try/finally for connection safety.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
e4fafe5187 Fix SSRF redirect bypass, identity permissions, error leakage, and DB connection leaks
- SSRF: disable automatic redirects, manually follow up to 5 hops with
  IP re-validation at each step to prevent redirect-to-localhost bypass
- Identity file: enforce 0600 permissions on tinyweb_identity at load
  and creation to prevent other users from reading the private key
- Error messages: replace raw exception strings with generic messages
  to avoid leaking internal paths/hostnames to the UI
- DB connections: wrap all get_db() usage in try/finally to guarantee
  close() even when handlers throw mid-operation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
d101901361 Harden security: bookmark auth, CSP headers, per-session CSRF, and more
- Bookmark endpoint now requires a secret token (stored in settings)
- Style reset moved from GET to POST with CSRF protection
- Open redirect prevention in _redirect() helper
- Import capped at 100 URLs to prevent abuse
- page_tags cleaned up on delete + PRAGMA foreign_keys enabled
- CSP, X-Frame-Options, X-Content-Type-Options on all responses
- CSRF tokens now per-session via double-submit cookie pattern
- Tag names URL-decoded for special characters
- Gateway forwards cookies in request data

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
28ba41feb5 Add security hardening: CSRF, SSRF, FTS5, and DELETE via POST
- CSRF: Generate random token at startup, include as hidden field in
  all 11 POST forms, validate at top of POST dispatch (returns 403)
- SSRF: Block private/internal IP ranges (127/8, 10/8, 172.16/12,
  192.168/16, 169.254/16, ::1, fc00::/7) by resolving hostname before
  fetch. Remove verify=False from requests.get().
- DELETE: Change /delete/<id> from GET (instant delete) to GET
  (confirmation page) + POST (actual delete) to prevent accidental
  deletion from prefetchers/crawlers.
- FTS5: Wrap search input in double quotes to neutralize FTS5
  operators (AND, OR, NOT, *, column:). Add try/except fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00
lichenblankie
7b0e56bd1d Add themes folder with kodama template and gitignore index.db
Save the custom kodama template to themes/kodama.html so it's
version-controlled as a file rather than only living in the database.
Stop tracking index.db since it's runtime data, not source code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 05:06:10 +00:00