From 01135ac8bd0f0aa28e50461d9f245b1667d61181 Mon Sep 17 00:00:00 2001 From: Snider Date: Sat, 21 Mar 2026 09:00:13 +0000 Subject: [PATCH] docs: rewrite README + add CI workflow with Codecov MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README reflects current API — DI framework, not the old CLI/GUI app. CI runs tests with coverage on push to main. Co-Authored-By: Virgil --- .github/workflows/ci.yml | 23 ++ README.md | 531 ++++++++------------------------------- 2 files changed, 126 insertions(+), 428 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c0a087f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ +name: CI + +on: + push: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Run tests with coverage + run: go test -coverprofile=coverage.out ./tests/... + + - name: Upload to Codecov + uses: codecov/codecov-action@v5 + with: + files: coverage.out diff --git a/README.md b/README.md index 4d213f6..55dea27 100644 --- a/README.md +++ b/README.md @@ -1,443 +1,118 @@ # Core -[![codecov](https://codecov.io/gh/host-uk/core/branch/dev/graph/badge.svg)](https://codecov.io/gh/host-uk/core) -[![Go Test Coverage](https://forge.lthn.ai/core/cli/actions/workflows/coverage.yml/badge.svg)](https://forge.lthn.ai/core/cli/actions/workflows/coverage.yml) -[![Code Scanning](https://forge.lthn.ai/core/cli/actions/workflows/codescan.yml/badge.svg)](https://forge.lthn.ai/core/cli/actions/workflows/codescan.yml) -[![Go Version](https://img.shields.io/github/go-mod/go-version/host-uk/core)](https://go.dev/) +[![codecov](https://codecov.io/gh/dAppCore/core/branch/main/graph/badge.svg)](https://codecov.io/gh/dAppCore/core) +[![Go Version](https://img.shields.io/github/go-mod/go-version/dAppCore/core)](https://go.dev/) [![License](https://img.shields.io/badge/License-EUPL--1.2-blue.svg)](https://opensource.org/licenses/EUPL-1.2) +[![Go Reference](https://pkg.go.dev/badge/dappco.re/go/core.svg)](https://pkg.go.dev/dappco.re/go/core) -Core is a Web3 Framework, written in Go using Wails.io to replace Electron and the bloat of browsers that, at their core, still live in their mum's basement. +Dependency injection and service lifecycle framework for Go. Zero external dependencies beyond `testify` for tests. -- Repo: https://forge.lthn.ai/core/cli +```go +import "dappco.re/go/core" +``` -## Vision +## Quick Start -Core is an **opinionated Web3 desktop application framework** providing: +```go +c := core.New(core.Options{ + {Key: "name", Value: "myapp"}, +}) -1. **Service-Oriented Architecture** - Pluggable services with dependency injection -2. **Encrypted Workspaces** - Each workspace gets its own PGP keypair, files are obfuscated -3. **Cross-Platform Storage** - Abstract storage backends (local, SFTP, WebDAV) behind a `Medium` interface -4. **Multi-Brand Support** - Same codebase powers different "hub" apps (AdminHub, ServerHub, GatewayHub, DeveloperHub, ClientHub) -5. **Built-in Crypto** - PGP encryption/signing, hashing, checksums as first-class citizens +// Register a service +c.Service("auth", core.Service{ + OnStart: func() core.Result { return core.Result{OK: true} }, + OnStop: func() core.Result { return core.Result{OK: true} }, +}) -**Mental model:** A secure, encrypted workspace manager where each "workspace" is a cryptographically isolated environment. The framework handles windows, menus, trays, config, and i18n. +// Retrieve it +r := c.Service("auth") +if r.OK { /* use r.Value */ } -## CLI Quick Start +// Register and run commands +c.Command("deploy", handler) +c.Cli().Run() +``` + +## Primitives + +### Options + +Key-value pairs that flow through all subsystems: + +```go +opts := core.Options{ + {Key: "name", Value: "brain"}, + {Key: "port", Value: 8080}, +} + +name := opts.String("name") +port := opts.Int("port") +ok := opts.Has("debug") +``` + +### Result + +Universal return type replacing `(value, error)`: + +```go +r := c.Data().New(core.Options{{Key: "name", Value: "store"}}) +if r.OK { use(r.Value) } + +// Map from Go conventions +r.Result(file, err) // OK = err == nil, Value = file +``` + +### Service + +Managed component with optional lifecycle hooks: + +```go +core.Service{ + Name: "cache", + Options: opts, + OnStart: func() core.Result { /* ... */ }, + OnStop: func() core.Result { /* ... */ }, + OnReload: func() core.Result { /* ... */ }, +} +``` + +## Subsystems + +| Accessor | Purpose | +|----------|---------| +| `c.Options()` | Input configuration | +| `c.App()` | Application identity | +| `c.Data()` | Embedded/stored content | +| `c.Drive()` | Resource handle registry | +| `c.Fs()` | Local filesystem I/O | +| `c.Config()` | Configuration + feature flags | +| `c.Cli()` | CLI surface layer | +| `c.Command("path")` | Command tree | +| `c.Service("name")` | Service registry | +| `c.Lock("name")` | Named mutexes | +| `c.IPC()` | Message bus | + +## IPC / Message Bus + +Fire-and-forget actions, request/response queries, and task dispatch: + +```go +// Register a handler +c.IPC().On(func(c *core.Core, msg core.Message) core.Result { + // handle message + return core.Result{OK: true} +}) + +// Dispatch +c.IPC().Action(core.Message{Action: "cache.flush"}) +``` + +## Install ```bash -# 1. Install Core -go install forge.lthn.ai/core/cli/cmd/core@latest - -# 2. Verify environment -core doctor - -# 3. Run tests in any Go/PHP project -core go test # or core php test - -# 4. Build and preview release -core build -core ci +go get dappco.re/go/core@latest ``` -For more details, see the [User Guide](docs/user-guide.md). +## License -## Framework Quick Start (Go) - -```go -import core "forge.lthn.ai/core/cli/pkg/framework/core" - -app, err := core.New( - core.WithServiceLock(), -) -``` - -## Prerequisites - -- [Go](https://go.dev/) 1.25+ -- [Node.js](https://nodejs.org/) -- [Wails](https://wails.io/) v3 -- [Task](https://taskfile.dev/) - -## Development Workflow (TDD) - -```bash -task test-gen # 1. Generate test stubs -task test # 2. Run tests (watch them fail) -# 3. Implement your feature -task test # 4. Run tests (watch them pass) -task review # 5. CodeRabbit review -``` - -## Building & Running - -```bash -# GUI (Wails) -task gui:dev # Development with hot-reload -task gui:build # Production build - -# CLI -task cli:build # Build to cmd/core/bin/core -task cli:run # Build and run -``` - -## Configuration - -Core uses a layered configuration system where values are resolved in the following priority: - -1. **Command-line flags** (if applicable) -2. **Environment variables** -3. **Configuration file** -4. **Default values** - -### Configuration File - -The default configuration file is located at `~/.core/config.yaml`. - -#### Format - -The file uses YAML format and supports nested structures. - -```yaml -# ~/.core/config.yaml -dev: - editor: vim - debug: true - -log: - level: info -``` - -### Environment Variables - -#### Layered Configuration Mapping - -Any configuration value can be overridden using environment variables with the `CORE_CONFIG_` prefix. After stripping the `CORE_CONFIG_` prefix, the remaining variable name is converted to lowercase and underscores are replaced with dots to map to the configuration hierarchy. - -**Examples:** -- `CORE_CONFIG_DEV_EDITOR=nano` maps to `dev.editor: nano` -- `CORE_CONFIG_LOG_LEVEL=debug` maps to `log.level: debug` - -#### Common Environment Variables - -| Variable | Description | -|----------|-------------| -| `CORE_DAEMON` | Set to `1` to run the application in daemon mode. | -| `NO_COLOR` | If set (to any value), disables ANSI color output. | -| `MCP_ADDR` | Address for the MCP TCP server (e.g., `localhost:9100`). If not set, MCP uses Stdio. | -| `COOLIFY_TOKEN` | API token for Coolify deployments. | -| `AGENTIC_TOKEN` | API token for Agentic services. | -| `UNIFI_URL` | URL of the UniFi controller (e.g., `https://192.168.1.1`). | -| `UNIFI_INSECURE` | Set to `1` or `true` to skip UniFi TLS verification. | - -## All Tasks - -| Task | Description | -|------|-------------| -| `task test` | Run all Go tests | -| `task test-gen` | Generate test stubs for public API | -| `task check` | go mod tidy + tests + review | -| `task review` | CodeRabbit review | -| `task cov` | Run tests with coverage report | -| `task cov-view` | Open HTML coverage report | -| `task sync` | Update public API Go files | - ---- - -## Architecture - -### Project Structure - -``` -. -├── main.go # CLI application entry point -├── pkg/ -│ ├── framework/core/ # Service container, DI, Runtime[T] -│ ├── crypt/ # Hashing, checksums, PGP -│ ├── io/ # Medium interface + backends -│ ├── help/ # In-app documentation -│ ├── i18n/ # Internationalization -│ ├── repos/ # Multi-repo registry & management -│ ├── agentic/ # AI agent task management -│ └── mcp/ # Model Context Protocol service -├── internal/ -│ ├── cmd/ # CLI command implementations -│ └── variants/ # Build variants (full, minimal, etc.) -└── go.mod # Go module definition -``` - -### Service Pattern (Dual-Constructor DI) - -Every service follows this pattern: - -```go -// Static DI - standalone use/testing (no core.Runtime) -func New() (*Service, error) - -// Dynamic DI - for core.WithService() registration -func Register(c *core.Core) (any, error) -``` - -Services embed `*core.Runtime[Options]` for access to `Core()` and `Config()`. - -### IPC/Action System - -Services implement `HandleIPCEvents(c *core.Core, msg core.Message) error` - auto-discovered via reflection. Handles typed actions like `core.ActionServiceStartup`. - ---- - -## Wails v3 Frontend Bindings - -Core uses [Wails v3](https://v3alpha.wails.io/) to expose Go methods to a WebView2 browser runtime. Wails automatically generates TypeScript bindings for registered services. - -**Documentation:** [Wails v3 Method Bindings](https://v3alpha.wails.io/features/bindings/methods/) - -### How It Works - -1. **Go services** with exported methods are registered with Wails -2. Run `wails3 generate bindings` (or `wails3 dev` / `wails3 build`) -3. **TypeScript SDK** is generated in `frontend/bindings/` -4. Frontend calls Go methods with full type safety, no HTTP overhead - -### Current Binding Architecture - -```go -// cmd/core-gui/main.go -app.RegisterService(application.NewService(coreService)) // Only Core is registered -``` - -**Problem:** Only `Core` is registered with Wails. Sub-services (crypt, workspace, display, etc.) are internal to Core's service map - their methods aren't directly exposed to JS. - -**Currently exposed** (see `cmd/core-gui/public/bindings/`): -```typescript -// From frontend: -import { ACTION, Config, Service } from './bindings/forge.lthn.ai/core/cli/pkg/core' - -ACTION(msg) // Broadcast IPC message -Config() // Get config service reference -Service("workspace") // Get service by name (returns any) -``` - -**NOT exposed:** Direct calls like `workspace.CreateWorkspace()` or `crypt.Hash()`. - -## Configuration Management - -Core uses a **centralized configuration service** implemented in `pkg/config`, with YAML-based persistence and layered overrides. - -The `pkg/config` package provides: - -- YAML-backed persistence at `~/.core/config.yaml` -- Dot-notation key access (for example: `cfg.Set("dev.editor", "vim")`, `cfg.GetString("dev.editor")`) -- Environment variable overlay support (env vars can override persisted values) -- Thread-safe operations for concurrent reads/writes - -Application code should treat `pkg/config` as the **primary configuration mechanism**. Direct reads/writes to YAML files should generally be avoided from application logic in favour of using this centralized service. - -### Project and Service Configuration Files - -In addition to the centralized configuration service, Core uses several YAML files for project-specific build/CI and service configuration. These live alongside (but are distinct from) the centralized configuration: - -- **Project Configuration** (in the `.core/` directory of the project root): - - `build.yaml`: Build targets, flags, and project metadata. - - `release.yaml`: Release automation, changelog settings, and publishing targets. - - `ci.yaml`: CI pipeline configuration. -- **Global Configuration** (in the `~/.core/` directory): - - `config.yaml`: Centralized user/framework settings and defaults, managed via `pkg/config`. - - `agentic.yaml`: Configuration for agentic services (BaseURL, Token, etc.). -- **Registry Configuration** (`repos.yaml`, auto-discovered): - - Multi-repo registry definition. - - Searched in the current directory and its parent directories (walking up). - - Then in `~/Code/host-uk/repos.yaml`. - - Finally in `~/.config/core/repos.yaml`. - -### Format - -All persisted configuration files described above use **YAML** format for readability and nested structure support. - -### The IPC Bridge Pattern (Chosen Architecture) - -Sub-services are accessed via Core's **IPC/ACTION system**, not direct Wails bindings: - -```typescript -// Frontend calls Core.ACTION() with typed messages -import { ACTION } from './bindings/forge.lthn.ai/core/cli/pkg/core' - -// Open a window -ACTION({ action: "display.open_window", name: "settings", options: { Title: "Settings", Width: 800 } }) - -// Switch workspace -ACTION({ action: "workspace.switch_workspace", name: "myworkspace" }) -``` - -Each service implements `HandleIPCEvents(c *core.Core, msg core.Message)` to process these messages: - -```go -// pkg/display/display.go -func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) error { - switch m := msg.(type) { - case map[string]any: - if action, ok := m["action"].(string); ok && action == "display.open_window" { - return s.handleOpenWindowAction(m) - } - } - return nil -} -``` - -**Why this pattern:** -- Single Wails service (Core) = simpler binding generation -- Services remain decoupled from Wails -- Centralized message routing via `ACTION()` -- Services can communicate internally using same pattern - -**Current gap:** Not all service methods have IPC handlers yet. See `HandleIPCEvents` in each service to understand what's wired up. - -### Generating Bindings - -Wails v3 bindings are typically generated in the GUI repository (e.g., `core-gui`). - -```bash -wails3 generate bindings # Regenerate after Go changes -``` - ---- - -### Service Interfaces (`pkg/framework/core/interfaces.go`) - -```go -type Config interface { - Get(key string, out any) error - Set(key string, v any) error -} - -type Display interface { - OpenWindow(opts ...WindowOption) error -} - -type Workspace interface { - CreateWorkspace(identifier, password string) (string, error) - SwitchWorkspace(name string) error - WorkspaceFileGet(filename string) (string, error) - WorkspaceFileSet(filename, content string) error -} - -type Crypt interface { - EncryptPGP(writer io.Writer, recipientPath, data string, ...) (string, error) - DecryptPGP(recipientPath, message, passphrase string, ...) (string, error) -} -``` - ---- - -## Current State (Prototype) - -### Working - -| Package | Notes | -|---------|-------| -| `pkg/framework/core` | Service container, DI, thread-safe - solid | -| `pkg/config` | Layered YAML configuration, XDG paths - solid | -| `pkg/crypt` | Hashing, checksums, symmetric/asymmetric - solid, well-tested | -| `pkg/help` | Embedded docs, full-text search - solid | -| `pkg/i18n` | Multi-language with go-i18n - solid | -| `pkg/io` | Medium interface + local backend - solid | -| `pkg/repos` | Multi-repo registry & management - solid | -| `pkg/agentic` | AI agent task management - solid | -| `pkg/mcp` | Model Context Protocol service - solid | - ---- - -## Package Deep Dives - -### pkg/crypt - -The crypt package provides a comprehensive suite of cryptographic primitives: -- **Hashing & Checksums**: SHA-256, SHA-512, and CRC32 support. -- **Symmetric Encryption**: AES-GCM and ChaCha20-Poly1305 for secure data at rest. -- **Key Derivation**: Argon2id for secure password hashing. -- **Asymmetric Encryption**: PGP implementation in the `pkg/crypt/openpgp` subpackage using `github.com/ProtonMail/go-crypto`. - -### pkg/io - Storage Abstraction - -```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 -} -``` - -Implementations: `local/`, `sftp/`, `webdav/` - ---- - -## Future Work - -### Phase 1: Core Stability -- [x] ~~Fix workspace medium injection (critical blocker)~~ -- [x] ~~Initialize `io.Local` global~~ -- [x] ~~Clean up dead code (orphaned vars, broken wrappers)~~ -- [x] ~~Wire up IPC handlers for all services (config, crypt, display, help, i18n, workspace)~~ -- [x] ~~Complete display menu handlers (New/List workspace)~~ -- [x] ~~Tray icon setup with asset embedding~~ -- [x] ~~Test coverage for io packages~~ -- [ ] System tray brand-specific menus - -### Phase 2: Multi-Brand Support -- [ ] Define brand configuration system (config? build flags?) -- [ ] Implement brand-specific tray menus (AdminHub, ServerHub, GatewayHub, DeveloperHub, ClientHub) -- [ ] Brand-specific theming/assets -- [ ] Per-brand default workspace configurations - -### Phase 3: Remote Storage -- [ ] Complete SFTP backend (`pkg/io/sftp/`) -- [ ] Complete WebDAV backend (`pkg/io/webdav/`) -- [ ] Workspace sync across storage backends -- [ ] Conflict resolution for multi-device access - -### Phase 4: Enhanced Crypto -- [ ] Key management UI (import/export, key rotation) -- [ ] Multi-recipient encryption -- [ ] Hardware key support (YubiKey, etc.) -- [ ] Encrypted workspace backup/restore - -### Phase 5: Developer Experience -- [ ] TypeScript types for IPC messages (codegen from Go structs) -- [ ] Hot-reload for service registration -- [ ] Plugin system for third-party services -- [ ] CLI tooling for workspace management - -### Phase 6: Distribution -- [ ] Auto-update mechanism -- [ ] Platform installers (DMG, MSI, AppImage) -- [ ] Signing and notarization -- [ ] Crash reporting integration - ---- - -## Getting Help - -- **[User Guide](docs/user-guide.md)**: Detailed usage and concepts. -- **[FAQ](docs/faq.md)**: Frequently asked questions. -- **[Workflows](docs/workflows.md)**: Common task sequences. -- **[Troubleshooting](docs/troubleshooting.md)**: Solving common issues. -- **[Configuration](docs/configuration.md)**: Config file reference. - -```bash -# Check environment -core doctor - -# Command help -core --help -``` - ---- - -## For New Contributors - -1. Run `task test` to verify all tests pass -2. Follow TDD: `task test-gen` creates stubs, implement to pass -3. The dual-constructor pattern is intentional: `New(deps)` for tests, `Register()` for runtime -4. IPC handlers in each service's `HandleIPCEvents()` are the frontend bridge +[EUPL-1.2](https://opensource.org/licenses/EUPL-1.2)