What DreenMind is
DreenMind is a local-first agent gateway written in Go. It runs on your machine, exposes a loopback HTTP API, and gives the operator a CLI plus an optional Telegram channel. The gateway, not the model, owns conversation state, tool execution, approvals, and the record of what happened.
The design target is one operator on one machine. There is no hosted control plane, no team tenancy, and no assumption that the model provider is trustworthy. The provider is an edge adapter. The state stays local.
Runtime
Single Go binary
Go 1.26.x; CGO disabled for release builds.
State
SQLite with WAL and FTS5
Sessions, messages, tool calls, approvals, usage.
Access
Loopback HTTP + bearer
CLI first; optional Telegram private chats.
Provider
OpenAI-compatible
Chat completions with tool calling.
Tools
Workspace and web fetch
Default-deny; exact host allowlists.
License
Apache-2.0
Permissive license.
One turn, in order
The turn loop is deliberately boring. The order is the safety property: state is written before an external effect, and the result is appended as untrusted data.
- The CLI or Telegram adapter submits a message to the gateway.
- The gateway persists the user message and creates or loads the session.
- The provider is called with a bounded history and the available tool schemas.
- The assistant message and any tool-call rows are persisted before execution.
- The tool is checked against policy: name, path, host, size, and approval state.
- If approval is required, the call waits up to 60 seconds for a CLI or API decision.
- The tool runs with a timeout and a bounded output size.
- The result is recorded as an untrusted tool message and fed back to the provider.
- The loop ends with a final assistant message, a failure, or an explicit stop.
- On restart, an interrupted tool call is closed with a synthetic error rather than replayed.
The receipt is not telemetry
The record is local and operator-owned. It exists so the next turn, the next process, and the operator can tell what happened. It is not sent to a hosted dashboard.
What the gateway owns
The database is the source of truth. The model context is a projection, not the record. The gateway writes before it acts and keeps enough detail to explain a failure without guessing.
| Record | Why it exists |
|---|---|
| Session | One conversation boundary, channel identity, and timestamps. |
| Message | User, assistant, tool, and system-visible turns with ordering. |
| Tool call | Name, arguments, policy decision, approval, result, error, timing. |
| Approval | Scoped request, expiry, single-use decision, and actor. |
| Usage | Provider token counts and model metadata when available. |
| Message search | FTS5 index with a rebuild path through doctor --repair. |
SQLite runs in WAL mode. Startup performs a quick integrity check. Corruption is treated as an operational failure, not a reason to silently reset state.
Security invariants
- Loopback only. The gateway binds to loopback. Remote access is the operator’s tunnel or reverse-proxy problem, not a built-in public listener.
- Secrets stay out of config. The vault uses Argon2id and AES-GCM. Secrets are referenced as
secret://and redacted from logs and errors. - Tools are deny-by-default. Workspace read/list are on; write and move are opt-in. Paths are confined with
os.Root, with UTF-8, size, and symlink checks. - Web fetch is exact-host. The allowlist is explicit. DNS is revalidated, private and link-local addresses are blocked, and redirects are not a way around the list.
- Approval is a capability. Requests are scoped, single-use, and expire after 15 minutes. The gateway exposes decisions through the CLI and HTTP API, not Telegram buttons.
- Tool output is untrusted. A fetched page or file cannot promote itself into instructions. The gateway records it as tool data.
- Sandbox is fail-closed. The Docker contract exists; execution is disabled when Docker is unavailable. There is no shell or execution tool.
Status, without rounding up
| Claim | Label | What the label means |
|---|---|---|
| Core loop | Tested | Agent, gateway, state, and app tests pass locally. |
| Provider adapter | Implemented Unverified | Works against a fake HTTP server; no live provider call. |
| Telegram adapter | Implemented Unverified | Private-chat logic and fake API tests; no live bot. |
| Approvals | Tested | Single-use, expiry, and timeout behavior tested. |
| Docker sandbox | Partial | Contract and fail-closed behavior tested; no escape suite. |
| Updates | Partial | Checksummed draft artifacts; no signing or SBOM. |
“Verified” is reserved for a claim tested in the environment it names. The current tree does not claim verified live provider or Telegram behavior.
Known limits
- Source-first distribution; no hosted control plane.
- No live verification against a real model provider or the Telegram Bot API.
- No streaming responses, embeddings, image, or audio support.
- No semantic memory; message search is FTS5 only.
- No teams, shared tenancy, group Telegram chats, or multi-user approvals.
- No shell or execution tool, and no sandbox escape test suite.
- No signed updates, SBOM, or automatic updater.
- No rate limiting, per-tenant budgets, or compaction policy.
- Windows builds are supported, but untrusted execution remains disabled.
Non-goals
DreenMind is not trying to be a marketplace, a multi-agent orchestrator, or a hosted control plane. It is not a general workflow engine. It does not promise that a model is correct, safe, or cheap. It promises that the gateway records what it allowed and what it refused.
Those refusals are part of the product. A smaller surface is easier to test, explain, and recover.