Files
Charon/CONTRIBUTING.md
akanealw eec8c28fb3
Some checks are pending
Go Benchmark / Performance Regression Check (push) Waiting to run
Cerberus Integration / Cerberus Security Stack Integration (push) Waiting to run
Upload Coverage to Codecov / Backend Codecov Upload (push) Waiting to run
Upload Coverage to Codecov / Frontend Codecov Upload (push) Waiting to run
CodeQL - Analyze / CodeQL analysis (go) (push) Waiting to run
CodeQL - Analyze / CodeQL analysis (javascript-typescript) (push) Waiting to run
CrowdSec Integration / CrowdSec Bouncer Integration (push) Waiting to run
Docker Build, Publish & Test / build-and-push (push) Waiting to run
Docker Build, Publish & Test / Security Scan PR Image (push) Blocked by required conditions
Quality Checks / Auth Route Protection Contract (push) Waiting to run
Quality Checks / Codecov Trigger/Comment Parity Guard (push) Waiting to run
Quality Checks / Backend (Go) (push) Waiting to run
Quality Checks / Frontend (React) (push) Waiting to run
Rate Limit integration / Rate Limiting Integration (push) Waiting to run
Security Scan (PR) / Trivy Binary Scan (push) Waiting to run
Supply Chain Verification (PR) / Verify Supply Chain (push) Waiting to run
WAF integration / Coraza WAF Integration (push) Waiting to run
changed perms
2026-04-22 18:19:14 +00:00

1227 lines
31 KiB
Markdown
Executable File

# Contributing to Charon
Thank you for your interest in contributing to CaddyProxyManager+! This document provides guidelines and instructions for contributing to the project.
## Table of Contents
- [Code of Conduct](#code-of-conduct)
- [Getting Started](#getting-started)
- [Development Workflow](#development-workflow)
- [Coding Standards](#coding-standards)
- [Testing Guidelines](#testing-guidelines)
- [Pull Request Process](#pull-request-process)
- [Issue Guidelines](#issue-guidelines)
- [Documentation](#documentation)
## Code of Conduct
This project follows a Code of Conduct that all contributors are expected to adhere to:
- Be respectful and inclusive
- Welcome newcomers and help them get started
- Focus on what's best for the community
- Show empathy towards other community members
## Getting Started
-### Prerequisites
- **go 1.26.0+** for backend development
- **Node.js 20+** and npm for frontend development
- Git for version control
- A GitHub account
### Development Tools
Install golangci-lint for lefthook pre-commit-phase hooks (required for Go development):
Also install lefthook itself so the git hooks work:
```bash
# Option 1: Homebrew (macOS/Linux)
brew install lefthook
# Option 2: Go install
go install github.com/evilmartians/lefthook@latest
```
```bash
# Option 1: Homebrew (macOS/Linux)
brew install golangci-lint
# Option 2: Go install (any platform)
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Option 3: Binary installation (see https://golangci-lint.run/usage/install/)
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin
```
Ensure `$GOPATH/bin` is in your `PATH`:
```bash
export PATH="$PATH:$(go env GOPATH)/bin"
```
Verify installation:
```bash
golangci-lint --version
# Should output: golangci-lint has version 1.xx.x ...
```
**Note:** Lefthook pre-commit-phase hooks will **BLOCK commits** if golangci-lint finds issues. This is intentional - fix the issues before committing.
### CI/CD Go Version Management
GitHub Actions workflows automatically use go 1.26.0 via `GOTOOLCHAIN: auto`, which allows the `setup-go` action to download and use the correct Go version even if the CI environment has an older version installed. This ensures consistent builds across all workflows.
For local development, install go 1.26.0+ from [go.dev/dl](https://go.dev/dl/).
### Go Version Updates
When the project's Go version is updated (usually by Renovate):
1. **Pull the latest changes**
```bash
git pull
```
2. **Update your local Go installation**
```bash
# Run the Go update skill (downloads and installs the new version)
.github/skills/scripts/skill-runner.sh utility-update-go-version
```
3. **Rebuild your development tools**
```bash
# This fixes lefthook hook errors and IDE issues
./scripts/rebuild-go-tools.sh
```
4. **Restart your IDE's Go language server**
- VS Code: Reload window (`Cmd/Ctrl+Shift+P` → "Developer: Reload Window")
- GoLand: File → Invalidate Caches → Restart
**Why do I need to do this?**
Development tools like golangci-lint and gopls are compiled programs. When you upgrade Go, these tools still run on the old version and will break with errors like:
```
error: some/file.go:123:4: undefined: runtime.NewlyAddedFunction
```
Rebuilding tools with `./scripts/rebuild-go-tools.sh` fixes this by compiling them with your new Go version.
**What if I forget?**
Don't worry! The lefthook pre-commit hook will detect the version mismatch and automatically rebuild tools for you. You'll see:
```
⚠️ golangci-lint Go version mismatch:
golangci-lint: 1.25.6
system Go: 1.26.0
🔧 Rebuilding golangci-lint with current Go version...
```
See [Go Version Upgrades Guide](docs/development/go_version_upgrades.md) for troubleshooting.
### Fork and Clone
1. Fork the repository on GitHub
2. Clone your fork locally:
```bash
git clone https://github.com/YOUR_USERNAME/charon.git
cd charon
```
1. Add the upstream remote:
```bash
git remote add upstream https://github.com/Wikid82/charon.git
```
### Set Up Development Environment
**Backend:**
```bash
cd backend
go mod download
go run ./cmd/seed/main.go # Seed test data
go run ./cmd/api/main.go # Start backend
```
**Frontend:**
```bash
cd frontend
npm install
npm run dev # Start frontend dev server
```
## Development Workflow
### Branching Strategy
- **main** - Production-ready code (stable releases)
- **nightly** - Pre-release testing branch (automated daily builds at 02:00 UTC)
- **development** - Main development branch (default for contributions)
- **feature/** - Feature branches (e.g., `feature/add-ssl-support`)
- **bugfix/** - Bug fix branches (e.g., `bugfix/fix-import-crash`)
- **hotfix/** - Urgent production fixes
### Branch Flow
The project uses a three-tier branching model:
```
development → nightly → main
(unstable) (testing) (stable)
```
**Flow details:**
1. **development → nightly**: Automated daily merge at 02:00 UTC
2. **nightly → main**: Manual PR after validation and testing
3. **Contributors always branch from `development`**
**Why nightly?**
- Provides a testing ground for features before production
- Automated daily builds catch integration issues
- Users can test pre-release features via `nightly` Docker tag
- Maintainers validate stability before merging to `main`
### Creating a Feature Branch
Always branch from `development`:
```bash
git checkout development
git pull upstream development
git checkout -b feature/your-feature-name
```
**Note:** Never branch from `nightly` or `main`. The `nightly` branch is managed by automation and receives daily merges from `development`.
### Commit Message Guidelines
Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification:
```
<type>(<scope>): <subject>
<body>
<footer>
```
**Types:**
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation only
- `style`: Code style changes (formatting, etc.)
- `refactor`: Code refactoring
- `test`: Adding or updating tests
- `chore`: Maintenance tasks
**Examples:**
```
feat(proxy-hosts): add SSL certificate upload
- Implement certificate upload endpoint
- Add UI for certificate management
- Update database schema
Closes #123
```
```
fix(import): resolve conflict detection bug
When importing Caddyfiles with multiple domains, conflicts
were not being detected properly.
Fixes #456
```
### Keeping Your Fork Updated
```bash
git checkout development
git fetch upstream
git merge upstream/development
git push origin development
```
## Coding Standards
### Go Backend
- Follow standard Go formatting (`gofmt`)
- Use meaningful variable and function names
- Write godoc comments for exported functions
- Keep functions small and focused
- Handle errors explicitly
**Example:**
```go
// GetProxyHost retrieves a proxy host by UUID.
// Returns an error if the host is not found.
func GetProxyHost(uuid string) (*models.ProxyHost, error) {
var host models.ProxyHost
if err := db.First(&host, "uuid = ?", uuid).Error; err != nil {
return nil, fmt.Errorf("proxy host not found: %w", err)
}
return &host, nil
}
```
### TypeScript Frontend
- Use TypeScript for type safety
- Follow React best practices and hooks patterns
- Use functional components
- Destructure props at function signature
- Extract reusable logic into custom hooks
**Example:**
```typescript
interface ProxyHostFormProps {
host?: ProxyHost
onSubmit: (data: ProxyHostData) => Promise<void>
onCancel: () => void
}
export function ProxyHostForm({ host, onSubmit, onCancel }: ProxyHostFormProps) {
const [domain, setDomain] = useState(host?.domain ?? '')
// ... component logic
}
```
### CSS/Styling
- Use TailwindCSS utility classes
- Follow the dark theme color palette
- Keep custom CSS minimal
- Use semantic color names from the theme
## Testing Guidelines
### Testing Against Nightly Builds
Before submitting a PR, test your changes against the latest nightly build:
**Pull latest nightly:**
```bash
docker pull ghcr.io/wikid82/charon:nightly
```
**Run your local changes against nightly:**
```bash
# Start nightly container
docker run -d --name charon-nightly \
-p 8080:8080 \
ghcr.io/wikid82/charon:nightly
# Test your feature/fix
curl http://localhost:8080/api/v1/health
# Clean up
docker stop charon-nightly && docker rm charon-nightly
```
**Integration testing:**
If your changes affect existing features, verify compatibility:
1. Deploy nightly build in test environment
2. Run your modified frontend/backend against it
3. Verify no regressions in existing functionality
4. Document any breaking changes in your PR
**Reporting nightly issues:**
If you find bugs in nightly builds:
1. Check if the issue exists in `development` branch
2. Open an issue tagged with `nightly` label
3. Include nightly build date or commit SHA
4. Provide reproduction steps
### Backend Tests
Write tests for all new functionality:
```go
func TestGetProxyHost(t *testing.T) {
// Setup
db := setupTestDB(t)
host := createTestHost(db)
// Execute
result, err := GetProxyHost(host.UUID)
// Assert
assert.NoError(t, err)
assert.Equal(t, host.Domain, result.Domain)
}
```
**Run tests:**
```bash
go test ./... -v
go test -cover ./...
```
### Frontend Tests
Write component and hook tests using Vitest and React Testing Library:
```typescript
describe('ProxyHostForm', () => {
it('renders create form with empty fields', async () => {
render(
<ProxyHostForm onSubmit={vi.fn()} onCancel={vi.fn()} />
)
await waitFor(() => {
expect(screen.getByText('Add Proxy Host')).toBeInTheDocument()
})
})
})
```
**Run tests:**
```bash
npm test # Watch mode
npm run test:coverage # Coverage report
```
### CrowdSec Frontend Test Coverage
The CrowdSec integration has comprehensive frontend test coverage (100%) across all modules:
- **API Clients** - All CrowdSec API endpoints tested with error handling
- **React Query Hooks** - Complete hook testing with query invalidation
- **Data & Utilities** - Preset validation and export functionality
- **162 tests total** - All passing with no flaky tests
See [QA Coverage Report](docs/reports/qa_crowdsec_frontend_coverage_report.md) for details.
### Test Coverage
- Aim for 85%+ code coverage (current backend: 85.4%)
- All new features must include tests
- Bug fixes should include regression tests
- CrowdSec modules maintain 100% frontend coverage
---
## Testing Emergency Break Glass Protocol
When contributing changes to security modules (ACL, WAF, Cerberus, Rate Limiting, CrowdSec), you **MUST** test that the emergency break glass protocol still functions correctly. A broken emergency recovery system can lock administrators out of their own systems during production incidents.
### Why This Matters
The emergency break glass protocol is a critical safety mechanism. If your changes break emergency access:
- ❌ Administrators locked out by security modules cannot recover
- ❌ Production incidents become catastrophic (no way to regain access)
- ❌ System may require physical access or complete rebuild
**Always test emergency recovery before merging security-related PRs.**
### Quick Test Procedure
#### Prerequisites
```bash
# Ensure container is running
docker-compose up -d
# Set emergency token
export CHARON_EMERGENCY_TOKEN=test-emergency-token-for-e2e-32chars
```
#### Test 1: Verify Lockout Scenario
Enable security modules with restrictive settings to simulate a lockout:
```bash
# Enable ACL with restrictive whitelist (via API or database)
curl -X POST http://localhost:8080/api/v1/settings \
-H "Content-Type: application/json" \
-d '{"key": "security.acl.enabled", "value": "true"}'
# Enable WAF in block mode
curl -X POST http://localhost:8080/api/v1/settings \
-H "Content-Type: application/json" \
-d '{"key": "security.waf.enabled", "value": "true"}'
# Enable Cerberus
curl -X POST http://localhost:8080/api/v1/settings \
-H "Content-Type: application/json" \
-d '{"key": "feature.cerberus.enabled", "value": "true"}'
```
#### Test 2: Verify You're Locked Out
Attempt to access a protected endpoint (should fail):
```bash
# Attempt normal access
curl http://localhost:8080/api/v1/proxy-hosts
# Expected response: 403 Forbidden
# {
# "error": "Blocked by access control list"
# }
```
If you're **NOT** blocked, investigate why security isn't working before proceeding.
#### Test 3: Test Emergency Token Works (Tier 1)
Use the emergency token to regain access:
```bash
# Send emergency reset request
curl -X POST http://localhost:8080/api/v1/emergency/security-reset \
-H "X-Emergency-Token: test-emergency-token-for-e2e-32chars" \
-H "Content-Type: application/json"
# Expected response: 200 OK
# {
# "success": true,
# "message": "All security modules have been disabled",
# "disabled_modules": [
# "feature.cerberus.enabled",
# "security.acl.enabled",
# "security.waf.enabled",
# "security.rate_limit.enabled",
# "security.crowdsec.enabled"
# ]
# }
```
**If this fails:** Your changes broke Tier 1 emergency access. Fix before merging.
#### Test 4: Verify Lockout is Cleared
Confirm you can now access protected endpoints:
```bash
# Wait for settings to propagate
sleep 5
# Test normal access (should work now)
curl http://localhost:8080/api/v1/proxy-hosts
# Expected response: 200 OK
# [... list of proxy hosts ...]
```
#### Test 5: Test Emergency Server (Tier 2 - Optional)
If the emergency server is enabled (`CHARON_EMERGENCY_SERVER_ENABLED=true`):
```bash
# Test emergency server health
curl http://localhost:2019/health
# Expected: {"status":"ok","server":"emergency"}
# Test emergency reset via emergency server
curl -X POST http://localhost:2019/emergency/security-reset \
-H "X-Emergency-Token: test-emergency-token-for-e2e-32chars" \
-u admin:changeme
# Expected: {"success":true, ...}
```
### Complete Test Script
Save this as `scripts/test-emergency-access.sh`:
```bash
#!/usr/bin/env bash
set -euo pipefail
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo -e "${YELLOW}Testing Emergency Break Glass Protocol${NC}"
echo "========================================"
echo ""
# Configuration
BASE_URL="http://localhost:8080"
EMERGENCY_TOKEN="${CHARON_EMERGENCY_TOKEN:-test-emergency-token-for-e2e-32chars}"
# Test 1: Enable security (create lockout scenario)
echo -e "${YELLOW}Test 1: Creating lockout scenario...${NC}"
curl -s -X POST "$BASE_URL/api/v1/settings" \
-H "Content-Type: application/json" \
-d '{"key": "security.acl.enabled", "value": "true"}' > /dev/null
curl -s -X POST "$BASE_URL/api/v1/settings" \
-H "Content-Type: application/json" \
-d '{"key": "feature.cerberus.enabled", "value": "true"}' > /dev/null
sleep 2
echo -e "${GREEN}✓ Security enabled${NC}"
echo ""
# Test 2: Verify lockout
echo -e "${YELLOW}Test 2: Verifying lockout...${NC}"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/api/v1/proxy-hosts")
if [ "$RESPONSE" = "403" ]; then
echo -e "${GREEN}✓ Lockout confirmed (403 Forbidden)${NC}"
else
echo -e "${RED}✗ Expected 403, got $RESPONSE${NC}"
echo -e "${YELLOW}Warning: Security may not be blocking correctly${NC}"
fi
echo ""
# Test 3: Emergency token recovery
echo -e "${YELLOW}Test 3: Testing emergency token...${NC}"
RESPONSE=$(curl -s -X POST "$BASE_URL/api/v1/emergency/security-reset" \
-H "X-Emergency-Token: $EMERGENCY_TOKEN" \
-H "Content-Type: application/json")
if echo "$RESPONSE" | grep -q '"success":true'; then
echo -e "${GREEN}✓ Emergency token works${NC}"
else
echo -e "${RED}✗ Emergency token failed${NC}"
echo "Response: $RESPONSE"
exit 1
fi
echo ""
# Test 4: Verify access restored
echo -e "${YELLOW}Test 4: Verifying access restored...${NC}"
sleep 5
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/api/v1/proxy-hosts")
if [ "$RESPONSE" = "200" ]; then
echo -e "${GREEN}✓ Access restored (200 OK)${NC}"
else
echo -e "${RED}✗ Access not restored, got $RESPONSE${NC}"
exit 1
fi
echo ""
# Test 5: Emergency server (if enabled)
if curl -s http://localhost:2019/health > /dev/null 2>&1; then
echo -e "${YELLOW}Test 5: Testing emergency server...${NC}"
RESPONSE=$(curl -s http://localhost:2019/health)
if echo "$RESPONSE" | grep -q '"server":"emergency"'; then
echo -e "${GREEN}✓ Emergency server responding${NC}"
else
echo -e "${RED}✗ Emergency server not responding correctly${NC}"
fi
else
echo -e "${YELLOW}Test 5: Skipped (emergency server not enabled)${NC}"
fi
echo ""
echo "========================================"
echo -e "${GREEN}All tests passed! Emergency access is functional.${NC}"
```
Make executable and run:
```bash
chmod +x scripts/test-emergency-access.sh
./scripts/test-emergency-access.sh
```
### Integration Test (Go)
Add to your backend test suite:
```go
func TestEmergencyAccessIntegration(t *testing.T) {
// Setup test database and router
db := setupTestDB(t)
router := setupTestRouter(db)
// Enable security (create lockout scenario)
enableSecurity(t, db)
// Test 1: Regular endpoint should be blocked
req := httptest.NewRequest(http.MethodGET, "/api/v1/proxy-hosts", nil)
req.RemoteAddr = "127.0.0.1:12345"
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusForbidden, w.Code, "Regular access should be blocked")
// Test 2: Emergency endpoint should work with valid token
req = httptest.NewRequest(http.MethodPOST, "/api/v1/emergency/security-reset", nil)
req.Header.Set("X-Emergency-Token", "test-emergency-token-for-e2e-32chars")
req.RemoteAddr = "127.0.0.1:12345"
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code, "Emergency endpoint should work")
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
// Test 3: Regular endpoint should work after emergency reset
time.Sleep(2 * time.Second)
req = httptest.NewRequest(http.MethodGET, "/api/v1/proxy-hosts", nil)
req.RemoteAddr = "127.0.0.1:12345"
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code, "Access should be restored after emergency reset")
}
```
### E2E Test (Playwright)
Add to your Playwright test suite:
```typescript
import { test, expect } from '@playwright/test'
test.describe('Emergency Break Glass Protocol', () => {
test('should recover from complete security lockout', async ({ request }) => {
const baseURL = 'http://localhost:8080'
const emergencyToken = 'test-emergency-token-for-e2e-32chars'
// Step 1: Enable all security modules
await request.post(`${baseURL}/api/v1/settings`, {
data: { key: 'feature.cerberus.enabled', value: 'true' }
})
await request.post(`${baseURL}/api/v1/settings`, {
data: { key: 'security.acl.enabled', value: 'true' }
})
// Wait for settings to propagate
await new Promise(resolve => setTimeout(resolve, 2000))
// Step 2: Verify lockout (expect 403)
const lockedResponse = await request.get(`${baseURL}/api/v1/proxy-hosts`)
expect(lockedResponse.status()).toBe(403)
// Step 3: Use emergency token to recover
const emergencyResponse = await request.post(
`${baseURL}/api/v1/emergency/security-reset`,
{
headers: { 'X-Emergency-Token': emergencyToken }
}
)
expect(emergencyResponse.status()).toBe(200)
const body = await emergencyResponse.json()
expect(body.success).toBe(true)
expect(body.disabled_modules).toContain('security.acl.enabled')
// Wait for settings to propagate
await new Promise(resolve => setTimeout(resolve, 2000))
// Step 4: Verify access restored
const restoredResponse = await request.get(`${baseURL}/api/v1/proxy-hosts`)
expect(restoredResponse.ok()).toBeTruthy()
})
})
```
### When to Run These Tests
Run emergency access tests:
- ✅ **Before every PR** that touches security-related code
- ✅ **After modifying** ACL, WAF, Cerberus, or Rate Limiting modules
- ✅ **After changing** middleware order or request pipeline
- ✅ **After updating** authentication or authorization logic
- ✅ **Before releases** to ensure emergency access works in production
### Troubleshooting Test Failures
**Emergency token returns 401 Unauthorized:**
- Verify `CHARON_EMERGENCY_TOKEN` is set correctly
- Check token is at least 32 characters
- Ensure token matches exactly (no whitespace or line breaks)
**Emergency token returns 403 Forbidden:**
- Tier 1 bypass may be blocked at Caddy/CrowdSec layer
- Test Tier 2 (emergency server) instead
- Check `CHARON_MANAGEMENT_CIDRS` includes your test IP
**Access not restored after emergency reset:**
- Check response includes `"success":true`
- Verify settings were actually disabled in database
- Increase wait time between reset and verification (may need > 5 seconds)
- Check logs: `docker logs charon | grep emergency`
**Emergency server not responding:**
- Verify `CHARON_EMERGENCY_SERVER_ENABLED=true` in environment
- Check port 2019 is exposed in docker-compose.yml
- Test with Basic Auth if configured: `curl -u admin:password`
### Related Documentation
- [Emergency Lockout Recovery Runbook](docs/runbooks/emergency-lockout-recovery.md)
- [Emergency Token Rotation Guide](docs/runbooks/emergency-token-rotation.md)
- [Configuration Examples](docs/configuration/emergency-setup.md)
- [Break Glass Protocol Design](docs/plans/break_glass_protocol_redesign.md)
## Adding New Skills
Charon uses [Agent Skills](https://agentskills.io) for AI-discoverable development tasks. Skills are standardized, self-documenting task definitions that can be executed by humans and AI assistants.
### What is a Skill?
A skill is a combination of:
- **YAML Frontmatter**: Metadata following the [agentskills.io specification](https://agentskills.io/specification)
- **Markdown Documentation**: Usage instructions, examples, and troubleshooting
- **Execution Script**: Shell script that performs the actual task
### When to Create a Skill
Create a new skill when you have a:
- **Repeatable task** that developers run frequently
- **Complex workflow** that benefits from documentation
- **CI/CD operation** that should be AI-discoverable
- **Development tool** that needs consistent execution
**Examples**: Running tests, building artifacts, security scans, database operations, deployment tasks
### Skill Creation Process
#### 1. Plan Your Skill
Before creating, define:
- **Name**: Use `{category}-{feature}-{variant}` format (e.g., `test-backend-coverage`)
- **Category**: test, integration-test, security, qa, build, utility, docker
- **Purpose**: One clear sentence describing what it does
- **Requirements**: Tools, environment variables, permissions needed
- **Output**: What the skill produces (exit codes, files, reports)
#### 2. Create Directory Structure
```bash
# Create skill directory
mkdir -p .github/skills/{skill-name}-scripts
# Skill files will be:
# .github/skills/{skill-name}.SKILL.md # Documentation
# .github/skills/{skill-name}-scripts/run.sh # Execution script
```
#### 3. Write the SKILL.md File
Use the template structure:
```markdown
---
# agentskills.io specification v1.0
name: "skill-name"
version: "1.0.0"
description: "Brief description (max 120 chars)"
author: "Charon Project"
license: "MIT"
tags:
- "tag1"
- "tag2"
compatibility:
os:
- "linux"
- "darwin"
shells:
- "bash"
requirements:
- name: "tool"
version: ">=1.0"
optional: false
metadata:
category: "category-name"
execution_time: "short|medium|long"
risk_level: "low|medium|high"
ci_cd_safe: true|false
---
# Skill Name
## Overview
Brief description of what this skill does.
## Prerequisites
- List required tools
- List required permissions
- List environment setup
## Usage
```bash
.github/skills/scripts/skill-runner.sh skill-name
```
## Examples
### Example 1: Basic Usage
```bash
# Description
command example
```
## Error Handling
- Common errors and solutions
- Exit codes and meanings
## Related Skills
- [related-skill](./related-skill.SKILL.md)
---
**Last Updated**: YYYY-MM-DD
**Maintained by**: Charon Project
**Source**: Original implementation or script path
```
#### 4. Create the Execution Script
Create `.github/skills/{skill-name}-scripts/run.sh`:
```bash
#!/usr/bin/env bash
set -euo pipefail
# Source helper scripts
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILLS_SCRIPTS_DIR="$(cd "${SCRIPT_DIR}/../scripts" && pwd)"
source "${SKILLS_SCRIPTS_DIR}/_logging_helpers.sh"
source "${SKILLS_SCRIPTS_DIR}/_error_handling_helpers.sh"
source "${SKILLS_SCRIPTS_DIR}/_environment_helpers.sh"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
# Validate environment
log_step "ENVIRONMENT" "Validating prerequisites"
check_command_exists "required-tool" "Please install required-tool"
# Execute skill logic
log_step "EXECUTION" "Running skill"
cd "${PROJECT_ROOT}"
# Your skill implementation here
if ! your-command; then
error_exit "Skill execution failed"
fi
log_success "Skill completed successfully"
```
Make it executable:
```bash
chmod +x .github/skills/{skill-name}-scripts/run.sh
```
#### 5. Validate the Skill
Run the validation tool:
```bash
# Validate single skill
python3 .github/skills/scripts/validate-skills.py --single .github/skills/{skill-name}.SKILL.md
# Validate all skills
python3 .github/skills/scripts/validate-skills.py
```
Fix any validation errors before proceeding.
#### 6. Test the Skill
Test execution:
```bash
# Direct execution
.github/skills/scripts/skill-runner.sh {skill-name}
# Verify output
# Check exit codes
# Confirm expected behavior
```
#### 7. Add VS Code Task (Optional)
If the skill should be available in VS Code's task menu, add to `.vscode/tasks.json`:
```json
{
"label": "Category: Skill Name",
"type": "shell",
"command": ".github/skills/scripts/skill-runner.sh skill-name",
"group": "test"
}
```
#### 8. Update Documentation
Add your skill to `.github/skills/README.md`:
```markdown
| [skill-name](./skill-name.SKILL.md) | category | Description | ✅ Active |
```
### Validation Requirements
All skills must pass validation:
- ✅ **Required fields**: name, version, description, author, license, tags
- ✅ **Name format**: kebab-case (lowercase, hyphens)
- ✅ **Version format**: Semantic versioning (x.y.z)
- ✅ **Description**: Max 120 characters
- ✅ **Tags**: Minimum 2, maximum 5
- ✅ **Executable script**: Must exist and be executable
### Best Practices
**Documentation:**
- Keep SKILL.md under 500 lines
- Include real-world examples
- Document all prerequisites clearly
- Add troubleshooting section for common issues
**Scripts:**
- Always use helper functions for logging and error handling
- Validate environment before execution
- Make scripts idempotent when possible
- Clean up resources on exit (use trap)
**Testing:**
- Test skill in clean environment
- Verify all exit codes
- Check output format consistency
- Test error scenarios
**Metadata:**
- Set accurate `execution_time` (short < 1min, medium 1-5min, long > 5min)
- Use `ci_cd_safe: false` for interactive or risky operations
- Mark `idempotent: true` only if truly safe to run repeatedly
- Include all dependencies in `requirements`
### Helper Scripts Reference
Charon provides helper scripts for common operations:
**Logging** (`_logging_helpers.sh`):
- `log_info`, `log_success`, `log_warning`, `log_error`, `log_debug`
- `log_step` for section headers
- `log_command` to log before executing
**Error Handling** (`_error_handling_helpers.sh`):
- `error_exit` to print error and exit
- `check_command_exists`, `check_file_exists`, `check_dir_exists`
- `run_with_retry` for network operations
- `trap_error` for automatic error trapping
**Environment** (`_environment_helpers.sh`):
- `validate_go_environment`, `validate_python_environment`, `validate_node_environment`
- `validate_docker_environment`
- `set_default_env` for environment variables
- `get_project_root` to find repository root
### Resources
- **[Agent Skills README](.github/skills/README.md)** — Complete skills documentation
- **[agentskills.io Specification](https://agentskills.io/specification)** — Standard format
- **[Existing Skills](.github/skills/)** — Reference implementations
- **[Migration Guide](docs/AGENT_SKILLS_MIGRATION.md)** — Background and benefits
## Pull Request Process
### Before Submitting
1. **Ensure tests pass:**
```bash
# Backend
go test ./...
# Frontend
npm test -- --run
```
1. **Check code quality:**
```bash
# Go formatting
go fmt ./...
# Frontend linting
npm run lint
```
1. **Update documentation** if needed
2. **Add tests** for new functionality
3. **Rebase on latest development** branch
### Submitting a Pull Request
1. Push your branch to your fork:
```bash
git push origin feature/your-feature-name
```
1. Open a Pull Request on GitHub
2. Fill out the PR template completely
3. Link related issues using "Closes #123" or "Fixes #456"
4. Request review from maintainers
### PR Template
```markdown
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Manual testing performed
- [ ] All tests passing
## Screenshots (if applicable)
Add screenshots of UI changes
## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review performed
- [ ] Comments added for complex code
- [ ] Documentation updated
- [ ] No new warnings generated
```
### Review Process
- Maintainers will review within 2-3 business days
- Address review feedback promptly
- Keep discussions focused and professional
- Be open to suggestions and alternative approaches
## Issue Guidelines
### Reporting Bugs
Use the bug report template and include:
- Clear, descriptive title
- Steps to reproduce
- Expected vs actual behavior
- Environment details (OS, browser, Go version, etc.)
- Screenshots or error logs
- Potential solutions (if known)
### Feature Requests
Use the feature request template and include:
- Clear description of the feature
- Use case and motivation
- Potential implementation approach
- Mockups or examples (if applicable)
### Issue Labels
- `bug` - Something isn't working
- `enhancement` - New feature or request
- `documentation` - Documentation improvements
- `good first issue` - Good for newcomers
- `help wanted` - Extra attention needed
- `priority: high` - Urgent issue
- `wontfix` - Will not be fixed
## Documentation
### Code Documentation
- Add docstrings to all exported functions
- Include examples in complex functions
- Document return types and error conditions
- Keep comments up-to-date with code changes
### Project Documentation
When adding features, update:
- `README.md` - User-facing information
- `docs/api.md` - API changes
- `docs/import-guide.md` - Import feature updates
- `docs/database-schema.md` - Schema changes
## Recognition
Contributors will be recognized in:
- CONTRIBUTORS.md file
- Release notes for significant contributions
- GitHub contributors page
## Questions?
- Open a [Discussion](https://github.com/Wikid82/charon/discussions) for general questions
- Join our community chat (coming soon)
- Tag maintainers in issues for urgent matters
## License
By contributing, you agree that your contributions will be licensed under the project's MIT License.
---
Thank you for contributing to CaddyProxyManager+! 🎉