Hacking on Osmedeus
This document describes the technical architecture and development practices for Osmedeus. Itโs intended for developers who want to understand, modify, or extend the codebase.Table of Contents
Architecture Overview
Osmedeus follows a layered architecture:Core Components
Workflow Types
Step Types
remote-bash Step Type
Theremote-bash step type allows per-step Docker or SSH execution, independent of the module-level runner:
Decision Routing (Conditional Branching)
Steps can include decision routing to jump to different steps based on switch/case matching:_end special value terminates workflow execution from the current step.
Execution Context
- Variables are set by the executor (built-in variables)
- Params are user-provided
- Exports are step outputs that propagate to subsequent steps
Workflow Engine
Parser
The parser (internal/parser/parser.go) handles YAML parsing:
Loader
The loader (internal/parser/loader.go) provides caching and lookup:
- Check cache
- Try
workflows/<name>.yaml - Try
workflows/<name>-flow.yaml - Try
workflows/modules/<name>.yaml - Try
workflows/modules/<name>-module.yaml
Execution Pipeline
Flow
Executor
- Initialize execution context with built-in variables
- Create and setup the appropriate runner
- Iterate through steps, dispatching to appropriate handler
- Handle pre-conditions, exports, and decision routing
- Process on_success/on_error actions
Step Dispatcher
The dispatcher uses a plugin registry pattern for extensible step type handling:BashExecutor- handlesbashstepsFunctionExecutor- handlesfunctionstepsForeachExecutor- handlesforeachstepsParallelExecutor- handlesparallel-stepsstepsRemoteBashExecutor- handlesremote-bashstepsHTTPExecutor- handleshttpstepsLLMExecutor- handlesllmsteps
Runner System
Interface
Host Runner
Simple local execution usingos/exec:
Docker Runner
Supports both ephemeral (docker run --rm) and persistent (docker exec) modes:
SSH Runner
Usesgolang.org/x/crypto/ssh for remote execution:
Authentication Middleware
Auth Types
The server supports two authentication methods:| Method | Header | Description |
|---|---|---|
| API Key | x-osm-api-key | Simple token-based auth |
| JWT | Authorization: Bearer <token> | Token from /osm/api/login |
Priority Logic
- API Key Auth - If
EnabledAuthAPIis true - JWT Auth - If API key auth disabled and NoAuth is false
- No Auth - If NoAuth option is true
APIKeyAuth Implementation
- Case-sensitive exact matching
- Rejects empty/whitespace-only keys
- Rejects placeholder values (โnullโ, โundefinedโ, โnilโ)
Template Engine
Variable Resolution
The template engine (internal/template/engine.go) handles {{variable}} interpolation:
- Check context variables
- Check environment variables (optional)
- Return empty string if not found
Built-in Variable Injection
Foreach Variable Syntax
Foreach uses[[variable]] syntax (double brackets) to avoid conflicts with template variables:
Function Registry
Otto JavaScript Runtime
Functions are implemented in Go and exposed to an Otto JavaScript VM:Adding New Functions
- Add the Go implementation in the appropriate file:
- Register in
registerFunctions():
Output and Control Functions
These functions provide output and execution control within workflows:Function Execution
Scheduler System
Trigger Types
Scheduler
The scheduler manages workflow triggers using gocron for cron jobs and fsnotify for file watching:Event Filtering
Events are matched using JavaScript expressions:Database Layer
Multi-Engine Support
Models
Repository Pattern
Schedule Operations
JSONL Import
Testing
Test Structure
Running Tests
Writing Tests
Use testify for assertions:Adding New Features
Adding a New Step Type
- Define the type in
internal/core/types.go:
- Create executor in
internal/executor/mynew_executor.go:
- Register in dispatcher (
internal/executor/dispatcher.go):
Adding a New Runner
- Create runner in
internal/runner/myrunner.go:
- Add type in
internal/core/types.go:
- Register in factory (
internal/runner/runner.go):
Adding a New Installer Mode
- Create installer in
internal/installer/mymode.go:
- Add flag in
pkg/cli/install.go:
- Register in
runInstallBinary()switch statement.
internal/installer/nix.go for a complete example.
Adding a New API Endpoint
- Add handler in
pkg/server/handlers/handlers.go:
- Register route in
pkg/server/server.go:
Adding a New CLI Command
- Create command file in
pkg/cli/mycommand.go:
- Register in
pkg/cli/root.go:
CLI Shortcuts and Tips
Command Aliases
osmedeus func- alias forosmedeus functionosmedeus func e- alias forosmedeus function eval
New Scan Flags
-c, --concurrency- Number of targets to scan concurrently--timeout- Scan timeout (e.g.,2h,3h,1d)--repeat- Repeat scan after completion--repeat-wait-time- Wait time between repeats (e.g.,30m,1h,1d)-mcan be specified multiple times to run modules in sequence
Debugging Tips
- Use
osmedeus --usage-exampleto see comprehensive examples for all commands - Use
--verboseor--debugfor detailed logging - Use
--dry-runto preview scan execution without running commands - Use
--log-file-tmpto create timestamped log files for debugging
Code Style
- Use
go fmtandgolangci-lint - Follow Go naming conventions
- Use structured logging with zap
- Return errors, donโt panic
- Use context for cancellation
- Write tests for new features