# Get the install script from an invite link
curl -s https://grove.nook.website/invite/<token> | bash
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
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
| 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) |
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;
}
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d grove.nook.website
# Auto-renewal: systemctl enable certbot.timer
~/.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
}
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'
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
GET /api/health โ disk space, chunk count, peer countGET /api/replication-status โ per-peer replication healthGET /api/route-speeds โ route performance dataweb.py (or systemd journal: journalctl -u grove)~/.grove/peer_health.json
# 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.
# Via dashboard: Settings โ Restore Keys (upload ZIP)
# Via API:
curl -sf -X POST http://localhost:5678/api/restore-keys -F "file=@grove-keys.zip"
See SECURITY.md for full details. Quick checklist:
chmod 600~/.grove directory is chmod 700