creel
Creel is a secure AI assistant designed to run tasks involving LLMs while keeping sensitive data safe. It separates the process of fetching data requiring credentials from the LLM's processing, preventing unauthorized access. Users can schedule tasks like daily briefings or interact with the agent through a command-line interface or iMessage. Creel's architecture uses isolated containers for each tool, limiting access to only the necessary credentials. This design significantly reduces the risk of data breaches, even if one component is compromised. It's ideal for individuals and organizations needing automated tasks with access to sensitive information. Creel provides a robust and secure way to leverage LLMs for various applications.
Creel solves the problem of securely integrating LLMs with tools and credentials, which is often a security risk in traditional agentic LLM systems. Instead of giving the LLM direct access to everything, Creel isolates tools, preventing prompt injection or compromised tools from accessing sensitive data, making it a safer alternative to manual processes or simpler tools.
CAPABILITIES & CONSTRAINTS
README
# Creel
<p align="center">
<img src="assets/creel-logo.jpg" alt="Creel" width="400">
</p>
A secure LLM task runner and personal AI assistant that separates credential-bearing data fetching from LLM processing. Supports both scheduled tasks (morning briefings, weather summaries) and interactive agent mode (chat via CLI or iMessage with tool calling).
A creel is a wicker basket usually used for carrying fish or blocks of peat. It is also the fish trap used to catch lobsters and other crustaceans.
## Why
Agentic LLM systems give the model access to tools, credentials, and untrusted input all at once. Creel enforces **per-tool isolation** — each executor runs in its own container with only the credentials it needs:
| Component | Has access to | Does NOT have |
|-----------|--------------|---------------|
| Each executor | Only its own credential (one OAuth scope, one API key) | LLM, other tools' credentials |
| Bridge executors | Scoped HTTP token for one macOS app | LLM, other bridge endpoints |
| LLM Runner | Anthropic API key only | Any tool credentials |
| Orchestrator | All secrets, LLM output | Untrusted external input |
Even if prompt injection occurs (e.g., via a calendar event title), the LLM container has nothing to exfiltrate except its own API key. A compromised executor can only access its one scoped credential — not your email, not your files, not your messages.
## Architecture
```mermaid
flowchart TD
subgraph orch["Orchestrator"]
direction TB
schedule["Scheduler / Agent Loop"]
guardian["Guardian Pipeline"]
template["Prompt Builder"]
output["Output Router"]
end
subgraph containers["Docker Executors (isolated)"]
direction TB
google["Google Suite\n📅 Calendar · 📧 Gmail · 📁 Drive"]
web["Web Tools\n🔍 Search · 🌐 Fetch · 🌤 Weather"]
exec["Shell / Exec\n⚙️ Mounted paths only"]
end
subgraph bridge["Host Bridge (macOS native)"]
direction TB
bridge_api["FastAPI Server"]
native["📝 Notes · ✅ Reminders\n📋 Things 3 · 💬 iMessage"]
end
subgraph llm_container["LLM Container"]
llm["Claude\n🔑 Anthropic API key only"]
end
subgraph channels["Channels"]
cli["TUI / CLI"]
imsg["iMessage"]
end
channels -- "message" --> orch
schedule -- "tool call" --> guardian
guardian -- "approved" --> containers
guardian -- "approved" --> bridge_api
bridge_api --> native
containers -- "JSON result" --> template
bridge_api -- "JSON result" --> template
template -- "prompt\n(no secrets)" --> llm
llm -- "response" --> output
output --> channels
style containers fill:#2d333b,stroke:#f47067,stroke-width:2px,color:#f0f0f0
style bridge fill:#2d333b,stroke:#fd7e14,stroke-width:2px,color:#f0f0f0
style llm_container fill:#2d333b,stroke:#f47067,stroke-width:2px,color:#f0f0f0
style orch fill:#2d333b,stroke:#58a6ff,stroke-width:2px,color:#f0f0f0
style channels fill:#2d333b,stroke:#3fb950,stroke-width:2px,color:#f0f0f0
```
> **Key insight:** Each red box is a separate security boundary. The LLM never sees credentials. Executors only get their own scoped secret. Even a compromised tool can't reach other tools' data.
### Agent Mode
In agent mode, the same security boundary applies — the LLM requests tool calls, but the orchestrator handles secrets injection and executor execution:
```mermaid
flowchart TD
CH["Channels\nstdin | iMsg | BB"] -- "incoming message" --> SM["Session Manager\n(JSON files)"]
SM -- "message + history" --> AL["Agent Loop"]
AL --> LLM["LLM call"]
LLM --> TU{"tool_use?"}
TU -- no --> resp["Response"]
TU -- yes --> EX["Execute via executor\n(secrets injected)"]
EX --> TR["tool_result"] --> AL
```
Scheduled tasks can also use agent mode by setting `mode: agent` in the task YAML.
### Guardian
The guardian layer screens inputs and validates tool calls before they execute. All stages are optional and independently configurable in `agent.yaml`:
```mermaid
flowchart TD
A["Incoming message"] --> B["screen_input(text)\n← before session history"]
B --> FC["FastClassifier\nDeBERTa/ONNX, ~10ms"]
B --> LJ["LLMJudge\nHaiku, ~300ms, off by default"]
FC --> blocked{"blocked?"}
LJ --> blocked
blocked -- yes --> reject["Return rejection,\nskip agent loop"]
blocked -- no --> agent["Agent loop → LLM returns tool_use"]
agent --> VA["validate_action(tool, args)\n← before execute_tool_call"]
VA --> PE["PolicyEngine\nYAML rules, <1ms"]
VA --> CC["CoherenceCheck\nHaiku, ~300ms"]
PE -- allow --> execute["Execute"]
PE -- review --> approval["Human approval\nor auto_approve"]
PE -- deny --> err1["Return error to LLM"]
CC -- coherent --> execute
CC -- incoherent --> err2["Return error to LLM"]
```
**Stages:**
| Stage | Component | What it does |
|-------|-----------|--------------|
| 1 | Fast classifier | Local DeBERTa model scores prompt-injection likelihood against a confidence threshold |
| 2 | LLM judge | Secondary Haiku-based check (disabled by default) |
| 3 | Policy engine | `fnmatch` rules in `policies/default.yaml` map tool names to allow/review/deny |
| 4 | Coherence check | LLM-based check that tool calls match the user's original intent (catches prompt injection causing unrelated actions) |
**Policy rules** (`policies/default.yaml`):
```yaml
allow: [check_weather, check_calendar, check_email, read_email, check_drive, check_messages, get_chats, react_imessage]
review: [send_*, upload_*, create_*, mark_*, trash_*]
deny: [delete_*]
# Tools that match 'review' rules but can skip human approval
auto_approve:
- mark_read
- react_imessage
```
Deny wins over review, review wins over allow. Unknown tools default to review. Tools listed in `auto_approve` skip the human confirmation prompt even when matched by a review rule.
**Human-in-the-loop review:** Tools matching `review` rules prompt the user for approval before executing. In the TUI, this appears as an inline confirmation dialog. A configurable timeout (default 60s) denies the action if no response is received.
**Audit logging** writes to `guardian_audit.jsonl` with hashed inputs (never raw text), tool names, arg keys (not values), verdicts, and confidence scores.
**Configuration** in `agent.yaml`:
```yaml
guardian:
enabled: true
review:
timeout_seconds: 60
default_on_timeout: deny
fast_classifier:
enabled: true
threshold: 0.95
model_name: protectai/deberta-v3-base-prompt-injection-v2
llm_judge:
enabled: false
coherence:
enabled: true
model: claude-haiku-4-5-20251001
max_tokens: 256
policy:
enabled: true
policy_file: policies/default.yaml
audit:
enabled: true
log_file: guardian_audit.jsonl
```
## Host Bridge
For macOS-specific tools (Apple Notes, Apple Reminders, Things 3, iMessage), Docker containers can't directly execute AppleScript or access macOS applications. The host bridge solves this by running a FastAPI server on the host system that provides authenticated HTTP endpoints for containerized executors.
**Why**: Docker containers are sandboxed and can't access the macOS scripting bridge or application APIs that tools like Notes, Reminders, and Things 3 require.
**How**: A FastAPI server (`python -m bridge.server`) runs as a host process and exposes REST endpoints at `/notes/*`, `/reminders/*`, `/things/*`, and `/imessage/*`. Containerized executors make HTTP requests to these endpoints with scoped authentication tokens.
**Security**: Each executor receives a scoped token that only grants access to its specific tool endpoints. For example, the `apple_notes` executor can only call `/notes/*` endpoints, not `/reminders/*` or `/things/*`.
**CLI Integration**: The bridge server delegates to command-line tools:
- **Apple Notes**: `memo` CLI for reading/writing notes
- **Apple Reminders**: `remindctl` CLI for managing rem
[truncated…]PUBLIC HISTORY
IDENTITY
Identity inferred from code signals. No PROVENANCE.yml found.
Is this yours? Claim it →METADATA
README BADGE
Add to your README:
