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