Getting Started

Install, configure, and run Blackwood in a few minutes.

1. Prerequisites

If building from source, you also need:

2. Install

Option A: Download a release

Grab the latest binary from GitHub Releases:

# macOS (Apple Silicon)
curl -L https://github.com/csweichel/blackwood/releases/latest/download/blackwood_darwin_arm64.tar.gz | tar xz

# macOS (Intel)
curl -L https://github.com/csweichel/blackwood/releases/latest/download/blackwood_darwin_amd64.tar.gz | tar xz

# Linux (x86_64)
curl -L https://github.com/csweichel/blackwood/releases/latest/download/blackwood_linux_amd64.tar.gz | tar xz

# Linux (arm64)
curl -L https://github.com/csweichel/blackwood/releases/latest/download/blackwood_linux_arm64.tar.gz | tar xz

Move the binary somewhere on your PATH:

sudo mv blackwood /usr/local/bin/

Option B: Build from source

git clone https://github.com/csweichel/blackwood.git
cd blackwood

# Build the web UI
cd web && npm ci && npm run build && cd ..

# Copy built UI into the embed directory
rm -rf cmd/blackwood/static && cp -r web/dist cmd/blackwood/static

# Build the server
go build -o blackwood ./cmd/blackwood

3. Configure

Interactive setup (recommended)

The quickest way to configure Blackwood is the interactive setup command. It creates directories, stores your secrets, and generates a config file:

./blackwood setup

Once complete, skip ahead to Run the Server.

Manual setup

Alternatively, create the data directory and config by hand:

mkdir -p ~/.blackwood/secrets

Store your OpenAI API key

Secrets are read from files, not stored in the config directly. This avoids leaking keys in version control or process listings.

# Write your API key to a file (replace sk-... with your actual key)
echo -n "sk-..." > ~/.blackwood/secrets/openai-api-key
chmod 600 ~/.blackwood/secrets/openai-api-key

Create the config file

Save this as ~/.blackwood/config.yaml:

server:
  addr: ":8080"
  data_dir: ~/.blackwood

openai:
  api_key_file: ~/.blackwood/secrets/openai-api-key
  model: gpt-5.2
  chat_model: gpt-5.2
  embedding_model: text-embedding-3-small

Env var fallback: If you prefer environment variables, skip the config file entirely. Set OPENAI_API_KEY and Blackwood will use it. The config file takes priority when both are present.

Config reference

All fields and their defaults:

Field Default Description
server.addr :8080 Listen address
server.data_dir ~/.blackwood SQLite database and attachments
server.tls.cert_file Path to TLS certificate (enables HTTPS when both cert and key are set)
server.tls.key_file Path to TLS private key
openai.api_key_file Path to file containing your OpenAI API key
openai.model gpt-5.2 Model for OCR and vision
openai.chat_model same as model Model for RAG chat
openai.embedding_model text-embedding-3-small Model for semantic embeddings
openai.ocr_prompt built-in Custom prompt for handwriting OCR
whatsapp.verify_token Webhook verification token
whatsapp.app_secret_file Path to file containing app secret
whatsapp.access_token_file Path to file containing access token
whatsapp.phone_number_id WhatsApp Business phone number ID
telegram.bot_token_file Path to file containing Telegram bot token
telegram.allowed_chat_ids [] (allow all) List of Telegram chat IDs allowed to use the bot
granola.oauth_token_file Path to file containing Granola OAuth token (from blackwood granola-login)
granola.poll_interval 1h How often to check for new or updated meeting notes

4. Run the Server

blackwood --config ~/.blackwood/config.yaml

Open http://localhost:8080 in your browser. You should see the calendar view with today's date.

CLI flags override config values:

# Listen on a different port
blackwood --config ~/.blackwood/config.yaml --addr :3000

# Use a different data directory
blackwood --config ~/.blackwood/config.yaml --data-dir /var/lib/blackwood

Quick start without a config file:

export OPENAI_API_KEY=sk-...
blackwood

This uses all defaults: listens on :8080, stores data in ~/.blackwood.

5. Keyboard Shortcuts

Shortcut Action
Cmd+D Jump to today's note
Cmd+/ Toggle between notes and chat
Cmd+T Insert current time (in edit mode)
Cmd+Enter Save and exit edit mode
Esc Exit edit mode

On Windows/Linux, use Ctrl instead of Cmd.

6. WhatsApp Integration (Optional)

Blackwood can receive text, voice, and photo messages via WhatsApp. Messages are appended to today's daily note.

Setup

  1. Create a Meta Developer app with WhatsApp Business API access.
  2. In the app dashboard, note your Phone Number ID and generate a permanent Access Token.
  3. Choose a Verify Token (any string you pick).
  4. Store the secrets:
echo -n "your-app-secret" > ~/.blackwood/secrets/whatsapp-app-secret
echo -n "your-access-token" > ~/.blackwood/secrets/whatsapp-access-token
chmod 600 ~/.blackwood/secrets/whatsapp-*

Add the WhatsApp section to your config:

whatsapp:
  verify_token: your-verify-token
  app_secret_file: ~/.blackwood/secrets/whatsapp-app-secret
  access_token_file: ~/.blackwood/secrets/whatsapp-access-token
  phone_number_id: "123456789"

Webhook URL

In the Meta Developer dashboard, set the webhook URL to:

https://your-domain.com/api/webhooks/whatsapp

Blackwood must be reachable from the internet. Use a reverse proxy (nginx, Caddy) or a tunnel (ngrok, Cloudflare Tunnel) to expose it.

Security: The webhook validates request signatures using your app secret. Do not expose the server without HTTPS in production.

7. Telegram Bot (Optional)

Send text, voice messages, and photos from Telegram to your daily notes. The bot uses long polling — no public URL or webhook setup is needed.

Create a bot with @BotFather

  1. Open Telegram and search for @BotFather.
  2. Send /newbot and follow the prompts to choose a display name and username for your bot.
  3. BotFather replies with a bot token like 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11. Copy it.

Store the token

echo -n "YOUR_BOT_TOKEN" > ~/.blackwood/secrets/telegram-bot-token
chmod 600 ~/.blackwood/secrets/telegram-bot-token

Configure

Add the Telegram section to your config:

telegram:
  bot_token_file: ~/.blackwood/secrets/telegram-bot-token

Without a config file: Set the TELEGRAM_BOT_TOKEN environment variable instead.

Authorize your chat

Start Blackwood. The server logs will show a 6-digit authorization code:

telegram: bot started — send this code to the bot to authorize a chat  auth_code=482910

Open your bot in Telegram and send that code as a message. The bot replies with “✓ Authorized!” and your chat is connected. The authorization is persisted in the database — you only need to do this once.

The code rotates after each use, so you can authorize multiple devices or group chats by checking the logs each time.

To disconnect a chat, send /revoke to the bot.

Static allowlist: You can also pre-authorize chat IDs in the config file. These are always authorized and can’t be revoked via /revoke:

telegram:
  bot_token_file: ~/.blackwood/secrets/telegram-bot-token
  allowed_chat_ids:
    - 123456789

What the bot does

You send Blackwood does
Text message Adds it as a text entry in today's note
Voice message Transcribes via Whisper, adds transcription as entry
Photo Describes via vision model, adds description as entry

All messages are appended to the daily note with a timestamp and “Telegram” source label. Audio and photo files are stored as attachments alongside the note.

Security: Keep your bot token secret. Anyone with the token has full control over the bot. Without authorization, the bot rejects all messages and tells the sender to provide the auth code.

8. Granola Meeting Notes (Optional)

Blackwood can automatically import meeting notes from Granola via the Granola MCP server. The sync runs periodically, fetching new or updated notes and writing them as entries on the day each meeting occurred.

Each imported note includes the meeting title, date, attendees, AI-enhanced notes, private notes, and transcript (paid Granola tiers).

Setup

  1. Log in via OAuth:
    blackwood granola-login

    This opens your browser for authentication and saves the token to ~/.blackwood/secrets/granola-oauth-token.

  2. Add the granola section to your config:
    granola:
      oauth_token_file: ~/.blackwood/secrets/granola-oauth-token
      poll_interval: 1h

Alternatively, set the GRANOLA_OAUTH_TOKEN environment variable. Granola sync auto-enables when an OAuth token is configured.

9. Viwoods File Watcher (Optional)

Blackwood can watch a directory for Viwoods .note files, run OCR on each page, and append the results to your daily notes. Add the watcher section to your config:

watcher:
  watch_dir: /path/to/viwoods/notes
  poll_interval: 30s

The watcher runs inside the main blackwood process. No separate binary is needed.

10. Running as a Service

To run Blackwood on startup, create a systemd user service:

mkdir -p ~/.config/systemd/user

Save as ~/.config/systemd/user/blackwood.service:

[Unit]
Description=Blackwood Server
After=network.target

[Service]
ExecStart=/usr/local/bin/blackwood --config %h/.blackwood/config.yaml
Restart=on-failure
RestartSec=5

[Install]
WantedBy=default.target

Enable and start:

systemctl --user daemon-reload
systemctl --user enable --now blackwood

# Check status
systemctl --user status blackwood

# View logs
journalctl --user -u blackwood -f

macOS: Use a launchd plist instead. Place it in ~/Library/LaunchAgents/ and load with launchctl load.