@@ -90,10 +90,11 @@ and the [MCP tools](#mcp-server) both call, so they behave the same and are test
|
||||
tool only translates its request into a service call and the result into a response, and never
|
||||
touches the database.
|
||||
|
||||
Todos are stored in a local [SQLite](https://www.sqlite.org/) file, `data/todo.db` by default
|
||||
(git-ignored), which is created and migrated on the first todo request. Set `TODO_DB_PATH` to put it
|
||||
elsewhere. In Docker, mount a volume at that path (e.g. `docker run -v tbd-data:/app/data ...`), or the
|
||||
todos are lost with the container.
|
||||
Everything the app stores (todos, and the MCP OAuth state) lives in one local
|
||||
[SQLite](https://www.sqlite.org/) file, `data/tbd.db` by default (git-ignored), which is created and
|
||||
migrated on first use. Each group owns its own tables (`tbd.todo.db`, `tbd.oauth.db`); `tbd.sqlite` opens the
|
||||
file and runs their migrations. Set `DB_PATH` to put it elsewhere. In Docker, mount a volume at that path
|
||||
(e.g. `docker run -v tbd-data:/app/data ...`), or the todos and MCP approvals are lost with the container.
|
||||
|
||||
Every id the app issues (a todo's, an OAuth grant's) is a UUID, never a number: a model that reads `7` as a
|
||||
todo's id mixes it up with a 7 in a title. (`owner-id` is the user's id in Gitea, which it issues, so it stays
|
||||
@@ -164,7 +165,7 @@ The consent page is what stops a stranger's client (anyone can register one) fro
|
||||
sending you a link: nothing is issued without your click. Redirect URIs must be `https`, `http` on
|
||||
`localhost`/`127.0.0.1`/`[::1]` (the port may vary), or an app's own scheme (`cursor://...`).
|
||||
|
||||
What's stored is in `OAUTH_DB_PATH`: registered clients, and for each approval the user and a *hash* of the
|
||||
What's stored is in the app's database (`DB_PATH`): registered clients, and for each approval the user and a *hash* of the
|
||||
current access and refresh tokens. To cut a client off, delete its row from `grants`. Admin-ness isn't in
|
||||
the token: it is looked up in `ADMIN_USERS` on every request, so removing someone from the list takes
|
||||
effect at once. A Gitea account that is disabled keeps working until its refresh token expires (up to 30
|
||||
@@ -188,8 +189,7 @@ login, **403** for a logged-in non-admin. With no allowlist, nobody is an admin.
|
||||
| `BASE_URL` | Public URL of this app; an `https://` one also marks the session cookie `Secure`. It is also the address of the MCP server: the OAuth issuer, and what MCP clients are told to use (`<BASE_URL>/mcp`) |
|
||||
| `ADMIN_USERS` | Comma-separated Gitea usernames (case-insensitive) allowed to use the admin API |
|
||||
| `SESSION_SECRET` | Signs and encrypts session cookies. Unset = a random key per start, so logins don't survive a restart |
|
||||
| `TODO_DB_PATH` | SQLite file for the todo group (default `data/todo.db`) |
|
||||
| `OAUTH_DB_PATH` | SQLite file for the MCP OAuth server: registered clients, codes and tokens (default `data/oauth.db`). Mount a volume for it in Docker too, or every MCP client has to be approved again after each restart |
|
||||
| `DB_PATH` | The app's SQLite file: todos, and the MCP OAuth server's registered clients, codes and tokens (default `data/tbd.db`). Mount a volume for it in Docker, or todos are lost and every MCP client has to be approved again after each restart |
|
||||
| `LMSTUDIO_URL`, `LMSTUDIO_MODEL`, `LMSTUDIO_API_KEY`, `LMSTUDIO_TIMEOUT_SECONDS` | The LM Studio server behind the [chatbot](#chatbot). Only the URL is needed |
|
||||
| `CHAT_TRACE_FILE` | Optional. A file to append a JSON trace of every chatbot turn to, [with the model's full reasoning and replies](#watching-the-chatbot-think). Off by default |
|
||||
| `NREPL_PORT`, `NREPL_BIND` | Where the debug nREPL listens (default `7888` on `127.0.0.1`) |
|
||||
@@ -500,4 +500,5 @@ To add a new group, create `tbd.<group>.routes` with a `routes` fn that returns
|
||||
vector, then add it to the router in `src/tbd/core.clj`. To make a route admin-only, put
|
||||
`:admin? true` in its route data (admin allowlist), or `:login? true` (any logged-in user; the user is
|
||||
passed to the handler as `:user`). A group with storage keeps it in its own `tbd.<group>.db`, apart
|
||||
from its business logic in `core.clj`.
|
||||
from its business logic in `core.clj`, with a `migrate!` fn for its tables in the app's one database;
|
||||
add it to the `sqlite/open` call in `make-router`.
|
||||
|
||||
Reference in New Issue
Block a user