# Extension Registry Service\n\nA high-performance Rust microservice that provides a unified REST API for extension discovery, versioning,\nand download from multiple sources (Gitea releases and OCI registries).\n\n## Features\n\n- **Multi-Backend Support**: Fetch extensions from Gitea releases and OCI registries\n- **Unified REST API**: Single API for all extension operations\n- **Smart Caching**: LRU cache with TTL to reduce backend API calls\n- **Prometheus Metrics**: Built-in metrics for monitoring\n- **Health Monitoring**: Health checks for all backends\n- **Type-Safe**: Strong typing for extension metadata\n- **Async/Await**: High-performance async operations with Tokio\n- **Docker Support**: Production-ready containerization\n\n## Architecture\n\n```{$detected_lang}\n┌─────────────────────────────────────────────────\n────────────┐\n│ Extension Registry API │\n│ (axum) │\n├─────────────────────────────────────────────────\n────────────┤\n│ │\n│ ┌────────────────┐ ┌────────────────┐ \n┌──────────────┐ │\n│ │ Gitea Client │ │ OCI Client │ │ LRU Cache │ │\n│ │ (reqwest) │ │ (reqwest) │ │ (parking) │ │\n│ └────────────────┘ └────────────────┘ \n└──────────────┘ │\n│ │ │ │ │\n└─────────┼────────────────────┼──────────────────\n──┼─────────┘\n │ │ │\n ▼ ▼ ▼\n ┌──────────┐ ┌──────────┐ ┌──────────┐\n │ Gitea │ │ OCI │ │ Memory │\n │ Releases │ │ Registry │ │ │\n └──────────┘ └──────────┘ └──────────┘\n```\n\n## Installation\n\n### Building from Source\n\n```{$detected_lang}\ncd provisioning/platform/extension-registry\ncargo build --release\n```\n\n### Docker Build\n\n```{$detected_lang}\ndocker build -t extension-registry:latest .\n```\n\n### Running with Cargo\n\n```{$detected_lang}\ncargo run -- --config config.toml --port 8082\n```\n\n### Running with Docker\n\n```{$detected_lang}\ndocker run -d \\n -p 8082:8082 \\n -v $(pwd)/config.toml:/app/config.toml:ro \\n -v $(pwd)/tokens:/app/tokens:ro \\n extension-registry:latest\n```\n\n## Configuration\n\nCreate a `config.toml` file (see `config.example.toml`):\n\n```{$detected_lang}\n[server]\nhost = "0.0.0.0"\nport = 8082\nworkers = 4\nenable_cors = true\nenable_compression = true\n\n# Gitea backend (optional)\n[gitea]\nurl = "https://gitea.example.com"\norganization = "provisioning-extensions"\ntoken_path = "/path/to/gitea-token.txt"\ntimeout_seconds = 30\nverify_ssl = true\n\n# OCI registry backend (optional)\n[oci]\nregistry = "registry.example.com"\nnamespace = "provisioning"\nauth_token_path = "/path/to/oci-token.txt"\ntimeout_seconds = 30\nverify_ssl = true\n\n# Cache configuration\n[cache]\ncapacity = 1000\nttl_seconds = 300\nenable_metadata_cache = true\nenable_list_cache = true\n```\n\n**Note**: At least one backend (Gitea or OCI) must be configured.\n\n## API Endpoints\n\n### Extension Operations\n\n#### List Extensions\n\n```{$detected_lang}\nGET /api/v1/extensions\n```\n\nQuery parameters:\n\n- `type` (optional): Filter by extension type (`provider`, `taskserv`, `cluster`)\n- `source` (optional): Filter by source (`gitea`, `oci`)\n- `limit` (optional): Maximum results (default: 100)\n- `offset` (optional): Pagination offset (default: 0)\n\nExample:\n\n```{$detected_lang}\ncurl http://localhost:8082/api/v1/extensions?type=provider&limit=10\n```\n\nResponse:\n\n```{$detected_lang}\n[\n {\n "name": "aws",\n "type": "provider",\n "version": "1.2.0",\n "description": "AWS provider for provisioning",\n "author": "provisioning-team",\n "repository": "https://gitea.example.com/org/aws_prov",\n "source": "gitea",\n "published_at": "2025-10-06T12:00:00Z",\n "download_url": "https://gitea.example.com/org/aws_prov/releases/download/1.2.0/aws_prov.tar.gz",\n "size": 1024000\n }\n]\n```\n\n#### Get Extension\n\n```{$detected_lang}\nGET /api/v1/extensions/{type}/{name}\n```\n\nExample:\n\n```{$detected_lang}\ncurl http://localhost:8082/api/v1/extensions/provider/aws\n```\n\n#### List Versions\n\n```{$detected_lang}\nGET /api/v1/extensions/{type}/{name}/versions\n```\n\nExample:\n\n```{$detected_lang}\ncurl http://localhost:8082/api/v1/extensions/provider/aws/versions\n```\n\nResponse:\n\n```{$detected_lang}\n[\n {\n "version": "1.2.0",\n "published_at": "2025-10-06T12:00:00Z",\n "download_url": "https://gitea.example.com/org/aws_prov/releases/download/1.2.0/aws_prov.tar.gz",\n "size": 1024000\n },\n {\n "version": "1.1.0",\n "published_at": "2025-09-15T10:30:00Z",\n "download_url": "https://gitea.example.com/org/aws_prov/releases/download/1.1.0/aws_prov.tar.gz",\n "size": 980000\n }\n]\n```\n\n#### Download Extension\n\n```{$detected_lang}\nGET /api/v1/extensions/{type}/{name}/{version}\n```\n\nExample:\n\n```{$detected_lang}\ncurl -O http://localhost:8082/api/v1/extensions/provider/aws/1.2.0\n```\n\nReturns binary data with `Content-Type: application/octet-stream`.\n\n#### Search Extensions\n\n```{$detected_lang}\nGET /api/v1/extensions/search?q={query}\n```\n\nQuery parameters:\n\n- `q` (required): Search query\n- `type` (optional): Filter by extension type\n- `limit` (optional): Maximum results (default: 50)\n\nExample:\n\n```{$detected_lang}\ncurl http://localhost:8082/api/v1/extensions/search?q=kubernetes&type=taskserv\n```\n\n### System Endpoints\n\n#### Health Check\n\n```{$detected_lang}\nGET /api/v1/health\n```\n\nExample:\n\n```{$detected_lang}\ncurl http://localhost:8082/api/v1/health\n```\n\nResponse:\n\n```{$detected_lang}\n{\n "status": "healthy",\n "version": "0.1.0",\n "uptime": 3600,\n "backends": {\n "gitea": {\n "enabled": true,\n "healthy": true\n },\n "oci": {\n "enabled": true,\n "healthy": true\n }\n }\n}\n```\n\n#### Metrics\n\n```{$detected_lang}\nGET /api/v1/metrics\n```\n\nReturns Prometheus-formatted metrics:\n\n```{$detected_lang}\n# HELP http_requests_total Total HTTP requests\n# TYPE http_requests_total counter\nhttp_requests_total 1234\n\n# HELP cache_hits_total Total cache hits\n# TYPE cache_hits_total counter\ncache_hits_total 567\n\n# HELP cache_misses_total Total cache misses\n# TYPE cache_misses_total counter\ncache_misses_total 123\n```\n\n#### Cache Statistics\n\n```{$detected_lang}\nGET /api/v1/cache/stats\n```\n\nResponse:\n\n```{$detected_lang}\n{\n "list_entries": 45,\n "metadata_entries": 120,\n "version_entries": 80,\n "total_entries": 245\n}\n```\n\n## Extension Naming Conventions\n\n### Gitea Repositories\n\nExtensions in Gitea follow specific naming patterns:\n\n- **Providers**: `{name}_prov` (e.g., `aws_prov`, `upcloud_prov`)\n- **Task Services**: `{name}_taskserv` (e.g., `kubernetes_taskserv`, `postgres_taskserv`)\n- **Clusters**: `{name}_cluster` (e.g., `buildkit_cluster`, `ci_cluster`)\n\n### OCI Artifacts\n\nExtensions in OCI registries follow these patterns:\n\n- **Providers**: `{namespace}/{name}-provider` (e.g., `provisioning/aws-provider`)\n- **Task Services**: `{namespace}/{name}-taskserv` (e.g., `provisioning/kubernetes-taskserv`)\n- **Clusters**: `{namespace}/{name}-cluster` (e.g., `provisioning/buildkit-cluster`)\n\n## Caching Strategy\n\nThe service implements a multi-level LRU cache with TTL:\n\n1. **List Cache**: Caches extension lists (filtered by type/source)\n2. **Metadata Cache**: Caches individual extension metadata\n3. **Version Cache**: Caches version lists per extension\n\nCache behavior:\n\n- **Capacity**: Configurable (default: 1000 entries)\n- **TTL**: Configurable (default: 5 minutes)\n- **Eviction**: LRU (Least Recently Used)\n- **Invalidation**: Automatic on TTL expiration\n\nCache keys:\n\n- List: `list:{type}:{source}`\n- Metadata: `{type}/{name}`\n- Versions: `{type}/{name}/versions`\n\n## Error Handling\n\nThe API uses standard HTTP status codes:\n\n- `200 OK`: Successful operation\n- `400 Bad Request`: Invalid input (e.g., invalid extension type)\n- `401 Unauthorized`: Authentication failed\n- `404 Not Found`: Extension not found\n- `429 Too Many Requests`: Rate limit exceeded\n- `500 Internal Server Error`: Server error\n\nError response format:\n\n```{$detected_lang}\n{\n "error": "not_found",\n "message": "Extension provider/nonexistent not found"\n}\n```\n\n## Metrics and Monitoring\n\n### Prometheus Metrics\n\nAvailable metrics:\n\n- `http_requests_total`: Total HTTP requests\n- `http_request_duration_seconds`: Request duration histogram\n- `cache_hits_total`: Total cache hits\n- `cache_misses_total`: Total cache misses\n- `extensions_total`: Total extensions served\n\n### Health Checks\n\nThe health endpoint checks:\n\n- Service uptime\n- Gitea backend connectivity\n- OCI backend connectivity\n- Overall service status\n\n## Development\n\n### Project Structure\n\n```{$detected_lang}\nextension-registry/\n├── Cargo.toml # Rust dependencies\n├── src/\n│ ├── main.rs # Entry point\n│ ├── lib.rs # Library exports\n│ ├── config.rs # Configuration management\n│ ├── error.rs # Error types\n│ ├── api/\n│ │ ├── handlers.rs # HTTP handlers\n│ │ └── routes.rs # Route definitions\n│ ├── gitea/\n│ │ ├── client.rs # Gitea API client\n│ │ └── models.rs # Gitea data models\n│ ├── oci/\n│ │ ├── client.rs # OCI registry client\n│ │ └── models.rs # OCI data models\n│ ├── cache/\n│ │ └── lru_cache.rs # LRU caching\n│ └── models/\n│ └── extension.rs # Extension models\n├── tests/\n│ └── integration_test.rs # Integration tests\n├── Dockerfile # Docker build\n└── README.md # This file\n```\n\n### Running Tests\n\n```{$detected_lang}\n# Run all tests\ncargo test\n\n# Run with output\ncargo test -- --nocapture\n\n# Run specific test\ncargo test test_health_check\n```\n\n### Code Quality\n\n```{$detected_lang}\n# Format code\ncargo fmt\n\n# Run clippy\ncargo clippy\n\n# Check for security vulnerabilities\ncargo audit\n```\n\n## Deployment\n\n### Systemd Service\n\nCreate `/etc/systemd/system/extension-registry.service`:\n\n```{$detected_lang}\n[Unit]\nDescription=Extension Registry Service\nAfter=network.target\n\n[Service]\nType=simple\nUser=registry\nWorkingDirectory=/opt/extension-registry\nExecStart=/usr/local/bin/extension-registry --config /etc/extension-registry/config.toml\nRestart=on-failure\nRestartSec=5s\n\n[Install]\nWantedBy=multi-user.target\n```\n\nEnable and start:\n\n```{$detected_lang}\nsudo systemctl enable extension-registry\nsudo systemctl start extension-registry\nsudo systemctl status extension-registry\n```\n\n### Docker Compose\n\n```{$detected_lang}\nversion: '3.8'\n\nservices:\n extension-registry:\n image: extension-registry:latest\n ports:\n - "8082:8082"\n volumes:\n - ./config.toml:/app/config.toml:ro\n - ./tokens:/app/tokens:ro\n restart: unless-stopped\n healthcheck:\n test: ["CMD", "curl", "-f", "http://localhost:8082/api/v1/health"]\n interval: 30s\n timeout: 3s\n retries: 3\n start_period: 5s\n```\n\n### Kubernetes Deployment\n\n```{$detected_lang}\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: extension-registry\nspec:\n replicas: 3\n selector:\n matchLabels:\n app: extension-registry\n template:\n metadata:\n labels:\n app: extension-registry\n spec:\n containers:\n - name: extension-registry\n image: extension-registry:latest\n ports:\n - containerPort: 8082\n volumeMounts:\n - name: config\n mountPath: /app/config.toml\n subPath: config.toml\n - name: tokens\n mountPath: /app/tokens\n livenessProbe:\n httpGet:\n path: /api/v1/health\n port: 8082\n initialDelaySeconds: 5\n periodSeconds: 10\n readinessProbe:\n httpGet:\n path: /api/v1/health\n port: 8082\n initialDelaySeconds: 5\n periodSeconds: 5\n volumes:\n - name: config\n configMap:\n name: extension-registry-config\n - name: tokens\n secret:\n secretName: extension-registry-tokens\n---\napiVersion: v1\nkind: Service\nmetadata:\n name: extension-registry\nspec:\n selector:\n app: extension-registry\n ports:\n - port: 8082\n targetPort: 8082\n type: ClusterIP\n```\n\n## Security\n\n### Authentication\n\n- Gitea: Token-based authentication via `token_path`\n- OCI: Optional token authentication via `auth_token_path`\n\n### Best Practices\n\n1. **Store tokens securely**: Use file permissions (600) for token files\n2. **Enable SSL verification**: Set `verify_ssl = true` in production\n3. **Use HTTPS**: Always use HTTPS for Gitea and OCI registries\n4. **Limit CORS**: Configure CORS appropriately for production\n5. **Rate limiting**: Consider adding rate limiting for public APIs\n6. **Network isolation**: Run service in isolated network segments\n\n## Performance\n\n### Benchmarks\n\nTypical performance characteristics:\n\n- **Cached requests**: <5ms response time\n- **Uncached requests**: 50-200ms (depends on backend latency)\n- **Cache hit ratio**: ~85-95% in typical workloads\n- **Throughput**: 1000+ req/s on modern hardware\n\n### Optimization Tips\n\n1. **Increase cache capacity**: For large extension catalogs\n2. **Tune TTL**: Balance freshness vs. performance\n3. **Use multiple workers**: Scale with CPU cores\n4. **Enable compression**: Reduce bandwidth usage\n5. **Connection pooling**: Reuse HTTP connections to backends\n\n## Troubleshooting\n\n### Common Issues\n\n#### Service won't start\n\n- Check configuration file syntax\n- Verify token files exist and are readable\n- Check network connectivity to backends\n\n#### Extensions not found\n\n- Verify backend configuration (URL, organization, namespace)\n- Check backend connectivity with health endpoint\n- Review logs for authentication errors\n\n#### Slow responses\n\n- Check backend latency\n- Increase cache capacity or TTL\n- Review Prometheus metrics for bottlenecks\n\n### Logging\n\nEnable debug logging:\n\n```{$detected_lang}\nextension-registry --log-level debug\n```\n\nEnable JSON logging for structured logs:\n\n```{$detected_lang}\nextension-registry --json-log\n```\n\n## License\n\nPart of the Provisioning Project.\n\n## Contributing\n\nSee main project documentation for contribution guidelines.\n\n## Support\n\nFor issues and questions, please refer to the main provisioning project documentation.