The Dict app is a dictionary from English, German, French, Spanish, Italian or Latin into Chinese, Japanese or Korean. A word it has not met is written by a model while you watch, and then kept, so the next time its entry opens at once.
For you:
For a script or a coding agent that talks to exe over HTTP:
A lookup is one line of up to 80 characters. Capitals and extra spaces do not matter: Serendipity and serendipity are one entry.
Under the headword an entry gives, from the top:
| Part | What it gives |
|---|---|
| Pronunciation | The word in IPA: British and American for English, Classical and Ecclesiastical for Latin. |
| Forms | The forms a learner needs, such as a German noun's article and plural, or a Latin verb's principal parts. |
| Definition | The word's senses, each with short equivalents and a definition. |
| Examples | Two sentences with their translations, one simple and one more complex. |
| Etymology | Where the word comes from. |
| Related Words | Words related to it, each of which looks itself up when clicked. |
| In Literature | How the word lives in literature, with a quotation or two when the model knows one word for word. |
A Chinese entry is written in Simplified Chinese.
Look up an inflected form (ran, ging, amāvī) and you get the headword's entry, with a line saying which form you typed.
An obvious typo (recieve, Weltschmertz) is read as the word it stands for:
A lookup that is not a word of the source language comes back with suggestions, and nothing is kept. A suggestion looks itself up when clicked.
A word the dictionary does not have yet takes a few seconds for its spelling and a minute or two for the entry. You can watch the work:
The session belongs to the daemon, not the window. Close the window and the entry is still written and kept. A window that asks for the word meanwhile — this one reopened, or another desk — joins the session, every step so far included, rather than starting another.
Entries are kept in the node's own dictionary, ~/.exe/dict.db (SQLite), so a word looked up before opens at once.
A word the dictionary does not have yet is written by Codex: an ephemeral session of the Codex CLI on this host, on gpt-6-astra at extra-high reasoning.
codex app-server that is never saved and never joins Codex's resume list.A host without the Codex CLI still opens every entry it keeps, and answers the rest with No Usable LLM Backend. Codex must be signed in (codex login) for the rest to be written.
A lookup that goes wrong says why on its page, with Try Again under the reason. A session that has not answered after ten minutes is stopped.
Each lookup has a tab, along the top of the page. Where a lookup opens depends on whether its entry has to be written:
A tab you are not looking at can wear a mark:
| Mark on a tab | What it means |
|---|---|
| A pulsing green dot | The tab is writing out of sight. |
| A black dot | The tab finished while you looked elsewhere. The dot stays until you open it. |
There is no limit on how many words are written at once. Each gets its session straight away, and the window follows all of them over a single connection, so a pile of writing tabs never ties up the browser. Tabs narrow as they crowd, and past that the bar scrolls sideways (a wheel, a trackpad or a finger).
The × on a tab closes it; a lone tab has none. Closing a tab that is writing does not stop its session: the entry is still written and kept.
The window remembers its tabs, the open one and its languages in this browser, and a reload brings back up to twelve tabs. A tab that was writing rejoins its session, and one whose session was lost to a daemon restart starts it again.
Change a pop-up and Dict shows the word's entry for the new pair, if the dictionary has one. If it has none, the page says so and waits for Look Up, so a stray change of pop-up never starts a session.
Changing the languages while the open tab writes opens the word in the new pair in a new tab.
Dict also works on its own, outside the desktop:
/apps/dict/ on your exe's address. Over Tailscale Serve that is https://<host>.ts.net/apps/dict/.From the home screen it opens full screen with its own icon, the red dictionary on the desk's lavender. There it:
In a narrow window, a phone's among them, the two languages take a row of their own above the word.
Two calls answer a lookup. Both name the languages by their codes and the word as q: from is en, de, fr, es, it or la, and to is zh, ja or ko. Like every /v1/ call they take the daemon's API token where one is set.
Read a kept entry. GET /v1/dict?from=en&to=zh&q=word answers a kept entry, and 404 when there is none. For a known typo it answers its word's entry, with corrected saying what was typed; exact=1 reads the text as typed.
$ curl -s '127.0.0.1:7777/v1/dict?from=en&to=zh&q=serendipity'
The answer is one JSON object: from, to, q, the entry itself, the model and effort that wrote it, and created, the time it was written.
Look up, and write if need be. POST /v1/dict with {"from", "to", "q"} answers newline-delimited JSON. A kept entry comes at once, as one {"entry": …} line. Otherwise a session writes it, and these lines follow it as it works:
| Line | What it says |
|---|---|
{"writing": true, …} | The first line: a session is writing. Its q is the word the session writes. |
{"step": {"t", "kind", …}} | A step of the session. |
{"text": …} | The entry's JSON, a fragment at a time. |
{"wait": seconds} | Sent every five seconds besides, for as long as the session works. |
{"entry": …} | The last line: the entry, with the session's tokens, and corrected for a typo. |
{"notfound": true, "suggestions": […]} | The last line when the lookup is no word. |
{"error": …} | The last line when the session failed. |
A step's kind is session, spell, spelled (with its verdict on the typed text), think, summary, thought, answer, usage or retry.
"exact": true in the body skips the spelling check. A word that has to be written on a host without the Codex CLI answers 503.
A lookup that joins a session reads every line from the start, and joins only a session it can trust. A plain lookup never shares an exact one's, whose text may be a typo, and an exact lookup never shares a spelling check still out. Each starts its own.
The window itself does not hold a request per lookup. It uses two more calls:
POST /v1/dict/start, with the same body, answers at once: {"entry": …}, or the session as {"flight": id, "key", "q", "model", "effort", "wait"}.GET /v1/dict/stream is one connection to every session the daemon is running, and to those finished in the last two minutes.On the stream each session is announced as {"flight": id, "began": {…}}. Its lines follow as POST /v1/dict sends them, with "flight" added, and then its last line. {"ready": true} ends the first round, and {"ping": …} keeps a quiet stream open.