โ† Dashboard ยท Docs ยทAdmin Guide

Grove Admin Guide

Deployment

Quick Start (single cell)


# Get the install script from an invite link
curl -s https://grove.nook.website/invite/<token> | bash

Manual Install


mkdir -p ~/.grove
pip3 install cryptography flask flask-socketio requests pynacl websocket-client websockets zeroconf
# Copy grove.py and web.py to ~/.grove/
cd ~/.grove && python3 web.py
# Dashboard at http://localhost:5678

systemd Services

Install script: systemd/install.sh

grove.service โ€” main dashboard + API


[Service]
User=grove
WorkingDirectory=/home/grove/.grove
ExecStart=/usr/bin/python3 web.py
Restart=always
RestartSec=5
MemoryMax=512M
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/home/grove/.grove /home/grove/GroveHome

grove-watchdog.service โ€” health monitor


[Service]
User=grove
ExecStart=/usr/bin/python3 watchdog.py --no-redirect
Restart=always

grove-relay.service โ€” WebSocket relay


[Service]
User=grove
ExecStart=/usr/bin/python3 web.py --relay-only --port 5680
Restart=always

Ports

Port Service Access
5678 Dashboard + API Tailscale/LAN only
5679 Watchdog panic page Tailscale/LAN only
5680 Relay WebSocket Can be public (for external peers)

Reverse Proxy (nginx)

For public-facing cells (like familynook):


server {
    listen 443 ssl;
    server_name grove.nook.website;
    
    ssl_certificate /etc/letsencrypt/live/grove.nook.website/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/grove.nook.website/privkey.pem;
    
    client_max_body_size 100M;
    add_header Strict-Transport-Security "max-age=31536000" always;

    # Dashboard
    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_buffering off;
    }

    # SocketIO (live updates)
    location /socket.io {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
    }

    # Relay WebSocket
    location /relay {
        proxy_pass http://127.0.0.1:5680;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }
}

server {
    listen 80;
    server_name grove.nook.website;
    return 301 https://$host$request_uri;
}

SSL with Let's Encrypt


sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d grove.nook.website
# Auto-renewal: systemctl enable certbot.timer

Configuration

~/.grove/config.json key fields:


{
    "self_name": "my-cell",          // How peers see you
    "web_port": 5678,                // Dashboard port
    "web_host": "0.0.0.0",          // Bind address
    "public_url": "https://grove.nook.website",  // Public URL (for invite links)
    "desired_factor": 2,             // Replication target
    "relay_url": "ws://100.96.243.69:5680",      // Relay to connect to
    "relay_host": true,              // Run relay server on this cell
    "storage_cap_gb": 50,            // Max storage to offer peers
    "peers": [...],                  // Peer list
    "preferred_peers": ["alice"],    // Favorite peers
    "watched_dirs": ["/home/user/Photos"],  // Auto-backup folders
    "ai_enabled": true,              // Enable GroveAI
    "ai_model": "nemotron-3-nano-4b-q4_k_m.gguf"  // AI model file
}

Fleet Management

Deploy to all cells

Use deploy.sh script or manual scp + restart:


# Copy updated files
scp web.py grove.py <user>@<host>:~/.grove/

# Restart (non-systemd)
ssh <user>@<host> 'kill $(cat ~/.grove/grove-web.pid); sleep 2; cd ~/.grove && nohup python3 web.py >> /tmp/grove-web.log 2>&1 & echo $! > grove-web.pid'

# Restart (systemd)
ssh <user>@<host> 'sudo systemctl restart grove'

Verify fleet builds


for cell in localhost:5678 100.96.243.69:5678 100.81.15.45:5678; do
  echo "$cell: $(curl -sf http://$cell/api/version | python3 -c 'import sys,json;print(json.load(sys.stdin)["build"])')"
done

Monitoring

Health checks

Logs


Backup & Recovery

Back up keys (critical!)


# Via dashboard: Settings โ†’ Backup Keys (downloads ZIP)
# Via API:
curl -sf http://localhost:5678/api/backup-keys > grove-keys.zip

If you lose ~/.grove/.key, your encrypted files are unrecoverable.

Restore keys


# Via dashboard: Settings โ†’ Restore Keys (upload ZIP)
# Via API:
curl -sf -X POST http://localhost:5678/api/restore-keys -F "file=@grove-keys.zip"

Security Hardening

See SECURITY.md for full details. Quick checklist: