docs: Phase 5 - Comprehensive deployment documentation
- Created detailed srv/README.md with: - Quick start guide - SystemD service setup instructions - Reverse proxy configuration (Caddy & Nginx) - Complete API endpoint reference - Client configuration examples - Troubleshooting guide - Security considerations - Future enhancement roadmap - Updated main README.md with server & sync features - Added sync command quick reference - Documented offline support and conflict resolution
This commit is contained in:
+51
-1
@@ -32,4 +32,54 @@ A task can be recurring. Then we have a template task and instances of that task
|
|||||||
A recurring task is given a status of recurring which hides it from view. The recurring task you create is called the template task, from which recurring tasks instances are created. So the template remains hidden, and the recurring instances that spawn from it are the tasks that you will see and complete.
|
A recurring task is given a status of recurring which hides it from view. The recurring task you create is called the template task, from which recurring tasks instances are created. So the template remains hidden, and the recurring instances that spawn from it are the tasks that you will see and complete.
|
||||||
|
|
||||||
## Storage
|
## Storage
|
||||||
Sqlite store.
|
SQLite database stored in `~/.config/jade/opal.db`
|
||||||
|
|
||||||
|
## Server & Sync
|
||||||
|
|
||||||
|
Opal-task includes a REST API server for syncing tasks across multiple devices.
|
||||||
|
|
||||||
|
### Quick Start
|
||||||
|
|
||||||
|
**Server Setup:**
|
||||||
|
```bash
|
||||||
|
# Build
|
||||||
|
go build -o opal main.go
|
||||||
|
|
||||||
|
# Generate API key
|
||||||
|
./opal server keygen --name "My Device" --db /var/lib/opal/opal.db
|
||||||
|
|
||||||
|
# Start server
|
||||||
|
./opal server start --addr :8080 --db /var/lib/opal/opal.db
|
||||||
|
```
|
||||||
|
|
||||||
|
**Client Setup:**
|
||||||
|
```bash
|
||||||
|
# Configure sync
|
||||||
|
opal sync init --url https://opal.yourdomain.com --key oak_abc123...
|
||||||
|
|
||||||
|
# Sync tasks
|
||||||
|
opal sync now
|
||||||
|
|
||||||
|
# Initial merge (for existing local database)
|
||||||
|
opal sync merge
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sync Commands
|
||||||
|
|
||||||
|
- `opal sync init` - Configure sync with server
|
||||||
|
- `opal sync status` - Show sync configuration and status
|
||||||
|
- `opal sync now` - Bidirectional sync
|
||||||
|
- `opal sync up` - Push local changes to server
|
||||||
|
- `opal sync down` - Pull server changes from server
|
||||||
|
- `opal sync merge` - Initial database merge (for first-time sync)
|
||||||
|
- `opal sync log` - View conflict resolution log
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- **Offline support** - Queue changes when server is unreachable
|
||||||
|
- **Conflict resolution** - Automatic conflict handling (last-write-wins by default)
|
||||||
|
- **Change tracking** - Full change log with configurable retention
|
||||||
|
- **API key authentication** - Secure bcrypt-hashed keys
|
||||||
|
- **Household sharing** - Single shared database for all family members
|
||||||
|
|
||||||
|
See [srv/README.md](srv/README.md) for detailed server deployment instructions.
|
||||||
|
|||||||
+365
-9
@@ -1,12 +1,368 @@
|
|||||||
# Server
|
# Opal-Task Server
|
||||||
Opal-task is now a great task CLI management tool, however I need my task list available elsewhere. For reading, yes, but also modification.
|
|
||||||
|
|
||||||
## Current infrastructure
|
Opal-task server provides a REST API for syncing tasks across multiple devices. This enables household task sharing and access from anywhere.
|
||||||
I host a VPS behind a Caddy reverse proxy. It hosts an authentik instance for SSO to my services.
|
|
||||||
|
|
||||||
## Usecase
|
## Features
|
||||||
- Use within household to share tasks or filters. For our first version we should use one task database and share all data. This might branch off to sharing with access control (sharing specific tags/projects for instance)
|
|
||||||
- CRUD tasks on the go
|
|
||||||
|
|
||||||
## Implementation
|
- **REST API** - Chi-based HTTP API for task management
|
||||||
I was thinking of hosting an api, then building a minimal svelte-kit frontend that could be used as a PWA on our devices. What are your thoughts? We could use oauth for authentication through authentik. for now disregard the front-end implementation.
|
- **API Key Authentication** - Secure authentication using bcrypt-hashed keys
|
||||||
|
- **Bidirectional Sync** - Push and pull changes with conflict resolution
|
||||||
|
- **Offline Support** - Queue changes when server is unreachable
|
||||||
|
- **Change Tracking** - Automatic change log with configurable retention
|
||||||
|
- **Single Database** - Shared household task database (v1)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Build the Binary
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd opal-task
|
||||||
|
go build -o opal main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Generate API Key
|
||||||
|
|
||||||
|
On your server (with direct database access):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./opal server keygen --name "My Device" --db /var/lib/opal/opal.db
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
```
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
API Key Generated Successfully
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
Name: My Device
|
||||||
|
Key: oak_abc123...
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
⚠️ IMPORTANT: Save this key securely!
|
||||||
|
It will not be displayed again.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Start the Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./opal server start --addr :8080 --db /var/lib/opal/opal.db
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Configure Client
|
||||||
|
|
||||||
|
On your local machine or phone:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
opal sync init --url https://opal.yourdomain.com --key oak_abc123...
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use interactive mode:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
opal sync init
|
||||||
|
# Enter server URL: https://opal.yourdomain.com
|
||||||
|
# Enter API key: oak_abc123...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Sync Your Tasks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Full bidirectional sync
|
||||||
|
opal sync now
|
||||||
|
|
||||||
|
# Push local changes only
|
||||||
|
opal sync up
|
||||||
|
|
||||||
|
# Pull server changes only
|
||||||
|
opal sync down
|
||||||
|
|
||||||
|
# Initial merge (for existing local database)
|
||||||
|
opal sync merge --prefer-local
|
||||||
|
```
|
||||||
|
|
||||||
|
## Server Deployment
|
||||||
|
|
||||||
|
### Option 1: SystemD Service (Recommended)
|
||||||
|
|
||||||
|
**1. Install Binary**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build
|
||||||
|
go build -o opal main.go
|
||||||
|
|
||||||
|
# Copy to system location
|
||||||
|
sudo cp opal /usr/local/bin/
|
||||||
|
|
||||||
|
# Create data directory
|
||||||
|
sudo mkdir -p /var/lib/opal
|
||||||
|
sudo chown $USER:$USER /var/lib/opal
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Generate First API Key**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
opal server keygen --name "Admin" --db /var/lib/opal/opal.db
|
||||||
|
# Save the generated key!
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Create SystemD Service**
|
||||||
|
|
||||||
|
Create `/etc/systemd/system/opal.service`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Opal Task API Server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=your-user
|
||||||
|
WorkingDirectory=/var/lib/opal
|
||||||
|
ExecStart=/usr/local/bin/opal server start --addr :8080 --db /var/lib/opal/opal.db
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Enable and Start Service**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable opal
|
||||||
|
sudo systemctl start opal
|
||||||
|
sudo systemctl status opal
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Direct Execution
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run in background
|
||||||
|
nohup ./opal server start --addr :8080 --db /var/lib/opal/opal.db > opal.log 2>&1 &
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reverse Proxy Setup
|
||||||
|
|
||||||
|
### Caddy (Recommended)
|
||||||
|
|
||||||
|
Add to your Caddyfile:
|
||||||
|
|
||||||
|
```
|
||||||
|
opal.yourdomain.com {
|
||||||
|
reverse_proxy localhost:8080
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Reload Caddy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl reload caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nginx
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name opal.yourdomain.com;
|
||||||
|
|
||||||
|
ssl_certificate /path/to/cert.pem;
|
||||||
|
ssl_certificate_key /path/to/key.pem;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:8080;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
All endpoints (except `/health`) require authentication via API key:
|
||||||
|
|
||||||
|
```
|
||||||
|
Authorization: Bearer oak_abc123...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Health Check
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /health
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns server status.
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /tasks - List tasks (with filters)
|
||||||
|
GET /tasks/{uuid} - Get specific task
|
||||||
|
POST /tasks - Create task
|
||||||
|
PUT /tasks/{uuid} - Update task
|
||||||
|
DELETE /tasks/{uuid} - Delete task
|
||||||
|
POST /tasks/{uuid}/complete - Mark task complete
|
||||||
|
POST /tasks/{uuid}/start - Start task
|
||||||
|
POST /tasks/{uuid}/stop - Stop task
|
||||||
|
```
|
||||||
|
|
||||||
|
**Query Parameters for GET /tasks:**
|
||||||
|
- `status` - pending, completed, deleted
|
||||||
|
- `project` - Project name
|
||||||
|
- `priority` - L, M, H, D
|
||||||
|
- `tag` - Tag name (can specify multiple)
|
||||||
|
|
||||||
|
### Tags
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /tasks/{uuid}/tags - Get task tags
|
||||||
|
POST /tasks/{uuid}/tags - Add tag
|
||||||
|
DELETE /tasks/{uuid}/tags/{tag} - Remove tag
|
||||||
|
GET /tags - List all tags
|
||||||
|
```
|
||||||
|
|
||||||
|
### Projects
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /projects - List all projects
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sync
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /sync/changes - Get changes since timestamp
|
||||||
|
POST /sync/push - Push local changes
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Keys
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /auth/keys - List API keys
|
||||||
|
DELETE /auth/keys/{id} - Revoke API key
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client Configuration
|
||||||
|
|
||||||
|
Sync settings are stored in `~/.config/jade/opal.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Task settings
|
||||||
|
default_filter: status:pending
|
||||||
|
default_sort: due,priority
|
||||||
|
color_output: true
|
||||||
|
week_start_day: monday
|
||||||
|
default_due_time: ""
|
||||||
|
|
||||||
|
# Sync settings
|
||||||
|
sync_enabled: true
|
||||||
|
sync_url: https://opal.yourdomain.com
|
||||||
|
sync_api_key: oak_abc123...
|
||||||
|
sync_client_id: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
sync_strategy: last-write-wins
|
||||||
|
sync_queue_offline: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sync Strategies
|
||||||
|
|
||||||
|
- **last-write-wins** (default) - Most recently modified version wins
|
||||||
|
- **server-wins** - Server version always wins
|
||||||
|
- **client-wins** - Client version always wins
|
||||||
|
|
||||||
|
## Change Log Retention
|
||||||
|
|
||||||
|
By default, the change log retains entries for 30 days. This can be configured in the database:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
UPDATE sync_config SET value = '60' WHERE key = 'change_log_retention_days';
|
||||||
|
```
|
||||||
|
|
||||||
|
Or programmatically:
|
||||||
|
|
||||||
|
```go
|
||||||
|
engine.SetChangeLogRetentionDays(60)
|
||||||
|
```
|
||||||
|
|
||||||
|
Cleanup runs manually or can be scheduled:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In your cron or systemd timer
|
||||||
|
./opal server cleanup --db /var/lib/opal/opal.db
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Server won't start
|
||||||
|
|
||||||
|
- Check if port 8080 is already in use: `sudo lsof -i :8080`
|
||||||
|
- Verify database path exists and is writable
|
||||||
|
- Check logs: `journalctl -u opal -f`
|
||||||
|
|
||||||
|
### Client can't connect
|
||||||
|
|
||||||
|
- Test server health: `curl https://opal.yourdomain.com/health`
|
||||||
|
- Verify API key is correct
|
||||||
|
- Check firewall allows traffic on port 8080
|
||||||
|
- Ensure reverse proxy is properly configured
|
||||||
|
|
||||||
|
### Sync conflicts
|
||||||
|
|
||||||
|
- View conflict log: `opal sync log`
|
||||||
|
- Conflicts are automatically resolved based on strategy
|
||||||
|
- By default, last-write-wins is used
|
||||||
|
|
||||||
|
### Offline changes not syncing
|
||||||
|
|
||||||
|
- Check queue status: `opal sync status`
|
||||||
|
- Verify `sync_queue_offline: true` in config
|
||||||
|
- Clear queue if needed: Delete `~/.config/jade/sync_queue.json`
|
||||||
|
|
||||||
|
## Database Schema
|
||||||
|
|
||||||
|
### Core Tables
|
||||||
|
|
||||||
|
- `tasks` - Main task storage
|
||||||
|
- `tags` - Task tags (many-to-many)
|
||||||
|
- `working_set` - Display ID mapping (client-local)
|
||||||
|
|
||||||
|
### Sync Tables
|
||||||
|
|
||||||
|
- `users` - User accounts (currently single shared user)
|
||||||
|
- `api_keys` - Authentication keys
|
||||||
|
- `sync_state` - Per-client sync state
|
||||||
|
- `change_log` - Change tracking (key:value format)
|
||||||
|
- `sync_config` - Server configuration
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
- API keys are hashed with bcrypt before storage
|
||||||
|
- Never log full API keys
|
||||||
|
- Use HTTPS in production (via reverse proxy)
|
||||||
|
- Implement rate limiting for production use
|
||||||
|
- Backup database regularly
|
||||||
|
- Rotate API keys periodically
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
- OAuth2 with Authentik for web/mobile clients
|
||||||
|
- Per-user task separation with sharing
|
||||||
|
- Real-time sync via WebSockets
|
||||||
|
- Web/mobile frontend (SvelteKit PWA)
|
||||||
|
- Access control (share specific tags/projects)
|
||||||
|
- Audit logging
|
||||||
|
- Database backup automation
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues or questions:
|
||||||
|
- Check logs: `journalctl -u opal -f`
|
||||||
|
- View sync status: `opal sync status`
|
||||||
|
- View conflicts: `opal sync log`
|
||||||
|
- Test connectivity: `curl https://opal.yourdomain.com/health`
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Same as opal-task main project.
|
||||||
|
|||||||
Reference in New Issue
Block a user