Skip to main content

Vault Encryption

The vault (~/.grind/vault.db) is encrypted at rest via libsql’s AES extension. The encryption key is stored inside ~/.grind/config.json as the encryptionKey field. Grind sets ~/.grind/ to 700 and config.json to 600 on every write, so the key is owner-readable only. If you lose config.json, your vault is unrecoverable. There is no key recovery mechanism. config.json holds both your settings and the encryption key — backing up vault.db alone is not sufficient. Back up the config:
The key is never transmitted anywhere. It is only read locally by the libsql client when opening the vault.

Companion Trust: Know What You’re Granting

The trust ladder has real consequences. Before escalating trust, understand what each level enables:
At trust level 4, a prompt injection in a webpage the companion fetches or a file it reads could cause it to execute arbitrary commands silently. Treat Sovereign trust like root access.

Tool Permission Scope

At trust ≥ 3, the agent can invoke:
  • bash: executes arbitrary shell commands in your environment
  • write_file: creates or overwrites any file your user can write
  • edit_file: modifies any file your user can write
These tools are intentionally powerful. The approval prompt at level 3 exists as a checkpoint. Read the tool call before approving.

Self-Hosting Hardening

When running Grind on a VPS or server, follow these practices in addition to the sections below:
  • Keep services loopback-only. Both the web app (:3000) and gateway (:5174) default to 127.0.0.1. Only expose them through a reverse proxy.
  • The web app has no built-in auth. Use an SSH tunnel, Tailscale, Cloudflare Access, or HTTP basic auth before exposing it publicly. Never put it directly on a public port.
  • Only expose /hooks/* gateway endpoints publicly — and only if you use Telegram, Discord, or WhatsApp integrations. Everything else should stay behind the proxy or be blocked.
  • Set all webhook verification secrets (GRIND_GATEWAY_TOKEN, GRIND_TELEGRAM_WEBHOOK_SECRET, GRIND_DISCORD_PUBLIC_KEY, GRIND_WHATSAPP_APP_SECRET). Unverified endpoints accept any POST.
  • Enable loginctl enable-linger on headless Linux servers so user services restart on boot.
  • Firewall: allow only TCP 22, 80, 443 inbound. The service ports (3000, 5174) must not be publicly reachable.
See Self-Hosting for the full guide including systemd setup, reverse proxy configs, and platform-specific examples.

Gateway Exposure

By default, the gateway binds to 127.0.0.1:5174 (localhost only). It is not accessible from the network. If you configure an externally reachable host (e.g. 0.0.0.0 or a public IP) to receive webhooks from Telegram or WhatsApp:
  • Ensure GRIND_GATEWAY_TOKEN is set for the /hooks/inbound endpoint
  • Discord and WhatsApp endpoints verify signatures automatically (Ed25519 / HMAC-SHA256)
  • Telegram does not sign payloads with HMAC or Ed25519. If GRIND_TELEGRAM_WEBHOOK_SECRET is set, Grind verifies the x-telegram-bot-api-secret-token header that Telegram attaches to every delivery — set this. If it is not set, your webhook URL path is the only gate; keep it non-guessable and treat it as a secret
  • Consider placing the gateway behind a reverse proxy (nginx, Caddy) with TLS
Exposing the gateway to the internet without signature verification on all endpoints is a risk. A malicious POST could create or complete quests.

WhatsApp Web (Baileys)

The WhatsApp Web integration connects via the unofficial Baileys library, which reverse-engineers the WhatsApp Web protocol.
  • Sessions can be remotely revoked by WhatsApp at any time, without warning
  • WhatsApp may ban accounts using unofficial clients, though this is uncommon for personal use
  • Session state is stored in your vault; re-linking requires scanning a new QR code
  • Do not use this for critical automation. Use the official WhatsApp Cloud API (GRIND_WHATSAPP_MODE=cloud) for stable production use

Turso Cloud Sync

When Turso sync is enabled (TURSO_DATABASE_URL + TURSO_AUTH_TOKEN), your vault data is replicated to Turso’s servers.
  • Data is encrypted in transit (TLS) and at rest on Turso
  • The TURSO_AUTH_TOKEN grants full read/write access to your database. Store it as a secret, not in a committed .env file
  • Grind does not share any data with Turso beyond the sync replication

Local-First as a Security Property

Grind’s local-first architecture means:
  • No account, no login, no external API calls unless you configure them
  • The AI provider (Anthropic, OpenAI, etc.) receives your conversation messages. This is unavoidable when using cloud AI. Use Ollama if you need fully offline operation
  • No telemetry, analytics, or crash reporting is sent anywhere
  • Quest data, skill trees, streaks, and logs never leave your machine unless you enable Turso sync or send them to an AI provider as context