Remote-Pulse Telegram Approval Flow Setup¶
F4-6 implementation: Defense-in-depth human-in-the-loop approval for destructive commands in sensitive groups (prod, iarq).
Overview¶
When Remote-Pulse server receives a destructive command (e.g., reboot, pkg_install, exec_shell) for a host in a sensitive group, it triggers a Telegram approval flow:
- Server creates command record with
approval_token - Server fires webhook to n8n
- n8n sends Telegram message with inline keyboard [Approve / Reject]
- Admin clicks button within 5 minutes
- n8n calls server callback endpoint (
/v1/admin/commands/approve/{token}or/reject/{token}) - Server marks command as
human_approved=trueorrejected_reason="..." - Agent polls and executes approved command (or skips rejected)
TTL: 5 minutes. After timeout, command auto-rejects with rejected_reason="approval_timeout".
Prerequisites¶
PRE-1: Telegram Bot¶
Decision pending (ADR-0008 Open Question #12):
- Option A: Reuse @Veraclawd_bot (token 8597804826:AAEW...XJQ_LCA)
- Option B: Create dedicated @RemotePulse_bot
Current recommendation: Reuse Veraclawd bot to avoid bot proliferation. Alerts already go there; approval requests fit same persona.
Token storage: n8n credential telegram-bot-credentials (type "Telegram API")
PRE-2: Telegram Chat ID¶
Admin chat ID for approval messages: 730947207 (Ramon's Telegram user ID).
Environment variable: TELEGRAM_CHAT_ID_ADMINS=730947207 in n8n LXC 200.
PRE-3: Server Public URL¶
n8n needs to call server callback endpoints. Server must be reachable from n8n LXC 200.
Options:
- Public URL: https://rp.monxas.casa (via Caddy HA LXC 270/271)
- Internal URL: http://192.168.0.196:8080 (direct to LXC 280)
Recommendation: Use internal URL for callbacks (faster, no TLS overhead). Caddy only needed for external enrollment.
Environment variable: RP_SERVER_PUBLIC_URL=http://192.168.0.196:8080 in n8n.
PRE-4: n8n Webhook URL¶
Server needs to fire approval request webhook to n8n.
Webhook path: /webhook/rp-approval (configured in n8n workflow)
Full URL: http://192.168.0.200:5678/webhook/rp-approval
Environment variable in server LXC 280:
Setup Steps¶
Step 1: Import n8n Workflow¶
-
Copy workflow JSON to n8n host:
scp homelab-infra/n8n/workflows/rp-approval-flow.json [email protected]:/tmp/ -
Import via n8n UI:
- Navigate to
http://192.168.0.200:5678 - Workflows → Import from File
- Select
/tmp/rp-approval-flow.json -
Click "Import"
-
Verify workflow has 8 nodes:
- Webhook Trigger
- Format Telegram Message
- Send Telegram Message
- Respond to Webhook
- Telegram Callback Trigger
- Parse Callback Data
- Call Server Endpoint
- Edit Telegram Message
Step 2: Configure n8n Credentials¶
- Create Telegram Bot credential (if not exists):
- Settings → Credentials → Add Credential
- Type: "Telegram API"
- Name:
telegram-bot-credentials - Access Token:
8597804826:AAEW...XJQ_LCA(or new bot token) -
Save
-
Set environment variables in n8n LXC 200:
ssh [email protected] # Edit /etc/systemd/system/n8n.service or docker-compose.yml # Add: Environment="TELEGRAM_CHAT_ID_ADMINS=730947207" Environment="RP_SERVER_PUBLIC_URL=http://192.168.0.196:8080" systemctl daemon-reload systemctl restart n8n # or docker-compose restart n8n
Step 3: Configure Server Environment¶
In Remote-Pulse server LXC 280:
ssh [email protected]
# Edit /etc/rp/server.env or systemd unit
cat >> /etc/rp/server.env <<EOF
RP_N8N_APPROVAL_WEBHOOK_URL=http://192.168.0.200:5678/webhook/rp-approval
EOF
systemctl restart remote-pulse-server
Step 4: Activate Workflow¶
- In n8n UI, open "Remote-Pulse Approval Flow"
- Click "Activate" toggle (top right)
- Verify webhook is listening:
http://192.168.0.200:5678/webhook/rp-approval
Step 5: Test End-to-End¶
Test 1: Webhook Delivery¶
# From any host with curl
curl -X POST http://192.168.0.200:5678/webhook/rp-approval \
-H "Content-Type: application/json" \
-d '{
"event": "approval_required",
"command_id": "test-123",
"command_type": "reboot",
"command_payload": {"delay_s": 30},
"host_hostname": "test-host",
"host_group": "prod",
"issued_by": "admin",
"issued_at": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"approval_token": "00000000-0000-0000-0000-000000000001",
"approval_url_accept": "http://192.168.0.196:8080/v1/admin/commands/approve/00000000-0000-0000-0000-000000000001",
"approval_url_reject": "http://192.168.0.196:8080/v1/admin/commands/reject/00000000-0000-0000-0000-000000000001",
"expires_at": "'$(date -u -v+5M +%Y-%m-%dT%H:%M:%SZ)'"
}'
Expected: Telegram message appears in chat 730947207 with inline buttons.
Test 2: Approval Click¶
- Click "✓ Approve" button in Telegram
- Check server logs:
ssh [email protected] journalctl -u remote-pulse-server -f | grep approval - Verify Telegram message edits to "✅ Approved by @username at ..."
Test 3: Full Integration¶
# Issue a destructive command from server CLI
rp admin exec --host=test-host-prod --cmd="echo test"
# Expected flow:
# 1. Command created with approval_token
# 2. Telegram message sent
# 3. Click Approve
# 4. Command marked human_approved=true
# 5. Agent polls and executes
Configuration Reference¶
Server Settings¶
| Setting | Default | Description |
|---|---|---|
RP_N8N_APPROVAL_WEBHOOK_URL |
None (required) | n8n webhook endpoint for approval requests |
RP_SERVER_PUBLIC_URL |
https://rp.monxas.casa |
Public URL for callback endpoints |
RP_APPROVAL_TTL_MINUTES |
5 |
Approval token TTL before auto-reject |
n8n Environment Variables¶
| Variable | Default | Description |
|---|---|---|
TELEGRAM_CHAT_ID_ADMINS |
None (required) | Admin chat ID for approval messages |
RP_SERVER_PUBLIC_URL |
https://rp.monxas.casa |
Server base URL for callbacks |
Telegram Bot Setup (if creating new bot)¶
- Message
@BotFatheron Telegram /newbot- Name:
RemotePulse(display name) - Username:
RemotePulse_bot(must end in_bot) - Copy token:
123456789:ABCdefGHIjklMNOpqrsTUVwxyz - Get chat ID:
Webhook Security¶
Defense Layers¶
- Single-use tokens:
approval_tokenis UUID v4, cleared after first use - TTL enforcement: Tokens expire 5min after
approval_requested_at - IP allowlist (TODO F5): n8n LXC 200 IP
192.168.0.200allowlisted in server firewall - HMAC signing (TODO F8): Optional HMAC-SHA256 signature on callbacks for cryptographic verification
IP Allowlist Configuration (Future)¶
HMAC Signing (Future)¶
If paranoid, add HMAC verification:
-
Generate shared secret:
-
Configure in both server and n8n:
-
n8n signs callback payload; server verifies before accepting.
Troubleshooting¶
No Telegram message appears¶
Symptoms: Server logs "approval webhook delivered", but no Telegram message.
Checks:
1. n8n workflow active? Check UI toggle.
2. Telegram credentials valid? Test with /getMe API call.
3. Chat ID correct? Send test message manually.
4. n8n logs: docker logs n8n or journalctl -u n8n.
Fix:
# Test Telegram bot manually
curl -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" \
-d "chat_id=730947207&text=Test"
Callback endpoint returns 404¶
Symptoms: Telegram button click, but server returns "Approval request not found".
Checks:
1. approval_token in database? Query:
approval_token is NULL after first callback.
3. Token expired? Check approval_requested_at + 5min < now().
Fix: Re-issue command, click faster.
Callback endpoint returns 403 "expired"¶
Symptoms: Click approve after 5+ minutes.
Fix: Commands auto-reject after 5min. Re-issue command and approve faster.
Server webhook fails to reach n8n¶
Symptoms: Server logs "approval webhook failed".
Checks:
1. n8n reachable? curl http://192.168.0.200:5678/webhook/rp-approval
2. Firewall blocking? Check LXC 280 → LXC 200 connectivity.
3. Webhook URL correct in server config?
Fix:
# Test connectivity
ssh [email protected]
curl -v http://192.168.0.200:5678/webhook/rp-approval
Workflow executes but callback fails¶
Symptoms: Telegram message sent, button clicked, but server never receives callback.
Checks:
1. RP_SERVER_PUBLIC_URL correct in n8n?
2. Server API running? curl http://192.168.0.196:8080/health
3. n8n execution logs: Check "Call Server Endpoint" node output.
Fix: Update RP_SERVER_PUBLIC_URL in n8n environment, restart workflow.
Maintenance¶
Cleanup Expired Tokens¶
Server runs automatic cleanup every 1 minute (scheduled task):
# Auto-rejects commands with approval_token older than 5min
UPDATE commands
SET rejected_reason = 'approval_timeout',
approval_token = NULL
WHERE approval_token IS NOT NULL
AND approval_requested_at < NOW() - INTERVAL '5 minutes'
AND human_approved = FALSE
AND rejected_reason IS NULL;
Manual cleanup:
Monitoring¶
Prometheus metrics (TODO F5-5):
- rp_approval_requested_total (Counter)
- rp_approval_approved_total (Counter, label: approver_id)
- rp_approval_rejected_total (Counter)
- rp_approval_timeout_total (Counter)
Grafana alert (TODO F5):
- rp_approval_timeout_total > 3 in 1h → warning ("High approval timeout rate")
Audit Trail¶
All approval decisions logged in:
1. Postgres: commands table with approved_by and rejected_reason
2. Loki: Server structured logs with approval_token, command_id, approver_id
Query audit trail:
SELECT
id,
command_type,
issued_by,
approved_by,
rejected_reason,
approval_requested_at,
approval_responded_at
FROM commands
WHERE approval_requested_at IS NOT NULL
ORDER BY approval_requested_at DESC
LIMIT 50;
Integration with Local Policy¶
Approval flow complements agent local policy enforcement (F4-5):
- Server-side: Command requires Telegram approval (per ADR-0008 table)
- Agent-side: Even with
human_approved=true, agent still checks/etc/rp/allow-remote-execflag
Example: exec_shell on prod host:
- Server: requires Telegram approval → sends to n8n → admin approves → human_approved=true
- Agent: receives signed command with human_approved=true → checks local flag /etc/rp/allow-remote-exec → if missing, rejects with local_policy_deny
Defense-in-depth: Server compromise + Telegram compromise still can't execute destructive commands on agents without local flags.
Future Enhancements¶
F5: Multi-admin approval¶
Support multiple admins with role-based approval:
- TELEGRAM_CHAT_ID_ADMINS=730947207,987654321 (comma-separated)
- n8n sends to group chat, first responder wins
F8: Audit log secondary sink¶
Replicate approval decisions to Loki for forensics:
F8: Approval history dashboard¶
Grafana panel showing: - Approval rate by command_type - Median approval latency (time to click) - Top approvers by count
References¶
- ADR-0008 §13: Telegram approval flow specification
- F4-5 local-policy: Agent-side enforcement docs
- F4-4 signing: Ed25519 command signing
- n8n workflow:
/Users/ramonkamibayashicarrera/homelab-infra/n8n/workflows/rp-approval-flow.json - Server router:
monxas-remote-pulse/server/src/rp_server/routers/approvals.py