479 lines
12 KiB
Markdown
479 lines
12 KiB
Markdown
|
|
# Alfred Proxy - Complete Setup Guide
|
||
|
|
|
||
|
|
**Updated:** 2026-02-04
|
||
|
|
**Status:** ✅ Fully functional with FCM push notifications
|
||
|
|
|
||
|
|
## What This Is
|
||
|
|
|
||
|
|
Alfred Proxy is an authentication and notification bridge that:
|
||
|
|
1. Validates OAuth tokens from the Alfred mobile app (via Authentik)
|
||
|
|
2. Injects OpenClaw gateway tokens (server-side, never exposed)
|
||
|
|
3. Sends Firebase Cloud Messaging push notifications to mobile devices
|
||
|
|
4. Persists FCM tokens across restarts for reliable alarm delivery
|
||
|
|
|
||
|
|
## Quick Start
|
||
|
|
|
||
|
|
### Prerequisites
|
||
|
|
|
||
|
|
- Node.js 24+ installed
|
||
|
|
- OpenClaw gateway running on localhost:18789
|
||
|
|
- Authentik OAuth provider configured
|
||
|
|
- Firebase project with FCM enabled
|
||
|
|
- Google Cloud service account with correct permissions
|
||
|
|
|
||
|
|
### 1. Install Dependencies
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd ~/.openclaw/workspace/alfred-proxy
|
||
|
|
npm install
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Configure Firebase (FCM)
|
||
|
|
|
||
|
|
#### a. Create Service Account
|
||
|
|
|
||
|
|
1. Go to: https://console.cloud.google.com/iam-admin/serviceaccounts?project=YOUR_PROJECT_ID
|
||
|
|
2. Click "Create Service Account"
|
||
|
|
3. Name: `alfred-fcm-server`
|
||
|
|
4. Grant role: **Firebase Admin SDK Administrator Service Agent**
|
||
|
|
- This role includes `cloudmessaging.messages.create` permission
|
||
|
|
- Do NOT use "Firebase Cloud Messaging Admin" (legacy API)
|
||
|
|
5. Download JSON key
|
||
|
|
|
||
|
|
#### b. Enable FCM API
|
||
|
|
|
||
|
|
1. Go to: https://console.cloud.google.com/apis/library/fcm.googleapis.com?project=YOUR_PROJECT_ID
|
||
|
|
2. Click "Enable"
|
||
|
|
|
||
|
|
#### c. Place Service Account Key
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cp ~/Downloads/your-key.json service-account.json
|
||
|
|
chmod 600 service-account.json
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Configure Environment
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Copy example config
|
||
|
|
cp .env.example .env
|
||
|
|
|
||
|
|
# Edit with your settings
|
||
|
|
nano .env
|
||
|
|
```
|
||
|
|
|
||
|
|
**Required settings:**
|
||
|
|
```bash
|
||
|
|
PROXY_PORT=18790
|
||
|
|
OPENCLAW_URL=ws://127.0.0.1:18789
|
||
|
|
OPENCLAW_TOKEN=your-openclaw-token # From ~/.openclaw/openclaw.json
|
||
|
|
AUTHENTIK_URL=https://auth.your-domain.com
|
||
|
|
AUTHENTIK_CLIENT_ID=your-client-id
|
||
|
|
REQUIRE_AUTH=true
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Start the Proxy
|
||
|
|
|
||
|
|
**Development (auto-reload):**
|
||
|
|
```bash
|
||
|
|
npm run dev
|
||
|
|
```
|
||
|
|
|
||
|
|
**Production (systemd service):**
|
||
|
|
```bash
|
||
|
|
# Copy service file
|
||
|
|
cp alfred-proxy.service ~/.config/systemd/user/
|
||
|
|
|
||
|
|
# Start and enable
|
||
|
|
systemctl --user daemon-reload
|
||
|
|
systemctl --user enable alfred-proxy.service
|
||
|
|
systemctl --user start alfred-proxy.service
|
||
|
|
|
||
|
|
# Check status
|
||
|
|
systemctl --user status alfred-proxy.service
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. Verify Setup
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Check proxy is running
|
||
|
|
curl http://localhost:18790/health
|
||
|
|
# Expected: {"status":"ok","service":"alfred-proxy"}
|
||
|
|
|
||
|
|
# Check logs
|
||
|
|
tail -f /tmp/alfred-proxy.log
|
||
|
|
|
||
|
|
# Look for:
|
||
|
|
# [firebase] Firebase Admin SDK initialized
|
||
|
|
# [alfred-proxy] Service started successfully
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6. Test FCM Notifications
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Send test alarm
|
||
|
|
./alfred-notify --alarm "Test alarm!"
|
||
|
|
|
||
|
|
# Check logs for success
|
||
|
|
tail -f /tmp/alfred-proxy.log | grep fcm
|
||
|
|
# Expected: [fcm] Successfully sent X message(s)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Features
|
||
|
|
|
||
|
|
### ✅ Working Features
|
||
|
|
|
||
|
|
- **OAuth Authentication**: Validates Authentik tokens via userinfo endpoint
|
||
|
|
- **Token Injection**: Injects OpenClaw gateway token server-side
|
||
|
|
- **FCM Push Notifications**: Send notifications even when app is closed
|
||
|
|
- **Token Persistence**: FCM tokens persist across proxy restarts (`fcm-tokens.json`)
|
||
|
|
- **Dual Delivery**: WebSocket (if connected) + FCM (always)
|
||
|
|
- **CLI Wrapper**: `alfred-notify` command for easy notification sending
|
||
|
|
- **Auto-Reconnection**: App reconnects and re-registers FCM token automatically
|
||
|
|
|
||
|
|
### Notification Types
|
||
|
|
|
||
|
|
- **Alarm**: High-priority, wakes device
|
||
|
|
- **Alert**: Standard priority notification
|
||
|
|
- **Silent**: Vibrate only or completely silent
|
||
|
|
|
||
|
|
## Usage
|
||
|
|
|
||
|
|
### Send Notification via CLI
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Simple alarm
|
||
|
|
./alfred-notify --alarm "Wake up!"
|
||
|
|
|
||
|
|
# Notification with custom title
|
||
|
|
./alfred-notify --title "Kitchen Timer" "Oven is ready"
|
||
|
|
|
||
|
|
# Silent notification
|
||
|
|
./alfred-notify --no-sound --no-vibrate "Background task complete"
|
||
|
|
```
|
||
|
|
|
||
|
|
Run `./alfred-notify --help` for all options.
|
||
|
|
|
||
|
|
### Send Notification via HTTP API
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X POST http://localhost:18790/api/notify \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{
|
||
|
|
"notificationType": "alarm",
|
||
|
|
"title": "Test",
|
||
|
|
"message": "This is a test alarm",
|
||
|
|
"priority": "high",
|
||
|
|
"sound": true,
|
||
|
|
"vibrate": true
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"success": true,
|
||
|
|
"clients": 1, // WebSocket clients notified
|
||
|
|
"fcm": 1 // FCM devices notified
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Schedule Alarms with Cron
|
||
|
|
|
||
|
|
Use OpenClaw's cron tool to schedule alarms:
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// Example: 5 minute alarm
|
||
|
|
{
|
||
|
|
"name": "5 minute alarm",
|
||
|
|
"schedule": {
|
||
|
|
"kind": "at",
|
||
|
|
"atMs": Date.now() + (5 * 60 * 1000)
|
||
|
|
},
|
||
|
|
"payload": {
|
||
|
|
"kind": "agentTurn",
|
||
|
|
"message": "Run: alfred-notify --alarm '⏰ 5 minute alarm is up!'",
|
||
|
|
"deliver": false
|
||
|
|
},
|
||
|
|
"sessionTarget": "isolated"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critical:** Always use `sessionTarget: "isolated"` + `agentTurn` for alarms.
|
||
|
|
|
||
|
|
## Monitoring
|
||
|
|
|
||
|
|
### Check Proxy Status
|
||
|
|
|
||
|
|
```bash
|
||
|
|
systemctl --user status alfred-proxy.service
|
||
|
|
```
|
||
|
|
|
||
|
|
### View Logs
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Live tail
|
||
|
|
tail -f /tmp/alfred-proxy.log
|
||
|
|
|
||
|
|
# Check for FCM activity
|
||
|
|
grep "fcm" /tmp/alfred-proxy.log | tail -20
|
||
|
|
|
||
|
|
# Check for errors
|
||
|
|
grep "Error\|error" /tmp/alfred-proxy.log | tail -20
|
||
|
|
```
|
||
|
|
|
||
|
|
### Check Token Persistence
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cat fcm-tokens.json
|
||
|
|
```
|
||
|
|
|
||
|
|
Should show registered FCM tokens:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"user-id-hash": [
|
||
|
|
"fcm-token-string"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Monitor Notification Delivery
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Send test
|
||
|
|
./alfred-notify "Test"
|
||
|
|
|
||
|
|
# Check logs
|
||
|
|
tail -f /tmp/alfred-proxy.log
|
||
|
|
```
|
||
|
|
|
||
|
|
Look for:
|
||
|
|
```
|
||
|
|
[notify] Broadcasting notification: type=alert title="Alfred" message="Test"
|
||
|
|
[notify] Sent notification to X connected client(s)
|
||
|
|
[fcm] Sending push notification to Y registered device(s)
|
||
|
|
[fcm] Successfully sent Y message(s)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Permission Denied Error
|
||
|
|
|
||
|
|
**Error:**
|
||
|
|
```
|
||
|
|
[fcm] Error: Permission 'cloudmessaging.messages.create' denied
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution:**
|
||
|
|
1. Verify service account role is **Firebase Admin SDK Administrator Service Agent**
|
||
|
|
- Go to: https://console.cloud.google.com/iam-admin/iam
|
||
|
|
- Find service account: `alfred-fcm-server@...`
|
||
|
|
- Should have role: `roles/firebase.sdkAdminServiceAgent`
|
||
|
|
2. Verify FCM API is enabled
|
||
|
|
3. Regenerate service account key if > 1 hour old
|
||
|
|
4. Restart proxy after updating key
|
||
|
|
|
||
|
|
### No Tokens Registered
|
||
|
|
|
||
|
|
**Error:**
|
||
|
|
```
|
||
|
|
[fcm] No FCM tokens registered, skipping push notification
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution:**
|
||
|
|
1. Open Alfred mobile app
|
||
|
|
2. Verify connection (should show "Connected ✅")
|
||
|
|
3. Check logs for token registration:
|
||
|
|
```
|
||
|
|
[fcm] Registering token for user...
|
||
|
|
[fcm] Saved tokens to disk
|
||
|
|
```
|
||
|
|
4. Verify `fcm-tokens.json` file exists and has content
|
||
|
|
|
||
|
|
### Tokens Lost After Restart
|
||
|
|
|
||
|
|
**This should NOT happen if setup is correct.**
|
||
|
|
|
||
|
|
If tokens are lost:
|
||
|
|
1. Check `fcm-tokens.json` exists in proxy directory
|
||
|
|
2. Check file permissions: `chmod 600 fcm-tokens.json`
|
||
|
|
3. Verify logs show: `[fcm] Loaded X token(s) for Y user(s) from disk`
|
||
|
|
4. Check app sends token on every connection (should be automatic)
|
||
|
|
|
||
|
|
### OAuth Token Expired
|
||
|
|
|
||
|
|
**Error:**
|
||
|
|
```
|
||
|
|
[auth] Authentik validation failed: 401
|
||
|
|
[proxy] Invalid OAuth token, rejecting connection
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution:**
|
||
|
|
1. App needs to implement token refresh (future fix)
|
||
|
|
2. For now: logout and login again in the app
|
||
|
|
3. Or disable auth temporarily: `REQUIRE_AUTH=false` in `.env`
|
||
|
|
|
||
|
|
### App Not Receiving Notifications
|
||
|
|
|
||
|
|
**Checklist:**
|
||
|
|
1. ✅ Proxy running? `systemctl --user status alfred-proxy.service`
|
||
|
|
2. ✅ FCM tokens registered? `cat fcm-tokens.json`
|
||
|
|
3. ✅ Service account has correct role? Check IAM
|
||
|
|
4. ✅ FCM API enabled? Check APIs & Services
|
||
|
|
5. ✅ Test notification succeeds? `./alfred-notify "Test"`
|
||
|
|
6. ✅ Logs show success? `grep "Successfully sent" /tmp/alfred-proxy.log`
|
||
|
|
|
||
|
|
If all checks pass but still no notifications:
|
||
|
|
- Check app is installed and has notification permissions
|
||
|
|
- Check device has network connectivity
|
||
|
|
- Check Firebase Cloud Messaging isn't blocked by firewall
|
||
|
|
- Try uninstall/reinstall app to get fresh FCM token
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
```
|
||
|
|
┌──────────────────────┐
|
||
|
|
│ Alfred Mobile App │
|
||
|
|
│ (Android/OAuth) │
|
||
|
|
└──────────┬───────────┘
|
||
|
|
│ wss:// + OAuth token
|
||
|
|
▼
|
||
|
|
┌──────────────────────┐
|
||
|
|
│ Alfred Proxy │
|
||
|
|
│ - Validate OAuth │
|
||
|
|
│ - Inject OpenClaw │
|
||
|
|
│ token │
|
||
|
|
│ - Store FCM tokens │
|
||
|
|
│ - Send FCM push │
|
||
|
|
└──────────┬───────────┘
|
||
|
|
│
|
||
|
|
┌──────┴───────┐
|
||
|
|
│ │
|
||
|
|
▼ ▼
|
||
|
|
┌────────┐ ┌────────────┐
|
||
|
|
│OpenClaw│ │ Firebase │
|
||
|
|
│Gateway │ │ FCM │
|
||
|
|
│(local) │ └─────┬──────┘
|
||
|
|
└────────┘ │
|
||
|
|
▼
|
||
|
|
┌──────────────┐
|
||
|
|
│ Mobile Device│
|
||
|
|
│ (push notif) │
|
||
|
|
└──────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
## Files & Directories
|
||
|
|
|
||
|
|
```
|
||
|
|
alfred-proxy/
|
||
|
|
├── server.js # Main proxy server
|
||
|
|
├── alfred-notify # CLI wrapper for notifications
|
||
|
|
├── .env # Configuration (git-ignored)
|
||
|
|
├── service-account.json # Firebase credentials (git-ignored)
|
||
|
|
├── fcm-tokens.json # Persisted FCM tokens (git-ignored)
|
||
|
|
├── package.json # Dependencies
|
||
|
|
├── alfred-proxy.service # Systemd service file
|
||
|
|
├── README.md # Project overview
|
||
|
|
├── SETUP_COMPLETE.md # This file
|
||
|
|
├── FCM_SETUP.md # Firebase setup (see alfred-mobile/)
|
||
|
|
└── /tmp/alfred-proxy.log # Runtime logs
|
||
|
|
```
|
||
|
|
|
||
|
|
## Security
|
||
|
|
|
||
|
|
### Sensitive Files (Never Commit)
|
||
|
|
|
||
|
|
- `.env` - Contains API tokens
|
||
|
|
- `service-account.json` - Firebase credentials
|
||
|
|
- `fcm-tokens.json` - User device tokens
|
||
|
|
|
||
|
|
All added to `.gitignore`.
|
||
|
|
|
||
|
|
### Best Practices
|
||
|
|
|
||
|
|
1. **Restrict service account permissions**: Use minimal role (Firebase Admin SDK Administrator Service Agent)
|
||
|
|
2. **Rotate keys regularly**: Generate new service account key every 90 days
|
||
|
|
3. **File permissions**: `chmod 600` for sensitive files
|
||
|
|
4. **Network isolation**: OpenClaw runs on localhost only, not exposed
|
||
|
|
5. **OAuth validation**: All connections validated with Authentik before access
|
||
|
|
|
||
|
|
## Performance
|
||
|
|
|
||
|
|
### Resource Usage
|
||
|
|
|
||
|
|
- **Memory**: ~50-100MB RSS
|
||
|
|
- **CPU**: <1% idle, ~5% during notification bursts
|
||
|
|
- **Network**: Minimal (WebSocket keepalives + FCM API calls)
|
||
|
|
|
||
|
|
### Scalability
|
||
|
|
|
||
|
|
- Supports multiple connected devices simultaneously
|
||
|
|
- FCM batch sending for efficiency
|
||
|
|
- Token persistence reduces startup overhead
|
||
|
|
|
||
|
|
### Monitoring
|
||
|
|
|
||
|
|
Monitor with:
|
||
|
|
```bash
|
||
|
|
# Process stats
|
||
|
|
ps aux | grep "node server.js"
|
||
|
|
|
||
|
|
# Connection count
|
||
|
|
ss -tn | grep :18790 | wc -l
|
||
|
|
|
||
|
|
# Log size
|
||
|
|
du -h /tmp/alfred-proxy.log
|
||
|
|
```
|
||
|
|
|
||
|
|
## Updating
|
||
|
|
|
||
|
|
### Update Proxy Code
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd ~/.openclaw/workspace/alfred-proxy
|
||
|
|
git pull # If using git
|
||
|
|
npm install # If dependencies changed
|
||
|
|
systemctl --user restart alfred-proxy.service
|
||
|
|
```
|
||
|
|
|
||
|
|
### Update Service Account Key
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Download new key from Google Cloud Console
|
||
|
|
cp ~/Downloads/new-key.json service-account.json
|
||
|
|
chmod 600 service-account.json
|
||
|
|
systemctl --user restart alfred-proxy.service
|
||
|
|
|
||
|
|
# Verify
|
||
|
|
tail -f /tmp/alfred-proxy.log | grep firebase
|
||
|
|
# Should show: [firebase] Firebase Admin SDK initialized
|
||
|
|
```
|
||
|
|
|
||
|
|
## Support & Documentation
|
||
|
|
|
||
|
|
- **FCM Setup**: See `../alfred-mobile/FCM_SETUP.md`
|
||
|
|
- **Agent Integration**: See `../alfred-mobile/AGENT_TOOLS.md`
|
||
|
|
- **OpenClaw Docs**: https://docs.openclaw.ai
|
||
|
|
- **Firebase Docs**: https://firebase.google.com/docs/cloud-messaging
|
||
|
|
|
||
|
|
## Version History
|
||
|
|
|
||
|
|
### 2026-02-04: FCM Token Persistence
|
||
|
|
- Added token save/load to `fcm-tokens.json`
|
||
|
|
- App sends token on every connection
|
||
|
|
- Fixed Firebase IAM permissions (correct role)
|
||
|
|
- Created `alfred-notify` CLI wrapper
|
||
|
|
- Updated all documentation
|
||
|
|
|
||
|
|
### 2026-02-03: Initial Release
|
||
|
|
- OAuth authentication via Authentik
|
||
|
|
- OpenClaw token injection
|
||
|
|
- Basic FCM notification support
|
||
|
|
- WebSocket proxy functionality
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Status**: ✅ Production Ready
|
||
|
|
**Last Updated**: 2026-02-04
|
||
|
|
**Maintainer**: Josh (Shadow)
|