Observe Linux host metadata, package redacted context, and record guarded action intent—without pretending to be an autonomous remediation platform.
Quick start · Capabilities · Architecture · HQ roadmap · Security
Snuffles is an open-source, observation-first Linux host monitoring toolkit for AI agents and human operators. It combines a Cloudflare Worker/Vinext dashboard, Cloudflare D1 state, an optional signed VPS metadata probe, redacted ZIP snapshots, bounded Telegram delivery, and an experimental Model Context Protocol (MCP) JSON-RPC endpoint.
It is designed for a small Linux server where an agent runs locally, connects through SSH, or calls an explicitly authorized API. Snowball is the opt-in permission boundary for guarded action intent: it can record a qualifying containment request as pending_approval, but it does not change a firewall, host, or Cloudflare rule.
Important
Snuffles is a proof of concept—not a managed security product, SIEM, SOC, incident-response service, high-availability system, backup solution, or autonomous remediation engine. Initial dashboard signals are illustrative until replaced by persisted D1 state.
Giving an AI agent unrestricted shell access is easy. Giving it a small, inspectable, documented boundary is harder. Snuffles explores that boundary through four principles:
- Observe metadata, not raw logs. The optional probe sends a strict projection of host health fields.
- Keep secrets write-only. Provider tokens are encrypted with AES-GCM and never returned by state or MCP reads.
- Separate intent from execution. Snowball records approval-aware intent; this checkout has no provider mutation engine.
- Remain useful without an AI model. Host state and the optional hourly Telegram summary are deterministic local tools.
| Capability | Status | Boundary |
|---|---|---|
| Responsive React/Vinext control-room dashboard | ✅ Implemented | Uses illustrative data when D1 is unavailable; persisted empty state stays empty |
GET / POST /api/state with Cloudflare D1 |
✅ Implemented | Six explicit state actions; authorization is deployment-configured |
| Signed Linux host metadata probe | ✅ Implemented | HMAC-SHA256, replay ID, clock window, 16 KiB limit, no raw logs |
| Redacted ZIP context snapshots | ✅ Implemented | Explicit projections, SHA-256 checksum, latest-20 retention |
| Telegram snapshot and monitoring-summary delivery | ✅ Bounded | Owner-only, one configured destination, allowlisted metadata |
| Provider credential checks | ✅ Bounded | Fixed Cloudflare, Telegram, and Slack verification endpoints only |
| MCP-compatible JSON-RPC tools | 🧪 Experimental | Stateless POST; no SSE and no provider-side mutation |
| Snowball containment intent | ✅ Implemented | Records pending_approval; applies no host or edge change |
| Ubuntu systemd handoff | 🧰 Prepared | Examples only; operator provisioning is required |
| Live log/event ingestion and automatic remediation | ❌ Not implemented | Prospective, not advertised as available |
The detailed source of truth is docs/capability-status.md.
- Node.js 22.13.0 or newer
- npm
- A local clone
git clone https://github.com/TurabiOzturk/snuffles.git
cd snuffles
npm ci
cp .env.example .dev.vars
npm run devOpen the local URL printed by Vinext. The template uses SNUFFLES_API_MODE=ssh-tunnel, which has no application-level login and must remain on loopback. Do not expose that mode to a public network.
The dashboard can render without D1 by showing an explicitly labeled illustrative preview. For durable state, run with the local Cloudflare binding configured by .openai/hosting.json or provide a deployed D1 binding named DB.
npm run check # lint + types + build/tests + production audit
npm run build # build Worker and static assets
npm audit --omit=dev # production dependency auditflowchart LR
Operator["Operator / AI agent"] -->|authorized HTTP| State["State API"]
Client["MCP client"] -->|bounded JSON-RPC POST| MCP["MCP compatibility route"]
Probe["Optional Linux host probe"] -->|HMAC + replay ID| Agent["Host metadata route"]
Timer["Optional hourly timer"] --> Notify["Redacted Telegram summary"]
State --> D1[(Cloudflare D1)]
MCP --> D1
Agent --> D1
D1 --> Snapshot["Redacted ZIP snapshot"]
Snapshot -->|owner-authorized| Telegram["Configured Telegram destination"]
Snowball["Snowball permission"] -. "allows intent record" .-> Pending["pending_approval"]
Pending -. "no execution path" .-> Boundary["Host/provider unchanged"]
| Mode | Use case | Security boundary |
|---|---|---|
| Local preview | UI, API, and contract development | Localhost only |
| Ubuntu handoff | Local Worker/D1 on a small Linux VPS | 127.0.0.1:4417 through SSH forwarding |
| Cloudflare | Hosted Worker with provisioned D1 | Private Site identity, API token, or signed webhook |
See docs/deployment-modes.md before exposing an instance.
Snuffles HQ is the planned singular monitoring and coordination node for a fleet of independent Snuffles hosts. Each host will keep its local observation boundary, state, and optional delivery path; HQ will provide one place to understand and coordinate the fleet.
flowchart LR
H1["Snuffles host A"] -->|normalized metadata| HQ["Snuffles HQ\nsingular coordination node"]
H2["Snuffles host B"] -->|normalized metadata| HQ
H3["Snuffles host N"] -->|normalized metadata| HQ
HQ --> Fleet["Fleet status + presentation"]
HQ --> Index["Redacted snapshot index"]
HQ --> Coordination["Operator / agent coordination"]
Planned HQ responsibilities:
- register multiple Snuffles hosts and show their latest normalized health state;
- aggregate allowlisted metadata and signal summaries without becoming a raw-log or secret collector;
- present fleet-level status, redacted snapshot indexes, and action-intent history;
- coordinate reviewed monitoring templates and operator/agent workflows; and
- preserve explicit authorization, redaction, audit, approval, and failure boundaries for every cross-host operation.
Roadmap sequence:
- Publish typed host-to-HQ HTTP contracts, OpenAPI artifacts, and generated SDKs.
- Add authenticated host registration, heartbeat, replay protection, and bounded retention.
- Build the multi-host fleet dashboard and redacted snapshot index.
- Add explicit approval coordination and auditable receipts without silently executing host changes.
- Document backup, migration, degraded-HQ, and host-disconnection behavior before calling HQ production-ready.
Note
HQ is prospective. It is not implemented by the current routes or dashboard, and it will not be the only source of host truth. A Snuffles host must remain understandable and useful when HQ, an AI agent, or a provider integration is unavailable. A singular HQ node is a coordination topology—not a claim of high availability, failover, incident response, or automatic recovery.
| Route | Methods | Purpose |
|---|---|---|
/api/state |
GET, POST |
Read workspace state and execute six bounded state actions |
/api/agent |
POST |
Accept a signed, recent, metadata-only host snapshot |
/api/snapshots |
GET, POST |
List, create, download, and owner-deliver redacted ZIPs |
/api/notifications |
POST |
Discover Telegram destination metadata or send one safe summary |
/api/mcp |
POST |
Initialize MCP, list tools, and perform guarded tool calls |
Authorization modes are ssh-tunnel, site-private, token, and webhook. Request headers, signing strings, limits, owner behavior, and replay handling are documented in docs/agent-boundaries.md.
With local loopback-only ssh-tunnel mode:
curl --fail-with-body http://127.0.0.1:3000/api/mcp \
--header 'content-type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}'Use the URL printed by the local runtime if it selects a different port. Hosted callers must include the credentials required by their configured mode.
| Tool | Kind | Effect |
|---|---|---|
snuffles.discover |
Context | Return the safe resource catalog and latest normalized host snapshot |
snuffles.observe |
Read | Read persisted normalized signals and status |
snuffles.explain |
Read | Return one normalized signal and its evidence array |
snuffles.plan_build |
Plan | Produce a deterministic read-only-first build plan |
snuffles.test_integration |
Owner action | Call one fixed provider credential-verification endpoint |
snuffles.snapshot |
Context/delivery | Create a redacted ZIP; optionally deliver it to Telegram as owner |
snowball.recommend |
Plan | Recommend observation or a pending containment request |
snowball.contain |
Guarded intent | Record pending_approval; never apply a provider rule |
snowball.wake / snowball.sleep |
Owner action | Enable or revoke guarded intent mode |
The optional probe executes fixed, read-oriented commands and projects only:
- hostname, OS release, kernel, and architecture;
- systemd state and failed unit names;
- root filesystem percentage and available space;
- Nginx installation/version and configuration-test status;
- Docker version and running-container count;
- journal disk usage; and
- listening addresses and ports.
It does not send Nginx logs, journal entries, SSH event bodies, Docker logs, environment variables, credentials, or arbitrary command output. Bodies are byte-bounded and signed over timestamp.deliveryId.exactBody.
Snuffles defaults to observation and fails closed when required authorization or secret configuration is absent.
- Integration tokens are encrypted with AES-GCM, a fresh IV, and provider-bound associated data.
- Host probes and signed webhooks use separate HMAC-SHA256 secrets and replay IDs.
- Mutation routes enforce origin checks, strict body limits, explicit JSON errors, action admission limits, and owner checks where required.
- Snapshot exports contain allowlisted projections rather than raw database rows.
- Worker responses add anti-sniffing, anti-framing, referrer, permissions, opener, and resource-policy headers.
- Telegram provider errors are reduced to generic messages; provider payloads are not forwarded.
Read SECURITY.md before deployment. Never commit .env files, provider tokens, API keys, HMAC secrets, owner identifiers, real host snapshots, or production hostnames.
Copy .env.example and use synthetic local values. Important variables include:
| Variable | Purpose |
|---|---|
SNUFFLES_API_MODE |
Select the API authorization boundary |
SNUFFLES_API_KEY |
Authenticate token-mode requests |
SNUFFLES_WEBHOOK_SECRET |
Verify state webhook signatures |
SNUFFLES_AGENT_HMAC_SECRET |
Verify host probe snapshots |
SNUFFLES_SECRETS_KEY |
Encrypt provider credentials; minimum 32 characters |
SNUFFLES_WORKSPACE_ID |
Select the single deployment workspace |
SNUFFLES_ALLOWED_ORIGINS |
Allow additional browser origins for writes/MCP |
Several launcher-owned placeholders are intentionally inactive in application code. See docs/configuration.md for the exact consumer of every variable.
app/ Dashboard, API routes, validation, redaction, and MCP tools
db/ Drizzle schema and D1 access
drizzle/ Reviewed D1 migrations
deploy/ Prepared Ubuntu systemd examples
docs/ Architecture, boundaries, deployment, and capability status
public/ Discovery metadata and original project artwork
scripts/ Optional host probe and hourly monitor
tests/ Rendering, security, snapshot, monitoring, and contract tests
worker/ Cloudflare Worker entry point and response hardening
- Architecture and non-goals
- Agent/API security boundaries
- Capability status
- Configuration reference
- Runtime and deployment modes
- Ubuntu handoff
- Contributing
- Security policy
Contributions are welcome when they preserve the separation between observation, planning, delivery, and external side effects. Read AGENTS.md, ARCHITECTURE.md, and CONTRIBUTING.md before changing a route, integration, credential path, snapshot, or guarded action.
npm ci
npm run checkA capability is not “implemented” until its authorization, validation, redaction, side effect, bounded failure behavior, tests, and operator documentation all exist.
Source code and original project artwork are available under the MIT License. Asset details are recorded in docs/asset-licensing.md.
Last reviewed: 2026-09-03 · release 0.8.0