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.
- Discord: http://discord.dappco.re
- Repo: https://github.com/Snider/Core
Core is an opinionated Web3 desktop application framework providing:
- Service-Oriented Architecture - Pluggable services with dependency injection
- Encrypted Workspaces - Each workspace gets its own PGP keypair, files are obfuscated
- Cross-Platform Storage - Abstract storage backends (local, SFTP, WebDAV) behind a
Mediuminterface - Multi-Brand Support - Same codebase powers different "hub" apps (AdminHub, ServerHub, GatewayHub, DeveloperHub, ClientHub)
- Built-in Crypto - PGP encryption/signing, hashing, checksums as first-class citizens
Mental model: A secure, encrypted workspace manager where each "workspace" is a cryptographically isolated environment. The framework handles windows, menus, trays, config, and i18n.
import core "github.com/Snider/Core"
app := core.New(
core.WithServiceLock(),
)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# 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| 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 |
Generate coverage.txt |
task cov-view |
Open HTML coverage report |
task sync |
Update public API Go files |
.
├── core.go # Facade re-exporting pkg/core
├── pkg/
│ ├── core/ # Service container, DI, Runtime[T]
│ ├── config/ # JSON persistence, XDG paths
│ ├── display/ # Windows, tray, menus (Wails)
│ ├── crypt/ # Hashing, checksums, PGP
│ │ └── openpgp/ # Full PGP implementation
│ ├── io/ # Medium interface + backends
│ ├── workspace/ # Encrypted workspace management
│ ├── help/ # In-app documentation
│ └── i18n/ # Internationalization
├── cmd/
│ ├── core/ # CLI application
│ └── core-gui/ # Wails GUI application
└── go.work # Links root, cmd/core, cmd/core-gui
Every service follows this pattern:
// 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().
Services implement HandleIPCEvents(c *core.Core, msg core.Message) error - auto-discovered via reflection. Handles typed actions like core.ActionServiceStartup.
Core uses Wails v3 to expose Go methods to a WebView2 browser runtime. Wails automatically generates TypeScript bindings for registered services.
Documentation: Wails v3 Method Bindings
- Go services with exported methods are registered with Wails
- Run
wails3 generate bindings(orwails3 dev/wails3 build) - TypeScript SDK is generated in
frontend/bindings/ - Frontend calls Go methods with full type safety, no HTTP overhead
// cmd/core-gui/main.go
app.RegisterService(application.NewService(coreService)) // Only Core is registeredProblem: 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/):
// From frontend:
import { ACTION, Config, Service } from './bindings/github.com/Snider/Core/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().
Sub-services are accessed via Core's IPC/ACTION system, not direct Wails bindings:
// Frontend calls Core.ACTION() with typed messages
import { ACTION } from './bindings/github.com/Snider/Core/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:
// 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.
cd cmd/core-gui
wails3 generate bindings # Regenerate after Go changesBindings output to cmd/core-gui/public/bindings/github.com/Snider/Core/ mirroring Go package structure.
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)
}| Package | Notes |
|---|---|
pkg/core |
Service container, DI, thread-safe - solid |
pkg/config |
JSON persistence, XDG paths - solid |
pkg/crypt |
Hashing, checksums, PGP - solid, well-tested |
pkg/help |
Embedded docs, Show/ShowAt - solid |
pkg/i18n |
Multi-language with go-i18n - solid |
pkg/io |
Medium interface + local backend - solid |
pkg/workspace |
Workspace creation, switching, file ops - functional |
| Package | Issues |
|---|---|
pkg/display |
Window creation works; menu/tray handlers are TODOs |
pkg/display/tray.go:52-63 - Commented brand-specific menu items need implementation.
| Package | Notes |
|---|---|
pkg/display |
Integration tests requiring Wails runtime (27% unit coverage) |
Each workspace is:
- Identified by LTHN hash of user identifier
- Has directory structure:
config/,log/,data/,files/,keys/ - Gets a PGP keypair generated on creation
- Files accessed via obfuscated paths
The workspaceList maps workspace IDs to public keys.
Full PGP using github.com/ProtonMail/go-crypto:
CreateKeyPair(name, passphrase)- RSA-4096 with revocation certEncryptPGP()- Encrypt + optional signingDecryptPGP()- Decrypt + optional signature verification
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/
-
Fix workspace medium injection (critical blocker) -
Initializeio.Localglobal -
Clean up dead code (orphaned vars, broken wrappers) -
Wire up IPC handlers for all services (config, crypt, display, help, i18n, workspace) -
Complete display menu handlers (New/List workspace) -
Tray icon setup with asset embedding -
Test coverage for io packages - System tray brand-specific menus
- 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
- Complete SFTP backend (
pkg/io/sftp/) - Complete WebDAV backend (
pkg/io/webdav/) - Workspace sync across storage backends
- Conflict resolution for multi-device access
- Key management UI (import/export, key rotation)
- Multi-recipient encryption
- Hardware key support (YubiKey, etc.)
- Encrypted workspace backup/restore
- 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
- Auto-update mechanism
- Platform installers (DMG, MSI, AppImage)
- Signing and notarization
- Crash reporting integration
- Run
task testto verify all tests pass - Follow TDD:
task test-gencreates stubs, implement to pass - The dual-constructor pattern is intentional:
New(deps)for tests,Register()for runtime - See
cmd/core-gui/main.gofor how services wire together - IPC handlers in each service's
HandleIPCEvents()are the frontend bridge