f1c55ca79b
Audit findings addressed: - PROJECT.md: add 'What This Is' and 'Key Decisions' sections (per audit Step 2) - ARCHITECTURE.md: add internal/jobspec (HCL parser) and internal/model (domain types) to system diagram and component details - PLANS.md: expand REQ coverage lines for Phase 1 and Phase 3 to include all REQ-IDs the work actually delivers (REQ-003 offline-first, REQ-008 slog, REQ-010 --json, REQ-020 hashicorp/hcl) ---ci--- project: orca phase: 0 milestone: v0.1 status: fix ---/ci---
8.7 KiB
8.7 KiB
Architecture: Orca
System Overview
Orca is a single-binary, offline-first orchestration engine. The system consists of six logical components, all compiled into one orca binary and selected via subcommands.
┌─────────────────────────────────────────────────────────────┐
│ orca (single binary) │
├─────────────────────────────────────────────────────────────┤
│ CLI Layer (Cobra) │
│ ├── orca version │
│ ├── orca init │
│ ├── orca status │
│ ├── orca node {join,leave,list} │
│ └── orca job {run,list,stop,logs} │
├─────────────────────────────────────────────────────────────┤
│ Daemon Layer (net/http server) │
│ ├── /healthz (liveness) │
│ ├── /readyz (readiness) │
│ ├── /v1/jobs/* (job control API) │
│ ├── /v1/nodes/* (node registry API) │
│ └── /v1/tasks/* (task lifecycle API) │
├─────────────────────────────────────────────────────────────┤
│ Job Spec Parser (hashicorp/hcl/v2) │
│ └── reads .hcl files → validates → hands to engine │
├─────────────────────────────────────────────────────────────┤
│ Domain Model (internal/model) │
│ ├── Node, Job, Task structs (no I/O) │
│ └── shared by store, engine, daemon │
├─────────────────────────────────────────────────────────────┤
│ Core Engine │
│ ├── Node Registry (in-memory + SQLite persistence) │
│ ├── Task Executor (os/exec with WaitDelay, Go 1.25+) │
│ ├── Job Scheduler (single-node for v0.1) │
│ └── Audit Logger (log/slog JSON handler) │
├─────────────────────────────────────────────────────────────┤
│ State Store (modernc/sqlite, CGO-free) │
│ ~/.orca/orca.db │
└─────────────────────────────────────────────────────────────┘
Component Details
1. CLI Layer (cmd/orca, internal/cli)
- Framework: Cobra (industry standard, familiar to operators)
- Subcommands:
version,init,status,node,job - Output: Human-readable by default;
--jsonflag for machine consumption - Discovery: All subcommands self-document via Cobra's auto-generated help
2. Daemon Layer (internal/daemon)
- Server:
net/httpwithhttp.ServeMux(no external router for v0.1) - TLS:
crypto/tlswith self-signed certs (mTLS-ready) - Ports: Configurable (default
:8443for API,:8080for health) - Graceful Shutdown:
signal.NotifyContextwith SIGINT/SIGTERM
3. Core Engine (internal/engine)
- Node Registry: In-memory map of node IDs → metadata, persisted to SQLite
- Task Executor:
os/exec.CommandContextwithWaitDelay(Go 1.25+) for clean process termination - Job Scheduler: Single-node FIFO queue (multi-node deferred to v0.2+)
- Audit Logger:
slog.NewJSONHandler(os.Stderr, ...)with structured fields
4. State Store (internal/store)
- Driver:
modernc.org/sqlite(pure Go, CGO-free) - Location:
~/.orca/orca.db(user-mode) or/var/lib/orca/orca.db(system-mode) - Schema:
nodes,jobs,tasks,audit_logtables - Migrations: Embedded SQL files, applied on startup
5. Job Spec Parser (internal/jobspec)
- Parser:
hashicorp/hcl/v2(industry-standard, familiar to Nomad users) - Format: HCL files (
.hcl); YAML fallback considered but deferred - Use:
orca job run <spec.hcl>reads and validates the spec, hands off to the engine
6. Domain Model (internal/model)
- Types:
Node,Job,Task— pure data structs with no I/O - State machines: Node (pending/ready/left), Task (pending/running/complete/failed)
- Use: shared by
internal/store,internal/engine,internal/daemonto avoid circular dependencies
Data Model
Node
type Node struct {
ID string
Name string
Address string
State string
JoinedAt time.Time
LastSeen time.Time
Metadata map[string]string
}
Job
type Job struct {
ID string
Name string
Spec string
Status string
CreatedAt time.Time
StartedAt *time.Time
EndedAt *time.Time
}
Task
type Task struct {
ID string
JobID string
Command string
Args []string
Env []string
PID int
ExitCode int
Status string
CreatedAt time.Time
StartedAt *time.Time
EndedAt *time.Time
}
Security Architecture
Authentication
- v0.1: mTLS for all API endpoints (self-signed CA)
- v0.2+: Token-based auth as alternative
Audit Logging
- All state-changing operations emit structured log records
- Fields:
timestamp,actor,action,resource,result,error - Stored in SQLite
audit_logtable and stderr (JSON)
Input Validation
- All CLI inputs validated via Cobra's
Args/ValidArgsfunctions - All API inputs validated at handler boundary
- HCL/YAML specs parsed with strict schemas
Key Architectural Decisions
| ID | Decision | Rationale |
|---|---|---|
| AD-001 | Single binary with subcommands | Simpler distribution, aligns with simplicity pillar |
| AD-002 | modernc/sqlite (CGO-free) | Cross-compile friendly, no CGO toolchain needed |
| AD-003 | net/http (no framework) | Stdlib suffices; avoids external router dependency |
| AD-004 | os/exec with WaitDelay (Go 1.25+) | Clean process termination, native to Go |
| AD-005 | Cobra for CLI | Industry standard, familiar to operators |
| AD-006 | slog for logging | Native to Go 1.21+, no external dependency |
| AD-007 | HCL for job specs | Familiar to Nomad/HashiCorp users |
| AD-008 | Single-node scheduling (v0.1) | Multi-node scheduling deferred to v0.2+ |
Anti-Patterns (Explicitly Avoided)
- No controller/agent split (single binary)
- No CRDs / custom resource definitions
- No web UI (CLI-only)
- No service mesh
- No container runtime integration
- No multi-tenancy
- No cloud provider integrations
- No auto-scaling
- No admission controllers
- No complex scheduling algorithms
Dependency Map (minimal)
github.com/spf13/cobra # CLI framework
github.com/hashicorp/hcl/v2 # HCL parser
modernc.org/sqlite # SQLite (pure Go)
github.com/google/uuid # UUID generation
Total: ~4 direct dependencies. No web framework, no ORM, no RPC framework.
Deployment Model
User Machine Server Node
┌──────────┐ ┌──────────────────┐
│ orca CLI │─────── mTLS ──────────▶│ orca daemon │
│ │ │ ├── API server │
│ │ │ ├── Engine │
│ │ │ └── SQLite store │
└──────────┘ └──────────────────┘
For v0.1, the CLI and daemon can be the same binary on the same machine. Multi-node is deferred.