API Reference¶
This document provides detailed information about the opnDossier internal Go API and its components.
Overview¶
opnDossier is structured with clear separation between CLI interface (cmd/) and internal implementation (internal/). All packages under internal/ are private to the module and not importable by external consumers.
Package Structure¶
opndossier/
├── cmd/ # CLI commands (Cobra framework)
│ ├── root.go # Root command, global flags, version subcommand
│ ├── context.go # CommandContext for dependency injection
│ ├── convert.go # Convert command
│ ├── display.go # Display command
│ ├── validate.go # Validate command
│ ├── shared_flags.go # Shared flags (section, wrap, audit)
│ ├── exitcodes.go # Structured exit codes
│ └── help.go # Custom help templates
├── internal/ # Private application logic
│ ├── cfgparser/ # XML parsing and validation
│ ├── config/ # Configuration management (Viper)
│ ├── converter/ # Data conversion and report generation
│ │ ├── builder/ # Programmatic markdown builder
│ │ └── formatters/ # Security scoring, transformers
│ ├── compliance/ # Plugin interfaces
│ ├── plugins/ # Plugin implementations (stig, sans, firewall)
│ ├── audit/ # Audit engine and plugin management
│ ├── display/ # Terminal display formatting
│ ├── export/ # File export functionality
│ ├── logging/ # Structured logging (charmbracelet/log)
│ ├── progress/ # CLI progress indicators
│ └── validator/ # Configuration validation
├── pkg/ # Public API packages
│ ├── model/ # Platform-agnostic CommonDevice domain model
│ ├── parser/ # Factory + DeviceParser interface
│ │ └── opnsense/ # OPNsense parser + schema→CommonDevice converter
│ └── schema/
│ └── opnsense/ # Canonical OPNsense XML data model structs
└── main.go # Entry point
CLI Package (cmd/)¶
Root Command¶
CommandContext Pattern¶
The cmd package uses CommandContext for dependency injection into subcommands:
type CommandContext struct {
Config *config.Config
Logger *logging.Logger
}
// Access in subcommands:
cmdCtx := GetCommandContext(cmd)
logger := cmdCtx.Logger
config := cmdCtx.Config
Global Flags¶
| Flag | Type | Default | Description |
|---|---|---|---|
--config |
string | "" |
Custom config file path |
--verbose, -v |
bool | false | Enable debug logging |
--quiet, -q |
bool | false | Suppress non-error output |
--color |
string | "auto" |
Color output (auto, always, never) |
--no-progress |
bool | false | Disable progress indicators |
--timestamps |
bool | false | Include timestamps in logs |
--minimal |
bool | false | Minimal output mode |
--json-output |
bool | false | Output errors in JSON format |
Parser Package (internal/cfgparser)¶
DeviceParser Interface¶
The DeviceParser interface (defined in pkg/parser/factory.go) abstracts device-specific parsing behind a common contract:
type DeviceParser interface {
Parse(ctx context.Context, r io.Reader) (*common.CommonDevice, error)
ParseAndValidate(ctx context.Context, r io.Reader) (*common.CommonDevice, error)
}
The parser.Factory auto-detects device type from the XML root element and delegates to the appropriate DeviceParser. The underlying OPNsense XML parser (internal/cfgparser/XMLParser) still produces schema.OpnSenseDocument, which is then converted to common.CommonDevice by the OPNsense-specific parser in pkg/parser/opnsense/.
Factory Usage¶
factory := parser.NewFactory(cfgparser.NewXMLParser())
file, err := os.Open("config.xml")
if err != nil {
return err
}
defer file.Close()
// Auto-detect device type and parse to CommonDevice
device, err := factory.CreateDevice(context.Background(), file, "", false)
if err != nil {
return fmt.Errorf("parse failed: %w", err)
}
// With validation
device, err := factory.CreateDevice(context.Background(), file, "", true)
if err != nil {
return fmt.Errorf("parse and validate failed: %w", err)
}
The underlying XMLParser (internal/cfgparser/) supports UTF-8, US-ASCII, ISO-8859-1 (Latin1), and Windows-1252 encodings. Input is limited to 10MB by default (DefaultMaxInputSize).
Breaking Change: ParserFactory / NewParserFactory() were renamed to Factory / NewFactory() to comply with Go naming conventions (revive stutters rule). The internal/model/ re-export layer was removed; import pkg/parser directly. NewFactory() now requires an XMLDecoder argument.
| Old | New |
|---|---|
parser.ParserFactory |
parser.Factory |
parser.NewParserFactory() |
parser.NewFactory(cfgparser.NewXMLParser()) |
model.NewParserFactory() |
parser.NewFactory(cfgparser.NewXMLParser()) |
parser.NewFactory() |
parser.NewFactory(cfgparser.NewXMLParser()) |
Data Model (pkg/schema/opnsense, pkg/model)¶
CommonDevice¶
The platform-agnostic device model, defined in pkg/model/:
type CommonDevice struct {
DeviceType DeviceType `json:"device_type" yaml:"device_type"`
Version string `json:"version,omitempty" yaml:"version,omitempty"`
System System `json:"system" yaml:"system,omitempty"`
Interfaces []Interface `json:"interfaces,omitempty" yaml:"interfaces,omitempty"`
FirewallRules []FirewallRule `json:"firewallRules,omitempty" yaml:"firewallRules,omitempty"`
NAT NATConfig `json:"nat" yaml:"nat,omitempty"`
VPN VPN `json:"vpn" yaml:"vpn,omitempty"`
Routing Routing `json:"routing" yaml:"routing,omitempty"`
DNS DNSConfig `json:"dns" yaml:"dns,omitempty"`
DHCP []DHCPScope `json:"dhcp,omitempty" yaml:"dhcp,omitempty"`
// ... additional fields (Users, Groups, Certificates, etc.)
// Computed/enrichment fields (populated by prepareForExport)
Statistics *Statistics `json:"statistics,omitempty"`
SecurityAssessment *SecurityAssessment `json:"securityAssessment,omitempty"`
// ...
}
The XML DTO remains as schema.OpnSenseDocument in pkg/schema/opnsense/. The OPNsense-specific parser in pkg/parser/opnsense/ converts the XML DTO into CommonDevice. The pkg/parser/ package provides the Factory and DeviceParser interface for consumers.
Converter Package (internal/converter)¶
Converter Interface¶
type Converter interface {
ToMarkdown(ctx context.Context, data *common.CommonDevice) (string, error)
}
Generator Interface¶
type Generator interface {
Generate(ctx context.Context, cfg *common.CommonDevice, opts Options) (string, error)
}
type StreamingGenerator interface {
Generator
GenerateToWriter(ctx context.Context, w io.Writer, cfg *common.CommonDevice, opts Options) error
}
MarkdownBuilder (internal/converter/builder)¶
The MarkdownBuilder provides programmatic report generation:
builder := builder.NewMarkdownBuilder()
// Generate a standard report
report, err := builder.BuildStandardReport(doc)
// Generate a comprehensive report
report, err := builder.BuildComprehensiveReport(doc)
// Build individual sections
systemSection := builder.BuildSystemSection(doc)
networkSection := builder.BuildNetworkSection(doc)
securitySection := builder.BuildSecuritySection(doc)
servicesSection := builder.BuildServicesSection(doc)
Security Functions (internal/converter/formatters)¶
// Standalone security assessment functions
formatters.AssessRiskLevel("high") // Returns: "🟠 High Risk"
formatters.CalculateSecurityScore(doc) // Returns: 0-100
formatters.AssessServiceRisk("telnet") // Returns: risk label string
formatters.FilterSystemTunables(items, true) // Filter security-related tunables
formatters.GroupServicesByStatus(services) // Group by running/stopped
Configuration Package (internal/config)¶
Config Type¶
type Config struct {
InputFile string `mapstructure:"input_file"`
OutputFile string `mapstructure:"output_file"`
Verbose bool `mapstructure:"verbose"`
Quiet bool `mapstructure:"quiet"`
Theme string `mapstructure:"theme"`
Format string `mapstructure:"format"`
Sections []string `mapstructure:"sections"`
WrapWidth int `mapstructure:"wrap"`
JSONOutput bool `mapstructure:"json_output"`
Minimal bool `mapstructure:"minimal"`
NoProgress bool `mapstructure:"no_progress"`
// ... additional fields
}
Loading Configuration¶
// Load with default config file location
cfg, err := config.LoadConfig("")
// Load with CLI flag binding
cfg, err := config.LoadConfigWithFlags(configFile, cmd.Flags())
// Load with custom Viper instance
cfg, err := config.LoadConfigWithViper(configFile, v)
Configuration Precedence¶
- CLI Flags (highest priority)
- Environment Variables (
OPNDOSSIER_*) - Configuration File (
~/.opnDossier.yaml) - Default Values (lowest priority)
Export Package (internal/export)¶
FileExporter¶
type FileExporter struct {
// ...
}
func NewFileExporter(logger *logging.Logger) *FileExporter
// Export writes content to a file with path validation and atomic writes
func (e *FileExporter) Export(ctx context.Context, content, path string) error
Features: path traversal protection, atomic writes, platform-appropriate permissions (0600 default).
Logging Package (internal/logging)¶
Logger¶
Wraps charmbracelet/log for structured logging:
logger, err := logging.New(logging.Config{
Level: "info",
Format: "text",
Output: os.Stderr,
ReportCaller: true,
ReportTimestamp: true,
})
logger.Info("Processing file", "filename", path, "size", fileSize)
logger.Debug("Detailed info", "key", value)
logger.Warn("Potential issue", "error", err)
logger.Error("Operation failed", "error", err)
// Create scoped loggers
fileLogger := logger.WithFields("operation", "convert", "file", filename)
Compliance Package (internal/compliance)¶
Plugin Interface¶
type Plugin interface {
Name() string
Version() string
Description() string
RunChecks(device *common.CommonDevice) []Finding
GetControls() []Control
GetControlByID(id string) (*Control, error)
ValidateConfiguration() error
}
See Plugin Development Guide for details.
Error Handling¶
All packages use standard Go error handling with context-aware error wrapping:
Sentinel Errors¶
// cfgparser package
var ErrMissingOpnSenseDocumentRoot = errors.New("invalid XML: missing opnsense root element")
// converter package
var ErrNilDevice = errors.New("device configuration is nil")
Extension Points¶
Adding New Commands¶
- Create command file in
cmd/ - Implement command with
CommandContextpattern - Add to root command in
init() - Update help text
Adding Configuration Options¶
- Add field to
Configstruct ininternal/config/config.go - Set default in
LoadConfigWithViper - Add CLI flag in appropriate command file
- Add validation if needed
- Update documentation
Adding New Output Formats¶
- Implement the
Generatorinterface ininternal/converter/ - Register the format in the convert command
- Add tests and documentation
This API reference covers the internal interfaces. For the most up-to-date information, refer to the source code and inline documentation.