|
| 1 | +# AIMS — Re-Entry Roadmap |
| 2 | + |
| 3 | +> Written 2026-07-19. Companion to [`STATE.md`](./STATE.md) (where things are) and |
| 4 | +> [`CLAUDE.md`](./CLAUDE.md) (architecture) and [`SCAN.md`](./SCAN.md) (scan model & |
| 5 | +> scanner-plug substrate). This is the *plan to pick the project back up* |
| 6 | +> after ~1 year: ordered phases, concrete tasks, and the reasoning behind the ordering. |
| 7 | +
|
| 8 | +## Guiding strategy |
| 9 | + |
| 10 | +1. **Compile first, features second.** Nothing is verifiable until the tree builds. Step 0 |
| 11 | + is an absolute prerequisite for everything else. |
| 12 | +2. **The model layer is the asset — protect it.** The generated `pb` layer is mature and |
| 13 | + compiles. Never hand-edit generated code; change `.proto` + `make gen`. Keep all |
| 14 | + hand-written behavior in the domain root `<name>.go` files and the `server/`, `cmd/` layers. |
| 15 | +3. **Finish one vertical slice before widening.** The **host** domain is the reference |
| 16 | + implementation (Read/Create + dedup + display + completions). Bring every other domain up |
| 17 | + to the host bar rather than starting new surface area. |
| 18 | +4. **Small, releasable increments.** Each phase below should end at a compiling, runnable |
| 19 | + state. Prefer a working narrow tool over a broad broken one. |
| 20 | +5. **Land the org migration early** so all later commits are already on `d3c3ptive`. |
| 21 | + |
| 22 | +## Priority-ordered phases |
| 23 | + |
| 24 | +### Phase 0 — Unblock the build ⛔ (do this first; ~half a day) |
| 25 | + |
| 26 | +The tree does not compile: `github.com/maxlandon/gondor/maltego` is broken at the pinned |
| 27 | +version, and every domain root package imports it (see STATE.md → Build status). |
| 28 | + |
| 29 | +**Key fact that makes this cheap:** `AsEntity()` / `maltego.*` is **defined but never called** |
| 30 | +anywhere in `server/`, `client/`, `cmd/`, or `db/`. The Maltego integration is currently dead |
| 31 | +weight, so we can decouple it without losing any working functionality. Options, cheapest first: |
| 32 | + |
| 33 | +- **(A) Isolate behind a build tag (recommended).** Move every `AsEntity()` method into |
| 34 | + `*_maltego.go` files guarded by `//go:build maltego`. Default builds drop the gondor import |
| 35 | + entirely and compile; the Maltego path is opt-in and can be repaired later. Lowest risk, |
| 36 | + reversible, preserves intent. |
| 37 | +- **(B) Fork/vendor & fix gondor.** Point the module at a fixed fork (e.g. `d3c3ptive/gondor`) |
| 38 | + via `replace` and repair the compile errors (`undefined: base`, `getDirectory`, |
| 39 | + `configuration.Entity`, `getNamePlural`). More work, but keeps Maltego always-on. Fits the |
| 40 | + org migration (gondor is also `maxlandon`-namespaced). |
| 41 | +- **(C) Delete the Maltego integration** outright (remove imports + `AsEntity`). Simplest, but |
| 42 | + throws away a stated secondary goal of the project. Only if Maltego is truly out of scope. |
| 43 | + |
| 44 | +**Acceptance:** `GOWORK=off go build ./...` succeeds (allow first-run for the large |
| 45 | +tailscale/gvisor download). Add a CI or a Makefile `build`/`test` target to keep it green. |
| 46 | + |
| 47 | +> Sub-note: the full build pulls tailscale + gvisor via `reeflective/team`. If that transport |
| 48 | +> weight is unwanted long-term, consider whether the teamserver transport should be optional. |
| 49 | +
|
| 50 | +### Phase 1 — Correctness & hygiene sweep (~half a day, right after it compiles) |
| 51 | + |
| 52 | +Cheap fixes that remove confusion before building on top: |
| 53 | + |
| 54 | +- **Untangle the c2 file/type swap.** `server/c2/channel.go` implements the Agent server and |
| 55 | + `server/c2/agent.go` the Channel server (`type channelServer`). Rename files/types to match |
| 56 | + contents and fix the mislabeled `Unimplemented` messages ("UpsertChannel" in the agent file, |
| 57 | + etc.). Do this *before* extending c2. |
| 58 | +- **Remove debug leftovers:** `println(c.Type)` in `host/host.go` (`Purpose`); `fmt.Println(val)` |
| 59 | + and the empty `if head == "Purpose" {}` blocks in `cmd/display/details.go`. |
| 60 | +- **Fix `cmd/display/defaults.go` `init()`:** the `stdoutTerm/stdinTerm/stderrTerm` assignments |
| 61 | + are crossed (stdout←os.Stderr, stderr←os.Stdin, stdinTerm never set). Table sizing reads |
| 62 | + `stderrTerm.Fd()` — verify it points at a real terminal. |
| 63 | +- **Prune the stray `network` service stubs** copied from host (`ReadHost`/`ListHost`/ |
| 64 | + `UpsertHost` in `server/network/service.go`) — dead, misleading methods. |
| 65 | + |
| 66 | +### Phase 2 — Complete the gRPC CRUD (the core functional gap; ~1 week) |
| 67 | + |
| 68 | +Read/Create exist for exercised domains; **Upsert and Delete are stubbed almost everywhere, |
| 69 | +and two whole services (Users, Logins) are fully stubbed.** The RPC protos define |
| 70 | +**Create / Read / Upsert / Delete** (List folds into Read via `*Filters`; Update via Upsert). |
| 71 | + |
| 72 | +Use **`server/host/host.go` as the template** for every method. The pattern is: |
| 73 | +PB→ORM (`ToORM`), build preload clauses (`WithPreloads`), query/write via GORM, ORM→PB (`ToPB`). |
| 74 | + |
| 75 | +Task list (each = copy the host pattern + wire dedup/preloads): |
| 76 | + |
| 77 | +| Domain/service | Implement | Reference / notes | |
| 78 | +|---|---|---| |
| 79 | +| host Hosts | `Upsert`, `Delete` | finish the commented-out bodies already sketched in `host.go` | |
| 80 | +| host **Users** | all: `Create/Read/Upsert/Delete` | fully stubbed; mirror Hosts | |
| 81 | +| network Services | `Create`, `Upsert`, `Delete` | Read/List done; reuse `identical.go` for dedup | |
| 82 | +| credential Credentials | `Create`, `Upsert`, `Delete` | Read/List done | |
| 83 | +| credential **Logins** | all | fully stubbed | |
| 84 | +| scan Scans | `Upsert`, `Delete` | Create/Read done | |
| 85 | +| c2 Agents/Channels | `Upsert`, `Delete` | after Phase 1 rename | |
| 86 | + |
| 87 | +Cross-cutting for this phase: |
| 88 | +- **Standardize the dedup story.** `internal/db.FilterNew` + per-domain `AreXIdentical` |
| 89 | + (`*/identical.go`) already exist for hosts/scans/services — extend to credentials/users so |
| 90 | + re-imports don't duplicate. |
| 91 | +- **Decide List vs Read.** Some server types expose a `List` method not in the proto. Either |
| 92 | + add `List` RPCs to the protos and regenerate, or drop the extra methods for consistency. |
| 93 | +- **Delete semantics.** Confirm GORM cascade behavior (README claims sane cascade defaults); |
| 94 | + add tests that deleting a Host removes its owned Ports/OS/Trace rows. |
| 95 | + |
| 96 | +### Phase 3 — Wire the CLI actions (~2–3 days) |
| 97 | + |
| 98 | +The command tree, flags, and completions exist, but several handlers are no-ops. |
| 99 | + |
| 100 | +- **Implement the empty `RunE`s:** `hosts add` / `hosts rm` (they `return nil`), and audit the |
| 101 | + other domains' `add`/`rm`/`show` for the same. `add` should read `-f/--file`, unmarshal, and |
| 102 | + call the (now-real) `Create`/`Upsert`; `rm` should resolve the ID/hostname completion arg and |
| 103 | + call `Delete`. |
| 104 | +- **Wire `import`.** `cmd/export/` has `ImportCommand` (JSON/XML via protoreflect) — hook it |
| 105 | + into each domain command alongside the working `export`, so nmap XML / saved objects can be |
| 106 | + loaded. This is the payoff of the "many tools feed one DB" thesis. |
| 107 | +- **End-to-end smoke test:** `aims` teamserver up → `import` an nmap XML → `hosts list` / |
| 108 | + `hosts show` → `export`. This exercises the whole stack and validates the data model claims. |
| 109 | + |
| 110 | +### Phase 4 — Finish the designed-but-empty APIs (backlog; scope as needed) |
| 111 | + |
| 112 | +- **Credential scope helpers** (`credential/core.go`: `WhereLoggedInHost`, `WhereOriginIs`, |
| 113 | + `WhereOriginServiceForHost`, `WhereOriginSessionForHost`) — the Metasploit-style querying |
| 114 | + API. Implement as GORM scopes returning `func(*gorm.DB) *gorm.DB`. |
| 115 | +- **Maltego** — repair whichever Phase-0 option was chosen; finish the stubbed `AsEntity()` |
| 116 | + (e.g. `network/service.go` returns an empty `maltego.Entity{}`). This delivers the "objects |
| 117 | + as Maltego entities" secondary goal. |
| 118 | +- **Scan runner** — `git log` shows an "idea for running scans" (`scan/target.go`, |
| 119 | + `de2505f`). Decide whether AIMS *runs* scanners (nmap/sx/zgrab) or only *stores* their |
| 120 | + output. The README leans "storage/spec only"; a runner is a scope expansion to make deliberately. |
| 121 | + |
| 122 | +## Cross-cutting workstreams (do alongside the phases) |
| 123 | + |
| 124 | +- **Org migration to `d3c3ptive`.** Module path is already `github.com/d3c3ptive/aims` |
| 125 | + (good). When the GitHub repo moves: verify `buf.gen-*.yaml` `go_package_prefix` (already |
| 126 | + `d3c3ptive`), and resolve the **`maxlandon/gondor`** dependency (fork to `d3c3ptive/gondor` |
| 127 | + or drop) so no `maxlandon` trace remains — this dovetails with Phase 0 option (B). |
| 128 | +- **Testing.** There are essentially no tests. Add, in priority order: (1) a build/`go vet` |
| 129 | + gate, (2) round-trip `ToORM`/`ToPB` tests per domain, (3) dedup (`AreXIdentical`) tests, |
| 130 | + (4) the nmap-XML → Host unmarshal path (the interoperability contract). |
| 131 | +- **Doc drift.** Update `README.md`: no `vendor/` (module cache), generated code sits next to |
| 132 | + each `.proto` (not `proto/gen/`), codegen files are at repo root. Keep `CLAUDE.md`/`STATE.md` |
| 133 | + current as the source of truth. |
| 134 | + |
| 135 | +## Suggested first sitting (if you only have a few hours) |
| 136 | + |
| 137 | +1. Phase 0 option (A): build-tag the Maltego methods → get `go build ./...` green. |
| 138 | +2. Phase 1: c2 rename + delete the debug prints + fix the `init()` swap. |
| 139 | +3. Commit. You now have a compiling, coherent base to grow from — everything else is additive. |
0 commit comments