Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions staging/sharukhan/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# AGENTS.md — sharukhan

**Authored by hand for this subproject.** The SDD sources this project follows reference an `AGENTS.md` but generate it from an Azure-centric APM toolchain (`apm install && apm compile`) that has no bearing on a local Rust CLI, and neither source commits the file. Nothing here is inherited; it is written for Rust and for this tool's threat model.

## Roles

| Agent | Responsibility | Hard boundary |
|---|---|---|
| **PM** | PRD and FRDs — defines *what* | Never specifies crates, module layout, schemas, or flags' internal handling |
| **Dev Lead** | Feasibility review on the PRD PR | Simplicity first: rejects scope not explicitly requested |
| **Architect** | ADRs — one decision each, ≥3 options considered | Does not write implementation |
| **Developer** | Tasks and code | Consumes ADRs; hands unclear design decisions back to the Architect rather than deciding in code |

## Rust standards

- `cargo clippy -- -D warnings` and `cargo fmt --check` are gates, not suggestions.
- **No `unwrap()` or `expect()` outside `#[cfg(test)]`.** A CLI that panics on a missing binary or a malformed VMX gives the operator a backtrace instead of a diagnosis.
- Errors are typed at module boundaries and contextual at the binary boundary. A failure must say *which* input was bad and *what was expected*.
- Print measured values, never a bare OK/FAIL. "tool missing" and "tool present but not executable by this user" need different fixes and are indistinguishable in a boolean — this rule is inherited from the shell tooling `sharukhan` replaces and is not negotiable.
- Every check that can be vacuous carries a negative control.
- Unit tests live in `#[cfg(test)] mod tests`; integration tests in `tests/<module>.rs` mirroring `src/<module>.rs`.

## Security posture

`sharukhan` orchestrates VMs, runs installs, and stores results. It is a test harness, not a production service, but it handles credentials and executes external binaries, so:

- **No credential ever reaches a process argument.** Arguments are world-readable via `/proc`. Secrets travel by environment or file descriptor only.
- **No credential is ever written to the memory database or a log.** Fields that could carry one are redacted at the boundary, and the redaction is tested.
- **Every external command is invoked with an argument vector**, never a shell string. No interpolation of caller data into a shell.
- **Every SQL statement is parameterised.** No string-built SQL, ever.
- **Paths derived from configuration are validated** before use in destructive operations. A teardown that accepts an arbitrary path is a footgun.
- **Destructive operations stash, never delete**, by default, and target one named VM. Blanket operations across a hypervisor are forbidden — this host runs other people's VMs.
- Prerequisite checks report the *version and provenance* of each external binary, because a version-skewed dependency (openssh built against a different OpenSSL) presents exactly like an unreachable host.

Control mappings for these are recorded in `specs/adr/` and the PRD's non-functional requirements rather than duplicated here.
98 changes: 98 additions & 0 deletions staging/sharukhan/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# sharukhan — Architecture

`sharukhan` is a single standalone Rust CLI that replaces the shell tooling in `staging/vm-lab` and `staging/mission-control`. It checks its own prerequisites, provisions VMware Workstation VMs, drives Photon OS installs both unattended and operator-assisted, verifies the result, and records every finding in a queryable memory database.

The name is the tool; the job is mission control for the ISO permutation matrix.

## Why one binary

The shell tooling worked but had three structural problems that a matrix runner cannot tolerate, each observed rather than hypothesised:

- **Inputs resolved implicitly.** `runPh5_normal.sh` resolved its patch relative to its own location, so two checkouts silently used different patches; the two copies on the host had drifted 78 lines and 8-vs-27 files apart. The failure surfaced as `patch does not apply` against a spec — indistinguishable from a rebase problem.
- **Checks that could not fail.** `vm-lab/scripts/40-check-staging.sh` never exits non-zero. Fine for an inspection tool, useless as a gate.
- **Portability landmines.** `/usr/bin/grep` is toybox in a non-interactive shell and has no `-a`, returning *zero matches* on a NUL-bearing serial log rather than erroring; interactively the same name is `ugrep`, which behaves differently again. `sed \U` and `grep -P` are GNU-only and absent. A verdict computed by such a pipeline can be silently vacuous.

A compiled binary with typed errors, explicit inputs and real exit codes removes all three by construction.

## Layers

```
+-------------------------------------------+
cli | clap surface: doctor / build / run / |
| status / stop / report / db |
+---------------------+---------------------+
|
orchestration +--------------------v---------------------+
| permutation planner, scheduler, |
| background job control |
+--------------------+---------------------+
|
domain +----------+----------+---+------+-----------+----------+
| preflight| iso | vm | install | verify |
| (probes) | (build/ | (vmx, | (auto via | (oracle, |
| | cache) | disk) | guestinfo;| harvest)|
| | | | or human)| |
+----+-----+----+-----+----+-----+-----+-----+-----+----+
| | | | |
adapters +----v----------v----------v-----------v-----------v---+
| process runner (argv only) | fs | ssh | xorriso | git |
+------------------------+-------------------------+---+
|
persistence +-----------v-----------+
| memory database |
| (findings, runs, |
| checks, artifacts) |
+-----------------------+
```

Every layer depends only downward. The domain layer never shells out directly; it goes through the process-runner adapter, which takes an argument vector and never a shell string. That single choke point is what makes the security posture testable.

## The axis model

The matrix separates cleanly, and the separation is what makes 34 permutations cost 4 builds:

| Axis | Values | Decided at | Consequence |
|---|---|---|---|
| ISO type | `minimal`, `full` | **build** | separate ISO |
| Installer version | `2.8`, `latest` | **build** | separate ISO |
| STIG hardening | `no`, `yes` | install | free |
| Root filesystem | `ext4`, `btrfs` | install | free |
| Delivery | `kickstart`, `ui` | install | free |

Install-time axes are free because Photon's `isoInstaller` reads `guestinfo.kickstart.data` through `vmtoolsd`, and `vmtoolsd` is present in the installer initrd. A per-permutation kickstart is one VMX line — no ISO remaster, no HTTP server, no boot-menu interaction.

The `ui` value cannot be automated: the STIG menu exists only in the curses configurator, so no kickstart can answer it. Those permutations are operator-assisted by design, not by omission.

## Memory database

Results are not files that happen to be greppable; they are rows. The database is the system of record and `MEMORY.md` is a generated view over it that always refers to it rather than duplicating it — so the two cannot disagree.

Entities: `run`, `permutation`, `check`, `artifact`, `finding`, `job`. A `check` carries the PR it proves, which is what turns a failure into `PR#22 regressed` rather than `something broke`.

## Security posture

The tool orchestrates VMs, handles credentials and executes external binaries. Controls are chosen against NIST SP 800-53 families and MITRE ATT&CK techniques and are recorded in ADRs, not asserted here. The defence-in-depth summary:

- credentials never occupy a process argument (`/proc` is world-readable) and never reach the database or a log
- external commands are argument vectors, never shell strings
- SQL is always parameterised
- destructive operations stash rather than delete, target one named VM, and validate their paths first — the host runs other people's VMs
- prerequisite checks report version *and* provenance, because a version-skewed dependency presents exactly like an unreachable host

## SDD Methodology

This subproject is developed spec-first. Artifacts live in [`specs/`](specs/); the phases, identifier chain, quality gates and branch/commit conventions are defined in [`specs/README.md`](specs/README.md). Implementation is gated behind a merged PRD.

The methodology is reconstructed from the maintainer's `vCenter-CVE-drift-analyzer` and from `sitoader/SDD-book-tracking-app`, adapted to Rust. Neither source contains a constitution file; [`AGENTS.md`](AGENTS.md) was authored by hand and says so.

## Open Initiatives

| Phase | Deliverable | Status |
|---|---|---|
| 0 | `ARCHITECTURE.md`, `specs/README.md`, `AGENTS.md` | In Progress |
| 1 | `specs/prd.md` | Pending |
| 2 | Dev Lead review on the PRD PR | Pending |
| 3 | `specs/adr/0001`–`000n` | Pending |
| 4 | `specs/features/*.md` | Pending |
| 5 | `specs/tasks/NNN-task-*.md` + index | Pending |
| 6 | `src/`, `tests/` — one PR per task | Pending |
60 changes: 60 additions & 0 deletions staging/sharukhan/specs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# sharukhan — Specifications

This directory holds the Spec-Driven Development artifacts for `sharukhan`. Layout follows the convention used by the other SDD-tracked subprojects in this repository (`tdnf-depgraph`, `vCenter-CVE-drift-analyzer`, `docsystem`).

## Layout

| Path | Purpose |
|------|---------|
| `prd.md` | Product Requirements Document — problem, stakeholders, goals/non-goals, acceptance criteria. One per active initiative; superseded PRDs move under `archive/`. |
| `adr/NNNN-<slug>.md` | Architecture Decision Records. Numbered globally across this subproject, never renumbered. Each ADR captures one irreversible decision with context, alternatives considered, and consequences. |
| `features/<slug>.md` | Feature-level reference docs — schemas, algorithms, contracts, file-format specifications. Linked from PRD and ADRs. |
| `tasks/NNN-task-<slug>.md` | Implementation task breakdown with acceptance tests. Numbered within an initiative; one task per pull request where practical. |
| `findings/YYYY-MM-DD-<slug>.md` | Empirical records written when implementation disproves a spec assumption. Each carries a `Resolution` naming the PR that amended the spec. |

## SDD Workflow

Each initiative progresses through phases, with each phase gating the next via a merged pull request:

1. **Phase 0 — Scaffolding.** Create or refresh `ARCHITECTURE.md` and this README; ensure the subproject is ready to receive specs.
2. **Phase 1 — PRD.** Author `prd.md`. Implementation is blocked until the PRD merges.
3. **Phase 2 — Dev Lead review.** Feasibility check on the PRD PR (no separate file; recorded as a PR review).
4. **Phase 3 — ADRs.** One PR (or one per ADR) covering all architectural decisions implied by the PRD.
5. **Phase 4 — Feature specs.** Concrete schemas, pseudocode, and contracts.
6. **Phase 5 — Task breakdown.** `specs/tasks/NNN-task-*.md` with one row per implementation step and its acceptance test.
7. **Phase 6 — Implementation.** One PR per task. Each PR cites the task ID and updates the task status.

Branch naming follows the repo's existing convention: `sdd/<initiative>-phase-N-<slug>` (e.g. `sdd/sharukhan-phase-0-init`).

Commit subjects follow the existing pattern: `<subproject> phase-N task NNN[-NNN]: <imperative summary>`.

## Identifier chain

Traceability runs PRD → FRD → ADR → task → test, using the same four-level scheme as the sibling subprojects:

| Level | Form | Declared in | Cited by |
|---|---|---|---|
| Requirement | `REQ-n` | `prd.md` §4 | FRD header `Related PRD Requirements` |
| Acceptance criterion | `AC-n` | `prd.md` §6 table, with a `Verifier` column | task acceptance checkboxes |
| Feature | `FRD-00n` | `features/<slug>.md` header | task header `Feature` |
| Decision | `ADR-000n` | `adr/NNNN-<slug>.md` | task header, tasks README |
| Task | `Task NNN` | `tasks/NNN-task-<slug>.md` | PR title and body |

The PRD's acceptance-criteria table is the traceability matrix: every `AC-n` names the task or test that verifies it, and no task is complete while an `AC` it claims remains unverified.

## Quality gates

A task may be marked Complete only when all of the following hold. These are the Rust equivalents of the Python gates used by the sibling subprojects, and are stated here because they differ by toolchain.

- `cargo test` passes with zero failures
- `cargo clippy -- -D warnings` is clean
- `cargo fmt --check` is clean
- Coverage ≥ 80% for the modules the task touches (the in-repo precedent; `vCenter-CVE-drift-analyzer` uses 80%, `SDD-book-tracking-app` uses 85%)
- Every acceptance criterion the task claims is demonstrated by a named test
- `sharukhan --help` output is accurate for any surface the task changes

## A note on provenance

This methodology is reconstructed from two sources the maintainer nominated — `vCenter-CVE-drift-analyzer`'s `SDD Methodology` section and `sitoader/SDD-book-tracking-app` — plus the conventions already live in this repository.

**Neither source contains a constitution file.** There is no `constitution.md`, no `memory/`, and no `.specify/` directory in either. The nearest equivalent is an `AGENTS.md` that `SDD-book-tracking-app` references but does not commit, because it is generated by an Azure-centric APM toolchain that does not apply to a local Rust CLI. The `AGENTS.md` in this subproject was therefore **authored by hand for Rust**, not generated and not inherited. It is called out here so nobody later mistakes it for an upstream artifact.