Addons

Everything with reach beyond the working directory is an addon, and every addon is off until you install it. Not disabled: absent.

Managing them

tacet addon list                 # what exists, and what is installed
tacet addon install shell        # asks before it writes anything
tacet addon try shell            # call it once by hand
tacet addon close shell          # keep the config, take the tool away
tacet addon open shell           # put it back
tacet addon remove shell         # forget the config too

A closed addon's tools are not in the catalog the model is shown, so it cannot call them, mention them, or fail at them.

What each one opens

AddonToolsWhere the line is
web-searchweb_search, web_fetchYour own SearXNG, running locally under Docker or at an address you already have
shellshellOnly the programs you list, and no shell interpretation
workspacenoneNamed directories the file tools may also reach
httphttpOnly the exact hosts you list, HTTPS only, no redirects
dbdb, and db_write only if you name a fileRead-only SQLite by default, through the sqlite3 binary already on your machine
clipboardclipboardReads and writes the system clipboard

There is no shell in the shell addon

The command runs as a program plus a list of arguments and never through sh -c, so ; rm -rf / arrives as an argument and nothing parses it. The allow list is not checked after the fact either: it is the argument's schema, so the constrained decoder cannot generate a program name outside it.

Allowing a program allows everything that program can do

curl is network access. git can push. That is why shell sits behind the same approval question as web_search and http: once a turn has touched personal data, every call that could carry it off the machine stops and asks you first.

Writing to a database is a second tool, not a flag

db is read only and stays read only. Its lock is sqlite3 -readonly, measured against your own binary before the tool is built. If you want a database changed you name the file while installing the addon, and a separate db_write tool appears. Name nothing and it does not exist, which is the only gate that holds here: the SQL is free text, so a model that has the tool can always spell DROP TABLE.

Every db_write call is measured before it happens. The statement runs first against a copy of the file, and what you are shown is the difference that copy actually took: objects created, dropped or redefined, row counts moved, journal mode changed. Then it asks. Nothing is written until you say yes, the measured copy is left beside the database as <name>.tacet-backup, and the question is asked again for the next statement, so a no is about one statement rather than the session.

Two things db_write does not promise

It is not one statement: sqlite3 re-parses the string it is given, so SELECT 1; DROP TABLE t runs both halves. That was measured, and it is why the effect is shown rather than the statement filtered. And allowing a file allows everything SQL can do to that file. What does hold is the file boundary: -safe refuses ATTACH, VACUUM INTO and writefile(), all three checked at startup, so the statement cannot reach a second file.

Web search points at your own server

There is no default provider and no shared endpoint. The web-search addon wants a SearXNG root you control, either a local one under Docker or one you already run. TACET_SEARXNG sets it for a single session. Requests that resolve to a private or loopback address are refused at the socket boundary, so a search result cannot be used to reach something on your own network.

Third-party tools plug in elsewhere

Addons are the fixed set above. A tool somebody else wrote joins through an MCP connection instead, and passes the same four gates as a built-in one.