The DuckOps Agent is a high‑performance, stateless worker engine designed to execute security and DevSecOps tasks in isolated environments. It follows a strict Kernel‑Policy Architecture built on Hexagonal principles, ensuring clear separation of concerns, testability, and easy extensibility.
The project is organized into the following layers (see architecture.md for details):
- Domain – Core business types (
Tool,Task,Result). No external dependencies. - Kernel – Orchestrates execution. Depends only on the domain.
registry.go– Registers all available tools.runtime.go– Wraps tool execution with panic recovery, metrics, and logging.dispatcher.go– Routes incoming tasks from the MessageBus (RabbitMQ) to the kernel.
- Ports – Interfaces for external systems (LLM, Memory, Filesystem, etc.).
- Adapters – Implement ports (RabbitMQ, gRPC, DB, LLM). Contain no business logic.
- Tools – Stateless execution units that implement the
domain.Toolinterface and use only ports. - Cmd – CLI entry point that invokes
kernel.Execute(task).
Agent/
├─ cmd/ # CLI entry points
├─ domain/ # Core types and interfaces
├─ kernel/ # Registry, Runtime, Dispatcher
├─ ports/ # Port interfaces (LLM, Memory, FS, etc.)
├─ adapters/ # Implementations of ports
│ ├─ rabbitmq/ # RabbitMQ adapter
│ ├─ grpc/ # gRPC adapter
│ └─ llm/ # LLM adapter
├─ tools/ # Stateless tool implementations
│ ├─ scan/ # Example: SecurityScanner
│ └─ remediation/ # Example: RemediationTool
└─ internal/ # Shared utilities (logging, errors)
- Maintains a map of tool name →
domain.Toolinstance. - Populated during bootstrap by each tool calling
registry.Register(name, tool).
- Executes a tool inside a safe wrapper.
- Handles panic recovery, logs execution time, and returns a structured
domain.Result.
- Listens on the MessageBus (RabbitMQ) for incoming tasks.
- Deserialises the payload and forwards it to the kernel.
Every tool must implement the following interface (see domain/tool.go):
type Tool interface {
Name() string
Run(ctx context.Context, task Task) (Result, error)
}- Stateless – No internal mutable state between runs.
- No direct I/O – Use injected ports for filesystem, LLM, memory, etc.
- Error Handling – Return
types.Newortypes.WrapwrappedAppErrorobjects. - Deterministic – Given the same input, the tool should produce the same output (unless explicitly nondeterministic, e.g., LLM).
type SecurityScanner struct {
llm ports.LLM // injected port
}
func (s *SecurityScanner) Name() string { return "security-scan" }
func (s *SecurityScanner) Run(ctx context.Context, task domain.Task) (domain.Result, error) {
target, ok := task.Args["target"].(string)
if !ok {
return domain.Result{}, types.New(types.ErrCodeInvalidInput, "missing target path")
}
finding, err := s.llm.Generate(ctx, "Scan this: "+target)
if err != nil {
return domain.Result{}, types.Wrap(err, types.ErrCodeToolFailed, "llm analysis failed")
}
return domain.Result{Success: true, Data: finding}, nil
}- Create a new package under
tools/. - Implement the
Toolinterface. - Register the tool in
kernel/registry.goduring bootstrap. - Write unit tests in the same package (
*_test.go). - Add any required port interfaces in
ports/and adapters if needed. - Update
README.md(this file) with a short description under Available Tools.
- Run all unit tests:
go test ./... - Use the
mockadapters inadapters/mock/to stub external services. - CI pipeline runs
golangci-lintandgo vetto enforce code quality.
<<<<<<< HEAD
This repository depends on DuckOps Shared.
It provides the shared AppError system, LLM ports, and event types used to communicate with the server.
fb454bbb5b4bd6625d326e3c041c589d119b8087
<<<<<<< HEAD
# Example go.mod replace
replace github.com/SecDuckOps/shared => ../shared=======
- Fork the repository.
- Create a feature branch (
git checkout -b feat/your-feature). - Follow the Hexagonal Architecture guidelines – keep domain pure.
- Write tests for every new function.
- Run
go fmtandgolangci-lintlocally. - Submit a Pull Request with a clear description and link to the relevant issue.
fb454bbb5b4bd6625d326e3c041c589d119b8087
<<<<<<< HEAD The Agent is a worker that interacts with the Server asynchronously via RabbitMQ. It is essentially a "Remote Procedure Call" target for the Server's Orchestrator. It remains completely unaware of the Server's persistence or state machine.
MIT License – see LICENSE file.
fb454bbb5b4bd6625d326e3c041c589d119b8087
DuckOps Agent: Secure, Isolated, Autonomous.