Skip to content

Juhandu 0.3.0

Choose a tag to compare

@neostryder neostryder released this 17 Oct 01:37
· 18 commits to main since this release

Added

  • [Visible] [UI] Reading can be triaged by swipe or keyboard. On a touch screen, swiping an item right Likes it and swiping left Dislikes it, and a swipe starts only on the card body, never on its buttons or links. On a keyboard, j and k move between items, l likes, d dislikes, o opens, and r refreshes. The shortcuts button in the Reading toolbar turns both the keys and the swipes off or back on.
  • [Visible] [UI] Pulling down at the top of Reading on a phone refreshes it. While the app is open it also checks for new items every minute and shows a "N new. Show them" pill, so the list stays put under the item being read.
  • [Visible] [UI] The home-screen icon shows the unread count and has shortcuts. On systems that support app badges, the installed icon carries the number of unread items. A long press on it opens Briefing, Reading, Filtered out, or Add a feed.
  • [Visible] [UI] Juhandu appears in the phone's share sheet. Sharing a link to it opens Add a feed with the address already filled in.
  • [Visible] [Compatibility] The jev judge can send its requests through an Augur Jev gateway. With JUHANDU_JEV_GATEWAY_URL and JUHANDU_JEV_GATEWAY_TOKEN (or JUHANDU_JEV_GATEWAY_TOKEN_FILE) set, every Jev request goes to the gateway first, and private items are marked sensitive so the gateway keeps them off hosted Jev. A request goes straight to hosted Jev only when the gateway cannot be reached or fails on its own and a direct key is set, and never after the gateway refuses it. The gateway alone is enough to turn on the jev judge.
  • [Visible] [Backup] A nightly copy of the database can go to an S3-compatible bucket such as Cloudflare R2. scripts/backup-to-r2.sh runs on the Docker host, takes a consistent copy while Juhandu keeps running, and can download the newest backup for a restore.

Changed

  • [Visible] [UI] Reading shows 40 items at a time and loads more while scrolling. The next 40 arrive as the list nears the bottom, and "That is everything." marks the end. Rows outside the screen are not drawn, so long lists scroll smoothly.

  • [Visible] [UI] The app opens faster. The Rules, Feeds, Pipes, and Connect screens load when first opened, and they are fetched quietly in the background after start. The scripts the first screen needs are preloaded, and the web fonts no longer hold up the first paint.

  • [Visible] [UI] Every screen fits a phone without sideways scrolling. The browser's own pull-to-refresh and bounce no longer compete with the app's, the tap highlight is gone, and the Getting started links are easier to tap.

  • [Visible] [UI] Juhandu has a new home-screen and browser-tab icon, a golden herald's horn with a burgundy banner. The icon is a photographic image in 192 and 512 pixel sizes, with a separate maskable version that fits Android's circular and rounded crops, and it replaces the drawn seal icon and the script that generated it. A phone fetches a linked icon without the sign-in cookie, so behind an access proxy the old icon links got a login page back and the home screen showed a plain letter tile; the page now carries its small icons inside itself, so they always load.

  • [Visible] [Compatibility] Juhandu embeds text with EmbeddingGemma 2 through Cogitator's embed-gemma2 role. A gateway without that role gets the requests on its embed role (Nomic Embed Text v1.5), as before. Vectors from different models are never compared, so the first run after the upgrade embeds every item again in the background. Each item keeps its old vector until the new one is ready, and nothing waits on the work. Each model now gets the prefixes it was trained with: EmbeddingGemma 2 puts "title: none | text: " before items and "task: search result | query: " before rule sentences and examples, and other models keep "clustering: " for both. JUHANDU_EMBED_DOC_PREFIX and the new JUHANDU_EMBED_QUERY_PREFIX override them, and the new JUHANDU_COGITATOR_EMBED_ROLE names the role that embeds text. Two cutoffs moved so they let through about as many pairs as before. Two items now need a similarity of 0.82 (was 0.8) before the judge is asked whether they are the same story, set by JUHANDU_MERGE_SIMILARITY, and two voted items count as the same topic for rule proposals at 0.75 (was 0.72).

Fixed

  • [Visible] [Compatibility] Embeddings answered by another machine in a Cogitator role's chain are accepted. Cogitator can name the serving model with its machine attached, such as embeddinggemma-2@bilbo, and Juhandu used to read that as a different model and discard the vectors. It now ignores the machine name.
  • [Visible] [Compatibility] A single private or restricted YouTube video no longer pauses Gemini for an hour. When Google answers a video request with a 403, Juhandu checks the key with a plain request that names no video, and pauses Gemini only if that request is refused too; otherwise only that video is marked as one Gemini cannot read, and the next video goes to Gemini as usual. A 401 or an invalid-key 400 still pauses Gemini at once.
  • [Visible] [Compatibility] Screens loaded on demand now carry the version in their address. Before this, an upgrade could leave a cache serving the old copy of a screen.
  • [Visible] [Compatibility] The home-screen manifest is now requested with the sign-in cookie. Behind an access proxy, the phone now finds the app's name and icon instead of a login page.
  • [Visible] [Security] Assistant write tools only change items the assistant can read. The vote_item and mark_read MCP tools leave items from private feeds untouched unless private feeds are turned on for the assistant.
  • [Visible] [Security] Rule proposal drafts go to the model at the private tier whenever they include a title from a private feed. Every liked or disliked title in the request now sets the tier, where before only the items being clustered did.
  • [Visible] [Security] Removing a stored secret clears it from every backend, including an older key file kept beside the current store. The removal reports an error if any backend could not delete the secret. Removing a feed deletes its stored login first and keeps the feed if that step fails, so the owner can retry.
  • [Visible] [Security] The JSON API now compares the port as well as the host name when it checks a request's Origin. A request whose Origin names a different port than the Host it was sent to counts as cross-site, even when that host name is allowed. Setups behind a proxy on port 80 or 443 behave as before.