Skip to content

Privacy

Alcuin is a desktop client. It talks to the Postgres servers you connect to and to nothing else, with exceptions you control.

What leaves your machine

  • Queries and data go only to the database you point Alcuin at, over the connection you configured (direct, TLS, or an SSH tunnel).
  • Update checks ask GitHub Releases for the latest version when the app starts and every six hours while it runs, if you are on a packaged build. The request carries the app version and platform, nothing else. Every release so far is a pre-release, so turning Receive pre-release builds off in Settings stops update checks finding anything until the first full release.
  • The assistant, only if you turn it on in Settings. It is off until you do; see If you turn on the assistant.
  • What an AI agent reads, only if you make an agent token in Settings, under Agents. Alcuin sends nothing to the agent’s model itself, but what the agent reads through Alcuin goes wherever that agent sends it; see If you connect an AI agent.

With the assistant off and no agent token, that is the whole list. Alcuin has no telemetry, no analytics, and no crash upload.

If you turn on the assistant

The assistant is off by default. Turning it on in Settings, under AI Assistant, sends requests to the one address you enter there: Anthropic’s API (https://api.anthropic.com unless you change it), or an OpenAI-compatible server you name, such as Ollama or LM Studio on your own machine. Alcuin calls no other address for it. It does not follow redirects, so a request can’t be passed on somewhere else. Before anything is sent to an address, Alcuin asks you to allow it in a dialog of its own. It asks again whenever the address changes, and an address you don’t allow gets nothing until Alcuin restarts.

When. A request is sent only when you ask for one:

  • you send a message, or press Explain this plan or Fix this error, in the assistant pane;
  • you send a description in the pane’s Describe mode (Describe it on an empty database’s Start page);
  • you press Test connection in Settings;
  • you have also turned on “Name history and saved queries”, and you open the History panel, or the Save query dialog opens.

What. Each message sends:

  • the SQL in the editor, and the part you selected;
  • for each table that SQL names, its columns, keys, constraints, indexes and column comments;
  • the database server’s version;
  • for Explain this plan, the plan, including its timings and row counts;
  • for Fix this error, the server’s error message, hint and position;
  • for Describe it, instead of the editor’s SQL: every table of the schema it builds in, with its columns, keys, constraints, indexes and whether row security is on; the schema’s types; and the extensions installed or available;
  • your message, and the earlier messages in the same conversation.

The whole context is held to about 8,000 tokens.

Rows. No row data is sent, with one exception. Each message has an “Include 20 result rows” switch, off by default. With it on, the first 20 rows of the current result go with that one message.

Naming history. Naming history sends the SQL of the unnamed entries you can see, in one request. It sends the text as history keeps it, with role passwords already replaced by [redacted]. The Save query dialog sends the query you are saving, redacted the same way.

The key. Your API key is kept with your passwords, encrypted by the operating system’s key store. It is bound to the address you saved it for. If the address changes, the key is not sent until you enter it again for the new one.

Seeing it. Settings, under AI Assistant, shows exactly what the last request sent: its address, its headers (the key’s value is never shown) and its body. What happens to a request after it arrives is up to the provider you chose and its own policy.

Running SQL. SQL the assistant proposes is inserted into your editor and never runs by itself. A schema it proposes with Describe it changes nothing until you press Apply or Stage; to check a proposal before showing it, Alcuin runs its statements in a transaction that is rolled back, as it does for any table you stage.

If you connect an AI agent

AI agents (Claude Code, Cursor and other MCP clients) can read your databases through Alcuin, but only with a token you make in Settings, under Agents. There are none until you make one. An agent runs on this machine and talks to Alcuin over a local socket. What it reads goes wherever the agent sends it, usually its model’s provider, under that agent’s own policy, not Alcuin’s.

What a token can read. Only the connections you select when you make it, and only read-only: statements run in a read-only transaction that is always rolled back. A token can:

  • list its connections;
  • search and describe tables and views, with their columns and DDL;
  • run one SELECT, EXPLAIN or SHOW at a time, at most 500 rows and 64 KB per result;
  • read the server’s sessions and health findings.

The token’s data scope narrows all of it: the schemas it may see, and the tables and columns it leaves out. A column left out is not named in any answer, and other sessions’ statements that name something outside the scope are hidden. Other sessions’ statements have their values replaced by $1, $2 and so on.

Alcuin checks the scope one statement at a time, just before it runs. It follows what the statement would really read: through views and materialized views, and from parent tables into their child tables and partitions. It refuses functions outside the server’s own catalogs, whose code it cannot check, and TOAST tables, column statistics and the server’s raw session list. Because the check is per statement, it is not a lasting boundary: a view or function changed later is checked again at the next statement. The lasting boundary is the database role the connection uses. Settings can write the statements for a dedicated role whose grants match the scope. Grants do not hold back a SECURITY DEFINER function, which reads with its owner’s rights, and by default every role can call one: the role check names those the role could call, and the statements suggest revoking them from PUBLIC.

Production. A token covers no production connection by default. Adding one is a separate choice when you make the token, and its wording says that rows will go to the agent’s model. A connection that becomes production later is suspended for the token until you allow it again.

Seeing what agents did. Every statement an agent runs is kept in the connection’s query history, marked with the token’s name: open History and choose Agents. While an agent is connected, the status bar says which one and on which connection. Settings, under Agents, shows when each token was last used.

Revoking. Revoking a token in Settings ends its access at once. Sessions already open are refused at their next request and disconnected. Alcuin keeps only a hash of each token in agent-tokens.json, never the token itself.

Crash reports

Electron writes a crash dump on this machine when a process crashes. Alcuin never uploads it. The diagnostics bundle lists the dump file names so you can decide whether to attach one to a bug report; the dumps themselves are not included.

The diagnostics bundle

Help → Save Diagnostics Bundle… writes one text file where you choose. It contains:

  • the Alcuin, Electron, Chromium and Node versions, and the OS and architecture
  • the shell settings file (update channel, window background color, and the paths of up to 50 project folders you opened)
  • the last 500 lines of the log file (below)
  • the names of crash dump files on this machine

Before writing, anything shaped like a password, token or connection-URL credential is replaced with [redacted]. Read the file before you share it; it is yours.

The log file

Alcuin keeps a log of what the app itself does, so a bug report still has it after a crash or a restart: ~/Library/Logs/alcuin on macOS, ~/.config/alcuin/logs on Linux. Help → Open Logs Folder shows it. It holds the engine’s and the app’s own messages, and every line passes the same redaction as the diagnostics bundle before it is written, which takes out passwords and other credentials. It does not filter out query text: a lost connection, a crash or some warnings keep their message, which can quote part of a query or a value. Read it before you share it. main.log grows to 2 MB, then replaces main.old.log, so the two together stay near 4 MB. Only you can read them, and Alcuin never uploads them.

What stays on your machine

  • Connection profiles, query history, saved queries, open tabs and staged edits live in state.sqlite under the app’s user data directory.
  • Passwords, SSH passphrases and the assistant’s API key are encrypted with the operating system’s key store and kept in secrets.json next to it. They never enter the database file, the logs, or the diagnostics bundle.
  • Agent tokens are kept as hashes in agent-tokens.json, next to them.
  • A file that can’t be read is set aside next to itself (….corrupt-<time>), never deleted, and the app says so when it starts.
  • Result pages fetched for scrolling are spooled to an encrypted temporary file that is deleted when the result closes and swept when the app starts.