myxce/.kiro/steering/tech.md
Rongsong Shen f5fbc6bcac
feat(eliminate-frontend-makefile): task 3.3 - update live documentation (11 files)
Removes every live current-fact reference to src/frontend/Makefile
targets or Makefile-specific fixed paths across 11 docs (steering,
frontend/CLAUDE.md, root CLAUDE.md, deployment docs, and two skill
docs), replacing with the Meson-based equivalents established by
tasks 1-3.2: /xce-build apiserver, meson test --suite apiserver,
direct tests/regression/run.sh invocation.

Round-1 review rejected the first pass: xce-deploy-ha/SKILL.md had
over-corrected by deleting all mention of deploy-ha.sh's still-real
/tmp/apiserver legacy fallback (that script's discovery logic is
unchanged, unlike run-resource-verify.sh's, which genuinely lost its
fallback in task 3.1 -- xce-test-resources/SKILL.md's identical-
looking edit was correct for that reason). Fixed by restoring an
accurate, Makefile-free description of the real fallback at all four
touched sites. Re-reviewed and approved.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
2026-09-03 14:22:54 +00:00

6 KiB

Technology Stack

Architecture

xcecli (Go/Cobra) ──REST──► apiserver (Go/Gin) ──CGO──► C daemon layer
                                                          controller / scheduler / worker / autoscaler
                                                          ↑↑↑ all linked against libxce (src/lib/)

Request path inside apiserver: Gin Router → Middleware (WhitelistURL → WhitelistIP → RateLimit → JWT → CORS) → RBAC Check → Controller → Services → In-Memory Store

All controller state is in-memory. No persistent database in the API tier; job/queue/host state is pushed live into the in-memory stores from the C controller backend over CGO (see Key Technical Decision #2), not loaded from a file. C daemons communicate via TCP using a custom messaging layer: iomgr (epoll-based I/O loop), vtaskmgr (thread pool for packet dispatch), and xce_request/xce_reply for non-blocking send/receive. WAL + checkpoint provide persistence at the controller level.

Core Technologies

  • Go 1.23.4 — apiserver (REST API) and xcecli (CLI). Two independent Go modules, no go.work.
  • C (gnu17) — controller, worker, scheduler, autoscaler, libxce. No C++.
  • Gin v1.10.0 — HTTP router, middleware, binding
  • Cobra v1.9.1 — CLI framework for xcecli
  • Zap v1.27.0 — structured logging throughout (both Go modules)
  • Viper v1.19.0 / v1.20.1 — YAML config management
  • Cap'n Proto — wire serialization for WAL and daemon-to-daemon replication
  • iomgr / vtaskmgr / delaymgr — libxce async I/O framework: single-threaded epoll loop (iomgr), thread-pool packet dispatch (vtaskmgr), timer thread for periodic/blocking tasks (delaymgr)

Build Systems

System Scope
Meson ≥ 1.5 C daemons + Go modules via tools/go_build wrapper — sole build/test path for apiserver/xcecli
Nix flakes Hermetic dev environment, AppImage packaging
# Full build (C + Go, debug + ASAN)
/xce-build

# Go development loop (apiserver + xcecli)
/xce-build apiserver                          # builds both binaries + runs unit tests
meson test -C <build_dir> --suite apiserver   # CGO-backed backend tests
XCE=<build_dir>/frontend/xcecli/xcecli URL=http://localhost:8080 \
    src/frontend/tests/regression/run.sh      # regression suite

# Nix package builds (from src/)
nix build .#xcecore        # C daemons + apiserver
nix build .#xcecore_debug  # debug + ASAN
nix build .#xcecli         # CLI only

Development Standards

Controller Pattern (Go)

// Struct: {Resource}Controller (typically an empty struct{})
type JobController struct{}

// Handler: {Action}{Resource}
func (j JobController) CreateJob(ctx *gin.Context) {
    // Validate input
    var req models.JobSubmitRequest
    if err := utils.BindJSONStrict(ctx, &req); err != nil { ReturnError(ctx, code, err.Error(), nil); return }
    parsedIndices, err := preCheckJobRequest(&req)
    if err != nil { ReturnError(ctx, code, err.Error(), nil); return }

    // Business logic — package-level services function, not a controller field
    err = services.SubmitJob(&newJob, arraySize)
    if err != nil { ReturnError(ctx, code, err.Error(), nil); return }

    ReturnSuccess(ctx, code, "submitted", result)
}

Early return on every error. Log with zap fields before returning.

Testing Requirements

  • TDD mandatory: Write failing test first, then implementation.
  • Go unit tests: Table-driven, no external dependencies, t.TempDir() for fixtures.
  • C tests: Add test case in testsuite/ before modifying implementation.
  • /xce-build apiserver's unit-test step must pass without a live server.
  • tests/regression/run.sh requires XCE + URL env vars.

Code Formatting

  • Go: gofmt / goimports (post-edit hooks configured)
  • C: clang-format using project config at xce/.clang-format

Auth and RBAC

  • Auth backend set in config/auth.yaml (provider: config_file | pam | ldap | oauth2). Only config_file and pam (Linux + CGO) are functional; ldap and oauth2 are documented as future/stub providers whose Authenticate() always returns a "not yet implemented" error.
  • Permissions are a plain action string (e.g. "job:view", "queue:submit") paired with a separate structured domain list (e.g. {type: self}, {type: all}) in rbac.yaml — not a combined "action:scope" string. controllers.RequirePermission() checks the action string only; domain/scope is evaluated separately.
  • RBAC loaded from config/rbac.yaml, enforced by controllers.RequirePermission() middleware.

Key Technical Decisions

  1. No Go workspace — apiserver and xcecli are independent modules; changes to shared types require updating both.
  2. In-memory API state — no database in Go tier; store.InitStores() allocates empty per-resource stores at startup, and job/queue/host/etc. state is then pushed in live from the C controller backend over CGO (backend.RegisterToBackend() + backend.StartNotificationListener() in cmd/main.go). Only RBAC (config/rbac.yaml) and auth (config/auth.yaml) are actually loaded from YAML at startup.
  3. CGO bridge — backend/ package wraps C library calls; unit tests run CGO_ENABLED=0, backend tests need CGO_ENABLED=1 inside nix dev shell.
  4. Swagger-first API docs — swaggo/swag annotations on handlers; docs served at runtime by Gin.
  5. Structured logging — every log entry carries context via zap.String(), zap.Error(). Never use fmt.Println for operational output.
  6. Single-writer HA with full Raft log replication — controller is always one active primary; standby election and log replication use a Raft protocol (term-based voting, freshness gates, AppendEntries request/reply, per-peer next_index[]/match_index[] progress vectors, (term,index) log matching/truncation, commit-index advance) implemented in the src/lib/raft/ shared module (raft.c/raft.h) and validated by the raft-log-replication, raft-commit-safety, raft-ha-core-safety, and ha-term-safety specs.