diff --git a/CLAUDE.md b/CLAUDE.md index 68792cf..2081074 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,11 +5,21 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Commands ```bash -composer test # Run all tests -composer test -- --filter=Name # Run single test by name -composer pint # Format code with Laravel Pint +composer test # Run all tests (PHPUnit) +composer test -- --filter=Name # Run single test by name +composer test -- --testsuite=Unit # Run specific test suite +composer pint # Format code with Laravel Pint +./vendor/bin/pint --dirty # Format only changed files ``` +## Coding Standards + +- **UK English**: colour, organisation, centre (never American spellings) +- **Strict types**: `declare(strict_types=1);` in every PHP file +- **Type hints**: All parameters and return types required +- **Testing**: PHPUnit with Orchestra Testbench +- **License**: EUPL-1.2 + ## Architecture ### Event-Driven Module Loading @@ -22,6 +32,8 @@ LifecycleEventProvider::register() └── ModuleRegistry::register() # Wires LazyModuleListener for each event ``` +**Key benefit**: Web requests don't load admin modules; API requests don't load web modules. + ### Frontages Frontages are ServiceProviders in `src/Core/Front/` that fire context-specific lifecycle events: @@ -34,19 +46,22 @@ Frontages are ServiceProviders in `src/Core/Front/` that fire context-specific l | Client | `ClientRoutesRegistering` | `client` | Authenticated SaaS | | Cli | `ConsoleBooting` | - | Artisan commands | | Mcp | `McpToolsRegistering` | - | MCP tool handlers | +| - | `FrameworkBooted` | - | Late-stage initialisation | ### L1 Packages Subdirectories under `src/Core/` are self-contained "L1 packages" with their own Boot.php, migrations, tests, and views: ``` -src/Core/Activity/ # Activity logging +src/Core/Activity/ # Activity logging (wraps spatie/laravel-activitylog) src/Core/Bouncer/ # Security blocking/redirects src/Core/Cdn/ # CDN integration src/Core/Config/ # Dynamic configuration +src/Core/Front/ # Frontage system (Web, Admin, Api, Client, Cli, Mcp) src/Core/Lang/ # Translation system -src/Core/Media/ # Media handling +src/Core/Media/ # Media handling with thumbnail helpers src/Core/Search/ # Search functionality +src/Core/Seo/ # SEO utilities ``` ### Module Pattern @@ -63,6 +78,7 @@ class Boot { $event->views('example', __DIR__.'/Views'); $event->routes(fn () => require __DIR__.'/Routes/web.php'); + $event->livewire('example.widget', ExampleWidget::class); } } ``` @@ -88,20 +104,62 @@ class CreateOrder { use Action; + public function __construct(private OrderService $orders) {} + public function handle(User $user, array $data): Order { - return Order::create($data); + return $this->orders->create($user, $data); } } // Usage: CreateOrder::run($user, $validated); ``` +### Seeder Ordering + +Seeders use PHP attributes for dependency ordering: + +```php +use Core\Database\Seeders\Attributes\SeederPriority; +use Core\Database\Seeders\Attributes\SeederAfter; + +#[SeederPriority(50)] // Lower runs first (default 50) +#[SeederAfter(FeatureSeeder::class)] +class PackageSeeder extends Seeder +{ + public function run(): void + { + if (! Schema::hasTable('packages')) return; // Guard missing tables + // ... + } +} +``` + +### HLCRF Layout System + +Data-driven layouts with five regions (Header, Left, Content, Right, Footer): + +```php +use Core\Front\Components\Layout; + +$page = Layout::make('HCF') // Variant: Header-Content-Footer + ->h(view('header')) + ->c($content) + ->f(view('footer')); +``` + +Variant strings: `C` (content only), `HCF` (standard page), `HLCF` (with sidebar), `HLCRF` (full dashboard). + ## Testing -Uses Orchestra Testbench. Tests can live: +Uses Orchestra Testbench with in-memory SQLite. Tests can live: - `tests/Feature/` and `tests/Unit/` - main test suites - `src/Core/{Package}/Tests/` - L1 package co-located tests - `src/Mod/{Module}/Tests/` - module co-located tests Test fixtures are in `tests/Fixtures/`. + +Base test class provides: +```php +$this->getFixturePath('Mod') // Returns tests/Fixtures/Mod path +``` diff --git a/docs/packages/commerce/architecture.md b/docs/packages/commerce/architecture.md new file mode 100644 index 0000000..a459a8a --- /dev/null +++ b/docs/packages/commerce/architecture.md @@ -0,0 +1,402 @@ +--- +title: Architecture +description: Technical architecture of the core-commerce package +updated: 2026-01-29 +--- + +# Commerce Architecture + +This document describes the technical architecture of the `core-commerce` package, which provides billing, subscriptions, and payment processing for the Host UK platform. + +## Overview + +The commerce module implements a multi-gateway payment system supporting cryptocurrency (BTCPay) and traditional card payments (Stripe). It handles the complete commerce lifecycle from checkout to recurring billing, dunning, and refunds. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Commerce Module │ +├─────────────────────────────────────────────────────────────────┤ +│ Services Layer │ +│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │ +│ │ Commerce │ │ Subscription │ │ Dunning │ │ +│ │ Service │ │ Service │ │ Service │ │ +│ └─────────────┘ └──────────────┘ └───────────────┘ │ +│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │ +│ │ Invoice │ │ Coupon │ │ Tax │ │ +│ │ Service │ │ Service │ │ Service │ │ +│ └─────────────┘ └──────────────┘ └───────────────┘ │ +├─────────────────────────────────────────────────────────────────┤ +│ Gateway Layer │ +│ ┌──────────────────────┐ ┌──────────────────────┐ │ +│ │ BTCPayGateway │ │ StripeGateway │ │ +│ │ (Primary) │ │ (Secondary) │ │ +│ └──────────────────────┘ └──────────────────────┘ │ +│ │ │ │ +│ └────────────┬─────────────┘ │ +│ │ │ +│ ┌────────────▼─────────────┐ │ +│ │ PaymentGatewayContract │ │ +│ └──────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Core Concepts + +### Orderable Interface + +The commerce system uses polymorphic relationships via the `Orderable` contract. Both `Workspace` and `User` models can place orders, enabling: + +- **Workspace orders**: Subscription packages, team features +- **User orders**: Individual boosts, one-time purchases + +```php +interface Orderable +{ + public function getBillingName(): string; + public function getBillingEmail(): string; + public function getBillingAddress(): array; + public function getTaxCountry(): ?string; +} +``` + +### Order Lifecycle + +``` +┌──────────┐ ┌────────────┐ ┌──────────┐ ┌────────┐ +│ pending │───▶│ processing │───▶│ paid │───▶│refunded│ +└──────────┘ └────────────┘ └──────────┘ └────────┘ + │ │ + │ │ + ▼ ▼ +┌──────────┐ ┌──────────┐ +│cancelled │ │ failed │ +└──────────┘ └──────────┘ +``` + +1. **pending**: Order created, awaiting checkout +2. **processing**: Customer redirected to payment gateway +3. **paid**: Payment confirmed, entitlements provisioned +4. **failed**: Payment declined or expired +5. **cancelled**: Customer abandoned checkout +6. **refunded**: Full refund processed + +### Subscription States + +``` +┌────────┐ ┌──────────┐ ┌────────┐ ┌───────────┐ +│ active │───▶│ past_due │───▶│ paused │───▶│ cancelled │ +└────────┘ └──────────┘ └────────┘ └───────────┘ + │ │ │ + ▼ │ │ +┌──────────┐ │ │ +│ trialing │────────┘ │ +└──────────┘ │ + │ │ + └─────────────────────────────┘ +``` + +- **active**: Subscription in good standing +- **trialing**: Within trial period (no payment required) +- **past_due**: Payment failed, within retry window +- **paused**: Billing paused (dunning or user-initiated) +- **cancelled**: Subscription ended + +## Service Layer + +### CommerceService + +Main orchestration service. Coordinates order creation, checkout, and fulfillment. + +```php +// Create an order +$order = $commerce->createOrder($workspace, $package, 'monthly', $coupon); + +// Create checkout session (redirects to gateway) +$checkout = $commerce->createCheckout($order, 'btcpay', $successUrl, $cancelUrl); + +// Fulfill order after payment (called by webhook) +$commerce->fulfillOrder($order, $payment); +``` + +Key responsibilities: +- Gateway selection and initialization +- Customer management across gateways +- Order-to-entitlement provisioning +- Currency formatting and conversion + +### SubscriptionService + +Manages subscription lifecycle without gateway interaction. + +```php +// Create local subscription record +$subscription = $subscriptions->create($workspacePackage, 'monthly'); + +// Handle plan changes with proration +$result = $subscriptions->changePlan($subscription, $newPackage, prorate: true); + +// Pause/unpause with limits +$subscriptions->pause($subscription); +$subscriptions->unpause($subscription); +``` + +Proration calculation: +``` +creditAmount = currentPrice * (daysRemaining / totalPeriodDays) +proratedNewCost = newPrice * (daysRemaining / totalPeriodDays) +netAmount = proratedNewCost - creditAmount +``` + +### DunningService + +Handles failed payment recovery with exponential backoff. + +``` +Day 0: Payment fails → subscription marked past_due +Day 1: First retry +Day 3: Second retry +Day 7: Third retry → subscription paused +Day 14: Workspace suspended (features restricted) +Day 30: Subscription cancelled +``` + +Configuration in `config.php`: +```php +'dunning' => [ + 'retry_days' => [1, 3, 7], + 'suspend_after_days' => 14, + 'cancel_after_days' => 30, + 'initial_grace_hours' => 24, +], +``` + +### TaxService + +Jurisdiction-based tax calculation supporting: +- UK VAT (20%) +- EU VAT via VIES validation +- US state sales tax (nexus-based) +- Australian GST (10%) + +B2B reverse charge is applied automatically when a valid VAT number is provided for EU customers. + +```php +$taxResult = $taxService->calculate($workspace, $amount); +// Returns: TaxResult with taxAmount, taxRate, jurisdiction, isExempt +``` + +## Payment Gateways + +### PaymentGatewayContract + +All gateways implement this interface ensuring consistent behavior: + +```php +interface PaymentGatewayContract +{ + // Identity + public function getIdentifier(): string; + public function isEnabled(): bool; + + // Customer management + public function createCustomer(Workspace $workspace): string; + + // Checkout + public function createCheckoutSession(Order $order, ...): array; + public function getCheckoutSession(string $sessionId): array; + + // Payments + public function charge(Workspace $workspace, int $amountCents, ...): Payment; + public function chargePaymentMethod(PaymentMethod $pm, ...): Payment; + + // Subscriptions + public function createSubscription(Workspace $workspace, ...): Subscription; + public function cancelSubscription(Subscription $sub, bool $immediately): void; + + // Webhooks + public function verifyWebhookSignature(string $payload, string $sig): bool; + public function parseWebhookEvent(string $payload): array; +} +``` + +### BTCPayGateway (Primary) + +Cryptocurrency payment gateway supporting BTC, LTC, XMR. + +**Characteristics:** +- No saved payment methods (each payment is unique) +- No automatic recurring billing (requires customer action) +- Invoice-based workflow with expiry +- HMAC signature verification for webhooks + +**Webhook Events:** +- `InvoiceCreated` → No action +- `InvoiceReceivedPayment` → Order status: processing +- `InvoiceProcessing` → Waiting for confirmations +- `InvoiceSettled` → Fulfill order +- `InvoiceExpired` → Mark order failed + +### StripeGateway (Secondary) + +Traditional card payment gateway. + +**Characteristics:** +- Saved payment methods for recurring +- Automatic subscription billing +- Setup intents for card-on-file +- Stripe Customer Portal integration + +**Webhook Events:** +- `checkout.session.completed` → Fulfill order +- `invoice.paid` → Renew subscription +- `invoice.payment_failed` → Trigger dunning +- `customer.subscription.deleted` → Revoke entitlements + +## Data Models + +### Entity Relationship + +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Workspace │────▶│ Order │────▶│ OrderItem │ +└─────────────┘ └─────────────┘ └─────────────┘ + │ │ + │ ▼ + │ ┌─────────────┐ ┌─────────────┐ + │ │ Invoice │────▶│InvoiceItem │ + │ └─────────────┘ └─────────────┘ + │ │ + │ ▼ + │ ┌─────────────┐ ┌─────────────┐ + └───────────▶│ Payment │────▶│ Refund │ + └─────────────┘ └─────────────┘ + │ + ▼ +┌─────────────┐ ┌─────────────┐ +│ Coupon │────▶│ CouponUsage │ +└─────────────┘ └─────────────┘ +``` + +### Multi-Entity Commerce (M1/M2/M3) + +The commerce module supports a hierarchical entity structure: + +- **M1 (Master Company)**: Source of truth, owns product catalog +- **M2 (Facade/Storefront)**: Selects from M1 catalog, can override content +- **M3 (Dropshipper)**: Full inheritance, no management responsibility + +``` + ┌─────────┐ + │ M1 │ ← Product catalog owner + └────┬────┘ + │ + ┌────────┴────────┐ + │ │ +┌─────▼─────┐ ┌─────▼─────┐ +│ M2 │ │ M2 │ ← Storefronts +└─────┬─────┘ └───────────┘ + │ +┌─────▼─────┐ +│ M3 │ ← Dropshipper +└───────────┘ +``` + +Permission matrix controls which operations each entity type can perform, with a "training mode" for undefined permissions. + +## Event System + +### Domain Events + +```php +// Dispatched automatically on model changes +SubscriptionCreated::class → RewardAgentReferralOnSubscription +SubscriptionRenewed::class → ResetUsageOnRenewal +OrderPaid::class → CreateReferralCommission +``` + +### Listeners + +- `ProvisionSocialHostSubscription`: Product-specific provisioning logic +- `RewardAgentReferralOnSubscription`: Attribute referral for new subscriptions +- `ResetUsageOnRenewal`: Clear usage counters on billing period reset +- `CreateReferralCommission`: Calculate affiliate commission on paid orders + +## Directory Structure + +``` +core-commerce/ +├── Boot.php # ServiceProvider, event registration +├── config.php # All configuration (currencies, gateways, tax) +├── Concerns/ # Traits for models +├── Console/ # Artisan commands (dunning, reminders) +├── Contracts/ # Interfaces (Orderable) +├── Controllers/ # HTTP controllers +│ ├── Api/ # REST API endpoints +│ └── Webhooks/ # Gateway webhook handlers +├── Data/ # DTOs and value objects +├── Events/ # Domain events +├── Exceptions/ # Custom exceptions +├── Jobs/ # Queue jobs +├── Lang/ # Translations +├── Listeners/ # Event listeners +├── Mail/ # Mailable classes +├── Mcp/ # MCP tool handlers +├── Middleware/ # HTTP middleware +├── Migrations/ # Database migrations +├── Models/ # Eloquent models +├── Notifications/ # Laravel notifications +├── routes/ # Route definitions +├── Services/ # Business logic layer +│ └── PaymentGateway/ # Gateway implementations +├── tests/ # Pest tests +└── View/ # Blade templates and Livewire components + ├── Blade/ # Blade templates + └── Modal/ # Livewire components (Admin/Web) +``` + +## Configuration + +All commerce configuration lives in `config.php`: + +```php +return [ + 'currency' => 'GBP', // Default currency + 'currencies' => [...], // Supported currencies, exchange rates + 'gateways' => [ + 'btcpay' => [...], // Primary gateway + 'stripe' => [...], // Secondary gateway + ], + 'billing' => [...], // Invoice prefixes, due days + 'dunning' => [...], // Retry schedule, suspension timing + 'tax' => [...], // Tax rates, VAT validation + 'subscriptions' => [...], // Proration, pause limits + 'checkout' => [...], // Session TTL, country restrictions + 'features' => [...], // Toggle coupons, refunds, trials + 'usage_billing' => [...], // Metered billing settings + 'matrix' => [...], // M1/M2/M3 permission matrix +]; +``` + +## Testing + +Tests use Pest with `RefreshDatabase` trait: + +```bash +# Run all tests +composer test + +# Run specific test file +vendor/bin/pest tests/Feature/CheckoutFlowTest.php + +# Run tests matching pattern +vendor/bin/pest --filter="proration" +``` + +Test categories: +- `CheckoutFlowTest`: End-to-end order flow +- `SubscriptionServiceTest`: Subscription lifecycle, proration +- `DunningServiceTest`: Payment recovery flows +- `WebhookTest`: Gateway webhook handling +- `TaxServiceTest`: Tax calculation, VAT validation +- `CouponServiceTest`: Discount application +- `RefundServiceTest`: Refund processing diff --git a/docs/packages/commerce/security.md b/docs/packages/commerce/security.md new file mode 100644 index 0000000..e69b7e0 --- /dev/null +++ b/docs/packages/commerce/security.md @@ -0,0 +1,327 @@ +--- +title: Security +description: Security considerations and audit notes for core-commerce +updated: 2026-01-29 +--- + +# Security Considerations + +This document outlines security controls, known risks, and recommendations for the `core-commerce` package. + +## Authentication & Authorisation + +### API Authentication + +| Endpoint Type | Authentication Method | Notes | +|--------------|----------------------|-------| +| Webhooks (`/api/webhooks/*`) | HMAC signature | Gateway-specific verification | +| Billing API (`/api/commerce/*`) | Laravel `auth` middleware | Session/Sanctum token | +| Provisioning API | Bearer token (planned) | Currently commented out | + +### Webhook Security + +Both payment gateways use HMAC signature verification: + +**BTCPay:** +```php +// Signature in BTCPay-Sig header +$expectedSignature = hash_hmac('sha256', $payload, $webhookSecret); +hash_equals($expectedSignature, $providedSignature); +``` + +**Stripe:** +```php +// Uses Stripe SDK signature verification +\Stripe\Webhook::constructEvent($payload, $signature, $webhookSecret); +``` + +### Current Gaps + +1. **No idempotency enforcement** - Webhook handlers check order state (`isPaid()`) but don't store processed event IDs. Replay attacks within the state-check window are possible. + +2. **No IP allowlisting** - Webhook endpoints accept connections from any IP. Consider adding gateway IP ranges to allowlist. + +3. **Rate limiting is global** - Current throttle (`120,1`) applies globally, not per-IP. A malicious actor could exhaust the limit. + +## Data Protection + +### Sensitive Data Handling + +| Data Type | Storage | Protection | +|-----------|---------|------------| +| Card details | Never stored | Handled by gateways via redirect | +| Gateway API keys | Environment variables | Not in codebase | +| Webhook secrets | Environment variables | Used for HMAC | +| Tax IDs (VAT numbers) | Encrypted column recommended | Currently plain text | +| Billing addresses | Database JSON column | Consider encryption | + +### PCI DSS Compliance + +The commerce module is designed to be **PCI DSS SAQ A** compliant: + +- No card data ever touches Host UK servers +- Checkout redirects to hosted payment pages (BTCPay/Stripe) +- Only tokenized references (customer IDs, payment method IDs) are stored +- No direct card number input in application + +### GDPR Considerations + +Personal data in commerce models: +- `orders.billing_name`, `billing_email`, `billing_address` +- `invoices.billing_*` fields +- `referrals.ip_address`, `user_agent` + +**Recommendations:** +- Implement data export for billing history (right of access) +- Add retention policy for old orders/invoices +- Hash or truncate IP addresses after 90 days +- Document lawful basis for processing (contract performance) + +## Input Validation + +### Current Controls + +```php +// Coupon codes normalized +$data['code'] = strtoupper($data['code']); + +// Order totals calculated server-side +$taxResult = $this->taxService->calculateForOrderable($orderable, $taxableAmount); +$total = $subtotal - $discountAmount + $setupFee + $taxResult->taxAmount; + +// Gateway responses logged without sensitive data +protected function sanitiseErrorMessage($response): string +``` + +### Validation Gaps + +1. **Billing address structure** - Accepted as array without schema validation +2. **Coupon code length** - No maximum length enforcement +3. **Metadata fields** - JSON columns accept arbitrary structure + +### Recommendations + +```php +// Add validation rules +$rules = [ + 'billing_address.line1' => ['required', 'string', 'max:255'], + 'billing_address.city' => ['required', 'string', 'max:100'], + 'billing_address.country' => ['required', 'string', 'size:2'], + 'billing_address.postal_code' => ['required', 'string', 'max:20'], + 'coupon_code' => ['nullable', 'string', 'max:32', 'alpha_dash'], +]; +``` + +## Transaction Security + +### Idempotency + +Order creation supports idempotency keys: + +```php +if ($idempotencyKey) { + $existingOrder = Order::where('idempotency_key', $idempotencyKey)->first(); + if ($existingOrder) { + return $existingOrder; + } +} +``` + +**Gap:** Webhooks don't use idempotency. Add `WebhookEvent` lookup: + +```php +if (WebhookEvent::where('idempotency_key', $event['id'])->exists()) { + return response('Already processed', 200); +} +``` + +### Race Conditions + +**Identified risks:** + +1. **Concurrent subscription operations** - Pause/unpause/cancel without locks +2. **Coupon redemption** - `incrementUsage()` without atomic check +3. **Payout requests** - Commission assignment without row locks + +**Mitigation:** Add `FOR UPDATE` locks or use atomic operations: + +```php +// Use DB::transaction with locking +$commission = ReferralCommission::lockForUpdate() + ->where('id', $commissionId) + ->where('status', 'matured') + ->first(); +``` + +### Amount Verification + +**Current state:** BTCPay webhook trusts order total without verifying against gateway response. + +**Risk:** Under/overpayment handling undefined. + +**Recommendation:** +```php +$settledAmount = $invoiceData['raw']['amount'] ?? null; +if ($settledAmount !== null && abs($settledAmount - $order->total) > 0.01) { + Log::warning('Payment amount mismatch', [ + 'order_total' => $order->total, + 'settled_amount' => $settledAmount, + ]); + // Handle partial payment or overpayment +} +``` + +## Fraud Prevention + +### Current Controls + +- Checkout session TTL (30 minutes default) +- Rate limiting on API endpoints +- Idempotency keys for order creation + +### Missing Controls + +1. **Velocity checks** - No detection of rapid-fire order attempts +2. **Geo-blocking** - No IP geolocation validation against billing country +3. **Card testing detection** - No small-amount charge pattern detection +4. **Device fingerprinting** - No device/browser tracking + +### Recommendations + +```php +// Add CheckoutRateLimiter to createCheckout +$rateLimiter = app(CheckoutRateLimiter::class); +if (!$rateLimiter->attempt($workspace->id)) { + throw new TooManyCheckoutAttemptsException(); +} + +// Consider Stripe Radar for card payments +'stripe' => [ + 'radar_enabled' => true, + 'block_threshold' => 75, // Block if risk score > 75 +], +``` + +## Audit Logging + +### What's Logged + +- Order status changes via `LogsActivity` trait +- Subscription status changes via `LogsActivity` trait +- Webhook events via `WebhookLogger` service +- Payment failures and retries + +### What's Not Logged + +- Failed authentication attempts on billing API +- Coupon validation failures +- Tax ID validation API calls +- Admin actions on refunds/credit notes + +### Recommendations + +Add audit events for: +```php +// Sensitive operations +activity('commerce') + ->causedBy($admin) + ->performedOn($refund) + ->withProperties(['reason' => $reason]) + ->log('Refund processed'); +``` + +## Secrets Management + +### Environment Variables + +```bash +# Gateway credentials +BTCPAY_URL=https://pay.host.uk.com +BTCPAY_STORE_ID=xxx +BTCPAY_API_KEY=xxx +BTCPAY_WEBHOOK_SECRET=xxx + +STRIPE_KEY=pk_xxx +STRIPE_SECRET=sk_xxx +STRIPE_WEBHOOK_SECRET=whsec_xxx + +# Tax API credentials +COMMERCE_EXCHANGE_RATE_API_KEY=xxx +``` + +### Key Rotation + +No automated key rotation currently implemented. + +**Recommendations:** +- Store credentials in secrets manager (AWS Secrets Manager, HashiCorp Vault) +- Implement webhook secret rotation with grace period +- Alert on API key exposure in logs + +## Security Checklist + +### Before Production + +- [ ] Webhook secrets are unique per environment +- [ ] Rate limiting tuned for expected traffic +- [ ] Error messages don't leak internal details +- [ ] API keys not in version control +- [ ] SSL/TLS required for all endpoints + +### Ongoing + +- [ ] Monitor webhook failure rates +- [ ] Review failed payment patterns weekly +- [ ] Audit refund activity monthly +- [ ] Update gateway SDKs quarterly +- [ ] Penetration test annually + +## Incident Response + +### Compromised API Key + +1. Revoke key immediately in gateway dashboard +2. Generate new key +3. Update environment variable +4. Restart application +5. Audit recent transactions for anomalies + +### Webhook Secret Leaked + +1. Generate new secret in gateway +2. Update both old and new in config (grace period) +3. Monitor for invalid signature attempts +4. Remove old secret after 24 hours + +### Suspected Fraud + +1. Pause affected subscription +2. Flag orders for manual review +3. Contact gateway for chargeback advice +4. Document in incident log + +## Third-Party Dependencies + +### Gateway SDKs + +| Package | Version | Security Notes | +|---------|---------|----------------| +| `stripe/stripe-php` | ^12.0 | Keep updated for security patches | + +### Other Dependencies + +- `spatie/laravel-activitylog` - Audit logging +- `barryvdh/laravel-dompdf` - PDF generation (ensure no user input in HTML) + +### Dependency Audit + +Run regularly: +```bash +composer audit +``` + +## Contact + +Report security issues to: security@host.uk.com + +Do not open public issues for security vulnerabilities. diff --git a/docs/packages/commerce/webhooks.md b/docs/packages/commerce/webhooks.md new file mode 100644 index 0000000..cf66a92 --- /dev/null +++ b/docs/packages/commerce/webhooks.md @@ -0,0 +1,387 @@ +--- +title: Webhooks +description: Payment gateway webhook handling documentation +updated: 2026-01-29 +--- + +# Webhook Handling + +This document describes how payment gateway webhooks are processed in the commerce module. + +## Overview + +Payment gateways notify the application of payment events via webhooks. These are HTTP POST requests sent to predefined endpoints when payment state changes. + +``` +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ BTCPay │ │ Host UK │ │ Stripe │ +│ Server │ │ Commerce │ │ API │ +└──────┬───────┘ └──────┬───────┘ └──────┬───────┘ + │ │ │ + │ POST /api/webhooks/ │ │ + │ btcpay │ │ + │ ───────────────────────▶│ │ + │ │ │ + │ │ POST /api/webhooks/ │ + │ │ stripe │ + │ │◀───────────────────────── + │ │ │ +``` + +## Endpoints + +| Gateway | Endpoint | Signature Header | +|---------|----------|------------------| +| BTCPay | `POST /api/webhooks/btcpay` | `BTCPay-Sig` | +| Stripe | `POST /api/webhooks/stripe` | `Stripe-Signature` | + +Both endpoints: +- Rate limited: 120 requests per minute +- No authentication middleware (signature verification only) +- Return 200 for successful processing (even if event is skipped) +- Return 401 for invalid signatures +- Return 500 for processing errors (triggers gateway retry) + +## BTCPay Webhooks + +### Configuration + +In BTCPay Server dashboard: +1. Navigate to Store Settings > Webhooks +2. Create webhook with URL: `https://yourdomain.com/api/webhooks/btcpay` +3. Select events to send +4. Copy webhook secret to `BTCPAY_WEBHOOK_SECRET` + +### Event Types + +| BTCPay Event | Mapped Type | Action | +|--------------|-------------|--------| +| `InvoiceCreated` | `invoice.created` | No action | +| `InvoiceReceivedPayment` | `invoice.payment_received` | Order → processing | +| `InvoiceProcessing` | `invoice.processing` | Order → processing | +| `InvoiceSettled` | `invoice.paid` | Fulfil order | +| `InvoiceExpired` | `invoice.expired` | Order → failed | +| `InvoiceInvalid` | `invoice.failed` | Order → failed | + +### Processing Flow + +```php +// BTCPayWebhookController::handle() + +1. Verify signature + └── 401 if invalid + +2. Parse event + └── Extract type, invoice ID, metadata + +3. Log webhook event + └── WebhookLogger creates audit record + +4. Route to handler (in transaction) + ├── invoice.paid → handleSettled() + ├── invoice.expired → handleExpired() + └── default → handleUnknownEvent() + +5. Return response + └── 200 OK (even for skipped events) +``` + +### Invoice Settlement Handler + +```php +protected function handleSettled(array $event): Response +{ + // 1. Find order by gateway session ID + $order = Order::where('gateway', 'btcpay') + ->where('gateway_session_id', $event['id']) + ->first(); + + // 2. Skip if already paid (idempotency) + if ($order->isPaid()) { + return response('Already processed', 200); + } + + // 3. Create payment record + $payment = Payment::create([ + 'gateway' => 'btcpay', + 'gateway_payment_id' => $event['id'], + 'amount' => $order->total, + 'status' => 'succeeded', + // ... + ]); + + // 4. Fulfil order (provisions entitlements, creates invoice) + $this->commerce->fulfillOrder($order, $payment); + + // 5. Send confirmation email + $this->sendOrderConfirmation($order); + + return response('OK', 200); +} +``` + +## Stripe Webhooks + +### Configuration + +In Stripe Dashboard: +1. Navigate to Developers > Webhooks +2. Add endpoint: `https://yourdomain.com/api/webhooks/stripe` +3. Select events to listen for +4. Copy signing secret to `STRIPE_WEBHOOK_SECRET` + +### Event Types + +| Stripe Event | Action | +|--------------|--------| +| `checkout.session.completed` | Fulfil order, create subscription | +| `invoice.paid` | Renew subscription period | +| `invoice.payment_failed` | Mark past_due, trigger dunning | +| `customer.subscription.created` | Fallback (usually handled by checkout) | +| `customer.subscription.updated` | Sync status, period dates | +| `customer.subscription.deleted` | Cancel, revoke entitlements | +| `payment_method.attached` | Store payment method | +| `payment_method.detached` | Deactivate payment method | +| `payment_method.updated` | Update card details | +| `setup_intent.succeeded` | Attach payment method from setup flow | + +### Checkout Completion Handler + +```php +protected function handleCheckoutCompleted(array $event): Response +{ + $session = $event['raw']['data']['object']; + $orderId = $session['metadata']['order_id']; + + // Find and validate order + $order = Order::find($orderId); + if (!$order || $order->isPaid()) { + return response('Already processed', 200); + } + + // Create payment record + $payment = Payment::create([ + 'gateway' => 'stripe', + 'gateway_payment_id' => $session['payment_intent'], + 'amount' => $session['amount_total'] / 100, + 'status' => 'succeeded', + ]); + + // Handle subscription if present + if (!empty($session['subscription'])) { + $this->createOrUpdateSubscriptionFromSession($order, $session); + } + + // Fulfil order + $this->commerce->fulfillOrder($order, $payment); + + return response('OK', 200); +} +``` + +### Subscription Invoice Handler + +```php +protected function handleInvoicePaid(array $event): Response +{ + $invoice = $event['raw']['data']['object']; + $subscriptionId = $invoice['subscription']; + + // Find subscription + $subscription = Subscription::where('gateway', 'stripe') + ->where('gateway_subscription_id', $subscriptionId) + ->first(); + + // Update period dates + $subscription->renew( + Carbon::createFromTimestamp($invoice['period_start']), + Carbon::createFromTimestamp($invoice['period_end']) + ); + + // Create payment record + $payment = Payment::create([...]); + + // Create local invoice + $this->invoiceService->createForRenewal($subscription->workspace, ...); + + return response('OK', 200); +} +``` + +## Signature Verification + +### BTCPay + +```php +// BTCPayGateway::verifyWebhookSignature() + +$providedSignature = $signature; +if (str_starts_with($signature, 'sha256=')) { + $providedSignature = substr($signature, 7); +} + +$expectedSignature = hash_hmac('sha256', $payload, $this->webhookSecret); + +return hash_equals($expectedSignature, $providedSignature); +``` + +### Stripe + +```php +// StripeGateway::verifyWebhookSignature() + +try { + \Stripe\Webhook::constructEvent($payload, $signature, $this->webhookSecret); + return true; +} catch (\Exception $e) { + return false; +} +``` + +## Webhook Logging + +All webhook events are logged via `WebhookLogger`: + +```php +// Start logging +$this->webhookLogger->startFromParsedEvent('btcpay', $event, $payload, $request); + +// Link to entities for audit trail +$this->webhookLogger->linkOrder($order); +$this->webhookLogger->linkSubscription($subscription); + +// Mark outcome +$this->webhookLogger->success($response); +$this->webhookLogger->fail($errorMessage, $statusCode); +$this->webhookLogger->skip($reason); +``` + +Logged data includes: +- Event type and ID +- Raw payload (encrypted) +- IP address and user agent +- Processing outcome +- Related order/subscription IDs + +## Error Handling + +### Gateway Retries + +Both gateways retry failed webhooks: + +| Gateway | Retry Schedule | Max Attempts | +|---------|---------------|--------------| +| BTCPay | Exponential backoff | Configurable | +| Stripe | Exponential over 3 days | ~20 attempts | + +**Important:** Return `200 OK` even for events that are skipped or already processed. Only return `500` for actual processing errors that should be retried. + +### Transaction Safety + +All webhook handlers wrap processing in database transactions: + +```php +try { + $response = DB::transaction(function () use ($event) { + return match ($event['type']) { + 'invoice.paid' => $this->handleSettled($event), + // ... + }; + }); + return $response; +} catch (\Exception $e) { + Log::error('Webhook processing error', [...]); + return response('Processing error', 500); +} +``` + +## Testing Webhooks + +### Local Development + +Use gateway CLI tools to send test webhooks: + +**BTCPay:** +```bash +# Trigger test webhook from BTCPay admin +# Or use btcpay-cli if available +``` + +**Stripe:** +```bash +# Forward webhooks to local +stripe listen --forward-to localhost:8000/api/webhooks/stripe + +# Trigger specific event +stripe trigger checkout.session.completed +``` + +### Automated Tests + +See `tests/Feature/WebhookTest.php` for webhook handler tests: + +```php +test('btcpay settled webhook fulfils order', function () { + $order = Order::factory()->create(['status' => 'processing']); + + $payload = json_encode([ + 'type' => 'InvoiceSettled', + 'invoiceId' => $order->gateway_session_id, + // ... + ]); + + $signature = hash_hmac('sha256', $payload, config('commerce.gateways.btcpay.webhook_secret')); + + $response = $this->postJson('/api/webhooks/btcpay', [], [ + 'BTCPay-Sig' => $signature, + 'Content-Type' => 'application/json', + ]); + + $response->assertStatus(200); + expect($order->fresh()->status)->toBe('paid'); +}); +``` + +## Troubleshooting + +### Common Issues + +**401 Invalid Signature** +- Check webhook secret matches environment variable +- Ensure raw payload is used (not parsed JSON) +- Verify signature header name is correct + +**Order Not Found** +- Check `gateway_session_id` matches invoice ID +- Verify order was created before webhook arrived +- Check for typos in metadata passed to gateway + +**Duplicate Processing** +- Normal behavior if webhook is retried +- Order state check (`isPaid()`) prevents double fulfillment +- Consider adding idempotency key storage + +### Debug Logging + +Enable verbose logging temporarily: + +```php +// In webhook controller +Log::debug('Webhook payload', [ + 'type' => $event['type'], + 'id' => $event['id'], + 'raw' => $event['raw'], +]); +``` + +### Webhook Event Viewer + +Query logged events: + +```sql +SELECT * FROM commerce_webhook_events +WHERE event_type = 'InvoiceSettled' + AND status = 'failed' +ORDER BY created_at DESC +LIMIT 10; +``` diff --git a/docs/packages/content/architecture.md b/docs/packages/content/architecture.md new file mode 100644 index 0000000..405652d --- /dev/null +++ b/docs/packages/content/architecture.md @@ -0,0 +1,422 @@ +--- +title: Architecture +description: Technical architecture of the core-content package +updated: 2026-01-29 +--- + +# Architecture + +The `core-content` package provides headless CMS functionality for the Host UK platform. It handles content management, AI-powered generation, revision history, webhooks for external CMS integration, and search capabilities. + +## Package Overview + +**Namespace:** `Core\Mod\Content\` +**Entry Point:** `Boot.php` (Laravel Service Provider) +**Dependencies:** +- `core-php` (Foundation framework, events) +- `core-tenant` (Workspaces, users, entitlements) +- Optional: `core-agentic` (AI services for content generation) +- Optional: `core-mcp` (MCP tool handlers) + +## Directory Structure + +``` +core-content/ +├── Boot.php # Service provider with event listeners +├── config.php # Package configuration +├── Models/ # Eloquent models (10 models) +├── Services/ # Business logic services +├── Controllers/ # API and web controllers +│ └── Api/ # REST API controllers +├── Jobs/ # Queue jobs +├── Mcp/ # MCP tool handlers +│ └── Handlers/ # Individual MCP tools +├── Concerns/ # Traits +├── Console/ # Artisan commands +│ └── Commands/ # Command implementations +├── Enums/ # PHP enums +├── Migrations/ # Database migrations +├── Observers/ # Model observers +├── routes/ # Route definitions +├── View/ # Livewire components and Blade views +│ ├── Modal/ # Livewire components +│ └── Blade/ # Blade templates +├── tests/ # Test suite +└── docs/ # Documentation +``` + +## Core Concepts + +### Content Items + +The primary content model. Supports multiple content types and sources: + +```php +// Content types (where content originates) +enum ContentType: string { + case NATIVE = 'native'; // Created in Host Hub editor + case HOSTUK = 'hostuk'; // Alias for native (backwards compat) + case SATELLITE = 'satellite'; // Per-service content + case WORDPRESS = 'wordpress'; // Legacy synced content +} +``` + +Content items belong to workspaces and have: +- Title, slug, excerpt, content (HTML/Markdown/JSON) +- Status (draft, publish, future, private, pending) +- Author and last editor tracking +- Revision history +- Taxonomy (categories, tags) +- SEO metadata +- Preview tokens for sharing unpublished content +- CDN cache invalidation tracking + +### Content Briefs + +Briefs drive AI-powered content generation. They define what content to create: + +```php +// Brief content types (what to generate) +enum BriefContentType: string { + case HELP_ARTICLE = 'help_article'; // Documentation + case BLOG_POST = 'blog_post'; // Blog articles + case LANDING_PAGE = 'landing_page'; // Marketing pages + case SOCIAL_POST = 'social_post'; // Social media +} +``` + +Brief workflow: `pending` -> `queued` -> `generating` -> `review` -> `published` + +### Revisions + +Every content change creates an immutable revision snapshot. Revisions support: +- Change type tracking (edit, autosave, restore, publish) +- Word/character count tracking +- Side-by-side diff comparison with LCS algorithm +- Configurable retention policies (max count, max age) + +## Service Layer + +### AIGatewayService + +Orchestrates two-stage AI content generation: + +1. **Stage 1: Draft (Gemini)** - Fast, cost-effective initial generation +2. **Stage 2: Refine (Claude)** - Quality refinement and brand voice alignment + +```php +$gateway = app(AIGatewayService::class); + +// Two-stage pipeline +$result = $gateway->generateAndRefine($brief); + +// Or individual stages +$draft = $gateway->generateDraft($brief); +$refined = $gateway->refineDraft($brief, $draftContent); + +// Direct Claude generation (skip Gemini) +$content = $gateway->generateDirect($brief); +``` + +### ContentSearchService + +Full-text search with multiple backend support: + +```php +// Backends (configured via CONTENT_SEARCH_BACKEND) +const BACKEND_DATABASE = 'database'; // LIKE queries with relevance +const BACKEND_SCOUT_DATABASE = 'scout_database'; // Laravel Scout +const BACKEND_MEILISEARCH = 'meilisearch'; // Laravel Scout + Meilisearch +``` + +Features: +- Relevance scoring (title > slug > excerpt > content) +- Filters: type, status, category, tag, date range, content_type +- Autocomplete suggestions +- Re-indexing support for Scout backends + +### WebhookRetryService + +Handles failed webhook processing with exponential backoff: + +``` +Retry intervals: 1m, 5m, 15m, 1h, 4h +Max retries: 5 (configurable per webhook) +``` + +### ContentRender + +Public-facing content renderer with caching: +- Homepage, blog listing, post, page rendering +- Cache TTL: 1 hour production, 1 minute development +- Cache key sanitisation for special characters + +### CdnPurgeService + +CDN cache invalidation via Bunny CDN: +- Triggered by ContentItemObserver on publish/update +- URL-based and tag-based purging +- Workspace-level cache clearing + +## Event-Driven Architecture + +The package uses the event-driven module loading pattern from `core-php`: + +```php +class Boot extends ServiceProvider +{ + public static array $listens = [ + WebRoutesRegistering::class => 'onWebRoutes', + ApiRoutesRegistering::class => 'onApiRoutes', + ConsoleBooting::class => 'onConsole', + McpToolsRegistering::class => 'onMcpTools', + ]; +} +``` + +Handlers register: +- **Web Routes:** Public blog, help pages, content preview +- **API Routes:** REST API for briefs, media, search, generation +- **Console:** Artisan commands for scheduling, pruning +- **MCP Tools:** AI agent content management tools + +## API Structure + +### Authenticated Endpoints (Session or API Key) + +``` +# Content Briefs +GET /api/content/briefs # List briefs +POST /api/content/briefs # Create brief +GET /api/content/briefs/{id} # Get brief +PUT /api/content/briefs/{id} # Update brief +DELETE /api/content/briefs/{id} # Delete brief +POST /api/content/briefs/bulk # Bulk create +GET /api/content/briefs/next # Next ready for processing + +# AI Generation (rate limited: 10/min) +POST /api/content/generate/draft # Generate draft (Gemini) +POST /api/content/generate/refine # Refine draft (Claude) +POST /api/content/generate/full # Full pipeline +POST /api/content/generate/social # Social posts from content + +# Content Search (rate limited: 60/min) +GET /api/content/search # Full-text search +GET /api/content/search/suggest # Autocomplete +GET /api/content/search/info # Backend info +POST /api/content/search/reindex # Trigger re-index + +# Revisions +GET /api/content/items/{id}/revisions # List revisions +GET /api/content/revisions/{id} # Get revision +POST /api/content/revisions/{id}/restore # Restore revision +GET /api/content/revisions/{id}/compare/{other} # Compare + +# Preview +POST /api/content/items/{id}/preview/generate # Generate preview link +DELETE /api/content/items/{id}/preview/revoke # Revoke preview link +``` + +### Public Endpoints + +``` +# Webhooks (signature verified, no auth) +POST /api/content/webhooks/{endpoint} # Receive external webhooks + +# Web Routes +GET /blog # Blog listing +GET /blog/{slug} # Blog post +GET /help # Help centre +GET /help/{slug} # Help article +GET /content/preview/{id} # Preview content +``` + +## Rate Limiting + +Defined in `Boot::configureRateLimiting()`: + +| Limiter | Authenticated | Unauthenticated | +|---------|---------------|-----------------| +| `content-generate` | 10/min per user/workspace | 2/min per IP | +| `content-briefs` | 30/min per user | 5/min per IP | +| `content-webhooks` | 60/min per endpoint | 30/min per IP | +| `content-search` | Configurable (default 60/min) | 20/min per IP | + +## MCP Tools + +Seven MCP tools for AI agent integration: + +| Tool | Description | +|------|-------------| +| `content_list` | List content items with filters | +| `content_read` | Read content by ID or slug | +| `content_search` | Full-text search | +| `content_create` | Create new content | +| `content_update` | Update existing content | +| `content_delete` | Soft delete content | +| `content_taxonomies` | List categories and tags | + +All tools: +- Require workspace resolution +- Check entitlements (`content.mcp_access`, `content.items`) +- Log actions to MCP session +- Return structured responses + +## Data Flow + +### Content Creation via MCP + +``` +Agent Request + ↓ +ContentCreateHandler::handle() + ↓ +resolveWorkspace() → Workspace model + ↓ +checkEntitlement() → EntitlementService + ↓ +ContentItem::create() + ↓ +createRevision() → ContentRevision + ↓ +recordUsage() → EntitlementService + ↓ +Response with content ID +``` + +### Webhook Processing + +``` +External CMS + ↓ +POST /api/content/webhooks/{endpoint} + ↓ +ContentWebhookController::receive() + ↓ +Verify signature → ContentWebhookEndpoint::verifySignature() + ↓ +Check type allowed → ContentWebhookEndpoint::isTypeAllowed() + ↓ +Create ContentWebhookLog + ↓ +Dispatch ProcessContentWebhook job + ↓ +Job::handle() + ↓ +Process based on event type (wordpress.*, cms.*, generic.*) + ↓ +Create/Update/Delete ContentItem + ↓ +Mark log completed +``` + +### AI Generation Pipeline + +``` +ContentBrief + ↓ +GenerateContentJob dispatched + ↓ +Stage 1: AIGatewayService::generateDraft() + ↓ +GeminiService::generate() → Draft content + ↓ +Brief::markDraftComplete() + ↓ +Stage 2: AIGatewayService::refineDraft() + ↓ +ClaudeService::generate() → Refined content + ↓ +Brief::markRefined() + ↓ +AIUsage records created for each stage +``` + +## Configuration + +Key settings in `config.php`: + +```php +return [ + 'generation' => [ + 'default_timeout' => env('CONTENT_GENERATION_TIMEOUT', 300), + 'timeouts' => [ + 'help_article' => 180, + 'blog_post' => 240, + 'landing_page' => 300, + 'social_post' => 60, + ], + 'max_retries' => 3, + 'backoff' => [30, 60, 120], + ], + 'revisions' => [ + 'max_per_item' => env('CONTENT_MAX_REVISIONS', 50), + 'max_age_days' => 180, + 'preserve_published' => true, + ], + 'cache' => [ + 'ttl' => env('CONTENT_CACHE_TTL', 3600), + 'prefix' => 'content:render', + ], + 'search' => [ + 'backend' => env('CONTENT_SEARCH_BACKEND', 'database'), + 'min_query_length' => 2, + 'max_per_page' => 50, + 'default_per_page' => 20, + 'rate_limit' => 60, + ], +]; +``` + +## Database Schema + +### Primary Tables + +| Table | Purpose | +|-------|---------| +| `content_items` | Content storage (posts, pages) | +| `content_revisions` | Version history | +| `content_taxonomies` | Categories and tags | +| `content_item_taxonomy` | Pivot table | +| `content_media` | Media attachments | +| `content_authors` | Author profiles | +| `content_briefs` | AI generation briefs | +| `content_tasks` | Scheduled content tasks | +| `content_webhook_endpoints` | Webhook configurations | +| `content_webhook_logs` | Webhook processing logs | +| `ai_usage` | AI API usage tracking | +| `prompts` | AI prompt templates | +| `prompt_versions` | Prompt version history | + +### Key Indexes + +- `content_items`: Composite indexes on `(workspace_id, slug, type)`, `(workspace_id, status, type)`, `(workspace_id, status, content_type)` +- `content_revisions`: Index on `(content_item_id, revision_number)` +- `content_webhook_logs`: Index on `(workspace_id, status)`, `(status, created_at)` + +## Extension Points + +### Adding New Content Types + +1. Add value to `ContentType` enum +2. Update `ContentType::isNative()` if applicable +3. Add any type-specific scopes to `ContentItem` + +### Adding New AI Generation Types + +1. Add value to `BriefContentType` enum +2. Add timeout to `config.php` generation.timeouts +3. Add prompt in `AIGatewayService::getDraftSystemPrompt()` + +### Adding New Webhook Event Types + +1. Add to `ContentWebhookEndpoint::ALLOWED_TYPES` +2. Add handler in `ProcessContentWebhook::processWordPress()` or `processCms()` +3. Add event type mapping in `ContentWebhookController::normaliseEventType()` + +### Adding New MCP Tools + +1. Create handler in `Mcp/Handlers/` implementing `McpToolHandler` +2. Define `schema()` with tool name, description, input schema +3. Implement `handle()` with workspace resolution and entitlement checks +4. Register in `Boot::onMcpTools()` diff --git a/docs/packages/content/security.md b/docs/packages/content/security.md new file mode 100644 index 0000000..0b3d220 --- /dev/null +++ b/docs/packages/content/security.md @@ -0,0 +1,389 @@ +--- +title: Security +description: Security considerations and audit notes for core-content +updated: 2026-01-29 +--- + +# Security + +This document covers security considerations, known risks, and recommended mitigations for the `core-content` package. + +## Authentication and Authorisation + +### API Authentication + +The content API supports two authentication methods: + +1. **Session Authentication** (`auth` middleware) + - For browser-based access + - CSRF protection via Laravel's standard middleware + +2. **API Key Authentication** (`api.auth` middleware) + - For programmatic access + - Keys prefixed with `hk_` + - Scope enforcement via `api.scope.enforce` middleware + +### Webhook Authentication + +Webhooks use HMAC signature verification instead of session/API key auth: + +```php +// Signature verification in ContentWebhookEndpoint +public function verifySignature(string $payload, ?string $signature): bool +{ + $expectedSignature = hash_hmac('sha256', $payload, $this->secret); + return hash_equals($expectedSignature, $signature); +} +``` + +**Supported signature headers:** +- `X-Signature` +- `X-Hub-Signature-256` (GitHub format) +- `X-WP-Webhook-Signature` (WordPress format) +- `X-Content-Signature` +- `Signature` + +### MCP Tool Authentication + +MCP tools authenticate via the MCP session context. Workspace access is verified through: +- Workspace resolution (by slug or ID) +- Entitlement checks (`content.mcp_access`, `content.items`) + +## Known Security Considerations + +### HIGH: HTML Sanitisation Fallback + +**Location:** `Models/ContentItem.php:333-351` + +**Issue:** The `getSanitisedContent()` method falls back to `strip_tags()` if HTMLPurifier is unavailable. This is insufficient for XSS protection. + +```php +// Current fallback (insufficient) +$allowedTags = '


......'; +return strip_tags($content, $allowedTags); +``` + +**Risk:** XSS attacks via crafted HTML in content body. + +**Mitigation:** +1. Ensure HTMLPurifier is installed in production +2. Add package check in boot to fail loudly if missing +3. Consider using `voku/anti-xss` as a lighter alternative + +### HIGH: Webhook Signature Optional + +**Location:** `Models/ContentWebhookEndpoint.php:205-210` + +**Issue:** When no secret is configured, signature verification is skipped: + +```php +if (empty($this->secret)) { + return true; // Accepts all requests +} +``` + +**Risk:** Unauthenticated webhook injection if endpoint has no secret. + +**Mitigation:** +1. Require secrets for all production endpoints +2. Add explicit `allow_unsigned` flag if intentional +3. Log warning when unsigned webhooks are accepted +4. Rate limit unsigned endpoints more aggressively + +### MEDIUM: Workspace Access in MCP Handlers + +**Location:** `Mcp/Handlers/*.php` + +**Issue:** Workspace resolution allows lookup by ID: + +```php +return Workspace::where('slug', $slug) + ->orWhere('id', $slug) + ->first(); +``` + +**Risk:** If an attacker knows a workspace ID, they could potentially access content without being a workspace member. + +**Mitigation:** +1. Always verify workspace membership after resolution +2. Use entitlement checks (already present but verify coverage) +3. Consider removing ID-based lookup for MCP + +### MEDIUM: Preview Token Enumeration + +**Location:** `Controllers/ContentPreviewController.php` + +**Issue:** No rate limiting on preview token generation endpoint. An attacker could probe for valid content IDs. + +**Mitigation:** +1. Add rate limiting (30/min per user) +2. Use constant-time responses regardless of content existence +3. Consider using UUIDs instead of sequential IDs for preview URLs + +### LOW: Webhook Payload Content Types + +**Location:** `Jobs/ProcessContentWebhook.php:288-289` + +**Issue:** Content type from external webhook is assigned directly: + +```php +$contentItem->content_type = ContentType::NATIVE; +``` + +**Risk:** External systems could potentially inject invalid content types. + +**Mitigation:** +1. Validate against `ContentType` enum +2. Default to a safe type if validation fails +3. Log invalid types for monitoring + +## Input Validation + +### API Request Validation + +All API controllers use Laravel's validation: + +```php +$validated = $request->validate([ + 'q' => 'required|string|min:2|max:500', + 'type' => 'nullable|string|in:post,page', + 'status' => 'nullable', + // ... +]); +``` + +**Validated inputs:** +- Search queries (min/max length, string type) +- Content types (enum validation) +- Pagination (min/max values) +- Date ranges (date format, logical order) + +### MCP Input Validation + +MCP handlers validate via JSON schema: + +```php +'inputSchema' => [ + 'type' => 'object', + 'properties' => [ + 'workspace' => ['type' => 'string'], + 'title' => ['type' => 'string'], + 'type' => ['type' => 'string', 'enum' => ['post', 'page']], + ], + 'required' => ['workspace', 'title'], +] +``` + +### Webhook Payload Validation + +Webhook payloads undergo: +- JSON decode validation +- Event type normalisation +- Content ID extraction with fallbacks + +**Note:** Payload content is stored in JSON column without full validation. Processing logic handles missing/invalid fields gracefully. + +## Rate Limiting + +### Configured Limiters + +| Endpoint | Auth | Unauthenticated | Key | +|----------|------|-----------------|-----| +| AI Generation | 10/min | 2/min | `content-generate` | +| Brief Creation | 30/min | 5/min | `content-briefs` | +| Webhooks | 60/min | 30/min | `content-webhooks` | +| Search | 60/min | 20/min | `content-search` | + +### Rate Limit Bypass Risks + +1. **IP Spoofing:** Ensure `X-Forwarded-For` handling is configured correctly +2. **Workspace Switching:** Workspace-based limits should use user ID as fallback +3. **API Key Sharing:** Each key should have independent limits + +## Data Protection + +### Sensitive Data Handling + +**Encrypted at rest:** +- `ContentWebhookEndpoint.secret` (cast to `encrypted`) +- `ContentWebhookEndpoint.previous_secret` (cast to `encrypted`) + +**Hidden from serialisation:** +- Webhook secrets (via `$hidden` property) + +### PII Considerations + +Content may contain PII in: +- Article body content +- Author information +- Webhook payloads + +**Recommendations:** +1. Implement content retention policies +2. Add GDPR data export/deletion support +3. Log access to PII-containing content + +## Webhook Security + +### Circuit Breaker + +Endpoints automatically disable after 10 consecutive failures: + +```php +const MAX_FAILURES = 10; + +public function incrementFailureCount(): void +{ + $this->increment('failure_count'); + if ($this->failure_count >= self::MAX_FAILURES) { + $this->update(['is_enabled' => false]); + } +} +``` + +### Secret Rotation + +Grace period support for secret rotation: + +```php +public function isInGracePeriod(): bool +{ + // Accepts both current and previous secret during grace +} +``` + +Default grace period: 24 hours + +### Allowed Event Types + +Endpoints can restrict which event types they accept: + +```php +const ALLOWED_TYPES = [ + 'wordpress.post_created', + 'wordpress.post_updated', + // ... + 'generic.payload', +]; +``` + +Wildcard support: `wordpress.*` matches all WordPress events. + +## Content Security + +### XSS Prevention + +1. **Input:** Content stored as-is to preserve formatting +2. **Output:** `getSanitisedContent()` for public rendering +3. **Admin:** Trusted content displayed with proper escaping + +**Blade template guidelines:** +- Use `{{ $title }}` for plain text (auto-escaped) +- Use `{!! $content !!}` only for sanitised HTML +- Comments document which fields need which treatment + +### SQL Injection + +All database queries use: +- Eloquent ORM (parameterised queries) +- Query builder with bindings +- No raw SQL with user input + +### CSRF Protection + +Web routes include CSRF middleware automatically. API routes exempt (use API key auth). + +## Audit Logging + +### Logged Events + +- Webhook receipt and processing +- AI generation requests and results +- Content creation/update/deletion via MCP +- CDN cache purges +- Authentication failures + +### Log Levels + +| Event | Level | +|-------|-------| +| Webhook signature failure | WARNING | +| Circuit breaker triggered | WARNING | +| Processing failure | ERROR | +| Successful operations | INFO | +| Skipped operations | DEBUG | + +## Recommendations + +### Immediate (P1) + +1. [ ] Require HTMLPurifier or equivalent in production +2. [ ] Make webhook signature verification mandatory +3. [ ] Add rate limiting to preview generation +4. [ ] Validate content_type from webhook payloads + +### Short-term (P2) + +1. [ ] Add comprehensive audit logging +2. [ ] Implement content access logging +3. [ ] Add IP allowlisting option for webhooks +4. [ ] Create security-focused test suite + +### Long-term (P3+) + +1. [ ] Implement content encryption at rest option +2. [ ] Add GDPR compliance features +3. [ ] Create security monitoring dashboard +4. [ ] Add anomaly detection for webhook patterns + +## Security Testing + +### Manual Testing Checklist + +``` +[ ] Verify webhook signature rejection with invalid signature +[ ] Test rate limiting enforcement +[ ] Confirm XSS payloads are sanitised +[ ] Verify workspace isolation in API responses +[ ] Test preview token expiration +[ ] Verify CSRF protection on web routes +[ ] Test SQL injection attempts in search +[ ] Verify file type validation on media uploads +``` + +### Automated Testing + +```bash +# Run security-focused tests +./vendor/bin/pest --filter=Security + +# Check for common vulnerabilities +./vendor/bin/pint --test # Code style (includes some security patterns) +``` + +## Incident Response + +### Webhook Compromise + +1. Disable affected endpoint +2. Rotate all secrets +3. Review webhook logs for suspicious patterns +4. Regenerate secrets for all endpoints + +### Content Injection + +1. Identify affected content items +2. Restore from revision history +3. Review webhook source +4. Add additional validation + +### API Key Leak + +1. Revoke compromised key +2. Review access logs +3. Generate new key with reduced scope +4. Monitor for unauthorised access + +## Contact + +Security issues should be reported to the security team. Do not create public issues for security vulnerabilities. diff --git a/docs/packages/developer/architecture.md b/docs/packages/developer/architecture.md new file mode 100644 index 0000000..b7539b8 --- /dev/null +++ b/docs/packages/developer/architecture.md @@ -0,0 +1,390 @@ +--- +title: Architecture +description: Technical architecture of the core-developer package +updated: 2026-01-29 +--- + +# Architecture + +The `core-developer` package provides administrative developer tools for the Host UK platform. It is designed exclusively for "Hades" tier users (god-mode access) and includes debugging, monitoring, and server management capabilities. + +## Package Overview + +| Aspect | Detail | +|--------|--------| +| Namespace | `Core\Developer\` | +| Type | L1 Module (Laravel Package) | +| Dependencies | `host-uk/core`, `host-uk/core-admin` | +| PHP Version | 8.2+ | +| Laravel Version | 11.x / 12.x | +| Livewire Version | 3.x / 4.x | + +## Directory Structure + +``` +src/ +├── Boot.php # Service provider & event handlers +├── Controllers/ +│ └── DevController.php # REST API endpoints +├── Concerns/ +│ └── RemoteServerManager.php # SSH connection trait +├── Console/Commands/ +│ └── CopyDeviceFrames.php # Asset management command +├── Data/ +│ └── RouteTestResult.php # DTO for route test results +├── Exceptions/ +│ └── SshConnectionException.php +├── Lang/ +│ └── en_GB/developer.php # Translations +├── Listeners/ +│ └── SetHadesCookie.php # Login event listener +├── Middleware/ +│ ├── ApplyIconSettings.php # Icon preferences from cookies +│ └── RequireHades.php # Authorization middleware +├── Migrations/ +│ └── 0001_01_01_000001_create_developer_tables.php +├── Models/ +│ └── Server.php # SSH server model +├── Providers/ +│ ├── HorizonServiceProvider.php +│ └── TelescopeServiceProvider.php +├── Routes/ +│ └── admin.php # Route definitions +├── Services/ +│ ├── LogReaderService.php # Log file parsing +│ └── RouteTestService.php # Route testing logic +├── Tests/ +│ └── UseCase/ +│ └── DevToolsBasic.php # Feature tests +└── View/ + ├── Blade/ + │ └── admin/ # Blade templates + │ ├── activity-log.blade.php + │ ├── cache.blade.php + │ ├── database.blade.php + │ ├── logs.blade.php + │ ├── route-inspector.blade.php + │ ├── routes.blade.php + │ └── servers.blade.php + └── Modal/ + └── Admin/ # Livewire components + ├── ActivityLog.php + ├── Cache.php + ├── Database.php + ├── Logs.php + ├── RouteInspector.php + ├── Routes.php + └── Servers.php +``` + +## Event-Driven Module Loading + +The module uses the Core Framework's event-driven lazy loading pattern. The `Boot` class declares which events it listens to: + +```php +public static array $listens = [ + AdminPanelBooting::class => 'onAdminPanel', + ConsoleBooting::class => 'onConsole', +]; +``` + +This ensures routes, views, and commands are only registered when the admin panel or console is actually used. + +### Lifecycle Events + +| Event | Handler | What Happens | +|-------|---------|--------------| +| `AdminPanelBooting` | `onAdminPanel()` | Registers views, routes, Pulse override | +| `ConsoleBooting` | `onConsole()` | Registers Artisan commands | + +## Core Components + +### 1. Livewire Admin Pages + +All admin pages are full-page Livewire components using attribute-based configuration: + +```php +#[Title('Application Logs')] +#[Layout('hub::admin.layouts.app')] +class Logs extends Component +``` + +Each component: +- Checks Hades access in `mount()` +- Uses `developer::admin.{name}` view namespace +- Has corresponding Blade template in `View/Blade/admin/` + +### 2. API Controller + +`DevController` provides REST endpoints for: +- `/hub/api/dev/logs` - Recent log entries +- `/hub/api/dev/routes` - Route listing +- `/hub/api/dev/session` - Session/request info +- `/hub/api/dev/clear/{type}` - Cache clearing + +All endpoints are protected by `RequireHades` middleware and rate limiting. + +### 3. Services + +**LogReaderService** +- Memory-efficient log reading (reads from end of file) +- Parses Laravel log format +- Automatic sensitive data redaction +- Multi-log file support (daily/single channels) + +**RouteTestService** +- Route discovery and formatting +- Request building with parameters +- In-process request execution +- Response formatting and metrics + +### 4. RemoteServerManager Trait + +Provides SSH connection management for classes that need remote server access: + +```php +class DeployApplication implements ShouldQueue +{ + use RemoteServerManager; + + public function handle(): void + { + $this->withConnection($this->server, function () { + $this->run('cd /var/www && git pull'); + }); + } +} +``` + +Key methods: +- `connect()` / `disconnect()` - Connection lifecycle +- `withConnection()` - Guaranteed cleanup pattern +- `run()` / `runMany()` - Command execution +- `fileExists()` / `readFile()` / `writeFile()` - File operations +- `getDiskUsage()` / `getMemoryUsage()` - Server stats + +## Data Flow + +### Admin Page Request + +``` +Browser Request + ↓ +Laravel Router → /hub/dev/logs + ↓ +Livewire Component (Logs.php) + ↓ +mount() → checkHadesAccess() + ↓ +loadLogs() → LogReaderService + ↓ +render() → developer::admin.logs + ↓ +Response (HTML) +``` + +### API Request + +``` +Browser/JS Request + ↓ +Laravel Router → /hub/api/dev/logs + ↓ +RequireHades Middleware + ↓ +Rate Limiter (throttle:dev-logs) + ↓ +DevController::logs() + ↓ +LogReaderService + ↓ +Response (JSON) +``` + +### SSH Connection + +``` +Servers Component + ↓ +testConnection($serverId) + ↓ +Server::findOrFail() + ↓ +Write temp key file + ↓ +Process::run(['ssh', ...]) + ↓ +Parse result + ↓ +Update server status + ↓ +Clean up temp file +``` + +## Database Schema + +### servers table + +| Column | Type | Description | +|--------|------|-------------| +| id | bigint | Primary key | +| workspace_id | bigint | FK to workspaces | +| name | varchar(128) | Display name | +| ip | varchar(45) | IPv4/IPv6 address | +| port | smallint | SSH port (default 22) | +| user | varchar(64) | SSH username | +| private_key | text | Encrypted SSH key | +| status | varchar(32) | pending/connected/failed | +| last_connected_at | timestamp | Last successful connection | +| timestamps | | created_at, updated_at | +| soft_deletes | | deleted_at | + +Indexes: +- `workspace_id` +- `(workspace_id, status)` composite + +## Admin Menu Structure + +The module registers a "Dev Tools" menu group with these items: + +``` +Dev Tools (admin group, priority 80) +├── Logs → /hub/dev/logs +├── Activity → /hub/dev/activity +├── Servers → /hub/dev/servers +├── Database → /hub/dev/database +├── Routes → /hub/dev/routes +├── Route Inspector → /hub/dev/route-inspector +└── Cache → /hub/dev/cache +``` + +The menu is only visible to users with `admin` flag (Hades tier). + +## Rate Limiting + +API endpoints have rate limits configured in `Boot::configureRateLimiting()`: + +| Limiter | Limit | Purpose | +|---------|-------|---------| +| `dev-cache-clear` | 10/min | Prevent rapid cache clears | +| `dev-logs` | 30/min | Log reading | +| `dev-routes` | 30/min | Route listing | +| `dev-session` | 60/min | Session info | + +Rate limits are per-user (or per-IP for unauthenticated requests). + +## Third-Party Integrations + +### Laravel Telescope + +Custom `TelescopeServiceProvider` configures: +- Gate for Hades-only access in production +- Entry filtering (errors, failed jobs in production) +- Sensitive header/parameter hiding + +### Laravel Horizon + +Custom `HorizonServiceProvider` configures: +- Gate for Hades-only access +- Notification routing from config (email, SMS, Slack) + +### Laravel Pulse + +Custom Pulse dashboard view override at `View/Blade/vendor/pulse/dashboard.blade.php`. + +## Configuration + +The module expects these config keys (should be in `config/developer.php`): + +```php +return [ + // Hades cookie token + 'hades_token' => env('HADES_TOKEN'), + + // SSH settings + 'ssh' => [ + 'connection_timeout' => 30, + 'command_timeout' => 60, + ], + + // Horizon notifications + 'horizon' => [ + 'mail_to' => env('HORIZON_MAIL_TO'), + 'sms_to' => env('HORIZON_SMS_TO'), + 'slack_webhook' => env('HORIZON_SLACK_WEBHOOK'), + 'slack_channel' => env('HORIZON_SLACK_CHANNEL', '#alerts'), + ], +]; +``` + +## Extension Points + +### Adding New Admin Pages + +1. Create Livewire component in `View/Modal/Admin/` +2. Create Blade view in `View/Blade/admin/` +3. Add route in `Routes/admin.php` +4. Add menu item in `Boot::adminMenuItems()` +5. Add translations in `Lang/en_GB/developer.php` + +### Adding New API Endpoints + +1. Add method to `DevController` +2. Add route in `Routes/admin.php` API group +3. Create rate limiter in `Boot::configureRateLimiting()` +4. Apply `throttle:limiter-name` middleware + +### Using RemoteServerManager + +```php +use Core\Developer\Concerns\RemoteServerManager; + +class MyJob +{ + use RemoteServerManager; + + public function handle(Server $server): void + { + $this->withConnection($server, function () { + // Commands executed on remote server + $result = $this->run('whoami'); + // ... + }); + } +} +``` + +## Performance Considerations + +1. **Log Reading** - Uses backwards reading to avoid loading entire log into memory. Configurable `maxBytes` limit. + +2. **Route Caching** - Routes are computed once per request. The `RouteInspector` uses `#[Computed(cache: true)]` for route list. + +3. **Query Log** - Enabled only in local environment (`Boot::boot()`). + +4. **SSH Connections** - Always disconnect via `withConnection()` pattern to prevent resource leaks. + +## Dependencies + +### Composer Requirements + +- `host-uk/core` - Core framework +- `host-uk/core-admin` - Admin panel infrastructure +- `phpseclib3` - SSH connections (via RemoteServerManager) +- `spatie/laravel-activitylog` - Activity logging + +### Frontend Dependencies + +- Flux UI components +- Tailwind CSS +- Livewire 3.x + +## Testing Strategy + +Tests use Pest syntax and focus on: +- Page rendering and content +- Authorization enforcement +- API endpoint behaviour +- Service logic + +Test database: SQLite in-memory with Telescope/Pulse disabled. diff --git a/docs/packages/developer/security.md b/docs/packages/developer/security.md new file mode 100644 index 0000000..ae737f6 --- /dev/null +++ b/docs/packages/developer/security.md @@ -0,0 +1,269 @@ +--- +title: Security +description: Security considerations and audit notes for core-developer +updated: 2026-01-29 +--- + +# Security Considerations + +The `core-developer` package provides powerful administrative capabilities that require careful security controls. This document outlines the security model, known risks, and mitigation strategies. + +## Threat Model + +### Assets Protected + +1. **Application logs** - May contain tokens, passwords, PII in error messages +2. **Database access** - Read-only query execution against production data +3. **SSH keys** - Encrypted private keys for server connections +4. **Cache data** - Application cache, session data, config cache +5. **Route information** - Full application route structure + +### Threat Actors + +1. **Unauthorized users** - Non-Hades users attempting to access dev tools +2. **Compromised Hades account** - Attacker with valid Hades credentials +3. **SSRF/Injection** - Attacker manipulating dev tools to access internal resources +4. **Data exfiltration** - Extracting sensitive data via dev tools + +## Authorization Model + +### Hades Tier Requirement + +All developer tools require "Hades" access, verified via the `isHades()` method on the User model. This is enforced at multiple layers: + +| Layer | Implementation | File | +|-------|----------------|------| +| Middleware | `RequireHades::handle()` | `src/Middleware/RequireHades.php` | +| Component | `checkHadesAccess()` in `mount()` | All Livewire components | +| API | Controller `authorize()` calls | `src/Controllers/DevController.php` | +| Menu | `admin` flag filtering | `src/Boot.php` | + +### Defence in Depth + +The authorization is intentionally redundant: +- API routes use `RequireHades` middleware +- Livewire components check in `mount()` +- Some controller methods call `$this->authorize()` + +This ensures access is blocked even if one layer fails. + +### Known Issue: Test Environment + +Tests currently pass without setting Hades tier on the test user. This suggests authorization may not be properly enforced in the test environment. See TODO.md for remediation. + +## Data Protection + +### Log Redaction + +The `LogReaderService` automatically redacts sensitive patterns before displaying logs: + +| Pattern | Replacement | +|---------|-------------| +| Stripe API keys | `[STRIPE_KEY_REDACTED]` | +| GitHub tokens | `[GITHUB_TOKEN_REDACTED]` | +| Bearer tokens | `Bearer [TOKEN_REDACTED]` | +| API keys/secrets | `[KEY_REDACTED]` / `[REDACTED]` | +| AWS credentials | `[AWS_KEY_REDACTED]` / `[AWS_SECRET_REDACTED]` | +| Database URLs | Connection strings with `[USER]:[PASS]` | +| Email addresses | Partial: `jo***@example.com` | +| IP addresses | Partial: `192.168.xxx.xxx` | +| Credit card numbers | `[CARD_REDACTED]` | +| JWT tokens | `[JWT_REDACTED]` | +| Private keys | `[PRIVATE_KEY_REDACTED]` | + +**Limitation**: Patterns are regex-based and may not catch all sensitive data. Custom application secrets with non-standard formats will not be redacted. + +### SSH Key Storage + +Server private keys are: +- Encrypted at rest using Laravel's `encrypted` cast +- Hidden from serialization (`$hidden` array) +- Never exposed in API responses or views +- Stored in `text` column (supports long keys) + +### Database Query Tool + +The database query component restricts access to read-only operations: + +```php +protected const ALLOWED_STATEMENTS = ['SELECT', 'SHOW', 'DESCRIBE', 'EXPLAIN']; +``` + +**Known Risk**: The current implementation only checks the first word, which does not prevent: +- Stacked queries: `SELECT 1; DROP TABLE users` +- Subqueries with side effects (MySQL stored procedures) + +**Mitigation**: Use a proper SQL parser or prevent semicolons entirely. + +### Session Data Exposure + +The `/hub/api/dev/session` endpoint exposes: +- Session ID +- User IP address +- User agent (truncated to 100 chars) +- Request method and URL + +This is intentional for debugging but could be abused for session hijacking if credentials are compromised. + +## Rate Limiting + +All API endpoints have rate limits to prevent abuse: + +| Endpoint | Limit | Rationale | +|----------|-------|-----------| +| Cache clear | 10/min | Prevent DoS via rapid cache invalidation | +| Log reading | 30/min | Limit log scraping | +| Route listing | 30/min | Prevent enumeration attacks | +| Session info | 60/min | Higher limit for debugging workflows | + +Rate limits are per-user (authenticated) or per-IP (unauthenticated). + +## SSH Connection Security + +### Key Handling + +The `testConnection()` method in `Servers.php` creates a temporary key file: + +```php +$tempKeyPath = sys_get_temp_dir().'/ssh_test_'.uniqid(); +file_put_contents($tempKeyPath, $server->getDecryptedPrivateKey()); +chmod($tempKeyPath, 0600); +``` + +**Risk**: Predictable filename pattern and race condition window between write and use. + +**Recommendation**: Use `tempnam()` for unique filename, write with restrictive umask. + +### Connection Validation + +- `StrictHostKeyChecking=no` is used for convenience but prevents MITM detection +- `BatchMode=yes` prevents interactive prompts +- `ConnectTimeout=10` limits hanging connections + +### Workspace Isolation + +The `RemoteServerManager::connect()` method validates workspace ownership before connecting: + +```php +if (! $server->belongsToCurrentWorkspace()) { + throw new SshConnectionException('Unauthorised access to server.', $server->name); +} +``` + +This prevents cross-tenant server access. + +## Route Testing Security + +### Environment Restriction + +Route testing is only available in `local` and `testing` environments: + +```php +public function isTestingAllowed(): bool +{ + return App::environment(['local', 'testing']); +} +``` + +This prevents accidental data modification in production. + +### Destructive Operation Warnings + +Routes using `DELETE`, `PUT`, `PATCH`, `POST` methods are marked as destructive and show warnings in the UI. + +### CSRF Consideration + +Test requests bypass CSRF as they are internal requests. The `X-Requested-With: XMLHttpRequest` header is set by default. + +## Cookie Security + +### Hades Cookie + +The `SetHadesCookie` listener sets a cookie on login: + +| Attribute | Value | Purpose | +|-----------|-------|---------| +| Value | Encrypted token | Validates Hades status | +| Duration | 1 year | Long-lived for convenience | +| HttpOnly | true | Prevents XSS access | +| Secure | true (production) | HTTPS only in production | +| SameSite | lax | CSRF protection | + +### Icon Settings Cookie + +`ApplyIconSettings` middleware reads `icon-style` and `icon-size` cookies set by JavaScript. These are stored in session for Blade component access. + +**Risk**: Cookie values are user-controlled. Ensure they are properly escaped in views. + +## Audit Logging + +### Logged Actions + +| Action | What's Logged | +|--------|---------------| +| Log clear | user_id, email, previous_size_bytes, IP | +| Database query | user_id, email, query, row_count, execution_time, IP | +| Blocked query | user_id, email, query (attempted), IP | +| Route test | user_id, route, method, IP | +| Server failure | Server ID, failure reason (via activity log) | + +### Activity Log + +Server model uses Spatie ActivityLog for tracking changes: +- Logged fields: name, ip, port, user, status +- Only dirty attributes logged +- Empty logs suppressed + +## Third-Party Security + +### Telescope + +- Sensitive headers hidden: `cookie`, `x-csrf-token`, `x-xsrf-token` +- Sensitive parameters hidden: `_token` +- Gate restricts to Hades users (production) or all users (local) + +### Horizon + +- Gate restricts to Hades users +- Notifications configured via config (not hardcoded emails) + +## Security Checklist for New Features + +When adding new developer tools: + +- [ ] Enforce Hades authorization in middleware AND component +- [ ] Add rate limiting for API endpoints +- [ ] Redact sensitive data in output +- [ ] Audit destructive operations +- [ ] Restrict environment (local/testing) for dangerous features +- [ ] Validate and sanitize all user input +- [ ] Use prepared statements for database queries +- [ ] Clean up temporary files/resources +- [ ] Document security considerations + +## Incident Response + +### If Hades credentials are compromised: + +1. Revoke the user's Hades access +2. Rotate `HADES_TOKEN` environment variable +3. Review audit logs for suspicious activity +4. Check server access logs for SSH activity +5. Consider rotating SSH keys for connected servers + +### If SSH key is exposed: + +1. Delete the server record immediately +2. Regenerate SSH key on the actual server +3. Review server logs for unauthorized access +4. Update the server record with new key + +## Recommendations for Production + +1. **Separate Hades token per environment** - Don't use same token across staging/production +2. **Regular audit log review** - Monitor for unusual access patterns +3. **Limit Hades users** - Only grant to essential personnel +4. **Use hardware keys** - For servers, prefer hardware security modules +5. **Network segmentation** - Restrict admin panel to internal networks +6. **Two-factor authentication** - Require 2FA for Hades-tier accounts +7. **Session timeout** - Consider shorter session duration for Hades users diff --git a/docs/packages/go/cmd/ai/example.md b/docs/packages/go/cmd/ai/example.md new file mode 100644 index 0000000..b115b09 --- /dev/null +++ b/docs/packages/go/cmd/ai/example.md @@ -0,0 +1,100 @@ +# AI Examples + +## Workflow Example + +Complete task management workflow: + +```bash +# 1. List available tasks +core ai tasks --status pending + +# 2. Auto-select and claim a task +core ai task --auto --claim + +# 3. Work on the task... + +# 4. Update progress +core ai task:update abc123 --progress 75 + +# 5. Commit with task reference +core ai task:commit abc123 -m 'implement feature' + +# 6. Create PR +core ai task:pr abc123 + +# 7. Mark complete +core ai task:complete abc123 --output 'Feature implemented and PR created' +``` + +## Task Filtering + +```bash +# By status +core ai tasks --status pending +core ai tasks --status in_progress + +# By priority +core ai tasks --priority critical +core ai tasks --priority high + +# By labels +core ai tasks --labels bug,urgent + +# Combined filters +core ai tasks --status pending --priority high --labels bug +``` + +## Task Updates + +```bash +# Change status +core ai task:update abc123 --status in_progress +core ai task:update abc123 --status blocked + +# Update progress +core ai task:update abc123 --progress 25 +core ai task:update abc123 --progress 50 --notes 'Halfway done' +core ai task:update abc123 --progress 100 +``` + +## Git Integration + +```bash +# Commit with task reference +core ai task:commit abc123 -m 'add authentication' + +# With scope +core ai task:commit abc123 -m 'fix login' --scope auth + +# Commit and push +core ai task:commit abc123 -m 'complete feature' --push + +# Create PR +core ai task:pr abc123 + +# Draft PR +core ai task:pr abc123 --draft + +# PR with labels +core ai task:pr abc123 --labels 'enhancement,ready-for-review' + +# PR to different base +core ai task:pr abc123 --base develop +``` + +## Configuration + +### Environment Variables + +```env +AGENTIC_TOKEN=your-api-token +AGENTIC_BASE_URL=https://agentic.example.com +``` + +### ~/.core/agentic.yaml + +```yaml +token: your-api-token +base_url: https://agentic.example.com +default_project: my-project +``` diff --git a/docs/packages/go/cmd/ai/index.md b/docs/packages/go/cmd/ai/index.md new file mode 100644 index 0000000..f6c49be --- /dev/null +++ b/docs/packages/go/cmd/ai/index.md @@ -0,0 +1,262 @@ +# core ai + +AI agent task management and Claude Code integration. + +## Task Management Commands + +| Command | Description | +|---------|-------------| +| `tasks` | List available tasks from core-agentic | +| `task` | View task details or auto-select | +| `task:update` | Update task status or progress | +| `task:complete` | Mark task as completed or failed | +| `task:commit` | Create git commit with task reference | +| `task:pr` | Create GitHub PR linked to task | + +## Claude Integration + +| Command | Description | +|---------|-------------| +| `claude run` | Run Claude Code in current directory | +| `claude config` | Manage Claude configuration | + +--- + +## Configuration + +Task commands load configuration from: +1. Environment variables (`AGENTIC_TOKEN`, `AGENTIC_BASE_URL`) +2. `.env` file in current directory +3. `~/.core/agentic.yaml` + +--- + +## ai tasks + +List available tasks from core-agentic. + +```bash +core ai tasks [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--status` | Filter by status (`pending`, `in_progress`, `completed`, `blocked`) | +| `--priority` | Filter by priority (`critical`, `high`, `medium`, `low`) | +| `--labels` | Filter by labels (comma-separated) | +| `--project` | Filter by project | +| `--limit` | Max number of tasks to return (default: 20) | + +### Examples + +```bash +# List all pending tasks +core ai tasks + +# Filter by status and priority +core ai tasks --status pending --priority high + +# Filter by labels +core ai tasks --labels bug,urgent +``` + +--- + +## ai task + +View task details or auto-select a task. + +```bash +core ai task [task-id] [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--auto` | Auto-select highest priority pending task | +| `--claim` | Claim the task after showing details | +| `--context` | Show gathered context for AI collaboration | + +### Examples + +```bash +# Show task details +core ai task abc123 + +# Show and claim +core ai task abc123 --claim + +# Show with context +core ai task abc123 --context + +# Auto-select highest priority pending task +core ai task --auto +``` + +--- + +## ai task:update + +Update a task's status, progress, or notes. + +```bash +core ai task:update [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--status` | New status (`pending`, `in_progress`, `completed`, `blocked`) | +| `--progress` | Progress percentage (0-100) | +| `--notes` | Notes about the update | + +### Examples + +```bash +# Set task to in progress +core ai task:update abc123 --status in_progress + +# Update progress with notes +core ai task:update abc123 --progress 50 --notes 'Halfway done' +``` + +--- + +## ai task:complete + +Mark a task as completed with optional output and artifacts. + +```bash +core ai task:complete [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--output` | Summary of the completed work | +| `--failed` | Mark the task as failed | +| `--error` | Error message if failed | + +### Examples + +```bash +# Complete successfully +core ai task:complete abc123 --output 'Feature implemented' + +# Mark as failed +core ai task:complete abc123 --failed --error 'Build failed' +``` + +--- + +## ai task:commit + +Create a git commit with a task reference and co-author attribution. + +```bash +core ai task:commit [flags] +``` + +Commit message format: +``` +feat(scope): description + +Task: #123 +Co-Authored-By: Claude +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `-m`, `--message` | Commit message (without task reference) | +| `--scope` | Scope for the commit type (e.g., `auth`, `api`, `ui`) | +| `--push` | Push changes after committing | + +### Examples + +```bash +# Commit with message +core ai task:commit abc123 --message 'add user authentication' + +# With scope +core ai task:commit abc123 -m 'fix login bug' --scope auth + +# Commit and push +core ai task:commit abc123 -m 'update docs' --push +``` + +--- + +## ai task:pr + +Create a GitHub pull request linked to a task. + +```bash +core ai task:pr [flags] +``` + +Requires the GitHub CLI (`gh`) to be installed and authenticated. + +### Flags + +| Flag | Description | +|------|-------------| +| `--title` | PR title (defaults to task title) | +| `--base` | Base branch (defaults to main) | +| `--draft` | Create as draft PR | +| `--labels` | Labels to add (comma-separated) | + +### Examples + +```bash +# Create PR with defaults +core ai task:pr abc123 + +# Custom title +core ai task:pr abc123 --title 'Add authentication feature' + +# Draft PR with labels +core ai task:pr abc123 --draft --labels 'enhancement,needs-review' + +# Target different base branch +core ai task:pr abc123 --base develop +``` + +--- + +## ai claude + +Claude Code integration commands. + +### ai claude run + +Run Claude Code in the current directory. + +```bash +core ai claude run +``` + +### ai claude config + +Manage Claude configuration. + +```bash +core ai claude config +``` + +--- + +## Workflow Example + +See [Workflow Example](example.md#workflow-example) for a complete task management workflow. + +## See Also + +- [dev](../dev/) - Multi-repo workflow commands +- [Claude Code documentation](https://claude.ai/code) diff --git a/docs/packages/go/cmd/build/example.md b/docs/packages/go/cmd/build/example.md new file mode 100644 index 0000000..da2f3b4 --- /dev/null +++ b/docs/packages/go/cmd/build/example.md @@ -0,0 +1,83 @@ +# Build Examples + +## Quick Start + +```bash +# Auto-detect and build +core build + +# Build for specific platforms +core build --targets linux/amd64,darwin/arm64 + +# CI mode +core build --ci +``` + +## Configuration + +`.core/build.yaml`: + +```yaml +version: 1 + +project: + name: myapp + binary: myapp + +build: + main: ./cmd/myapp + ldflags: + - -s -w + - -X main.version={{.Version}} + +targets: + - os: linux + arch: amd64 + - os: linux + arch: arm64 + - os: darwin + arch: arm64 +``` + +## Cross-Platform Build + +```bash +core build --targets linux/amd64,linux/arm64,darwin/arm64,windows/amd64 +``` + +Output: +``` +dist/ +├── myapp-linux-amd64.tar.gz +├── myapp-linux-arm64.tar.gz +├── myapp-darwin-arm64.tar.gz +├── myapp-windows-amd64.zip +└── CHECKSUMS.txt +``` + +## Code Signing + +```yaml +sign: + enabled: true + gpg: + key: $GPG_KEY_ID + macos: + identity: "Developer ID Application: Your Name (TEAM_ID)" + notarize: true + apple_id: $APPLE_ID + team_id: $APPLE_TEAM_ID + app_password: $APPLE_APP_PASSWORD +``` + +## Docker Build + +```bash +core build --type docker --image ghcr.io/myorg/myapp +``` + +## Wails Desktop App + +```bash +core build --type wails --targets darwin/arm64,windows/amd64 +``` diff --git a/docs/packages/go/cmd/build.md b/docs/packages/go/cmd/build/index.md similarity index 64% rename from docs/packages/go/cmd/build.md rename to docs/packages/go/cmd/build/index.md index 12c7d05..6956e65 100644 --- a/docs/packages/go/cmd/build.md +++ b/docs/packages/go/cmd/build/index.md @@ -2,6 +2,14 @@ Build Go, Wails, Docker, and LinuxKit projects with automatic project detection. +## Subcommands + +| Command | Description | +|---------|-------------| +| [sdk](sdk/) | Generate API SDKs from OpenAPI | +| `from-path` | Build from a local directory | +| `pwa` | Build from a live PWA URL | + ## Usage ```bash @@ -12,12 +20,17 @@ core build [flags] | Flag | Description | |------|-------------| -| `--type` | Project type: `go`, `wails`, `docker`, `linuxkit` (auto-detected) | +| `--type` | Project type: `go`, `wails`, `docker`, `linuxkit`, `taskfile` (auto-detected) | | `--targets` | Build targets: `linux/amd64,darwin/arm64,windows/amd64` | | `--output` | Output directory (default: `dist`) | -| `--ci` | CI mode - non-interactive, fail fast | +| `--ci` | CI mode - minimal output with JSON artifact list at the end | | `--image` | Docker image name (for docker builds) | -| `--no-sign` | Skip code signing | +| `--config` | Config file path (for linuxkit: YAML config, for docker: Dockerfile) | +| `--format` | Output format for linuxkit (iso-bios, qcow2-bios, raw, vmdk) | +| `--push` | Push Docker image after build (default: false) | +| `--archive` | Create archives (tar.gz for linux/darwin, zip for windows) - default: true | +| `--checksum` | Generate SHA256 checksums and CHECKSUMS.txt - default: true | +| `--no-sign` | Skip all code signing | | `--notarize` | Enable macOS notarization (requires Apple credentials) | ## Examples @@ -53,6 +66,9 @@ core build --type docker # With custom image name core build --type docker --image ghcr.io/myorg/myapp + +# Build and push to registry +core build --type docker --image ghcr.io/myorg/myapp --push ``` ### LinuxKit Image @@ -60,6 +76,9 @@ core build --type docker --image ghcr.io/myorg/myapp ```bash # Build LinuxKit ISO core build --type linuxkit + +# Build with specific format +core build --type linuxkit --config linuxkit.yml --format qcow2-bios ``` ## Project Detection @@ -71,6 +90,7 @@ Core automatically detects project type based on files: | `wails.json` | Wails | | `go.mod` | Go | | `Dockerfile` | Docker | +| `Taskfile.yml` | Taskfile | | `composer.json` | PHP | | `package.json` | Node | @@ -90,40 +110,7 @@ dist/ ## Configuration -Optional `.core/build.yaml`: - -```yaml -version: 1 - -project: - name: myapp - binary: myapp - -build: - main: ./cmd/myapp - ldflags: - - -s -w - - -X main.version={{.Version}} - -targets: - - os: linux - arch: amd64 - - os: linux - arch: arm64 - - os: darwin - arch: arm64 - -sign: - enabled: true - gpg: - key: $GPG_KEY_ID - macos: - identity: "Developer ID Application: Your Name (TEAM_ID)" - notarize: false - apple_id: $APPLE_ID - team_id: $APPLE_TEAM_ID - app_password: $APPLE_APP_PASSWORD -``` +Optional `.core/build.yaml` - see [Configuration](example.md#configuration) for examples. ## Code Signing @@ -169,3 +156,21 @@ core build --notarize | `APPLE_ID` | Apple account email | | `APPLE_TEAM_ID` | Apple Developer Team ID | | `APPLE_APP_PASSWORD` | App-specific password for notarization | + +## Building from PWAs and Static Sites + +### Build from Local Directory + +Build a desktop app from static web application files: + +```bash +core build from-path --path ./dist +``` + +### Build from Live PWA + +Build a desktop app from a live Progressive Web App URL: + +```bash +core build pwa --url https://example.com +``` diff --git a/docs/packages/go/cmd/build/sdk/example.md b/docs/packages/go/cmd/build/sdk/example.md new file mode 100644 index 0000000..e832308 --- /dev/null +++ b/docs/packages/go/cmd/build/sdk/example.md @@ -0,0 +1,56 @@ +# SDK Build Examples + +## Generate All SDKs + +```bash +core build sdk +``` + +## Specific Language + +```bash +core build sdk --lang typescript +core build sdk --lang php +core build sdk --lang go +``` + +## Custom Spec + +```bash +core build sdk --spec ./api/openapi.yaml +``` + +## With Version + +```bash +core build sdk --version v2.0.0 +``` + +## Preview + +```bash +core build sdk --dry-run +``` + +## Configuration + +`.core/sdk.yaml`: + +```yaml +version: 1 + +spec: ./api/openapi.yaml + +languages: + - name: typescript + output: sdk/typescript + package: "@myorg/api-client" + + - name: php + output: sdk/php + namespace: MyOrg\ApiClient + + - name: go + output: sdk/go + module: github.com/myorg/api-client-go +``` diff --git a/docs/packages/go/cmd/build/sdk/index.md b/docs/packages/go/cmd/build/sdk/index.md new file mode 100644 index 0000000..084c5ef --- /dev/null +++ b/docs/packages/go/cmd/build/sdk/index.md @@ -0,0 +1,27 @@ +# core build sdk + +Generate typed API clients from OpenAPI specifications. Supports TypeScript, Python, Go, and PHP. + +## Usage + +```bash +core build sdk [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--spec` | Path to OpenAPI spec file | +| `--lang` | Generate only this language (typescript, python, go, php) | +| `--version` | Version to embed in generated SDKs | +| `--dry-run` | Show what would be generated without writing files | + +## Examples + +```bash +core build sdk # Generate all +core build sdk --lang typescript # TypeScript only +core build sdk --spec ./api.yaml # Custom spec +core build sdk --dry-run # Preview +``` diff --git a/docs/packages/go/cmd/ci/changelog/example.md b/docs/packages/go/cmd/ci/changelog/example.md new file mode 100644 index 0000000..101cad7 --- /dev/null +++ b/docs/packages/go/cmd/ci/changelog/example.md @@ -0,0 +1,36 @@ +# CI Changelog Examples + +```bash +core ci changelog +``` + +## Output + +```markdown +## v1.2.0 + +### Features +- Add user authentication (#123) +- Support dark mode (#124) + +### Bug Fixes +- Fix memory leak in worker (#125) + +### Performance +- Optimize database queries (#126) +``` + +## Configuration + +`.core/release.yaml`: + +```yaml +changelog: + include: + - feat + - fix + - perf + exclude: + - chore + - docs +``` diff --git a/docs/packages/go/cmd/ci/changelog/index.md b/docs/packages/go/cmd/ci/changelog/index.md new file mode 100644 index 0000000..ffc0712 --- /dev/null +++ b/docs/packages/go/cmd/ci/changelog/index.md @@ -0,0 +1,28 @@ +# core ci changelog + +Generate changelog from conventional commits. + +## Usage + +```bash +core ci changelog +``` + +## Output + +Generates markdown changelog from git commits since last tag: + +```markdown +## v1.2.0 + +### Features +- Add user authentication (#123) +- Support dark mode (#124) + +### Bug Fixes +- Fix memory leak in worker (#125) +``` + +## Configuration + +See [configuration.md](../../../configuration.md) for changelog configuration options. diff --git a/docs/packages/go/cmd/ci/example.md b/docs/packages/go/cmd/ci/example.md new file mode 100644 index 0000000..faf4720 --- /dev/null +++ b/docs/packages/go/cmd/ci/example.md @@ -0,0 +1,90 @@ +# CI Examples + +## Quick Start + +```bash +# Build first +core build + +# Preview release +core ci + +# Publish +core ci --we-are-go-for-launch +``` + +## Configuration + +`.core/release.yaml`: + +```yaml +version: 1 + +project: + name: myapp + repository: host-uk/myapp + +publishers: + - type: github +``` + +## Publisher Examples + +### GitHub + Docker + +```yaml +publishers: + - type: github + + - type: docker + registry: ghcr.io + image: host-uk/myapp + platforms: + - linux/amd64 + - linux/arm64 + tags: + - latest + - "{{.Version}}" +``` + +### Full Stack (GitHub + npm + Homebrew) + +```yaml +publishers: + - type: github + + - type: npm + package: "@host-uk/myapp" + access: public + + - type: homebrew + tap: host-uk/homebrew-tap +``` + +### LinuxKit Image + +```yaml +publishers: + - type: linuxkit + config: .core/linuxkit/server.yml + formats: + - iso + - qcow2 + platforms: + - linux/amd64 + - linux/arm64 +``` + +## Changelog Configuration + +```yaml +changelog: + include: + - feat + - fix + - perf + exclude: + - chore + - docs + - test +``` diff --git a/docs/packages/go/cmd/ci/index.md b/docs/packages/go/cmd/ci/index.md new file mode 100644 index 0000000..ee2c759 --- /dev/null +++ b/docs/packages/go/cmd/ci/index.md @@ -0,0 +1,79 @@ +# core ci + +Publish releases to GitHub, Docker, npm, Homebrew, and more. + +**Safety:** Dry-run by default. Use `--we-are-go-for-launch` to actually publish. + +## Subcommands + +| Command | Description | +|---------|-------------| +| [init](init/) | Initialize release config | +| [changelog](changelog/) | Generate changelog | +| [version](version/) | Show determined version | + +## Usage + +```bash +core ci [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--we-are-go-for-launch` | Actually publish (default is dry-run) | +| `--version` | Override version | +| `--draft` | Create as draft release | +| `--prerelease` | Mark as prerelease | + +## Examples + +```bash +# Preview what would be published (safe) +core ci + +# Actually publish +core ci --we-are-go-for-launch + +# Publish as draft +core ci --we-are-go-for-launch --draft + +# Publish as prerelease +core ci --we-are-go-for-launch --prerelease +``` + +## Workflow + +Build and publish are **separated** to prevent accidents: + +```bash +# Step 1: Build artifacts +core build +core build sdk + +# Step 2: Preview (dry-run by default) +core ci + +# Step 3: Publish (explicit flag required) +core ci --we-are-go-for-launch +``` + +## Publishers + +See [Publisher Examples](example.md#publisher-examples) for configuration. + +| Type | Target | +|------|--------| +| `github` | GitHub Releases | +| `docker` | Container registries | +| `linuxkit` | LinuxKit images | +| `npm` | npm registry | +| `homebrew` | Homebrew tap | +| `scoop` | Scoop bucket | +| `aur` | Arch User Repository | +| `chocolatey` | Chocolatey | + +## Changelog + +Auto-generated from conventional commits. See [Changelog Configuration](example.md#changelog-configuration). diff --git a/docs/packages/go/cmd/ci/init/example.md b/docs/packages/go/cmd/ci/init/example.md new file mode 100644 index 0000000..8f76ab9 --- /dev/null +++ b/docs/packages/go/cmd/ci/init/example.md @@ -0,0 +1,17 @@ +# CI Init Examples + +```bash +core ci init +``` + +Creates `.core/release.yaml`: + +```yaml +version: 1 + +project: + name: myapp + +publishers: + - type: github +``` diff --git a/docs/packages/go/cmd/ci/init/index.md b/docs/packages/go/cmd/ci/init/index.md new file mode 100644 index 0000000..23ba068 --- /dev/null +++ b/docs/packages/go/cmd/ci/init/index.md @@ -0,0 +1,11 @@ +# core ci init + +Initialize release configuration. + +## Usage + +```bash +core ci init +``` + +Creates `.core/release.yaml` with default configuration. See [Configuration](../example.md#configuration) for output format. diff --git a/docs/packages/go/cmd/ci/version/example.md b/docs/packages/go/cmd/ci/version/example.md new file mode 100644 index 0000000..e669d65 --- /dev/null +++ b/docs/packages/go/cmd/ci/version/example.md @@ -0,0 +1,18 @@ +# CI Version Examples + +```bash +core ci version +``` + +## Output + +``` +v1.2.0 +``` + +## Version Resolution + +1. `--version` flag (if provided) +2. Git tag on HEAD +3. Latest git tag + increment +4. `v0.0.1` (no tags) diff --git a/docs/packages/go/cmd/ci/version/index.md b/docs/packages/go/cmd/ci/version/index.md new file mode 100644 index 0000000..7014a34 --- /dev/null +++ b/docs/packages/go/cmd/ci/version/index.md @@ -0,0 +1,21 @@ +# core ci version + +Show the determined release version. + +## Usage + +```bash +core ci version +``` + +## Output + +``` +v1.2.0 +``` + +Version is determined from: +1. `--version` flag (if provided) +2. Git tag on HEAD +3. Latest git tag + increment +4. `v0.0.1` (if no tags exist) diff --git a/docs/packages/go/cmd/dev/ci/index.md b/docs/packages/go/cmd/dev/ci/index.md new file mode 100644 index 0000000..0cf8442 --- /dev/null +++ b/docs/packages/go/cmd/dev/ci/index.md @@ -0,0 +1,61 @@ +# core dev ci + +Check CI status across all repositories. + +Fetches GitHub Actions workflow status for all repos. Shows latest run status for each repo. Requires the `gh` CLI to be installed and authenticated. + +## Usage + +```bash +core dev ci [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to repos.yaml (auto-detected if not specified) | +| `--branch` | Filter by branch (default: main) | +| `--failed` | Show only failed runs | + +## Examples + +```bash +# Check CI status for all repos +core dev ci + +# Check specific branch +core dev ci --branch develop + +# Show only failures +core dev ci --failed +``` + +## Output + +``` +core-php ✓ passing 2m ago +core-tenant ✓ passing 5m ago +core-admin ✗ failed 12m ago +core-api ⏳ running now +core-bio ✓ passing 1h ago +``` + +## Status Icons + +| Symbol | Meaning | +|--------|---------| +| `✓` | Passing | +| `✗` | Failed | +| `⏳` | Running | +| `-` | No runs | + +## Requirements + +- GitHub CLI (`gh`) must be installed +- Must be authenticated: `gh auth login` + +## See Also + +- [issues command](../issues/) - List open issues +- [reviews command](../reviews/) - List PRs needing review diff --git a/docs/packages/go/cmd/dev/commit/index.md b/docs/packages/go/cmd/dev/commit/index.md new file mode 100644 index 0000000..4258fb1 --- /dev/null +++ b/docs/packages/go/cmd/dev/commit/index.md @@ -0,0 +1,46 @@ +# core dev commit + +Claude-assisted commits across repositories. + +Uses Claude to create commits for dirty repos. Shows uncommitted changes and invokes Claude to generate commit messages. + +## Usage + +```bash +core dev commit [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to repos.yaml (auto-detected if not specified) | +| `--all` | Commit all dirty repos without prompting | + +## Examples + +```bash +# Interactive commit (prompts for each repo) +core dev commit + +# Commit all dirty repos automatically +core dev commit --all + +# Use specific registry +core dev commit --registry ~/projects/repos.yaml +``` + +## How It Works + +1. Scans all repositories for uncommitted changes +2. For each dirty repo: + - Shows the diff + - Invokes Claude to generate a commit message + - Creates the commit with `Co-Authored-By: Claude` +3. Reports success/failure for each repo + +## See Also + +- [health command](../health/) - Check repo status +- [push command](../push/) - Push commits after committing +- [work command](../work/) - Full workflow (status + commit + push) diff --git a/docs/packages/go/cmd/dev/example.md b/docs/packages/go/cmd/dev/example.md new file mode 100644 index 0000000..da75b5e --- /dev/null +++ b/docs/packages/go/cmd/dev/example.md @@ -0,0 +1,203 @@ +# Dev Examples + +## Multi-Repo Workflow + +```bash +# Quick status +core dev health + +# Detailed breakdown +core dev health --verbose + +# Full workflow +core dev work + +# Status only +core dev work --status + +# Commit and push +core dev work --commit + +# Commit dirty repos +core dev commit + +# Commit all without prompting +core dev commit --all + +# Push unpushed +core dev push + +# Push without confirmation +core dev push --force + +# Pull behind repos +core dev pull + +# Pull all repos +core dev pull --all +``` + +## GitHub Integration + +```bash +# Open issues +core dev issues + +# Filter by assignee +core dev issues --assignee @me + +# Limit results +core dev issues --limit 5 + +# PRs needing review +core dev reviews + +# All PRs including drafts +core dev reviews --all + +# Filter by author +core dev reviews --author username + +# CI status +core dev ci + +# Only failed runs +core dev ci --failed + +# Specific branch +core dev ci --branch develop +``` + +## Dependency Analysis + +```bash +# What depends on core-php? +core dev impact core-php +``` + +## Task Management + +```bash +# List tasks +core ai tasks + +# Filter by status and priority +core ai tasks --status pending --priority high + +# Filter by labels +core ai tasks --labels bug,urgent + +# Show task details +core ai task abc123 + +# Auto-select highest priority task +core ai task --auto + +# Claim a task +core ai task abc123 --claim + +# Update task status +core ai task:update abc123 --status in_progress + +# Add progress notes +core ai task:update abc123 --progress 50 --notes 'Halfway done' + +# Complete a task +core ai task:complete abc123 --output 'Feature implemented' + +# Mark as failed +core ai task:complete abc123 --failed --error 'Build failed' + +# Commit with task reference +core ai task:commit abc123 -m 'add user authentication' + +# Commit with scope and push +core ai task:commit abc123 -m 'fix login bug' --scope auth --push + +# Create PR for task +core ai task:pr abc123 + +# Create draft PR with labels +core ai task:pr abc123 --draft --labels 'enhancement,needs-review' +``` + +## Service API Management + +```bash +# Synchronize public service APIs +core dev sync + +# Or using the api command +core dev api sync +``` + +## Dev Environment + +```bash +# First time setup +core dev install +core dev boot + +# Open shell +core dev shell + +# Mount and serve +core dev serve + +# Run tests +core dev test + +# Sandboxed Claude +core dev claude +``` + +## Configuration + +### repos.yaml + +```yaml +org: host-uk +repos: + core-php: + type: package + description: Foundation framework + core-tenant: + type: package + depends: [core-php] +``` + +### ~/.core/config.yaml + +```yaml +version: 1 + +images: + source: auto # auto | github | registry | cdn + + cdn: + url: https://images.example.com/core-devops + + github: + repo: host-uk/core-images + + registry: + image: ghcr.io/host-uk/core-devops +``` + +### .core/test.yaml + +```yaml +version: 1 + +commands: + - name: unit + run: vendor/bin/pest --parallel + - name: types + run: vendor/bin/phpstan analyse + - name: lint + run: vendor/bin/pint --test + +env: + APP_ENV: testing + DB_CONNECTION: sqlite +``` diff --git a/docs/packages/go/cmd/dev/health/index.md b/docs/packages/go/cmd/dev/health/index.md new file mode 100644 index 0000000..d104689 --- /dev/null +++ b/docs/packages/go/cmd/dev/health/index.md @@ -0,0 +1,52 @@ +# core dev health + +Quick health check across all repositories. + +Shows a summary of repository health: total repos, dirty repos, unpushed commits, etc. + +## Usage + +```bash +core dev health [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to repos.yaml (auto-detected if not specified) | +| `--verbose` | Show detailed breakdown | + +## Examples + +```bash +# Quick health summary +core dev health + +# Detailed breakdown +core dev health --verbose + +# Use specific registry +core dev health --registry ~/projects/repos.yaml +``` + +## Output + +``` +18 repos │ 2 dirty │ 1 ahead │ all synced +``` + +With `--verbose`: + +``` +Repos: 18 +Dirty: 2 (core-php, core-admin) +Ahead: 1 (core-tenant) +Behind: 0 +Synced: ✓ +``` + +## See Also + +- [work command](../work/) - Full workflow (status + commit + push) +- [commit command](../commit/) - Claude-assisted commits diff --git a/docs/packages/go/cmd/dev/impact/index.md b/docs/packages/go/cmd/dev/impact/index.md new file mode 100644 index 0000000..ac96e04 --- /dev/null +++ b/docs/packages/go/cmd/dev/impact/index.md @@ -0,0 +1,65 @@ +# core dev impact + +Show impact of changing a repository. + +Analyses the dependency graph to show which repos would be affected by changes to the specified repo. + +## Usage + +```bash +core dev impact [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to repos.yaml (auto-detected if not specified) | + +## Examples + +```bash +# Show what depends on core-php +core dev impact core-php + +# Show what depends on core-tenant +core dev impact core-tenant +``` + +## Output + +``` +Impact of changes to core-php: + +Direct dependents (5): + core-tenant + core-admin + core-api + core-mcp + core-commerce + +Indirect dependents (12): + core-bio (via core-tenant) + core-social (via core-tenant) + core-analytics (via core-tenant) + core-notify (via core-tenant) + core-trust (via core-tenant) + core-support (via core-tenant) + core-content (via core-tenant) + core-developer (via core-tenant) + core-agentic (via core-mcp) + ... + +Total: 17 repos affected +``` + +## Use Cases + +- Before making breaking changes, see what needs updating +- Plan release order based on dependency graph +- Understand the ripple effect of changes + +## See Also + +- [health command](../health/) - Quick repo status +- [setup command](../../setup/) - Clone repos with dependencies diff --git a/docs/packages/go/cmd/dev.md b/docs/packages/go/cmd/dev/index.md similarity index 53% rename from docs/packages/go/cmd/dev.md rename to docs/packages/go/cmd/dev/index.md index 696d457..56a5090 100644 --- a/docs/packages/go/cmd/dev.md +++ b/docs/packages/go/cmd/dev/index.md @@ -1,24 +1,55 @@ # core dev -Portable development environment with 100+ embedded tools. +Multi-repo workflow and portable development environment. -## Overview - -Core DevOps provides a sandboxed, immutable development environment based on LinuxKit. It includes AI tools (Claude, Gemini), runtimes (Go, Node, PHP, Python, Rust), and infrastructure tools (Docker, Kubernetes, Terraform). - -## Commands +## Multi-Repo Commands | Command | Description | |---------|-------------| -| `install` | Download the core-devops image for your platform | -| `boot` | Start the development environment | -| `stop` | Stop the running environment | -| `status` | Show environment status | -| `shell` | Open a shell in the environment | -| `serve` | Mount project and start dev server | -| `test` | Run tests inside the environment | -| `claude` | Start sandboxed Claude session | -| `update` | Update to latest image | +| [work](work/) | Full workflow: status + commit + push | +| `health` | Quick health check across repos | +| `commit` | Claude-assisted commits | +| `push` | Push repos with unpushed commits | +| `pull` | Pull repos that are behind | +| `issues` | List open issues | +| `reviews` | List PRs needing review | +| `ci` | Check CI status | +| `impact` | Show dependency impact | +| `api` | Tools for managing service APIs | +| `sync` | Synchronize public service APIs | + +## Task Management Commands + +> **Note:** Task management commands have moved to [`core ai`](../ai/). + +| Command | Description | +|---------|-------------| +| [`ai tasks`](../ai/) | List available tasks from core-agentic | +| [`ai task`](../ai/) | Show task details or auto-select a task | +| [`ai task:update`](../ai/) | Update task status or progress | +| [`ai task:complete`](../ai/) | Mark a task as completed | +| [`ai task:commit`](../ai/) | Auto-commit changes with task reference | +| [`ai task:pr`](../ai/) | Create a pull request for a task | + +## Dev Environment Commands + +| Command | Description | +|---------|-------------| +| `install` | Download the core-devops image | +| `boot` | Start the environment | +| `stop` | Stop the environment | +| `status` | Show status | +| `shell` | Open shell | +| `serve` | Start dev server | +| `test` | Run tests | +| `claude` | Sandboxed Claude | +| `update` | Update image | + +--- + +## Dev Environment Overview + +Core DevOps provides a sandboxed, immutable development environment based on LinuxKit with 100+ embedded tools. ## Quick Start @@ -39,24 +70,16 @@ core dev serve Download the core-devops image for your platform. ```bash -core dev install [flags] +core dev install ``` -### Flags - -| Flag | Description | -|------|-------------| -| `--source` | Image source: `github`, `registry`, `cdn` (default: auto) | -| `--force` | Force re-download even if exists | +Downloads the platform-specific dev environment image including Go, PHP, Node.js, Python, Docker, and Claude CLI. Downloads are cached at `~/.core/images/`. ### Examples ```bash # Download image (auto-detects platform) core dev install - -# Force re-download -core dev install --force ``` ## dev boot @@ -72,8 +95,8 @@ core dev boot [flags] | Flag | Description | |------|-------------| | `--memory` | Memory allocation in MB (default: 4096) | -| `--cpus` | Number of CPUs (default: 4) | -| `--name` | Container name (default: core-dev) | +| `--cpus` | Number of CPUs (default: 2) | +| `--fresh` | Stop existing and start fresh | ### Examples @@ -82,7 +105,10 @@ core dev boot [flags] core dev boot # More resources -core dev boot --memory 8192 --cpus 8 +core dev boot --memory 8192 --cpus 4 + +# Fresh start +core dev boot --fresh ``` ## dev shell @@ -90,9 +116,11 @@ core dev boot --memory 8192 --cpus 8 Open a shell in the running environment. ```bash -core dev shell [flags] +core dev shell [flags] [-- command] ``` +Uses SSH by default, or serial console with `--console`. + ### Flags | Flag | Description | @@ -107,6 +135,9 @@ core dev shell # Serial console (for debugging) core dev shell --console + +# Run a command +core dev shell -- ls -la ``` ## dev serve @@ -155,11 +186,11 @@ core dev test [flags] [-- custom command] | Flag | Description | |------|-------------| -| `--unit` | Run only unit tests | +| `--name` | Run named test command from `.core/test.yaml` | ### Test Detection -Core auto-detects the test framework: +Core auto-detects the test framework or uses `.core/test.yaml`: 1. `.core/test.yaml` - Custom config 2. `composer.json` → `composer test` @@ -174,29 +205,16 @@ Core auto-detects the test framework: # Auto-detect and run tests core dev test +# Run named test from config +core dev test --name integration + # Custom command core dev test -- go test -v ./pkg/... ``` ### Test Configuration -Create `.core/test.yaml` for custom test setup: - -```yaml -version: 1 - -commands: - - name: unit - run: vendor/bin/pest --parallel - - name: types - run: vendor/bin/phpstan analyse - - name: lint - run: vendor/bin/pint --test - -env: - APP_ENV: testing - DB_CONNECTION: sqlite -``` +Create `.core/test.yaml` for custom test setup - see [Configuration](example.md#configuration) for examples. ## dev claude @@ -210,8 +228,9 @@ core dev claude [flags] | Flag | Description | |------|-------------| -| `--no-auth` | Clean session without host credentials | -| `--auth` | Selective auth forwarding (e.g., `gh,anthropic`) | +| `--model` | Model to use (`opus`, `sonnet`) | +| `--no-auth` | Don't forward any auth credentials | +| `--auth` | Selective auth forwarding (`gh`, `anthropic`, `ssh`, `git`) | ### What Gets Forwarded @@ -227,11 +246,14 @@ By default, these are forwarded to the sandbox: # Full auth forwarding (default) core dev claude +# Use Opus model +core dev claude --model opus + # Clean sandbox core dev claude --no-auth -# Only GitHub auth -core dev claude --auth=gh +# Only GitHub and Anthropic auth +core dev claude --auth gh,anthropic ``` ### Why Use This? @@ -258,7 +280,7 @@ Output includes: ## dev update -Check for and download newer images. +Check for and apply updates. ```bash core dev update [flags] @@ -268,7 +290,17 @@ core dev update [flags] | Flag | Description | |------|-------------| -| `--force` | Force download even if up to date | +| `--apply` | Download and apply the update | + +### Examples + +```bash +# Check for updates +core dev update + +# Apply available update +core dev update --apply +``` ## Embedded Tools @@ -293,23 +325,7 @@ The core-devops image includes 100+ tools: ## Configuration -Global config in `~/.core/config.yaml`: - -```yaml -version: 1 - -images: - source: auto # auto | github | registry | cdn - - cdn: - url: https://images.example.com/core-devops - - github: - repo: host-uk/core-images - - registry: - image: ghcr.io/host-uk/core-devops -``` +Global config in `~/.core/config.yaml` - see [Configuration](example.md#configuration) for examples. ## Image Storage @@ -323,3 +339,50 @@ Images are stored in `~/.core/images/`: ├── core-devops-linux-amd64.qcow2 └── manifest.json ``` + +## Multi-Repo Commands + +See the [work](work/) page for detailed documentation on multi-repo commands. + +### dev ci + +Check GitHub Actions workflow status across all repos. + +```bash +core dev ci [flags] +``` + +#### Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to `repos.yaml` (auto-detected if not specified) | +| `--branch` | Filter by branch (default: main) | +| `--failed` | Show only failed runs | + +Requires the `gh` CLI to be installed and authenticated. + +### dev api + +Tools for managing service APIs. + +```bash +core dev api sync +``` + +Synchronizes the public service APIs with their internal implementations. + +### dev sync + +Alias for `core dev api sync`. Synchronizes the public service APIs with their internal implementations. + +```bash +core dev sync +``` + +This command scans the `pkg` directory for services and ensures that the top-level public API for each service is in sync with its internal implementation. It automatically generates the necessary Go files with type aliases. + +## See Also + +- [work](work/) - Multi-repo workflow commands (`core dev work`, `core dev health`, etc.) +- [ai](../ai/) - Task management commands (`core ai tasks`, `core ai task`, etc.) diff --git a/docs/packages/go/cmd/dev/issues/index.md b/docs/packages/go/cmd/dev/issues/index.md new file mode 100644 index 0000000..36091eb --- /dev/null +++ b/docs/packages/go/cmd/dev/issues/index.md @@ -0,0 +1,57 @@ +# core dev issues + +List open issues across all repositories. + +Fetches open issues from GitHub for all repos in the registry. Requires the `gh` CLI to be installed and authenticated. + +## Usage + +```bash +core dev issues [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to repos.yaml (auto-detected if not specified) | +| `--assignee` | Filter by assignee (use `@me` for yourself) | +| `--limit` | Max issues per repo (default 10) | + +## Examples + +```bash +# List all open issues +core dev issues + +# Show issues assigned to you +core dev issues --assignee @me + +# Limit to 5 issues per repo +core dev issues --limit 5 + +# Filter by specific assignee +core dev issues --assignee username +``` + +## Output + +``` +core-php (3 issues) + #42 Add retry logic to HTTP client bug + #38 Update documentation for v2 API docs + #35 Support custom serializers enhancement + +core-tenant (1 issue) + #12 Workspace isolation bug bug, critical +``` + +## Requirements + +- GitHub CLI (`gh`) must be installed +- Must be authenticated: `gh auth login` + +## See Also + +- [reviews command](../reviews/) - List PRs needing review +- [ci command](../ci/) - Check CI status diff --git a/docs/packages/go/cmd/dev/pull/index.md b/docs/packages/go/cmd/dev/pull/index.md new file mode 100644 index 0000000..1f6f3df --- /dev/null +++ b/docs/packages/go/cmd/dev/pull/index.md @@ -0,0 +1,47 @@ +# core dev pull + +Pull updates across all repositories. + +Pulls updates for all repos. By default only pulls repos that are behind. Use `--all` to pull all repos. + +## Usage + +```bash +core dev pull [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to repos.yaml (auto-detected if not specified) | +| `--all` | Pull all repos, not just those behind | + +## Examples + +```bash +# Pull only repos that are behind +core dev pull + +# Pull all repos +core dev pull --all + +# Use specific registry +core dev pull --registry ~/projects/repos.yaml +``` + +## Output + +``` +Pulling 2 repo(s) that are behind: + ✓ core-php (3 commits) + ✓ core-tenant (1 commit) + +Done: 2 pulled +``` + +## See Also + +- [push command](../push/) - Push local commits +- [health command](../health/) - Check sync status +- [work command](../work/) - Full workflow diff --git a/docs/packages/go/cmd/dev/push/index.md b/docs/packages/go/cmd/dev/push/index.md new file mode 100644 index 0000000..0c11195 --- /dev/null +++ b/docs/packages/go/cmd/dev/push/index.md @@ -0,0 +1,52 @@ +# core dev push + +Push commits across all repositories. + +Pushes unpushed commits for all repos. Shows repos with commits to push and confirms before pushing. + +## Usage + +```bash +core dev push [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to repos.yaml (auto-detected if not specified) | +| `--force` | Skip confirmation prompt | + +## Examples + +```bash +# Push with confirmation +core dev push + +# Push without confirmation +core dev push --force + +# Use specific registry +core dev push --registry ~/projects/repos.yaml +``` + +## Output + +``` +3 repo(s) with unpushed commits: + core-php: 2 commit(s) + core-admin: 1 commit(s) + core-tenant: 1 commit(s) + +Push all? [y/N] y + + ✓ core-php + ✓ core-admin + ✓ core-tenant +``` + +## See Also + +- [commit command](../commit/) - Create commits before pushing +- [pull command](../pull/) - Pull updates from remote +- [work command](../work/) - Full workflow (status + commit + push) diff --git a/docs/packages/go/cmd/dev/reviews/index.md b/docs/packages/go/cmd/dev/reviews/index.md new file mode 100644 index 0000000..44c09ad --- /dev/null +++ b/docs/packages/go/cmd/dev/reviews/index.md @@ -0,0 +1,61 @@ +# core dev reviews + +List PRs needing review across all repositories. + +Fetches open PRs from GitHub for all repos in the registry. Shows review status (approved, changes requested, pending). Requires the `gh` CLI to be installed and authenticated. + +## Usage + +```bash +core dev reviews [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to repos.yaml (auto-detected if not specified) | +| `--all` | Show all PRs including drafts | +| `--author` | Filter by PR author | + +## Examples + +```bash +# List PRs needing review +core dev reviews + +# Include draft PRs +core dev reviews --all + +# Filter by author +core dev reviews --author username +``` + +## Output + +``` +core-php (2 PRs) + #45 feat: Add caching layer ✓ approved @alice + #43 fix: Memory leak in worker ⏳ pending @bob + +core-admin (1 PR) + #28 refactor: Extract components ✗ changes @charlie +``` + +## Review Status + +| Symbol | Meaning | +|--------|---------| +| `✓` | Approved | +| `⏳` | Pending review | +| `✗` | Changes requested | + +## Requirements + +- GitHub CLI (`gh`) must be installed +- Must be authenticated: `gh auth login` + +## See Also + +- [issues command](../issues/) - List open issues +- [ci command](../ci/) - Check CI status diff --git a/docs/packages/go/cmd/dev/work/example.md b/docs/packages/go/cmd/dev/work/example.md new file mode 100644 index 0000000..74db3fb --- /dev/null +++ b/docs/packages/go/cmd/dev/work/example.md @@ -0,0 +1,33 @@ +# Dev Work Examples + +```bash +# Full workflow: status → commit → push +core dev work + +# Status only +core dev work --status +``` + +## Output + +``` +┌─────────────┬────────┬──────────┬─────────┐ +│ Repo │ Branch │ Status │ Behind │ +├─────────────┼────────┼──────────┼─────────┤ +│ core-php │ main │ clean │ 0 │ +│ core-tenant │ main │ 2 files │ 0 │ +│ core-admin │ dev │ clean │ 3 │ +└─────────────┴────────┴──────────┴─────────┘ +``` + +## Registry + +```yaml +repos: + - name: core + path: ./core + url: https://github.com/host-uk/core + - name: core-php + path: ./core-php + url: https://github.com/host-uk/core-php +``` diff --git a/docs/packages/go/cmd/dev/work/index.md b/docs/packages/go/cmd/dev/work/index.md new file mode 100644 index 0000000..454fe22 --- /dev/null +++ b/docs/packages/go/cmd/dev/work/index.md @@ -0,0 +1,293 @@ +# core dev work + +Multi-repo git operations for managing the host-uk organization. + +## Overview + +The `core dev work` command and related subcommands help manage multiple repositories in the host-uk ecosystem simultaneously. + +## Commands + +| Command | Description | +|---------|-------------| +| `core dev work` | Full workflow: status + commit + push | +| `core dev work --status` | Status table only | +| `core dev work --commit` | Use Claude to commit dirty repos | +| `core dev health` | Quick health check across all repos | +| `core dev commit` | Claude-assisted commits across repos | +| `core dev push` | Push commits across all repos | +| `core dev pull` | Pull updates across all repos | +| `core dev issues` | List open issues across all repos | +| `core dev reviews` | List PRs needing review | +| `core dev ci` | Check CI status across all repos | +| `core dev impact` | Show impact of changing a repo | + +## core dev work + +Manage git status, commits, and pushes across multiple repositories. + +```bash +core dev work [flags] +``` + +Reads `repos.yaml` to discover repositories and their relationships. Shows status, optionally commits with Claude, and pushes changes. + +### Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to `repos.yaml` (auto-detected if not specified) | +| `--status` | Show status only, don't push | +| `--commit` | Use Claude to commit dirty repos before pushing | + +### Examples + +```bash +# Full workflow +core dev work + +# Status only +core dev work --status + +# Commit and push +core dev work --commit +``` + +## core dev health + +Quick health check showing summary of repository health across all repos. + +```bash +core dev health [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to `repos.yaml` (auto-detected if not specified) | +| `--verbose` | Show detailed breakdown | + +Output shows: +- Total repos +- Dirty repos +- Unpushed commits +- Repos behind remote + +### Examples + +```bash +# Quick summary +core dev health + +# Detailed breakdown +core dev health --verbose +``` + +## core dev issues + +List open issues across all repositories. + +```bash +core dev issues [flags] +``` + +Fetches open issues from GitHub for all repos in the registry. Requires the `gh` CLI to be installed and authenticated. + +### Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to `repos.yaml` (auto-detected if not specified) | +| `--assignee` | Filter by assignee (use `@me` for yourself) | +| `--limit` | Max issues per repo (default: 10) | + +### Examples + +```bash +# List all open issues +core dev issues + +# Filter by assignee +core dev issues --assignee @me + +# Limit results +core dev issues --limit 5 +``` + +## core dev reviews + +List pull requests needing review across all repos. + +```bash +core dev reviews [flags] +``` + +Fetches open PRs from GitHub for all repos in the registry. Shows review status (approved, changes requested, pending). Requires the `gh` CLI to be installed and authenticated. + +### Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to `repos.yaml` (auto-detected if not specified) | +| `--all` | Show all PRs including drafts | +| `--author` | Filter by PR author | + +### Examples + +```bash +# List PRs needing review +core dev reviews + +# Show all PRs including drafts +core dev reviews --all + +# Filter by author +core dev reviews --author username +``` + +## core dev commit + +Create commits across repos with Claude assistance. + +```bash +core dev commit [flags] +``` + +Uses Claude to create commits for dirty repos. Shows uncommitted changes and invokes Claude to generate commit messages. + +### Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to `repos.yaml` (auto-detected if not specified) | +| `--all` | Commit all dirty repos without prompting | + +### Examples + +```bash +# Commit with prompts +core dev commit + +# Commit all automatically +core dev commit --all +``` + +## core dev push + +Push commits across all repos. + +```bash +core dev push [flags] +``` + +Pushes unpushed commits for all repos. Shows repos with commits to push and confirms before pushing. + +### Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to `repos.yaml` (auto-detected if not specified) | +| `--force` | Skip confirmation prompt | + +### Examples + +```bash +# Push with confirmation +core dev push + +# Skip confirmation +core dev push --force +``` + +## core dev pull + +Pull updates across all repos. + +```bash +core dev pull [flags] +``` + +Pulls updates for all repos. By default only pulls repos that are behind. Use `--all` to pull all repos. + +### Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to `repos.yaml` (auto-detected if not specified) | +| `--all` | Pull all repos, not just those behind | + +### Examples + +```bash +# Pull repos that are behind +core dev pull + +# Pull all repos +core dev pull --all +``` + +## core dev ci + +Check GitHub Actions workflow status across all repos. + +```bash +core dev ci [flags] +``` + +Fetches GitHub Actions workflow status for all repos. Shows latest run status for each repo. Requires the `gh` CLI to be installed and authenticated. + +### Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to `repos.yaml` (auto-detected if not specified) | +| `--branch` | Filter by branch (default: main) | +| `--failed` | Show only failed runs | + +### Examples + +```bash +# Show CI status for all repos +core dev ci + +# Show only failed runs +core dev ci --failed + +# Check specific branch +core dev ci --branch develop +``` + +## core dev impact + +Show the impact of changing a repository. + +```bash +core dev impact [flags] +``` + +Analyzes the dependency graph to show which repos would be affected by changes to the specified repo. + +### Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to `repos.yaml` (auto-detected if not specified) | + +### Examples + +```bash +# Show impact of changing core-php +core dev impact core-php +``` + +## Registry + +These commands use `repos.yaml` to know which repos to manage. See [repos.yaml](../../../configuration.md#reposyaml) for format. + +Use `core setup` to clone all repos from the registry. + +## See Also + +- [setup command](../../setup/) - Clone repos from registry +- [search command](../../pkg/search/) - Find and install repos diff --git a/docs/packages/go/cmd/docs/example.md b/docs/packages/go/cmd/docs/example.md new file mode 100644 index 0000000..7729970 --- /dev/null +++ b/docs/packages/go/cmd/docs/example.md @@ -0,0 +1,14 @@ +# Docs Examples + +## List + +```bash +core docs list +``` + +## Sync + +```bash +core docs sync +core docs sync --output ./docs +``` diff --git a/docs/packages/go/cmd/docs.md b/docs/packages/go/cmd/docs/index.md similarity index 97% rename from docs/packages/go/cmd/docs.md rename to docs/packages/go/cmd/docs/index.md index 083334a..d73ebf0 100644 --- a/docs/packages/go/cmd/docs.md +++ b/docs/packages/go/cmd/docs/index.md @@ -107,4 +107,4 @@ The synced docs are used to build https://core.help: ## See Also -- [Configuration](../configuration.md) - Project configuration +- [Configuration](../../configuration.md) - Project configuration diff --git a/docs/packages/go/cmd/doctor/example.md b/docs/packages/go/cmd/doctor/example.md new file mode 100644 index 0000000..ba94d71 --- /dev/null +++ b/docs/packages/go/cmd/doctor/example.md @@ -0,0 +1,20 @@ +# Doctor Examples + +```bash +core doctor +``` + +## Output + +``` +✓ go 1.25.0 +✓ git 2.43.0 +✓ gh 2.40.0 +✓ docker 24.0.7 +✓ task 3.30.0 +✓ golangci-lint 1.55.0 +✗ wails (not installed) +✓ php 8.3.0 +✓ composer 2.6.0 +✓ node 20.10.0 +``` diff --git a/docs/packages/go/cmd/doctor.md b/docs/packages/go/cmd/doctor/index.md similarity index 83% rename from docs/packages/go/cmd/doctor.md rename to docs/packages/go/cmd/doctor/index.md index 6ee4da5..02cc44d 100644 --- a/docs/packages/go/cmd/doctor.md +++ b/docs/packages/go/cmd/doctor/index.md @@ -5,9 +5,15 @@ Check your development environment for required tools and configuration. ## Usage ```bash -core doctor +core doctor [flags] ``` +## Flags + +| Flag | Description | +|------|-------------| +| `--verbose` | Show detailed version information | + ## What It Checks ### Required Tools @@ -71,5 +77,5 @@ All checks passed! ## See Also -- [setup command](setup.md) - Clone repos from registry -- [dev install](dev.md) - Install development environment +- [setup command](../setup/) - Clone repos from registry +- [dev](../dev/) - Development environment diff --git a/docs/packages/go/cmd/go/cov/example.md b/docs/packages/go/cmd/go/cov/example.md new file mode 100644 index 0000000..4fdc6c2 --- /dev/null +++ b/docs/packages/go/cmd/go/cov/example.md @@ -0,0 +1,18 @@ +# Go Coverage Examples + +```bash +# Summary +core go cov + +# HTML report +core go cov --html + +# Open in browser +core go cov --open + +# Fail if below threshold +core go cov --threshold 80 + +# Specific package +core go cov --pkg ./pkg/release +``` diff --git a/docs/packages/go/cmd/go/cov/index.md b/docs/packages/go/cmd/go/cov/index.md new file mode 100644 index 0000000..3adeca3 --- /dev/null +++ b/docs/packages/go/cmd/go/cov/index.md @@ -0,0 +1,28 @@ +# core go cov + +Generate coverage report with thresholds. + +## Usage + +```bash +core go cov [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--pkg` | Package to test (default: `./...`) | +| `--html` | Generate HTML coverage report | +| `--open` | Generate and open HTML report in browser | +| `--threshold` | Minimum coverage percentage (exit 1 if below) | + +## Examples + +```bash +core go cov # Summary +core go cov --html # HTML report +core go cov --open # Open in browser +core go cov --threshold 80 # Fail if < 80% +core go cov --pkg ./pkg/release # Specific package +``` diff --git a/docs/packages/go/cmd/go/example.md b/docs/packages/go/cmd/go/example.md new file mode 100644 index 0000000..51ad71a --- /dev/null +++ b/docs/packages/go/cmd/go/example.md @@ -0,0 +1,89 @@ +# Go Examples + +## Testing + +```bash +# Run all tests +core go test + +# Specific package +core go test --pkg ./pkg/core + +# Specific test +core go test --run TestHash + +# With coverage +core go test --coverage + +# Race detection +core go test --race +``` + +## Coverage + +```bash +# Summary +core go cov + +# HTML report +core go cov --html + +# Open in browser +core go cov --open + +# Fail if below threshold +core go cov --threshold 80 +``` + +## Formatting + +```bash +# Check +core go fmt + +# Fix +core go fmt --fix + +# Show diff +core go fmt --diff +``` + +## Linting + +```bash +# Check +core go lint + +# Auto-fix +core go lint --fix +``` + +## Installing + +```bash +# Auto-detect cmd/ +core go install + +# Specific path +core go install ./cmd/myapp + +# Pure Go (no CGO) +core go install --no-cgo +``` + +## Module Management + +```bash +core go mod tidy +core go mod download +core go mod verify +core go mod graph +``` + +## Workspace + +```bash +core go work sync +core go work init +core go work use ./pkg/mymodule +``` diff --git a/docs/packages/go/cmd/go/fmt/example.md b/docs/packages/go/cmd/go/fmt/example.md new file mode 100644 index 0000000..40233e0 --- /dev/null +++ b/docs/packages/go/cmd/go/fmt/example.md @@ -0,0 +1,12 @@ +# Go Format Examples + +```bash +# Check only +core go fmt + +# Apply fixes +core go fmt --fix + +# Show diff +core go fmt --diff +``` diff --git a/docs/packages/go/cmd/go/fmt/index.md b/docs/packages/go/cmd/go/fmt/index.md new file mode 100644 index 0000000..fe6113e --- /dev/null +++ b/docs/packages/go/cmd/go/fmt/index.md @@ -0,0 +1,25 @@ +# core go fmt + +Format Go code using goimports or gofmt. + +## Usage + +```bash +core go fmt [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--fix` | Fix formatting in place | +| `--diff` | Show diff of changes | +| `--check` | Check only, exit 1 if not formatted | + +## Examples + +```bash +core go fmt # Check formatting +core go fmt --fix # Fix formatting +core go fmt --diff # Show diff +``` diff --git a/docs/packages/go/cmd/go/index.md b/docs/packages/go/cmd/go/index.md new file mode 100644 index 0000000..981953c --- /dev/null +++ b/docs/packages/go/cmd/go/index.md @@ -0,0 +1,15 @@ +# core go + +Go development tools with enhanced output and environment setup. + +## Subcommands + +| Command | Description | +|---------|-------------| +| [test](test/) | Run tests with coverage | +| [cov](cov/) | Run tests with coverage report | +| [fmt](fmt/) | Format Go code | +| [lint](lint/) | Run golangci-lint | +| [install](install/) | Install Go binary | +| [mod](mod/) | Module management | +| [work](work/) | Workspace management | diff --git a/docs/packages/go/cmd/go/install/example.md b/docs/packages/go/cmd/go/install/example.md new file mode 100644 index 0000000..bba88cd --- /dev/null +++ b/docs/packages/go/cmd/go/install/example.md @@ -0,0 +1,15 @@ +# Go Install Examples + +```bash +# Auto-detect cmd/ +core go install + +# Specific path +core go install ./cmd/myapp + +# Pure Go (no CGO) +core go install --no-cgo + +# Verbose +core go install -v +``` diff --git a/docs/packages/go/cmd/go/install/index.md b/docs/packages/go/cmd/go/install/index.md new file mode 100644 index 0000000..e7bd109 --- /dev/null +++ b/docs/packages/go/cmd/go/install/index.md @@ -0,0 +1,25 @@ +# core go install + +Install Go binary with auto-detection. + +## Usage + +```bash +core go install [path] [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--no-cgo` | Disable CGO | +| `-v` | Verbose | + +## Examples + +```bash +core go install # Install current module +core go install ./cmd/core # Install specific path +core go install --no-cgo # Pure Go (no C dependencies) +core go install -v # Verbose output +``` diff --git a/docs/packages/go/cmd/go/lint/example.md b/docs/packages/go/cmd/go/lint/example.md new file mode 100644 index 0000000..56b46d4 --- /dev/null +++ b/docs/packages/go/cmd/go/lint/example.md @@ -0,0 +1,22 @@ +# Go Lint Examples + +```bash +# Check +core go lint + +# Auto-fix +core go lint --fix +``` + +## Configuration + +`.golangci.yml`: + +```yaml +linters: + enable: + - gofmt + - govet + - errcheck + - staticcheck +``` diff --git a/docs/packages/go/cmd/go/lint/index.md b/docs/packages/go/cmd/go/lint/index.md new file mode 100644 index 0000000..5f9e804 --- /dev/null +++ b/docs/packages/go/cmd/go/lint/index.md @@ -0,0 +1,22 @@ +# core go lint + +Run golangci-lint. + +## Usage + +```bash +core go lint [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--fix` | Fix issues automatically | + +## Examples + +```bash +core go lint # Check +core go lint --fix # Auto-fix +``` diff --git a/docs/packages/go/cmd/go/mod/download/index.md b/docs/packages/go/cmd/go/mod/download/index.md new file mode 100644 index 0000000..240ef6d --- /dev/null +++ b/docs/packages/go/cmd/go/mod/download/index.md @@ -0,0 +1,29 @@ +# core go mod download + +Download modules to local cache. + +Wrapper around `go mod download`. Downloads all dependencies to the module cache. + +## Usage + +```bash +core go mod download +``` + +## What It Does + +- Downloads all modules in go.mod to `$GOPATH/pkg/mod` +- Useful for pre-populating cache for offline builds +- Validates checksums against go.sum + +## Examples + +```bash +# Download all dependencies +core go mod download +``` + +## See Also + +- [tidy](../tidy/) - Clean up go.mod +- [verify](../verify/) - Verify checksums diff --git a/docs/packages/go/cmd/go/mod/example.md b/docs/packages/go/cmd/go/mod/example.md new file mode 100644 index 0000000..57d2e66 --- /dev/null +++ b/docs/packages/go/cmd/go/mod/example.md @@ -0,0 +1,15 @@ +# Go Module Examples + +```bash +# Tidy +core go mod tidy + +# Download +core go mod download + +# Verify +core go mod verify + +# Graph +core go mod graph +``` diff --git a/docs/packages/go/cmd/go/mod/graph/index.md b/docs/packages/go/cmd/go/mod/graph/index.md new file mode 100644 index 0000000..2aa2619 --- /dev/null +++ b/docs/packages/go/cmd/go/mod/graph/index.md @@ -0,0 +1,44 @@ +# core go mod graph + +Print module dependency graph. + +Wrapper around `go mod graph`. Outputs the module dependency graph in text form. + +## Usage + +```bash +core go mod graph +``` + +## What It Does + +- Prints module dependencies as pairs +- Each line shows: `module@version dependency@version` +- Useful for understanding dependency relationships + +## Examples + +```bash +# Print dependency graph +core go mod graph + +# Find who depends on a specific module +core go mod graph | grep "some/module" + +# Visualise with graphviz +core go mod graph | dot -Tpng -o deps.png +``` + +## Output + +``` +github.com/host-uk/core github.com/stretchr/testify@v1.11.1 +github.com/stretchr/testify@v1.11.1 github.com/davecgh/go-spew@v1.1.2 +github.com/stretchr/testify@v1.11.1 github.com/pmezard/go-difflib@v1.0.1 +... +``` + +## See Also + +- [tidy](../tidy/) - Clean up go.mod +- [dev impact](../../../dev/impact/) - Show repo dependency impact diff --git a/docs/packages/go/cmd/go/mod/index.md b/docs/packages/go/cmd/go/mod/index.md new file mode 100644 index 0000000..ee8e46e --- /dev/null +++ b/docs/packages/go/cmd/go/mod/index.md @@ -0,0 +1,21 @@ +# core go mod + +Module management. + +## Subcommands + +| Command | Description | +|---------|-------------| +| `tidy` | Add missing and remove unused modules | +| `download` | Download modules to local cache | +| `verify` | Verify dependencies | +| `graph` | Print module dependency graph | + +## Examples + +```bash +core go mod tidy +core go mod download +core go mod verify +core go mod graph +``` diff --git a/docs/packages/go/cmd/go/mod/tidy/index.md b/docs/packages/go/cmd/go/mod/tidy/index.md new file mode 100644 index 0000000..684b07e --- /dev/null +++ b/docs/packages/go/cmd/go/mod/tidy/index.md @@ -0,0 +1,29 @@ +# core go mod tidy + +Add missing and remove unused modules. + +Wrapper around `go mod tidy`. Ensures go.mod and go.sum are in sync with the source code. + +## Usage + +```bash +core go mod tidy +``` + +## What It Does + +- Adds missing module requirements +- Removes unused module requirements +- Updates go.sum with checksums + +## Examples + +```bash +# Tidy the current module +core go mod tidy +``` + +## See Also + +- [download](../download/) - Download modules +- [verify](../verify/) - Verify dependencies diff --git a/docs/packages/go/cmd/go/mod/verify/index.md b/docs/packages/go/cmd/go/mod/verify/index.md new file mode 100644 index 0000000..e01dc2a --- /dev/null +++ b/docs/packages/go/cmd/go/mod/verify/index.md @@ -0,0 +1,41 @@ +# core go mod verify + +Verify dependencies have not been modified. + +Wrapper around `go mod verify`. Checks that dependencies in the module cache match their checksums in go.sum. + +## Usage + +```bash +core go mod verify +``` + +## What It Does + +- Verifies each module in the cache +- Compares against go.sum checksums +- Reports any tampering or corruption + +## Examples + +```bash +# Verify all dependencies +core go mod verify +``` + +## Output + +``` +all modules verified +``` + +Or if verification fails: + +``` +github.com/example/pkg v1.2.3: dir has been modified +``` + +## See Also + +- [download](../download/) - Download modules +- [tidy](../tidy/) - Clean up go.mod diff --git a/docs/packages/go/cmd/go/test/example.md b/docs/packages/go/cmd/go/test/example.md new file mode 100644 index 0000000..85ff1b5 --- /dev/null +++ b/docs/packages/go/cmd/go/test/example.md @@ -0,0 +1,27 @@ +# Go Test Examples + +```bash +# All tests +core go test + +# Specific package +core go test --pkg ./pkg/core + +# Specific test +core go test --run TestHash + +# With coverage +core go test --coverage + +# Race detection +core go test --race + +# Short tests only +core go test --short + +# Verbose +core go test -v + +# JSON output (CI) +core go test --json +``` diff --git a/docs/packages/go/cmd/go/test/index.md b/docs/packages/go/cmd/go/test/index.md new file mode 100644 index 0000000..8b54524 --- /dev/null +++ b/docs/packages/go/cmd/go/test/index.md @@ -0,0 +1,31 @@ +# core go test + +Run Go tests with coverage and filtered output. + +## Usage + +```bash +core go test [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--pkg` | Package to test (default: `./...`) | +| `--run` | Run only tests matching regexp | +| `--short` | Run only short tests | +| `--race` | Enable race detector | +| `--coverage` | Show detailed per-package coverage | +| `--json` | Output JSON results | +| `-v` | Verbose output | + +## Examples + +```bash +core go test # All tests +core go test --pkg ./pkg/core # Specific package +core go test --run TestHash # Specific test +core go test --coverage # With coverage +core go test --race # Race detection +``` diff --git a/docs/packages/go/cmd/go/work/index.md b/docs/packages/go/cmd/go/work/index.md new file mode 100644 index 0000000..4022507 --- /dev/null +++ b/docs/packages/go/cmd/go/work/index.md @@ -0,0 +1,19 @@ +# core go work + +Go workspace management commands. + +## Subcommands + +| Command | Description | +|---------|-------------| +| `sync` | Sync go.work with modules | +| `init` | Initialize go.work | +| `use` | Add module to workspace | + +## Examples + +```bash +core go work sync # Sync workspace +core go work init # Initialize workspace +core go work use ./pkg/mymodule # Add module to workspace +``` diff --git a/docs/packages/go/cmd/go/work/init/index.md b/docs/packages/go/cmd/go/work/init/index.md new file mode 100644 index 0000000..6527324 --- /dev/null +++ b/docs/packages/go/cmd/go/work/init/index.md @@ -0,0 +1,40 @@ +# core go work init + +Initialize a Go workspace. + +Wrapper around `go work init`. Creates a new go.work file in the current directory. + +## Usage + +```bash +core go work init +``` + +## What It Does + +- Creates a go.work file +- Automatically adds current module if go.mod exists +- Enables multi-module development + +## Examples + +```bash +# Initialize workspace +core go work init + +# Then add more modules +core go work use ./pkg/mymodule +``` + +## Generated File + +```go +go 1.25 + +use . +``` + +## See Also + +- [use](../use/) - Add module to workspace +- [sync](../sync/) - Sync workspace diff --git a/docs/packages/go/cmd/go/work/sync/index.md b/docs/packages/go/cmd/go/work/sync/index.md new file mode 100644 index 0000000..38caed1 --- /dev/null +++ b/docs/packages/go/cmd/go/work/sync/index.md @@ -0,0 +1,35 @@ +# core go work sync + +Sync go.work with modules. + +Wrapper around `go work sync`. Synchronises the workspace's build list back to the workspace modules. + +## Usage + +```bash +core go work sync +``` + +## What It Does + +- Updates each module's go.mod to match the workspace build list +- Ensures all modules use compatible dependency versions +- Run after adding new modules or updating dependencies + +## Examples + +```bash +# Sync workspace +core go work sync +``` + +## When To Use + +- After running `go get` to update a dependency +- After adding a new module with `core go work use` +- When modules have conflicting dependency versions + +## See Also + +- [init](../init/) - Initialize workspace +- [use](../use/) - Add module to workspace diff --git a/docs/packages/go/cmd/go/work/use/index.md b/docs/packages/go/cmd/go/work/use/index.md new file mode 100644 index 0000000..25e0cab --- /dev/null +++ b/docs/packages/go/cmd/go/work/use/index.md @@ -0,0 +1,46 @@ +# core go work use + +Add module to workspace. + +Wrapper around `go work use`. Adds one or more modules to the go.work file. + +## Usage + +```bash +core go work use [paths...] +``` + +## What It Does + +- Adds specified module paths to go.work +- Auto-discovers modules if no paths given +- Enables developing multiple modules together + +## Examples + +```bash +# Add a specific module +core go work use ./pkg/mymodule + +# Add multiple modules +core go work use ./pkg/one ./pkg/two + +# Auto-discover and add all modules +core go work use +``` + +## Auto-Discovery + +When called without arguments, scans for go.mod files and adds all found modules: + +```bash +core go work use +# Added ./pkg/build +# Added ./pkg/repos +# Added ./cmd/core +``` + +## See Also + +- [init](../init/) - Initialize workspace +- [sync](../sync/) - Sync workspace diff --git a/docs/packages/go/cmd/index.md b/docs/packages/go/cmd/index.md new file mode 100644 index 0000000..fce3183 --- /dev/null +++ b/docs/packages/go/cmd/index.md @@ -0,0 +1,31 @@ +# Core CLI + +Unified interface for Go/PHP development, multi-repo management, and deployment. + +## Commands + +| Command | Description | +|---------|-------------| +| [ai](ai/) | AI agent task management and Claude integration | +| [go](go/) | Go development tools | +| [php](php/) | Laravel/PHP development tools | +| [build](build/) | Build projects | +| [ci](ci/) | Publish releases | +| [sdk](sdk/) | SDK validation and compatibility | +| [dev](dev/) | Multi-repo workflow + dev environment | +| [pkg](pkg/) | Package management | +| [vm](vm/) | LinuxKit VM management | +| [docs](docs/) | Documentation management | +| [setup](setup/) | Clone repos from registry | +| [doctor](doctor/) | Check environment | +| [test](test/) | Run Go tests with coverage | + +## Installation + +```bash +go install github.com/host-uk/core/cmd/core@latest +``` + +Verify: `core doctor` + +See [Getting Started](../getting-started.md) for all installation options. diff --git a/docs/packages/go/cmd/php.md b/docs/packages/go/cmd/php.md deleted file mode 100644 index ff3a203..0000000 --- a/docs/packages/go/cmd/php.md +++ /dev/null @@ -1,138 +0,0 @@ -# core php - -Laravel/PHP development environment with FrankenPHP, Vite, Horizon, Reverb, and Redis. - -## Commands - -| Command | Description | -|---------|-------------| -| `core php dev` | Start development environment | -| `core php test` | Run PHPUnit/Pest tests | -| `core php fmt` | Format with Laravel Pint | -| `core php analyse` | Static analysis with PHPStan | -| `core php build` | Build production container | -| `core php deploy` | Deploy to Coolify | - -## Development Environment - -```bash -# Start all services -core php dev -``` - -This starts: -- FrankenPHP/Octane (HTTP server) -- Vite dev server (frontend) -- Laravel Horizon (queues) -- Laravel Reverb (WebSockets) -- Redis - -```bash -# View unified logs -core php logs - -# Stop all services -core php stop -``` - -## Testing - -```bash -# Run tests -core php test - -# Parallel testing -core php test --parallel - -# With coverage -core php test --coverage -``` - -## Code Quality - -```bash -# Format code -core php fmt - -# Static analysis -core php analyse - -# Run both -core php fmt && core php analyse -``` - -## Building - -```bash -# Build Docker container -core php build - -# Build LinuxKit image -core php build --type linuxkit - -# Run production locally -core php serve --production -``` - -## Deployment - -```bash -# Deploy to Coolify -core php deploy - -# Deploy to staging -core php deploy --staging - -# Check deployment status -core php deploy:status - -# Rollback -core php deploy:rollback -``` - -## Package Management - -Link local packages for development: - -```bash -# Link a local package -core php packages link ../my-package - -# Update linked packages -core php packages update - -# Unlink -core php packages unlink my-package -``` - -## SSL/HTTPS - -Local SSL with mkcert: - -```bash -# Auto-configured with core php dev -# Uses mkcert for trusted local certificates -``` - -## Configuration - -Optional `.core/php.yaml`: - -```yaml -version: 1 - -dev: - domain: myapp.test - ssl: true - services: - - frankenphp - - vite - - horizon - - reverb - - redis - -deploy: - coolify: - server: https://coolify.example.com - project: my-project -``` diff --git a/docs/packages/go/cmd/php/example.md b/docs/packages/go/cmd/php/example.md new file mode 100644 index 0000000..96e1600 --- /dev/null +++ b/docs/packages/go/cmd/php/example.md @@ -0,0 +1,111 @@ +# PHP Examples + +## Development + +```bash +# Start all services +core php dev + +# With HTTPS +core php dev --https + +# Skip services +core php dev --no-vite --no-horizon +``` + +## Testing + +```bash +# Run all +core php test + +# Parallel +core php test --parallel + +# With coverage +core php test --coverage + +# Filter +core php test --filter UserTest +``` + +## Code Quality + +```bash +# Format +core php fmt --fix + +# Static analysis +core php analyse --level 9 +``` + +## Deployment + +```bash +# Production +core php deploy + +# Staging +core php deploy --staging + +# Wait for completion +core php deploy --wait + +# Check status +core php deploy:status + +# Rollback +core php deploy:rollback +``` + +## Configuration + +### .env + +```env +COOLIFY_URL=https://coolify.example.com +COOLIFY_TOKEN=your-api-token +COOLIFY_APP_ID=production-app-id +COOLIFY_STAGING_APP_ID=staging-app-id +``` + +### .core/php.yaml + +```yaml +version: 1 + +dev: + domain: myapp.test + ssl: true + services: + - frankenphp + - vite + - horizon + - reverb + - redis + +deploy: + coolify: + server: https://coolify.example.com + project: my-project +``` + +## Package Linking + +```bash +# Link local packages +core php packages link ../my-package + +# Update linked +core php packages update + +# Unlink +core php packages unlink my-package +``` + +## SSL Setup + +```bash +core php ssl +core php ssl --domain myapp.test +``` diff --git a/docs/packages/go/cmd/php/index.md b/docs/packages/go/cmd/php/index.md new file mode 100644 index 0000000..83ad596 --- /dev/null +++ b/docs/packages/go/cmd/php/index.md @@ -0,0 +1,413 @@ +# core php + +Laravel/PHP development tools with FrankenPHP. + +## Commands + +### Development + +| Command | Description | +|---------|-------------| +| [`dev`](#php-dev) | Start development environment | +| [`logs`](#php-logs) | View service logs | +| [`stop`](#php-stop) | Stop all services | +| [`status`](#php-status) | Show service status | +| [`ssl`](#php-ssl) | Setup SSL certificates with mkcert | + +### Build & Production + +| Command | Description | +|---------|-------------| +| [`build`](#php-build) | Build Docker or LinuxKit image | +| [`serve`](#php-serve) | Run production container | +| [`shell`](#php-shell) | Open shell in running container | + +### Code Quality + +| Command | Description | +|---------|-------------| +| [`test`](#php-test) | Run PHP tests (PHPUnit/Pest) | +| [`fmt`](#php-fmt) | Format code with Laravel Pint | +| [`analyse`](#php-analyse) | Run PHPStan static analysis | + +### Package Management + +| Command | Description | +|---------|-------------| +| [`packages link`](#php-packages-link) | Link local packages by path | +| [`packages unlink`](#php-packages-unlink) | Unlink packages by name | +| [`packages update`](#php-packages-update) | Update linked packages | +| [`packages list`](#php-packages-list) | List linked packages | + +### Deployment (Coolify) + +| Command | Description | +|---------|-------------| +| [`deploy`](#php-deploy) | Deploy to Coolify | +| [`deploy:status`](#php-deploystatus) | Show deployment status | +| [`deploy:rollback`](#php-deployrollback) | Rollback to previous deployment | +| [`deploy:list`](#php-deploylist) | List recent deployments | + +--- + +## php dev + +Start the Laravel development environment with all detected services. + +```bash +core php dev [flags] +``` + +### Services Orchestrated + +- **FrankenPHP/Octane** - HTTP server (port 8000, HTTPS on 443) +- **Vite** - Frontend dev server (port 5173) +- **Laravel Horizon** - Queue workers +- **Laravel Reverb** - WebSocket server (port 8080) +- **Redis** - Cache and queue backend (port 6379) + +### Flags + +| Flag | Description | +|------|-------------| +| `--no-vite` | Skip Vite dev server | +| `--no-horizon` | Skip Laravel Horizon | +| `--no-reverb` | Skip Laravel Reverb | +| `--no-redis` | Skip Redis server | +| `--https` | Enable HTTPS with mkcert | +| `--domain` | Domain for SSL certificate (default: from APP_URL) | +| `--port` | FrankenPHP port (default: 8000) | + +### Examples + +```bash +# Start all detected services +core php dev + +# With HTTPS +core php dev --https + +# Skip optional services +core php dev --no-horizon --no-reverb +``` + +--- + +## php logs + +Stream unified logs from all running services. + +```bash +core php logs [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--follow` | Follow log output | +| `--service` | Specific service (frankenphp, vite, horizon, reverb, redis) | + +--- + +## php stop + +Stop all running Laravel services. + +```bash +core php stop +``` + +--- + +## php status + +Show the status of all Laravel services and project configuration. + +```bash +core php status +``` + +--- + +## php ssl + +Setup local SSL certificates using mkcert. + +```bash +core php ssl [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--domain` | Domain for certificate (default: from APP_URL or localhost) | + +--- + +## php build + +Build a production-ready container image. + +```bash +core php build [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--type` | Build type: `docker` (default) or `linuxkit` | +| `--name` | Image name (default: project directory name) | +| `--tag` | Image tag (default: latest) | +| `--platform` | Target platform (e.g., linux/amd64, linux/arm64) | +| `--dockerfile` | Path to custom Dockerfile | +| `--output` | Output path for LinuxKit image | +| `--format` | LinuxKit format: qcow2 (default), iso, raw, vmdk | +| `--template` | LinuxKit template name (default: server-php) | +| `--no-cache` | Build without cache | + +### Examples + +```bash +# Build Docker image +core php build + +# With custom name and tag +core php build --name myapp --tag v1.0 + +# Build LinuxKit image +core php build --type linuxkit +``` + +--- + +## php serve + +Run a production container. + +```bash +core php serve [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--name` | Docker image name (required) | +| `--tag` | Image tag (default: latest) | +| `--container` | Container name | +| `--port` | HTTP port (default: 80) | +| `--https-port` | HTTPS port (default: 443) | +| `-d` | Run in detached mode | +| `--env-file` | Path to environment file | + +### Examples + +```bash +core php serve --name myapp +core php serve --name myapp -d +core php serve --name myapp --port 8080 +``` + +--- + +## php shell + +Open an interactive shell in a running container. + +```bash +core php shell +``` + +--- + +## php test + +Run PHP tests using PHPUnit or Pest. + +```bash +core php test [flags] +``` + +Auto-detects Pest if `tests/Pest.php` exists. + +### Flags + +| Flag | Description | +|------|-------------| +| `--parallel` | Run tests in parallel | +| `--coverage` | Generate code coverage | +| `--filter` | Filter tests by name pattern | +| `--group` | Run only tests in specified group | + +### Examples + +```bash +core php test +core php test --parallel --coverage +core php test --filter UserTest +``` + +--- + +## php fmt + +Format PHP code using Laravel Pint. + +```bash +core php fmt [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--fix` | Auto-fix formatting issues | +| `--diff` | Show diff of changes | + +--- + +## php analyse + +Run PHPStan or Larastan static analysis. + +```bash +core php analyse [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--level` | PHPStan analysis level (0-9) | +| `--memory` | Memory limit (e.g., 2G) | + +--- + +## php packages link + +Link local PHP packages for development. + +```bash +core php packages link [...] +``` + +Adds path repositories to composer.json with symlink enabled. + +--- + +## php packages unlink + +Remove linked packages from composer.json. + +```bash +core php packages unlink [...] +``` + +--- + +## php packages update + +Update linked packages via Composer. + +```bash +core php packages update [...] +``` + +--- + +## php packages list + +List all locally linked packages. + +```bash +core php packages list +``` + +--- + +## php deploy + +Deploy the PHP application to Coolify. + +```bash +core php deploy [flags] +``` + +### Configuration + +Requires environment variables in `.env`: +``` +COOLIFY_URL=https://coolify.example.com +COOLIFY_TOKEN=your-api-token +COOLIFY_APP_ID=production-app-id +COOLIFY_STAGING_APP_ID=staging-app-id +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--staging` | Deploy to staging environment | +| `--force` | Force deployment even if no changes detected | +| `--wait` | Wait for deployment to complete | + +--- + +## php deploy:status + +Show the status of a deployment. + +```bash +core php deploy:status [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--staging` | Check staging environment | +| `--id` | Specific deployment ID | + +--- + +## php deploy:rollback + +Rollback to a previous deployment. + +```bash +core php deploy:rollback [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--staging` | Rollback staging environment | +| `--id` | Specific deployment ID to rollback to | +| `--wait` | Wait for rollback to complete | + +--- + +## php deploy:list + +List recent deployments. + +```bash +core php deploy:list [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--staging` | List staging deployments | +| `--limit` | Number of deployments (default: 10) | + +--- + +## Configuration + +Optional `.core/php.yaml` - see [Configuration](example.md#configuration) for examples. diff --git a/docs/packages/go/cmd/pkg/example.md b/docs/packages/go/cmd/pkg/example.md new file mode 100644 index 0000000..7904aae --- /dev/null +++ b/docs/packages/go/cmd/pkg/example.md @@ -0,0 +1,36 @@ +# Package Examples + +## Search + +```bash +core pkg search core- +core pkg search api +core pkg search --org myorg +``` + +## Install + +```bash +core pkg install core-api +core pkg install host-uk/core-api +``` + +## List + +```bash +core pkg list +core pkg list --format json +``` + +## Update + +```bash +core pkg update +core pkg update core-api +``` + +## Outdated + +```bash +core pkg outdated +``` diff --git a/docs/packages/go/cmd/pkg/index.md b/docs/packages/go/cmd/pkg/index.md new file mode 100644 index 0000000..fcc218b --- /dev/null +++ b/docs/packages/go/cmd/pkg/index.md @@ -0,0 +1,144 @@ +# core pkg + +Package management for host-uk repositories. + +## Usage + +```bash +core pkg [flags] +``` + +## Commands + +| Command | Description | +|---------|-------------| +| [`search`](#pkg-search) | Search GitHub for packages | +| [`install`](#pkg-install) | Clone a package from GitHub | +| [`list`](#pkg-list) | List installed packages | +| [`update`](#pkg-update) | Update installed packages | +| [`outdated`](#pkg-outdated) | Check for outdated packages | + +--- + +## pkg search + +Search GitHub for host-uk packages. + +```bash +core pkg search [flags] +``` + +Results are cached for 1 hour in `.core/cache/`. + +### Flags + +| Flag | Description | +|------|-------------| +| `--org` | GitHub organisation (default: host-uk) | +| `--pattern` | Repo name pattern (* for wildcard) | +| `--type` | Filter by type in name (mod, services, plug, website) | +| `--limit` | Max results (default: 50) | +| `--refresh` | Bypass cache and fetch fresh data | + +### Examples + +```bash +# List all repos in org +core pkg search + +# Search for core-* repos +core pkg search --pattern 'core-*' + +# Search different org +core pkg search --org mycompany + +# Bypass cache +core pkg search --refresh +``` + +--- + +## pkg install + +Clone a package from GitHub. + +```bash +core pkg install [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--dir` | Target directory (default: ./packages or current dir) | +| `--add` | Add to repos.yaml registry | + +### Examples + +```bash +# Clone to packages/ +core pkg install host-uk/core-php + +# Clone to custom directory +core pkg install host-uk/core-tenant --dir ./packages + +# Clone and add to registry +core pkg install host-uk/core-admin --add +``` + +--- + +## pkg list + +List installed packages from repos.yaml. + +```bash +core pkg list +``` + +Shows installed status (✓) and description for each package. + +--- + +## pkg update + +Pull latest changes for installed packages. + +```bash +core pkg update [...] [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--all` | Update all packages | + +### Examples + +```bash +# Update specific package +core pkg update core-php + +# Update all packages +core pkg update --all +``` + +--- + +## pkg outdated + +Check which packages have unpulled commits. + +```bash +core pkg outdated +``` + +Fetches from remote and shows packages that are behind. + +--- + +## See Also + +- [setup](../setup/) - Clone all repos from registry +- [dev work](../dev/work/) - Multi-repo workflow diff --git a/docs/packages/go/cmd/pkg/search/example.md b/docs/packages/go/cmd/pkg/search/example.md new file mode 100644 index 0000000..fbcaa6f --- /dev/null +++ b/docs/packages/go/cmd/pkg/search/example.md @@ -0,0 +1,23 @@ +# Package Search Examples + +```bash +# Find all core-* packages +core pkg search core- + +# Search term +core pkg search api + +# Different org +core pkg search --org myorg query +``` + +## Output + +``` +┌──────────────┬─────────────────────────────┐ +│ Package │ Description │ +├──────────────┼─────────────────────────────┤ +│ core-api │ REST API framework │ +│ core-auth │ Authentication utilities │ +└──────────────┴─────────────────────────────┘ +``` diff --git a/docs/packages/go/cmd/pkg/search/index.md b/docs/packages/go/cmd/pkg/search/index.md new file mode 100644 index 0000000..57fea91 --- /dev/null +++ b/docs/packages/go/cmd/pkg/search/index.md @@ -0,0 +1,75 @@ +# core pkg search + +Search GitHub for repositories matching a pattern. + +Uses `gh` CLI for authenticated search. Results are cached for 1 hour. + +## Usage + +```bash +core pkg search [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--pattern` | Repo name pattern (* for wildcard) | +| `--org` | GitHub organization (default: host-uk) | +| `--type` | Filter by type in name (mod, services, plug, website) | +| `--limit` | Max results (default: 50) | +| `--refresh` | Bypass cache and fetch fresh data | + +## Examples + +```bash +# List all host-uk repos +core pkg search + +# Search for core-* repos +core pkg search --pattern "core-*" + +# Search different org +core pkg search --org mycompany + +# Filter by type +core pkg search --type services + +# Bypass cache +core pkg search --refresh + +# Combine filters +core pkg search --pattern "core-*" --type mod --limit 20 +``` + +## Output + +``` +Found 5 repositories: + + host-uk/core + Go CLI for the host-uk ecosystem + ★ 42 Go Updated 2 hours ago + + host-uk/core-php + PHP/Laravel packages for Core + ★ 18 PHP Updated 1 day ago + + host-uk/core-images + Docker and LinuxKit images + ★ 8 Dockerfile Updated 3 days ago +``` + +## Authentication + +Uses GitHub CLI (`gh`) authentication. Ensure you're logged in: + +```bash +gh auth status +gh auth login # if not authenticated +``` + +## See Also + +- [pkg install](../) - Clone a package from GitHub +- [setup command](../../setup/) - Clone all repos from registry diff --git a/docs/packages/go/cmd/release.md b/docs/packages/go/cmd/release.md deleted file mode 100644 index 0c5217b..0000000 --- a/docs/packages/go/cmd/release.md +++ /dev/null @@ -1,201 +0,0 @@ -# core release - -Build and publish releases to GitHub, npm, Homebrew, Scoop, AUR, Chocolatey, Docker, and LinuxKit. - -## Usage - -```bash -core release [flags] -``` - -## Flags - -| Flag | Description | -|------|-------------| -| `--dry-run` | Preview what would be published | -| `--version` | Override version (default: git tag) | -| `--target` | Release target: `sdk` for SDK-only release | -| `--draft` | Create release as draft | -| `--prerelease` | Mark release as prerelease | - -## Quick Start - -```bash -# Initialize release config -core release init - -# Preview release -core release --dry-run - -# Release -core release - -# SDK-only release -core release --target sdk -``` - -## SDK Release - -Generate SDKs without building binaries: - -```bash -# Generate SDKs with version from git tag -core release --target sdk - -# Explicit version -core release --target sdk --version v1.2.3 - -# Preview -core release --target sdk --dry-run -``` - -This will: -1. Determine version from git tags (or `--version` flag) -2. Run breaking change detection if configured -3. Generate SDKs for all configured languages -4. Output to `sdk/` directory - -See [SDK commands](sdk.md) for more details. - -## Publishers - -### GitHub Releases - -Uploads artifacts and changelog to GitHub Releases. - -```yaml -publishers: - - type: github - prerelease: false - draft: false -``` - -### npm - -Publishes binary wrapper that downloads correct platform binary. - -```yaml -publishers: - - type: npm - package: "@myorg/myapp" - access: public -``` - -Requires `NPM_TOKEN` environment variable. - -### Homebrew - -Generates formula and commits to tap repository. - -```yaml -publishers: - - type: homebrew - tap: myorg/homebrew-tap - formula: myapp # optional, defaults to project name -``` - -For official Homebrew PR: - -```yaml -publishers: - - type: homebrew - tap: myorg/homebrew-tap - official: - enabled: true - output: dist/homebrew -``` - -### Scoop - -Generates manifest and commits to bucket repository. - -```yaml -publishers: - - type: scoop - bucket: myorg/scoop-bucket -``` - -### AUR - -Generates PKGBUILD and pushes to AUR. - -```yaml -publishers: - - type: aur - maintainer: "Your Name " -``` - -### Chocolatey - -Generates NuSpec and optionally pushes to Chocolatey. - -```yaml -publishers: - - type: chocolatey - push: false # generate only -``` - -Set `push: true` and `CHOCOLATEY_API_KEY` to publish. - -### Docker - -Builds and pushes multi-arch Docker images. - -```yaml -publishers: - - type: docker - registry: ghcr.io - image: myorg/myapp - platforms: - - linux/amd64 - - linux/arm64 - tags: - - latest - - "{{.Version}}" -``` - -### LinuxKit - -Builds immutable LinuxKit images. - -```yaml -publishers: - - type: linuxkit - config: .core/linuxkit/server.yml - formats: - - iso - - qcow2 - - docker # Immutable container - platforms: - - linux/amd64 - - linux/arm64 -``` - -## Full Example - -See [examples/full-release.yaml](examples/full-release.yaml) for a complete configuration. - -## Changelog - -Changelog is auto-generated from conventional commits: - -``` -feat: Add new feature → Features -fix: Fix bug → Bug Fixes -perf: Improve performance → Performance -refactor: Refactor code → Refactoring -``` - -Configure in `.core/release.yaml`: - -```yaml -changelog: - include: - - feat - - fix - - perf - exclude: - - chore - - docs - - test -``` diff --git a/docs/packages/go/cmd/run.md b/docs/packages/go/cmd/run.md deleted file mode 100644 index 1e760ce..0000000 --- a/docs/packages/go/cmd/run.md +++ /dev/null @@ -1,132 +0,0 @@ -# core run - -Run LinuxKit images with qemu or hyperkit. - -## Usage - -```bash -core run [flags] -``` - -## Flags - -| Flag | Description | -|------|-------------| -| `-d, --detach` | Run in background | -| `--cpus` | Number of CPUs (default: 1) | -| `--mem` | Memory in MB (default: 1024) | -| `--disk` | Disk size (default: none) | -| `--template` | Use built-in template | - -## Examples - -### Run ISO Image - -```bash -# Run LinuxKit ISO -core run server.iso - -# With more resources -core run server.iso --cpus 2 --mem 2048 - -# Detached mode -core run server.iso -d -``` - -### Run from Template - -```bash -# List available templates -core templates - -# Run template -core run --template core-dev -``` - -### Formats - -Supported image formats: -- `.iso` - Bootable ISO -- `.qcow2` - QEMU disk image -- `.raw` - Raw disk image -- `.vmdk` - VMware disk - -## Container Management - -```bash -# List running containers -core ps - -# View logs -core logs - -# Follow logs -core logs -f - -# Execute command -core exec - -# Stop container -core stop -``` - -## Templates - -Built-in templates in `.core/linuxkit/`: - -| Template | Description | -|----------|-------------| -| `core-dev` | Development environment | -| `server-php` | FrankenPHP server | - -### Custom Templates - -Create `.core/linuxkit/mytemplate.yml`: - -```yaml -kernel: - image: linuxkit/kernel:6.6 - cmdline: "console=tty0" - -init: - - linuxkit/init:latest - - linuxkit/runc:latest - - linuxkit/containerd:latest - -services: - - name: myservice - image: myorg/myservice:latest - -files: - - path: /etc/myconfig - contents: | - key: value -``` - -Then run: - -```bash -core run --template mytemplate -``` - -## Networking - -LinuxKit VMs get their own network namespace. Port forwarding: - -```bash -# Forward port 8080 -core run server.iso -p 8080:80 - -# Multiple ports -core run server.iso -p 8080:80 -p 8443:443 -``` - -## Disk Persistence - -```bash -# Create persistent disk -core run server.iso --disk 10G - -# Attach existing disk -core run server.iso --disk /path/to/disk.qcow2 -``` diff --git a/docs/packages/go/cmd/sdk.md b/docs/packages/go/cmd/sdk.md deleted file mode 100644 index 03d23f7..0000000 --- a/docs/packages/go/cmd/sdk.md +++ /dev/null @@ -1,188 +0,0 @@ -# core sdk - -Generate typed API clients from OpenAPI specifications. - -## Usage - -```bash -core sdk [flags] -``` - -## Commands - -| Command | Description | -|---------|-------------| -| `generate` | Generate SDKs from OpenAPI spec | -| `validate` | Validate OpenAPI spec | -| `diff` | Check for breaking API changes | - -## sdk generate - -Generate typed API clients for multiple languages. - -```bash -core sdk generate [flags] -``` - -### Flags - -| Flag | Description | -|------|-------------| -| `--spec` | Path to OpenAPI spec file (auto-detected) | -| `--lang` | Generate only this language | - -### Examples - -```bash -# Generate all configured SDKs -core sdk generate - -# Generate only TypeScript SDK -core sdk generate --lang typescript - -# Use specific spec file -core sdk generate --spec api/openapi.yaml -``` - -### Supported Languages - -| Language | Generator | -|----------|-----------| -| TypeScript | openapi-generator (typescript-fetch) | -| Python | openapi-generator (python) | -| Go | openapi-generator (go) | -| PHP | openapi-generator (php) | - -## sdk validate - -Validate an OpenAPI specification file. - -```bash -core sdk validate [flags] -``` - -### Flags - -| Flag | Description | -|------|-------------| -| `--spec` | Path to OpenAPI spec file (auto-detected) | - -### Examples - -```bash -# Validate detected spec -core sdk validate - -# Validate specific file -core sdk validate --spec api/openapi.yaml -``` - -## sdk diff - -Check for breaking changes between API versions. - -```bash -core sdk diff [flags] -``` - -### Flags - -| Flag | Description | -|------|-------------| -| `--base` | Base spec version (git tag or file path) | -| `--spec` | Current spec file (auto-detected) | - -### Examples - -```bash -# Compare against previous release -core sdk diff --base v1.0.0 - -# Compare two files -core sdk diff --base old-api.yaml --spec new-api.yaml -``` - -### Breaking Changes Detected - -- Removed endpoints -- Changed parameter types -- Removed required fields -- Changed response types - -## Release Integration - -Generate SDKs as part of the release process: - -```bash -# Generate SDKs for release -core release --target sdk - -# With explicit version -core release --target sdk --version v1.2.3 - -# Preview what would be generated -core release --target sdk --dry-run -``` - -See [release command](release.md) for full details. - -## Configuration - -Configure SDK generation in `.core/release.yaml`: - -```yaml -sdk: - # OpenAPI spec path (auto-detected if not set) - spec: api/openapi.yaml - - # Languages to generate - languages: - - typescript - - python - - go - - php - - # Output directory - output: sdk - - # Package naming - package: - name: my-api-sdk - - # Breaking change detection - diff: - enabled: true - fail_on_breaking: false # Warn but continue -``` - -## Spec Auto-Detection - -Core looks for OpenAPI specs in this order: - -1. Path specified in config (`sdk.spec`) -2. `openapi.yaml` / `openapi.json` -3. `api/openapi.yaml` / `api/openapi.json` -4. `docs/openapi.yaml` / `docs/openapi.json` -5. Laravel Scramble endpoint (`/docs/api.json`) - -## Output Structure - -Generated SDKs are placed in language-specific directories: - -``` -sdk/ -├── typescript/ -│ ├── src/ -│ ├── package.json -│ └── tsconfig.json -├── python/ -│ ├── my_api_sdk/ -│ ├── setup.py -│ └── requirements.txt -├── go/ -│ ├── client.go -│ └── go.mod -└── php/ - ├── src/ - └── composer.json -``` diff --git a/docs/packages/go/cmd/sdk/example.md b/docs/packages/go/cmd/sdk/example.md new file mode 100644 index 0000000..2fada8c --- /dev/null +++ b/docs/packages/go/cmd/sdk/example.md @@ -0,0 +1,35 @@ +# SDK Examples + +## Validate + +```bash +core sdk validate +core sdk validate --spec ./api.yaml +``` + +## Diff + +```bash +# Compare with tag +core sdk diff --base v1.0.0 + +# Compare files +core sdk diff --base ./old-api.yaml --spec ./new-api.yaml +``` + +## Output + +``` +Breaking changes detected: + +- DELETE /users/{id}/profile + Endpoint removed + +- PATCH /users/{id} + Required field 'email' added + +Non-breaking changes: + ++ POST /users/{id}/avatar + New endpoint added +``` diff --git a/docs/packages/go/cmd/sdk/index.md b/docs/packages/go/cmd/sdk/index.md new file mode 100644 index 0000000..bd6828c --- /dev/null +++ b/docs/packages/go/cmd/sdk/index.md @@ -0,0 +1,106 @@ +# core sdk + +SDK validation and API compatibility tools. + +To generate SDKs, use: `core build sdk` + +## Usage + +```bash +core sdk [flags] +``` + +## Commands + +| Command | Description | +|---------|-------------| +| `diff` | Check for breaking API changes | +| `validate` | Validate OpenAPI spec | + +## sdk validate + +Validate an OpenAPI specification file. + +```bash +core sdk validate [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--spec` | Path to OpenAPI spec file (auto-detected) | + +### Examples + +```bash +# Validate detected spec +core sdk validate + +# Validate specific file +core sdk validate --spec api/openapi.yaml +``` + +## sdk diff + +Check for breaking changes between API versions. + +```bash +core sdk diff [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `--base` | Base spec version (git tag or file path) | +| `--spec` | Current spec file (auto-detected) | + +### Examples + +```bash +# Compare against previous release +core sdk diff --base v1.0.0 + +# Compare two files +core sdk diff --base old-api.yaml --spec new-api.yaml +``` + +### Breaking Changes Detected + +- Removed endpoints +- Changed parameter types +- Removed required fields +- Changed response types + +## SDK Generation + +SDK generation is handled by `core build sdk`, not this command. + +```bash +# Generate SDKs +core build sdk + +# Generate specific language +core build sdk --lang typescript + +# Preview without writing +core build sdk --dry-run +``` + +See [build sdk](../build/sdk/) for generation details. + +## Spec Auto-Detection + +Core looks for OpenAPI specs in this order: + +1. Path specified in config (`sdk.spec`) +2. `openapi.yaml` / `openapi.json` +3. `api/openapi.yaml` / `api/openapi.json` +4. `docs/openapi.yaml` / `docs/openapi.json` +5. Laravel Scramble endpoint (`/docs/api.json`) + +## See Also + +- [build sdk](../build/sdk/) - Generate SDKs from OpenAPI +- [ci command](../ci/) - Release workflow diff --git a/docs/packages/go/cmd/search.md b/docs/packages/go/cmd/search.md deleted file mode 100644 index 0e71734..0000000 --- a/docs/packages/go/cmd/search.md +++ /dev/null @@ -1,112 +0,0 @@ -# core search & install - -Search GitHub for repositories and install them locally. - -## core search - -Search GitHub for repositories matching a pattern. - -```bash -core search [flags] -``` - -### Flags - -| Flag | Description | -|------|-------------| -| `--org` | Search within a specific organization | -| `--limit` | Maximum results (default: 10) | -| `--language` | Filter by programming language | - -### Examples - -```bash -# Search by pattern -core search "cli tool" - -# Search within organization -core search --org host-uk - -# Search with language filter -core search --org host-uk --language go - -# Search all core-* repos -core search "core-" --org host-uk -``` - -### Output - -``` -Found 5 repositories: - - host-uk/core - Go CLI for the host-uk ecosystem - ★ 42 Go Updated 2 hours ago - - host-uk/core-php - PHP/Laravel packages for Core - ★ 18 PHP Updated 1 day ago - - host-uk/core-images - Docker and LinuxKit images - ★ 8 Dockerfile Updated 3 days ago -``` - -## core install - -Clone a repository from GitHub. - -```bash -core install [flags] -``` - -### Flags - -| Flag | Description | -|------|-------------| -| `--path` | Destination directory (default: current dir) | -| `--branch` | Clone specific branch | -| `--depth` | Shallow clone depth | - -### Examples - -```bash -# Install by full name -core install host-uk/core - -# Install to specific path -core install host-uk/core --path ~/Code/host-uk - -# Install specific branch -core install host-uk/core --branch dev - -# Shallow clone -core install host-uk/core --depth 1 -``` - -### Authentication - -Uses GitHub CLI (`gh`) authentication. Ensure you're logged in: - -```bash -gh auth status -gh auth login # if not authenticated -``` - -## Workflow Example - -```bash -# Find repositories -core search --org host-uk - -# Install one -core install host-uk/core-php --path ~/Code/host-uk - -# Check setup -core doctor -``` - -## See Also - -- [setup command](setup.md) - Clone all repos from registry -- [doctor command](doctor.md) - Check environment diff --git a/docs/packages/go/cmd/setup.md b/docs/packages/go/cmd/setup.md deleted file mode 100644 index 2bcb4d1..0000000 --- a/docs/packages/go/cmd/setup.md +++ /dev/null @@ -1,94 +0,0 @@ -# core setup - -Clone all repositories from the registry. - -## Usage - -```bash -core setup [flags] -``` - -## Flags - -| Flag | Description | -|------|-------------| -| `--registry` | Path to repos.yaml | -| `--path` | Base directory for cloning (default: current dir) | -| `--ssh` | Use SSH URLs instead of HTTPS | - -## Examples - -```bash -# Clone all repos from registry -core setup - -# Clone to specific directory -core setup --path ~/Code/host-uk - -# Use SSH for cloning -core setup --ssh -``` - -## Registry Format - -The registry file (`repos.yaml`) defines repositories: - -```yaml -repos: - - name: core - url: https://github.com/host-uk/core - description: Go CLI for the host-uk ecosystem - - - name: core-php - url: https://github.com/host-uk/core-php - description: PHP/Laravel packages - - - name: core-images - url: https://github.com/host-uk/core-images - description: Docker and LinuxKit images - - - name: core-api - url: https://github.com/host-uk/core-api - description: API service -``` - -## Output - -``` -Setting up host-uk workspace... - -Cloning repositories: - [1/4] core............... ✓ - [2/4] core-php........... ✓ - [3/4] core-images........ ✓ - [4/4] core-api........... ✓ - -Done! 4 repositories cloned to ~/Code/host-uk -``` - -## Finding Registry - -Core looks for `repos.yaml` in: - -1. Current directory -2. Parent directories (up to 5 levels) -3. `~/.core/repos.yaml` - -## After Setup - -```bash -# Check health of all repos -core health - -# Pull latest changes -core pull --all - -# Check CI status -core ci -``` - -## See Also - -- [work commands](work.md) - Multi-repo operations -- [search command](search.md) - Find repos on GitHub -- [install command](search.md) - Clone individual repos diff --git a/docs/packages/go/cmd/setup/example.md b/docs/packages/go/cmd/setup/example.md new file mode 100644 index 0000000..23f2410 --- /dev/null +++ b/docs/packages/go/cmd/setup/example.md @@ -0,0 +1,293 @@ +# Setup Examples + +## Clone from Registry + +```bash +# Clone all repos defined in repos.yaml +core setup + +# Preview what would be cloned +core setup --dry-run + +# Only foundation packages +core setup --only foundation + +# Multiple types +core setup --only foundation,module + +# Use specific registry file +core setup --registry ~/projects/repos.yaml +``` + +## Bootstrap New Workspace + +```bash +# In an empty directory - bootstraps in place +mkdir my-workspace && cd my-workspace +core setup + +# Shows interactive wizard to select packages: +# ┌─────────────────────────────────────────────┐ +# │ Select packages to clone │ +# │ Use space to select, enter to confirm │ +# │ │ +# │ ── Foundation (core framework) ── │ +# │ ☑ core-php Foundation framework │ +# │ ☑ core-tenant Multi-tenancy module │ +# │ │ +# │ ── Products (applications) ── │ +# │ ☐ core-bio Link-in-bio product │ +# │ ☐ core-social Social scheduling │ +# └─────────────────────────────────────────────┘ + +# Non-interactive: clone all packages +core setup --all + +# Create workspace in subdirectory +cd ~/Code +core setup --name my-project + +# CI mode: fully non-interactive +core setup --all --name ci-test +``` + +## Setup Single Repository + +```bash +# In a git repo without .core/ configuration +cd ~/Code/my-go-project +core setup + +# Shows choice dialog: +# ┌─────────────────────────────────────────────┐ +# │ Setup options │ +# │ You're in a git repository. What would you │ +# │ like to do? │ +# │ │ +# │ ● Setup this repo (create .core/ config) │ +# │ ○ Create a new workspace (clone repos) │ +# └─────────────────────────────────────────────┘ + +# Preview generated configuration +core setup --dry-run + +# Output: +# → Setting up repository configuration +# +# ✓ Detected project type: go +# → Also found: (none) +# +# → Would create: +# /Users/you/Code/my-go-project/.core/build.yaml +# +# Configuration preview: +# version: 1 +# project: +# name: my-go-project +# description: Go application +# main: ./cmd/my-go-project +# binary: my-go-project +# ... +``` + +## Configuration Files + +### repos.yaml (Workspace Registry) + +```yaml +org: host-uk +base_path: . +defaults: + ci: github + license: EUPL-1.2 + branch: main +repos: + core-php: + type: foundation + description: Foundation framework + core-tenant: + type: module + depends_on: [core-php] + description: Multi-tenancy module + core-admin: + type: module + depends_on: [core-php, core-tenant] + description: Admin panel + core-bio: + type: product + depends_on: [core-php, core-tenant] + description: Link-in-bio product + domain: bio.host.uk.com + core-devops: + type: foundation + clone: false # Already exists, skip cloning +``` + +### .core/build.yaml (Repository Config) + +Generated for Go projects: + +```yaml +version: 1 +project: + name: my-project + description: Go application + main: ./cmd/my-project + binary: my-project +build: + cgo: false + flags: + - -trimpath + ldflags: + - -s + - -w + env: [] +targets: + - os: linux + arch: amd64 + - os: linux + arch: arm64 + - os: darwin + arch: amd64 + - os: darwin + arch: arm64 + - os: windows + arch: amd64 +sign: + enabled: false +``` + +Generated for Wails projects: + +```yaml +version: 1 +project: + name: my-app + description: Wails desktop application + main: . + binary: my-app +targets: + - os: darwin + arch: amd64 + - os: darwin + arch: arm64 + - os: windows + arch: amd64 + - os: linux + arch: amd64 +``` + +### .core/release.yaml (Release Config) + +Generated for Go projects: + +```yaml +version: 1 +project: + name: my-project + repository: owner/my-project + +changelog: + include: + - feat + - fix + - perf + - refactor + exclude: + - chore + - docs + - style + - test + +publishers: + - type: github + draft: false + prerelease: false +``` + +### .core/test.yaml (Test Config) + +Generated for Go projects: + +```yaml +version: 1 + +commands: + - name: unit + run: go test ./... + - name: coverage + run: go test -coverprofile=coverage.out ./... + - name: race + run: go test -race ./... + +env: + CGO_ENABLED: "0" +``` + +Generated for PHP projects: + +```yaml +version: 1 + +commands: + - name: unit + run: vendor/bin/pest --parallel + - name: types + run: vendor/bin/phpstan analyse + - name: lint + run: vendor/bin/pint --test + +env: + APP_ENV: testing + DB_CONNECTION: sqlite +``` + +Generated for Node.js projects: + +```yaml +version: 1 + +commands: + - name: unit + run: npm test + - name: lint + run: npm run lint + - name: typecheck + run: npm run typecheck + +env: + NODE_ENV: test +``` + +## Workflow Examples + +### New Developer Setup + +```bash +# Clone the workspace +mkdir host-uk && cd host-uk +core setup + +# Select packages in wizard, then: +core health # Check all repos are healthy +core doctor # Verify environment +``` + +### CI Pipeline Setup + +```bash +# Non-interactive full clone +core setup --all --name workspace + +# Or with specific packages +core setup --only foundation,module --name workspace +``` + +### Adding Build Config to Existing Repo + +```bash +cd my-existing-project +core setup # Choose "Setup this repo" +# Edit .core/build.yaml as needed +core build # Build the project +``` diff --git a/docs/packages/go/cmd/setup/index.md b/docs/packages/go/cmd/setup/index.md new file mode 100644 index 0000000..d07121f --- /dev/null +++ b/docs/packages/go/cmd/setup/index.md @@ -0,0 +1,213 @@ +# core setup + +Clone repositories from registry or bootstrap a new workspace. + +## Overview + +The `setup` command operates in three modes: + +1. **Registry mode** - When `repos.yaml` exists nearby, clones repositories into packages/ +2. **Bootstrap mode** - When no registry exists, clones `core-devops` first, then presents an interactive wizard to select packages +3. **Repo setup mode** - When run in a git repo root, offers to create `.core/build.yaml` configuration + +## Usage + +```bash +core setup [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--registry` | Path to repos.yaml (auto-detected if not specified) | +| `--dry-run` | Show what would be cloned without cloning | +| `--only` | Only clone repos of these types (comma-separated: foundation,module,product) | +| `--all` | Skip wizard, clone all packages (non-interactive) | +| `--name` | Project directory name for bootstrap mode | +| `--build` | Run build after cloning | + +--- + +## Registry Mode + +When `repos.yaml` is found nearby (current directory or parents), setup clones all defined repositories: + +```bash +# In a directory with repos.yaml +core setup + +# Preview what would be cloned +core setup --dry-run + +# Only clone foundation packages +core setup --only foundation + +# Multiple types +core setup --only foundation,module +``` + +In registry mode with a TTY, an interactive wizard allows you to select which packages to clone. Use `--all` to skip the wizard and clone everything. + +--- + +## Bootstrap Mode + +When no `repos.yaml` exists, setup enters bootstrap mode: + +```bash +# In an empty directory - bootstraps workspace in place +mkdir my-project && cd my-project +core setup + +# In a non-empty directory - creates subdirectory +cd ~/Code +core setup --name my-workspace + +# Non-interactive: clone all packages +core setup --all --name ci-test +``` + +Bootstrap mode: +1. Detects if current directory is empty +2. If not empty, prompts for project name (or uses `--name`) +3. Clones `core-devops` (contains `repos.yaml`) +4. Loads the registry from core-devops +5. Shows interactive package selection wizard (unless `--all`) +6. Clones selected packages +7. Optionally runs build (with `--build`) + +--- + +## Repo Setup Mode + +When run in a git repository root (without `repos.yaml`), setup offers two choices: + +1. **Setup Working Directory** - Creates `.core/build.yaml` based on detected project type +2. **Create Package** - Creates a subdirectory and clones packages there + +```bash +cd ~/Code/my-go-project +core setup + +# Output: +# >> This directory is a git repository +# > Setup Working Directory +# Create Package (clone repos into subdirectory) +``` + +Choosing "Setup Working Directory" detects the project type and generates configuration: + +| Detected File | Project Type | +|---------------|--------------| +| `wails.json` | Wails | +| `go.mod` | Go | +| `composer.json` | PHP | +| `package.json` | Node.js | + +Creates three config files in `.core/`: + +| File | Purpose | +|------|---------| +| `build.yaml` | Build targets, flags, output settings | +| `release.yaml` | Changelog format, GitHub release config | +| `test.yaml` | Test commands, environment variables | + +Also auto-detects GitHub repo from git remote for release config. + +See [Configuration Files](example.md#configuration-files) for generated config examples. + +--- + +## Interactive Wizard + +When running in a terminal (TTY), the setup command presents an interactive multi-select wizard: + +- Packages are grouped by type (foundation, module, product, template) +- Use arrow keys to navigate +- Press space to select/deselect packages +- Type to filter the list +- Press enter to confirm selection + +The wizard is skipped when: +- `--all` flag is specified +- Not running in a TTY (e.g., CI pipelines) +- `--dry-run` is specified + +--- + +## Examples + +### Clone from Registry + +```bash +# Clone all repos (interactive wizard) +core setup + +# Clone all repos (non-interactive) +core setup --all + +# Preview without cloning +core setup --dry-run + +# Only foundation packages +core setup --only foundation +``` + +### Bootstrap New Workspace + +```bash +# Interactive bootstrap in empty directory +mkdir workspace && cd workspace +core setup + +# Non-interactive with all packages +core setup --all --name my-project + +# Bootstrap and run build +core setup --all --name my-project --build +``` + +--- + +## Registry Format + +The registry file (`repos.yaml`) defines repositories. See [Configuration Files](example.md#configuration-files) for format. + +--- + +## Finding Registry + +Core looks for `repos.yaml` in: + +1. Current directory +2. Parent directories (walking up to root) +3. `~/Code/host-uk/repos.yaml` +4. `~/.config/core/repos.yaml` + +--- + +## After Setup + +```bash +# Check workspace health +core dev health + +# Full workflow (status + commit + push) +core dev work + +# Build the project +core build + +# Run tests +core go test # Go projects +core php test # PHP projects +``` + +--- + +## See Also + +- [dev work](../dev/work/) - Multi-repo operations +- [build](../build/) - Build projects +- [doctor](../doctor/) - Check environment diff --git a/docs/packages/go/cmd/skill.md b/docs/packages/go/cmd/skill.md deleted file mode 100644 index 5d0ff79..0000000 --- a/docs/packages/go/cmd/skill.md +++ /dev/null @@ -1,125 +0,0 @@ -# Claude Code Skill - -The `core` CLI includes a Claude Code skill that helps Claude use the correct commands when working in host-uk repositories. - -## What It Does - -The skill provides Claude with: -- Command quick reference for all `core` commands -- Decision tree for choosing the right command -- Common mistakes to avoid -- Best practices for testing, building, and releasing - -## Installation - -### Automatic (Project-Based) - -When working in any host-uk repository that includes `.claude/skills/core/`, Claude automatically discovers and uses the skill. - -### Global Install - -Install the skill globally so it works in any project: - -```bash -# If you have the repo cloned -cd /path/to/core -./.claude/skills/core/install.sh - -# Or via curl -curl -fsSL https://raw.githubusercontent.com/host-uk/core/main/.claude/skills/core/install.sh | bash -``` - -This copies the skill to `~/.claude/skills/core/`. - -## Usage - -### Automatic Invocation - -Claude automatically uses the skill when: -- Running tests in a Go project -- Building or releasing -- Working across multiple repos -- Checking CI status or issues - -### Manual Invocation - -Type `/core` in Claude Code to see the full command reference. - -## What Claude Learns - -### Testing - -``` -Wrong: go test ./... -Right: core test - -Why: core test sets MACOSX_DEPLOYMENT_TARGET, filters linker warnings, - and provides colour-coded coverage output. -``` - -### Building - -``` -Wrong: go build -Right: core build - -Why: core build handles cross-compilation, code signing, archiving, - and checksums automatically. -``` - -### Multi-Repo Workflows - -``` -Wrong: cd into each repo, run git status -Right: core health - -Why: Aggregated view across all repos in one command. -``` - -## Command Reference - -The skill includes documentation for: - -| Category | Commands | -|----------|----------| -| Testing | `core test`, `core test --coverage`, `core test --json` | -| Building | `core build`, `core build --targets`, `core build --ci` | -| Releasing | `core release`, `core sdk` | -| Multi-Repo | `core health`, `core work`, `core commit`, `core push`, `core pull` | -| GitHub | `core issues`, `core reviews`, `core ci` | -| Environment | `core doctor`, `core setup`, `core search`, `core install` | -| PHP | `core php dev`, `core php artisan` | -| Containers | `core run`, `core ps`, `core stop`, `core logs`, `core exec` | -| Docs | `core docs list`, `core docs sync` | - -## Customisation - -The skill is a markdown file at `.claude/skills/core/SKILL.md`. You can: - -1. **Fork and modify** - Copy to your own repo's `.claude/skills/` and customise -2. **Extend** - Add project-specific commands or workflows -3. **Override** - Project skills take precedence over global skills - -## Troubleshooting - -### Skill Not Loading - -Check if the skill exists: -```bash -ls ~/.claude/skills/core/SKILL.md -# or -ls .claude/skills/core/SKILL.md -``` - -### Reinstall - -```bash -rm -rf ~/.claude/skills/core -curl -fsSL https://raw.githubusercontent.com/host-uk/core/main/.claude/skills/core/install.sh | bash -``` - -## See Also - -- [test command](test.md) - Run tests with coverage -- [build command](build.md) - Build projects -- [work command](work.md) - Multi-repo operations diff --git a/docs/packages/go/cmd/test.md b/docs/packages/go/cmd/test.md deleted file mode 100644 index 3091c21..0000000 --- a/docs/packages/go/cmd/test.md +++ /dev/null @@ -1,134 +0,0 @@ -# core test - -Run Go tests with coverage reporting and clean output. - -## Usage - -```bash -core test [flags] -``` - -## Flags - -| Flag | Description | -|------|-------------| -| `--verbose` | Stream test output as it runs | -| `--coverage` | Show detailed per-package coverage breakdown | -| `--pkg ` | Package pattern to test (default: `./...`) | -| `--run ` | Run only tests matching this regex | -| `--short` | Skip long-running tests | -| `--race` | Enable race detector | -| `--json` | Output JSON for CI/agents | - -## Examples - -```bash -# Run all tests with coverage summary -core test - -# Show test output as it runs -core test --verbose - -# Show detailed coverage by package -core test --coverage - -# Test specific packages -core test --pkg ./pkg/crypt -core test --pkg ./pkg/... - -# Run specific tests by name -core test --run TestHash -core test --run "Test.*Good" - -# Skip integration tests -core test --short - -# Check for race conditions -core test --race - -# CI/agent mode with JSON output -core test --json -``` - -## Output - -### Default Output - -``` -Test: Running tests - Package: ./... - - ✓ 14 passed - - Coverage: 75.1% - -PASS All tests passed -``` - -### With `--coverage` Flag - -``` -Test: Running tests - Package: ./... - - ✓ 14 passed - - Coverage by package: - pkg/crypt 91.2% - pkg/crypt/lthn 100.0% - pkg/io 96.0% - pkg/plugin 93.3% - pkg/runtime 83.3% - pkg/workspace 73.9% - pkg/container 65.6% - pkg/release 40.8% - pkg/php 26.0% - pkg/release/publishers 13.3% - - Average 75.1% - -PASS All tests passed -``` - -### JSON Output (`--json`) - -```json -{ - "passed": 14, - "failed": 0, - "skipped": 0, - "coverage": 75.1, - "exit_code": 0, - "failed_packages": [] -} -``` - -## Features - -### macOS Linker Warning Suppression - -Sets `MACOSX_DEPLOYMENT_TARGET=26.0` automatically to suppress CGO linker warnings on macOS. The warnings are also filtered from output for clean DX. - -### Coverage Colour Coding - -Coverage percentages are colour-coded: -- **Green**: 80%+ coverage -- **Amber**: 50-79% coverage -- **Red**: Below 50% coverage - -### Package Name Shortening - -Package names are shortened for readability: -- `github.com/host-uk/core/pkg/crypt` → `pkg/crypt` - -## Exit Codes - -| Code | Meaning | -|------|---------| -| 0 | All tests passed | -| 1 | One or more tests failed | - -## See Also - -- [build command](build.md) - Build Go projects -- [doctor command](doctor.md) - Check development environment diff --git a/docs/packages/go/cmd/test/example.md b/docs/packages/go/cmd/test/example.md new file mode 100644 index 0000000..9e2a4a7 --- /dev/null +++ b/docs/packages/go/cmd/test/example.md @@ -0,0 +1,8 @@ +# Test Examples + +**Note:** Prefer `core go test` or `core php test` instead. + +```bash +core test +core test --coverage +``` diff --git a/docs/packages/go/cmd/test/index.md b/docs/packages/go/cmd/test/index.md new file mode 100644 index 0000000..920baea --- /dev/null +++ b/docs/packages/go/cmd/test/index.md @@ -0,0 +1,74 @@ +# core test + +Run Go tests with coverage reporting. + +Sets `MACOSX_DEPLOYMENT_TARGET=26.0` to suppress linker warnings on macOS. + +## Usage + +```bash +core test [flags] +``` + +## Flags + +| Flag | Description | +|------|-------------| +| `--coverage` | Show detailed per-package coverage | +| `--json` | Output JSON for CI/agents | +| `--pkg` | Package pattern to test (default: ./...) | +| `--race` | Enable race detector | +| `--run` | Run only tests matching this regex | +| `--short` | Skip long-running tests | +| `--verbose` | Show test output as it runs | + +## Examples + +```bash +# Run all tests with coverage summary +core test + +# Show test output as it runs +core test --verbose + +# Detailed per-package coverage +core test --coverage + +# Test specific packages +core test --pkg ./pkg/... + +# Run specific test by name +core test --run TestName + +# Run tests matching pattern +core test --run "Test.*Good" + +# Skip long-running tests +core test --short + +# Enable race detector +core test --race + +# Output JSON for CI/agents +core test --json +``` + +## JSON Output + +With `--json`, outputs structured results: + +```json +{ + "passed": 14, + "failed": 0, + "skipped": 0, + "coverage": 75.1, + "exit_code": 0, + "failed_packages": [] +} +``` + +## See Also + +- [go test](../go/test/) - Go-specific test options +- [go cov](../go/cov/) - Coverage reports diff --git a/docs/packages/go/cmd/vm/example.md b/docs/packages/go/cmd/vm/example.md new file mode 100644 index 0000000..f31f97e --- /dev/null +++ b/docs/packages/go/cmd/vm/example.md @@ -0,0 +1,52 @@ +# VM Examples + +## Running VMs + +```bash +# Run image +core vm run server.iso + +# Detached with resources +core vm run -d --memory 4096 --cpus 4 server.iso + +# From template +core vm run --template core-dev --var SSH_KEY="ssh-rsa AAAA..." +``` + +## Management + +```bash +# List running +core vm ps + +# Include stopped +core vm ps -a + +# Stop +core vm stop abc123 + +# View logs +core vm logs abc123 + +# Follow logs +core vm logs -f abc123 + +# Execute command +core vm exec abc123 ls -la + +# Shell +core vm exec abc123 /bin/sh +``` + +## Templates + +```bash +# List +core vm templates + +# Show content +core vm templates show core-dev + +# Show variables +core vm templates vars core-dev +``` diff --git a/docs/packages/go/cmd/vm/index.md b/docs/packages/go/cmd/vm/index.md new file mode 100644 index 0000000..ec0be0f --- /dev/null +++ b/docs/packages/go/cmd/vm/index.md @@ -0,0 +1,163 @@ +# core vm + +LinuxKit VM management. + +LinuxKit VMs are lightweight, immutable VMs built from YAML templates. +They run using qemu or hyperkit depending on your system. + +## Usage + +```bash +core vm [flags] +``` + +## Commands + +| Command | Description | +|---------|-------------| +| [`run`](#vm-run) | Run a LinuxKit image or template | +| [`ps`](#vm-ps) | List running VMs | +| [`stop`](#vm-stop) | Stop a VM | +| [`logs`](#vm-logs) | View VM logs | +| [`exec`](#vm-exec) | Execute command in VM | +| [templates](templates/) | Manage LinuxKit templates | + +--- + +## vm run + +Run a LinuxKit image or build from a template. + +```bash +core vm run [flags] +core vm run --template [flags] +``` + +Supported image formats: `.iso`, `.qcow2`, `.vmdk`, `.raw` + +### Flags + +| Flag | Description | +|------|-------------| +| `--template` | Run from a LinuxKit template (build + run) | +| `--var` | Template variable in KEY=VALUE format (repeatable) | +| `--name` | Name for the container | +| `--memory` | Memory in MB (default: 1024) | +| `--cpus` | CPU count (default: 1) | +| `--ssh-port` | SSH port for exec commands (default: 2222) | +| `-d` | Run in detached mode (background) | + +### Examples + +```bash +# Run from image file +core vm run image.iso + +# Run detached with more resources +core vm run -d image.qcow2 --memory 2048 --cpus 4 + +# Run from template +core vm run --template core-dev --var SSH_KEY="ssh-rsa AAAA..." + +# Multiple template variables +core vm run --template server-php --var SSH_KEY="..." --var DOMAIN=example.com +``` + +--- + +## vm ps + +List running VMs. + +```bash +core vm ps [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `-a` | Show all (including stopped) | + +### Output + +``` +ID NAME IMAGE STATUS STARTED PID +abc12345 myvm ...core-dev.qcow2 running 5m 12345 +``` + +--- + +## vm stop + +Stop a running VM by ID or name. + +```bash +core vm stop +``` + +Supports partial ID matching. + +### Examples + +```bash +# Full ID +core vm stop abc12345678 + +# Partial ID +core vm stop abc1 +``` + +--- + +## vm logs + +View VM logs. + +```bash +core vm logs [flags] +``` + +### Flags + +| Flag | Description | +|------|-------------| +| `-f` | Follow log output | + +### Examples + +```bash +# View logs +core vm logs abc12345 + +# Follow logs +core vm logs -f abc1 +``` + +--- + +## vm exec + +Execute a command in a running VM via SSH. + +```bash +core vm exec +``` + +### Examples + +```bash +# List files +core vm exec abc12345 ls -la + +# Open shell +core vm exec abc1 /bin/sh +``` + +--- + +## See Also + +- [templates](templates/) - Manage LinuxKit templates +- [build](../build/) - Build LinuxKit images +- [dev](../dev/) - Dev environment management diff --git a/docs/packages/go/cmd/vm/templates/example.md b/docs/packages/go/cmd/vm/templates/example.md new file mode 100644 index 0000000..c1f8b35 --- /dev/null +++ b/docs/packages/go/cmd/vm/templates/example.md @@ -0,0 +1,53 @@ +# VM Templates Examples + +## List + +```bash +core vm templates +``` + +## Show + +```bash +core vm templates show core-dev +``` + +## Variables + +```bash +core vm templates vars core-dev +``` + +## Output + +``` +Variables for core-dev: + SSH_KEY (required) SSH public key + MEMORY (optional) Memory in MB (default: 4096) + CPUS (optional) CPU count (default: 4) +``` + +## Using Templates + +```bash +core vm run --template core-dev --var SSH_KEY="ssh-rsa AAAA..." +``` + +## Template Format + +`.core/linuxkit/myserver.yml`: + +```yaml +kernel: + image: linuxkit/kernel:5.15 + cmdline: "console=tty0" + +init: + - linuxkit/init:v1.0.0 + +services: + - name: sshd + image: linuxkit/sshd:v1.0.0 + - name: myapp + image: ghcr.io/myorg/myapp:latest +``` diff --git a/docs/packages/go/cmd/templates.md b/docs/packages/go/cmd/vm/templates/index.md similarity index 60% rename from docs/packages/go/cmd/templates.md rename to docs/packages/go/cmd/vm/templates/index.md index 4573020..7ca3700 100644 --- a/docs/packages/go/cmd/templates.md +++ b/docs/packages/go/cmd/vm/templates/index.md @@ -1,11 +1,11 @@ -# core templates +# core vm templates Manage LinuxKit templates for container images. ## Usage ```bash -core templates [command] +core vm templates [command] ``` ## Commands @@ -14,13 +14,14 @@ core templates [command] |---------|-------------| | `list` | List available templates | | `show` | Show template details | +| `vars` | Show template variables | ## templates list List all available LinuxKit templates. ```bash -core templates list +core vm templates list ``` ### Output @@ -46,13 +47,13 @@ Available Templates: Show details of a specific template. ```bash -core templates show +core vm templates show ``` ### Example ```bash -core templates show core-dev +core vm templates show core-dev ``` Output: @@ -77,6 +78,28 @@ Services: Size: ~1.8GB ``` +## templates vars + +Show variables defined by a template. + +```bash +core vm templates vars +``` + +### Example + +```bash +core vm templates vars core-dev +``` + +Output: +``` +Variables for core-dev: + SSH_KEY (required) SSH public key + MEMORY (optional) Memory in MB (default: 4096) + CPUS (optional) CPU count (default: 4) +``` + ## Template Locations Templates are searched in order: @@ -87,31 +110,15 @@ Templates are searched in order: ## Creating Templates -Create a LinuxKit YAML in `.core/linuxkit/`: - -```yaml -# .core/linuxkit/myserver.yml -kernel: - image: linuxkit/kernel:5.15 - cmdline: "console=tty0" - -init: - - linuxkit/init:v1.0.0 - -services: - - name: sshd - image: linuxkit/sshd:v1.0.0 - - name: myapp - image: ghcr.io/myorg/myapp:latest -``` +Create a LinuxKit YAML in `.core/linuxkit/`. See [Template Format](example.md#template-format) for examples. Run with: ```bash -core run --template myserver +core vm run --template myserver ``` ## See Also -- [run command](run.md) - Run LinuxKit images -- [build command](build.md) - Build LinuxKit images +- [vm command](../) - Run LinuxKit images +- [build command](../../build/) - Build LinuxKit images diff --git a/docs/packages/go/cmd/work.md b/docs/packages/go/cmd/work.md deleted file mode 100644 index 8337e43..0000000 --- a/docs/packages/go/cmd/work.md +++ /dev/null @@ -1,160 +0,0 @@ -# core work - -Multi-repo git operations for managing the host-uk organization. - -## Overview - -The `work` command and related commands (`health`, `issues`, `reviews`, `commit`, `push`, `pull`, `impact`, `ci`) help manage multiple repositories in the host-uk ecosystem simultaneously. - -## Commands - -| Command | Description | -|---------|-------------| -| `core work` | Multi-repo git operations | -| `core health` | Quick health check across all repos | -| `core issues` | List open issues across all repos | -| `core reviews` | List PRs needing review | -| `core commit` | Claude-assisted commits across repos | -| `core push` | Push commits across all repos | -| `core pull` | Pull updates across all repos | -| `core impact` | Show impact of changing a repo | -| `core ci` | Check CI status across all repos | - -## core health - -Quick health check showing status of all repos. - -```bash -core health -``` - -Output shows: -- Git status (clean/dirty) -- Current branch -- Commits ahead/behind remote -- CI status - -## core issues - -List open issues across all repositories. - -```bash -core issues [flags] -``` - -### Flags - -| Flag | Description | -|------|-------------| -| `--assignee` | Filter by assignee | -| `--label` | Filter by label | -| `--limit` | Max issues per repo | - -## core reviews - -List pull requests needing review. - -```bash -core reviews [flags] -``` - -Shows PRs where: -- You are a requested reviewer -- PR is open and not draft -- CI is passing - -## core commit - -Create commits across repos with Claude assistance. - -```bash -core commit [flags] -``` - -### Flags - -| Flag | Description | -|------|-------------| -| `--message` | Commit message (auto-generated if not provided) | -| `--all` | Commit in all dirty repos | - -Claude analyzes changes and suggests conventional commit messages. - -## core push - -Push commits across all repos. - -```bash -core push [flags] -``` - -### Flags - -| Flag | Description | -|------|-------------| -| `--all` | Push all repos with unpushed commits | -| `--force` | Force push (use with caution) | - -## core pull - -Pull updates across all repos. - -```bash -core pull [flags] -``` - -### Flags - -| Flag | Description | -|------|-------------| -| `--all` | Pull all repos | -| `--rebase` | Rebase instead of merge | - -## core impact - -Show the impact of changing a repository. - -```bash -core impact -``` - -Shows: -- Dependent repos -- Reverse dependencies -- Potential breaking changes - -## core ci - -Check CI status across all repos. - -```bash -core ci [flags] -``` - -### Flags - -| Flag | Description | -|------|-------------| -| `--watch` | Watch for status changes | -| `--failing` | Show only failing repos | - -## Registry - -These commands use `repos.yaml` to know which repos to manage: - -```yaml -repos: - - name: core - path: ./core - url: https://github.com/host-uk/core - - name: core-php - path: ./core-php - url: https://github.com/host-uk/core-php -``` - -Use `core setup` to clone all repos from the registry. - -## See Also - -- [setup command](setup.md) - Clone repos from registry -- [search command](search.md) - Find and install repos diff --git a/docs/packages/go/configuration.md b/docs/packages/go/configuration.md index 84e7901..deabb68 100644 --- a/docs/packages/go/configuration.md +++ b/docs/packages/go/configuration.md @@ -205,14 +205,148 @@ files: port: 8080 ``` +## repos.yaml + +Package registry for multi-repo workspaces: + +```yaml +# Organisation name (used for GitHub URLs) +org: host-uk + +# Base path for cloning (default: current directory) +base_path: . + +# Default settings for all repos +defaults: + ci: github + license: EUPL-1.2 + branch: main + +# Repository definitions +repos: + # Foundation packages (no dependencies) + core-php: + type: foundation + description: Foundation framework + + core-devops: + type: foundation + description: Development environment + clone: false # Skip during setup (already exists) + + # Module packages (depend on foundation) + core-tenant: + type: module + depends_on: [core-php] + description: Multi-tenancy module + + core-admin: + type: module + depends_on: [core-php, core-tenant] + description: Admin panel + + core-api: + type: module + depends_on: [core-php] + description: REST API framework + + # Product packages (user-facing applications) + core-bio: + type: product + depends_on: [core-php, core-tenant] + description: Link-in-bio product + domain: bio.host.uk.com + + core-social: + type: product + depends_on: [core-php, core-tenant] + description: Social scheduling + domain: social.host.uk.com + + # Templates + core-template: + type: template + description: Starter template for new projects +``` + +### repos.yaml Fields + +| Field | Required | Description | +|-------|----------|-------------| +| `org` | Yes | GitHub organisation name | +| `base_path` | No | Directory for cloning (default: `.`) | +| `defaults` | No | Default settings applied to all repos | +| `repos` | Yes | Map of repository definitions | + +### Repository Fields + +| Field | Required | Description | +|-------|----------|-------------| +| `type` | Yes | `foundation`, `module`, `product`, or `template` | +| `description` | No | Human-readable description | +| `depends_on` | No | List of package dependencies | +| `clone` | No | Set `false` to skip during setup | +| `domain` | No | Production domain (for products) | +| `branch` | No | Override default branch | + +### Package Types + +| Type | Description | Dependencies | +|------|-------------|--------------| +| `foundation` | Core framework packages | None | +| `module` | Reusable modules | Foundation packages | +| `product` | User-facing applications | Foundation + modules | +| `template` | Starter templates | Any | + +--- + ## Environment Variables +Complete reference of environment variables used by Core CLI. + +### Authentication + +| Variable | Used By | Description | +|----------|---------|-------------| +| `GITHUB_TOKEN` | `core ci`, `core dev` | GitHub API authentication | +| `ANTHROPIC_API_KEY` | `core ai`, `core dev claude` | Claude API key | +| `AGENTIC_TOKEN` | `core ai task*` | Agentic API authentication | +| `AGENTIC_BASE_URL` | `core ai task*` | Agentic API endpoint | + +### Publishing + +| Variable | Used By | Description | +|----------|---------|-------------| +| `NPM_TOKEN` | `core ci` (npm publisher) | npm registry auth token | +| `CHOCOLATEY_API_KEY` | `core ci` (chocolatey publisher) | Chocolatey API key | +| `DOCKER_USERNAME` | `core ci` (docker publisher) | Docker registry username | +| `DOCKER_PASSWORD` | `core ci` (docker publisher) | Docker registry password | + +### Deployment + +| Variable | Used By | Description | +|----------|---------|-------------| +| `COOLIFY_URL` | `core php deploy` | Coolify server URL | +| `COOLIFY_TOKEN` | `core php deploy` | Coolify API token | +| `COOLIFY_APP_ID` | `core php deploy` | Production application ID | +| `COOLIFY_STAGING_APP_ID` | `core php deploy --staging` | Staging application ID | + +### Build + +| Variable | Used By | Description | +|----------|---------|-------------| +| `CGO_ENABLED` | `core build`, `core go *` | Enable/disable CGO (default: 0) | +| `GOOS` | `core build` | Target operating system | +| `GOARCH` | `core build` | Target architecture | + +### Configuration Paths + | Variable | Description | |----------|-------------| -| `GITHUB_TOKEN` | GitHub authentication (via gh CLI) | -| `NPM_TOKEN` | npm publish token | -| `CHOCOLATEY_API_KEY` | Chocolatey publish key | -| `COOLIFY_TOKEN` | Coolify deployment token | +| `CORE_CONFIG` | Override config directory (default: `~/.core/`) | +| `CORE_REGISTRY` | Override repos.yaml path | + +--- ## Defaults diff --git a/docs/packages/go/framework/architecture.md b/docs/packages/go/framework/architecture.md deleted file mode 100644 index a0bd1d0..0000000 --- a/docs/packages/go/framework/architecture.md +++ /dev/null @@ -1,134 +0,0 @@ -# Architecture - -Core follows a modular, service-based architecture designed for maintainability and testability. - -## Overview - -``` -┌─────────────────────────────────────────────────────────┐ -│ Wails Application │ -├─────────────────────────────────────────────────────────┤ -│ Core │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ Display │ │ WebView │ │ MCP │ │ Config │ │ -│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ Crypt │ │ I18n │ │ IO │ │Workspace │ │ -│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ -├─────────────────────────────────────────────────────────┤ -│ Plugin System │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ Plugin A │ │ Plugin B │ │ Plugin C │ │ -│ └──────────┘ └──────────┘ └──────────┘ │ -└─────────────────────────────────────────────────────────┘ -``` - -## Core Container - -The `Core` struct is the central service container: - -```go -type Core struct { - services map[string]any // Service registry - actions []ActionHandler // IPC handlers - Features *Features // Feature flags - servicesLocked bool // Prevent late registration -} -``` - -### Service Registration - -Services are registered using factory functions: - -```go -core.New( - core.WithService(display.NewService), // Auto-discovered name - core.WithName("custom", myFactory), // Explicit name -) -``` - -### Service Retrieval - -Type-safe service retrieval: - -```go -// Returns error if not found -svc, err := core.ServiceFor[*display.Service](c, "display") - -// Panics if not found (use in init code) -svc := core.MustServiceFor[*display.Service](c, "display") -``` - -## Service Lifecycle - -Services can implement lifecycle interfaces: - -```go -// Called when app starts -type Startable interface { - OnStartup(ctx context.Context) error -} - -// Called when app shuts down -type Stoppable interface { - OnShutdown(ctx context.Context) error -} -``` - -## IPC / Actions - -Services communicate via the action system: - -```go -// Register a handler -c.RegisterAction(func(c *core.Core, msg core.Message) error { - if msg.Type == "my-action" { - // Handle message - } - return nil -}) - -// Send a message -c.ACTION(core.Message{ - Type: "my-action", - Data: map[string]any{"key": "value"}, -}) -``` - -## Frontend Bindings - -Wails generates TypeScript bindings automatically: - -```typescript -// Auto-generated from Go service -import { ShowNotification } from '@bindings/display/service'; - -await ShowNotification({ - title: "Hello", - message: "From TypeScript!" -}); -``` - -## Package Structure - -``` -pkg/ -├── core/ # Core container and interfaces -├── display/ # Window, tray, dialogs, clipboard -├── webview/ # JS execution, DOM, screenshots -├── mcp/ # Model Context Protocol server -├── config/ # Configuration persistence -├── crypt/ # Encryption and signing -├── i18n/ # Internationalization -├── io/ # File system helpers -├── workspace/ # Project management -├── plugin/ # Plugin system -└── module/ # Module system -``` - -## Design Principles - -1. **Dependency Injection**: Services receive dependencies via constructor -2. **Interface Segregation**: Small, focused interfaces -3. **Testability**: All services are mockable -4. **No Globals**: State contained in Core instance diff --git a/docs/packages/go/framework/config.md b/docs/packages/go/framework/config.md deleted file mode 100644 index c682e85..0000000 --- a/docs/packages/go/framework/config.md +++ /dev/null @@ -1,122 +0,0 @@ -# Config Service - -The Config service (`pkg/config`) provides unified configuration management with automatic persistence, feature flags, and XDG-compliant directory paths. - -## Features - -- JSON configuration with auto-save -- Feature flag management -- XDG Base Directory support -- Struct serialization helpers -- Type-safe get/set operations - -## Basic Usage - -```go -import "github.com/Snider/Core/pkg/config" - -// Standalone usage -cfg, err := config.New() -if err != nil { - log.Fatal(err) -} - -// With Core framework -c, _ := core.New( - core.WithService(config.Register), -) -cfg := core.MustServiceFor[*config.Service](c, "config") -``` - -## Get & Set Values - -```go -// Set a value (auto-saves) -err := cfg.Set("language", "fr") - -// Get a value -var lang string -err := cfg.Get("language", &lang) -``` - -Available configuration keys: - -| Key | Type | Description | -|-----|------|-------------| -| `language` | string | UI language code | -| `default_route` | string | Default navigation route | -| `configDir` | string | Config files directory | -| `dataDir` | string | Data files directory | -| `cacheDir` | string | Cache directory | -| `workspaceDir` | string | Workspaces directory | - -## Feature Flags - -```go -// Enable a feature -cfg.EnableFeature("dark_mode") - -// Check if enabled -if cfg.IsFeatureEnabled("dark_mode") { - // Apply dark theme -} - -// Disable a feature -cfg.DisableFeature("dark_mode") -``` - -## Struct Serialization - -Store complex data structures in separate JSON files: - -```go -type UserPrefs struct { - Theme string `json:"theme"` - Notifications bool `json:"notifications"` -} - -// Save struct to config/user_prefs.json -prefs := UserPrefs{Theme: "dark", Notifications: true} -err := cfg.SaveStruct("user_prefs", prefs) - -// Load struct from file -var loaded UserPrefs -err := cfg.LoadStruct("user_prefs", &loaded) -``` - -## Directory Paths - -The service automatically creates XDG-compliant directories: - -```go -// Access directory paths -fmt.Println(cfg.ConfigDir) // ~/.config/lethean or ~/lethean/config -fmt.Println(cfg.DataDir) // Data storage -fmt.Println(cfg.CacheDir) // Cache files -fmt.Println(cfg.WorkspaceDir) // User workspaces -``` - -## Manual Save - -Changes are auto-saved, but you can save explicitly: - -```go -err := cfg.Save() -``` - -## Frontend Usage (TypeScript) - -```typescript -import { Get, Set, IsFeatureEnabled } from '@bindings/config/service'; - -// Get configuration -const lang = await Get("language"); - -// Set configuration -await Set("default_route", "/dashboard"); - -// Check feature flag -if (await IsFeatureEnabled("dark_mode")) { - applyDarkTheme(); -} -``` diff --git a/docs/packages/go/framework/core.md b/docs/packages/go/framework/core.md deleted file mode 100644 index 3599fd7..0000000 --- a/docs/packages/go/framework/core.md +++ /dev/null @@ -1,312 +0,0 @@ -# Core API Reference - -Complete API reference for the Core framework (`pkg/core`). - -## Core Struct - -The central application container. - -### Creation - -```go -func New(opts ...Option) (*Core, error) -``` - -Creates a new Core instance with the specified options. - -### Methods - -#### Service Access - -```go -func ServiceFor[T any](c *Core, name string) (T, error) -``` - -Retrieves a service by name with type safety. - -```go -func MustServiceFor[T any](c *Core, name string) T -``` - -Retrieves a service by name, panics if not found or wrong type. - -#### Actions - -```go -func (c *Core) ACTION(msg Message) error -``` - -Broadcasts a message to all registered action handlers. - -```go -func (c *Core) RegisterAction(handler func(*Core, Message) error) -``` - -Registers an action handler. - -#### Service Registration - -```go -func (c *Core) AddService(name string, svc any) error -``` - -Manually adds a service to the registry. - -#### Config Access - -```go -func (c *Core) Config() *config.Service -``` - -Returns the config service if registered. - -## Options - -### WithService - -```go -func WithService(factory ServiceFactory) Option -``` - -Registers a service using its factory function. - -```go -c, _ := core.New( - core.WithService(config.Register), - core.WithService(display.NewService), -) -``` - -### WithName - -```go -func WithName(name string, factory ServiceFactory) Option -``` - -Registers a service with an explicit name. - -```go -c, _ := core.New( - core.WithName("mydb", database.NewService), -) -``` - -### WithAssets - -```go -func WithAssets(assets embed.FS) Option -``` - -Sets embedded assets for the application. - -### WithServiceLock - -```go -func WithServiceLock() Option -``` - -Prevents late service registration after initialization. - -## ServiceFactory - -```go -type ServiceFactory func(c *Core) (any, error) -``` - -Factory function signature for service creation. - -## Message - -```go -type Message interface{} -``` - -Messages can be any type. Common patterns: - -```go -// Map-based message -c.ACTION(map[string]any{ - "action": "user.created", - "id": "123", -}) - -// Typed message -type UserCreated struct { - ID string - Email string -} -c.ACTION(UserCreated{ID: "123", Email: "user@example.com"}) -``` - -## ServiceRuntime - -Generic helper for services that need Core access. - -```go -type ServiceRuntime[T any] struct { - core *Core - options T -} -``` - -### Creation - -```go -func NewServiceRuntime[T any](c *Core, opts T) *ServiceRuntime[T] -``` - -### Methods - -```go -func (r *ServiceRuntime[T]) Core() *Core -func (r *ServiceRuntime[T]) Options() T -func (r *ServiceRuntime[T]) Config() *config.Service -``` - -### Usage - -```go -type MyOptions struct { - Timeout time.Duration -} - -type MyService struct { - *core.ServiceRuntime[MyOptions] -} - -func NewMyService(c *core.Core) (any, error) { - opts := MyOptions{Timeout: 30 * time.Second} - return &MyService{ - ServiceRuntime: core.NewServiceRuntime(c, opts), - }, nil -} - -func (s *MyService) DoSomething() { - timeout := s.Options().Timeout - cfg := s.Config() - // ... -} -``` - -## Lifecycle Interfaces - -### Startable - -```go -type Startable interface { - OnStartup(ctx context.Context) error -} -``` - -Implement for initialization on app start. - -### Stoppable - -```go -type Stoppable interface { - OnShutdown(ctx context.Context) error -} -``` - -Implement for cleanup on app shutdown. - -### IPC Handler - -```go -type IPCHandler interface { - HandleIPCEvents(c *Core, msg Message) error -} -``` - -Automatically registered when using `WithService`. - -## Built-in Actions - -### ActionServiceStartup - -```go -type ActionServiceStartup struct{} -``` - -Sent to all services when application starts. - -### ActionServiceShutdown - -```go -type ActionServiceShutdown struct{} -``` - -Sent to all services when application shuts down. - -## Error Helpers - -```go -func E(service, operation string, err error) error -``` - -Creates a contextual error with service and operation info. - -```go -if err != nil { - return core.E("myservice", "Connect", err) -} -// Error: myservice.Connect: connection refused -``` - -## Complete Example - -```go -package main - -import ( - "context" - "github.com/Snider/Core/pkg/core" - "github.com/Snider/Core/pkg/config" -) - -type MyService struct { - *core.ServiceRuntime[struct{}] - data string -} - -func NewMyService(c *core.Core) (any, error) { - return &MyService{ - ServiceRuntime: core.NewServiceRuntime(c, struct{}{}), - data: "initialized", - }, nil -} - -func (s *MyService) OnStartup(ctx context.Context) error { - // Startup logic - return nil -} - -func (s *MyService) OnShutdown(ctx context.Context) error { - // Cleanup logic - return nil -} - -func (s *MyService) HandleIPCEvents(c *core.Core, msg core.Message) error { - switch m := msg.(type) { - case map[string]any: - if m["action"] == "myservice.update" { - s.data = m["data"].(string) - } - } - return nil -} - -func main() { - c, err := core.New( - core.WithService(config.Register), - core.WithService(NewMyService), - core.WithServiceLock(), - ) - if err != nil { - panic(err) - } - - svc := core.MustServiceFor[*MyService](c, "main") - _ = svc -} -``` diff --git a/docs/packages/go/framework/crypt.md b/docs/packages/go/framework/crypt.md deleted file mode 100644 index 730d787..0000000 --- a/docs/packages/go/framework/crypt.md +++ /dev/null @@ -1,133 +0,0 @@ -# Crypt Service - -The Crypt service (`pkg/crypt`) provides cryptographic utilities including hashing, checksums, RSA encryption, and PGP operations. - -## Features - -- Multiple hash algorithms (SHA512, SHA256, SHA1, MD5) -- Checksum functions (Fletcher, Luhn) -- RSA key generation and encryption -- PGP encryption, signing, and verification -- Symmetric PGP encryption - -## Basic Usage - -```go -import "github.com/Snider/Core/pkg/crypt" - -// Standalone usage -crypto, err := crypt.New() - -// With Core framework -c, _ := core.New( - core.WithService(crypt.Register), -) -crypto := core.MustServiceFor[*crypt.Service](c, "crypt") -``` - -## Hashing - -```go -// Available algorithms: SHA512, SHA256, SHA1, MD5, LTHN -hash := crypto.Hash(crypt.SHA256, "hello world") - -// Check if string is valid hash algorithm -isValid := crypto.IsHashAlgo("sha256") -``` - -## Checksums - -```go -// Luhn validation (credit card numbers) -isValid := crypto.Luhn("4532015112830366") - -// Fletcher checksums -f16 := crypto.Fletcher16("data") -f32 := crypto.Fletcher32("data") -f64 := crypto.Fletcher64("data") -``` - -## RSA Encryption - -```go -// Generate key pair (2048 or 4096 bits recommended) -publicKey, privateKey, err := crypto.GenerateRSAKeyPair(2048) - -// Encrypt with public key -ciphertext, err := crypto.EncryptRSA(publicKey, "secret message") - -// Decrypt with private key -plaintext, err := crypto.DecryptRSA(privateKey, ciphertext) -``` - -## PGP Encryption - -### Key Generation - -```go -// Generate PGP key pair -publicKey, privateKey, err := crypto.GeneratePGPKeyPair( - "User Name", - "user@example.com", - "Key comment", -) -``` - -### Asymmetric Encryption - -```go -// Encrypt for recipient -ciphertext, err := crypto.EncryptPGPToString(recipientPublicKey, "secret data") - -// Decrypt with private key -plaintext, err := crypto.DecryptPGP(privateKey, ciphertext) -``` - -### Symmetric Encryption - -```go -var buf bytes.Buffer -err := crypto.SymmetricallyEncryptPGP(&buf, "data", "passphrase") -``` - -### Signing & Verification - -```go -// Sign data -signature, err := crypto.SignPGP(privateKey, "data to sign") - -// Verify signature -err := crypto.VerifyPGP(publicKey, "data to sign", signature) -if err != nil { - // Signature invalid -} -``` - -## Hash Types - -| Constant | Algorithm | -|----------|-----------| -| `crypt.SHA512` | SHA-512 | -| `crypt.SHA256` | SHA-256 | -| `crypt.SHA1` | SHA-1 | -| `crypt.MD5` | MD5 | -| `crypt.LTHN` | Custom LTHN hash | - -## Frontend Usage (TypeScript) - -```typescript -import { - Hash, - GenerateRSAKeyPair, - EncryptRSA, - DecryptRSA -} from '@bindings/crypt/service'; - -// Hash data -const hash = await Hash("SHA256", "hello world"); - -// RSA encryption -const { publicKey, privateKey } = await GenerateRSAKeyPair(2048); -const encrypted = await EncryptRSA(publicKey, "secret"); -const decrypted = await DecryptRSA(privateKey, encrypted); -``` diff --git a/docs/packages/go/framework/display.md b/docs/packages/go/framework/display.md deleted file mode 100644 index 96f01b8..0000000 --- a/docs/packages/go/framework/display.md +++ /dev/null @@ -1,352 +0,0 @@ -# Display API Reference - -Complete API reference for the Display service (`pkg/display`). - -## Service Creation - -```go -func NewService(c *core.Core) (any, error) -``` - -## Window Management - -### CreateWindow - -```go -func (s *Service) CreateWindow(opts CreateWindowOptions) (*WindowInfo, error) -``` - -Creates a new window with the specified options. - -```go -type CreateWindowOptions struct { - Name string - Title string - URL string - X int - Y int - Width int - Height int -} -``` - -### CloseWindow - -```go -func (s *Service) CloseWindow(name string) error -``` - -### GetWindowInfo - -```go -func (s *Service) GetWindowInfo(name string) (*WindowInfo, error) -``` - -Returns: - -```go -type WindowInfo struct { - Name string - Title string - X int - Y int - Width int - Height int - IsVisible bool - IsFocused bool - IsMaximized bool - IsMinimized bool -} -``` - -### ListWindowInfos - -```go -func (s *Service) ListWindowInfos() []*WindowInfo -``` - -### Window Position & Size - -```go -func (s *Service) SetWindowPosition(name string, x, y int) error -func (s *Service) SetWindowSize(name string, width, height int) error -func (s *Service) SetWindowBounds(name string, x, y, width, height int) error -``` - -### Window State - -```go -func (s *Service) MaximizeWindow(name string) error -func (s *Service) MinimizeWindow(name string) error -func (s *Service) RestoreWindow(name string) error -func (s *Service) FocusWindow(name string) error -func (s *Service) SetWindowFullscreen(name string, fullscreen bool) error -func (s *Service) SetWindowAlwaysOnTop(name string, onTop bool) error -func (s *Service) SetWindowVisibility(name string, visible bool) error -``` - -### Window Title - -```go -func (s *Service) SetWindowTitle(name, title string) error -func (s *Service) GetWindowTitle(name string) (string, error) -``` - -### Window Background - -```go -func (s *Service) SetWindowBackgroundColour(name string, r, g, b, a uint8) error -``` - -### Focus - -```go -func (s *Service) GetFocusedWindow() string -``` - -## Screen Management - -### GetScreens - -```go -func (s *Service) GetScreens() []*Screen -``` - -Returns: - -```go -type Screen struct { - ID string - Name string - X int - Y int - Width int - Height int - ScaleFactor float64 - IsPrimary bool -} -``` - -### GetScreen - -```go -func (s *Service) GetScreen(id string) (*Screen, error) -``` - -### GetPrimaryScreen - -```go -func (s *Service) GetPrimaryScreen() (*Screen, error) -``` - -### GetScreenAtPoint - -```go -func (s *Service) GetScreenAtPoint(x, y int) (*Screen, error) -``` - -### GetScreenForWindow - -```go -func (s *Service) GetScreenForWindow(name string) (*Screen, error) -``` - -### GetWorkAreas - -```go -func (s *Service) GetWorkAreas() []*WorkArea -``` - -Returns usable screen space (excluding dock/taskbar). - -## Layout Management - -### SaveLayout / RestoreLayout - -```go -func (s *Service) SaveLayout(name string) error -func (s *Service) RestoreLayout(name string) error -func (s *Service) ListLayouts() []string -func (s *Service) DeleteLayout(name string) error -func (s *Service) GetLayout(name string) *Layout -``` - -### TileWindows - -```go -func (s *Service) TileWindows(mode TileMode, windows []string) error -``` - -Tile modes: - -```go -const ( - TileModeLeft TileMode = "left" - TileModeRight TileMode = "right" - TileModeGrid TileMode = "grid" - TileModeQuadrants TileMode = "quadrants" -) -``` - -### SnapWindow - -```go -func (s *Service) SnapWindow(name string, position SnapPosition) error -``` - -Snap positions: - -```go -const ( - SnapPositionLeft SnapPosition = "left" - SnapPositionRight SnapPosition = "right" - SnapPositionTop SnapPosition = "top" - SnapPositionBottom SnapPosition = "bottom" - SnapPositionTopLeft SnapPosition = "top-left" - SnapPositionTopRight SnapPosition = "top-right" - SnapPositionBottomLeft SnapPosition = "bottom-left" - SnapPositionBottomRight SnapPosition = "bottom-right" -) -``` - -### StackWindows - -```go -func (s *Service) StackWindows(windows []string, offsetX, offsetY int) error -``` - -### ApplyWorkflowLayout - -```go -func (s *Service) ApplyWorkflowLayout(workflow WorkflowType) error -``` - -Workflow types: - -```go -const ( - WorkflowCoding WorkflowType = "coding" - WorkflowDebugging WorkflowType = "debugging" - WorkflowPresenting WorkflowType = "presenting" -) -``` - -## Dialogs - -### File Dialogs - -```go -func (s *Service) OpenSingleFileDialog(opts OpenFileOptions) (string, error) -func (s *Service) OpenFileDialog(opts OpenFileOptions) ([]string, error) -func (s *Service) SaveFileDialog(opts SaveFileOptions) (string, error) -func (s *Service) OpenDirectoryDialog(opts OpenDirectoryOptions) (string, error) -``` - -Options: - -```go -type OpenFileOptions struct { - Title string - DefaultDirectory string - AllowMultiple bool - Filters []FileFilter -} - -type SaveFileOptions struct { - Title string - DefaultDirectory string - DefaultFilename string - Filters []FileFilter -} - -type FileFilter struct { - DisplayName string - Pattern string // e.g., "*.png;*.jpg" -} -``` - -### ConfirmDialog - -```go -func (s *Service) ConfirmDialog(title, message string) (bool, error) -``` - -### PromptDialog - -```go -func (s *Service) PromptDialog(title, message string) (string, bool, error) -``` - -## System Tray - -```go -func (s *Service) SetTrayIcon(icon []byte) error -func (s *Service) SetTrayTooltip(tooltip string) error -func (s *Service) SetTrayLabel(label string) error -func (s *Service) SetTrayMenu(items []TrayMenuItem) error -func (s *Service) GetTrayInfo() map[string]any -``` - -Menu item: - -```go -type TrayMenuItem struct { - Label string - ActionID string - IsSeparator bool -} -``` - -## Clipboard - -```go -func (s *Service) ReadClipboard() (string, error) -func (s *Service) WriteClipboard(text string) error -func (s *Service) HasClipboard() bool -func (s *Service) ClearClipboard() error -``` - -## Notifications - -```go -func (s *Service) ShowNotification(opts NotificationOptions) error -func (s *Service) ShowInfoNotification(title, message string) error -func (s *Service) ShowWarningNotification(title, message string) error -func (s *Service) ShowErrorNotification(title, message string) error -func (s *Service) RequestNotificationPermission() (bool, error) -func (s *Service) CheckNotificationPermission() (bool, error) -``` - -Options: - -```go -type NotificationOptions struct { - ID string - Title string - Message string - Subtitle string -} -``` - -## Theme - -```go -func (s *Service) GetTheme() *Theme -func (s *Service) GetSystemTheme() string -``` - -Returns: - -```go -type Theme struct { - IsDark bool -} -``` - -## Events - -```go -func (s *Service) GetEventManager() *EventManager -``` - -The EventManager handles WebSocket connections for real-time events. diff --git a/docs/packages/go/framework/e.md b/docs/packages/go/framework/e.md deleted file mode 100644 index 6db7114..0000000 --- a/docs/packages/go/framework/e.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: e ---- -# Service: `e` - -Package e provides a standardized error handling mechanism for the Core library. -It allows for wrapping errors with contextual information, making it easier to -trace the origin of an error and provide meaningful feedback. - -The design of this package is influenced by the need for a simple, yet powerful -way to handle errors that can occur in different layers of the application, -from low-level file operations to high-level service interactions. - -The key features of this package are: - - Error wrapping: The Op and an optional Msg field provide context about - where and why an error occurred. - - Stack traces: By wrapping errors, we can build a logical stack trace - that is more informative than a raw stack trace. - - Consistent error handling: Encourages a uniform approach to error - handling across the entire codebase. - -## Types - -### `type Error` - -`Error` represents a standardized error with operational context. - -```go -type Error struct { - // Op is the operation being performed, e.g., "config.Load". - Op string - // Msg is a human-readable message explaining the error. - Msg string - // Err is the underlying error that was wrapped. - Err error -} -``` - -#### Methods - -- `Error() string`: Error returns the string representation of the error. -- `Unwrap() error`: Unwrap provides compatibility for Go's errors.Is and errors.As functions. - -## Functions - -- `E(op, msg string, err error) error`: E is a helper function to create a new Error. - -This is the primary way to create errors that will be consumed by the system. For example: - -```go -return e.E("config.Load", "failed to load config file", err) -``` - -The `op` parameter should be in the format of `package.function` or `service.method`. The `msg` parameter should be a human-readable message that can be displayed to the user. The `err` parameter is the underlying error that is being wrapped. diff --git a/docs/packages/go/framework/help.md b/docs/packages/go/framework/help.md deleted file mode 100644 index 8f2614f..0000000 --- a/docs/packages/go/framework/help.md +++ /dev/null @@ -1,152 +0,0 @@ -# Help Service - -The Help service (`pkg/help`) provides an embeddable documentation system that displays MkDocs-based help content in a dedicated window. - -## Features - -- Embedded help content (MkDocs static site) -- Context-sensitive help navigation -- Works with or without Display service -- Multiple content sources (embedded, filesystem, custom) - -## Basic Usage - -```go -import "github.com/Snider/Core/pkg/help" - -// Create with default embedded content -helpService, err := help.New(help.Options{}) - -// Initialize with core dependencies -helpService.Init(coreInstance, displayService) -``` - -## Showing Help - -```go -// Show main help window -err := helpService.Show() - -// Show specific section -err := helpService.ShowAt("getting-started") -err := helpService.ShowAt("api/config") -``` - -## Options - -```go -type Options struct { - Source string // Path to help content directory - Assets fs.FS // Custom filesystem for assets -} -``` - -### Default Embedded Content - -```go -// Uses embedded MkDocs site -helpService, _ := help.New(help.Options{}) -``` - -### Custom Directory - -```go -// Use local directory -helpService, _ := help.New(help.Options{ - Source: "/path/to/docs/site", -}) -``` - -### Custom Filesystem - -```go -//go:embed docs/* -var docsFS embed.FS - -helpService, _ := help.New(help.Options{ - Assets: docsFS, -}) -``` - -## Integration with Core - -The help service can work standalone or integrated with Core: - -### With Display Service - -When Display service is available, help opens through the IPC action system: - -```go -// Automatically uses display.open_window action -helpService.Init(core, displayService) -helpService.Show() -``` - -### Without Display Service - -Falls back to direct Wails window creation: - -```go -// Creates window directly via Wails -helpService.Init(core, nil) -helpService.Show() -``` - -## Lifecycle - -```go -// Called on application startup -err := helpService.ServiceStartup(ctx) -``` - -## Building Help Content - -Help content is a static MkDocs site. To update: - -1. Edit documentation in `docs/` directory -2. Build with MkDocs: - ```bash - mkdocs build - ``` -3. The built site goes to `pkg/help/public/` -4. Content is embedded at compile time - -## Frontend Usage (TypeScript) - -```typescript -import { Show, ShowAt } from '@bindings/help/service'; - -// Open help window -await Show(); - -// Open specific section -await ShowAt("configuration"); -await ShowAt("api/display"); -``` - -## Help Window Options - -The help window opens with default settings: - -| Property | Value | -|----------|-------| -| Title | "Help" | -| Width | 800px | -| Height | 600px | - -## IPC Action - -When using Display service, help triggers this action: - -```go -{ - "action": "display.open_window", - "name": "help", - "options": { - "Title": "Help", - "Width": 800, - "Height": 600, - "URL": "/#anchor", // When using ShowAt - }, -} -``` diff --git a/docs/packages/go/framework/i18n.md b/docs/packages/go/framework/i18n.md deleted file mode 100644 index ce36b85..0000000 --- a/docs/packages/go/framework/i18n.md +++ /dev/null @@ -1,130 +0,0 @@ -# I18n Service - -The I18n service (`pkg/i18n`) provides internationalization and localization support with automatic language detection and template-based translations. - -## Features - -- JSON-based locale files -- Embedded locale bundles -- Automatic language detection from environment -- Template variable interpolation -- BCP 47 language tag support - -## Basic Usage - -```go -import "github.com/Snider/Core/pkg/i18n" - -// Create service (defaults to English) -i18n, err := i18n.New() -if err != nil { - log.Fatal(err) -} -``` - -## Setting Language - -```go -// Set language using BCP 47 tag -err := i18n.SetLanguage("fr") -err := i18n.SetLanguage("en-US") -err := i18n.SetLanguage("zh-Hans") -``` - -## Translating Messages - -```go -// Simple translation -msg := i18n.Translate("welcome_message") - -// With template data -msg := i18n.Translate("greeting", map[string]string{ - "Name": "John", -}) -// Template: "Hello, {{.Name}}!" -// Result: "Hello, John!" -``` - -## Available Languages - -```go -// Get list of available language codes -langs := i18n.AvailableLanguages() -// Returns: ["en", "es", "fr", "de", ...] -``` - -## Get All Messages - -```go -// Get all translations for a language -messages, err := i18n.GetAllMessages("en") -for key, value := range messages { - fmt.Printf("%s: %s\n", key, value) -} -``` - -## Locale File Format - -Locale files are JSON stored in `locales/` directory: - -```json -// locales/en.json -{ - "welcome": "Welcome to the application", - "greeting": "Hello, {{.Name}}!", - "items_count": { - "one": "{{.Count}} item", - "other": "{{.Count}} items" - } -} -``` - -## Adding New Languages - -1. Create a new JSON file in `pkg/i18n/locales/`: - ``` - locales/es.json - ``` - -2. Add translations: - ```json - { - "welcome": "Bienvenido a la aplicación", - "greeting": "¡Hola, {{.Name}}!" - } - ``` - -3. The service automatically loads embedded locales at startup. - -## Language Detection - -The service can detect system language from the `LANG` environment variable: - -```go -// Automatic detection happens internally -// LANG=fr_FR.UTF-8 -> French -// LANG=de_DE.UTF-8 -> German -``` - -## Frontend Usage (TypeScript) - -```typescript -import { - SetLanguage, - Translate, - AvailableLanguages, - GetAllMessages -} from '@bindings/i18n/service'; - -// Set language -await SetLanguage("fr"); - -// Translate -const welcome = await Translate("welcome_message"); - -// Get available languages for a selector -const languages = await AvailableLanguages(); - -// Load all messages for client-side caching -const messages = await GetAllMessages("en"); -``` diff --git a/docs/packages/go/framework/installation.md b/docs/packages/go/framework/installation.md deleted file mode 100644 index 49e179a..0000000 --- a/docs/packages/go/framework/installation.md +++ /dev/null @@ -1,76 +0,0 @@ -# Installation - -## Prerequisites - -### Go 1.22+ - -```bash -# macOS -brew install go - -# Linux -sudo apt install golang-go - -# Windows - download from https://go.dev/dl/ -``` - -### Wails v3 - -```bash -go install github.com/wailsapp/wails/v3/cmd/wails3@latest -``` - -### Task (Build Automation) - -```bash -# macOS -brew install go-task - -# Linux -sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d - -# Windows -choco install go-task -``` - -## Install Core - -```bash -go get github.com/Snider/Core@latest -``` - -## Verify Installation - -```bash -# Check Go -go version - -# Check Wails -wails3 version - -# Check Task -task --version -``` - -## IDE Setup - -### VS Code - -Install the Go extension and configure: - -```json -{ - "go.useLanguageServer": true, - "gopls": { - "ui.semanticTokens": true - } -} -``` - -### GoLand / IntelliJ - -Go support is built-in. Enable the Wails plugin for additional features. - -## Next Steps - -Continue to [Quick Start](quickstart.md) to create your first application. diff --git a/docs/packages/go/framework/io.md b/docs/packages/go/framework/io.md deleted file mode 100644 index 6274d5f..0000000 --- a/docs/packages/go/framework/io.md +++ /dev/null @@ -1,165 +0,0 @@ -# IO Service - -The IO package (`pkg/io`) provides a unified interface for file operations across different storage backends (local filesystem, S3, SFTP, etc.). - -## Features - -- Abstract `Medium` interface for storage backends -- Local filesystem implementation -- Copy between different mediums -- Mock implementation for testing - -## Medium Interface - -All storage backends implement the `Medium` interface: - -```go -type Medium interface { - Read(path string) (string, error) - Write(path, content string) error - EnsureDir(path string) error - IsFile(path string) bool - FileGet(path string) (string, error) - FileSet(path, content string) error -} -``` - -## Local Filesystem - -```go -import ( - "github.com/Snider/Core/pkg/io" - "github.com/Snider/Core/pkg/io/local" -) - -// Pre-initialized global medium (root = "/") -content, err := io.Local.Read("/etc/hosts") - -// Create sandboxed medium -medium, err := local.New("/app/data") -content, err := medium.Read("config.json") // Reads /app/data/config.json -``` - -## Basic Operations - -```go -// Read file -content, err := medium.Read("path/to/file.txt") - -// Write file -err := medium.Write("path/to/file.txt", "content") - -// Check if file exists -if medium.IsFile("config.json") { - // File exists -} - -// Ensure directory exists -err := medium.EnsureDir("path/to/dir") - -// Convenience methods -content, err := medium.FileGet("file.txt") -err := medium.FileSet("file.txt", "content") -``` - -## Helper Functions - -Package-level functions that work with any Medium: - -```go -// Read from medium -content, err := io.Read(medium, "file.txt") - -// Write to medium -err := io.Write(medium, "file.txt", "content") - -// Ensure directory -err := io.EnsureDir(medium, "path/to/dir") - -// Check if file -exists := io.IsFile(medium, "file.txt") -``` - -## Copy Between Mediums - -```go -localMedium, _ := local.New("/local/path") -remoteMedium := s3.New(bucket, region) // hypothetical S3 implementation - -// Copy from local to remote -err := io.Copy(localMedium, "data.json", remoteMedium, "backup/data.json") -``` - -## Mock Medium for Testing - -```go -import "github.com/Snider/Core/pkg/io" - -func TestMyFunction(t *testing.T) { - mock := io.NewMockMedium() - - // Pre-populate files - mock.Files["config.json"] = `{"key": "value"}` - mock.Dirs["data"] = true - - // Use in tests - myService := NewService(mock) - - // Verify writes - err := myService.SaveData("test") - if mock.Files["data/test.json"] != expectedContent { - t.Error("unexpected content") - } -} -``` - -## Creating Custom Backends - -Implement the `Medium` interface for custom storage: - -```go -type S3Medium struct { - bucket string - client *s3.Client -} - -func (m *S3Medium) Read(path string) (string, error) { - // Implement S3 read -} - -func (m *S3Medium) Write(path, content string) error { - // Implement S3 write -} - -// ... implement remaining methods -``` - -## Error Handling - -```go -content, err := medium.Read("missing.txt") -if err != nil { - // File not found or read error - log.Printf("Read failed: %v", err) -} -``` - -## Frontend Usage - -The IO package is primarily used server-side. Frontend file operations should use the Display service dialogs or direct API calls: - -```typescript -import { OpenFileDialog, SaveFileDialog } from '@bindings/display/service'; - -// Open file picker -const path = await OpenFileDialog({ - title: "Select File", - filters: [{ displayName: "Text", pattern: "*.txt" }] -}); - -// Save file picker -const savePath = await SaveFileDialog({ - title: "Save As", - defaultFilename: "document.txt" -}); -``` diff --git a/docs/packages/go/framework/ipc.md b/docs/packages/go/framework/ipc.md deleted file mode 100644 index d506504..0000000 --- a/docs/packages/go/framework/ipc.md +++ /dev/null @@ -1,119 +0,0 @@ -# IPC & Actions - -Core provides an inter-process communication system for services to communicate without tight coupling. - -## Message Structure - -```go -type Message struct { - Type string // Message type identifier - Data map[string]any // Message payload - Source string // Originating service (optional) - Timestamp time.Time // When message was created -} -``` - -## Sending Messages - -```go -c.ACTION(core.Message{ - Type: "user.created", - Data: map[string]any{ - "id": "123", - "email": "user@example.com", - }, -}) -``` - -## Handling Messages - -Register action handlers during service initialization: - -```go -func NewNotificationService(c *core.Core) (any, error) { - svc := &NotificationService{} - - // Register handler - c.RegisterAction(func(c *core.Core, msg core.Message) error { - return svc.handleAction(msg) - }) - - return svc, nil -} - -func (s *NotificationService) handleAction(msg core.Message) error { - switch msg.Type { - case "user.created": - email := msg.Data["email"].(string) - return s.sendWelcomeEmail(email) - } - return nil -} -``` - -## Auto-Discovery - -Services implementing `HandleIPCEvents` are automatically registered: - -```go -type MyService struct{} - -// Automatically registered when using WithService -func (s *MyService) HandleIPCEvents(c *core.Core, msg core.Message) error { - // Handle messages - return nil -} -``` - -## Common Patterns - -### Request/Response - -```go -// Sender -responseChan := make(chan any) -c.ACTION(core.Message{ - Type: "data.request", - Data: map[string]any{ - "query": "SELECT * FROM users", - "response": responseChan, - }, -}) -result := <-responseChan - -// Handler -func (s *DataService) handleAction(msg core.Message) error { - if msg.Type == "data.request" { - query := msg.Data["query"].(string) - respChan := msg.Data["response"].(chan any) - - result, err := s.execute(query) - if err != nil { - return err - } - - respChan <- result - } - return nil -} -``` - -### Event Broadcasting - -```go -// Broadcast to all listeners -c.ACTION(core.Message{ - Type: "system.config.changed", - Data: map[string]any{ - "key": "theme", - "value": "dark", - }, -}) -``` - -## Best Practices - -1. **Use namespaced types** - `service.action` format -2. **Keep payloads simple** - Use primitive types when possible -3. **Handle errors** - Return errors from handlers -4. **Document message types** - Create constants for message types diff --git a/docs/packages/go/framework/lifecycle.md b/docs/packages/go/framework/lifecycle.md deleted file mode 100644 index 1830ce0..0000000 --- a/docs/packages/go/framework/lifecycle.md +++ /dev/null @@ -1,101 +0,0 @@ -# Service Lifecycle - -Core provides lifecycle hooks for services to initialize and clean up resources. - -## Lifecycle Interfaces - -### Startable - -Called when the application starts: - -```go -type Startable interface { - OnStartup(ctx context.Context) error -} -``` - -### Stoppable - -Called when the application shuts down: - -```go -type Stoppable interface { - OnShutdown(ctx context.Context) error -} -``` - -## Implementation Example - -```go -type DatabaseService struct { - db *sql.DB -} - -func (s *DatabaseService) OnStartup(ctx context.Context) error { - db, err := sql.Open("postgres", os.Getenv("DATABASE_URL")) - if err != nil { - return err - } - - // Verify connection - if err := db.PingContext(ctx); err != nil { - return err - } - - s.db = db - return nil -} - -func (s *DatabaseService) OnShutdown(ctx context.Context) error { - if s.db != nil { - return s.db.Close() - } - return nil -} -``` - -## Lifecycle Order - -1. **Registration**: Services registered via `core.New()` -2. **Wails Binding**: Services bound to Wails app -3. **Startup**: `OnStartup()` called for each Startable service -4. **Running**: Application runs -5. **Shutdown**: `OnShutdown()` called for each Stoppable service - -## Context Usage - -The context passed to lifecycle methods includes: - -- Cancellation signal for graceful shutdown -- Deadline for timeout handling - -```go -func (s *Service) OnStartup(ctx context.Context) error { - select { - case <-ctx.Done(): - return ctx.Err() - case <-s.initialize(): - return nil - } -} -``` - -## Error Handling - -If `OnStartup` returns an error, the application will fail to start: - -```go -func (s *Service) OnStartup(ctx context.Context) error { - if err := s.validate(); err != nil { - return fmt.Errorf("validation failed: %w", err) - } - return nil -} -``` - -## Best Practices - -1. **Keep startup fast** - Defer heavy initialization -2. **Handle context cancellation** - Support graceful shutdown -3. **Clean up resources** - Always implement OnShutdown for services with resources -4. **Log lifecycle events** - Helps with debugging diff --git a/docs/packages/go/framework/mcp-bridge.md b/docs/packages/go/framework/mcp-bridge.md deleted file mode 100644 index d654a3e..0000000 --- a/docs/packages/go/framework/mcp-bridge.md +++ /dev/null @@ -1,220 +0,0 @@ -# MCP Bridge - -The MCP Bridge (`cmd/core-gui/mcp_bridge.go`) connects the Model Context Protocol server with Display, WebView, and WebSocket services. - -## Overview - -The MCP Bridge provides an HTTP API for AI assistants to interact with the desktop application, enabling: - -- Window and screen management -- JavaScript execution in webviews -- DOM interaction (click, type, select) -- Screenshot capture -- File and process management -- Real-time events via WebSocket - -## HTTP Endpoints - -| Endpoint | Description | -|----------|-------------| -| `GET /health` | Health check | -| `GET /mcp` | Server capabilities | -| `GET /mcp/tools` | List available tools | -| `POST /mcp/call` | Execute a tool | -| `WS /ws` | WebSocket for GUI clients | -| `WS /events` | WebSocket for display events | - -## Server Capabilities - -```bash -curl http://localhost:9877/mcp -``` - -Response: - -```json -{ - "name": "core", - "version": "0.1.0", - "capabilities": { - "webview": true, - "display": true, - "windowControl": true, - "screenControl": true, - "websocket": "ws://localhost:9877/ws", - "events": "ws://localhost:9877/events" - } -} -``` - -## Tool Categories - -### File Operations - -| Tool | Description | -|------|-------------| -| `file_read` | Read file contents | -| `file_write` | Write content to file | -| `file_edit` | Edit file by replacing text | -| `file_delete` | Delete a file | -| `file_exists` | Check if file exists | -| `dir_list` | List directory contents | -| `dir_create` | Create directory | - -### Window Control - -| Tool | Description | -|------|-------------| -| `window_list` | List all windows | -| `window_create` | Create new window | -| `window_close` | Close window | -| `window_position` | Move window | -| `window_size` | Resize window | -| `window_maximize` | Maximize window | -| `window_minimize` | Minimize window | -| `window_focus` | Bring window to front | - -### WebView Interaction - -| Tool | Description | -|------|-------------| -| `webview_eval` | Execute JavaScript | -| `webview_click` | Click element | -| `webview_type` | Type into element | -| `webview_screenshot` | Capture page | -| `webview_navigate` | Navigate to URL | -| `webview_console` | Get console messages | - -### Screen Management - -| Tool | Description | -|------|-------------| -| `screen_list` | List all monitors | -| `screen_primary` | Get primary screen | -| `screen_at_point` | Get screen at coordinates | -| `screen_work_areas` | Get usable screen space | - -### Layout Management - -| Tool | Description | -|------|-------------| -| `layout_save` | Save window arrangement | -| `layout_restore` | Restore saved layout | -| `layout_tile` | Auto-tile windows | -| `layout_snap` | Snap window to edge | - -## Calling Tools - -```bash -# List windows -curl -X POST http://localhost:9877/mcp/call \ - -H "Content-Type: application/json" \ - -d '{"tool": "window_list", "params": {}}' - -# Move window -curl -X POST http://localhost:9877/mcp/call \ - -H "Content-Type: application/json" \ - -d '{ - "tool": "window_position", - "params": {"name": "main", "x": 100, "y": 100} - }' - -# Execute JavaScript -curl -X POST http://localhost:9877/mcp/call \ - -H "Content-Type: application/json" \ - -d '{ - "tool": "webview_eval", - "params": { - "window": "main", - "code": "document.title" - } - }' - -# Click element -curl -X POST http://localhost:9877/mcp/call \ - -H "Content-Type: application/json" \ - -d '{ - "tool": "webview_click", - "params": { - "window": "main", - "selector": "#submit-button" - } - }' - -# Take screenshot -curl -X POST http://localhost:9877/mcp/call \ - -H "Content-Type: application/json" \ - -d '{ - "tool": "webview_screenshot", - "params": {"window": "main"} - }' -``` - -## WebSocket Events - -Connect to `/events` for real-time display events: - -```javascript -const ws = new WebSocket('ws://localhost:9877/events'); - -ws.onmessage = (event) => { - const data = JSON.parse(event.data); - switch (data.type) { - case 'window.focus': - console.log('Window focused:', data.name); - break; - case 'window.move': - console.log('Window moved:', data.name, data.x, data.y); - break; - case 'theme.change': - console.log('Theme changed:', data.isDark); - break; - } -}; -``` - -Event types: - -- `window.focus` - Window received focus -- `window.blur` - Window lost focus -- `window.move` - Window position changed -- `window.resize` - Window size changed -- `window.close` - Window was closed -- `window.create` - New window created -- `theme.change` - System theme changed -- `screen.change` - Screen configuration changed - -## Go Integration - -```go -import "github.com/Snider/Core/cmd/core-gui" - -// Create bridge -bridge := NewMCPBridge(9877, displayService) - -// Access services -mcpSvc := bridge.GetMCPService() -webview := bridge.GetWebView() -display := bridge.GetDisplay() -``` - -## Configuration - -The bridge starts automatically on Wails app startup via the `ServiceStartup` lifecycle hook: - -```go -func (b *MCPBridge) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { - b.app = application.Get() - b.webview.SetApp(b.app) - go b.startHTTPServer() - return nil -} -``` - -## Security - -The MCP server binds to localhost only by default. For production: - -- Consider firewall rules -- Add authentication if needed -- Limit exposed tools diff --git a/docs/packages/go/framework/mcp.md b/docs/packages/go/framework/mcp.md deleted file mode 100644 index cf437b2..0000000 --- a/docs/packages/go/framework/mcp.md +++ /dev/null @@ -1,151 +0,0 @@ -# MCP Service - -The MCP service (`pkg/mcp`) implements the [Model Context Protocol](https://modelcontextprotocol.io/), enabling AI assistants like Claude to interact with your application. - -## Overview - -MCP provides a standardized way for AI tools to: - -- Execute operations in your application -- Query application state -- Interact with the UI -- Manage files and processes - -## Basic Setup - -```go -import "github.com/Snider/Core/pkg/mcp" - -// Create standalone MCP server -mcpService := mcp.NewStandaloneWithPort(9877) - -// Or integrate with Core -c, _ := core.New( - core.WithService(mcp.NewService), -) -``` - -## Available Tools - -The MCP service exposes numerous tools organized by category: - -### File Operations - -| Tool | Description | -|------|-------------| -| `file_read` | Read file contents | -| `file_write` | Write content to file | -| `file_edit` | Replace text in file | -| `file_delete` | Delete a file | -| `file_exists` | Check if file exists | -| `dir_list` | List directory contents | -| `dir_create` | Create directory | - -### Window Control - -| Tool | Description | -|------|-------------| -| `window_list` | List all windows | -| `window_create` | Create new window | -| `window_close` | Close window | -| `window_position` | Move window | -| `window_size` | Resize window | -| `window_maximize` | Maximize window | -| `window_minimize` | Minimize window | -| `window_focus` | Bring to front | - -### WebView Interaction - -| Tool | Description | -|------|-------------| -| `webview_eval` | Execute JavaScript | -| `webview_click` | Click element | -| `webview_type` | Type into element | -| `webview_screenshot` | Capture page | -| `webview_navigate` | Navigate to URL | -| `webview_console` | Get console logs | - -### Process Management - -| Tool | Description | -|------|-------------| -| `process_start` | Start a process | -| `process_stop` | Stop a process | -| `process_list` | List running processes | -| `process_output` | Get process output | - -## HTTP API - -The MCP service exposes an HTTP API: - -```bash -# Health check -curl http://localhost:9877/health - -# List available tools -curl http://localhost:9877/mcp/tools - -# Call a tool -curl -X POST http://localhost:9877/mcp/call \ - -H "Content-Type: application/json" \ - -d '{"tool": "window_list", "params": {}}' -``` - -## WebSocket Events - -Connect to `/events` for real-time updates: - -```javascript -const ws = new WebSocket('ws://localhost:9877/events'); -ws.onmessage = (event) => { - const data = JSON.parse(event.data); - console.log('Event:', data.type, data.data); -}; -``` - -## Integration with Display Service - -```go -mcpService := mcp.NewStandaloneWithPort(9877) -mcpService.SetDisplay(displayService) -mcpService.SetWebView(webviewService) -``` - -## Example: Claude Integration - -When Claude connects via MCP, it can: - -``` -User: "Move the settings window to the left side of the screen" - -Claude uses: window_position("settings", 0, 100) -``` - -``` -User: "Take a screenshot of the app" - -Claude uses: webview_screenshot("main") -``` - -``` -User: "Click the submit button" - -Claude uses: webview_click("main", "#submit-btn") -``` - -## Security Considerations - -- MCP server binds to localhost by default -- No authentication (designed for local AI assistants) -- Consider firewall rules for production - -## Configuration - -```go -// Custom port -mcp.NewStandaloneWithPort(8080) - -// With all services -bridge := NewMCPBridge(9877, displayService) -bridge.SetWebView(webviewService) -``` diff --git a/docs/packages/go/framework/modules.md b/docs/packages/go/framework/modules.md deleted file mode 100644 index d6a748d..0000000 --- a/docs/packages/go/framework/modules.md +++ /dev/null @@ -1,271 +0,0 @@ -# Module System - -The Module system (`pkg/module`) provides a declarative way to register UI menus, routes, and API endpoints using the `.itw3.json` configuration format. - -## Features - -- Declarative module configuration -- UI menu contributions -- Frontend route registration -- API endpoint declarations -- Multi-context support (developer, retail, miner) -- Binary/daemon management -- Module dependencies - -## Module Config Format - -Modules are defined using `.itw3.json` files: - -```json -{ - "code": "wallet", - "type": "core", - "name": "Wallet Manager", - "version": "1.0.0", - "namespace": "finance", - "description": "Cryptocurrency wallet management", - "author": "Your Name", - "contexts": ["default", "retail"], - "menu": [...], - "routes": [...], - "api": [...], - "config": {...} -} -``` - -## Module Types - -| Type | Description | -|------|-------------| -| `core` | Built-in core functionality | -| `app` | External web application | -| `bin` | Binary/daemon wrapper | - -## UI Contexts - -Modules can target specific UI contexts: - -| Context | Description | -|---------|-------------| -| `default` | Standard user interface | -| `developer` | Developer tools and debugging | -| `retail` | Point-of-sale interface | -| `miner` | Mining operations interface | - -## Menu Contributions - -Add items to the application menu: - -```json -{ - "menu": [ - { - "id": "wallet-send", - "label": "Send Funds", - "icon": "send", - "route": "/wallet/send", - "accelerator": "CmdOrCtrl+Shift+S", - "contexts": ["default", "retail"], - "order": 10 - }, - { - "id": "wallet-receive", - "label": "Receive", - "icon": "receive", - "route": "/wallet/receive", - "order": 20 - }, - { - "separator": true - }, - { - "id": "wallet-settings", - "label": "Settings", - "action": "wallet.open_settings", - "children": [ - {"id": "wallet-backup", "label": "Backup", "action": "wallet.backup"}, - {"id": "wallet-restore", "label": "Restore", "action": "wallet.restore"} - ] - } - ] -} -``` - -## Route Contributions - -Register frontend routes: - -```json -{ - "routes": [ - { - "path": "/wallet", - "component": "wallet-dashboard", - "title": "Wallet", - "icon": "wallet", - "contexts": ["default"] - }, - { - "path": "/wallet/send", - "component": "wallet-send-form", - "title": "Send Funds" - } - ] -} -``` - -## API Declarations - -Declare API endpoints the module provides: - -```json -{ - "api": [ - { - "method": "GET", - "path": "/balance", - "description": "Get wallet balance" - }, - { - "method": "POST", - "path": "/send", - "description": "Send transaction" - } - ] -} -``` - -## Binary Downloads - -For `bin` type modules, specify platform binaries: - -```json -{ - "downloads": { - "app": "https://example.com/wallet-ui.tar.gz", - "x86_64": { - "darwin": { - "url": "https://example.com/wallet-darwin-x64", - "checksum": "sha256:abc123..." - }, - "linux": { - "url": "https://example.com/wallet-linux-x64", - "checksum": "sha256:def456..." - }, - "windows": { - "url": "https://example.com/wallet-win-x64.exe", - "checksum": "sha256:ghi789..." - } - }, - "aarch64": { - "darwin": { - "url": "https://example.com/wallet-darwin-arm64" - } - } - } -} -``` - -## Web App Configuration - -For `app` type modules: - -```json -{ - "app": { - "url": "https://example.com/wallet-app.tar.gz", - "type": "spa", - "hooks": [ - { - "type": "rename", - "from": "dist", - "to": "wallet" - } - ] - } -} -``` - -## Dependencies - -Declare module dependencies: - -```json -{ - "depends": ["core", "crypto"] -} -``` - -## Using in Go - -### Module Registration - -```go -import "github.com/Snider/Core/pkg/module" - -// Create from config -cfg := module.Config{ - Code: "wallet", - Type: module.TypeCore, - Name: "Wallet", - Namespace: "finance", -} - -mod := module.Module{ - Config: cfg, - Handler: myHandler, -} -``` - -### Gin Router Integration - -```go -type WalletModule struct{} - -func (m *WalletModule) RegisterRoutes(group *gin.RouterGroup) { - group.GET("/balance", m.getBalance) - group.POST("/send", m.sendTransaction) -} - -// Register with Gin -router := gin.Default() -apiGroup := router.Group("/api/finance/wallet") -walletModule.RegisterRoutes(apiGroup) -``` - -## Registry Service - -The registry manages all modules: - -```go -import "github.com/Snider/Core/pkg/module" - -registry := module.NewRegistry() - -// Register module -registry.Register(walletModule) - -// Get module by code -mod := registry.Get("wallet") - -// List all modules -modules := registry.List() - -// Get modules for context -devModules := registry.ForContext(module.ContextDeveloper) -``` - -## Built-in Modules - -Core provides several built-in modules: - -- System information -- Configuration management -- Process management -- File operations - -Access via: - -```go -builtins := module.BuiltinModules() -``` diff --git a/docs/packages/go/framework/overview.md b/docs/packages/go/framework/overview.md deleted file mode 100644 index 1c6f110..0000000 --- a/docs/packages/go/framework/overview.md +++ /dev/null @@ -1,175 +0,0 @@ -# GUI Application - -The Core GUI (`cmd/core-gui`) is a Wails v3 desktop application that demonstrates the Core framework capabilities with integrated MCP support. - -## Features - -- Angular frontend with Wails bindings -- MCP HTTP server for AI tool integration -- WebView automation capabilities -- Real-time WebSocket communication -- System tray support -- Multi-window management - -## Architecture - -``` -┌─────────────────────────────────────────┐ -│ Angular Frontend │ -│ (TypeScript + Wails Bindings) │ -└─────────────────┬───────────────────────┘ - │ IPC -┌─────────────────┴───────────────────────┐ -│ Wails Runtime │ -│ (Window, Events, Bindings) │ -└─────────────────┬───────────────────────┘ - │ -┌─────────────────┴───────────────────────┐ -│ MCP Bridge │ -│ ┌─────────┬──────────┬─────────────┐ │ -│ │ Display │ WebView │ WebSocket │ │ -│ │ Service │ Service │ Hub │ │ -│ └─────────┴──────────┴─────────────┘ │ -└─────────────────────────────────────────┘ -``` - -## Running the GUI - -### Development Mode - -```bash -# From project root -task gui:dev - -# Or directly -cd cmd/core-gui -wails3 dev -``` - -### Production Build - -```bash -task gui:build -``` - -## Directory Structure - -``` -cmd/core-gui/ -├── main.go # Application entry point -├── mcp_bridge.go # MCP HTTP server and tool handler -├── claude_bridge.go # Claude MCP client (optional) -├── frontend/ # Angular application -│ ├── src/ -│ │ ├── app/ # Angular components -│ │ └── lib/ # Shared utilities -│ └── bindings/ # Generated Wails bindings -└── public/ # Static assets -``` - -## Services Integrated - -The GUI integrates several Core services: - -| Service | Purpose | -|---------|---------| -| Display | Window management, dialogs, tray | -| WebView | JavaScript execution, DOM interaction | -| MCP | AI tool protocol server | -| WebSocket | Real-time communication | - -## Configuration - -The application uses the Config service for settings: - -```go -// Default settings -DefaultRoute: "/" -Language: "en" -Features: [] -``` - -## Frontend Bindings - -Wails generates TypeScript bindings for Go services: - -```typescript -import { CreateWindow, ShowNotification } from '@bindings/display/service'; -import { Translate, SetLanguage } from '@bindings/i18n/service'; - -// Create a new window -await CreateWindow({ - name: "settings", - title: "Settings", - width: 800, - height: 600 -}); - -// Show notification -await ShowNotification({ - title: "Success", - message: "Operation completed!" -}); -``` - -## WebSocket Communication - -Connect to the WebSocket endpoint for real-time updates: - -```typescript -const ws = new WebSocket('ws://localhost:9877/ws'); - -ws.onmessage = (event) => { - const data = JSON.parse(event.data); - console.log('Received:', data); -}; - -ws.send(JSON.stringify({ - type: 'ping', - data: {} -})); -``` - -## System Tray - -The application includes system tray support: - -```go -// Set tray menu -display.SetTrayMenu([]display.TrayMenuItem{ - {Label: "Open", ActionID: "open"}, - {Label: "Settings", ActionID: "settings"}, - {IsSeparator: true}, - {Label: "Quit", ActionID: "quit"}, -}) -``` - -## Building for Distribution - -### macOS - -```bash -task gui:build -# Creates: build/bin/core-gui.app -``` - -### Windows - -```bash -task gui:build -# Creates: build/bin/core-gui.exe -``` - -### Linux - -```bash -task gui:build -# Creates: build/bin/core-gui -``` - -## Environment Variables - -| Variable | Description | -|----------|-------------| -| `MCP_PORT` | MCP server port (default: 9877) | -| `DEBUG` | Enable debug logging | diff --git a/docs/packages/go/framework/plugins.md b/docs/packages/go/framework/plugins.md deleted file mode 100644 index 945ff29..0000000 --- a/docs/packages/go/framework/plugins.md +++ /dev/null @@ -1,172 +0,0 @@ -# Plugin System - -The Plugin system (`pkg/plugin`) allows you to extend Core applications with HTTP-based plugins that register routes under `/api/{namespace}/{name}/`. - -## Features - -- Namespace-based organization -- HTTP handler registration -- Lifecycle hooks (OnRegister, OnUnregister) -- Wails service integration - -## Plugin Interface - -All plugins implement the `Plugin` interface: - -```go -type Plugin interface { - // Name returns the unique identifier for this plugin - Name() string - - // Namespace returns the plugin's namespace (e.g., "core", "mining") - Namespace() string - - // ServeHTTP handles HTTP requests routed to this plugin - http.Handler - - // OnRegister is called when the plugin is registered - OnRegister(ctx context.Context) error - - // OnUnregister is called when the plugin is being removed - OnUnregister(ctx context.Context) error -} -``` - -## Using BasePlugin - -For simple plugins, embed `BasePlugin`: - -```go -import "github.com/Snider/Core/pkg/plugin" - -func NewMyPlugin() *plugin.BasePlugin { - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Hello from plugin!")) - }) - - return plugin.NewBasePlugin("myapp", "greeting", handler). - WithDescription("A simple greeting plugin"). - WithVersion("1.0.0") -} -``` - -## Custom Plugin Implementation - -For more control, implement the full interface: - -```go -type DataPlugin struct { - db *sql.DB -} - -func (p *DataPlugin) Name() string { return "data" } -func (p *DataPlugin) Namespace() string { return "myapp" } - -func (p *DataPlugin) ServeHTTP(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/users": - p.handleUsers(w, r) - case "/items": - p.handleItems(w, r) - default: - http.NotFound(w, r) - } -} - -func (p *DataPlugin) OnRegister(ctx context.Context) error { - // Initialize database connection - db, err := sql.Open("postgres", os.Getenv("DATABASE_URL")) - if err != nil { - return err - } - p.db = db - return nil -} - -func (p *DataPlugin) OnUnregister(ctx context.Context) error { - if p.db != nil { - return p.db.Close() - } - return nil -} -``` - -## Plugin Info - -Access plugin metadata: - -```go -info := myPlugin.Info() -fmt.Println(info.Name) // "greeting" -fmt.Println(info.Namespace) // "myapp" -fmt.Println(info.Description) // "A simple greeting plugin" -fmt.Println(info.Version) // "1.0.0" -``` - -## Wails Integration - -Register plugins as Wails services: - -```go -app := application.New(application.Options{ - Services: []application.Service{ - application.NewServiceWithOptions( - myPlugin, - plugin.ServiceOptionsForPlugin(myPlugin), - ), - }, -}) -``` - -## URL Routing - -Plugins receive requests at: - -``` -/api/{namespace}/{name}/{path} -``` - -Examples: -- `/api/myapp/greeting/` → GreetingPlugin -- `/api/myapp/data/users` → DataPlugin (path: "/users") -- `/api/core/system/health` → SystemPlugin (path: "/health") - -## Built-in Plugins - -### System Plugin - -Located at `pkg/plugin/builtin/system`: - -```go -// Provides system information endpoints -/api/core/system/info - Application info -/api/core/system/health - Health check -``` - -## Plugin Router - -The Router manages plugin registration: - -```go -import "github.com/Snider/Core/pkg/plugin" - -router := plugin.NewRouter() - -// Register plugins -router.Register(ctx, myPlugin) -router.Register(ctx, dataPlugin) - -// Get all registered plugins -plugins := router.List() - -// Unregister a plugin -router.Unregister(ctx, "myapp", "greeting") -``` - -## Best Practices - -1. **Use namespaces** to group related plugins -2. **Implement OnRegister** for initialization that can fail -3. **Implement OnUnregister** to clean up resources -4. **Return meaningful errors** from lifecycle hooks -5. **Use standard HTTP patterns** in ServeHTTP diff --git a/docs/packages/go/framework/quickstart.md b/docs/packages/go/framework/quickstart.md deleted file mode 100644 index 5fe43b2..0000000 --- a/docs/packages/go/framework/quickstart.md +++ /dev/null @@ -1,128 +0,0 @@ -# Quick Start - -Build a simple Core application in 5 minutes. - -## Create Project - -```bash -mkdir myapp && cd myapp -go mod init myapp -``` - -## Install Dependencies - -```bash -go get github.com/Snider/Core@latest -go get github.com/wailsapp/wails/v3@latest -``` - -## Create Main File - -Create `main.go`: - -```go -package main - -import ( - "context" - "embed" - "log" - - "github.com/Snider/Core/pkg/core" - "github.com/Snider/Core/pkg/display" - "github.com/wailsapp/wails/v3/pkg/application" -) - -//go:embed all:frontend/dist -var assets embed.FS - -func main() { - // Initialize Core with display service - c, err := core.New( - core.WithAssets(assets), - core.WithService(display.NewService), - ) - if err != nil { - log.Fatal(err) - } - - // Get display service for window creation - displaySvc := core.MustServiceFor[*display.Service](c, "display") - - // Create Wails application - app := application.New(application.Options{ - Name: "My App", - Assets: application.AssetOptions{ - FS: assets, - }, - }) - - // Create main window - app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ - Title: "My App", - Width: 1200, - Height: 800, - URL: "/", - }) - - // Register display service with Wails - app.RegisterService(displaySvc) - - // Run application - if err := app.Run(); err != nil { - log.Fatal(err) - } -} -``` - -## Create Frontend - -Create a minimal frontend: - -```bash -mkdir -p frontend/dist -``` - -Create `frontend/dist/index.html`: - -```html - - - - My App - - - -

Hello from Core!

- - -``` - -## Run Development Mode - -```bash -wails3 dev -``` - -## Build for Production - -```bash -wails3 build -``` - -## Next Steps - -- [Architecture](architecture.md) - Understand how Core works -- [Display Service](../services/display.md) - Window and dialog management -- [MCP Integration](../services/mcp.md) - AI tool support diff --git a/docs/packages/go/framework/runtime.md b/docs/packages/go/framework/runtime.md deleted file mode 100644 index c5f1d88..0000000 --- a/docs/packages/go/framework/runtime.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: runtime ---- -# Service: `runtime` - -The `runtime` service provides the main entry point for the application and a helper structure for services to interact with the `Core`. - -## Types - -### `type Runtime` - -`Runtime` is the top-level container that holds the `Core` instance and the Wails application. It serves as the bridge between Wails and the Core framework. - -```go -type Runtime struct { - // Core is the central service manager - Core *Core - // app is the Wails application instance - app *application.App -} -``` - -### `type ServiceRuntime[T any]` - -`ServiceRuntime` is a generic helper struct designed to be embedded in service implementations. It provides easy access to the `Core` and service-specific options. - -```go -type ServiceRuntime[T any] struct { - core *Core - opts T -} -``` - -### `type ServiceFactory` - -`ServiceFactory` is a function type that creates a service instance. - -```go -type ServiceFactory func() (any, error) -``` - -## Functions - -### `func NewRuntime(app *application.App) (*Runtime, error)` - -`NewRuntime` creates and wires together all application services using default settings. It is the standard way to initialize the runtime. - -### `func NewWithFactories(app *application.App, factories map[string]ServiceFactory) (*Runtime, error)` - -`NewWithFactories` creates a new `Runtime` instance using a provided map of service factories. This allows for flexible, dynamic service registration. - -### `func NewServiceRuntime[T any](c *Core, opts T) *ServiceRuntime[T]` - -`NewServiceRuntime` creates a new `ServiceRuntime` instance. This is typically used in a service's factory or constructor. - -## Methods - -### `func (r *Runtime) ServiceName() string` - -`ServiceName` returns the name of the service ("Core"). This is used by Wails for service identification. - -### `func (r *Runtime) ServiceStartup(ctx context.Context, options application.ServiceOptions)` - -`ServiceStartup` delegates the startup lifecycle event to the underlying `Core`, which in turn initializes all registered services. - -### `func (r *Runtime) ServiceShutdown(ctx context.Context)` - -`ServiceShutdown` delegates the shutdown lifecycle event to the underlying `Core`. - -### `func (r *ServiceRuntime[T]) Core() *Core` - -`Core` returns the central `Core` instance, giving the service access to other services and features. - -### `func (r *ServiceRuntime[T]) Config() Config` - -`Config` returns the registered `Config` service from the `Core`. It is a convenience method for accessing configuration. diff --git a/docs/packages/go/framework/services.md b/docs/packages/go/framework/services.md deleted file mode 100644 index 1f1a4a6..0000000 --- a/docs/packages/go/framework/services.md +++ /dev/null @@ -1,119 +0,0 @@ -# Services - -Services are the building blocks of a Core application. Each service encapsulates a specific domain of functionality. - -## Creating a Service - -```go -package myservice - -import ( - "context" - "github.com/Snider/Core/pkg/core" -) - -type Service struct { - core *core.Core -} - -// Factory function for registration -func NewService(c *core.Core) (any, error) { - return &Service{core: c}, nil -} - -// Implement Startable for startup logic -func (s *Service) OnStartup(ctx context.Context) error { - // Initialize resources - return nil -} - -// Implement Stoppable for cleanup -func (s *Service) OnShutdown(ctx context.Context) error { - // Release resources - return nil -} -``` - -## Registering Services - -```go -c, err := core.New( - // Auto-discover name from package path - core.WithService(myservice.NewService), - - // Explicit name - core.WithName("custom", func(c *core.Core) (any, error) { - return &CustomService{}, nil - }), -) -``` - -## Retrieving Services - -```go -// Safe retrieval with error -svc, err := core.ServiceFor[*myservice.Service](c, "myservice") -if err != nil { - log.Printf("Service not found: %v", err) -} - -// Must retrieval (panics if not found) -svc := core.MustServiceFor[*myservice.Service](c, "myservice") -``` - -## Service Dependencies - -Services can depend on other services: - -```go -func NewOrderService(c *core.Core) (any, error) { - // Get required dependencies - userSvc := core.MustServiceFor[*user.Service](c, "user") - paymentSvc := core.MustServiceFor[*payment.Service](c, "payment") - - return &OrderService{ - users: userSvc, - payments: paymentSvc, - }, nil -} -``` - -!!! warning "Dependency Order" - Register dependencies before services that use them. Core does not automatically resolve dependency order. - -## Exposing to Frontend - -Services are automatically exposed to the frontend via Wails bindings: - -```go -// Go service method -func (s *Service) GetUser(id string) (*User, error) { - return s.db.FindUser(id) -} -``` - -```typescript -// TypeScript (auto-generated) -import { GetUser } from '@bindings/myservice/service'; - -const user = await GetUser("123"); -``` - -## Testing Services - -```go -func TestMyService(t *testing.T) { - // Create mock core - c, _ := core.New() - - // Create service - svc, err := NewService(c) - if err != nil { - t.Fatal(err) - } - - // Test methods - result := svc.(*Service).DoSomething() - assert.Equal(t, expected, result) -} -``` diff --git a/docs/packages/go/framework/webview.md b/docs/packages/go/framework/webview.md deleted file mode 100644 index 9a7f5a2..0000000 --- a/docs/packages/go/framework/webview.md +++ /dev/null @@ -1,215 +0,0 @@ -# WebView Service - -The WebView service (`pkg/webview`) provides programmatic interaction with web content in your application windows. - -## Features - -- JavaScript execution -- DOM manipulation -- Element interaction (click, type, select) -- Console message capture -- Screenshots -- Network request monitoring -- Performance metrics - -## Basic Usage - -```go -import "github.com/Snider/Core/pkg/webview" - -// Create service -wv := webview.New() - -// Set Wails app reference -wv.SetApp(app) -``` - -## JavaScript Execution - -```go -// Execute JavaScript and get result -result, err := wv.ExecJS("main", ` - document.title -`) - -// Execute complex scripts -result, err := wv.ExecJS("main", ` - const items = document.querySelectorAll('.item'); - Array.from(items).map(el => el.textContent); -`) -``` - -## DOM Interaction - -### Click Element - -```go -err := wv.Click("main", "#submit-button") -err := wv.Click("main", ".nav-link:first-child") -``` - -### Type Text - -```go -err := wv.Type("main", "#search-input", "hello world") -``` - -### Select Option - -```go -err := wv.Select("main", "#country-select", "US") -``` - -### Check/Uncheck - -```go -err := wv.Check("main", "#agree-checkbox", true) -``` - -### Hover - -```go -err := wv.Hover("main", ".dropdown-trigger") -``` - -### Scroll - -```go -// Scroll to element -err := wv.Scroll("main", "#section-3", 0, 0) - -// Scroll by coordinates -err := wv.Scroll("main", "", 0, 500) -``` - -## Element Information - -### Query Selector - -```go -elements, err := wv.QuerySelector("main", ".list-item") -``` - -### Element Info - -```go -info, err := wv.GetElementInfo("main", "#user-card") -// Returns: tag, id, classes, text, attributes, bounds -``` - -### Computed Styles - -```go -styles, err := wv.GetComputedStyle("main", ".button", - []string{"color", "background-color", "font-size"}) -``` - -### DOM Tree - -```go -tree, err := wv.GetDOMTree("main", 5) // max depth 5 -``` - -## Console Messages - -```go -// Setup console listener -wv.SetupConsoleListener() - -// Inject capture script -wv.InjectConsoleCapture("main") - -// Get messages -messages := wv.GetConsoleMessages("all", 100) -messages := wv.GetConsoleMessages("error", 50) - -// Clear buffer -wv.ClearConsole() - -// Get errors only -errors := wv.GetErrors(50) -``` - -## Screenshots - -```go -// Full page screenshot (base64 PNG) -data, err := wv.Screenshot("main") - -// Element screenshot -data, err := wv.ScreenshotElement("main", "#chart") - -// Export as PDF -pdfData, err := wv.ExportToPDF("main", map[string]any{ - "margin": 20, -}) -``` - -## Page Information - -```go -// Get current URL -url, err := wv.GetURL("main") - -// Get page title -title, err := wv.GetTitle("main") - -// Get page source -source, err := wv.GetPageSource("main") - -// Navigate -err := wv.Navigate("main", "https://example.com") -``` - -## Network Monitoring - -```go -// Inject network interceptor -wv.InjectNetworkInterceptor("main") - -// Get captured requests -requests, err := wv.GetNetworkRequests("main", 100) - -// Clear request log -wv.ClearNetworkRequests("main") -``` - -## Performance Metrics - -```go -metrics, err := wv.GetPerformance("main") -// Returns: loadTime, domContentLoaded, firstPaint, etc. -``` - -## Resource Listing - -```go -resources, err := wv.GetResources("main") -// Returns: scripts, stylesheets, images, fonts, etc. -``` - -## Visual Debugging - -```go -// Highlight element temporarily -err := wv.Highlight("main", "#target-element", 2000) // 2 seconds -``` - -## Window Listing - -```go -windows := wv.ListWindows() -for _, w := range windows { - fmt.Println(w.Name) -} -``` - -## Frontend Usage - -The WebView service is primarily used server-side for: - -- Automated testing -- AI assistant interactions (via MCP) -- Scripted UI interactions - -For normal frontend development, use standard DOM APIs directly. diff --git a/docs/packages/go/framework/workspace.md b/docs/packages/go/framework/workspace.md deleted file mode 100644 index e57c9a1..0000000 --- a/docs/packages/go/framework/workspace.md +++ /dev/null @@ -1,152 +0,0 @@ -# Workspace Service - -The Workspace service (`pkg/workspace`) manages isolated user workspaces with encrypted storage and PGP key pairs. - -## Features - -- Isolated workspace environments -- PGP key pair generation per workspace -- Encrypted workspace identification -- File operations within workspace context -- Multiple workspace support - -## Basic Usage - -```go -import "github.com/Snider/Core/pkg/workspace" - -// With IO medium (standalone) -medium, _ := local.New("/app/workspaces") -ws, err := workspace.New(medium) - -// With Core framework (recommended) -c, _ := core.New( - core.WithService(workspace.Register), -) -ws := core.MustServiceFor[*workspace.Service](c, "workspace") -``` - -## Creating Workspaces - -```go -// Create a new encrypted workspace -workspaceID, err := ws.CreateWorkspace("my-project", "secure-password") -// Returns obfuscated workspace ID - -// Workspace structure created: -// workspaces/ -// / -// config/ -// log/ -// data/ -// files/ -// keys/ -// key.pub (PGP public key) -// key.priv (PGP private key) -``` - -## Switching Workspaces - -```go -// Switch to a workspace -err := ws.SwitchWorkspace(workspaceID) - -// Switch to default workspace -err := ws.SwitchWorkspace("default") -``` - -## Workspace File Operations - -```go -// Write file to active workspace -err := ws.WorkspaceFileSet("config/settings.json", jsonData) - -// Read file from active workspace -content, err := ws.WorkspaceFileGet("config/settings.json") -``` - -## Listing Workspaces - -```go -// Get all workspace IDs -workspaces := ws.ListWorkspaces() -for _, id := range workspaces { - fmt.Println(id) -} -``` - -## Active Workspace - -```go -// Get current workspace info -active := ws.ActiveWorkspace() -if active != nil { - fmt.Println("Name:", active.Name) - fmt.Println("Path:", active.Path) -} -``` - -## Workspace Structure - -Each workspace contains: - -| Directory | Purpose | -|-----------|---------| -| `config/` | Workspace configuration files | -| `log/` | Workspace logs | -| `data/` | Application data | -| `files/` | User files | -| `keys/` | PGP key pair | - -## Security Model - -Workspaces use a two-level hashing scheme: - -1. **Real Name**: Hash of the identifier -2. **Workspace ID**: Hash of `workspace/{real_name}` - -This prevents workspace enumeration while allowing consistent access. - -## IPC Events - -The workspace service responds to IPC messages: - -```go -// Switch workspace via IPC -c.ACTION(core.Message{ - Type: "workspace.switch_workspace", - Data: map[string]any{ - "name": workspaceID, - }, -}) -``` - -## Frontend Usage (TypeScript) - -```typescript -import { - CreateWorkspace, - SwitchWorkspace, - WorkspaceFileGet, - WorkspaceFileSet, - ListWorkspaces, - ActiveWorkspace -} from '@bindings/workspace/service'; - -// Create workspace -const wsId = await CreateWorkspace("my-project", "password"); - -// Switch workspace -await SwitchWorkspace(wsId); - -// Read/write files -const config = await WorkspaceFileGet("config/app.json"); -await WorkspaceFileSet("config/app.json", JSON.stringify(newConfig)); - -// List all workspaces -const workspaces = await ListWorkspaces(); - -// Get active workspace -const active = await ActiveWorkspace(); -console.log(`Current: ${active.Name} at ${active.Path}`); -``` diff --git a/docs/packages/go/getting-started.md b/docs/packages/go/getting-started.md new file mode 100644 index 0000000..ad374ab --- /dev/null +++ b/docs/packages/go/getting-started.md @@ -0,0 +1,191 @@ +# Getting Started + +This guide walks you through installing Core and running your first build. + +## Prerequisites + +Before installing Core, ensure you have: + +| Tool | Minimum Version | Check Command | +|------|-----------------|---------------| +| Go | 1.23+ | `go version` | +| Git | 2.30+ | `git --version` | + +Optional (for specific features): + +| Tool | Required For | Install | +|------|--------------|---------| +| `gh` | GitHub integration (`core dev issues`, `core dev reviews`) | [cli.github.com](https://cli.github.com) | +| Docker | Container builds | [docker.com](https://docker.com) | +| `task` | Task automation | `go install github.com/go-task/task/v3/cmd/task@latest` | + +## Installation + +### Option 1: Go Install (Recommended) + +```bash +# Install latest release +go install github.com/host-uk/core/cmd/core@latest + +# Verify installation +core doctor +``` + +If `core: command not found`, add Go's bin directory to your PATH: + +```bash +export PATH="$PATH:$(go env GOPATH)/bin" +``` + +### Option 2: Download Binary + +Download pre-built binaries from [GitHub Releases](https://github.com/host-uk/core/releases): + +```bash +# macOS (Apple Silicon) +curl -Lo core https://github.com/host-uk/core/releases/latest/download/core-darwin-arm64 +chmod +x core +sudo mv core /usr/local/bin/ + +# macOS (Intel) +curl -Lo core https://github.com/host-uk/core/releases/latest/download/core-darwin-amd64 +chmod +x core +sudo mv core /usr/local/bin/ + +# Linux (x86_64) +curl -Lo core https://github.com/host-uk/core/releases/latest/download/core-linux-amd64 +chmod +x core +sudo mv core /usr/local/bin/ +``` + +### Option 3: Build from Source + +```bash +# Clone repository +git clone https://github.com/host-uk/core.git +cd core + +# Build with Task (recommended) +task cli:build +# Binary at ./bin/core + +# Or build with Go directly +CGO_ENABLED=0 go build -o core ./cmd/core/ +sudo mv core /usr/local/bin/ +``` + +## Your First Build + +### 1. Navigate to a Go Project + +```bash +cd ~/Code/my-go-project +``` + +### 2. Initialise Configuration + +```bash +core setup +``` + +This detects your project type and creates configuration files in `.core/`: +- `build.yaml` - Build settings +- `release.yaml` - Release configuration +- `test.yaml` - Test commands + +### 3. Build + +```bash +core build +``` + +Output appears in `dist/`: + +``` +dist/ +├── my-project-darwin-arm64.tar.gz +├── my-project-linux-amd64.tar.gz +└── CHECKSUMS.txt +``` + +### 4. Cross-Compile (Optional) + +```bash +core build --targets linux/amd64,linux/arm64,darwin/arm64,windows/amd64 +``` + +## Your First Release + +Releases are **safe by default** - Core runs in dry-run mode unless you explicitly confirm. + +### 1. Preview + +```bash +core ci +``` + +This shows what would be published without actually publishing. + +### 2. Publish + +```bash +core ci --we-are-go-for-launch +``` + +This creates a GitHub release with your built artifacts. + +## Multi-Repo Workflow + +If you work with multiple repositories (like the host-uk ecosystem): + +### 1. Clone All Repositories + +```bash +mkdir host-uk && cd host-uk +core setup +``` + +Select packages in the interactive wizard. + +### 2. Check Status + +```bash +core dev health +# Output: "18 repos │ clean │ synced" +``` + +### 3. Work Across Repos + +```bash +core dev work --status # See status table +core dev work # Commit and push all dirty repos +``` + +## Next Steps + +| Task | Command | Documentation | +|------|---------|---------------| +| Run tests | `core go test` | [go/test](cmd/go/test/) | +| Format code | `core go fmt --fix` | [go/fmt](cmd/go/fmt/) | +| Lint code | `core go lint` | [go/lint](cmd/go/lint/) | +| PHP development | `core php dev` | [php](cmd/php/) | +| View all commands | `core --help` | [cmd](cmd/) | + +## Getting Help + +```bash +# Check environment +core doctor + +# Command help +core --help + +# Full documentation +https://github.com/host-uk/core/tree/main/docs +``` + +## See Also + +- [Configuration](configuration.md) - All config options +- [Workflows](workflows.md) - Common task sequences +- [Troubleshooting](troubleshooting.md) - When things go wrong diff --git a/docs/packages/go/glossary.md b/docs/packages/go/glossary.md new file mode 100644 index 0000000..ea9d280 --- /dev/null +++ b/docs/packages/go/glossary.md @@ -0,0 +1,112 @@ +# Glossary + +Definitions of terms used throughout Core CLI documentation. + +## A + +### Artifact +A file produced by a build, typically a binary, archive, or checksum file. Artifacts are stored in the `dist/` directory and published during releases. + +## C + +### CGO +Go's mechanism for calling C code. Core disables CGO by default (`CGO_ENABLED=0`) to produce statically-linked binaries that don't depend on system libraries. + +### Changelog +Automatically generated list of changes between releases, created from conventional commit messages. Configure in `.core/release.yaml`. + +### Conventional Commits +A commit message format: `type(scope): description`. Types include `feat`, `fix`, `docs`, `chore`. Core uses this to generate changelogs. + +## D + +### Dry-run +A mode where commands show what they would do without actually doing it. `core ci` runs in dry-run mode by default for safety. + +## F + +### Foundation Package +A core package with no dependencies on other packages. Examples: `core-php`, `core-devops`. These form the base of the dependency tree. + +### FrankenPHP +A modern PHP application server used by `core php dev`. Combines PHP with Caddy for high-performance serving. + +## G + +### `gh` +The GitHub CLI tool. Required for commands that interact with GitHub: `core dev issues`, `core dev reviews`, `core dev ci`. + +## L + +### LinuxKit +A toolkit for building lightweight, immutable Linux distributions. Core can build LinuxKit images via `core build --type linuxkit`. + +## M + +### Module (Go) +A collection of Go packages with a `go.mod` file. Core's Go commands operate on modules. + +### Module (Package) +A host-uk package that depends on foundation packages. Examples: `core-tenant`, `core-admin`. Compare with **Foundation Package** and **Product**. + +## P + +### Package +An individual repository in the host-uk ecosystem. Packages are defined in `repos.yaml` and managed with `core pkg` commands. + +### Package Index +The `repos.yaml` file that lists all packages in a workspace. Contains metadata like dependencies, type, and description. + +### Product +A user-facing application package. Examples: `core-bio`, `core-social`. Products depend on foundation and module packages. + +### Publisher +A release target configured in `.core/release.yaml`. Types include `github`, `docker`, `npm`, `homebrew`, `linuxkit`. + +## R + +### Registry (Docker/npm) +A remote repository for container images or npm packages. Core can publish to registries during releases. + +### `repos.yaml` +The package index file defining all repositories in a workspace. Used by multi-repo commands like `core dev work`. + +## S + +### SDK +Software Development Kit. Core can generate API client SDKs from OpenAPI specs via `core build sdk`. + +## T + +### Target +A build target specified as `os/arch`, e.g., `linux/amd64`, `darwin/arm64`. Use `--targets` flag to specify. + +## W + +### Wails +A framework for building desktop applications with Go backends and web frontends. Core detects Wails projects and uses appropriate build commands. + +### Workspace (Go) +A Go 1.18+ feature for working with multiple modules simultaneously. Managed via `core go work` commands. + +### Workspace (Multi-repo) +A directory containing multiple packages from `repos.yaml`. Created via `core setup` and managed with `core dev` commands. + +## Symbols + +### `.core/` +Directory containing project configuration files: +- `build.yaml` - Build settings +- `release.yaml` - Release targets +- `test.yaml` - Test configuration +- `linuxkit/` - LinuxKit templates + +### `--we-are-go-for-launch` +Flag to disable dry-run mode and actually publish a release. Named as a deliberate friction to prevent accidental releases. + +--- + +## See Also + +- [Configuration](configuration.md) - Config file reference +- [Getting Started](getting-started.md) - First-time setup diff --git a/docs/packages/go/index.md b/docs/packages/go/index.md index 7d2dabb..83f647e 100644 --- a/docs/packages/go/index.md +++ b/docs/packages/go/index.md @@ -1,92 +1,65 @@ -# Go Framework +# Core CLI -Core is a native application framework for Go, built on Wails v3. It provides dependency injection, service lifecycle management, IPC messaging, and a unified CLI for building, releasing, and deploying applications. +Core is a unified CLI for the host-uk ecosystem - build, release, and deploy Go, Wails, PHP, and container workloads. ## Installation ```bash -# Go install +# Via Go (recommended) go install github.com/host-uk/core/cmd/core@latest -# Or download from releases -curl -fsSL https://github.com/host-uk/core/releases/latest/download/core-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/').tar.gz | tar -xzf - -C /usr/local/bin +# Or download binary from releases +curl -Lo core https://github.com/host-uk/core/releases/latest/download/core-$(go env GOOS)-$(go env GOARCH) +chmod +x core && sudo mv core /usr/local/bin/ + +# Verify +core doctor ``` -## Commands +See [Getting Started](getting-started.md) for all installation options including building from source. -### Build & Release +## Command Reference + +See [cmd/](cmd/) for full command documentation. | Command | Description | |---------|-------------| -| [`core build`](cmd/build.md) | Build Go, Wails, Docker, and LinuxKit projects | -| [`core release`](cmd/release.md) | Build and publish to GitHub, npm, Homebrew, etc. | -| [`core sdk`](cmd/sdk.md) | Generate and manage API SDKs | - -### Containers - -| Command | Description | -|---------|-------------| -| [`core run`](cmd/run.md) | Run LinuxKit images with qemu/hyperkit | -| `core ps` | List running containers | -| `core stop` | Stop running containers | -| `core logs` | View container logs | -| `core exec` | Execute commands in containers | -| [`core templates`](cmd/templates.md) | Manage LinuxKit templates | - -### Development - -| Command | Description | -|---------|-------------| -| [`core dev`](cmd/dev.md) | Portable development environment (100+ tools) | -| [`core php`](cmd/php.md) | Laravel/PHP development tools | -| [`core test`](cmd/test.md) | Run tests with coverage reporting | -| [`core doctor`](cmd/doctor.md) | Check development environment | - -### GitHub & Multi-Repo - -| Command | Description | -|---------|-------------| -| [`core search`](cmd/search.md) | Search GitHub for repositories | -| [`core install`](cmd/search.md) | Clone a repository from GitHub | -| [`core setup`](cmd/setup.md) | Clone all repos from registry | -| [`core work`](cmd/work.md) | Multi-repo git operations | -| [`core health`](cmd/work.md) | Quick health check across repos | -| [`core issues`](cmd/work.md) | List open issues across repos | -| [`core reviews`](cmd/work.md) | List PRs needing review | -| [`core ci`](cmd/work.md) | Check CI status across repos | - -### Documentation - -| Command | Description | -|---------|-------------| -| [`core docs`](cmd/docs.md) | Documentation management | - -### Integrations - -| Integration | Description | -|-------------|-------------| -| [Claude Code Skill](cmd/skill.md) | AI assistant integration for command guidance | +| [go](cmd/go/) | Go development (test, fmt, lint, cov) | +| [php](cmd/php/) | Laravel/PHP development | +| [build](cmd/build/) | Build Go, Wails, Docker, LinuxKit projects | +| [ci](cmd/ci/) | Publish releases (dry-run by default) | +| [sdk](cmd/sdk/) | SDK generation and validation | +| [dev](cmd/dev/) | Multi-repo workflow + dev environment | +| [pkg](cmd/pkg/) | Package search and install | +| [vm](cmd/vm/) | LinuxKit VM management | +| [docs](cmd/docs/) | Documentation management | +| [setup](cmd/setup/) | Clone repos from registry | +| [doctor](cmd/doctor/) | Check development environment | ## Quick Start ```bash -# Build a Go project -core build +# Go development +core go test # Run tests +core go test --coverage # With coverage +core go fmt # Format code +core go lint # Lint code -# Build for specific targets +# Build +core build # Auto-detect and build core build --targets linux/amd64,darwin/arm64 -# Release to GitHub -core release +# Release (dry-run by default) +core ci # Preview release +core ci --we-are-go-for-launch # Actually publish -# Release to multiple package managers -core release # Publishes to all configured targets +# Multi-repo workflow +core dev work # Status + commit + push +core dev work --status # Just show status -# Start PHP dev environment -core php dev - -# Run a LinuxKit image -core run server.iso +# PHP development +core php dev # Start dev environment +core php test # Run tests ``` ## Configuration @@ -98,35 +71,28 @@ Core uses `.core/` directory for project configuration: ├── release.yaml # Release targets and settings ├── build.yaml # Build configuration (optional) └── linuxkit/ # LinuxKit templates - └── server.yml ``` -## Documentation +And `repos.yaml` in workspace root for multi-repo management. -### Command Reference -- [Build](cmd/build.md) - Cross-platform builds with code signing -- [Release](cmd/release.md) - Publishing to package managers -- [SDK](cmd/sdk.md) - Generate API clients from OpenAPI -- [Run](cmd/run.md) - Container management -- [Templates](cmd/templates.md) - LinuxKit templates -- [Dev](cmd/dev.md) - Portable development environment -- [PHP](cmd/php.md) - Laravel development -- [Test](cmd/test.md) - Run tests with coverage -- [Doctor](cmd/doctor.md) - Environment check -- [Search & Install](cmd/search.md) - GitHub integration -- [Setup](cmd/setup.md) - Clone repos from registry -- [Work](cmd/work.md) - Multi-repo operations -- [Docs](cmd/docs.md) - Documentation management -- [Claude Code Skill](cmd/skill.md) - AI assistant integration +## Guides + +- [Getting Started](getting-started.md) - Installation and first steps +- [Workflows](workflows.md) - Common task sequences +- [Troubleshooting](troubleshooting.md) - When things go wrong +- [Migration](migration.md) - Moving from legacy tools + +## Reference -### Reference - [Configuration](configuration.md) - All config options -- [Examples](examples/) - Sample configurations +- [Glossary](glossary.md) - Term definitions -## Framework +## Claude Code Skill -Core also provides a Go framework for building desktop applications: +Install the skill to teach Claude Code how to use the Core CLI: -- [Framework Overview](framework/overview.md) -- [Services](framework/services.md) -- [Lifecycle](framework/lifecycle.md) +```bash +curl -fsSL https://raw.githubusercontent.com/host-uk/core/main/.claude/skills/core/install.sh | bash +``` + +See [skill/](skill/) for details. diff --git a/docs/packages/go/migration.md b/docs/packages/go/migration.md new file mode 100644 index 0000000..e5c4606 --- /dev/null +++ b/docs/packages/go/migration.md @@ -0,0 +1,233 @@ +# Migration Guide + +Migrating from legacy scripts and tools to Core CLI. + +## From push-all.sh + +The `push-all.sh` script has been replaced by `core dev` commands. + +| Legacy | Core CLI | Notes | +|--------|----------|-------| +| `./push-all.sh --status` | `core dev work --status` | Status table | +| `./push-all.sh --commit` | `core dev commit` | Commit dirty repos | +| `./push-all.sh` | `core dev work` | Full workflow | + +### Quick Migration + +```bash +# Instead of +./push-all.sh --status + +# Use +core dev work --status +``` + +### New Features + +Core CLI adds features not available in the legacy script: + +```bash +# Quick health summary +core dev health +# Output: "18 repos │ clean │ synced" + +# Pull repos that are behind +core dev pull + +# GitHub integration +core dev issues # List open issues +core dev reviews # List PRs needing review +core dev ci # Check CI status + +# Dependency analysis +core dev impact core-php # What depends on core-php? +``` + +--- + +## From Raw Go Commands + +Core wraps Go commands with enhanced defaults and output. + +| Raw Command | Core CLI | Benefits | +|-------------|----------|----------| +| `go test ./...` | `core go test` | Filters warnings, sets CGO_ENABLED=0 | +| `go test -coverprofile=...` | `core go cov` | HTML reports, thresholds | +| `gofmt -w .` | `core go fmt --fix` | Uses goimports if available | +| `golangci-lint run` | `core go lint` | Consistent interface | +| `go build` | `core build` | Cross-compile, sign, archive | + +### Why Use Core? + +```bash +# Raw go test shows linker warnings on macOS +go test ./... +# ld: warning: -no_pie is deprecated... + +# Core filters noise +core go test +# PASS (clean output) +``` + +### Environment Setup + +Core automatically sets: +- `CGO_ENABLED=0` - Static binaries +- `MACOSX_DEPLOYMENT_TARGET=26.0` - Suppress macOS warnings +- Colour output for coverage reports + +--- + +## From Raw PHP Commands + +Core orchestrates Laravel development services. + +| Raw Command | Core CLI | Benefits | +|-------------|----------|----------| +| `php artisan serve` | `core php dev` | Adds Vite, Horizon, Reverb, Redis | +| `./vendor/bin/pest` | `core php test` | Auto-detects test runner | +| `./vendor/bin/pint` | `core php fmt --fix` | Consistent interface | +| Manual Coolify deploy | `core php deploy` | Tracked, scriptable | + +### Development Server Comparison + +```bash +# Raw: Start each service manually +php artisan serve & +npm run dev & +php artisan horizon & +php artisan reverb:start & + +# Core: One command +core php dev +# Starts all services, shows unified logs +``` + +--- + +## From goreleaser + +Core's release system is simpler than goreleaser for host-uk projects. + +| goreleaser | Core CLI | +|------------|----------| +| `.goreleaser.yaml` | `.core/release.yaml` | +| `goreleaser release --snapshot` | `core ci` (dry-run) | +| `goreleaser release` | `core ci --we-are-go-for-launch` | + +### Configuration Migration + +**goreleaser:** +```yaml +builds: + - main: ./cmd/app + goos: [linux, darwin, windows] + goarch: [amd64, arm64] + +archives: + - format: tar.gz + files: [LICENSE, README.md] + +release: + github: + owner: host-uk + name: app +``` + +**Core:** +```yaml +version: 1 + +project: + name: app + repository: host-uk/app + +targets: + - os: linux + arch: amd64 + - os: darwin + arch: arm64 + +publishers: + - type: github +``` + +### Key Differences + +1. **Separate build and release** - Core separates `core build` from `core ci` +2. **Safe by default** - `core ci` is dry-run unless `--we-are-go-for-launch` +3. **Simpler config** - Fewer options, sensible defaults + +--- + +## From Manual Git Operations + +Core automates multi-repo git workflows. + +| Manual | Core CLI | +|--------|----------| +| `cd repo1 && git status && cd ../repo2 && ...` | `core dev work --status` | +| Check each repo for uncommitted changes | `core dev health` | +| Commit each repo individually | `core dev commit` | +| Push each repo individually | `core dev push` | + +### Example: Committing Across Repos + +**Manual:** +```bash +cd core-php +git add -A +git commit -m "feat: add feature" +cd ../core-tenant +git add -A +git commit -m "feat: use new feature" +# ... repeat for each repo +``` + +**Core:** +```bash +core dev commit +# Interactive: reviews changes, suggests messages +# Adds Co-Authored-By automatically +``` + +--- + +## Deprecated Commands + +These commands have been removed or renamed: + +| Deprecated | Replacement | Version | +|------------|-------------|---------| +| `core sdk generate` | `core build sdk` | v0.5.0 | +| `core dev task*` | `core ai task*` | v0.8.0 | +| `core release` | `core ci` | v0.6.0 | + +--- + +## Version Compatibility + +| Core Version | Go Version | Breaking Changes | +|--------------|------------|------------------| +| v1.0.0+ | 1.23+ | Stable API | +| v0.8.0 | 1.22+ | Task commands moved to `ai` | +| v0.6.0 | 1.22+ | Release command renamed to `ci` | +| v0.5.0 | 1.21+ | SDK generation moved to `build sdk` | + +--- + +## Getting Help + +If you encounter issues during migration: + +1. Check [Troubleshooting](troubleshooting.md) +2. Run `core doctor` to verify setup +3. Use `--help` on any command: `core dev work --help` + +--- + +## See Also + +- [Getting Started](getting-started.md) - Fresh installation +- [Workflows](workflows.md) - Common task sequences +- [Configuration](configuration.md) - Config file reference diff --git a/docs/packages/go/skill/index.md b/docs/packages/go/skill/index.md new file mode 100644 index 0000000..40ae3ad --- /dev/null +++ b/docs/packages/go/skill/index.md @@ -0,0 +1,35 @@ +# Claude Code Skill + +The `core` skill teaches Claude Code how to use the Core CLI effectively. + +## Installation + +```bash +curl -fsSL https://raw.githubusercontent.com/host-uk/core/main/.claude/skills/core/install.sh | bash +``` + +Or if you have the repo cloned: + +```bash +./.claude/skills/core/install.sh +``` + +## What it does + +Once installed, Claude Code will: + +- Auto-invoke when working in host-uk repositories +- Use `core` commands instead of raw `go`/`php`/`git` commands +- Follow the correct patterns for testing, building, and releasing + +## Manual invocation + +Type `/core` in Claude Code to invoke the skill manually. + +## Updating + +Re-run the install command to update to the latest version. + +## Location + +Skills are installed to `~/.claude/skills/core/SKILL.md`. diff --git a/docs/packages/go/troubleshooting.md b/docs/packages/go/troubleshooting.md new file mode 100644 index 0000000..c075f3a --- /dev/null +++ b/docs/packages/go/troubleshooting.md @@ -0,0 +1,332 @@ +# Troubleshooting + +Common issues and how to resolve them. + +## Installation Issues + +### "command not found: core" + +**Cause:** Go's bin directory is not in your PATH. + +**Fix:** + +```bash +# Add to ~/.bashrc or ~/.zshrc +export PATH="$PATH:$(go env GOPATH)/bin" + +# Then reload +source ~/.bashrc # or ~/.zshrc +``` + +### "go: module github.com/host-uk/core: no matching versions" + +**Cause:** Go module proxy hasn't cached the latest version yet. + +**Fix:** + +```bash +# Bypass proxy +GOPROXY=direct go install github.com/host-uk/core/cmd/core@latest +``` + +--- + +## Build Issues + +### "no Go files in..." + +**Cause:** Core couldn't find a main package to build. + +**Fix:** + +1. Check you're in the correct directory +2. Ensure `.core/build.yaml` has the correct `main` path: + +```yaml +project: + main: ./cmd/myapp # Path to main package +``` + +### "CGO_ENABLED=1 but no C compiler" + +**Cause:** Build requires CGO but no C compiler is available. + +**Fix:** + +```bash +# Option 1: Disable CGO (if not needed) +core build # Core disables CGO by default + +# Option 2: Install a C compiler +# macOS +xcode-select --install + +# Ubuntu/Debian +sudo apt install build-essential + +# Windows +# Install MinGW or use WSL +``` + +### Build succeeds but binary doesn't run + +**Cause:** Built for wrong architecture. + +**Fix:** + +```bash +# Check what you built +file dist/myapp-* + +# Build for your current platform +core build --targets $(go env GOOS)/$(go env GOARCH) +``` + +--- + +## Release Issues + +### "dry-run mode, use --we-are-go-for-launch to publish" + +**This is expected behaviour.** Core runs in dry-run mode by default for safety. + +**To actually publish:** + +```bash +core ci --we-are-go-for-launch +``` + +### "failed to create release: 401 Unauthorized" + +**Cause:** GitHub token missing or invalid. + +**Fix:** + +```bash +# Authenticate with GitHub CLI +gh auth login + +# Or set token directly +export GITHUB_TOKEN=ghp_xxxxxxxxxxxx +``` + +### "no artifacts found in dist/" + +**Cause:** You need to build before releasing. + +**Fix:** + +```bash +# Build first +core build + +# Then release +core ci --we-are-go-for-launch +``` + +### "tag already exists" + +**Cause:** Trying to release a version that's already been released. + +**Fix:** + +1. Update version in your code/config +2. Or delete the existing tag (if intentional): + +```bash +git tag -d v1.0.0 +git push origin :refs/tags/v1.0.0 +``` + +--- + +## Multi-Repo Issues + +### "repos.yaml not found" + +**Cause:** Core can't find the package registry. + +**Fix:** + +Core looks for `repos.yaml` in: +1. Current directory +2. Parent directories (walking up to root) +3. `~/Code/host-uk/repos.yaml` +4. `~/.config/core/repos.yaml` + +Either: +- Run commands from a directory with `repos.yaml` +- Use `--registry /path/to/repos.yaml` +- Run `core setup` to bootstrap a new workspace + +### "failed to clone: Permission denied (publickey)" + +**Cause:** SSH key not configured for GitHub. + +**Fix:** + +```bash +# Check SSH connection +ssh -T git@github.com + +# If that fails, add your key +ssh-add ~/.ssh/id_ed25519 + +# Or configure SSH +# See: https://docs.github.com/en/authentication/connecting-to-github-with-ssh +``` + +### "repository not found" during setup + +**Cause:** You don't have access to the repository, or it doesn't exist. + +**Fix:** + +1. Check you're authenticated: `gh auth status` +2. Verify the repo exists and you have access +3. For private repos, ensure your token has `repo` scope + +--- + +## GitHub Integration Issues + +### "gh: command not found" + +**Cause:** GitHub CLI not installed. + +**Fix:** + +```bash +# macOS +brew install gh + +# Ubuntu/Debian +sudo apt install gh + +# Windows +winget install GitHub.cli + +# Then authenticate +gh auth login +``` + +### "core dev issues" shows nothing + +**Possible causes:** + +1. No open issues exist +2. Not authenticated with GitHub +3. Not in a directory with `repos.yaml` + +**Fix:** + +```bash +# Check auth +gh auth status + +# Check you're in a workspace +ls repos.yaml + +# Show all issues including closed +core dev issues --all +``` + +--- + +## PHP Issues + +### "frankenphp: command not found" + +**Cause:** FrankenPHP not installed. + +**Fix:** + +```bash +# macOS +brew install frankenphp + +# Or use Docker +core php dev --docker +``` + +### "core php dev" exits immediately + +**Cause:** Usually a port conflict or missing dependency. + +**Fix:** + +```bash +# Check if port 8000 is in use +lsof -i :8000 + +# Try a different port +core php dev --port 9000 + +# Check logs for errors +core php logs +``` + +--- + +## Performance Issues + +### Commands are slow + +**Possible causes:** + +1. Large number of repositories +2. Network latency to GitHub +3. Go module downloads + +**Fix:** + +```bash +# For multi-repo commands, use health for quick check +core dev health # Fast summary + +# Instead of +core dev work --status # Full table (slower) + +# Pre-download Go modules +go mod download +``` + +--- + +## Getting More Help + +### Enable Verbose Output + +Most commands support `-v` or `--verbose`: + +```bash +core build -v +core go test -v +``` + +### Check Environment + +```bash +core doctor +``` + +This verifies all required tools are installed and configured. + +### Report Issues + +If you've found a bug: + +1. Check existing issues: https://github.com/host-uk/core/issues +2. Create a new issue with: + - Core version (`core --version`) + - OS and architecture (`go env GOOS GOARCH`) + - Command that failed + - Full error output + +--- + +## See Also + +- [Getting Started](getting-started.md) - Installation and first steps +- [Configuration](configuration.md) - Config file reference +- [doctor](cmd/doctor/) - Environment verification diff --git a/docs/packages/go/workflows.md b/docs/packages/go/workflows.md new file mode 100644 index 0000000..96b0c9f --- /dev/null +++ b/docs/packages/go/workflows.md @@ -0,0 +1,334 @@ +# Workflows + +Common end-to-end workflows for Core CLI. + +## Go Project: Build and Release + +Complete workflow from code to GitHub release. + +```bash +# 1. Run tests +core go test + +# 2. Check coverage +core go cov --threshold 80 + +# 3. Format and lint +core go fmt --fix +core go lint + +# 4. Build for all platforms +core build --targets linux/amd64,linux/arm64,darwin/arm64,windows/amd64 + +# 5. Preview release (dry-run) +core ci + +# 6. Publish +core ci --we-are-go-for-launch +``` + +**Output structure:** + +``` +dist/ +├── myapp-darwin-arm64.tar.gz +├── myapp-linux-amd64.tar.gz +├── myapp-linux-arm64.tar.gz +├── myapp-windows-amd64.zip +└── CHECKSUMS.txt +``` + +--- + +## PHP Project: Development to Deployment + +Local development through to production deployment. + +```bash +# 1. Start development environment +core php dev + +# 2. Run tests (in another terminal) +core php test --parallel + +# 3. Check code quality +core php fmt --fix +core php analyse + +# 4. Deploy to staging +core php deploy --staging --wait + +# 5. Verify staging +# (manual testing) + +# 6. Deploy to production +core php deploy --wait + +# 7. Monitor +core php deploy:status +``` + +**Rollback if needed:** + +```bash +core php deploy:rollback +``` + +--- + +## Multi-Repo: Daily Workflow + +Working across the host-uk monorepo. + +### Morning: Sync Everything + +```bash +# Quick health check +core dev health + +# Pull all repos that are behind +core dev pull --all + +# Check for issues assigned to you +core dev issues --assignee @me +``` + +### During Development + +```bash +# Work on code... + +# Check status across all repos +core dev work --status + +# Commit changes (Claude-assisted messages) +core dev commit + +# Push when ready +core dev push +``` + +### End of Day + +```bash +# Full workflow: status → commit → push +core dev work + +# Check CI status +core dev ci + +# Review any failed builds +core dev ci --failed +``` + +--- + +## New Developer: Environment Setup + +First-time setup for a new team member. + +```bash +# 1. Verify prerequisites +core doctor + +# 2. Create workspace directory +mkdir ~/Code/host-uk && cd ~/Code/host-uk + +# 3. Bootstrap workspace (interactive) +core setup + +# 4. Select packages in wizard +# Use arrow keys, space to select, enter to confirm + +# 5. Verify setup +core dev health + +# 6. Start working +core dev work --status +``` + +--- + +## CI Pipeline: Automated Build + +Example GitHub Actions workflow. + +```yaml +# .github/workflows/release.yml +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: '1.23' + + - name: Install Core + run: go install github.com/host-uk/core/cmd/core@latest + + - name: Build + run: core build --ci + + - name: Release + run: core ci --we-are-go-for-launch + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + +--- + +## SDK Generation: API Client Updates + +Generate SDK clients when API changes. + +```bash +# 1. Validate OpenAPI spec +core sdk validate + +# 2. Check for breaking changes +core sdk diff --base v1.0.0 + +# 3. Generate SDKs +core build sdk + +# 4. Review generated code +git diff + +# 5. Commit if satisfied +git add -A && git commit -m "chore: regenerate SDK clients" +``` + +--- + +## Dependency Update: Cross-Repo Change + +When updating a shared package like `core-php`. + +```bash +# 1. Make changes in core-php +cd ~/Code/host-uk/core-php +# ... edit code ... + +# 2. Run tests +core go test # or core php test + +# 3. Check what depends on core-php +core dev impact core-php + +# Output: +# core-tenant (direct) +# core-admin (via core-tenant) +# core-api (direct) +# ... + +# 4. Commit core-php changes +core dev commit + +# 5. Update dependent packages +cd ~/Code/host-uk +for pkg in core-tenant core-admin core-api; do + cd $pkg + composer update host-uk/core-php + core php test + cd .. +done + +# 6. Commit all updates +core dev work +``` + +--- + +## Hotfix: Emergency Production Fix + +Fast path for critical fixes. + +```bash +# 1. Create hotfix branch +git checkout -b hotfix/critical-bug main + +# 2. Make fix +# ... edit code ... + +# 3. Test +core go test --run TestCriticalPath + +# 4. Build +core build + +# 5. Preview release +core ci --prerelease + +# 6. Publish hotfix +core ci --we-are-go-for-launch --prerelease + +# 7. Merge back to main +git checkout main +git merge hotfix/critical-bug +git push +``` + +--- + +## Documentation: Sync Across Repos + +Keep documentation synchronised. + +```bash +# 1. List all docs +core docs list + +# 2. Sync to central location +core docs sync --output ./docs-site + +# 3. Review changes +git diff docs-site/ + +# 4. Commit +git add docs-site/ +git commit -m "docs: sync from packages" +``` + +--- + +## Troubleshooting: Failed Build + +When a build fails. + +```bash +# 1. Check environment +core doctor + +# 2. Clean previous artifacts +rm -rf dist/ + +# 3. Verbose build +core build -v + +# 4. If Go-specific issues +core go mod tidy +core go mod verify + +# 5. Check for test failures +core go test -v + +# 6. Review configuration +cat .core/build.yaml +``` + +--- + +## See Also + +- [Getting Started](getting-started.md) - First-time setup +- [Troubleshooting](troubleshooting.md) - When things go wrong +- [Configuration](configuration.md) - Config file reference diff --git a/docs/packages/tenant/architecture.md b/docs/packages/tenant/architecture.md new file mode 100644 index 0000000..22b4b99 --- /dev/null +++ b/docs/packages/tenant/architecture.md @@ -0,0 +1,422 @@ +--- +title: Architecture +description: Technical architecture of the core-tenant multi-tenancy package +updated: 2026-01-29 +--- + +# core-tenant Architecture + +This document describes the technical architecture of the core-tenant package, which provides multi-tenancy, user management, and entitlement systems for the Host UK platform. + +## Overview + +core-tenant is the foundational tenancy layer that enables: + +- **Workspaces** - The primary tenant boundary (organisations, teams) +- **Namespaces** - Product-level isolation within or across workspaces +- **Entitlements** - Feature access control, usage limits, and billing integration +- **User Management** - Authentication, 2FA, and workspace membership + +## Core Concepts + +### Tenant Hierarchy + +``` +User +├── owns Workspaces (can own multiple) +│ ├── has WorkspacePackages (entitlements) +│ ├── has Boosts (temporary limit increases) +│ ├── has Members (users with roles/permissions) +│ ├── has Teams (permission groups) +│ └── owns Namespaces (product boundaries) +└── owns Namespaces (personal, not workspace-linked) +``` + +### Workspace + +The `Workspace` model is the primary tenant boundary. All tenant-scoped data references a workspace_id. + +**Key Properties:** +- `slug` - URL-safe unique identifier +- `domain` - Optional custom domain +- `settings` - JSON configuration blob +- `stripe_customer_id` / `btcpay_customer_id` - Billing integration + +**Relationships:** +- `users()` - Members via pivot table +- `workspacePackages()` - Active entitlement packages +- `boosts()` - Temporary limit increases +- `namespaces()` - Owned namespaces (polymorphic) + +### Namespace + +The `Namespace_` model provides a universal product boundary. Products belong to namespaces rather than directly to users/workspaces. + +**Ownership Patterns:** +1. **User-owned**: Individual creator with personal namespace +2. **Workspace-owned**: Agency managing client namespaces +3. **User with workspace billing**: Personal namespace but billed to workspace + +**Entitlement Cascade:** +1. Check namespace-level packages first +2. Fall back to workspace pool (if namespace has workspace_id) +3. Fall back to user tier (for user-owned namespaces) + +### BelongsToWorkspace Trait + +Models that are workspace-scoped should use the `BelongsToWorkspace` trait: + +```php +class Account extends Model +{ + use BelongsToWorkspace; +} +``` + +**Security Features:** +- Auto-assigns `workspace_id` on create (or throws exception) +- Provides `ownedByCurrentWorkspace()` scope +- Auto-invalidates workspace cache on model changes + +**Strict Mode:** +When `WorkspaceScope::isStrictModeEnabled()` is true: +- Creating models without workspace context throws `MissingWorkspaceContextException` +- Querying without context throws exception +- This prevents accidental cross-tenant data access + +## Entitlement System + +### Feature Types + +Features (`entitlement_features` table) have three types: + +| Type | Description | Example | +|------|-------------|---------| +| `boolean` | On/off access | Beta features | +| `limit` | Numeric limit with usage tracking | 100 AI credits/month | +| `unlimited` | No limit | Unlimited social accounts | + +### Reset Types + +| Type | Description | +|------|-------------| +| `none` | No reset (cumulative) | +| `monthly` | Resets at billing cycle start | +| `rolling` | Rolling window (e.g., last 30 days) | + +### Package Model + +Packages bundle features with specific limits: + +``` +Package (creator) +├── Feature: ai.credits (limit: 100) +├── Feature: social.accounts (limit: 5) +└── Feature: tier.apollo (boolean) +``` + +### Boost Model + +Boosts provide temporary limit increases: + +| Boost Type | Description | +|------------|-------------| +| `add_limit` | Adds to existing limit | +| `enable` | Enables a boolean feature | +| `unlimited` | Makes feature unlimited | + +| Duration Type | Description | +|---------------|-------------| +| `cycle_bound` | Expires at billing cycle end | +| `duration` | Expires after set period | +| `permanent` | Never expires | + +### Entitlement Check Flow + +``` +EntitlementService::can($workspace, 'ai.credits', quantity: 5) +│ +├─> Get Feature by code +│ └─> Get pool feature code (for hierarchical features) +│ +├─> Calculate total limit +│ ├─> Sum limits from active WorkspacePackages +│ └─> Add remaining limits from active Boosts +│ +├─> Get current usage +│ ├─> Check reset type (monthly/rolling/none) +│ └─> Sum UsageRecords in window +│ +└─> Return EntitlementResult + ├─> allowed: bool + ├─> limit: int|null + ├─> used: int + ├─> remaining: int|null + └─> reason: string (if denied) +``` + +### Caching Strategy + +Entitlement data is cached with 5-minute TTL: +- `entitlement:{workspace_id}:limit:{feature_code}` +- `entitlement:{workspace_id}:usage:{feature_code}` + +Cache invalidation occurs on: +- Package provision/suspend/cancel +- Boost provision/expire +- Usage recording + +## Service Layer + +### WorkspaceManager + +Manages workspace context and basic CRUD: + +```php +$manager = app(WorkspaceManager::class); +$manager->setCurrent($workspace); // Set context +$manager->loadBySlug('acme'); // Load by slug +$manager->create($user, $attrs); // Create workspace +$manager->addUser($workspace, $user); // Add member +``` + +### EntitlementService + +Core API for entitlement checks and management: + +```php +$service = app(EntitlementService::class); + +// Check feature access +$result = $service->can($workspace, 'ai.credits', quantity: 5); +if ($result->isAllowed()) { + // Record usage after action + $service->recordUsage($workspace, 'ai.credits', quantity: 5); +} + +// Provision packages +$service->provisionPackage($workspace, 'creator', [ + 'source' => 'blesta', + 'billing_cycle_anchor' => now(), +]); + +// Suspend/reactivate +$service->suspendWorkspace($workspace); +$service->reactivateWorkspace($workspace); +``` + +### WorkspaceTeamService + +Manages teams and permissions: + +```php +$teamService = app(WorkspaceTeamService::class); +$teamService->forWorkspace($workspace); + +// Check permissions +if ($teamService->hasPermission($user, 'social.write')) { + // User can write social content +} + +// Team management +$team = $teamService->createTeam([ + 'name' => 'Content Creators', + 'permissions' => ['social.read', 'social.write'], +]); +$teamService->addMemberToTeam($user, $team); +``` + +### WorkspaceCacheManager + +Workspace-scoped caching with tag support: + +```php +$cache = app(WorkspaceCacheManager::class); + +// Cache workspace data +$data = $cache->remember($workspace, 'expensive-query', 300, function () { + return ExpensiveModel::forWorkspace($workspace)->get(); +}); + +// Flush workspace cache +$cache->flush($workspace); +``` + +## Middleware + +### RequireWorkspaceContext + +Ensures workspace context before processing: + +```php +Route::middleware('workspace.required')->group(function () { + // Routes here require workspace context +}); + +// With user access validation +Route::middleware('workspace.required:validate')->group(function () { + // Also validates user has access to workspace +}); +``` + +Workspace resolved from (in order): +1. Request attribute `workspace_model` +2. `Workspace::current()` (session/auth) +3. Request input `workspace_id` +4. Header `X-Workspace-ID` +5. Query param `workspace` + +### CheckWorkspacePermission + +Checks user has specific permissions: + +```php +Route::middleware('workspace.permission:social.write')->group(function () { + // Requires social.write permission +}); + +// Multiple permissions (OR logic) +Route::middleware('workspace.permission:admin,owner')->group(function () { + // Requires admin OR owner role +}); +``` + +## Event System + +### Lifecycle Events + +The module uses event-driven lazy loading: + +```php +class Boot extends ServiceProvider +{ + public static array $listens = [ + AdminPanelBooting::class => 'onAdminPanel', + ApiRoutesRegistering::class => 'onApiRoutes', + WebRoutesRegistering::class => 'onWebRoutes', + ConsoleBooting::class => 'onConsole', + ]; +} +``` + +### Entitlement Webhooks + +External systems can subscribe to entitlement events: + +| Event | Trigger | +|-------|---------| +| `limit_warning` | Usage at 80% or 90% | +| `limit_reached` | Usage at 100% | +| `package_changed` | Package add/change/remove | +| `boost_activated` | Boost provisioned | +| `boost_expired` | Boost expired | + +Webhooks include: +- HMAC-SHA256 signature verification +- Automatic retry with exponential backoff +- Circuit breaker after consecutive failures + +## Two-Factor Authentication + +### TotpService + +RFC 6238 compliant TOTP implementation: + +```php +$totp = app(TwoFactorAuthenticationProvider::class); + +// Generate secret +$secret = $totp->generateSecretKey(); // 160-bit base32 + +// Generate QR code URL +$url = $totp->qrCodeUrl('AppName', $user->email, $secret); + +// Verify code +if ($totp->verify($secret, $userCode)) { + // Valid +} +``` + +### TwoFactorAuthenticatable Trait + +Add to User model: + +```php +class User extends Authenticatable +{ + use TwoFactorAuthenticatable; +} + +// Enable 2FA +$secret = $user->enableTwoFactorAuth(); +// User scans QR, enters code +if ($user->verifyTwoFactorCode($code)) { + $recoveryCodes = $user->confirmTwoFactorAuth(); +} + +// Disable +$user->disableTwoFactorAuth(); +``` + +## Database Schema + +### Core Tables + +| Table | Purpose | +|-------|---------| +| `users` | User accounts | +| `workspaces` | Tenant organisations | +| `user_workspace` | User-workspace pivot | +| `namespaces` | Product boundaries | + +### Entitlement Tables + +| Table | Purpose | +|-------|---------| +| `entitlement_features` | Feature definitions | +| `entitlement_packages` | Package definitions | +| `entitlement_package_features` | Package-feature pivot | +| `entitlement_workspace_packages` | Workspace package assignments | +| `entitlement_namespace_packages` | Namespace package assignments | +| `entitlement_boosts` | Active boosts | +| `entitlement_usage_records` | Usage tracking | +| `entitlement_logs` | Audit log | + +### Team Tables + +| Table | Purpose | +|-------|---------| +| `workspace_teams` | Team definitions | +| `workspace_invitations` | Pending invitations | + +## Configuration + +The package uses these config keys: + +```php +// config/core.php +return [ + 'workspace_cache' => [ + 'enabled' => true, + 'ttl' => 300, + 'prefix' => 'workspace_cache', + 'use_tags' => true, + ], +]; +``` + +## Testing + +Tests are in `tests/Feature/` using Pest: + +```bash +composer test # All tests +vendor/bin/pest tests/Feature/EntitlementServiceTest.php # Single file +vendor/bin/pest --filter="can method" # Filter by name +``` + +Key test files: +- `EntitlementServiceTest.php` - Core entitlement logic +- `WorkspaceSecurityTest.php` - Tenant isolation +- `WorkspaceCacheTest.php` - Caching behaviour +- `TwoFactorAuthenticatableTest.php` - 2FA flows diff --git a/docs/packages/tenant/entitlements.md b/docs/packages/tenant/entitlements.md new file mode 100644 index 0000000..ec72523 --- /dev/null +++ b/docs/packages/tenant/entitlements.md @@ -0,0 +1,465 @@ +--- +title: Entitlements +description: Guide to the entitlement system for feature access and usage limits +updated: 2026-01-29 +--- + +# Entitlement System + +The entitlement system controls feature access, usage limits, and billing integration for workspaces and namespaces. + +## Quick Start + +### Check Feature Access + +```php +use Core\Tenant\Services\EntitlementService; + +$entitlements = app(EntitlementService::class); + +// Check if workspace can use a feature +$result = $entitlements->can($workspace, 'ai.credits', quantity: 5); + +if ($result->isAllowed()) { + // Perform action + $entitlements->recordUsage($workspace, 'ai.credits', quantity: 5, user: $user); +} else { + // Handle denial + return response()->json([ + 'error' => $result->reason, + 'limit' => $result->limit, + 'used' => $result->used, + ], 403); +} +``` + +### Via Workspace Model + +```php +$result = $workspace->can('social.accounts'); + +if ($result->isAllowed()) { + $workspace->recordUsage('social.accounts'); +} +``` + +## Concepts + +### Features + +Features are defined in the `entitlement_features` table: + +| Field | Description | +|-------|-------------| +| `code` | Unique identifier (e.g., `ai.credits`, `social.accounts`) | +| `type` | `boolean`, `limit`, or `unlimited` | +| `reset_type` | `none`, `monthly`, or `rolling` | +| `rolling_window_days` | Days for rolling window | +| `parent_feature_id` | For hierarchical features (pool sharing) | + +**Feature Types:** + +| Type | Behaviour | +|------|-----------| +| `boolean` | Binary on/off access | +| `limit` | Numeric limit with usage tracking | +| `unlimited` | Feature enabled with no limits | + +**Reset Types:** + +| Type | Behaviour | +|------|-----------| +| `none` | Usage accumulates forever | +| `monthly` | Resets at billing cycle start | +| `rolling` | Rolling window (e.g., last 30 days) | + +### Packages + +Packages bundle features with specific limits: + +```php +// Example package definition +$package = Package::create([ + 'code' => 'creator', + 'name' => 'Creator Plan', + 'is_base_package' => true, + 'monthly_price' => 19.99, +]); + +// Attach features +$package->features()->attach($aiCreditsFeature->id, ['limit_value' => 100]); +$package->features()->attach($socialAccountsFeature->id, ['limit_value' => 5]); +``` + +### Workspace Packages + +Packages are provisioned to workspaces: + +```php +$workspacePackage = $entitlements->provisionPackage($workspace, 'creator', [ + 'source' => EntitlementLog::SOURCE_BLESTA, + 'billing_cycle_anchor' => now(), + 'blesta_service_id' => 'srv_12345', +]); +``` + +**Statuses:** +- `active` - Package is in use +- `suspended` - Temporarily disabled (e.g., payment failed) +- `cancelled` - Permanently ended +- `expired` - Past expiry date + +### Boosts + +Boosts provide temporary limit increases: + +```php +$boost = $entitlements->provisionBoost($workspace, 'ai.credits', [ + 'boost_type' => Boost::BOOST_TYPE_ADD_LIMIT, + 'limit_value' => 50, + 'duration_type' => Boost::DURATION_CYCLE_BOUND, +]); +``` + +**Boost Types:** + +| Type | Effect | +|------|--------| +| `add_limit` | Adds to package limit | +| `enable` | Enables boolean feature | +| `unlimited` | Makes feature unlimited | + +**Duration Types:** + +| Type | Expiry | +|------|--------| +| `cycle_bound` | Expires at billing cycle end | +| `duration` | Expires after set `expires_at` | +| `permanent` | Never expires | + +## API Reference + +### EntitlementService + +#### can() + +Check if a workspace can use a feature: + +```php +public function can( + Workspace $workspace, + string $featureCode, + int $quantity = 1 +): EntitlementResult +``` + +**Returns `EntitlementResult` with:** +- `isAllowed(): bool` +- `isDenied(): bool` +- `isUnlimited(): bool` +- `limit: ?int` +- `used: int` +- `remaining: ?int` +- `reason: ?string` +- `featureCode: string` +- `getUsagePercentage(): ?float` +- `isNearLimit(): bool` (>80%) +- `isAtLimit(): bool` (100%) + +#### canForNamespace() + +Check entitlement for a namespace with cascade: + +```php +public function canForNamespace( + Namespace_ $namespace, + string $featureCode, + int $quantity = 1 +): EntitlementResult +``` + +Cascade order: +1. Namespace-level packages +2. Workspace pool (if `namespace->workspace_id` set) +3. User tier (if namespace owned by user) + +#### recordUsage() + +Record feature usage: + +```php +public function recordUsage( + Workspace $workspace, + string $featureCode, + int $quantity = 1, + ?User $user = null, + ?array $metadata = null +): UsageRecord +``` + +#### provisionPackage() + +Assign a package to a workspace: + +```php +public function provisionPackage( + Workspace $workspace, + string $packageCode, + array $options = [] +): WorkspacePackage +``` + +**Options:** +- `source` - `system`, `blesta`, `admin`, `user` +- `billing_cycle_anchor` - Start of billing cycle +- `expires_at` - Package expiry date +- `blesta_service_id` - External billing reference +- `metadata` - Additional data + +#### provisionBoost() + +Add a temporary boost: + +```php +public function provisionBoost( + Workspace $workspace, + string $featureCode, + array $options = [] +): Boost +``` + +**Options:** +- `boost_type` - `add_limit`, `enable`, `unlimited` +- `duration_type` - `cycle_bound`, `duration`, `permanent` +- `limit_value` - Amount to add (for `add_limit`) +- `expires_at` - Expiry date (for `duration`) + +#### suspendWorkspace() / reactivateWorkspace() + +Manage workspace package status: + +```php +$entitlements->suspendWorkspace($workspace, 'blesta'); +$entitlements->reactivateWorkspace($workspace, 'admin'); +``` + +#### getUsageSummary() + +Get all feature usage for a workspace: + +```php +$summary = $entitlements->getUsageSummary($workspace); + +// Returns Collection grouped by category: +// [ +// 'ai' => [ +// ['code' => 'ai.credits', 'limit' => 100, 'used' => 50, ...], +// ], +// 'social' => [ +// ['code' => 'social.accounts', 'limit' => 5, 'used' => 3, ...], +// ], +// ] +``` + +## Namespace-Level Entitlements + +For products that operate at namespace level: + +```php +$result = $entitlements->canForNamespace($namespace, 'bio.pages'); + +if ($result->isAllowed()) { + $entitlements->recordNamespaceUsage($namespace, 'bio.pages', user: $user); +} + +// Provision namespace-specific package +$entitlements->provisionNamespacePackage($namespace, 'bio-pro'); +``` + +## Usage Alerts + +The `UsageAlertService` monitors usage and sends notifications: + +```php +// Check single workspace +$alerts = app(UsageAlertService::class)->checkWorkspace($workspace); + +// Check all workspaces (scheduled command) +php artisan tenant:check-usage-alerts +``` + +**Alert Thresholds:** +- 80% - Warning +- 90% - Critical +- 100% - Limit reached + +**Notification Channels:** +- Email to workspace owner +- Webhook events (`limit_warning`, `limit_reached`) + +## Billing Integration + +### Blesta API + +External endpoints for billing system integration: + +``` +POST /api/entitlements - Provision package +POST /api/entitlements/{id}/suspend - Suspend +POST /api/entitlements/{id}/unsuspend - Reactivate +POST /api/entitlements/{id}/cancel - Cancel +POST /api/entitlements/{id}/renew - Renew +GET /api/entitlements/{id} - Get details +``` + +### Cross-App API + +For other Host UK services to check entitlements: + +``` +GET /api/entitlements/check - Check feature access +POST /api/entitlements/usage - Record usage +GET /api/entitlements/summary - Get usage summary +``` + +## Webhooks + +Subscribe to entitlement events: + +```php +$webhookService = app(EntitlementWebhookService::class); + +$webhook = $webhookService->register($workspace, + name: 'Usage Alerts', + url: 'https://api.example.com/hooks/entitlements', + events: ['limit_warning', 'limit_reached'] +); +``` + +**Available Events:** +- `limit_warning` - 80%/90% threshold +- `limit_reached` - 100% threshold +- `package_changed` - Package add/change/remove +- `boost_activated` - New boost +- `boost_expired` - Boost expired + +**Payload Format:** + +```json +{ + "event": "limit_warning", + "data": { + "workspace_id": 123, + "feature_code": "ai.credits", + "threshold": 80, + "used": 80, + "limit": 100 + }, + "timestamp": "2026-01-29T12:00:00Z" +} +``` + +**Verification:** + +```php +$isValid = $webhookService->verifySignature( + $payload, + $request->header('X-Signature'), + $webhook->secret +); +``` + +## Best Practices + +### Check Before Action + +Always check entitlements before performing the action: + +```php +// Bad: Check after action +$account = SocialAccount::create([...]); +if (!$workspace->can('social.accounts')->isAllowed()) { + $account->delete(); + throw new \Exception('Limit exceeded'); +} + +// Good: Check before action +$result = $workspace->can('social.accounts'); +if ($result->isDenied()) { + throw new EntitlementException($result->reason); +} +$account = SocialAccount::create([...]); +$workspace->recordUsage('social.accounts'); +``` + +### Use Transactions + +For atomic check-and-record: + +```php +DB::transaction(function () use ($workspace, $user) { + $result = $workspace->can('ai.credits', 10); + + if ($result->isDenied()) { + throw new EntitlementException($result->reason); + } + + // Perform AI generation + $output = $aiService->generate($prompt); + + // Record usage + $workspace->recordUsage('ai.credits', 10, $user, [ + 'model' => 'claude-3', + 'tokens' => 1500, + ]); + + return $output; +}); +``` + +### Cache Considerations + +Entitlement checks are cached for 5 minutes. For real-time accuracy: + +```php +// Force cache refresh +$entitlements->invalidateCache($workspace); +$result = $entitlements->can($workspace, 'feature'); +``` + +### Feature Code Conventions + +Use dot notation for feature codes: + +``` +service.feature +service.feature.subfeature +``` + +Examples: +- `ai.credits` +- `social.accounts` +- `social.posts.scheduled` +- `bio.pages` +- `analytics.websites` + +### Hierarchical Features + +For shared pools, use parent features: + +```php +// Parent feature (pool) +$aiCredits = Feature::create([ + 'code' => 'ai.credits', + 'type' => Feature::TYPE_LIMIT, +]); + +// Child feature (uses parent pool) +$aiGeneration = Feature::create([ + 'code' => 'ai.generation', + 'parent_feature_id' => $aiCredits->id, +]); + +// Both check against ai.credits pool +$workspace->can('ai.generation'); // Uses ai.credits limit +``` diff --git a/docs/packages/tenant/security.md b/docs/packages/tenant/security.md new file mode 100644 index 0000000..15f9e9f --- /dev/null +++ b/docs/packages/tenant/security.md @@ -0,0 +1,309 @@ +--- +title: Security +description: Security considerations and audit notes for core-tenant +updated: 2026-01-29 +--- + +# Security Considerations + +This document outlines security considerations, implemented protections, and known areas requiring attention in the core-tenant package. + +## Multi-Tenant Data Isolation + +### Workspace Scope Enforcement + +The primary security mechanism is the `BelongsToWorkspace` trait which enforces workspace isolation at the model level. + +**How it works:** + +1. **Strict Mode** (default in web requests): Queries without workspace context throw `MissingWorkspaceContextException` +2. **Auto-assignment**: Creating models without explicit `workspace_id` uses current context or throws +3. **Cache invalidation**: Model changes automatically invalidate workspace-scoped cache + +**Code paths:** + +```php +// SAFE: Explicit workspace context +Account::forWorkspace($workspace)->get(); + +// SAFE: Uses current workspace from request +Account::ownedByCurrentWorkspace()->get(); + +// THROWS in strict mode: No workspace context +Account::query()->get(); // MissingWorkspaceContextException + +// DANGEROUS: Bypasses scope - use with caution +Account::query()->acrossWorkspaces()->get(); +WorkspaceScope::withoutStrictMode(fn() => Account::all()); +``` + +### Middleware Protection + +| Middleware | Purpose | +|------------|---------| +| `RequireWorkspaceContext` | Ensures workspace is set before route handling | +| `CheckWorkspacePermission` | Validates user has required permissions | + +**Recommendation:** Always use `workspace.required:validate` for user-facing routes to ensure the authenticated user actually has access to the resolved workspace. + +### Known Gaps + +1. **SEC-006**: The `RequireWorkspaceContext` middleware accepts workspace from headers/query params without mandatory user access validation. The `validate` parameter should be the default. + +2. **Cross-tenant API**: The `EntitlementApiController` accepts workspace lookups by email, which could allow enumeration of user-workspace associations. Consider adding authentication scopes. + +## Authentication Security + +### Password Storage + +Passwords are hashed using bcrypt via Laravel's `hashed` cast: + +```php +protected function casts(): array +{ + return [ + 'password' => 'hashed', + ]; +} +``` + +### Two-Factor Authentication + +**Implemented:** +- TOTP (RFC 6238) with 30-second time steps +- 6-digit codes with SHA-1 HMAC +- Clock drift tolerance (1 window each direction) +- 8 recovery codes (20 characters each) + +**Security Considerations:** + +1. **SEC-003**: TOTP secrets are stored in plaintext. Should use Laravel's `encrypted` cast. + - File: `Models/UserTwoFactorAuth.php` + - Risk: Database breach exposes all 2FA secrets + - Mitigation: Use `'secret_key' => 'encrypted'` cast + +2. Recovery codes are stored as JSON array. Consider hashing each code individually. + +3. No brute-force protection on TOTP verification endpoint (rate limiting should be applied at route level). + +### Session Security + +Standard Laravel session handling with: +- `sessions` table for database driver +- IP address and user agent tracking +- `remember_token` for persistent sessions + +## API Security + +### Blesta Integration API + +The `EntitlementApiController` provides endpoints for external billing system integration: + +| Endpoint | Risk | Mitigation | +|----------|------|------------| +| `POST /store` | Creates users/workspaces | Requires API auth | +| `POST /suspend/{id}` | Suspends access | Requires API auth | +| `POST /cancel/{id}` | Cancels packages | Requires API auth | + +**Known Issues:** + +1. **SEC-001**: No rate limiting on API endpoints + - Risk: Compromised API key could mass-provision accounts + - Mitigation: Add rate limiting middleware + +2. **SEC-002**: API authentication not visible in `Routes/api.php` + - Action: Verify Blesta routes have proper auth middleware + +### Webhook Security + +**Implemented:** +- HMAC-SHA256 signature on all payloads +- `X-Signature` header for verification +- 32-byte random secrets (256-bit) + +**Code:** +```php +// Signing (outbound) +$signature = hash_hmac('sha256', json_encode($payload), $webhook->secret); + +// Verification (inbound) +$expected = hash_hmac('sha256', $payload, $secret); +return hash_equals($expected, $signature); +``` + +**Known Issues:** + +1. **SEC-005**: Webhook test endpoint could be SSRF vector + - Risk: Attacker could probe internal network via webhook URL + - Mitigation: Validate URLs against blocklist, prevent internal IPs + +### Invitation Tokens + +**Implemented:** +- 64-character random tokens (`Str::random(64)`) +- Expiration dates with default 7-day TTL +- Single-use (marked accepted_at after use) + +**Known Issues:** + +1. **SEC-004**: Tokens stored in plaintext + - Risk: Database breach exposes all pending invitations + - Mitigation: Store hash, compare with `hash_equals()` + +2. No rate limiting on invitation acceptance endpoint + - Risk: Brute-force token guessing (though 64 chars is large keyspace) + - Mitigation: Add rate limiting, log failed attempts + +## Input Validation + +### EntitlementApiController + +```php +$validated = $request->validate([ + 'email' => 'required|email', + 'name' => 'required|string|max:255', + 'product_code' => 'required|string', + 'billing_cycle_anchor' => 'nullable|date', + 'expires_at' => 'nullable|date', + 'blesta_service_id' => 'nullable|string', +]); +``` + +**Note:** `blesta_service_id` and `product_code` are not sanitised for special characters. Consider adding regex validation if these are displayed in UI. + +### Workspace Manager Validation Rules + +The `WorkspaceManager` provides scoped uniqueness rules: + +```php +// Ensures uniqueness within workspace +$manager->uniqueRule('social_accounts', 'handle', softDelete: true); +``` + +## Logging and Audit + +### Entitlement Logs + +All entitlement changes are logged to `entitlement_logs`: + +```php +EntitlementLog::logPackageAction( + $workspace, + EntitlementLog::ACTION_PACKAGE_PROVISIONED, + $workspacePackage, + source: EntitlementLog::SOURCE_BLESTA, + newValues: $workspacePackage->toArray() +); +``` + +**Logged actions:** +- Package provision/suspend/cancel/reactivate/renew +- Boost provision/expire/cancel +- Usage recording + +**Not logged (should consider):** +- Workspace creation/deletion +- Member additions/removals +- Permission changes +- Login attempts + +### Security Event Logging + +Currently limited. Recommend adding: +- Failed authentication attempts +- 2FA setup/disable events +- Invitation accept/reject +- API key usage + +## Sensitive Data Handling + +### Hidden Attributes + +```php +// User model +protected $hidden = [ + 'password', + 'remember_token', +]; + +// Workspace model +protected $hidden = [ + 'wp_connector_secret', +]; +``` + +### Guarded Attributes + +```php +// Workspace model +protected $guarded = [ + 'wp_connector_secret', +]; +``` + +**Note:** Using `$fillable` is generally safer than `$guarded` for sensitive models. + +## Recommendations + +### Immediate (P1) + +1. Add rate limiting to all API endpoints +2. Encrypt 2FA secrets at rest +3. Hash invitation tokens before storage +4. Validate webhook URLs against SSRF attacks +5. Make user access validation default in RequireWorkspaceContext + +### Short-term (P2) + +1. Add comprehensive security event logging +2. Implement brute-force protection for: + - 2FA verification + - Invitation acceptance + - Password reset +3. Add API scopes for entitlement operations +4. Implement session fingerprinting (detect session hijacking) + +### Long-term (P3) + +1. Consider WebAuthn/FIDO2 as 2FA alternative +2. Implement cryptographic binding between user sessions and workspace access +3. Add anomaly detection for unusual entitlement patterns +4. Consider field-level encryption for sensitive workspace data + +## Security Testing + +### Existing Tests + +- `WorkspaceSecurityTest.php` - Tests tenant isolation +- `TwoFactorAuthenticatableTest.php` - Tests 2FA flows + +### Recommended Additional Tests + +1. Test workspace scope bypass attempts +2. Test API authentication failure handling +3. Test rate limiting behaviour +4. Test SSRF protection on webhook URLs +5. Test invitation token brute-force protection + +## Compliance Notes + +### GDPR Considerations + +1. **Account Deletion**: `ProcessAccountDeletion` job handles user data removal +2. **Data Export**: Not currently implemented (consider adding) +3. **Consent Tracking**: Not in scope of this package + +### PCI DSS + +If handling payment data: +- `stripe_customer_id` and `btcpay_customer_id` are stored (tokens, not card data) +- No direct card handling in this package +- Billing details (name, address) stored in workspace model + +## Incident Response + +If you discover a security vulnerability: + +1. Do not disclose publicly +2. Contact: security@host.uk.com (hypothetical) +3. Include: Vulnerability description, reproduction steps, impact assessment diff --git a/docs/packages/tenant/teams-permissions.md b/docs/packages/tenant/teams-permissions.md new file mode 100644 index 0000000..572f1b8 --- /dev/null +++ b/docs/packages/tenant/teams-permissions.md @@ -0,0 +1,447 @@ +--- +title: Teams and Permissions +description: Guide to workspace teams and permission management +updated: 2026-01-29 +--- + +# Teams and Permissions + +The team system provides fine-grained access control within workspaces through role-based teams with configurable permissions. + +## Overview + +``` +Workspace +├── Teams (permission groups) +│ ├── Owners (system team) +│ ├── Admins (system team) +│ ├── Members (system team, default) +│ └── Custom teams... +└── Members (users in workspace) + └── assigned to Team (or custom_permissions) +``` + +## Quick Start + +### Check Permissions + +```php +use Core\Tenant\Services\WorkspaceTeamService; + +$teamService = app(WorkspaceTeamService::class); +$teamService->forWorkspace($workspace); + +// Single permission +if ($teamService->hasPermission($user, 'social.write')) { + // User can create/edit social content +} + +// Any of multiple permissions +if ($teamService->hasAnyPermission($user, ['admin', 'owner'])) { + // User is admin or owner +} + +// All permissions required +if ($teamService->hasAllPermissions($user, ['social.read', 'social.write'])) { + // User has both permissions +} +``` + +### Via Middleware + +```php +// Single permission +Route::middleware('workspace.permission:social.write') + ->group(function () { + Route::post('/posts', [PostController::class, 'store']); + }); + +// Multiple permissions (OR logic) +Route::middleware('workspace.permission:admin,owner') + ->group(function () { + Route::get('/settings', [SettingsController::class, 'index']); + }); +``` + +## System Teams + +Three system teams are created by default: + +### Owners + +```php +[ + 'slug' => 'owner', + 'permissions' => ['*'], // All permissions + 'is_system' => true, +] +``` + +Workspace owners have unrestricted access to all features and settings. + +### Admins + +```php +[ + 'slug' => 'admin', + 'permissions' => [ + 'workspace.read', + 'workspace.manage_settings', + 'workspace.manage_members', + 'workspace.manage_billing', + // ... all service permissions + ], + 'is_system' => true, +] +``` + +Admins can manage workspace settings and members but cannot delete the workspace or transfer ownership. + +### Members + +```php +[ + 'slug' => 'member', + 'permissions' => [ + 'workspace.read', + 'social.read', 'social.write', + 'bio.read', 'bio.write', + // ... basic service access + ], + 'is_system' => true, + 'is_default' => true, +] +``` + +Default team for new members. Can use services but not manage workspace settings. + +## Permission Structure + +### Workspace Permissions + +| Permission | Description | +|------------|-------------| +| `workspace.read` | View workspace details | +| `workspace.manage_settings` | Edit workspace settings | +| `workspace.manage_members` | Invite/remove members | +| `workspace.manage_billing` | View/manage billing | + +### Service Permissions + +Each service follows the pattern: `service.read`, `service.write`, `service.delete` + +| Service | Permissions | +|---------|-------------| +| Social | `social.read`, `social.write`, `social.delete` | +| Bio | `bio.read`, `bio.write`, `bio.delete` | +| Analytics | `analytics.read`, `analytics.write` | +| Notify | `notify.read`, `notify.write` | +| Trust | `trust.read`, `trust.write` | +| API | `api.read`, `api.write` | + +### Wildcard Permission + +The `*` permission grants access to everything. Only used by the Owners team. + +## WorkspaceTeamService API + +### Team Management + +```php +$teamService = app(WorkspaceTeamService::class); +$teamService->forWorkspace($workspace); + +// List teams +$teams = $teamService->getTeams(); + +// Get specific team +$team = $teamService->getTeam($teamId); +$team = $teamService->getTeamBySlug('content-creators'); + +// Get default team for new members +$defaultTeam = $teamService->getDefaultTeam(); + +// Create custom team +$team = $teamService->createTeam([ + 'name' => 'Content Creators', + 'slug' => 'content-creators', + 'description' => 'Team for content creation staff', + 'permissions' => ['social.read', 'social.write', 'bio.read', 'bio.write'], + 'colour' => 'blue', +]); + +// Update team +$teamService->updateTeam($team, [ + 'permissions' => [...$team->permissions, 'analytics.read'], +]); + +// Delete team (non-system only) +$teamService->deleteTeam($team); +``` + +### Member Management + +```php +// Get member record +$member = $teamService->getMember($user); + +// List all members +$members = $teamService->getMembers(); + +// List team members +$teamMembers = $teamService->getTeamMembers($team); + +// Assign member to team +$teamService->addMemberToTeam($user, $team); + +// Remove from team +$teamService->removeMemberFromTeam($user); + +// Set custom permissions (override team) +$teamService->setMemberCustomPermissions($user, [ + 'social.read', + 'social.write', + // No social.delete +]); +``` + +### Permission Checks + +```php +// Get effective permissions +$permissions = $teamService->getMemberPermissions($user); +// Returns: ['workspace.read', 'social.read', 'social.write', ...] + +// Check single permission +$teamService->hasPermission($user, 'social.write'); + +// Check any permission (OR) +$teamService->hasAnyPermission($user, ['admin', 'owner']); + +// Check all permissions (AND) +$teamService->hasAllPermissions($user, ['social.read', 'social.write']); + +// Role checks +$teamService->isOwner($user); +$teamService->isAdmin($user); +``` + +## WorkspaceMember Model + +The `WorkspaceMember` model represents the user-workspace relationship: + +```php +$member = WorkspaceMember::where('workspace_id', $workspace->id) + ->where('user_id', $user->id) + ->first(); + +// Properties +$member->role; // 'owner', 'admin', 'member' +$member->team_id; // Associated team +$member->custom_permissions; // Override permissions (JSON) +$member->joined_at; +$member->invited_by; + +// Relationships +$member->user; +$member->team; +$member->inviter; + +// Permission methods +$member->getEffectivePermissions(); // Team + custom permissions +$member->hasPermission('social.write'); +$member->hasAnyPermission(['admin', 'owner']); +$member->hasAllPermissions(['social.read', 'social.write']); + +// Role checks +$member->isOwner(); +$member->isAdmin(); +``` + +### Permission Resolution + +Effective permissions are resolved in order: + +1. **Role-based**: Owner role grants `*`, Admin role grants admin permissions +2. **Team permissions**: Permissions from assigned team +3. **Custom permissions**: If set, completely override team permissions + +```php +public function getEffectivePermissions(): array +{ + // 1. Owner has all permissions + if ($this->isOwner()) { + return ['*']; + } + + // 2. Custom permissions override team + if (!empty($this->custom_permissions)) { + return $this->custom_permissions; + } + + // 3. Team permissions + return $this->team?->permissions ?? []; +} +``` + +## Workspace Invitations + +### Invite Users + +```php +// Via Workspace model +$invitation = $workspace->invite( + email: 'newuser@example.com', + role: 'member', + invitedBy: $currentUser, + expiresInDays: 7 +); + +// Invitation sent via WorkspaceInvitationNotification +``` + +### Accept Invitation + +```php +// Find and accept +$invitation = WorkspaceInvitation::findPendingByToken($token); + +if ($invitation && $invitation->accept($user)) { + // User added to workspace +} + +// Or via Workspace static method +Workspace::acceptInvitation($token, $user); +``` + +### Invitation States + +```php +$invitation->isPending(); // Not accepted, not expired +$invitation->isExpired(); // Past expires_at +$invitation->isAccepted(); // Has accepted_at +``` + +## Custom Teams + +### Creating Custom Teams + +```php +$team = $teamService->createTeam([ + 'name' => 'Social Media Managers', + 'slug' => 'social-managers', + 'description' => 'Team for managing social media accounts', + 'permissions' => [ + 'workspace.read', + 'social.read', + 'social.write', + 'social.delete', + 'analytics.read', + ], + 'colour' => 'purple', + 'is_default' => false, +]); +``` + +### Making Team Default + +```php +$teamService->updateTeam($team, ['is_default' => true]); +// Other teams automatically have is_default set to false +``` + +### Deleting Teams + +```php +// Only non-system teams can be deleted +// Teams with members cannot be deleted + +if ($team->is_system) { + throw new \RuntimeException('Cannot delete system teams'); +} + +if ($teamService->countTeamMembers($team) > 0) { + throw new \RuntimeException('Remove members first'); +} + +$teamService->deleteTeam($team); +``` + +## Seeding Default Teams + +When creating a new workspace: + +```php +$teamService->forWorkspace($workspace); +$teams = $teamService->seedDefaultTeams(); + +// Or ensure they exist (idempotent) +$teams = $teamService->ensureDefaultTeams(); +``` + +### Migrating Existing Members + +If migrating from role-based to team-based: + +```php +$migrated = $teamService->migrateExistingMembers(); +// Assigns members to teams based on their role: +// owner -> Owners team +// admin -> Admins team +// member -> Members team +``` + +## Best Practices + +### Use Middleware for Route Protection + +```php +Route::middleware(['auth', 'workspace.required', 'workspace.permission:social.write']) + ->group(function () { + Route::resource('posts', PostController::class); + }); +``` + +### Check Permissions in Controllers + +```php +public function store(Request $request) +{ + $teamService = app(WorkspaceTeamService::class); + $teamService->forWorkspace($request->attributes->get('workspace_model')); + + if (!$teamService->hasPermission($request->user(), 'social.write')) { + abort(403, 'You do not have permission to create posts'); + } + + // ... +} +``` + +### Use Policies with Teams + +```php +class PostPolicy +{ + public function create(User $user): bool + { + $teamService = app(WorkspaceTeamService::class); + return $teamService->hasPermission($user, 'social.write'); + } + + public function delete(User $user, Post $post): bool + { + $teamService = app(WorkspaceTeamService::class); + return $teamService->hasPermission($user, 'social.delete'); + } +} +``` + +### Permission Naming Conventions + +Follow the pattern: `service.action` + +- `service.read` - View resources +- `service.write` - Create/edit resources +- `service.delete` - Delete resources +- `workspace.manage_*` - Workspace admin actions