Monet — Personal AI Assistant.
A self-hostable, always-on personal assistant reachable over Telegram and SMS, driven by Claude Code CLI sessions with vector memory, a memory-hooks pipeline, dozens of agent personas, and scheduled background jobs. One host, one installer, one authorized chat id.
Install
Transcribed from the repository README; not yet executed from a clean environment:
git clone https://github.com/bulletproofsoftware-ai/bulletproof-monet.git cd bulletproof-monet cp .env.example .env ./install.sh --check ./install.sh

bulletproof-monet/docs/media/1Problem Statement
A coding-agent CLI is powerful but session-bound and desk-bound. It forgets everything between invocations and can only be reached from a terminal, which makes it useless for the things a personal assistant is actually for — a question while walking, a reminder at an odd hour, a research task that should run overnight.
Bridging a CLI to a messaging channel sounds simple and is not. Concurrent messages collide over shared session state, long-running work exceeds any reasonable webhook timeout, a crashed session strands the channel silently, and untrusted inbound text reaches an agent with real tool access.
Monet is the operational answer: session locking and lifecycle rules, a watchdog for stuck work, detached background jobs for anything slow, injection filtering on inbound content, and a memory pipeline that makes recall automatic rather than something the user must request.
2Architecture
Inbound Telegram and Twilio webhooks terminate at nginx, pass through n8n workflows, and spawn Claude Code CLI sessions. The bulk of the orchestration lives in large Bash handlers, with Python for the API and audit layer and TypeScript for the memory MCP server.
Session lifecycle
A per-channel flock prevents concurrent sessions from colliding. A state file tracks session id, last activity, and turn count; five minutes of inactivity starts a fresh session; resume is attempted first with fallback to new; a 40-turn ceiling triggers compaction at turns 15 and 30.
Memory hooks
Four Claude Code hooks wire memory into every turn: prompt-submit recall, a pre-tool gate that blocks file search until memory has been consulted, a session-start nudge, and a post-skill usage hook.
Hybrid recall
Retrieval merges BM25 and vector search through reciprocal rank fusion under a 6000-character injection budget, with polarity-aware ordering that marks corrections and negative feedback and sorts them first.
Reliability layer
A watchdog kills processes stuck past a six-minute threshold; a background runner detaches long jobs with a 30-minute timeout and tracks them on disk; cron retry logic covers transient failures.
Management API
A stdlib http.server on port 8091 provides read-only operational endpoints behind bearer auth, backed by an append-only SQLite audit database in WAL mode and live vector-store statistics.
Assistant Capabilities
- Multimodal messaging — text, images, voice (transcription and speech synthesis), documents, locations, and slash commands over Telegram.
- Agent personas — dozens of specialist personas invocable per request, with a model override for cost or capability tuning.
- Scheduled work — a daily briefing, minute-resolution reminders, and an overnight research queue that drains while the user sleeps.
- Cross-channel continuity — context carries between Telegram and SMS within a 30-minute window.
- Self-improvement loop — periodic harvesting mines reusable patterns from transcripts, with nightly skill scoring and a monthly coverage audit.
- Injection filtering — inbound content is scored for prompt-injection severity before it reaches an agent with tool access.
- Operational tooling — workflow fleet checks for cost, dead workflows, health, and parity, plus a dashboard and vault indexing.
3Requirements
Requirements describe the deployed behaviour documented and implemented in the repository.
| ID | Requirement |
|---|---|
| REQ-MN-001 | Accept inbound messages over Telegram and SMS via webhook. |
| REQ-MN-002 | Serialise per-channel sessions with an advisory lock. |
| REQ-MN-003 | Resume an existing session where possible and fall back to a new session. |
| REQ-MN-004 | Expire sessions after five minutes of inactivity and compact long sessions. |
| REQ-MN-005 | Recall relevant memory automatically on every prompt submission. |
| REQ-MN-006 | Block file-search tools until memory has been consulted. |
| REQ-MN-007 | Merge lexical and vector retrieval with rank fusion under a fixed injection budget. |
| REQ-MN-008 | Order recalled corrections and negative feedback ahead of other memories. |
| REQ-MN-009 | Detach long-running work to background jobs with a timeout and on-disk tracking. |
| REQ-MN-010 | Terminate stuck processes past a watchdog threshold. |
| REQ-MN-011 | Score inbound content for prompt injection before agent execution. |
| REQ-MN-012 | Record all activity to an append-only audit database and expose read-only operational endpoints. |
4Interfaces
Three interfaces: a messaging surface for the user, a management API for the operator, and hooks that wire memory into the agent runtime.
| Interface | Purpose |
|---|---|
| GET /health · /sessions · /audit | Service health, active sessions, audit trail |
| GET /usage · /usage/today · /stats | Token and cost accounting |
| GET /memory/stats · /cron | Vector store statistics and schedule status |
| /new · /export · /help | Session control (17-command deny-by-default allowlist) |
| /remind · /reminders | Reminders |
| /clip · /clips · /find · /search | Snippets and semantic search |
| /mark · /marks · /recall | Conversation bookmarks |
| /ss · /photo-log · /usage | Screenshots, photo history, token usage |
| /memory-export · /memory-stats | Memory inspection and export |
| UserPromptSubmit · PreToolUse hooks | Automatic recall and the memory-first gate |
| SessionStart · PostToolUse hooks | Session nudge and skill usage tracking |
5Integration Points
Monet is an integration layer by nature, binding a CLI agent to messaging, memory, and scheduling infrastructure.
- Telegram Bot API and Twilio — the two user-facing channels, both arriving through nginx and n8n.
- Claude Code CLI — the execution engine; every turn is a CLI session rather than a direct API call.
- Qdrant and Ollama — vector memory with local embeddings and a small local model for summarisation and skill detection.
- n8n — webhook routing and scheduled workflow execution, with sixteen exported workflow definitions.
- PostgreSQL and SQLite — workflow state and the append-only audit log respectively.
- Obsidian — vault indexing for retrieval over personal notes.
6Repository
The repository is a self-hostable package: a top-level Compose file that includes the sub-stacks, a 17-step install.sh orchestrator, and the handlers, jobs and services it installs.
| Path | Purpose |
|---|---|
| install.sh | 17-step installer with a credential-free --check dry run |
| docker-compose.yml | Top-level stack; include:s the sub-stacks under docker/ |
| deploy/ | nginx templates, webhook registration, and the smoke test |
| monet-*.sh | Channel handlers and scheduled job entrypoints |
| api/ | Management API and webhook entrypoints |
| bin/ | Operator CLI tools — agent invocation, review, research queue |
| claude/scripts/ | The four memory hooks |
| lib/ | Injection filtering, audit database, visual content generation |
| mcp/claude-memory-mcp/ | TypeScript memory MCP server |
| n8n/workflows/ | Sixteen exported workflow definitions |
| scripts/ | Memory governance, indexing, skill scoring, fleet checks |
| dashboard/ | Single-page operational dashboard |
7Implementation Notes
Constraints that matter before you install this on a host.
- It drives the Claude Code CLI with
--dangerously-skip-permissions. The agent can read, write, and run commands on the host without prompting, at the direction of whoever messages the bot. Run it on a dedicated VPS — not a workstation, and not a machine holding other people's data. - A single Telegram chat id is the entire authorization model. The webhook receiver compares every inbound update against it and silently drops the rest, so that one value is what stands between a stranger and a shell.
install.shruns 17 steps and supports a credential-free--checkdry run that mutates nothing outside the checkout. Run--checkbefore the real install.- Docker Compose 2.20 or newer is required: the top-level
docker-compose.ymluses the Compose Specinclude:key to pull in the sub-stacks. Compose v1 silently resolves it to a single service. - Several handlers exist in divergent copies at the repository root, under
src/, and underbridges/, at different file sizes. Identify the authoritative copy before modifying anything. - Every container port binds to
127.0.0.1and a reverse proxy terminates TLS in front, so the deployment assumes a single hardened host. - The memory layer blocks writes above a 0.92 similarity threshold to prevent near-duplicate accumulation.
- Credentials in
.env.exampleship empty;install.shgenerates every non-user-supplied secret withopenssl rand -hex 32on first run.