Table of Contents
- Project Structure
- Architecture Overview
- Core Components
- Workflow Engine
- Execution Pipeline
- Runner System
- Authentication Middleware
- Template Engine
- Function Registry
- Scheduler System
- Workflow Linter
- Database Layer
- Testing
- Adding New Features
- CLI Shortcuts and Tips
Project Structure
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:Event Functions
These functions enable event-driven workflows by generating and emitting events:- Server API - POST to
/osm/api/events/emitif server configured - Redis Pub/Sub - Publish to
osm:events:{topic}in distributed mode - Database Queue - Store in
event_logstable withprocessed=false - Webhooks - Send to configured webhook endpoints
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:Workflow Linter
The workflow linter (internal/linter/) provides static analysis of workflow YAML files to catch common issues before execution.
Usage
Severity Levels
| Severity | Description | Exit Code |
|---|---|---|
| info | Best practice suggestions (e.g., unused exports) | 0 |
| warning | Potential issues that may cause problems | 0 |
| error | Critical issues that will likely cause failures | 1 (with —check) |
Built-in Rules
| Rule | Severity | Description |
|---|---|---|
missing-required-field | warning | Detects missing required fields (name, kind, type) |
duplicate-step-name | warning | Detects multiple steps with the same name |
empty-step | warning | Detects steps with no executable content |
unused-variable | info | Detects exports that are never referenced |
invalid-goto | warning | Detects decision goto references to non-existent steps |
invalid-depends-on | warning | Detects depends_on references to non-existent steps |
circular-dependency | warning | Detects circular references in step dependencies |
undefined-variable rule is available but not enabled by default as it can produce false positives for dynamically-injected variables.
Built-in Variables
The linter recognizes all runtime-injected variables to avoid false positives. These include: Path Variables:BaseFolder, Binaries, Data, ExternalData, ExternalConfigs, Workflows, Workspaces, etc.
Target Variables: Target, target, TargetFile, TargetSpace
Output Variables: Output, output, Workspace, workspace
Metadata Variables: Version, RunUUID, TaskDate, TimeStamp, Today, RandomString
Heuristic Variables: TargetType, TargetRootDomain, TargetTLD, Org, TargetHost, TargetPort, etc.
Chunk Variables: ChunkIndex, ChunkSize, TotalChunks, ChunkStart, ChunkEnd
Linter Architecture
Adding a New Lint Rule
- Create the rule in
internal/linter/rules.go:
- Register in
GetDefaultRules():
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 evalosmedeus db ls- alias forosmedeus db list
Database CLI Commands
Query and manage database tables directly from the CLI:runs: run_uuid, workflow_name, target, workspace, status, completed_steps, total_steps, started_atevent_logs: topic, source, source_type, processed, data_type, workspace, dataartifacts: artifact_path, artifact_type, content_type, workspace, run_idassets: asset_value, host_ip, title, status_code, last_seen_at, technologiesschedules: name, workflow_name, trigger_type, schedule, is_enabled, run_countworkspaces: name, local_path, total_assets, total_vulns, risk_score, last_run
Function Evaluation CLI
Evaluate utility functions from the command line with bulk processing support: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