# Changelog All notable changes to killBottleneck. Dates are the release date of the tag. The version you are running is shown in the **About** dialog; the same string is in `KB_VERSION`. Upgrading is always `git pull && docker compose up -d --build` (see [Updating](https://killbottleneck.com/guide/updating)) — read the **Upgrade notes** below before you jump several versions. --- ## v1.0.1 — 2026-11-08 **The assistant on your own OpenAI key: reasoning models without extra settings, reminders and recurring events** **Upgrade notes:** no migration. If you added `KB_CHAT_OPENAI_EXTRA={"reasoning_effort":"none"}` to make the assistant work on OpenAI, you can keep it (it still takes precedence) or delete it. - **Reasoning models work with the assistant out of the box** (`openai` provider, e.g. `gpt-5.6-luna`). OpenAI refuses a reasoning setting other than `none` together with tool calling, so every assistant turn ended with *error 400*. The app now retries with `reasoning_effort: none` and then without the field, and remembers per model what worked, so later turns go through on the first call. - **Drawing through OpenAI defaults to `gpt-image-1-mini`** instead of `gpt-image-1`, which many new API keys no longer have (every picture failed at once). Set `KB_IMAGE_MODEL` to choose another model. The error message on a refused request no longer shows doubled braces. - **Reminders without a project:** "Remind me tomorrow at 8 to call the bank" now becomes a calendar event with a reminder, not an idea in the inbox. - **Recurring events:** "Every Monday at 9 we have a team meeting" becomes a series of events in the calendar (up to 30); when you don't say until when, the assistant usually asks first. A repeating *step* in a map keeps its own rhythm as before. - **New guide: [Your own OpenAI key](https://killbottleneck.com/tutorials/openai)** — the assistant and drawing on a self-hosted server with one OpenAI key, which models to pick, what we measured and what to do when something fails. --- ## v1.0.0 — 2026-11-07 **The first stable release — and killBottleneck is open source under the MIT license** **Upgrade notes:** one data migration fills in the language of existing accounts (`users.language = cs` wherever it was empty) so that nobody's e-mails or notifications change language. This is the first release the stable update channel has offered since v0.33-beta — if you jump from an older beta, read the *Upgrade notes* of the versions in between as usual (`git pull && docker compose up -d --build`, a backup first). - **License: MIT.** The whole product — server, web client, MCP server, desktop app — is released under the [MIT License](./LICENSE). Download it, run it, modify it, host it for others, build on it. The beta releases (v0.x) stay under the Sustainable Use License; the change applies from v1.0.0 on. - **The name and the logo stay with the authors** — see [TRADEMARKS.md](./TRADEMARKS.md). A fork needs its own name; "based on killBottleneck" is welcome. The commercial-license offer (`licence@`) is gone from every text. - **End of the beta.** The website, the README and the app no longer say "public beta"; the `KB_UPDATE_PRERELEASE` switch now means release candidates (`-rc`). - **English first.** `docker-compose.yml`, `.env.example` and the `Dockerfile` are commented in English only; new code, comments and logs are written in English; `AGENTS.md` describes the repository for people and AI agents. A regression test (`en-first`) keeps the amount of Czech in comments and logs from growing. - **Behaviour change — fallback language is English.** A user whose account has no language set now gets English e-mails, notifications and assistant texts instead of Czech, and so do answers to visitors who are not signed in (closed registration, public map links) unless their browser asks for Czech or Slovak. Existing accounts are not affected: the migration above writes `cs` into every empty `language` field, which is exactly how the server treated them before. An account created without a language (API, MCP) gets English server texts until its owner signs in through the web app, which then stores the browser's language (Czech/Slovak → Czech, everything else → English) as before. - The *About* dialog describes the MIT license and links to its full text; `NOTICE.md` and the third-party list are updated accordingly. **Fixes from the feature analysis of 5 Nov 2026** (behaviour defects verified live, no new decisions): - **"Delete selection" no longer loses data.** Bulk deletion skips goals whose task was assigned by someone else (only the assigner or the project owner may remove them) and says so, exactly like the trash button and the Delete key. Before, the server refused the whole save, the app stayed silent and later work was lost on reload. - **Lite quick add offers only projects you can write to** (owner, named "edit" share, team edit). A read-only project ended with "The requested resource wasn't found". - **Archiving by a non-owner is refused out loud** (400 with a message) instead of a silent 200 with no effect. - **Transferring project ownership (API for now, no button yet):** `POST /api/kb/maps/{id}/transfer` (owner or administrator; the previous owner stays a co-admin with edit rights) and, before deleting an account, *User administration* offers to take over the user's projects (`GET /api/kb/users/{id}/maps`, `POST /api/kb/users/{id}/transfer-maps`) — deleting an account used to delete its projects by cascade without warning. A bare `PATCH` of `owner` now derives `owner_email` and the share lists the same way. - **MCP: new `update_map` tool** (title, description) — renaming a project through an agent used to change only the apex text while the map list and the database kept the old title. 35 tools now; the HTTP `/mcp` catalog and the stdio server stay 1:1. - **Welcome map of a member:** the root goal reads "Welcome to killBottleneck" like the title (it said "Get going on your own"). - Small: the timer is shown on the apex for readers too; the Google button is hidden on the sign-up page in the native shell (as on sign-in). --- ## v0.101-beta — 2026-11-06 **License housekeeping from the third code review — nothing copyleft, every notice in order** **Upgrade notes:** one data migration (`1793956000_canvas_texty_vlastni`) rewrites the block texts of the two system templates *Business Model Canvas* and *Lean Canvas* — only if they still carry the original seed wording; a template an admin edited is left alone (and logged), personal copies are untouched. Rebuild the image as usual. - **PDF fonts renamed.** The Latin subsets of Liberation Sans / Liberation Serif used for invoices, Office PDFs and PDF text fixes are now called **KB Sans / KB Serif** (`frontend/public/fonts/pdf/KBSans-*.ttf`). The OFL reserves the name "Liberation" for unmodified fonts; the files now ship the upstream OFL text instead of a Debian copyright file that mentioned GPL-2+. Generated PDFs look exactly the same. - **Business Model Canvas and Lean Canvas** credit their authors (Osterwalder / Maurya, CC BY-SA 3.0) in the template description; the Lean Canvas block descriptions are reworded in our own words instead of the original's. - **License texts travel with every copy:** the Docker image serves `/LICENSE.txt` and `/THIRD-PARTY-LICENSES.txt`, and the *About* dialog links to both (also from the Android app, where the files live on the server). The About text no longer promises "no strings" — it says what the Sustainable Use License allows (own use, commercial too; no hosted resale, no white-label). - **`THIRD-PARTY-LICENSES.md`** regenerated: dual-licensed packages are marked with the option we use and the GPL text of jszip is no longer reproduced; Plus Jakarta Sans, the KB fonts and shadcn/ui are listed; the generator's allowlist no longer lets "(GPL-3.0 AND MIT)" through (`scripts/licence-pravidla.mjs`, shared with the test). `NOTICE.md` is a short, current notice instead of a stale package table. - **Website:** the Geist font ships its OFL text and is listed in `docs/THIRD-PARTY.md`. - **MCP package** `killbottleneck-mcp` 0.101.0 — the README lists `generate_image`, the version follows the app again. - Tests: `licence-soubory` (28 checks over the tree), `licence-instance` (instance serves the texts, templates seeded with the new wording), `upgrade-canvas-texty` (existing instance gets the new texts, personal copies stay). --- ## v0.100.1-beta — 2026-11-05 **A weekly picture count instead of credits** **Upgrade notes:** no migration. Nothing changes unless you set the new variable. - **`KB_OBRAZKY_TYDEN`** caps the pictures of the whole instance per week (from Monday, like the credit quota; empty / 0 = no cap). Every successful drawing counts — chat, New image, redraw, Office and Pages; the daily-summary banner does not. Over the cap the card ends with "All N pictures of this instance for this week are used up…", before any model or picture service is called. - **`KB_OBRAZEK_KREDITY=0`** now means pictures are outside the credit quota: neither the drawing nor the prompt that the assistant writes for New image costs credits, and an exhausted weekly credit quota no longer blocks them. --- ## v0.100-beta — 2026-11-05 **Pictures drawn by the assistant** **Upgrade notes:** four migrations (collection `ai_images` and its flags, `users.sumar_email`, `daily_summaries.odeslano`) run on start, no manual step. Drawing is **off until you connect a picture service** — without `KB_IMAGE_*` nothing new shows up and the assistant's prompts are unchanged. To turn it on with your own service, set `KB_IMAGE_PROVIDER=openai`, `KB_IMAGE_TOKEN` (API key) and optionally `KB_IMAGE_URL` / `KB_IMAGE_MODEL` (default `gpt-image-1`, the service must return JPEG); see `.env.example` and the Pictures guide. - **The assistant draws.** "Draw an illustration…", "make an e-mail banner…": the assistant writes a detailed prompt for the picture model and a card with the picture appears under the reply (download, redraw). Pictures with readable text need `KB_IMAGE_TEXT=1` and a second model; short captions work, an occasional typo is fixed by redrawing. - **Office → Pictures tab:** New picture without a chat (four shapes, seven styles, Painting by default), search, a viewer with arrows, full screen, download, redraw, pin forever and delete. Pictures used in no document or page are deleted after 90 days (the viewer says when); used and pinned ones are kept, and a used picture cannot be deleted. - **Office documents and Pages get pictures on their own:** a title picture and up to three slide pictures in presentations, a picture on cards, invitations and posters, and up to three illustrations in assistant Pages (public `/p/` pages included). Without a picture service everything works as before, just without pictures. - **Daily summary by e-mail** (each user turns it on in Notifications) with the day's banner when pictures are on; `KB_SUMAR_EMAIL_MAX` caps summary e-mails per instance and day (default 100). - **API and MCP:** `POST /v1/images` and the MCP tool `generate_image` (34 tools now). - **Limits:** a picture counts `KB_AI_GEN_VAHA` (default 2) towards the hourly AI limit; `KB_OBRAZEK_KREDITY` sets a fixed credit price per picture (empty = the model's reference price). Drawing interrupted by a restart ends as an error with "Try again" instead of spinning forever. --- ## v0.99-beta — 2026-11-05 **Fixes from the third code review (wave A)** **Upgrade notes:** no migration. The Docker image now includes `sqlite3` (+2.8 MB) because `backup.sh` uses it for consistent snapshots — rebuild with `git pull && docker compose up -d --build` before your next backup; an older image makes `backup.sh` stop with a clear message instead of writing a possibly broken archive. Behaviour change: events in an **archived calendar can no longer be edited or deleted** (unarchive first), the same rule that already applied to creating them. - **Backups are consistent while the instance runs.** `backup.sh` takes an `sqlite3 .backup` snapshot of the databases instead of packing the live WAL files (under write load 4 of 35 such archives were silently broken); `backup.sh restore` checks `PRAGMA integrity_check` before stopping the instance and refuses a damaged archive. - **The assistant survives reading large maps.** Tool results stored in the conversation are trimmed to what the model actually receives; previously the fourth read of a 250-node map hit the 200 kB message limit, the turn failed with 502 and the user's message (voice note, photo) was lost. A failed save is now logged and reported honestly. - **Calendars "for selected people only" can be assigned again.** The member list in Organization settings → Calendars was always empty since v0.83 (`getMembers` returned an object, the list expected an array). - **"Today / This week" in My Day matches the task list.** The client filter mirrors the server's definition (plan and deadline, "this week" = 2–7 days), so the chip count and the filtered list agree; the manual filter on the Tasks page is therefore labelled "In 2–7 days" now (today and tomorrow have their own chips); overdue tasks planned for later are no longer shown as overdue; the idea buffer is filtered by "Today" too. - **Timeline:** week 53 of 2026 is one block (ISO year in the week key) and a node's own plan moves the start of its bar. - **"Today" without a client date uses the instance time zone** (My Day, portfolio, assistant, summaries) instead of UTC. - **Archived calendars are read-only**: editing or deleting their events returns 400 like creating them did. - **The "new version" bell no longer repeats** every 30 days on instances that have not updated. - **MCP via a custom domain:** discovery and `WWW-Authenticate` answer with the address the client used (slug or custom domain), so connectors created before switching the domain keep working. - **Invited users get the inviter's language** (or the language sent with the invitation) — invitation and welcome e-mails are no longer always Czech. - **MCP package:** the stdio server knows the `offer_assistant` rule action and the `mode` field; its README lists all 33 tools. - Dev: `cloud/kontrola-env.sh` is green again and runs early in `run-all.sh` (right after the secrets check). ## v0.98-beta — 2026-11-03 **A different skin every day and clearer Organization settings** **Upgrade notes:** migration `1793260418_user_skin_rotace` adds the field `users.skin_rotace` (on/off, off by default) and runs automatically on start; no data changes and nothing to configure. Old links keep working: `/admin/users#ai` opens the *AI* tab and `/admin/users?fakturace=1` the *Subscription and invoices* tab. The Android app shows the new skin option after its next update. Self-hosters: `git pull && docker compose up -d --build`. - **A different skin every day.** New switch in *Appearance*: the built-in skins rotate on their own, the same one on all your devices in the same time zone (also in the simplified view and via the assistant). - **Clearer Organization settings.** The page is split into six tabs (People · Organization and look · Structure and deputies · Shared calendars · Subscription and invoices — just *Invoices* on a self-hosted server · AI) with a tab list on the left; the people table has 7 columns instead of 9 with password reset and account deletion in a ⋯ menu, and phones get a card per person. Nothing was removed and no permissions changed. ## v0.97-beta — 2026-11-03 **Security update — please upgrade** **Upgrade notes:** the bundled PocketBase moves from 0.39.6 to **0.39.11** (fixes a publicly known bug where a crafted request could crash the server — GHSA-84vh-m24q-wjjx) and the base image to Alpine 3.22; the download is verified by checksum during the build. Migration `1791700000_uploads_protected` runs automatically on start (once). **Sign-in rate limiting is now on** (20 attempts / 60 s, 20 record creations / 5 s, 300 API requests / 10 s, per client IP). If you run behind a reverse proxy (Caddy, nginx — see [HTTPS](https://killbottleneck.com/guide/https)), add `KB_TRUSTED_PROXY_HEADER=X-Forwarded-For` to `.env`, otherwise the whole company counts as one client; `KB_RATE_LIMITS=0` turns the limits off. No data changes. Self-hosters: `git pull && docker compose up -d --build`. - **Security:** PocketBase 0.39.11 — a crafted request can no longer crash the instance. - **Security:** sign-in and the API have server-side rate limits per client IP — password guessing is slowed down (new variables `KB_RATE_LIMITS`, `KB_TRUSTED_PROXY_HEADER`, see the upgrade notes). - **Security:** the "own AI server" address check could be tricked by a specially formed address into reaching the hosting provider's internal network; the check now reads the address the same way the connection does. - **Security:** comments — after posting, the author and the map a comment belongs to can no longer be changed through the API (only the text can). - **Security:** API keys — and the OAuth clients of connected AI agents — are revoked when the owner changes their password, resets it through "Forgotten password", or an administrator resets it. Connect the agent again afterwards. - **Security:** the legacy `uploads` collection (not used by the app — attachments live in `node_files`) is closed: its files are no longer readable without signing in and nothing new can be uploaded into it. --- ## v0.96-beta — 2026-11-02 **Office in the assistant: a presentation (PowerPoint or PDF), a greeting card, invitation, poster or certificate (PDF), and every document as PDF or Word** - **Presentation** — "Make a presentation of project #12 for the client": the assistant takes the facts from the map and writes the slide outline into **Documents** (editable text: `# title`, `## slide`, `- bullet`, `> speaker note`); the card and the Documents panel offer **PowerPoint (.pptx)**, **PDF** (16:9) and **Word**. The deck uses your skin colour and the organization logo; speaker notes go into the PPTX. Ask for changes ("shorten slide 3") — the assistant rewrites the outline, the previous version stays. (A web page shared by link is still the assistant's Pages; this is a file to download.) - **Greeting card, invitation, poster, certificate** — "Write season's greetings to clients", "an invitation to the Christmas party on 12 Dec at 6 pm": a printable **PDF** from one of four vector templates (A5 card, A5 invitation with When / Where and a QR code for a link, A4 poster, A4 certificate). In Documents you pick the template and colour and can add your own photo (PNG/JPG — it goes only into the file, nothing is stored). No AI pictures — the design is vector, logo and your photo. - **Every document as PDF or Word** — e-mails, notes, meeting points and summaries download on a letterhead (organization name + logo), with a page footer; e-mails carry To / Subject. Download as text stays. - **AI helper "Presentation, card, invitation"** in the AI block: the app asks first what to prepare, for whom and in which tone (no model call), then the assistant continues. The assistant also offers a deck after a project summary or meeting preparation. - All files are generated **in the browser**; the server never stores them. New document kinds `presentation` and `card` and a `meta` field on `ai_documents` (migration, no action needed). Private like all documents. - **Office panel.** The Documents button in the assistant header is now **Office** (briefcase) with the tabs **Documents**, **Pages** and **PDF** — the separate PDF button is gone (two look-alike icons were confusing). PDF files chosen in the tab survive switching tabs and closing the panel. **New** next to the filter (in every section, Invoices included) opens a **New document** form composed by the app (no model call): pick the kind — presentation, greeting card, invitation, poster, thank-you, e-mail, meeting points, note, project summary, invoice or free text — fill in a few details and **Prepare** sends the request to the assistant. An empty filter shows the kinds as chips; the AI-block helper opens the same form. After the assistant reads a map without writing anything, the app adds the chip **Turn this into a presentation** (the model rarely offered it on its own). - **Fixed (local models):** when the model asked to open a tool group that was already open, the server re-asked it with the same prompt and the model repeated the same request until the rounds ran out — the reply came back empty. The model now gets a one-line note naming the tools it already has and continues; a tool call "written" in the last round without tools yields a readable sentence instead of an empty bubble. A tool call written as text (`[Tool: name] {…}`, seen with Qwen-family models with low reasoning) is converted into a real call — only when it stands at the very start of the reply, and never for tools that change data without a confirmation card. ### Upgrade notes - Migration `1792001345_ai_documents_kancelar` adds the document kinds `presentation` and `card` and a JSON field `meta` on `ai_documents` (empty for existing documents). Its timestamp is older than the already applied `1792621271`, `1792928875` and `1793096888`; PocketBase applies it anyway. Nothing to do. - New npm dependencies `pptxgenjs` and `docx` (both MIT) are listed in `THIRD-PARTY-LICENSES.md`; the usual `docker compose up -d --build` picks them up. --- ## v0.95-beta — 2026-11-02 **From an invitation to the calendar: a photo, PDF or e-mail with several dates becomes events on one card** - **The assistant reads invitations and schedules.** A photo of a lecture schedule on a door, a PDF or a pasted e-mail with several dates → the new tool `create_events` puts every date on **one card** (weekday, time, place; *already in the calendar* and *at that time you have …* per line). Repetition ("every Tuesday until January") is expanded into single days (max 30), excluded days and past dates are left out (a past date that stays is marked *already passed*), a missing year means the nearest future date and unclear dates are asked about. Everything or nothing is written after the card is confirmed. - **Then the app offers a project for the preparation** (no AI call until you click; with two or more events, or one event from a photo, PDF or pasted e-mail): *Also create a project for the preparation* builds a project with sign-up, payment, equipment… and the deadlines the invitation states; the event dates themselves stay in the calendar. - **Events have a place** (`place`, ≤ 200 characters) — in the event dialog, the calendar, My day, lite, the reminder text, export/import, REST and MCP. A meeting room to *book* is still a shared calendar. - **Event series.** Events created together share a `series`; the event dialog offers **Delete the whole series**. - The image transcription now copies dates, weekdays, times, places and exceptions exactly and keeps tables row by row. - API: `POST /api/kb/v1/events/batch` (1–30 items, all or nothing, duplicates skipped and reported, one invitation notification per batch, no time off; a date already in the *same* calendar is skipped, even in a room calendar), `POST /api/kb/v1/events/series/{series}/delete`; MCP `create_events` (remote `/mcp` and the stdio server in the repository; the published npm package is still 0.63) and `place` on `create_event` / `update_event`. Unknown fields `location`, `venue`, `where`, `address` now hint at `place` (`location` used to hint at `calendar`). ### Upgrade notes - Migration `1792621271` adds `events.place` and `events.series` (empty for existing events). Nothing to do. --- ## v0.94-beta — 2026-10-29 **Two map styles: a project to finish, or a process that keeps running** **Upgrade notes:** migration `1792928875_kind_process` runs automatically on start (adds `process` to `goalmaps.kind`, `map_kind` to `templates` and the system template *Weekly office operations* — once; a system template you deleted is not restored); no new variables. Existing maps stay projects and are unchanged; existing recurrence rules keep working. The "today" a recurring step's next deadline is counted from now follows the instance's `TZ` (default UTC), like scheduled jobs. - **Project or process** — *New project* offers two tiles: 🎯 **Project** (finish something: % done, archive when everything is done) and 🔁 **Process** (keep something running: steps repeat, nothing gets finished). The style can be switched any time in ⋮ (owner only); steps and deadlines stay, recurring steps keep repeating (in a process they also reset their sub-steps — the server adds or removes that on the switch). A step added with **Add step** is assigned to you. See [Project and process](https://killbottleneck.com/features/project-and-process). - **Run health instead of %** — a process's apex shows *N on time · M overdue · last run*, the map overview has a **Run health** card (on time, overdue, runs in 30 days, a table of steps), Organization a **Processes** card and tile. Process steps never count as "Not moving". - **Richer recurrence** — weekdays (e.g. Tue and Thu), day of the month and every N-th (every 2 weeks…). "Every month on the 31st" now really stays on the 31st — a shorter month gets its last day, the next month the 31st again (before, it slid to the 28th for good). A recurring step coming back also clears the "when I'll work on it" plan; in a process its sub-steps start over (new rule action `reset_subtree`). The rhythm shows in words on the card for everyone who can see the map, and in My day. - **Fixed:** a reminder on a recurring goal was deleted when the goal was ticked off; it now stays and moves with the deadline. - **Fixed:** the next deadline of a recurring goal is counted from *today* in the instance's time zone (`TZ`, as the scheduled jobs already were), not in UTC — just after midnight in Prague a late step ticked off no longer comes back due "today". - **API / MCP / assistant** — `kind: "project" | "process"` on `POST /v1/maps` and `create_map`, `kind` on the map list and detail (the org map reads `org` in both), `health` on a process's detail and in the portfolio (a process has `pct`, `done`, `total` = `null`; `counts.projects` still counts all maps, the new `counts.processes` how many are processes), `every` / `days` / `month_day` on `set_deadline` with `advance`. On a public map a visitor who is not signed in sees a process's rhythm and on time / overdue, not when steps were ticked off. A new system template *Weekly office operations*; the former "Process template" label is now **Template with a schedule**. - **Assistant knows processes** — "make *Stock check* repeat every Friday" sets the step's rhythm the same way as the Recurrence switch (new tool `set_step_rhythm`, one card per step, first due date included), "change it to a project" switches the map style (`set_map_style`, owner only), and reading a map tells the assistant it is a process and how it runs. - **Projects and processes apart** — the home page has an **All · Projects · Processes** switch (shown once you have a process, remembered), and in Tasks a process is marked *🔁 Process* with *process goal* / *process step* rows and the step's rhythm. - Migration adds the `process` value to `goalmaps.kind` and `map_kind` to `templates` (no action needed). - **Fixed:** with some local models (Qwen family, e.g. on llama.cpp-style servers) the assistant occasionally got a tool call half in the model's own XML form — the rest of the call ended up inside one text field (a project goal reading `…`) and the other fields were lost. The server now cuts the field at the first such tag and fills in the parameters behind it, only those the tool really has, typed by its schema and never overwriting a value the model did give. An odd parameter name from the model can no longer break the assistant's reply. ## v0.93-beta — 2026-10-29 **Invoices for anyone in the world: supplier country, any currency (EUR, USD, GBP…), generic tax, free-form bank details, SEPA QR** **Upgrade notes:** migration `1793096888_clients_mena` runs automatically on start (adds the optional `clients.currency`); no new variables. Existing supplier profiles and invoices are unchanged (no country = Czech Republic, CZK, SPAYD QR as before). - **Supplier country.** The supplier profile (shared or own) has a `country` (ISO 3166-1 alpha-2; empty = CZ, so profiles from v0.86 are unchanged). It presets the currency, tax rate and label, the ID labels (IČO/DIČ · EIN/Tax ID · Company No./VAT No.) and the payment block; everything can be overridden (`currency`, `vat_rate`, `tax_label`, `bic`). Presets (one table for the form and the assistant): CZ, SK, US, GB, DE, AT, PL, NL, FR, IE, CH, CA, AU; any other country = EU (EUR, VAT, IBAN + BIC). Outside CZ/SK the bank account is **free text** (routing + account, sort code…) — the server no longer rejects it; a Czech account number or an IBAN is still recognized and validated. `ico` outside CZ/SK accepts letters and dashes (EIN `12-3456789`, `SC123456`); a tax ID is required for charging VAT except in the US. New tool fields `country`, `currency`, `tax_label`, `bic` on `set_invoicing_profile`; the English first-invoice form asks for the country first. - **Currency per invoice.** `create_invoice` takes an optional `currency`; otherwise the client's currency (new optional `clients.currency`, set in the clients dialog or by `save_client`), then the supplier's default. The invoice stores its currency; cards, the assistant's texts, the list and the PDF print it with a symbol (`1 600,00 €`, `$1,600.00`, `1,600.00 CZK`). `list_invoices` and the Documents list give totals **per currency**, never summed across currencies. - **QR payment by currency.** SPAYD only for CZK; EUR invoices with an IBAN get an **EPC (SEPA) QR code** (EPC069-12, BIC optional) read by banks across the EU; other currencies print the payment details without a QR code. DTO: `epc` next to `spayd`. - **PDF by country.** Labels follow the invoice language as before; the supplier's country adds the currency symbol and position, US date order (`10/09/2026`, English invoice only), the ID labels, the tax name ("Sales tax 8.25 %"), no "Not a VAT payer" line in the US ("Not VAT registered" in GB), date of taxable supply and variable symbol only for CZ/SK (others print a payment reference), BIC and multi-line bank details in the payment block. **A Czech invoice renders byte-for-text identically to v0.86** — `tests/faktura-pdf.js` compares against `tests/fixtures/faktura-cz-v086.json`. - **UI.** Organization settings / My account → invoice details: country select, currency, tax name, BIC, free-form bank details and a tax disclaimer ("the app does not check the tax rules of your country"). Clients dialog: invoice currency. Documents → Invoices: labels and dates by the supplier's country, unpaid totals per currency. - Docs: *Invoices to clients* → new section *Invoicing outside the Czech Republic* (preset table, tax disclaimer). - A Slovak supplier's domestic account number (`19-8742637541/1200`) yields a **Slovak IBAN** (SK31…) for the EPC QR code; free-form bank details are capped at 4 lines (address 8). `clients.currency` / `ico` are validated on the server for direct REST writes too (clients dialog). CAD/AUD print with the code (`1,600.00 CAD`) so totals per currency stay distinguishable. - Migration `1793096888_clients_mena` (`clients.currency`, text 3). Tests: `faktura-cisla` +25, `faktura-pdf` +17 (US, EUR with decoded EPC QR, GB, Czech supplier in EUR, v0.86 fixture), `ai-faktury` +2 sections (client currency → EUR invoice, explicit USD, per-currency totals, invalid currency, US member profile, routes). ## v0.92-beta — 2026-10-28 **Maintenance release for the hosted service. Nothing changes for self-hosted servers.** **Upgrade notes:** none — no migrations, no new variables for your own server. ## v0.91-beta — 2026-10-28 **Fix: pages scroll again with the mouse wheel. Since v0.90-beta, Projects, Tasks, Calendar and every other page with the top bar could not be scrolled with the wheel or the scrollbar, so items further down were out of reach.** - **Scrolling restored.** The scroll lock meant for an *open* dialog from the avatar menu (My account, Appearance, …) was applied permanently, because those dialogs are mounted even while closed. The lock now applies only while such a dialog is actually open and is released when it closes. - The pinned top bar behaves as intended: like a frozen header row — the bar stays at the top and the content below it scrolls with the wheel. **Upgrade notes:** none — no migrations, no new variables. ## v0.90-beta — 2026-10-27 **The top bar with the bell and your avatar now stays put everywhere: the assistant panel, the start-up aquarium, the left panels and the dialogs from the avatar menu all start below it, and Organization settings has the bar too.** - **Sticky top bar across the whole window.** Projects, Tasks, Calendar, Organization, Archive, Notifications and Organization settings keep the bar at the top while you scroll. It spans the full window width like the map editor's bar, so the avatar sits at the right edge on every page. - **Assistant below the bar.** The side panel, the aquarium shown next to the chat, the Documents panel, the idea buffer and the time log all begin at the bottom edge of the bar — the bell and the avatar are never covered. This replaces two earlier behaviours (the panel over the editor bar on wide screens, the aquarium over the bar). Full-screen relax with the PIN lock still covers everything, as before. - **Dialogs from the avatar menu** (My account, Appearance, Invite, Time worked, API keys, AI agents, Connect agent, Report a problem, About) open below the bar and the bar stays clickable: opening notifications keeps the dialog, clicking the dark veil closes it. Only one of these dialogs is open at a time. - **Organization settings** has the application bar (with the back arrow in it) like every other page. - **Editor banners** (“map changed elsewhere”, “project is archived”) move the bar and everything below it together. - **Phone:** the assistant panel and Documents start below the two-row bar. Lite mode is unchanged. - Hosted instances: assistant Pages are generated on the customer's own model too (no quota), when one is set. **Upgrade notes:** none — no migrations, no new variables. ## v0.89-beta — 2026-10-25 **Own model in the cloud: a hosted instance can run the assistant on the customer's own OpenAI-compatible service (home GPU behind a tunnel, own server or a provider of their choice); AI settings of the operator are now locked server-side** - **Organization settings → Own model (hosted instances only).** The administrator enters the address of an OpenAI-compatible API (usually ending in `/v1`), the model name and, if needed, a key, tests the connection and switches the assistant over. Turns on the own model **do not count towards the weekly quota** (logged with `zdroj=vlastni`, 0 credits); the hourly per-user brake stays. Photos for the assistant and voice transcription keep running on the operator's AI and keep using the quota. When the own model is down the assistant says so (`502`, `code: ai_vlastni`) and never silently falls back to the operator's AI. New routes `GET/POST /api/kb/ai-vlastni` and `POST /api/kb/ai-vlastni/test` (administrator only, `404` on self-hosted instances); `/api/kb/config` carries `chat_vlastni`. Guide: [Own model in the cloud](https://killbottleneck.com/tutorials/own-model-in-cloud). - **Private-address guard now judges IPv6 by value, not by spelling** (self-hosted AI settings too): `[::ffff:7f00:1]`, `[0:0:0:0:0:0:0:1]`, `[::1%25lo]`, NAT64 `64:ff9b::/96`, 6to4 and link-local forms are rejected like `127.0.0.1` (they used to pass). With an own model, turns carrying a photo or a voice note still use the weekly quota (vision and transcription run on the operator's AI); only the text calls to the customer's model are free. Extra fields of the own model cannot override `messages`, `tools`, `model` or `stream`. A stored key that can no longer be decrypted (changed instance key) stops the assistant with a clear message instead of calling the model without a key. - **The key never leaves the server.** It is stored encrypted (AES, `KB_ENCRYPTION_KEY` of the instance), `GET` returns only `token_set`, and it is sent only to the address it was saved for: a connection test against another origin goes without it, and changing the address to another origin without a new key deletes the old one. Private, LAN and Tailscale (`100.x`) addresses are refused on hosted instances — the model needs a public address (tunnel). - **Security (hosted instances):** `GET/POST /api/kb/ai-settings` and `POST /api/kb/ai-test` now return `403` on a hosted instance — until now only the UI hid them, so a customer administrator could redirect the operator's AI through the REST API. `POST /api/kb/ai-test` adds the stored token only when the tested address has the same origin as the saved one (before, the stored token went to whatever address the request named), and the first save of AI settings copies the token from `.env` only when the address stays at the same service. The chat re-checks a database-stored AI address against the private-network guard right before every call, like `/advisor` already did. - `llm.js` sends `Authorization` only when a token is set (an own `llama-server` without `--api-key` got an empty `Bearer ` header before). **Upgrade notes:** migrations add the `ai_vlastni` collection and `ai_chat_log.zdroj`. Hosted instances need `KB_ENCRYPTION_KEY` (exactly 32 characters; `tenant-add.sh` generates it once and keeps it) — without it the Own model section says so and stays off. Self-hosted instances are not affected: they keep the AI tile in Administration and the new section does not appear. Hosted instances that had saved `ai_settings` through the API lose that path (`403`); the assistant on hosted instances has always read `KB_CHAT_*` from the environment, so nothing changes for them. ## v0.88-beta — 2026-10-25 **Pages: the assistant (or a connected agent) writes a single-file HTML page — presentation, overview, small app — and you share it by a secret link or with the team** - **New tab Pages** in the panel next to the chat (beside Documents). Tell the assistant *"make me a presentation of the Q4 results for management"*: the assistant reads the project with its tools and hands over a brief; **the app then writes the whole HTML in a separate model call after the turn** (`create_page` only queues it — large HTML never travels through tool arguments or the chat history; the page writer runs with `KB_PAGE_NUM_PREDICT` output tokens and the result arrives through realtime, so a Cloudflare-cut response does not lose it). Rewrites via `update_page` keep the previous version (**Restore**); a failed write offers **Try again**. Rename by hand only; download as `.html`. - **Templates, so the feature is not hidden behind the right wording:** **New page** in the Pages tab (and *Page from a template* among the AI helpers and the chips under the chat) opens a form — template (presentation for management, team overview, customer summary, timeline, status report, checklist, custom brief), project, look, notes — and the app composes the message to the assistant. - **Visuals by default:** the outline assigns every section a visual (stat cards, progress bars, Gantt, timeline, donut, traffic light, table, checklist) and an icon, the skeleton ships a small CSS toolkit for them and each section draws its chart as inline SVG / CSS from the given facts — no external images; the templates ask for them explicitly (timeline = Gantt with a today marker, status report = traffic-light dots + cards). - **You can watch the page being written:** the writer builds it **section by section** (outline → look and skeleton → each section) and after every step stores the partial HTML; the panel shows the list of steps with the running one, the preview grows, and a **Code** toggle shows the HTML as it comes in. (PocketBase calls the model synchronously, so token streaming is not possible — this is the honest substitute; smaller calls also suit local models better.) A rewrite of a page built this way touches only the changed sections (keep / rewrite / new / delete), a page without section markers (uploaded by an agent) is rewritten as a whole. - **Sharing like a project:** **Share by link** turns on a secret `/p/<32-char token>` link that anyone opens without an account (**New link** invalidates the old one, **Turn the link off** stops it at once, 60 opens/min/IP); **People** lets you pick **named members of the organization** (as in the map's share dialog; `shared_with`), or the **whole team** — the other person finds the page in the **Shared with me** tab (never the token). The assistant does the same with `share_page user_on / user_off` + `email`, MCP and `/v1/pages/{id}/share` too. The link carries the address the request came in on (like a project link: LAN → LAN address, cloud → the instance domain); `KB_PUBLIC_URL` is used only when the request has no usable host (MCP over 127.0.0.1, cron). Pages are in **Download all my data** (without the token). Public pages keep working after a trial expires (like public maps); writes are 402. - **Sandbox, not sanitizing:** `/p/…` is served with `Content-Security-Policy: sandbox …` (no `allow-same-origin`), `nosniff`, `noindex`, `no-referrer`, `no-store`; the in-app preview and the `/stranka/:id` window use an `