Co-authored-by: Charon (snider-linux) <charon@lethean.io> Co-authored-by: Snider <snider@host.uk.com> Co-authored-by: Virgil <virgil@lethean.io> Co-authored-by: Claude <developers@lethean.io> Reviewed-on: #2 Co-authored-by: Snider <snider@lethean.io> Co-committed-by: Snider <snider@lethean.io>
14 KiB
Here is the technical documentation for the Core framework packages.
Core Framework Documentation
Package: pkg/log
1. Overview
pkg/log acts as the central observability and error handling primitive for the framework. It combines structured logging with a rich error type system (Err), allowing operational context (Operations, Codes) to travel with errors up the stack. It is designed to be used both standalone and as an injectable service within the Core framework.
2. Public API
Error Types & Functions
type Err: Struct implementingerrorwith fields forOp(operation),Msg(message),Err(wrapped error), andCode(machine-readable code).func E(op, msg string, err error) error: Creates a new error with operational context.func Wrap(err error, op, msg string) error: Wraps an existing error, preserving existing codes if present.func WrapCode(err error, code, op, msg string) error: Wraps an error and assigns a specific error code.func NewCode(code, msg string) error: Creates a sentinel error with a code.func Is(err, target error) bool: Wrapper forerrors.Is.func As(err error, target any) bool: Wrapper forerrors.As.func Join(errs ...error) error: Wrapper forerrors.Join.func Op(err error) string: Extracts the operation name from an error chain.func ErrCode(err error) string: Extracts the error code from an error chain.func StackTrace(err error) []string: Returns a slice of operations leading to the error.func LogError(err error, op, msg string) error: Logs an error and returns it wrapped (reduces boilerplate).func LogWarn(err error, op, msg string) error: Logs a warning and returns it wrapped.func Must(err error, op, msg string): Panics if error is not nil, logging it first.
Logging Types & Functions
type Logger: The main logging struct.type Level: Integer type for log verbosity (LevelQuiettoLevelDebug).type Options: Configuration struct for Logger (Level, Output, Rotation).type RotationOptions: Config for log file rotation (Size, Age, Backups, Compression).func New(opts Options) *Logger: Constructor.func Default() *Logger: Returns the global default logger.func SetDefault(l *Logger): Sets the global default logger.func (l *Logger) Debug/Info/Warn/Error/Security(msg string, keyvals ...any): Leveled logging methods.
Service Integration
type Service: WrapsLoggerfor framework integration.func NewService(opts Options) func(*framework.Core) (any, error): Factory for dependency injection.type QueryLevel,type TaskSetLevel: Message types for runtime management.
3. Internal Design
- Contextual Errors: The
Errstruct forms a linked list via theErrfield (inner error), allowing the reconstruction of a logical stack trace (opsequence) distinct from the runtime stack trace. - Concurrency: The
Loggeruses async.RWMutexto guard configuration and writes, ensuring thread safety. - Rotation Strategy: The
RotatingWriterimplementsio.WriteCloser. It lazily opens files and checks size thresholds on every write, leveragingpkg/ioto abstract the filesystem. - Framework Integration: The
Servicestruct embedsframework.ServiceRuntime, utilizing the Actor pattern (Queries and Tasks) to allow dynamic log level adjustment at runtime without restarting the application.
4. Dependencies
forge.lthn.ai/core/cli/pkg/io: Used byrotation.goto handle file operations (renaming, deleting, writing) abstractly.forge.lthn.ai/core/cli/pkg/framework: Used byservice.goto hook into the application lifecycle and message bus.- Standard Lib:
errors,fmt,os,sync,time.
5. Test Coverage Notes
- Error Unwrapping: Verify
errors.Isanderrors.Aswork correctly through deep chains oflog.Err. - Logical Stack Traces: Ensure
StackTrace()returns the correct order of operations["app.Run", "db.Query", "net.Dial"]. - Log Rotation: Critical to test the boundary conditions of
MaxSizeandMaxBackupsusing a Mock Medium to avoid actual disk I/O. - Concurrency: Race detection on
Loggerwhen changing levels while logging is active.
6. Integration Points
- Application-wide: This is the most imported package. All other packages should use
log.Eorlog.Wrapinstead offmt.Errorforerrors.New. - Core Framework: The
Serviceis designed to be passed tocore.New().
Package: pkg/config
1. Overview
pkg/config provides 12-factor app configuration management. It layers configuration sources in a specific precedence (Environment > Config File > Defaults) and exposes them via a typed API or a dot-notation getter. It abstracts the underlying storage, allowing configs to be loaded from disk or memory.
2. Public API
type Config: The main configuration manager.type Option: Functional option pattern for configuration.func New(opts ...Option) (*Config, error): Constructor.func LoadEnv(prefix string) map[string]any: Helper to parse environment variables into a map.func (c *Config) Get(key string, out any) error: Unmarshals a key (or root) into a struct.func (c *Config) Set(key string, v any) error: Sets a value and persists it to storage.func (c *Config) LoadFile(m coreio.Medium, path string) error: Merges a file into the current config.type Service: Framework service wrapper forConfig.func NewConfigService(c *core.Core) (any, error): Factory for dependency injection.
3. Internal Design
- Engine: Uses
spf13/viperas the underlying configuration engine for its merging and unmarshalling logic. - Abstraction: Unlike standard Viper usage, this package decouples the filesystem using
pkg/io.Medium. This allows the config system to work in sandboxed environments or with mock filesystems. - Persistence: The
Setmethod triggers an immediate write-back to the storage medium, making the config file the source of truth for runtime changes. - Environment Mapping: Automatically maps
CORE_CONFIG_FOO_BARtofoo.barusing astrings.Replacer.
4. Dependencies
github.com/spf13/viper: Core logic for map merging and unmarshalling.gopkg.in/yaml.v3: For marshalling data when saving.forge.lthn.ai/core/cli/pkg/io: For reading/writing config files.forge.lthn.ai/core/cli/pkg/framework/core: For service integration and error handling.
5. Test Coverage Notes
- Precedence: Verify that Environment variables override File values.
- Persistence: Test that
Set()writes valid YAML back to theMedium. - Type Safety: Ensure
Get()correctly unmarshals into complex structs and returns errors on type mismatches.
6. Integration Points
- Bootstrap: Usually the first service initialized in
core.New(). - Service Configuration: Other services (like
authorlog) should injectconfig.Serviceto retrieve their startup settings.
Package: pkg/io
1. Overview
pkg/io provides a filesystem abstraction layer (Medium). Its philosophy is to decouple business logic from the os package, facilitating easier testing (via mocks) and security (via sandboxing).
2. Public API
type Medium: Interface defining filesystem operations (Read,Write,List,Stat,Open,Create,Delete,Rename, etc.).var Local: A pre-initializedMediumfor the host root filesystem.func NewSandboxed(root string) (Medium, error): Returns aMediumrestricted to a specific directory.type MockMedium: In-memory implementation ofMediumfor testing.func NewMockMedium() *MockMedium: Constructor for the mock.- Helpers:
Read,Write,Copy,EnsureDir,IsFile,ReadStream,WriteStream(acceptMediumas first arg).
3. Internal Design
- Interface Segregation: The
Mediuminterface mimics the capabilities ofosandio/fsbut bundles them into a single dependency. - Mocking:
MockMediumusesmap[string]stringfor files andmap[string]boolfor directories. It implements manual path logic to simulate filesystem behavior (e.g., verifying a directory is empty before deletion) without touching the disk. - Sandboxing: The
localimplementation (imported internally) enforces path scoping to prevent traversal attacks when usingNewSandboxed.
4. Dependencies
- Standard Lib:
io,io/fs,os,path/filepath,strings,time. forge.lthn.ai/core/cli/pkg/io/local: (Implied) The concrete implementation for OS disk access.
5. Test Coverage Notes
- Mock fidelity: The
MockMediummust behave exactly like the OS. E.g.,Renameshould fail if the source doesn't exist;Deleteshould fail if a directory is not empty. - Sandboxing: Verify that
..traversal attempts inNewSandboxedcannot access files outside the root.
6. Integration Points
- Universal Dependency: Used by
log(rotation),config(loading), andauth(user DB). - Testing: Application code should accept
io.Mediumin constructors rather than usingos.Opendirectly, enabling unit tests to useNewMockMedium().
Package: pkg/crypt
1. Overview
pkg/crypt provides "batteries-included," opinionated cryptographic primitives. It abstracts away the complexity of parameter selection (salt length, iteration counts, nonce generation) to prevent misuse of crypto algorithms.
2. Public API
- Hashing:
HashPassword(Argon2id),VerifyPassword,HashBcrypt,VerifyBcrypt. - Symmetric:
Encrypt/Decrypt(ChaCha20-Poly1305),EncryptAES/DecryptAES(AES-GCM). - KDF:
DeriveKey(Argon2),DeriveKeyScrypt,HKDF. - Checksums:
SHA256File,SHA512File,SHA256Sum,SHA512Sum. - HMAC:
HMACSHA256,HMACSHA512,VerifyHMAC.
3. Internal Design
- Safe Defaults: Uses Argon2id for password hashing with tuned parameters (64MB memory, 3 iterations).
- Container Format: Symmetric encryption functions return a concatenated byte slice:
[Salt (16b) | Nonce (Variable) | Ciphertext]. This ensures the decryption function has everything it needs without separate state management. - Randomness: Automatically handles salt and nonce generation using
crypto/rand.
4. Dependencies
golang.org/x/crypto: For Argon2, ChaCha20, HKDF, Scrypt.- Standard Lib:
crypto/aes,crypto/cipher,crypto/rand,crypto/sha256.
5. Test Coverage Notes
- Interoperability: Ensure
Encryptoutput can be read byDecrypt. - Tamper Resistance: manually modifying a byte in the ciphertext or nonce must result in a decryption failure (AuthTag check).
- Vectors: Validate hashing against known test vectors where possible.
6. Integration Points
- Auth: Heavily used by
pkg/authfor password storage and potentially for encrypted user data. - Data Protection: Any service requiring data at rest encryption should use
crypt.Encrypt.
Package: pkg/auth
1. Overview
pkg/auth implements a persistent user identity system based on OpenPGP challenge-response authentication. It supports a unique "Air-Gapped" workflow where challenges and responses are exchanged via files, alongside standard online methods. It manages user lifecycles, sessions, and key storage.
2. Public API
type Authenticator: Main logic controller.type User: User metadata struct.type Session: Active session token struct.func New(m io.Medium, opts ...Option) *Authenticator: Constructor.func (a *Authenticator) Register(username, password string) (*User, error): Creates new user and PGP keys.func (a *Authenticator) Login(userID, password string) (*Session, error): Password-based fallback login.func (a *Authenticator) CreateChallenge(userID string) (*Challenge, error): Starts PGP auth flow.func (a *Authenticator) ValidateResponse(userID string, signedNonce []byte) (*Session, error): Completes PGP auth flow.func (a *Authenticator) ValidateSession(token string) (*Session, error): Checks token validity.func (a *Authenticator) WriteChallengeFile(userID, path string) error: For air-gapped flow.func (a *Authenticator) ReadResponseFile(userID, path string) (*Session, error): For air-gapped flow.
3. Internal Design
- Storage Layout: Uses a flat-file database approach on
io.Medium:users/{id}.pub: Public Key.users/{id}.key: Encrypted Private Key.users/{id}.lthn: Password Hash.users/{id}.json: Encrypted metadata.
- Identity: User IDs are hashes of usernames to anonymize storage structure.
- Flow:
- Server generates random nonce.
- Server encrypts nonce with User Public Key.
- User decrypts nonce (client-side) and signs it.
- Server validates signature against User Public Key.
4. Dependencies
forge.lthn.ai/core/cli/pkg/io: For user database storage.forge.lthn.ai/core/cli/pkg/crypt/lthn: (Implied) Specific password hashing.forge.lthn.ai/core/cli/pkg/crypt/pgp: (Implied) OpenPGP operations.forge.lthn.ai/core/cli/pkg/framework/core: Error handling.
5. Test Coverage Notes
- Flow Verification: Full integration test simulating a client: Register -> Get Challenge -> Decrypt/Sign (Mock Client) -> Validate -> Get Token.
- Security: Ensure
serveruser cannot be deleted. Ensure expired sessions are rejected. - Persistence: Ensure user data survives an
Authenticatorrestart (i.e., data is actually written to medium).
6. Integration Points
- API Gateways: HTTP handlers would call
ValidateSessionon every request. - CLI Tools: Would use
WriteChallengeFile/ReadResponseFilefor offline authentication.