docs: add human-friendly documentation
Some checks failed
CI / PHP 8.3 (push) Failing after 2m16s
CI / PHP 8.4 (push) Failing after 2m3s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-03-11 13:02:41 +00:00
parent 7d7c489509
commit 755c6b7134
3 changed files with 726 additions and 105 deletions

367
docs/architecture.md Normal file
View file

@ -0,0 +1,367 @@
---
title: Architecture
description: Internal design of core/php -- FrankenPHP handler, service orchestration, project detection, CI pipeline, deployment, and native bridge.
---
# Architecture
This document explains how the Go code in `forge.lthn.ai/core/php` is structured
and how the major subsystems interact.
## Command Registration
The module exposes two entry points for command registration:
- **`AddPHPCommands(root)`** -- adds commands under a `php` parent (for the
multi-purpose `core` binary where PHP is one of many command groups).
- **`AddPHPRootCommands(root)`** -- adds commands directly to the root (for the
standalone `core-php` binary where `dev`, `build`, etc. are top-level).
Both paths register the same set of commands and share workspace-aware
`PersistentPreRunE` logic that detects `.core/workspace.yaml` and `cd`s into the
active package directory before execution.
The standalone binary is minimal:
```go
// cmd/core-php/main.go
func main() {
cli.Main(
cli.WithCommands("php", php.AddPHPRootCommands),
)
}
```
### Command Tree
```
core-php (or core php)
dev Start all detected services (FrankenPHP, Vite, Horizon, Reverb, Redis)
logs Stream unified or per-service logs
stop Stop all running services
status Show project info and service detection
ssl Setup mkcert SSL certificates
build Build Docker or LinuxKit image
serve Run a production Docker container
shell <container> Open a shell in a running container
ci Run full QA pipeline (test, stan, psalm, fmt, audit, security)
packages
link <paths> Add Composer path repositories for local development
unlink <names> Remove path repositories
update [pkgs] Run composer update
list List linked packages
deploy Trigger Coolify deployment
deploy:status Check deployment status
deploy:rollback Rollback to a previous deployment
deploy:list List recent deployments
serve:embedded (CGO only) Serve via embedded FrankenPHP runtime
exec <cmd> (CGO only) Execute artisan via FrankenPHP
```
## FrankenPHP Handler (CGO)
Files: `handler.go`, `cmd_serve_frankenphp.go`, `env.go`, `extract.go`
Build tag: `//go:build cgo`
The `Handler` struct implements `http.Handler` and delegates all PHP processing
to the FrankenPHP C library. It supports two modes:
1. **Octane worker mode** -- Laravel stays booted in memory across requests.
Workers are persistent PHP processes that handle requests without
re-bootstrapping. This yields sub-millisecond response times.
2. **Standard mode** -- each request boots the PHP application from scratch.
Used as a fallback when Octane is not installed.
### Request Routing
`Handler.ServeHTTP` implements a try-files pattern similar to Caddy/Nginx:
1. If the URL maps to a directory, rewrite to `{dir}/index.php`.
2. If the file does not exist and the URL does not end in `.php`, rewrite to
`/index.php` (front controller).
3. Non-PHP files that exist on disc are served directly via `http.ServeFile`.
4. Everything else is passed to `frankenphp.ServeHTTP`.
### Initialisation
```go
handler, cleanup, err := php.NewHandler(laravelRoot, php.HandlerConfig{
NumThreads: 4,
NumWorkers: 2,
PHPIni: map[string]string{
"display_errors": "Off",
"opcache.enable": "1",
},
})
defer cleanup()
```
`NewHandler` tries to initialise FrankenPHP with workers first. If
`vendor/laravel/octane/bin/frankenphp-worker.php` exists, it passes the worker
script to `frankenphp.Init`. If that fails, it falls back to standard mode.
### Embedded Applications
`Extract()` copies an `embed.FS`-packaged Laravel application to a temporary
directory so that FrankenPHP can access real filesystem paths.
`PrepareRuntimeEnvironment()` then creates persistent data directories
(`~/Library/Application Support/{app}` on macOS, `~/.local/share/{app}` on
Linux), generates a `.env` file with an auto-generated `APP_KEY`, symlinks
`storage/` to the persistent location, and creates an empty SQLite database.
## Native Bridge
File: `bridge.go`
The bridge is a localhost-only HTTP server that allows PHP code to call back into
Go. This is needed because Livewire renders server-side in PHP and cannot call
Wails bindings (`window.go.*`) directly.
```go
bridge, err := php.NewBridge(myHandler)
// PHP can now POST to http://127.0.0.1:{bridge.Port()}/bridge/call
```
The bridge exposes two endpoints:
| Method | Path | Purpose |
|---|---|---|
| GET | `/bridge/health` | Health check (returns `{"status":"ok"}`) |
| POST | `/bridge/call` | Invoke a named method with JSON arguments |
The host application implements `BridgeHandler`:
```go
type BridgeHandler interface {
HandleBridgeCall(method string, args json.RawMessage) (any, error)
}
```
The bridge port is injected into Laravel's `.env` as `NATIVE_BRIDGE_URL`.
## Service Orchestration
Files: `php.go`, `services.go`, `services_unix.go`, `services_windows.go`
### DevServer
`DevServer` manages the lifecycle of all development services. It:
1. Detects which services are needed (via `DetectServices`).
2. Filters out services disabled by flags (`--no-vite`, `--no-horizon`, etc.).
3. Creates concrete service instances.
4. Starts all services, rolling back if any fail.
5. Provides unified log streaming (round-robin multiplexing from all service log files).
6. Stops services in reverse order on shutdown.
### Service Interface
All managed services implement:
```go
type Service interface {
Name() string
Start(ctx context.Context) error
Stop() error
Logs(follow bool) (io.ReadCloser, error)
Status() ServiceStatus
}
```
### Concrete Services
| Service | Binary | Default Port | Notes |
|---|---|---|---|
| `FrankenPHPService` | `php artisan octane:start --server=frankenphp` | 8000 | HTTPS via mkcert certificates |
| `ViteService` | `npm/pnpm/yarn/bun run dev` | 5173 | Auto-detects package manager |
| `HorizonService` | `php artisan horizon` | -- | Uses `horizon:terminate` for graceful stop |
| `ReverbService` | `php artisan reverb:start` | 8080 | WebSocket server |
| `RedisService` | `redis-server` | 6379 | Optional config file support |
All services inherit from `baseService`, which handles:
- Process creation with platform-specific `SysProcAttr` for clean shutdown.
- Log file creation under `.core/logs/`.
- Background process monitoring.
- Graceful stop with SIGTERM, then SIGKILL after 5 seconds.
## Project Detection
File: `detect.go`
The detection system inspects the filesystem to determine project capabilities:
| Function | Checks |
|---|---|
| `IsLaravelProject(dir)` | `artisan` exists and `composer.json` requires `laravel/framework` |
| `IsFrankenPHPProject(dir)` | `laravel/octane` in `composer.json`, `config/octane.php` mentions frankenphp |
| `IsPHPProject(dir)` | `composer.json` exists |
| `DetectServices(dir)` | Checks for Vite configs, Horizon config, Reverb config, Redis in `.env` |
| `DetectPackageManager(dir)` | Inspects lock files: `bun.lockb`, `pnpm-lock.yaml`, `yarn.lock`, `package-lock.json` |
| `GetLaravelAppName(dir)` | Reads `APP_NAME` from `.env` |
| `GetLaravelAppURL(dir)` | Reads `APP_URL` from `.env` |
## Dockerfile Generation
File: `dockerfile.go`
`GenerateDockerfile(dir)` produces a multi-stage Dockerfile by analysing
`composer.json`:
1. **PHP version** -- extracted from `composer.json`'s `require.php` constraint.
2. **Extensions** -- inferred from package dependencies (e.g., `laravel/horizon`
implies `redis` and `pcntl`; `intervention/image` implies `gd`).
3. **Frontend assets** -- if `package.json` has a `build` script, a Node.js
build stage is prepended.
4. **Base image** -- `dunglas/frankenphp` with Alpine variant by default.
The generated Dockerfile includes:
- Multi-stage build for frontend assets (Node 20 Alpine).
- Composer dependency installation with layer caching.
- Laravel config/route/view caching.
- Correct permissions for `storage/` and `bootstrap/cache/`.
- Health check via `curl -f http://localhost/up`.
- Octane start command if `laravel/octane` is detected.
## CI Pipeline
File: `cmd_ci.go`, `quality.go`, `testing.go`
The `ci` command runs six checks in sequence:
| Check | Tool | SARIF Support |
|---|---|---|
| `test` | Pest or PHPUnit (auto-detected) | No |
| `stan` | PHPStan or Larastan (auto-detected) | Yes |
| `psalm` | Psalm (skipped if not configured) | Yes |
| `fmt` | Laravel Pint (check-only mode) | No |
| `audit` | `composer audit` + `npm audit` | No |
| `security` | `.env` and filesystem security checks | No |
### Output Formats
- **Default** -- coloured terminal table with per-check status icons.
- **`--json`** -- structured `CIResult` JSON with per-check details.
- **`--summary`** -- Markdown table suitable for PR comments.
- **`--sarif`** -- SARIF files for stan/psalm, uploadable to GitHub Security.
- **`--upload-sarif`** -- uploads SARIF files via `gh api`.
### Failure Threshold
The `--fail-on` flag controls when the pipeline returns a non-zero exit code:
| Value | Fails On |
|---|---|
| `critical` | Only if issues with `Issues > 0` |
| `high` / `error` (default) | Any check with status `failed` |
| `warning` | Any check with status `failed` or `warning` |
### QA Pipeline Stages
The `quality.go` file also defines a broader QA pipeline (`QAOptions`) with
three stages:
- **Quick** -- `audit`, `fmt`, `stan`
- **Standard** -- `psalm` (if configured), `test`
- **Full** -- `rector` (if configured), `infection` (if configured)
## Deployment (Coolify)
Files: `deploy.go`, `coolify.go`
### Configuration
Coolify credentials are loaded from environment variables or `.env`:
```
COOLIFY_URL=https://coolify.example.com
COOLIFY_TOKEN=your-api-token
COOLIFY_APP_ID=app-uuid
COOLIFY_STAGING_APP_ID=staging-app-uuid (optional)
```
Environment variables take precedence over `.env` values.
### CoolifyClient
The `CoolifyClient` wraps the Coolify REST API:
```go
client := php.NewCoolifyClient(baseURL, token)
deployment, err := client.TriggerDeploy(ctx, appID, force)
deployment, err := client.GetDeployment(ctx, appID, deploymentID)
deployments, err := client.ListDeployments(ctx, appID, limit)
deployment, err := client.Rollback(ctx, appID, deploymentID)
app, err := client.GetApp(ctx, appID)
```
### Deployment Flow
1. Load config from `.env` or environment.
2. Resolve the app ID for the target environment (production or staging).
3. Trigger deployment via the Coolify API.
4. If `--wait` is set, poll every 5 seconds (up to 10 minutes) until the
deployment reaches a terminal state.
5. Print deployment status with coloured output.
### Rollback
If no specific deployment ID is provided, `Rollback()` fetches the 10 most
recent deployments, skips the current one, and rolls back to the last
successful deployment.
## Workspace Support
File: `workspace.go`
For multi-package repositories, a `.core/workspace.yaml` file at the workspace
root can set an active package:
```yaml
version: 1
active: core-tenant
packages_dir: ./packages
```
When present, the `PersistentPreRunE` hook automatically changes the working
directory to the active package before command execution. The workspace root is
found by walking up the directory tree.
## SSL Certificates
File: `ssl.go`
The `ssl` command and `--https` flag use [mkcert](https://github.com/FiloSottile/mkcert)
to generate locally-trusted SSL certificates. Certificates are stored in
`~/.core/ssl/` by default.
The `SetupSSLIfNeeded()` function is idempotent: it checks for existing
certificates before generating new ones. Generated certificates cover the
domain, `localhost`, `127.0.0.1`, and `::1`.
## Filesystem Abstraction
The module uses `io.Medium` from `forge.lthn.ai/core/go-io` for all filesystem
operations. The default medium is `io.Local` (real filesystem), but tests can
inject a mock medium via `SetMedium()`:
```go
php.SetMedium(myMockMedium)
defer php.SetMedium(io.Local)
```
This allows the detection, Dockerfile generation, package management, and
security check code to be tested without touching the real filesystem.

284
docs/development.md Normal file
View file

@ -0,0 +1,284 @@
---
title: Development
description: How to build, test, and contribute to core/php.
---
# Development
This guide covers building the `core-php` binary, running the test suite, and
contributing to the project.
## Prerequisites
- **Go 1.26+** (the module uses Go 1.26 features)
- **CGO toolchain** (optional, required only for FrankenPHP embedding)
- **Docker** (for container build/serve commands)
- **mkcert** (optional, for local SSL certificates)
- **PHP 8.3+** with Composer (for the PHP side of the project)
- **Node.js 20+** (optional, for frontend asset building)
## Building
### Standard build (no CGO)
The default build produces a binary without FrankenPHP embedding. The embedded
FrankenPHP commands (`serve:embedded`, `exec`) are excluded.
```bash
# Using the core CLI
core build
# Using go directly
go build -trimpath -ldflags="-s -w" -o bin/core-php ./cmd/core-php
```
Build configuration lives in `.core/build.yaml`:
```yaml
project:
name: core-php
main: ./cmd/core-php
binary: core-php
build:
cgo: false
flags:
- -trimpath
ldflags:
- -s
- -w
targets:
- os: linux
arch: amd64
- os: linux
arch: arm64
- os: darwin
arch: arm64
- os: windows
arch: amd64
```
### CGO build (with FrankenPHP)
To include the embedded FrankenPHP handler, enable CGO:
```bash
CGO_ENABLED=1 go build -trimpath -o bin/core-php ./cmd/core-php
```
This pulls in `github.com/dunglas/frankenphp` and links against the PHP C
library. The resulting binary can serve Laravel applications without a separate
PHP installation.
## Running Tests
```bash
# All Go tests
core go test
# -- or --
go test ./...
# Single test
core go test --run TestDetectServices
# -- or --
go test -run TestDetectServices ./...
# With race detector
go test -race ./...
# Coverage
core go cov
core go cov --open # Opens HTML report
```
### Test Conventions
Tests follow the `_Good`, `_Bad`, `_Ugly` suffix pattern from the Core
framework:
- **`_Good`** -- happy path, expected to succeed.
- **`_Bad`** -- expected error conditions, verifying error handling.
- **`_Ugly`** -- edge cases, panics, unusual inputs.
### Mock Filesystem
Tests that exercise detection, Dockerfile generation, or package management use
a mock `io.Medium` to avoid filesystem side effects:
```go
func TestDetectServices_Good(t *testing.T) {
mock := io.NewMockMedium()
mock.WriteFile("artisan", "")
mock.WriteFile("composer.json", `{"require":{"laravel/framework":"^11.0"}}`)
mock.WriteFile("vite.config.js", "")
php.SetMedium(mock)
defer php.SetMedium(io.Local)
services := php.DetectServices(".")
assert.Contains(t, services, php.ServiceFrankenPHP)
assert.Contains(t, services, php.ServiceVite)
}
```
### Test Files
| File | Covers |
|---|---|
| `php_test.go` | DevServer lifecycle, service filtering, options |
| `container_test.go` | Docker build, LinuxKit build, serve options |
| `detect_test.go` | Project detection, service detection, package manager detection |
| `dockerfile_test.go` | Dockerfile generation, PHP extension detection, version extraction |
| `deploy_test.go` | Deployment flow, rollback, status checking |
| `deploy_internal_test.go` | Internal deployment helpers |
| `coolify_test.go` | Coolify API client (HTTP mocking) |
| `packages_test.go` | Package linking, unlinking, listing |
| `services_test.go` | Service interface, base service, start/stop |
| `services_extended_test.go` | Extended service scenarios |
| `ssl_test.go` | SSL certificate paths, existence checking |
| `ssl_extended_test.go` | Extended SSL scenarios |
## Code Quality
```bash
# Format Go code
core go fmt
# Vet
core go vet
# Lint
core go lint
# Full QA (fmt + vet + lint + test)
core go qa
# Full QA with race detection, vulnerability scan, security checks
core go qa full
```
## Project Structure
```
forge.lthn.ai/core/php/
cmd/
core-php/
main.go # Binary entry point
locales/
*.json # Internationalised CLI strings
docker/
docker-compose.prod.yml
stubs/ # Template stubs
config/ # PHP configuration templates
src/ # PHP framework source (separate from Go code)
tests/ # PHP tests
docs/ # Documentation (this directory)
.core/
build.yaml # Build configuration
*.go # Go source (flat layout, single package)
```
The Go code uses a flat package layout -- all `.go` files are in the root
`php` package. This keeps imports simple: `import php "forge.lthn.ai/core/php"`.
## Adding a New Command
1. Create a new file `cmd_mycommand.go`.
2. Define the registration function:
```go
func addPHPMyCommand(parent *cli.Command) {
cmd := &cli.Command{
Use: "mycommand",
Short: i18n.T("cmd.php.mycommand.short"),
RunE: func(cmd *cli.Command, args []string) error {
// Implementation
return nil
},
}
parent.AddCommand(cmd)
}
```
3. Register it in `cmd.go` inside both `AddPHPCommands` and
`AddPHPRootCommands`:
```go
addPHPMyCommand(phpCmd) // or root, for standalone binary
```
4. Add the i18n key to `locales/en.json`.
## Adding a New Service
1. Define the service struct in `services.go`, embedding `baseService`:
```go
type MyService struct {
baseService
}
func NewMyService(dir string) *MyService {
return &MyService{
baseService: baseService{
name: "MyService",
port: 9999,
dir: dir,
},
}
}
func (s *MyService) Start(ctx context.Context) error {
return s.startProcess(ctx, "my-binary", []string{"--flag"}, nil)
}
func (s *MyService) Stop() error {
return s.stopProcess()
}
```
2. Add a `DetectedService` constant in `detect.go`:
```go
const ServiceMyService DetectedService = "myservice"
```
3. Add detection logic in `DetectServices()`.
4. Add a case in `DevServer.Start()` in `php.go`.
## Internationalisation
All user-facing strings use `i18n.T()` keys rather than hardcoded English.
Locale files live in `locales/` and are embedded via `//go:embed`:
```go
//go:embed locales/*.json
var localeFS embed.FS
func init() {
i18n.RegisterLocales(localeFS, "locales")
}
```
When adding new commands or messages, add the corresponding keys to the locale
files.
## Contributing
- Follow UK English conventions: colour, organisation, centre.
- All code is licenced under EUPL-1.2.
- Run `core go qa` before submitting changes.
- Use conventional commits: `type(scope): description`.
- Include `Co-Authored-By: Virgil <virgil@lethean.io>` if pair-programming with
the AI agent.

View file

@ -1,126 +1,96 @@
---
layout: home
hero:
name: Core PHP Framework
text: Modular Monolith for Laravel
tagline: Event-driven architecture with lazy module loading and built-in multi-tenancy
actions:
- theme: brand
text: Get Started
link: /guide/getting-started
- theme: alt
text: View on GitHub
link: https://github.com/host-uk/core-php
features:
- icon: ⚡️
title: Event-Driven Modules
details: Modules declare interest in lifecycle events and are only loaded when needed, reducing overhead for unused features.
- icon: 🔒
title: Multi-Tenant Isolation
details: Automatic workspace scoping for Eloquent models with strict mode enforcement prevents data leakage.
- icon: 🎯
title: Actions Pattern
details: Extract business logic into testable, reusable classes with automatic dependency injection.
- icon: 📝
title: Activity Logging
details: Built-in audit trails for model changes with minimal setup using Spatie Activity Log.
- icon: 🌱
title: Seeder Auto-Discovery
details: Automatic seeder ordering via priority and dependency attributes eliminates manual registration.
- icon: 🎨
title: HLCRF Layouts
details: Data-driven composable layouts with infinite nesting for flexible UI structures.
- icon: 🔐
title: Security First
details: Bouncer action gates, request whitelisting, and comprehensive input sanitization.
- icon: 🚀
title: Production Ready
details: Battle-tested in production with comprehensive test coverage and security audits.
title: core/php
description: Go-powered PHP/Laravel development toolkit with FrankenPHP embedding, service orchestration, CI pipelines, and Coolify deployment.
---
# core/php
`forge.lthn.ai/core/php` is a Go module that provides a comprehensive CLI toolkit
for PHP and Laravel development. It covers the full lifecycle: local development
with service orchestration, code quality assurance, Docker/LinuxKit image building,
and production deployment via the Coolify API.
The module also embeds FrankenPHP, allowing Laravel applications to be served
from a single Go binary with Octane worker mode for sub-millisecond response
times.
## Quick Start
### As a standalone binary
```bash
# Install via Composer
composer require host-uk/core
# Build the core-php binary
core build
# -- or --
go build -o bin/core-php ./cmd/core-php
# Create a module
php artisan make:mod Commerce
# Start the Laravel development environment
core-php dev
# Register lifecycle events
class Boot
{
public static array $listens = [
WebRoutesRegistering::class => 'onWebRoutes',
];
public function onWebRoutes(WebRoutesRegistering $event): void
{
$event->routes(fn () => require __DIR__.'/Routes/web.php');
}
}
# Run the CI pipeline
core-php ci
```
## Why Core PHP?
### As a library in a Go application
Traditional Laravel applications grow into monoliths with tight coupling and unclear boundaries. Microservices add complexity you may not need. **Core PHP provides a middle ground**: a structured monolith with clear module boundaries, lazy loading, and the ability to extract services later if needed.
```go
import php "forge.lthn.ai/core/php"
### Key Benefits
// Register commands under a "php" parent command
cli.Main(
cli.WithCommands("php", php.AddPHPRootCommands),
)
```
- **Reduced Complexity** - No network overhead, distributed tracing, or service mesh
- **Clear Boundaries** - Modules have explicit dependencies via lifecycle events
- **Performance** - Lazy loading means unused modules aren't loaded
- **Flexibility** - Start monolithic, extract services when it makes sense
- **Type Safety** - Full IDE support with no RPC serialization
## Packages
## Package Layout
<div class="package-grid">
| File / Directory | Purpose |
|---|---|
| `cmd/core-php/main.go` | Binary entry point -- registers all commands and calls `cli.Main()` |
| `cmd.go` | Top-level command registration (`AddPHPCommands`, `AddPHPRootCommands`) |
| `cmd_dev.go` | `dev`, `logs`, `stop`, `status`, `ssl` commands |
| `cmd_build.go` | `build` (Docker/LinuxKit) and `serve` (production container) commands |
| `cmd_ci.go` | `ci` command -- full QA pipeline with JSON/Markdown/SARIF output |
| `cmd_deploy.go` | `deploy`, `deploy:status`, `deploy:rollback`, `deploy:list` commands |
| `cmd_packages.go` | `packages link/unlink/update/list` commands |
| `cmd_serve_frankenphp.go` | `serve:embedded` and `exec` commands (CGO only) |
| `cmd_commands.go` | `AddCommands()` convenience wrapper |
| `handler.go` | FrankenPHP HTTP handler (`Handler`) -- CGO build tag |
| `bridge.go` | Native bridge -- localhost HTTP API for PHP-to-Go calls |
| `php.go` | `DevServer` -- multi-service orchestration (start, stop, logs, status) |
| `services.go` | `Service` interface and concrete implementations (FrankenPHP, Vite, Horizon, Reverb, Redis) |
| `detect.go` | Project detection: Laravel, FrankenPHP, Vite, Horizon, Reverb, Redis, package managers |
| `dockerfile.go` | Auto-generated Dockerfiles from `composer.json` analysis |
| `container.go` | `DockerBuildOptions`, `LinuxKitBuildOptions`, `ServeOptions`, and build/serve functions |
| `deploy.go` | Deployment orchestration -- `Deploy()`, `Rollback()`, `DeployStatus()` |
| `coolify.go` | Coolify API client (`CoolifyClient`) with deploy, rollback, status, and list operations |
| `quality.go` | QA tools: Pint, PHPStan/Larastan, Psalm, Rector, Infection, security checks, audit |
| `testing.go` | Test runner detection (Pest/PHPUnit) and execution |
| `ssl.go` | SSL certificate management via mkcert |
| `packages.go` | Composer path repository management (link/unlink local packages) |
| `env.go` | Runtime environment setup for embedded apps (CGO only) |
| `extract.go` | `Extract()` -- copies an `embed.FS` Laravel app to a temporary directory |
| `workspace.go` | Workspace configuration (`.core/workspace.yaml`) for multi-package repos |
| `i18n.go` | Locale registration for internationalised CLI strings |
| `services_unix.go` | Unix process group management (SIGTERM/SIGKILL) |
| `services_windows.go` | Windows process termination |
| `.core/build.yaml` | Build configuration for `core build` |
### [Core](/packages/core)
Event-driven architecture, module system, actions pattern, and multi-tenancy.
### [Admin](/packages/admin)
Livewire-powered admin panel with global search and service management.
## Dependencies
### [API](/packages/api)
REST API with OpenAPI docs, rate limiting, webhook signing, and secure keys.
| Module | Role |
|---|---|
| `forge.lthn.ai/core/cli` | CLI framework (Cobra wrapper, TUI styles, output helpers) |
| `forge.lthn.ai/core/go-i18n` | Internationalisation for command descriptions and messages |
| `forge.lthn.ai/core/go-io` | Filesystem abstraction (`Medium` interface) for testability |
| `forge.lthn.ai/core/go-process` | Process management utilities |
| `github.com/dunglas/frankenphp` | FrankenPHP embedding (CGO, optional) |
| `gopkg.in/yaml.v3` | YAML parsing for workspace configuration |
### [MCP](/packages/mcp)
Model Context Protocol tools for AI integrations with analytics and security.
</div>
## Licence
## Community
- **GitHub Discussions** - Ask questions and share ideas
- **Issue Tracker** - Report bugs and request features
- **Contributing** - See our [contributing guide](/contributing)
<style>
.package-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
margin-top: 2rem;
}
.package-grid > div {
padding: 1rem;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
}
.package-grid h3 {
margin-top: 0;
}
</style>
EUPL-1.2