chore: move processed issue files to created/ [skip ci]

This commit is contained in:
github-actions[bot]
2025-12-13 02:17:33 +00:00
parent 5b54b6582c
commit ade66af7da
8 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
Tasks
Repository: Wikid82/Charon
Branch: feature/beta-release
Purpose
-------
Create a tracked issue and sub-tasks to validate ACL-related changes introduced on the `feature/beta-release` branch. This file records the scope, test steps, and sub-issues so we can open a GitHub issue later or link this file in the issue body.
Top-level checklist
- [ ] Open GitHub Issue "ACL: Test and validate ACL changes (feature/beta-release)" and link this file
- [ ] Assign owner and target date
Sub-tasks (suggested GitHub issue checklist items)
1) Unit & Service Tests
- [ ] Add/verify unit tests for `internal/services/access_list_service.go` CRUD + validation
- [ ] Add tests for `internal/api/handlers/access_list_handler.go` endpoints (create/list/get/update/delete)
- Acceptance: all handler tests pass and coverage for `internal/api/handlers` rises by at least 3%.
2) Integration Tests
- [ ] Test ACL interactions with proxy hosts: ensure blocked/allowed behavior when ACLs applied to hosts
- [ ] Test ACL import via Caddy import workflow (multi-site) — ensure imported ACLs attach correctly
- Acceptance: end-to-end requests are blocked/allowed per ACL rules in an integration harness.
3) UI & API Validation
- [ ] Validate frontend UI toggles for ACL enable/disable reflect DB state
- [ ] Verify API endpoints that toggle ACL mode return correct status and persist in `settings`
- Acceptance: toggles update DB and the UI shows consistent state after refresh.
4) Security & Edge Cases
- [ ] Test denied webhook payloads / WAF interactions when ACLs are present
- [ ] Confirm rate-limit and CrowdSec interactions do not conflict with ACL rules
- Acceptance: no regressions found; documented edge cases.
5) Documentation & Release Notes
- [ ] Update `docs/features.md` with any behavior changes
- [ ] Add a short note in release notes describing ACL test coverage and migration steps
Manual Test Steps (quick guide)
- Set up local environment:
1. `cd backend && go run ./cmd/api` (or use docker compose)
2. Run frontend dev server: `cd frontend && npm run dev`
- Create an ACL via API or UI; attach it to a Proxy Host; verify request behavior.
- Import Caddyfiles (single & multi-site) with ACL directives and validate mapping.
Issue metadata (suggested)
- Title: ACL: Test and validate ACL changes (feature/beta-release)
- Labels: testing, needs-triage, acl, regression
- Assignees: @<owner-placeholder>
- Milestone: to be set
Notes
- Keep this file as the canonical checklist and paste into the GitHub issue body when opening the issue.

View File

@@ -0,0 +1,50 @@
### Additional Security Threats to Consider
**1. Supply Chain Attacks**
- **Threat:** Compromised Docker images, npm packages, Go modules
- **Current Protection:** ❌ None
- **Recommendation:** Add Trivy scanning (already in CI) + SBOM generation
**2. DNS Hijacking / Cache Poisoning**
- **Threat:** Attacker redirects DNS queries to malicious servers
- **Current Protection:** ❌ None (relies on system DNS resolver)
- **Recommendation:** Document use of encrypted DNS (DoH/DoT) in deployment guide
**3. TLS Downgrade Attacks**
- **Threat:** Force clients to use weak TLS versions
- **Current Protection:** ✅ Caddy enforces TLS 1.2+ by default
- **Recommendation:** Document minimum TLS version in security.md
**4. Certificate Transparency (CT) Log Poisoning**
- **Threat:** Attacker registers fraudulent certs for your domains
- **Current Protection:** ❌ None
- **Recommendation:** Add CT log monitoring (future feature)
**5. Privilege Escalation (Container Escape)**
- **Threat:** Attacker escapes Docker container to host OS
- **Current Protection:** ⚠️ Partial (Docker security best practices)
- **Recommendation:** Document running with least-privilege, read-only root filesystem
**6. Session Hijacking / Cookie Theft**
- **Threat:** Steal user session tokens via XSS or network sniffing
- **Current Protection:** ✅ HTTPOnly cookies, Secure flag, SameSite (verify implementation)
- **Recommendation:** Add CSP (Content Security Policy) headers
**7. Timing Attacks (Cryptographic Side-Channel)**
- **Threat:** Infer secrets by measuring response times
- **Current Protection:** ❌ Unknown (need bcrypt timing audit)
- **Recommendation:** Use constant-time comparison for tokens
**Enterprise-Level Security Gaps:**
- **Missing:** Security Incident Response Plan (SIRP)
- **Missing:** Automated security update notifications
- **Missing:** Multi-factor authentication (MFA) for admin accounts (Use Authentik via built in. No extra external containers. Consider adding SSO as well just for Charon. These are not meant to pass auth to Proxy Hosts. Charon is a reverse proxy, not a secure dashboard.)
- **Missing:** Audit logging for compliance (GDPR, SOC 2)

View File

@@ -0,0 +1,261 @@
# Sub-Issues for Bulk ACL Testing
## Parent Issue
[Link to main testing issue]
---
## Sub-Issue #1: Basic Functionality Testing
**Title**: `[Bulk ACL Testing] Basic Functionality - Selection and Application`
**Labels**: `testing`, `manual-testing`, `bulk-acl`
**Description**:
Test the core functionality of the bulk ACL feature - selecting hosts and applying access lists.
**Test Checklist:**
- [ ] Navigate to Proxy Hosts page
- [ ] Verify checkbox column appears in table
- [ ] Select individual hosts using checkboxes
- [ ] Verify "Select All" checkbox works correctly
- [ ] Confirm selection count displays accurately
- [ ] Click "Bulk Actions" button - modal should appear
- [ ] Select an ACL from dropdown - hosts should update
- [ ] Verify toast notification shows success message
- [ ] Confirm hosts table refreshes with updated ACL assignments
- [ ] Check database to verify `access_list_id` fields updated
**Expected Results:**
- All checkboxes functional
- Selection count accurate
- Modal displays correctly
- ACL applies to all selected hosts
- Database reflects changes
**Test Environment:** Local development
---
## Sub-Issue #2: ACL Removal Testing
**Title**: `[Bulk ACL Testing] ACL Removal Functionality`
**Labels**: `testing`, `manual-testing`, `bulk-acl`
**Description**:
Test the ability to remove access lists from multiple hosts simultaneously.
**Test Checklist:**
- [ ] Select hosts that have ACLs assigned
- [ ] Open Bulk Actions modal
- [ ] Select "🚫 Remove Access List" option
- [ ] Confirm removal dialog appears
- [ ] Proceed with removal
- [ ] Verify toast shows "Access list removed from X host(s)"
- [ ] Confirm hosts no longer have ACL assigned in UI
- [ ] Check database to verify `access_list_id` is NULL
**Expected Results:**
- Removal option clearly visible
- Confirmation dialog prevents accidental removal
- All selected hosts have ACL removed
- Database updated correctly (NULL values)
**Test Environment:** Local development
---
## Sub-Issue #3: Error Handling Testing
**Title**: `[Bulk ACL Testing] Error Handling and Edge Cases`
**Labels**: `testing`, `manual-testing`, `bulk-acl`, `error-handling`
**Description**:
Test error scenarios and edge cases to ensure graceful degradation.
**Test Checklist:**
- [ ] Select multiple hosts including one that doesn't exist
- [ ] Apply ACL via bulk action
- [ ] Verify toast shows partial success: "Updated X host(s), Y failed"
- [ ] Confirm successful hosts were updated
- [ ] Test with no hosts selected (button should not appear)
- [ ] Test with empty ACL list (dropdown should show appropriate message)
- [ ] Disconnect backend - verify network error handling
- [ ] Test applying invalid ACL ID (edge case)
**Expected Results:**
- Partial failures handled gracefully
- Clear error messages displayed
- No data corruption on partial failures
- Network errors caught and reported
**Test Environment:** Local development + simulated failures
---
## Sub-Issue #4: UI/UX Testing
**Title**: `[Bulk ACL Testing] UI/UX and Usability`
**Labels**: `testing`, `manual-testing`, `bulk-acl`, `ui-ux`
**Description**:
Test the user interface and experience aspects of the bulk ACL feature.
**Test Checklist:**
- [ ] Verify checkboxes align properly in table
- [ ] Test checkbox hover states
- [ ] Verify "Bulk Actions" button appears/disappears based on selection
- [ ] Test modal appearance and dismissal (click outside, ESC key)
- [ ] Verify dropdown styling and readability
- [ ] Test loading state (`isBulkUpdating`) - button should show "Updating..."
- [ ] Verify selection persists during table sorting
- [ ] Test selection persistence during table filtering (if applicable)
- [ ] Verify toast notifications don't overlap
- [ ] Test on mobile viewport (responsive design)
**Expected Results:**
- Clean, professional UI
- Intuitive user flow
- Proper loading states
- Mobile-friendly
- Accessible (keyboard navigation)
**Test Environment:** Local development (multiple screen sizes)
---
## Sub-Issue #5: Integration Testing
**Title**: `[Bulk ACL Testing] Integration and Performance`
**Labels**: `testing`, `manual-testing`, `bulk-acl`, `integration`, `performance`
**Description**:
Test the feature in realistic scenarios and with varying data loads.
**Test Checklist:**
- [ ] Create new ACL, immediately apply to multiple hosts
- [ ] Verify Caddy config reloads once (not per host)
- [ ] Test with 1 host selected
- [ ] Test with 10+ hosts selected (performance)
- [ ] Test with 50+ hosts selected (edge case)
- [ ] Apply ACL, then immediately remove it (rapid operations)
- [ ] Apply different ACLs sequentially to same host group
- [ ] Delete a host that's selected, then bulk apply ACL
- [ ] Disable an ACL, verify it doesn't appear in dropdown
- [ ] Test concurrent user scenarios (multi-tab if possible)
**Expected Results:**
- Single Caddy reload per bulk operation
- Performance acceptable up to 50+ hosts
- No race conditions with rapid operations
- Graceful handling of deleted/disabled entities
**Test Environment:** Docker production build
---
## Sub-Issue #6: Cross-Browser Testing
**Title**: `[Bulk ACL Testing] Cross-Browser Compatibility`
**Labels**: `testing`, `manual-testing`, `bulk-acl`, `cross-browser`
**Description**:
Verify the feature works across all major browsers and devices.
**Test Checklist:**
- [ ] Chrome/Chromium (latest)
- [ ] Firefox (latest)
- [ ] Safari (macOS/iOS)
- [ ] Edge (latest)
- [ ] Mobile Chrome (Android)
- [ ] Mobile Safari (iOS)
**Expected Results:**
- Feature works identically across all browsers
- No CSS layout issues
- No JavaScript errors in console
- Touch interactions work on mobile
**Test Environment:** Multiple browsers/devices
---
## Sub-Issue #7: Regression Testing
**Title**: `[Bulk ACL Testing] Regression Testing - Existing Features`
**Labels**: `testing`, `manual-testing`, `bulk-acl`, `regression`
**Description**:
Ensure the new bulk ACL feature doesn't break existing functionality.
**Test Checklist:**
- [ ] Verify individual proxy host edit still works
- [ ] Confirm single-host ACL assignment unchanged
- [ ] Test proxy host creation with ACL pre-selected
- [ ] Verify ACL deletion prevents assignment
- [ ] Confirm existing ACL features unaffected:
- [ ] IP-based rules
- [ ] Geo-blocking rules
- [ ] Local network only rules
- [ ] Test IP functionality
- [ ] Verify certificate assignment still works
- [ ] Test proxy host enable/disable toggle
**Expected Results:**
- Zero regressions
- All existing features work as before
- No performance degradation
- No new bugs introduced
**Test Environment:** Docker production build
---
## Creating Sub-Issues on GitHub
For each sub-issue above:
1. Go to the repository's Issues tab
2. Click "New Issue"
3. Copy the content from the relevant section
4. Add to the parent issue description: "Part of #[parent-issue-number]"
5. Assign appropriate labels
6. Set milestone to `v0.2.0-beta.2`
7. Assign to tester if known
## Testing Progress Tracking
Update the parent issue with:
```markdown
## Sub-Issues Progress
- [ ] #XXX - Basic Functionality Testing
- [ ] #XXX - ACL Removal Testing
- [ ] #XXX - Error Handling Testing
- [ ] #XXX - UI/UX Testing
- [ ] #XXX - Integration Testing
- [ ] #XXX - Cross-Browser Testing
- [ ] #XXX - Regression Testing
```

View File

@@ -0,0 +1,223 @@
# Issue: Test Bulk ACL Application Feature
**Labels**: `testing`, `enhancement`, `needs-testing`
**Milestone**: v0.2.0-beta.2
**Priority**: High
## Description
Comprehensive testing required for the newly implemented Bulk ACL (Access Control List) application feature. This feature allows users to apply or remove access lists from multiple proxy hosts simultaneously, replacing the previous manual per-host workflow.
## Feature Overview
**Implementation PR**: [Link to PR]
The bulk ACL feature introduces:
- Multi-select checkboxes in Proxy Hosts table
- Bulk Actions button with ACL selection modal
- Backend endpoint: `PUT /api/v1/proxy-hosts/bulk-update-acl`
- Comprehensive error handling for partial failures
## Testing Scope
### Backend Testing ✅ (Completed)
- [x] Unit tests for `BulkUpdateACL` handler (5 tests)
- [x] Success scenario: Apply ACL to multiple hosts
- [x] Success scenario: Remove ACL (null value)
- [x] Error handling: Partial failures (some hosts fail)
- [x] Validation: Empty UUIDs array
- [x] Validation: Invalid JSON payload
- **Coverage**: 82.2% maintained
### Frontend Testing ✅ (Completed)
- [x] Unit tests for `bulkUpdateACL` API client (5 tests)
- [x] Unit tests for `useBulkUpdateACL` hook (5 tests)
- [x] Build verification (TypeScript compilation)
- **Coverage**: 86.06% (improved from 85.57%)
### Manual Testing 🔴 (Required)
#### Sub-Issue #1: Basic Functionality Testing
**Checklist:**
- [ ] Navigate to Proxy Hosts page
- [ ] Verify checkbox column appears in table
- [ ] Select individual hosts using checkboxes
- [ ] Verify "Select All" checkbox works correctly
- [ ] Confirm selection count displays accurately
- [ ] Click "Bulk Actions" button - modal should appear
- [ ] Select an ACL from dropdown - hosts should update
- [ ] Verify toast notification shows success message
- [ ] Confirm hosts table refreshes with updated ACL assignments
- [ ] Check database to verify `access_list_id` fields updated
#### Sub-Issue #2: ACL Removal Testing
**Checklist:**
- [ ] Select hosts that have ACLs assigned
- [ ] Open Bulk Actions modal
- [ ] Select "🚫 Remove Access List" option
- [ ] Confirm removal dialog appears
- [ ] Proceed with removal
- [ ] Verify toast shows "Access list removed from X host(s)"
- [ ] Confirm hosts no longer have ACL assigned in UI
- [ ] Check database to verify `access_list_id` is NULL
#### Sub-Issue #3: Error Handling Testing
**Checklist:**
- [ ] Select multiple hosts including one that doesn't exist
- [ ] Apply ACL via bulk action
- [ ] Verify toast shows partial success: "Updated X host(s), Y failed"
- [ ] Confirm successful hosts were updated
- [ ] Test with no hosts selected (button should not appear)
- [ ] Test with empty ACL list (dropdown should show appropriate message)
- [ ] Disconnect backend - verify network error handling
- [ ] Test applying invalid ACL ID (edge case)
#### Sub-Issue #4: UI/UX Testing
**Checklist:**
- [ ] Verify checkboxes align properly in table
- [ ] Test checkbox hover states
- [ ] Verify "Bulk Actions" button appears/disappears based on selection
- [ ] Test modal appearance and dismissal (click outside, ESC key)
- [ ] Verify dropdown styling and readability
- [ ] Test loading state (`isBulkUpdating`) - button should show "Updating..."
- [ ] Verify selection persists during table sorting
- [ ] Test selection persistence during table filtering (if applicable)
- [ ] Verify toast notifications don't overlap
- [ ] Test on mobile viewport (responsive design)
#### Sub-Issue #5: Integration Testing
**Checklist:**
- [ ] Create new ACL, immediately apply to multiple hosts
- [ ] Verify Caddy config reloads once (not per host)
- [ ] Test with 1 host selected
- [ ] Test with 10+ hosts selected (performance)
- [ ] Test with 50+ hosts selected (edge case)
- [ ] Apply ACL, then immediately remove it (rapid operations)
- [ ] Apply different ACLs sequentially to same host group
- [ ] Delete a host that's selected, then bulk apply ACL
- [ ] Disable an ACL, verify it doesn't appear in dropdown
- [ ] Test concurrent user scenarios (multi-tab if possible)
#### Sub-Issue #6: Cross-Browser Testing
**Checklist:**
- [ ] Chrome/Chromium (latest)
- [ ] Firefox (latest)
- [ ] Safari (macOS/iOS)
- [ ] Edge (latest)
- [ ] Mobile Chrome (Android)
- [ ] Mobile Safari (iOS)
#### Sub-Issue #7: Regression Testing
**Checklist:**
- [ ] Verify individual proxy host edit still works
- [ ] Confirm single-host ACL assignment unchanged
- [ ] Test proxy host creation with ACL pre-selected
- [ ] Verify ACL deletion prevents assignment
- [ ] Confirm existing ACL features unaffected:
- [ ] IP-based rules
- [ ] Geo-blocking rules
- [ ] Local network only rules
- [ ] Test IP functionality
- [ ] Verify certificate assignment still works
- [ ] Test proxy host enable/disable toggle
## Test Environments
1. **Local Development**
- Docker: `docker-compose.local.yml`
- Backend: `http://localhost:8080`
- Frontend: `http://localhost:5173`
2. **Docker Production Build**
- Docker: `docker-compose.yml`
- Full stack: `http://localhost:80`
3. **VPS/Staging** (if available)
- Remote environment testing
- Real SSL certificates
- Multiple concurrent users
## Success Criteria
- ✅ All manual test checklists completed
- ✅ No critical bugs found
- ✅ Performance acceptable with 50+ hosts
- ✅ UI/UX meets design standards
- ✅ Cross-browser compatibility confirmed
- ✅ No regressions in existing features
- ✅ Documentation updated (if needed)
## Known Limitations
1. Selection state resets on page navigation
2. No "Select hosts without ACL" filter (potential enhancement)
3. No bulk operations from Access Lists page (future feature)
4. Maximum practical limit untested (100+ hosts)
## Related Files
**Backend:**
- `backend/internal/api/handlers/proxy_host_handler.go`
- `backend/internal/api/handlers/proxy_host_handler_test.go`
**Frontend:**
- `frontend/src/pages/ProxyHosts.tsx`
- `frontend/src/api/proxyHosts.ts`
- `frontend/src/hooks/useProxyHosts.ts`
- `frontend/src/api/__tests__/proxyHosts-bulk.test.ts`
- `frontend/src/hooks/__tests__/useProxyHosts-bulk.test.tsx`
**Documentation:**
- `BULK_ACL_FEATURE.md`
## Testing Timeline
**Suggested Schedule:**
- Day 1: Sub-issues #1-3 (Basic + Error Handling)
- Day 2: Sub-issues #4-5 (UI/UX + Integration)
- Day 3: Sub-issues #6-7 (Cross-browser + Regression)
## Reporting Issues
When bugs are found:
1. Create a new bug report with `[Bulk ACL]` prefix
2. Reference this testing issue
3. Include screenshots/videos
4. Provide reproduction steps
5. Tag with `bug`, `bulk-acl` labels
## Notes
- Feature has 100% backend test coverage for new code
- Feature has 100% frontend test coverage for new code
- Performance testing with large datasets (100+ hosts) recommended
- Consider adding E2E tests with Playwright/Cypress in future
---
**Implementation Date**: November 27, 2025
**Developer**: @copilot
**Reviewer**: TBD
**Tester**: TBD

View File

@@ -0,0 +1,185 @@
# Hecate: Tunnel & Pathway Manager
## 1. Overview
**Hecate** is the internal module within Charon responsible for managing third-party tunneling services. It serves as the "Goddess of Pathways," allowing Charon to route traffic not just to local ports, but through encrypted tunnels to remote networks without exposing ports on the public internet.
## 2. Architecture
Hecate is not a separate binary; it is a **Go package** (`internal/hecate`) running within the main Charon daemon.
### 2.1 The Provider Interface
To support multiple services (Tailscale, Cloudflare, Netbird), Hecate uses a strict Interface pattern.
```go
type TunnelProvider interface {
// Name returns the unique ID of the provider (e.g., "tailscale-01")
Name() string
// Status returns the current health (Connected, Connecting, Error)
Status() TunnelState
// Start initiates the tunnel daemon
Start(ctx context.Context) error
// Stop gracefully terminates the connection
Stop() error
// GetAddress returns the internal IP/DNS routed through the tunnel
GetAddress() string
}
```
### 2.2 Supported Integrations (Phase 1)
#### Cloudflare Tunnels (cloudflared)
- **Mechanism**: Charon manages the `cloudflared` binary via `os/exec`.
- **Config**: User provides the Token via the UI.
- **Outcome**: Exposes Charon directly to the edge without opening port 80/443 on the router.
#### Tailscale / Headscale
- **Mechanism**: Uses `tsnet` (Tailscale's Go library) to embed the node directly into Charon, OR manages the `tailscaled` socket.
- **Outcome**: Charon becomes a node on the Mesh VPN.
## 3. Dashboard Implementation (Unified UI)
**Hecate does NOT have a separate "Tunnels" tab.**
Instead, it is fully integrated into the **Remote Servers** dashboard to provide a unified experience for managing connectivity.
### 3.1 "Add Server" Workflow
When a user clicks "Add Server" in the dashboard, they are presented with a **Connection Type** dropdown that determines how Charon reaches the target.
#### Connection Types
1. **Direct / Manual (Existing)**
- **Use Case**: The server is on the same LAN or reachable via a static IP/DNS.
- **Fields**: `Host`, `Port`, `TLS Toggle`.
- **Backend**: Standard TCP dialer.
2. **Orthrus Agent (New)**
- **Use Case**: The server is behind a NAT/Firewall and cannot accept inbound connections.
- **Workflow**:
- User selects "Orthrus Agent".
- Charon generates a unique `AUTH_KEY`.
- UI displays a `docker-compose.yml` snippet pre-filled with the key and `CHARON_LINK`.
- User deploys the agent on the remote host.
- Hecate waits for the incoming WebSocket connection.
3. **Cloudflare Tunnel (Future)**
- **Use Case**: Exposing a service via Cloudflare's edge network.
- **Fields**: `Tunnel Token`.
- **Backend**: Hecate spawns/manages the `cloudflared` process.
### 3.2 Hecate's Role
Hecate acts as the invisible backend engine for these non-direct connection types. It manages the lifecycle of the tunnels and agents, while the UI simply shows the status (Online/Offline) of the "Server".
### 3.3 Install Options & UX Snippets
When a user selects `Orthrus Agent` or chooses a `Managed Tunnel` flow, the UI should offer multiple installation options so both containerized and non-containerized environments are supported.
Provide these install options as tabs/snippets in the `Add Server` flow:
- **Docker Compose**: A one-file snippet the user can copy/paste (already covered in `orthrus` docs).
- **Standalone Binary + systemd**: Download URL, SHA256, install+`systemd` unit snippet for Linux hosts.
- **Tarball + Installer**: For offline installs with checksum verification.
- **Deb / RPM**: `apt`/`yum` install commands (when packages are available).
- **Homebrew**: `brew tap` + `brew install` for macOS / Linuxbrew users.
- **Kubernetes DaemonSet**: YAML for fleet or cluster-based deployments.
UI Requirements:
- Show the generated `AUTH_KEY` prominently and a single-copy button.
- Provide checksum and GPG signature links for any downloadable artifact.
- Offer a small troubleshooting panel with commands like `journalctl -u orthrus -f` and `systemctl status orthrus`.
- Allow the user to copy a recommended sidecar snippet that runs a VPN client (e.g., Tailscale) next to Orthrus when desired.
## 4. API Endpoints
- `GET /api/hecate/status` - Returns health of all tunnels.
- `POST /api/hecate/configure` - Accepts auth tokens and provider types.
- `POST /api/hecate/logs` - Streams logs from the underlying tunnel binary (e.g., cloudflared logs) for debugging.
## 5. Security (Cerberus Integration)
Traffic entering through Hecate must still pass through Cerberus.
- Tunnels terminate **before** the middleware chain.
- Requests from a Cloudflare Tunnel are tagged `source:tunnel` and subjected to the same WAF rules as standard traffic.
## 6. Implementation Details
### 6.1 Process Supervision
Hecate will act as a process supervisor for external binaries like `cloudflared`.
- **Supervisor Pattern**: A `TunnelManager` struct will maintain a map of active `TunnelProvider` instances.
- **Lifecycle**:
- On startup, `TunnelManager` loads enabled configs from the DB.
- It launches the binary using `os/exec`.
- It monitors the process state. If the process exits unexpectedly, it triggers a **Restart Policy** (Exponential Backoff: 5s, 10s, 30s, 1m).
- **Graceful Shutdown**: When Charon shuts down, Hecate must send `SIGTERM` to all child processes and wait (with timeout) for them to exit.
### 6.2 Secrets Management
API tokens and sensitive credentials must not be stored in plaintext.
- **Encryption**: Sensitive fields (like Cloudflare Tokens) will be encrypted at rest in the SQLite database using AES-GCM.
- **Key Management**: An encryption key will be generated on first run and stored in `data/keys/hecate.key` (secured with 600 permissions), or provided via `CHARON_SECRET_KEY` env var.
### 6.3 Logging & Observability
- **Capture**: The `TunnelProvider` implementation will attach to the `Stdout` and `Stderr` pipes of the child process.
- **Storage**:
- **Hot Logs**: A circular buffer (Ring Buffer) in memory (last 1000 lines) for real-time dashboard viewing.
- **Cold Logs**: Rotated log files stored in `data/logs/tunnels/<provider>.log`.
- **Streaming**: The frontend will consume logs via a WebSocket endpoint (`/api/ws/hecate/logs/:id`) or Server-Sent Events (SSE) to display real-time output.
### 6.4 Frontend Components
- **TunnelStatusBadge**: Visual indicator (Green=Connected, Yellow=Starting, Red=Error/Stopped).
- **LogViewer**: A terminal-like component (using `xterm.js` or a virtualized list) to display the log stream.
- **ConfigForm**: A dynamic form that renders fields based on the selected provider (e.g., "Token" for Cloudflare, "Auth Key" for Tailscale).
## 7. Database Schema
We will introduce a new GORM model `TunnelConfig` in `internal/models`.
```go
package models
import (
"time"
"github.com/google/uuid"
"gorm.io/datatypes"
)
type TunnelProviderType string
const (
ProviderCloudflare TunnelProviderType = "cloudflare"
ProviderTailscale TunnelProviderType = "tailscale"
)
type TunnelConfig struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey" json:"id"`
Name string `gorm:"not null" json:"name"` // User-friendly name (e.g., "Home Lab Tunnel")
Provider TunnelProviderType `gorm:"not null" json:"provider"`
// EncryptedCredentials stores the API token or Auth Key.
// It is encrypted at rest and decrypted only when starting the process.
EncryptedCredentials []byte `gorm:"not null" json:"-"`
// Configuration stores provider-specific settings (JSON).
// e.g., Cloudflare specific flags, region settings, etc.
Configuration datatypes.JSON `json:"configuration"`
IsActive bool `gorm:"default:false" json:"is_active"` // User's desired state
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
```

View File

@@ -0,0 +1,255 @@
# Orthrus: Remote Socket Proxy Agent
## 1. Overview
**Orthrus** is a lightweight, standalone agent designed to run on remote servers. Named after the brother of Cerberus, its job is to guard the remote resource and securely transport it back to Charon.
It eliminates the need for SSH tunneling or complex port forwarding by utilizing the tunneling protocols managed by Hecate.
## 2. Operational Logic
Orthrus operates in **Reverse Mode**. It does not listen on a public port. Instead, it dials *out* to the tunneling network to connect with Charon.
++-
### 2.1 Core Functions
1. **Docker Socket Proxy:** Securely proxies the remote server's `/var/run/docker.sock` so Charon can auto-discover containers on the remote host.
2. **Service Proxy:** Proxies specific localhost ports (e.g., a database on port 5432) over the tunnel.
## 3. Technical Implementation
### 3.1 Tech Stack
* **Language:** Go (Golang)
* **Base Image:** `scratch` or `alpine` (Goal: < 20MB image size)
### 3.2 Configuration (Environment Variables)
Orthrus is configured entirely via Environment Variables for easy Docker Compose deployment.
| Variable | Description |
| :--- | :--- |
| `ORTHRUS_NAME` | Unique identifier for this agent (e.g., `vps-london-01`) |
| `ORTHRUS_MODE` | `socket` (Docker Socket) or `port` (Specific Port) |
| `CHARON_LINK` | The IP/DNS of the main Charon server (e.g., `100.x.y.z:8080` or `charon.example.com`) |
| `AUTH_KEY` | A shared secret or JWT generated by Charon to authorize this agent |
### 3.3 External Connectivity
**Orthrus does NOT manage VPNs or network tunnels internally.**
It relies entirely on the host operating system for network connectivity.
1. **User Responsibility**: The user must ensure the host running Orthrus can reach the `CHARON_LINK` address.
2. **VPNs**: If you are using Tailscale, WireGuard, or ZeroTier, you must install and configure the VPN client on the **Host OS** (or a sidecar container). Orthrus simply dials the IP provided in `CHARON_LINK`.
3. **Reverse Mode**: Orthrus initiates the connection. Charon waits for the incoming handshake. This means you do not need to open inbound ports on the Orthrus side, but Charon must be reachable.
### 3.4 The "Leash" Protocol (Communication)
Orthrus communicates with Charon via a custom gRPC stream or WebSocket called "The Leash."
1. **Handshake**: Orthrus connects to `Charon:InternalIP`.
2. **Auth**: Orthrus presents the `AUTH_KEY`.
3. **Registration**: Orthrus tells Charon: *"I have access to Docker Network X and Port Y."*
4. **Tunneling**: Charon requests a resource; Orthrus pipes the data securely over "The Leash."
## 4. Deployment Example (Docker Compose)
```yaml
services:
orthrus:
image: wikid82/orthrus:latest
container_name: orthrus-agent
restart: always
environment:
- ORTHRUS_NAME=remote-media-server
- CHARON_LINK=100.x.y.z:8080
- AUTH_KEY=ch_xxxxx_secret
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
# No ports required!
```
## 5. Security Considerations
* **Read-Only Socket**: By default, Orthrus mounts the Docker socket as Read-Only to prevent Charon (or a compromised Charon) from destroying the remote server.
* **Mutual TLS (mTLS)**: All communication between Charon and Orthrus should be encrypted via mTLS if not running inside an encrypted VPN (like Tailscale).
## 6. Implementation Details
### 6.1 Communication Architecture
Orthrus uses a **Reverse Tunnel** architecture established via **WebSockets** with **Yamux** multiplexing.
1. **Transport**: Secure WebSocket (`wss://`) initiates the connection from Orthrus to Charon. This bypasses inbound firewall rules on the remote network.
2. **Multiplexing**: [Yamux](https://github.com/hashicorp/yamux) is used over the WebSocket stream to create multiple logical channels.
* **Control Channel (Stream ID 0)**: Handles heartbeats, configuration updates, and command signals.
* **Data Channels (Stream ID > 0)**: Ephemeral streams created for each proxied request (e.g., a single HTTP request to the Docker socket or a TCP connection to a database).
### 6.2 Authentication & Security
* **Token-Based Handshake**: The `AUTH_KEY` is passed in the `Authorization` header during the WebSocket Upgrade request.
* **mTLS (Mutual TLS)**:
* **Charon as CA**: Charon maintains an internal Certificate Authority.
* **Enrollment**: On first connect with a valid `AUTH_KEY`, Orthrus generates a private key and sends a CSR. Charon signs it and returns the certificate.
* **Rotation**: Orthrus monitors certificate expiry and initiates a renewal request over the Control Channel 24 hours before expiration.
* **Encryption**: All traffic is TLS 1.3 encrypted.
### 6.3 Docker Socket Proxying (The "Muzzle")
To prevent security risks, Orthrus does not blindly pipe traffic to `/var/run/docker.sock`. It implements an application-level filter (The "Muzzle"):
1. **Parser**: Intercepts HTTP requests destined for the socket.
2. **Allowlist**: Only permits safe methods/endpoints (e.g., `GET /v1.xx/containers/json`, `GET /v1.xx/info`).
3. **Blocking**: Rejects `POST`, `DELETE`, `PUT` requests (unless explicitly configured to allow specific actions like "Restart Container") with a `403 Forbidden`.
### 6.4 Heartbeat & Health
* **Mechanism**: Orthrus sends a custom "Ping" packet over the Control Channel every 5 seconds.
* **Timeout**: Charon expects a "Ping" within 10 seconds. If missed, the agent is marked `Offline`.
* **Reconnection**: Orthrus implements exponential backoff (1s, 2s, 4s... max 30s) to reconnect if the link is severed.
## 7. Protocol Specification ("The Leash")
### 7.1 Handshake
```http
GET /api/v1/orthrus/connect HTTP/1.1
Host: charon.example.com
Upgrade: websocket
Connection: Upgrade
Authorization: Bearer <AUTH_KEY>
X-Orthrus-Version: 1.0.0
X-Orthrus-ID: <ORTHRUS_NAME>
```
### 7.2 Message Types (Control Channel)
Messages are Protobuf-encoded for efficiency.
* `HEARTBEAT`: `{ timestamp: int64, load_avg: float, memory_usage: int }`
* `PROXY_REQUEST`: Sent by Charon to request a new stream. `{ stream_id: int, target_type: "docker"|"tcp", target_addr: "localhost:5432" }`
* `CONFIG_UPDATE`: Sent by Charon to update allowlists or rotation policies.
### 7.3 Data Flow
1. **Charon** receives a request for a remote container (e.g., user views logs).
2. **Charon** sends `PROXY_REQUEST` on Control Channel.
3. **Orthrus** accepts, opens a new Yamux stream.
4. **Orthrus** dials the local Docker socket.
5. **Orthrus** pipes the stream, applying "The Muzzle" filter in real-time.
## 8. Repository Structure (Monorepo)
Orthrus resides in the **same repository** as Charon to ensure protocol synchronization and simplified CI/CD.
### 8.1 Directory Layout
To maintain a lightweight footprint (< 20MB), Orthrus uses a separate Go module within the `agent/` directory. This prevents it from inheriting Charon's heavy backend dependencies (GORM, SQLite, etc.).
```text
/projects/Charon
├── go.work # Manages the workspace (includes ./backend and ./agent)
├── backend/ # The Main Server (Heavy)
│ ├── go.mod
│ └── ...
├── agent/ # Orthrus (Lightweight)
│ ├── go.mod # Separate dependencies (Standard Lib + Yamux)
│ ├── main.go
│ └── Dockerfile # Separate build process
└── protocol/ # Shared Definitions (Protobufs)
├── go.mod
└── leash.proto
```
### 8.2 Build Strategy
* **Charon**: Built from `backend/Dockerfile`.
* **Orthrus**: Built from `agent/Dockerfile`.
* **CI/CD**: A single GitHub Action workflow builds and pushes both images (`charon:latest` and `orthrus:latest`) synchronously.
## 9. Packaging & Install Options
Orthrus should be distributed in multiple formats so users can choose one that fits their environment and security posture.
### 9.1 Supported Distribution Formats
* **Docker / Docker Compose**: easiest for container-based hosts.
* **Standalone static binary (recommended)**: small, copy to `/usr/local/bin`, run via `systemd`.
* **Deb / RPM packages**: for managed installs via `apt`/`yum`.
* **Homebrew formula**: for macOS / Linuxbrew users.
* **Tarball with installer**: for offline or custom installs.
* **Kubernetes DaemonSet**: for fleet deployment inside clusters.
### 9.2 Quick Install Snippets (copyable)
1) Docker Compose
```yaml
version: "3.8"
services:
orthrus:
image: wikid82/orthrus:latest
restart: always
environment:
- ORTHRUS_NAME=remote-media-server
- CHARON_LINK=100.x.y.z:8080
- AUTH_KEY=REPLACE_WITH_AUTH_KEY
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
```
2) Standalone binary + `systemd` (Linux)
```bash
# download and install
curl -L https://example.com/orthrus/latest/orthrus-linux-amd64 -o /usr/local/bin/orthrus
chmod +x /usr/local/bin/orthrus
# systemd unit (/etc/systemd/system/orthrus.service)
cat > /etc/systemd/system/orthrus.service <<'EOF'
[Unit]
Description=Orthrus agent
After=network.target
[Service]
Environment=ORTHRUS_NAME=remote-media-server
Environment=CHARON_LINK=100.x.y.z:8080
Environment=AUTH_KEY=REPLACE_WITH_AUTH_KEY
ExecStart=/usr/local/bin/orthrus
Restart=on-failure
User=root
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now orthrus
```
3) Tarball + install script
```bash
curl -L -o orthrus.tar.gz https://example.com/orthrus/vX.Y.Z/orthrus-linux-amd64.tar.gz
sha256sum orthrus.tar.gz # compare with UI-provided hash
tar -xzf orthrus.tar.gz -C /usr/local/bin
chmod +x /usr/local/bin/orthrus
# then use the systemd unit above
```
4) Homebrew (macOS / Linuxbrew)
```
brew tap wikid82/charon
brew install orthrus
```
5) Kubernetes DaemonSet
Provide a DaemonSet YAML referencing the `orthrus` image and the required env vars (`AUTH_KEY`, `CHARON_LINK`), optionally mounting the Docker socket or using hostNetworking.
### 9.3 Security & UX Notes
* Provide SHA256 checksums and GPG signatures for binary downloads.
* Avoid recommending `curl | sh`; prefer explicit steps and checksum verification.
* The Hecate UI should present each snippet as a selectable tab with a copy button and an inline checksum.
* Offer a one-click `AUTH_KEY` regenerate action in the UI and mark old keys revoked.

View File

@@ -0,0 +1,187 @@
# Plex Remote Access Helper & CGNAT Solver
> **GitHub Issue Template** - Copy this content to create a new GitHub issue
---
## Issue Title
`Plex Remote Access Helper & CGNAT Solver`
## Labels
`beta`, `feature`, `plus`, `ui`, `caddy`
---
## Description
Implement a "Plex Remote Access Helper" feature that assists users stuck behind CGNAT (Carrier-Grade NAT) to properly configure their Plex Media Server for remote streaming via a reverse proxy like Caddy. This feature addresses the common pain point of Plex remote access failures when users cannot open ports due to ISP limitations.
## Parent Issue
Extends #44 (Tailscale Network Integration) and #43 (Remote Servers Management)
## Why This Feature?
- **CGNAT is increasingly common** - Many ISPs (especially mobile carriers like T-Mobile) use Carrier-Grade NAT, preventing users from forwarding ports
- **Plex is one of the most popular homelab applications** - A significant portion of Charon users will have Plex
- **Manual configuration is error-prone** - Users often struggle with the correct Caddy configuration and Plex settings
- **Tailscale/VPN integration makes this possible** - With #44, users can access their home network, but Plex requires specific proxy headers for proper remote client handling
- **User story origin** - This feature was conceived from a real user experience solving CGNAT issues with Plex + Tailscale
## Use Cases
1. **T-Mobile/Starlink Home Internet users** - Cannot port forward, need VPN tunnel + reverse proxy
2. **Apartment/Dorm residents** - Shared internet without port access
3. **Privacy-conscious users** - Prefer VPN tunnel over exposing ports
4. **Multi-server Plex setups** - Proxying to multiple Plex instances
## Tasks
- [ ] Design "Plex Mode" toggle or "Media Server Helper" option in proxy host creation
- [ ] Implement automatic header injection for Plex compatibility:
- `X-Forwarded-For` - Client's real IP address
- `X-Forwarded-Proto` - HTTPS
- `X-Real-IP` - Client IP
- `X-Plex-Client-Identifier` - Passthrough
- [ ] Create "External Domain" text input for Plex custom URL setting
- [ ] Generate copy-paste snippet for Plex Settings → Network → Custom server access URLs
- [ ] Add Plex-specific Caddy configuration template
- [ ] Implement WebSocket support toggle (required for Plex Companion)
- [ ] Create validation/test button to verify proxy is working
- [ ] Add documentation/guide for CGNAT + Tailscale + Plex setup
- [ ] Implement connection type detection (show if traffic appears Local vs Remote in proxy logs)
- [ ] Add warning about bandwidth limiting implications when headers are missing
## Acceptance Criteria
- [ ] User can enable "Plex Mode" when creating a proxy host
- [ ] Correct headers are automatically added to Caddy config
- [ ] Copy-paste snippet generated for Plex custom URL setting
- [ ] WebSocket connections work for Plex Companion features
- [ ] Documentation explains full CGNAT + Tailscale + Plex workflow
- [ ] Remote streams correctly show as "Remote" in Plex dashboard (not "Local")
- [ ] Works with both HTTP and HTTPS upstream Plex servers
## Technical Considerations
### Caddy Configuration Template
```caddyfile
plex.example.com {
reverse_proxy localhost:32400 {
# Required headers for proper Plex remote access
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up X-Real-IP {remote_host}
# Preserve Plex-specific headers
header_up X-Plex-Client-Identifier {header.X-Plex-Client-Identifier}
header_up X-Plex-Device {header.X-Plex-Device}
header_up X-Plex-Device-Name {header.X-Plex-Device-Name}
header_up X-Plex-Platform {header.X-Plex-Platform}
header_up X-Plex-Platform-Version {header.X-Plex-Platform-Version}
header_up X-Plex-Product {header.X-Plex-Product}
header_up X-Plex-Token {header.X-Plex-Token}
header_up X-Plex-Version {header.X-Plex-Version}
# WebSocket support for Plex Companion
transport http {
read_buffer 8192
}
}
}
```
### Plex Settings Required
Users must configure in Plex Settings → Network:
- **Secure connections**: Preferred (not Required, to allow proxy)
- **Custom server access URLs**: `https://plex.example.com:443`
### Integration with Existing Features
- Leverage Remote Servers (#43) for Plex server discovery
- Use Tailscale integration (#44) for CGNAT bypass
- Apply to Cloudflare Tunnel (#47) for additional NAT traversal option
### Header Behavior Notes
- Without `X-Forwarded-For`: Plex sees all traffic as coming from the proxy's IP (e.g., Tailscale 100.x.x.x)
- This may cause Plex to treat remote traffic as "Local," bypassing bandwidth limits
- Users should be warned about this behavior in the UI
## UI/UX Design Notes
### Proxy Host Creation Form
Add a collapsible "Media Server Settings" section:
```
☑ Enable Plex Mode
External Domain for Plex: [ plex.example.com ]
[📋 Copy Plex Custom URL]
→ https://plex.example.com:443
Add this URL to Plex Settings → Network → Custom server access URLs
☑ Forward client IP headers (recommended)
☑ Enable WebSocket support
```
### Quick Start Template
In Onboarding Wizard (#30), add "Plex" as a Quick Start template option:
- Pre-configures port 32400
- Enables Plex Mode automatically
- Provides step-by-step instructions
## Documentation Sections to Add
1. **CGNAT Explained** - What is CGNAT and why it blocks remote access
2. **Tailscale + Plex Setup Guide** - Complete walkthrough
3. **Troubleshooting Remote Access** - Common issues and solutions
4. **Local vs Remote Traffic** - Explaining header behavior
5. **Bandwidth Limiting Gotcha** - Why headers matter for throttling
## Priority
Medium - Valuable user experience improvement, builds on #44
## Milestone
Beta
## Related Issues
- #44 (Tailscale Network Integration) - Provides the VPN tunnel
- #43 (Remote Servers Management) - Server discovery
- #47 (Cloudflare Tunnel Integration) - Alternative NAT traversal
- #30 (Onboarding Wizard) - Quick Start templates
## Future Extensions
- Support for other media servers (Jellyfin, Emby)
- Automatic Plex server detection via UPnP/SSDP
- Integration with Tautulli for monitoring
- Plex claim token setup assistance
---
## How to Create This Issue
1. Go to <https://github.com/Wikid82/charon/issues/new>
2. Use title: `Plex Remote Access Helper & CGNAT Solver`
3. Add labels: `beta`, `feature`, `plus`, `ui`, `caddy`
4. Copy the content from "## Description" through "## Future Extensions"
5. Submit the issue
---
*Issue specification created: 2025-11-27*
*Origin: Gemini-assisted Plex remote streaming solution using Tailscale*

View File

@@ -0,0 +1,364 @@
# Enhancement: Rotating Thematic Loading Animations
**Issue Type**: Enhancement
**Priority**: Low
**Status**: Future
**Component**: Frontend UI
**Related**: Caddy Reload UI Feedback Implementation
---
## 📋 Summary
Implement a hybrid approach for loading animations that randomly rotates between multiple thematic variations for both Charon (proxy operations) and Cerberus (security operations) themes. This adds visual variety and reinforces the mythological branding of the application.
---
## 🎯 Motivation
Currently, each operation type displays the same loading animation every time. While functional, this creates a repetitive user experience. By rotating between thematically consistent animation variants, we can:
1. **Reduce Visual Fatigue**: Users won't see the exact same animation on every operation
2. **Enhance Branding**: Multiple mythological references deepen the Charon/Cerberus theme
3. **Maintain Consistency**: All variants stay within their respective theme (blue/Charon or red/Cerberus)
4. **Add Delight**: Small surprises in UI create more engaging user experience
5. **Educational**: Each variant can teach users more about the mythology (e.g., Charon's obol coin)
---
## 🎨 Proposed Animation Variants
### Charon Theme (Proxy/General Operations)
**Color Palette**: Blue (#3B82F6, #60A5FA), Slate (#64748B, #475569)
| Animation | Description | Key Message Examples |
|-----------|-------------|---------------------|
| **Boat on Waves** (Current) | Boat silhouette bobbing on animated waves | "Ferrying across the Styx..." |
| **Rowing Oar** | Animated oar rowing motion in water | "Pulling through the mist..." / "The oar dips and rises..." |
| **River Flow** | Flowing water with current lines | "Drifting down the Styx..." / "Waters carry the change..." |
### Coin Theme (Authentication)
**Color Palette**: Gold (#F59E0B, #FBBF24), Amber (#D97706, #F59E0B)
| Animation | Description | Key Message Examples |
|-----------|-------------|---------------------|
| **Coin Flip** (Current) | Spinning obol (ancient Greek coin) on Y-axis | "Paying the ferryman..." / "Your obol grants passage" |
| **Coin Drop** | Coin falling and landing in palm | "The coin drops..." / "Payment accepted" |
| **Token Glow** | Glowing authentication token/key | "Token gleams..." / "The key turns..." |
| **Gate Opening** | Stone gate/door opening animation | "Gates part..." / "Passage granted" |
### Cerberus Theme (Security Operations)
**Color Palette**: Red (#DC2626, #EF4444), Amber (#F59E0B), Red-900 (#7F1D1D)
| Animation | Description | Key Message Examples |
|-----------|-------------|---------------------|
| **Three Heads Alert** (Current) | Three heads with glowing eyes and pulsing shield | "Guardian stands watch..." / "Three heads turn..." |
| **Shield Pulse** | Centered shield with pulsing defensive aura | "Barriers strengthen..." / "The ward pulses..." |
| **Guardian Stance** | Simplified Cerberus silhouette in alert pose | "Guarding the threshold..." / "Sentinel awakens..." |
| **Chain Links** | Animated chain links representing binding/security | "Chains of protection..." / "Bonds tighten..." |
---
## 🛠️ Technical Implementation
### Architecture
```tsx
// frontend/src/components/LoadingStates.tsx
type CharonVariant = 'boat' | 'coin' | 'oar' | 'river'
type CerberusVariant = 'heads' | 'shield' | 'stance' | 'chains'
interface LoadingMessages {
message: string
submessage: string
}
const CHARON_MESSAGES: Record<CharonVariant, LoadingMessages[]> = {
boat: [
{ message: "Ferrying across...", submessage: "Charon guides the way" },
{ message: "Crossing the Styx...", submessage: "The journey begins" }
],
coin: [
{ message: "Paying the ferryman...", submessage: "The obol tumbles" },
{ message: "Coin accepted...", submessage: "Passage granted" }
],
oar: [
{ message: "Pulling through the mist...", submessage: "The oar dips and rises" },
{ message: "Rowing steadily...", submessage: "Progress across dark waters" }
],
river: [
{ message: "Drifting down the Styx...", submessage: "Waters carry the change" },
{ message: "Current flows...", submessage: "The river guides all" }
]
}
const CERBERUS_MESSAGES: Record<CerberusVariant, LoadingMessages[]> = {
heads: [
{ message: "Three heads turn...", submessage: "Guardian stands watch" },
{ message: "Cerberus awakens...", submessage: "The gate is guarded" }
],
shield: [
{ message: "Barriers strengthen...", submessage: "The ward pulses" },
{ message: "Defenses activate...", submessage: "Protection grows" }
],
stance: [
{ message: "Guarding the threshold...", submessage: "Sentinel awakens" },
{ message: "Taking position...", submessage: "The guardian stands firm" }
],
chains: [
{ message: "Chains of protection...", submessage: "Bonds tighten" },
{ message: "Links secure...", submessage: "Nothing passes unchecked" }
]
}
// Randomly select variant on component mount
export function ConfigReloadOverlay({ type = 'charon', operationType }: Props) {
const [variant] = useState(() => {
if (type === 'cerberus') {
const variants: CerberusVariant[] = ['heads', 'shield', 'stance', 'chains']
return variants[Math.floor(Math.random() * variants.length)]
} else {
const variants: CharonVariant[] = ['boat', 'coin', 'oar', 'river']
return variants[Math.floor(Math.random() * variants.length)]
}
})
const [messages] = useState(() => {
const messageSet = type === 'cerberus'
? CERBERUS_MESSAGES[variant as CerberusVariant]
: CHARON_MESSAGES[variant as CharonVariant]
return messageSet[Math.floor(Math.random() * messageSet.length)]
})
// Render appropriate loader component based on variant
const Loader = getLoaderComponent(type, variant)
return (
<div className="fixed inset-0 bg-slate-900/70 backdrop-blur-sm flex items-center justify-center z-50">
<div className={/* theme styling */}>
<Loader size="lg" />
<div className="text-center">
<p className="text-slate-200 font-medium text-lg">{messages.message}</p>
<p className="text-slate-400 text-sm mt-2">{messages.submessage}</p>
</div>
</div>
</div>
)
}
```
### New Loader Components
Each variant needs its own component:
```tsx
// Charon Variants
export function CharonCoinLoader({ size }: LoaderProps) {
// Spinning coin with heads/tails alternating
}
export function CharonOarLoader({ size }: LoaderProps) {
// Rowing oar motion
}
export function CharonRiverLoader({ size }: LoaderProps) {
// Flowing water lines
}
// Cerberus Variants
export function CerberusShieldLoader({ size }: LoaderProps) {
// Pulsing shield with defensive aura
}
export function CerberusStanceLoader({ size }: LoaderProps) {
// Guardian dog in alert pose
}
export function CerberusChainsLoader({ size }: LoaderProps) {
// Animated chain links
}
```
---
## 📐 Animation Specifications
### Charon: Coin Flip
- **Visual**: Ancient Greek obol coin spinning on Y-axis
- **Animation**: 360° rotation every 2s, slight wobble
- **Colors**: Gold (#F59E0B) glint, slate shadow
- **Message Timing**: Change text on coin flip (heads vs tails)
### Charon: Rowing Oar
- **Visual**: Oar blade dipping into water, pulling back
- **Animation**: Arc motion, water ripples on dip
- **Colors**: Brown (#92400E) oar, blue (#3B82F6) water
- **Timing**: 3s cycle (dip 1s, pull 1.5s, lift 0.5s)
### Charon: River Flow
- **Visual**: Horizontal flowing lines with subtle particle drift
- **Animation**: Lines translate-x infinitely, particles bob
- **Colors**: Blue gradient (#1E3A8A#3B82F6)
- **Timing**: Continuous flow, particles move slower than lines
### Cerberus: Shield Pulse
- **Visual**: Shield outline with expanding aura rings
- **Animation**: Rings pulse outward and fade (like sonar)
- **Colors**: Red (#DC2626) shield, amber (#F59E0B) aura
- **Timing**: 2s pulse interval
### Cerberus: Guardian Stance
- **Visual**: Simplified three-headed dog silhouette, alert posture
- **Animation**: Heads swivel slightly, ears perk
- **Colors**: Red (#7F1D1D) body, amber (#F59E0B) eyes
- **Timing**: 3s head rotation cycle
### Cerberus: Chain Links
- **Visual**: 4-5 interlocking chain links
- **Animation**: Links tighten/loosen (scale transform)
- **Colors**: Gray (#475569) chains, red (#DC2626) accents
- **Timing**: 2.5s cycle (tighten 1s, loosen 1.5s)
---
## 🧪 Testing Strategy
### Visual Regression Tests
- Capture screenshots of each variant at key animation frames
- Verify animations play smoothly (no janky SVG rendering)
- Test across browsers (Chrome, Firefox, Safari)
### Unit Tests
```tsx
describe('ConfigReloadOverlay - Variant Selection', () => {
it('randomly selects Charon variant', () => {
const variants = new Set()
for (let i = 0; i < 20; i++) {
const { container } = render(<ConfigReloadOverlay type="charon" />)
// Extract which variant was rendered
variants.add(getRenderedVariant(container))
}
expect(variants.size).toBeGreaterThan(1) // Should see variety
})
it('randomly selects Cerberus variant', () => {
const variants = new Set()
for (let i = 0; i < 20; i++) {
const { container } = render(<ConfigReloadOverlay type="cerberus" />)
variants.add(getRenderedVariant(container))
}
expect(variants.size).toBeGreaterThan(1)
})
it('uses variant-specific messages', () => {
const { getByText } = render(<ConfigReloadOverlay type="charon" />)
// Should find ONE of the Charon messages
const hasCharonMessage =
getByText(/ferrying/i) ||
getByText(/coin/i) ||
getByText(/oar/i) ||
getByText(/river/i)
expect(hasCharonMessage).toBeTruthy()
})
})
```
### Manual Testing
- [ ] Trigger same operation 10 times, verify different animations appear
- [ ] Verify messages match animation theme (e.g., "Coin" messages with coin animation)
- [ ] Check performance (should be smooth at 60fps)
- [ ] Verify accessibility (screen readers announce state)
---
## 📦 Implementation Phases
### Phase 1: Core Infrastructure (2-3 hours)
- [ ] Create variant selection logic
- [ ] Create message mapping system
- [ ] Update `ConfigReloadOverlay` to accept variant prop
- [ ] Write unit tests for variant selection
### Phase 2: Charon Variants (3-4 hours)
- [ ] Implement `CharonOarLoader` component
- [ ] Implement `CharonRiverLoader` component
- [ ] Create messages for each variant
- [ ] Add Tailwind animations
### Phase 3: Coin Variants (3-4 hours)
- [ ] Implement `CoinDropLoader` component
- [ ] Implement `TokenGlowLoader` component
- [ ] Implement `GateOpeningLoader` component
- [ ] Create messages for each variant
- [ ] Add Tailwind animations
### Phase 4: Cerberus Variants (4-5 hours)
- [ ] Implement `CerberusShieldLoader` component
- [ ] Implement `CerberusStanceLoader` component
- [ ] Implement `CerberusChainsLoader` component
- [ ] Create messages for each variant
- [ ] Add Tailwind animations
### Phase 5: Integration & Polish (2-3 hours)
- [ ] Update all usage sites (ProxyHosts, WafConfig, etc.)
- [ ] Visual regression tests
- [ ] Performance profiling
- [ ] Documentation updates
**Total Estimated Time**: 15-19 hours
---
## 🎯 Success Metrics
- Users see at least 3 different animations within 10 operations
- Animation performance: 60fps on mid-range devices
- Zero accessibility regressions (WCAG 2.1 AA)
- Positive user feedback on visual variety
- Code coverage: >90% for variant selection logic
---
## 🚫 Out of Scope
- User preference for specific variant (always random)
- Custom animation timing controls
- Additional themes beyond Charon/Cerberus
- Sound effects or haptic feedback
- Animation of background overlay entrance/exit
---
## 📚 Research References
- **Charon Mythology**: [Wikipedia - Charon](https://en.wikipedia.org/wiki/Charon)
- **Cerberus Mythology**: [Wikipedia - Cerberus](https://en.wikipedia.org/wiki/Cerberus)
- **Obol Coin**: Payment for Charon's ferry service in Greek mythology
- **SVG Animation Performance**: [CSS-Tricks SVG Guide](https://css-tricks.com/guide-svg-animations-smil/)
- **React Loading States**: Best practices for UX during async operations
---
## 🔗 See Also
- Main Implementation: `docs/plans/current_spec.md`
- Charon Documentation: `docs/features.md`
- Cerberus Documentation: `docs/cerberus.md`