Repository navigation
Kotiko 1.0.0
Kotiko's first public release, for the Chrome Web Store and Firefox Add-ons. Learn a word in
any language and Kotiko slips it into the pages you already read, in whatever language you
read them; point at it to see what it means and how it's said. Your words stay in your
browser, lookups use your own AI key (a free OpenRouter key works) or a model on your own
computer, and an optional server of your own keeps your words in one place.
Upgrade notes
For anyone updating from Slovo 0.2, or running their own server:
- Slovo is now Kotiko. The data folder, service name, environment variables
(KOTIKO_DATA_DIR,KOTIKO_LOG_SQL) and pairing prefix are renamed. The server moves
your words and access key across on its first start; see the README's "Updating from
Slovo". - Update the server and the extension together. The extension no longer sends your
server's access key: it signs each request (Kotiko-HMAC v2) and checks the server's
signed answers. An older server turns signed requests away, and Kotiko asks you to update
it. Tools such ascurlcan still send the key as aBearertoken. - The server's address is
http://127.0.0.1:4747. A savedhttp://localhost:…address
moves to127.0.0.1on its own. The server now listens on both127.0.0.1and::1, and
won't start if another program already holds either. - The words table is rebuilt on the server's first start. Going back to 0.2 needs the
automatic backup the server makes first. - The server refuses to start on an unsafe data folder: one owned by another account,
holding links where Kotiko's files go, or writable by others while holding other files. It
says what it found and how to fix it. LLM_URL,TRANSCRIBE_URLandPUBLIC_URLcan't hold a user name or password; put
keys inLLM_API_KEYorTRANSCRIBE_API_KEY(or their_FILEforms).- Behind a reverse proxy, set
TRUSTED_PROXY_HEADERso each client gets its own limit
on wrong keys; otherwise all proxied clients share one. /healthanswers JSON (still 200 when healthy, 503 when the database fails), every
other path needs the access key however it's spelled, andrun.shruns in production
unlessMIX_ENVis set.
Security
Before this release, six independent reviewers attacked four release candidates. Every
finding at medium or above, and almost every lower one, was fixed with a regression test;
what remains is listed as accepted risks in the report,
docs/security/review-v1.0.0.md. No published version was
affected, so there are no advisories.
-
Security: a web page could restyle a swapped word (it sits in the page) so that it
covered the page invisibly; then your pointer resting anywhere, or your click on one of
the page's own buttons, opened the word's card, and the page could search the card's
text for guesses. The card now opens only on a swapped word you can see, and from the
mouse or a tap only when you point at the word itself. Enter on a focused word still
opens it. The privacy policy (version 5) says so. -
Security: Kotiko swapped words in text a page had hidden with a see-through filter, a
clipping shape or a mask, so the page could read those swaps and learn your words. Text
like that, and text faded to under 10 % opacity, is no longer swapped. Kotiko also
leaves text under any clipping shape or mask alone, even where you can see it, since it
can't tell how much of it shows. -
Security: Kotiko no longer sends your server's access key; it signs each request. Your
server signs each answer too, and Kotiko uses an answer only when it carries that
signature, so if another program takes your server's place (say, while the server
restarts) it gets neither your key nor your words. Update the server and extension
together: an older server turns signed requests away, and Kotiko asks you to update it.
Tools such ascurlcan still send the key as before. The privacy policy (version 4)
says so. -
Security: a web page, or any program on your computer, could lock Kotiko out of your
server for a minute by sending it a burst of wrong keys or malformed checks. Requests
from your own computer are no longer locked out; requests from other computers still
are, including those a reverse proxy on your computer passes on. -
Security: the server warns when it would send an API key over plain HTTP to another
machine, but missed an address written in capitals, such asHTTP://203.0.113.7/v1. It
now warns however the address is written. An OpenRouter address in capitals is also
recognised as OpenRouter. -
Security: a program that took your server's place while it was stopped could keep a
request Kotiko sent it and play it to your server once it was back, and get your word
list, when the clock of the computer Kotiko runs on was ahead of the server's. Your
server now makes a new random id each time it starts, gives it with its proof, and
accepts only requests signed with the current one; after a restart Kotiko asks for a
new proof and sends the request again by itself. Update the server and the extension
together: each turns the other's older version away and says so. -
Security: a Kotiko server set to a model of its own (
LLM_MODEL), for example on a paid
provider, sent no limit on how long an answer may be, so a model that kept writing could
cost you up to the provider's own limit for one word. Every lookup now asks for at most
1,200 tokens of answer (4,000 for refreshing pronunciations), like the extension does,
in the field OpenAI expects whenLLM_URLis OpenAI's. -
Security: a web page could read your word list. It could hide a long list of words on
the page, where you'd never see them, and read back which ones Kotiko swapped. Kotiko
now swaps only words you can see: text a page hides isn't touched, and text below the
screen is swapped as you scroll to it, which also makes very long pages faster. One
visit to a page swaps at most 500 different words, each in at most 3 of your languages,
so no page can read your whole list at once. The privacy policy (version 3) says what
sites can and can't see. -
Security: any web page could switch Kotiko off on itself, and could tell that you use
Kotiko even on sites you paused, on sites Kotiko leaves alone such as banks, or with
Kotiko turned off. Where Kotiko doesn't swap, it now adds nothing to the page. -
Security: a web page could open a word's card with a fake click and search the card's
text. The card now opens only when you point at, click, tap or press a key on a word. -
Security: the popup's "This page is in …" line showed the page's own language label as
written, so a page could put any sentence it liked in Kotiko's popup. Kotiko now shows
only a language name your browser knows, and nothing otherwise. -
Security: before Kotiko sends your server's access key, the server must now show it holds
the same key, without either side sending it. Another program listening at the address
(for example while your server is stopped) never gets the key. Update your Kotiko server
along with the extension: an older server can't show it, and Kotiko says so in Settings. -
Security: after "Delete everything", Kotiko starts afresh and no longer takes an old-style
server address and token that a page's script left in the browser's storage meanwhile. -
Security: Kotiko's default server address is now
http://127.0.0.1:4747, and an address
you type ashttp://localhost:…is used as127.0.0.1, for your server and for Ollama
and LM Studio. Browsers trylocalhostat the IPv6 address[::1]first, where another
account on the same computer could listen and receive your server's access token. An
address saved aslocalhostmoves to127.0.0.1on update; nothing to do on your side. -
Security: a web page that managed to run code inside Kotiko's page script could change
Kotiko's settings, because browsers let that script write Kotiko's storage. It could turn
Kotiko off, pause sites, hide languages, change the words pages show, queue a word to be
looked up with your own AI key and saved, pick another model, or change the languages
your server's Telegram bot answers in. Kotiko now keeps the real copy of all of these
where page scripts can't reach, puts back anything changed elsewhere, and only its own
pages change settings. Nothing to do on your side; your settings move over on update.
In Firefox (and Chrome before 140), the languages you read no longer follow from
Kotiko in your other browsers: set them in each one.
Your own server
-
Self-hosted server: the two addresses that answer without your access key,
/healthand
/api/v1/proof, also answered when written another way, such as/health/or
//api/v1/proof. Now only the exact addresses do; any other spelling needs the key. -
Self-hosted server: a key written in the query of
LLM_URLorTRANSCRIBE_URL(some
providers take?key=...) was written to the log at start. The log now shows the address
without its query (https://host/v1?…), and the query's values are taken out of every
log line. A query such as?api-version=...now also reaches the provider on every
request, after the path. -
Self-hosted server behind a reverse proxy: everyone the proxy passes on was counted as
one, so a stranger sending ten wrong keys a minute could keep your other devices out
for as long as they kept at it. Set the newTRUSTED_PROXY_HEADERto the header your
proxy puts each visitor's address in (x-forwarded-for,x-real-ip,
cf-connecting-iporforwarded), and each visitor is counted on their own. The server
also recognises more of the headers proxies add (Via,X-Client-IP,
Fastly-Client-IPand others), so visitors through such a proxy are no longer taken for
your own computer, which is never limited. -
Self-hosted server: after moving your words from the folder used before the rename to
Kotiko, the old copy (~/.local/share/slovo/slovo.db) and the old access key could stay
readable by other accounts. The server now makes them private after the move, and at
every start for installs moved earlier.install-service.sh --uninstall --delete-data
now deletes that old copy too. -
Self-hosted server: someone who could write in your data folder could choose your access
key (by leaving anapi-tokenthere) or send your words to a file of theirs (by putting
a link wherekotiko.dbgoes). The server now refuses to start, saying what it found
and how to fix it, when the data folder or one of Kotiko's files belongs to another
account, when one of Kotiko's files is a link, or when others can write in a data folder
that also holds other files. A folder others can write in that holds only Kotiko's files
is made private first, with a warning. To keep your words on another disk, point
KOTIKO_DATA_DIRthere instead of linkingkotiko.db. -
Self-hosted server: with
LOG_LEVEL=debug, words the model suggested and the server
refused were written to the log even withLOG_LOOKUPS=false. Now only the reasons are,
unless you turnLOG_LOOKUPSon. -
Self-hosted server: when your model or transcription key would go over plain
http://
to another machine (not this computer, not Tailscale), the server now warns at start,
because anyone on the network in between could read it. -
Self-hosted server: a user name and password written inside
LLM_URLor
TRANSCRIBE_URL(https://user:password@host/...) went into the log at start. The
server now refuses to start with such an address, without showing it; put the key in
LLM_API_KEYorTRANSCRIBE_API_KEYinstead. The same goes forPUBLIC_URL. -
Self-hosted server: an access key you chose yourself that looks easy to guess (few
different characters, a repeated piece, mostly one character) now gets a warning at
every start. DeleteAPI_TOKENto let the server make a random one. -
Self-hosted server: after 10 wrong access keys from one address within a minute, the
server refuses that address for the rest of the minute (429) and logs it once. Other
addresses, including yours, keep working. New routePOST /api/v1/proof: Kotiko can
check that an address really is your server, which proves it holds your access key
without sending it, before Kotiko sends the key there. -
Self-hosted server: another account on the same computer could receive your access key
by listening on[::1]:4747, the address browsers try first forlocalhost. The server
now listens on both127.0.0.1and::1by default (and withBIND=localhost), so
nobody else can take either; it refuses to start, saying which address, if another
program already holds one. In Kotiko, enterhttp://127.0.0.1:4747as your server's
address. -
Self-hosted server: requests for many different host names can no longer fill the log.
The server logs each refused name at most once an hour, and at most 1,000 names an hour;
past that it logs one line saying so, and how many it left out. -
Self-hosted server: other accounts on the same computer can no longer read your words.
The database (with the-waland-shmfiles next to it), the model list cache and a
new data folder are now readable only by you, like the API token and the backups
already were. At start, the server makes these files private again if an older version
left them readable, and logs what it changed. It leaves a data folder that also holds
other files as it is and warns with the command to fix it.run.shand the service
frominstall-service.shnow start the server with umask077; run
install-service.shagain to update the service. -
Self-hosted server: your keys can now live in their own files instead of
.env. Set
LLM_API_KEY_FILE=/path/to/filein place ofLLM_API_KEY=..., and the same for
API_TOKEN,TELEGRAM_BOT_TOKENandTRANSCRIBE_API_KEY; this is how Docker secrets
and systemd credentials hand keys to a program. To replace a key, replace its file and
restart. The server stops with a clear message if both forms are set or the file is
missing, empty or unreadable, and never prints what's in it. Existing.envfiles work
as before. -
The server follows the XDG Base Directory rules: with
XDG_DATA_HOMEset, new installs
keep their words in$XDG_DATA_HOME/kotiko, andinstall-service.shputs the service in
$XDG_CONFIG_HOME/systemd/user. Nothing moves for existing installs: words already in
~/.local/share/kotikoand a service already in~/.config/systemd/userstay where
they are. -
server/install-service.sh --uninstallremoves the service it installed (stops it,
turns off its start at boot, deleteskotiko.service) and says what it did. Your words
and.envare kept unless you add--delete-data, which deletes only the files Kotiko
made in its data folder. -
The Telegram bot no longer assumes you read English. It gives meanings in the languages
you read: the extension now tells your connected server which ones (and Kotiko's
interface language, if you chose one), and until it does, the bot uses your Telegram
app's language and says so on the first card./bases es ensets them from Telegram.
A word you read in two languages gets one card with both meanings, and Add saves both.
Cards show the pronunciation written for your language, the slow form and where the
pronunciation came from, like the word card on pages. Everything the bot says now comes
from the server's message file, ready for translations;/languagepicks the bot's
language, and it tells you once when yours isn't translated yet. When something breaks,
the chat gets a short reference to look up in the server log instead of the error's
text, and buttons carry the word's public id instead of an internal number. New routes:
GETandPUT /api/v1/profile. -
Turning off Wiktionary pronunciations with
KOTIKO_WIKTIONARY=falseno longer makes the
server warn at start that the setting is unknown and ignored. It always worked; only
the warning was wrong.
Changes
-
Slovo is now Kotiko. Your words move automatically from
~/.local/share/slovoto
~/.local/share/kotikoon first start; the old file is kept as a backup. The service is
now calledkotiko(install-service.shreplaces the old one). RenameSLOVO_DATA_DIR
toKOTIKO_DATA_DIRin.envif you set it (andSLOVO_LOG_SQLtoKOTIKO_LOG_SQL);
the old names still work for now, with a warning. Stop the old server before the first
start; keep the extension's folder where it is and reload it. See "Updating from Slovo"
in the README. -
Fixing a word's language no longer makes the word disappear when the model answers with the
same word in the same language ("Already in your list"): the word stays in your list. Before,
it went to Recently deleted. -
Privacy policy version 6: "What websites can see" now names every way a page can hide
text that Kotiko still swaps (a transparent text colour, text turned away), and says a
disguised word's card can open on a click as well as a resting pointer. -
Release process: the check that holds store uploads until the security review is closed
could miss a "Gate: open" line written with a lookalike letter from another alphabet, an
invisible character or a fullwidth colon, and read the review as closed. It now reads
each line as a person would, keeps the gate open for any such line, and says which line. -
Release process: the check that holds store uploads until the security review is closed
still missed "Gate: open" written as a list item, a table row, code, HTML, an entity or
math, a fake "Appendix" heading inside a comment or code block, a closing line hidden in
a comment, and "the review is still open" written as prose. It now reads all of these as
open, counts the closing line and the heading only where a reader sees them, and says
which line keeps the gate open. -
Settings and the welcome page now warn when the address of your lookup service starts
withhttp://and isn't on your computer: your key and the words you look up would
travel unencrypted. The server address already warned. -
Connect OpenRouter: a web page you have open can no longer spoil a sign-in in progress.
Before, any page could open Kotiko's return page with a made-up code while you were
signing in, and your real sign-in then said "This sign-in has expired". Kotiko now
takes a code only from the sign-in it started, and a code that doesn't work no longer
ends the sign-in. -
Connect OpenRouter with one click, without copying a key: "Connect OpenRouter (free)" is
now the first choice on the welcome page, and Settings, Word lookups has a "Connect
OpenRouter" button. OpenRouter's sign-in opens in a new tab; when you're done, it sends
you back to kotiko.org/connect/, Kotiko gets your key, and the page you started from
says you're connected. Pasting a key still works ("Paste a key instead"). -
New documentation site, ready to publish at kotiko.org: install guides for Chrome, Edge,
Brave and Firefox (with "coming soon" in place of the store links until the listings are
live), how to start with or without an AI key, one guide per word lookup service, help
for every message Kotiko can show, the guide to running your own server, and the privacy
policy, published from the same file the extension shows so the two can't differ. The
site sets no cookies, has no analytics and loads nothing from any other site. It also
has the page OpenRouter returns to after "Connect OpenRouter"; Kotiko reads the sign-in
code there and nowhere else.
The README is now a short introduction that points to the site. -
Kotiko's pages and word card work with keyboards and screen readers, and respect reduced
motion. Every control in the popup, the dashboard, the welcome tab and the word card can
be reached with Tab and shows a focus ring that nothing covers; dialogs keep Tab inside
and Esc closes them, putting you back where you were; buttons and links are at least
24 × 24 pixels; and with "reduce motion" on (your system's setting or Kotiko's own) nothing
slides or grows, the word card included. New in Settings, "Reading": choose what screen
readers hear on a swapped word (the word you're learning, the original word, or both),
and "Let me Tab to swapped words". Turning on swaps in buttons and menus now warns that
voice control commands may stop working. Fixed on the way: the restore preview showed
the word "null", the "Kotiko is off" state made text too faint to read, and long language
names on the dashboard's shelf were cut off.docs/accessibility.mdlists the automated
checks and the screen-reader pass done before each release. -
Clearer messages that tell you what still works and what to do next. When your server is
down or you're offline, Kotiko says your words still work on pages. When word lookup is
busy, out of free lookups or not set up, it says you can add the word yourself. A text too
long for one word points you to bulk add. The popup, the dashboard and the welcome page
now word every problem the same way, and "Details" has a "Copy details" button for bug
reports (it never copies your words). A lookup service that wants payment no longer
retries for days; it says so once. On the server, error messages come from one file
(server/priv/locales/en/messages.json), ready for translations, and a request body sent
as something other than JSON gets a clear415instead of500. -
Kotiko has a privacy policy: Settings → About → Privacy policy, or the link at the bottom
of the welcome page. It says, in plain words, what stays in your browser (the pages you
read, always), what leaves it and to whom (the text you type to add a word, to the AI
service you chose; the word alone to Wiktionary for its pronunciation), and how to
delete it. A list of everything Kotiko keeps and sends, with the code that does it, is in
docs/privacy/inventory.md. -
Kotiko now sends your words and keys only to the server and AI service you chose in its
own settings. If a website manages to change those settings behind your back, Kotiko
sends nothing there and puts your choice back. -
Typing a server address that starts with
http://and isn't on this computer or a
Tailscale address now shows a note under the field: your access key would travel
unencrypted, so usehttps://if others share the network. It still connects. -
Firefox: Kotiko's pages now say they don't want Firefox's default switch from
http://
tohttps://, which could stop a Kotiko server at anhttp://address on your home
network or Tailscale from connecting. Kotiko now needs Firefox 140 or later, and tells
Firefox it collects no data. The full name in the extensions list is
"Kotiko: learn languages while you browse". -
Back up your words to a file and restore them, export to a spreadsheet or Anki, and
delete everything with one button. In Settings, "Your data": a backup file holds every
word (and your settings, never your keys); restoring one shows what it will add or merge
first, never duplicates a word or undoes an edit you made since, and can be undone for a
day. The spreadsheet opens with every script intact, and Kotiko's bulk add reads it
back. The Anki file gives each language its own deck, and importing a newer one updates
the same cards. "Delete everything…" clears all Kotiko keeps in this browser (open pages
get their own words back), and can empty your Kotiko server too. On the server:
mix kotiko.export,mix kotiko.importandmix kotiko.reset. When your words are only
in this browser, the popup reminds you to back up once a month. -
Kotiko is ready to ship to the Chrome Web Store and Firefox Add-ons. Each release on
GitHub carries both store packages, a checksum list, a list of the server's dependencies,
and a signed record of how each file was built. Anyone can rebuild the packages from the
release's tag and get the same bytes; docs/verify.md shows how to check a download. -
Add a whole list at once: paste it into "Add words" in your word list, or drop a .txt,
.csv, .tsv, .json or Anki text export on it. Lines that already have a meaning are saved
with no lookup ("gato = cat", "dog - perro", "고양이:猫"), and Kotiko works out which side
is the word you're learning from the languages you read, not by assuming English. Check
every row first; words you have are marked, words without a meaning can be looked up,
and one Undo takes the whole list back. Pasting a list into the popup opens it there. -
Got the wrong language? Click the language on the result and pick another: Kotiko adds
it again in that language and removes the first. Lookups not working? "Add it yourself"
saves a word from a small form, with a meaning in each language you read. A language
chip beside the add box says which language the next word is in, "For pages in" picks
which of your languages it gets meanings for, a half-typed word survives closing the
popup, and a saved word has a speak button when your device has a voice for it. -
Adding words is safer. With a Kotiko server, adds now finish even if you close the
popup, and a retry never saves twice. Re-adding a word you have says "Already in your
list" with Open; adding forms says "Updated … new forms: …", and its Undo puts the word
back as it was. Undo now works for words added with your own AI key on a server, where
it always failed. A pasted sentence asks which words to keep before saving. Offline, an
add waits and finishes when you're back online. -
Each word on a page now shows one of your languages, the same everywhere on that page,
and stays put while you read; another page or another day may pick a different one.
New words show up first for a week. Focus on a language from the popup (a language you
add meanwhile waits until you stop), give one language more or less weight, put them in
an order, or turn on "Mix within the page" for the old rotation (Settings, Your
languages on a page). A word that is the page's own ("no" for "no") is no longer
swapped. -
Swapped words now follow each language's own capitals: no more "Lunes" for Monday
mid-sentence, and English "Monday" and "I" keep their capitals on a Spanish page. Turkish
gets its dotted İ, Greek capitals drop their accents, and Georgian always stays in its
everyday letters. Swapped Chinese, Japanese, Hindi, Thai or Burmese words no longer
change a paragraph's line spacing. -
Kotiko leaves alone what isn't an ordinary word: acronyms (the IT team), names (Will
Smith, Sr. Rosa), dates and codes (May 2026, Plan B), code editors, and login and payment
forms. Banks, payment, health, government and tax sites, and email you're writing, are
left alone by default (Settings, Pages), with "Swap words here anyway" in the popup. A
word's card has "Don't swap this word". An English quote on a page in another language
is now swapped. -
Kotiko no longer interferes with web apps built with React, Vue and the like: it changes
words without taking the page's own text away from it. It keeps working after a site
navigates without reloading, swaps new posts in a feed before you see them, stops if a
page keeps undoing its changes (the popup says so), no longer freezes very large pages,
and starts on tabs that were already open when you install or update it. Adding a word
only changes the text that has it. -
Pronunciations are now right. For languages with word stress, Kotiko takes how a word is
said from Wiktionary instead of trusting the AI, which got Russian stress wrong on about a
quarter of everyday words (это is EH-ta, not eh-TO). Words you already saved are checked
once in the background, and the word card says "Checked in Wiktionary". Only the word is
sent to Wiktionary, never the page you're reading. A pronunciation you typed yourself is
never changed. -
You can now add little words like "it", "the" or "el" by typing the word in the language
you're learning: "оно" or "это" for "it", "der" for "el". Before, Kotiko threw the answer
away unless you also typed the English (or Spanish) word. -
Kotiko now reads pages in the languages you read, whatever they are, including
languages written without spaces like Japanese, Chinese and Thai. It swaps words only
on pages (and parts of pages) in one of your languages and leaves the rest alone; the
popup says when a page is in a language you don't read. Contractions like "can't",
hyphenated words, accented words, web addresses and French "l'eau" stay intact, phrases
like "thank you" and "por favor" swap as one, and large word lists no longer slow pages
down (a 10,000-word list on a long article went from about 7 seconds to under a tenth
of one). -
Choose the languages you read in from the dashboard: Settings, Languages you read in.
Add any language your browser can read, drag them into order (the first is your main
one), or remove one; its meanings are kept and simply stop swapping. Each language says
how well Kotiko knows it, and how many of your words have no meaning in it yet, with a
link to type them. Updating from an older version keeps your words swapping: Kotiko adds
the languages your words already use to the ones your browser lists. -
Kotiko now works without a server. Your words live in your browser and Kotiko looks new
words up with your own free OpenRouter key, or any provider you like (OpenAI, Anthropic,
Google Gemini, Groq, Ollama, LM Studio or another address), set up in the dashboard's
Settings, Word lookups. Or type a word with its meaning, like "gato = cat", with no AI
at all. Adds never wait: they finish with the popup closed, and wait for busy or used-up
free lookups instead of failing. Your key and your server's access key are kept where web
pages can't read them. Already running a Kotiko server? Nothing changes; you can keep
using it, or move your words into the browser from Settings, Your Kotiko server. Back up
regularly: uninstalling an extension deletes its data. -
Adding a word now answers within 25 seconds, uses at most 3 requests, and remembers
words it already looked up for 30 days. Kotiko follows OpenRouter's current free models
automatically (best first, from our evaluation) and shows how many free lookups you have
left today. When lookups stop working, the popup says why in plain words (busy, used up
until a given time, or a key problem) instead of "the language model failed". The words
you look up no longer appear in the server's log. The server adds a table for the lookup
cache, so it backs up your database once more on first start. -
Buttons, toggles, menus, tabs and forms keep the site's own words, including clickable
labels built from plaindivs. A lone capital next to a code or numeral ("AOI I",
"World War I", "I-95") is no longer swapped as the word "I". -
Language names now come from the language code, so Cantonese is always Cantonese. Words
saved under old codes such as cmn or iw were merged into their languages; the server
backs up your database before it does this. -
Kotiko now checks every word the model suggests: it won't swap unrelated words like
"what" for как, never adds more than 5 words at once, and explains anything it rejects.
Ask in your own language, and meanings come back in the languages you read. Every new
word comes with how to say it, written for readers of your language, with the stressed
syllable in capitals.
⚠ BREAKING CHANGES
- server: the words table is rebuilt; downgrading needs the automatic backup.
- the data folder, service name, environment variables (KOTIKO_DATA_DIR, KOTIKO_LOG_SQL) and pairing prefix are renamed; see the README's 'Updating from Slovo'.
- server: /health returns JSON instead of "ok" (still 200 when healthy, 503 when the database fails), and run.sh runs in prod unless MIX_ENV is set.
- server: deny by default so encoded paths can't skip the token
Features
- a sturdier AI client that protects your free quota (b5a2d56)
- a sturdier AI client that protects your free quota (slice 10) (a23108c)
- accessibility baseline, audited with axe and kept by CI (slice 27) (4acaade)
- accessibility baseline, audited with axe and kept by CI (slice 27) (59af37e)
- brand: Mira logo, a black kitten head on purple (5d4e8d6)
- brand: new kitten logo and placeholder illustrations (b379c90)
- brand: use the maintainer's kitten logo everywhere (4325812)
- canonical language tags, shared word spec and the real prompt (35d0685)
- extension: back up, restore, export and delete everything (slice 12) (b25a852)
- extension: back up, restore, export and delete everything (slice 12) (c5144de)
- extension: bulk add, paste a list or drop a file (slice 13) (e404a18)
- extension: choose the languages you read in (slice 50) (d7314d9)
- extension: choose the languages you read in (slice 50) (4f1361c)
- extension: Connect OpenRouter, store screenshots over a real page, privacy policy v2 (ffc185a)
- extension: design system and redesigned popup (0393dec)
- extension: design system and redesigned popup (slices 06 and 20) (7570d7e)
- extension: every add is a safe background job; results, Undo, choosing (slice 24 §§1-5) (d38c2fd)
- extension: language controls for adds; popup loads only its first view (slice 24 §§6-9) (4e7517a)
- extension: leave names, acronyms and code alone (slice 16 §§1-3) (6d562c7)
- extension: local-first mode, no server needed (59ac833)
- extension: local-first mode, no server needed (slice 11) (6639c87)
- extension: one language per word per page; Focus, weights, order, mix (slice 18) (92e7a07)
- extension: privacy policy, store forms and secrets bound to their address (slice 28) (3f7a426)
- extension: privacy policy, store forms, and requests only to addresses you chose (slice 28) (79d0809)
- extension: read the OpenRouter sign-in code on kotiko.org/connect/ (ebab977)
- extension: sensitive sites, button and menu setting, never-swap list (slice 16 §§4-5) (04ea3da)
- extension: show Connect OpenRouter on the welcome tab and in settings (3f56738)
- extension: swap words without taking the page's text away (slice 15) (0e984b2)
- extension: swap words without taking the page's text away (slice 15) (3a7800c)
- extension: swapped words in their own language's capitals (slice 17) (61fdc53)
- extension: the dashboard, a live view of every word (87e3c79)
- extension: the dashboard, a live view of every word (slice 21) (b867a92)
- extension: the server proves it holds the token before it gets it (cc15012)
- extension: the welcome flow, your first word celebrated (slice 22) (72547e2)
- extension: the welcome flow, your first word, celebrated (8d871a2)
- extension: warn when a lookup service's address sends the key in clear (eb72445)
- extension: word popover with pronunciation and audio (8263611)
- extension: word popover with pronunciation and audio (slices 19, 34) (ab5b4d0)
- extension: words found in any language you read (slices 14, 16, 50) (4c8a46b)
- extension: words found in any language you read (slices 14, 16, 50) (429579d)
- language tags, shared word spec and the real prompt (slices 08, 09) (605162f)
- plain-language errors from one catalog (slice 25) (d60ac55)
- plain-language errors from one catalog (slice 25) (85940b5)
- pronunciations from Wiktionary, written by rule (slice 49 §4b) (19551c5)
- pronunciations from Wiktionary, written by rule (slice 49 §4b) (c6829bc)
- server: harden API auth and add the server test suite (07d5ec9)
- server: secrets from files, XDG folders and an uninstall (83ce39a)
- server: secrets from files, XDG folders and an uninstall (4afdf6b)
- server: the Telegram bot speaks the learner's language (622d156)
- server: the Telegram bot speaks the learner's language (slice 41 §9) (5e7832a)
- server: validated config, safe boot, redacted logs and JSON health (265ebb6)
- server: word model v2 with stable ids, safe merges and undo (102ca7e)
- site: the docs site for kotiko.org and a public-ready README (slice 44) (320c948)
- site: the docs site for kotiko.org, and a README that points to it (97e2943)
- site: the OpenSSF Best Practices badge in the footer (5d3bbcd)
- site: the OpenSSF Best Practices badge in the footer (3f04e66)
Bug fixes
- auth: sign the server's boot id, so a restart can't let a request replay (f5e08bc)
- boot-bound request signatures, per-client limits behind a proxy, and a stricter gate (review E-01, E-02, E-03, E-06, E-07) (1eb692a)
- content: don't swap text a filter, clip-path or mask hides (a660336)
- content: open the word card only on a word the learner sees (fbd2704)
- content: the word card opens only for the learner; filter, clip-path and mask count as hidden (review E-04, E-05) (2304bd6)
- extension: a browser update after an update from 0.2 no longer drops the learner's words (608a549)
- extension: a content type that isn't a string, and a test that depended on the date (8a08928)
- extension: a page can't spoil a Connect OpenRouter sign-in (aead17d)
- extension: a store born from Delete everything takes nothing planted (645df43)
- extension: an add refused for a rewritten address retries by itself (b1c3470)
- extension: benchmark uses casing.js; make casing about 10 times faster (287f70e)
- extension: bind where requests go, not only the secrets (slice 28) (b855ac3)
- extension: content scripts can't change settings or queue adds (SCR-448) (471ee18)
- extension: content scripts can't change settings or queue adds (SCR-448) (a164d23)
- extension: engine slices stay bounded on big pages; steadier tests (2bde8c8)
- extension: every lookup asks for at most the spec's number of tokens (f026ee3)
- extension: fixing a word's language no longer deletes it when the answer is the same word (54b280c)
- extension: localhost addresses are used as 127.0.0.1 (5fbaa00)
- extension: never send the server's token; sign each request (D-01) (f2e87d8)
- extension: never show a page's own lang text in the popup (2eceb7c)
- extension: never swap inside controls or numeral-like capitals (3b3fee4)
- extension: never swap inside controls or numeral-like capitals (c40fc88)
- extension: only Kotiko's pages may skip the wait between syncs (e5aa393)
- extension: open the languages you read in at #settings/languages (95d1a6d)
- extension: open the word card only for the learner's own input (1fffd51)
- extension: security review fixes for the background and settings (cf42809)
- extension: security review fixes for what web pages can see (7045c01)
- extension: show pages nothing of Kotiko where it doesn't swap (4b17f9f)
- extension: swap only text the learner sees, at most 500 words a page (a29ef3e)
- extension: sync that never shows stale results or errors (5293305)
- extension: the browser's languages stay on the device (abcd363)
- extension: the content scripts' live-copy name passes the build's test-hook guard (5f4a05c)
- extension: the pages' base languages never go back to an older list (7ca742b)
- fixing a word's language no longer deletes it; docs say typed words reach Wiktionary (launch checks F1, F5) (719f26c)
- keep a function-word meaning when the learner typed the word (6aae1f0)
- keep a function-word meaning when the learner typed the word (ea7603f)
- merge: the same gloss on a re-add is not a new form (519b8f6)
- output cap on the server, HTTP warning case, and a stricter store gate (review D-03, D-04, D-05) (3f966df)
- release: open the store gate only on the lead's exact closing line (582fcd5)
- release: read the security gate's lines as a reader sees them (a9e5db9)
- release: refuse a signed tag pushed under another tag's name (0876dbe)
- release: security review fixes for the release pipeline and CI (cf02268)
- release: send Firefox Add-ons the release's own zip (1f064cd)
- release: store screenshots show the popup over a real article (6fb7cf9)
- release: the gate check strips tags until none is left (d7896c3)
- release: the security gate reads lines as a reader sees them (34177fc)
- release: the store zips leave out the tests' hook (c0c4c02)
- scripts: read the live entry from a fixed id, not one taken from the document (1d21b42)
- scripts: the badge page lists only what differs from the live entry (6a708c9)
- server: a repeated query parameter is a 400, not a 500 (eba1343)
- server: count clients behind a proxy apart, and know more proxy headers (74d4fbf)
- server: deny by default so encoded paths can't skip the token (2693809)
- server: every lookup request carries the output cap (11139a8)
- server: fall back across free models and stop losing words (eec998d)
- server: keep a key in LLM_URL's query out of the log (c67ea02)
- server: keep refused words out of the debug log without LOG_LOOKUPS (c6a1317)
- server: keep the word list private from other accounts (137cc8f)
- server: keep the word list private from other accounts (6688f25)
- server: limit what a reverse proxy on this computer forwards (D-02) (3300a87)
- server: listen on both loopback addresses by default (269a648)
- server: make the old copy of the words private, and delete it with the data (713cb45)
- server: no local program or web page can lock the extension out (D-02) (0048fe8)
- server: open routes answer only their exact spelling (7d175d3)
- server: read the lookup cache without executable terms (0288dd8)
- server: refuse a data folder someone else could have planted files in (b1b75a1)
- server: refuse a password inside LLM_URL, TRANSCRIBE_URL or PUBLIC_URL (5e49cfe)
- server: say when LLM_API_KEY is missing or needs a restart (161bcaf)
- server: security review fixes for the self-hosted server (5308e05)
- server: skip script names the bundled PCRE doesn't know (d94561e)
- server: stop calling KOTIKO_WIKTIONARY an unknown setting (c772a01)
- server: stop refused host names flooding the log (e223a21)
- server: throttle wrong tokens and add a token proof route (eda0e3e)
- server: warn about an API token that is easy to guess (a2fb973)
- server: warn about plain HTTP however the address is written (2e8bc14)
- server: warn when a provider key goes over plain HTTP (0a1806e)
- server: words with Å or Ņ no longer break a lookup (0f4bb9c)
- show words already in the list instead of 'couldn't find a word' (5fced3a)
- sign every request instead of sending the server token; no local lockout (review D-01, D-02) (a981bc2)
- site: build once for the site's tests instead of once per file (f6b9a20)
- site: build once for the site's tests instead of once per file (273e45b)
- store: declare the learner's own key as authentication info on Firefox (573d33a)
- tests never reach Wiktionary; KOTIKO_WIKTIONARY turns it off (19bd8e1)
Refactoring
- rename Slovo to Kotiko (b762d9e)
Install or update
- Extension: Chrome Web Store (link added after the first review) or Firefox Add-ons (link added after the first review). Stores update installed copies on their own.
- Server (optional): in your Kotiko folder run
git pull, then restart it (./run.sh, orsystemctl --user restart kotiko). See Updating. - Extension 1.0 works with server 1.0.
Verify the files
The zips are the store packages, built from this tag; anyone can rebuild them byte for byte
(how). They contain only files from extension/ and the
license texts, no third-party code. The server's dependencies are listed in the attached CycloneDX
SBOM. Full steps, including checking the tag's signature: docs/verify.md.
sha256sum -c SHA256SUMS
gh attestation verify kotiko-chrome-1.0.0.zip --repo ScriptKittyOS/kotiko
gh attestation verify kotiko-firefox-1.0.0.zip --repo ScriptKittyOS/kotiko