Skip to content
Merged
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
55 changes: 55 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,58 @@ jobs:
# is what keeps them there.
- name: Audit dependencies
run: npm audit --audit-level=critical

docs:
name: Docs site build
runs-on: ubuntu-latest
timeout-minutes: 5
# Repo root, not app/ — mkdocs.yml and user-docs/ both live there.
defaults:
run:
working-directory: .

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip

- name: Install MkDocs Material
run: pip install -r requirements.txt

# --strict turns a broken nav entry, a dead cross-reference mkdocs itself
# can see, or a missing page into a failed build instead of a silently
# broken link on docs.teemops.com.
- name: Build docs.teemops.com
run: mkdocs build --strict

# user-docs/README.md's own link-checker, for the relative links mkdocs
# doesn't validate because they point at pages not yet in the nav.
- name: Check user-docs cross-references
run: |
python3 - <<'EOF'
import pathlib, re, sys
bad = []
for md in pathlib.Path("user-docs").rglob("*.md"):
text = re.sub(r"```.*?```", "", md.read_text(), flags=re.S)
for m in re.finditer(r"\]\((?!https?:|#)([^)#]+)(#[^)]*)?\)", text):
if not (md.parent / m.group(1)).resolve().exists():
bad.append(f"{md}: -> {m.group(1)}")
if bad:
print("\n".join(bad))
sys.exit(1)
print("all relative links resolve")
EOF

# SVGs fail as a silent broken image, not a build error — see
# user-docs/README.md's Diagrams section.
- name: Validate diagram SVGs
run: |
python3 -c "
import xml.etree.ElementTree as ET, glob
[ET.parse(f) for f in glob.glob('user-docs/assets/diagrams/*.svg')]
print('all diagrams parse as valid XML')
"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ dist/
.nuxt/
.output/

# docs.teemops.com build output (mkdocs build) — generated, not source
/site/

# MySQL Data Files
mysql-data/
generated/
Expand Down
62 changes: 40 additions & 22 deletions design/docs-site/information-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,12 @@ somewhere and needs porting and a change of voice, not that a `user-docs/` page
| Page | For | Status | Source | Priority |
| --- | --- | --- | --- | :---: |
| **Start here** |
| What TOPS is — and what it is not | All | ✍️ Write | `README.md` intro, roadmap Direction | 1 |
| What TOPS is — and what it is not | All | ✅ **Written** | `README.md` intro, `docs/PROGRESS.md` | **1** |
| How TOPS connects to AWS | Evaluator | ✅ **Built this session** | `design/architecture/aws-integration.html` | 1 |
| What it costs to run | Evaluator, Operator | ⚠️ Partial | `install.sh` `aws_intro()`, `docker-compose.README.md` | 2 |
| Install | Operator | ✅ Exists | `README.md` § Install — link, don't copy | 1 |
| Connect your first AWS account | Operator | ✅ Exists | `README.md` § Scanning a real AWS account | 1 |
| Run your first scan | Operator, User | ⚠️ Thin | `README.md`, `docs/features/scan-*.md` | 1 |
| Install | Operator | ✅ **Written** | `README.md` § Install — links, doesn't copy | **1** |
| Connect your first AWS account | Operator | ✅ **Written** | `README.md` § Scanning a real AWS account, the architecture page | **1** |
| Run your first scan | Operator, User | ✅ **Written** | `Pages/Scans/Index.vue`, `Pages/Scans/NewScanModal.vue` | **1** |
| **Using TOPS** |
| The dashboard | User | ✍️ Write | `Pages/Dashboard.vue` | 3 |
| Running a scan | User | ✍️ Write | `Pages/Scans/*`, `NewScanModal.vue` | 2 |
Expand All @@ -148,19 +148,23 @@ somewhere and needs porting and a change of voice, not that a `user-docs/` page
| Troubleshooting | Operator | ✅ Exists | `DEBUG.md`, `README.md` § If something goes wrong | 2 |
| Uninstalling | Operator, Evaluator | ⚠️ Partial | `docker-compose.README.md`; the two CFN stacks | 3 |
| **Security** |
| The security model | Evaluator | ✅ **Built this session** | The architecture page's boundary table | **1** |
| The security model | Evaluator | ✅ **Written** | Roadmap D-1/D-2/D-9, `docs/PROGRESS.md`, `SECURITY.md` | **1** |
| Where your data lives | Evaluator | ✍️ Write | Nothing leaves the install — needs stating plainly | 2 |
| Reporting a vulnerability | Evaluator | ✅ **Done** | `SECURITY.md` — link to it rather than restating it | **1** |
| Reporting a vulnerability | Evaluator | ✅ **Written** | `SECURITY.md` — links to it rather than restating it | **1** |
| Licence and trademark | Evaluator | ✅ Exists | `LICENSE`, `TRADEMARK.md`, roadmap D-7 | 3 |
| **Reference** |
| Glossary | All | ✅ Exists | `docs/GLOSSARY.md` | 3 |
| What TOPS scans — services and rules | Evaluator, User | ✅ Exists | `docs/planning.md`, the rule JSON | 2 |
| Command reference | Operator | ✍️ Write | `artisan` commands, `backup.sh`, `install.sh` flags | 4 |
| Release notes | All | ✅ Exists | `CHANGELOG.md` | 4 |

**Totals:** 34 pages — 12 exist in substance, 5 partial or scattered, 17 to write from
scratch. Roughly a third of the site is a porting-and-rewriting job rather than an authoring
job, which is the argument for doing the IA before the writing.
**Totals, original:** 34 pages — 12 exist in substance, 5 partial or scattered, 17 to write
from scratch. Roughly a third of the site was a porting-and-rewriting job rather than an
authoring job, which is the argument for doing the IA before the writing.

**Updated 2026-08-04:** 10 of 34 pages are now written and live in `user-docs/` — the
whole priority-1 set (all six Start here and Security pages, plus the two Using TOPS pages
from the first slice). 24 remain, all priority 2 or lower.

### Two gaps this inventory exposed

Expand Down Expand Up @@ -352,19 +356,33 @@ selection, and a theme whose image CSS is one rule away from correct. That is th
requirement list, and every MkDocs- or Docsify-class renderer meets it. **Nothing found here
argues for a framework.**

## Tooling stays deferred, on purpose

D-10 said plain Markdown, renderer chosen once there is content. **This design does not
change that**, and the wireframes should not be read as a vote for a framework.

What the wireframes commit to is only what any renderer in the MkDocs/Docsify class gives
you free: a left sidebar of sections, a content column at a readable measure, an on-page
table of contents, and search. Nothing in the IA needs MDX, React components, or a build
step. The one page with real layout demands — the architecture explainer — is already a
self-contained HTML page with inline SVG, which every candidate renderer can embed.

**Revisit when:** the first slice is written and the section count or search need actually
bites. Not before.
## Tooling: MkDocs + Material — decided 2026-08-03, recorded as D-13

D-10 said plain Markdown, renderer chosen once there is content. That trigger fired once
the first slice's four pages existed against an eight-section, 34-page map — section count
and search both actually bite now, not hypothetically.

**MkDocs with the Material theme**, deployed to `docs.teemops.com` via Cloudflare Pages
(build command `mkdocs build`, output directory `site`). It's the choice this document
already argued for without naming it: only what any renderer in the MkDocs/Docsify class
gives free — a left sidebar of sections, a content column at a readable measure, an
on-page table of contents, and search — nothing here ever needed MDX, React components, or
a hand-rolled build step. Chosen over Docsify specifically because it renders real static
HTML per page rather than client-side; the evaluator audience this document identifies
above arrives via a vendor-review request or a search result, not already inside the app,
and static HTML is what a crawler and a skimmed link both want. It adds no new language to
the repo — `python3` already backs the link-checker and SVG-validator scripts sitting next
to it in `user-docs/README.md`.

The one page with real layout demands — the architecture explainer — is already a
self-contained Markdown page with linked SVGs, which the theme embeds with one CSS rule
(`p:has(img) { max-width: none }`, the fix this document's sample test below found
necessary). Config lives in `mkdocs.yml` and `requirements.txt` at the repository root, and
`.github/workflows/tests.yml` gained a `docs` job so a broken nav entry or a dead diagram
fails CI instead of shipping to `docs.teemops.com`.

**Revisit when:** content needs something outside MkDocs' plugin ecosystem, or the Python
build step becomes a maintenance burden of its own.

## First slice

Expand Down
13 changes: 10 additions & 3 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Decisions already made, so we don't relitigate them. Each has a trigger for revi
| **D-10** | **User documentation lives in a top-level `user-docs/` directory, not under `docs/`, and deploys to `docs.teemops.com`.** One site for technical and non-technical readers — no separate tracks. | 2026-07-31 | See below. | The content outgrows plain Markdown, or a contributor proposes a better home. |
| **D-12** | **No security score.** Removed rather than recalibrated. Severity counts and the breakdown are what we show; a score can come back later if a design partner asks for one and we know what it should mean. | 2026-08-02 | See below. Closes [#91](https://github.com/teemops/tops/issues/91). | A design partner asks for a single headline number — and then design it so it can *move*. |
| **D-11** | **A finding is a durable record keyed by AWS account + resource + rule, and findings are current state. No per-scan history, no observations table.** The most recent scan that examined a resource is authoritative for its content; status belongs to the user. | 2026-08-01 | See below. Shipped as [#81](https://github.com/teemops/tops/issues/81). | A design partner asks for trends over time — and then treat it as a new data model, not an addition to this one. |
| **D-13** | **Docs renderer: MkDocs + Material theme**, deployed to `docs.teemops.com` via Cloudflare Pages (`mkdocs build`, output `site/`). Fills in the choice D-10 deliberately deferred. | 2026-08-03 | Clears the "MkDocs- or Docsify-class" bar D-10 set, now that section count and search actually bite (four pages against an eight-section, 34-page IA). Material gives sidebar nav, on-page TOC and client-side search for free — nothing here needed building by hand. Chosen over Docsify because it renders real static HTML per page rather than client-side, which matters for the evaluator audience D-10's own design work identified as arriving via search or a vendor-review link rather than already inside the app. Adds no new language to the repo: `python3` already backs the link-checker and SVG-validator scripts next to it in `user-docs/README.md`. | Content needs something outside MkDocs' plugin ecosystem, or the Python build step becomes a maintenance burden of its own. |

> **Naming collision, flagged 2026-08-01.** [`durable-findings.md`](./features/durable-findings.md)
> and the wireframes label the durable-findings decision **D-1**, which in *this* document is
Expand Down Expand Up @@ -585,9 +586,15 @@ Unlike the scanner-coverage backlog this doesn't have a natural per-item unit, s
as a first slice plus continuous growth rather than a checklist.

**First slice**
- [ ] `user-docs/` exists with a handful of pages covering what N-3 doesn't: reading and
resolving a finding, switching scan profiles, managing organization members
- [ ] Cloudflare Pages is connected and `docs.teemops.com` resolves to it
- [x] `user-docs/` exists — 10 of the IA's 34 pages written, closing the entire
priority-1 set: what TOPS is, how TOPS connects to AWS, install, connect your first
AWS account, run your first scan, what the IAM role can do, reading a finding,
resolving a finding, the security model, reporting a vulnerability. Scan profiles
and member management are priority-2, still ahead, per the IA's writing order
- [ ] Cloudflare Pages is connected and `docs.teemops.com` resolves to it — **D-13** picked
the renderer (MkDocs + Material) and the repo now builds; the Pages project itself
still needs connecting in the Cloudflare dashboard, which isn't something a repo
commit can do
- [ ] `README.md`'s setup section trims to a summary linking into `user-docs/`, so
installation instructions have exactly one canonical copy

Expand Down
64 changes: 64 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
site_name: TOPS Documentation
site_url: https://docs.teemops.com
site_description: >-
Documentation for running TOPS — connecting an AWS account, reading a finding,
and everyday use.
repo_url: https://github.com/teemops/tops
repo_name: teemops/tops
edit_uri: edit/develop/user-docs/

docs_dir: user-docs
site_dir: site

# README.md in user-docs/ is contributor-facing (conventions, archetypes, status)
# — see D-10. It documents how to write the site, it isn't a page of it.
exclude_docs: |
README.md

theme:
name: material
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
toggle:
icon: material/brightness-7
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
toggle:
icon: material/brightness-4
name: Switch to light mode
features:
- navigation.sections
- navigation.top
- navigation.indexes
- toc.follow
- search.suggest
- content.action.edit

markdown_extensions:
- tables
- toc:
permalink: true

extra_css:
- assets/extra.css

# Only pages that exist. Per user-docs/README.md's own rule ("no links to pages
# that do not exist yet"), the nav grows one entry per page as it's written —
# see design/docs-site/information-architecture.md for the full 34-page map.
nav:
- Start here:
- What TOPS is: start-here/what-tops-is.md
- How TOPS connects to AWS: start-here/how-tops-connects-to-aws.md
- Install: start-here/install.md
- Connect your first AWS account: start-here/connect-your-first-aws-account.md
- Run your first scan: start-here/run-your-first-scan.md
- Using TOPS:
- Reading a finding: using-tops/reading-a-finding.md
- Resolving a finding: using-tops/resolving-a-finding.md
- AWS accounts:
- What the IAM role can do: aws-accounts/what-the-iam-role-can-do.md
- Security:
- The security model: security/the-security-model.md
- Reporting a vulnerability: security/reporting-a-vulnerability.md
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mkdocs-material==9.7.7
23 changes: 17 additions & 6 deletions user-docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ to `docs/`, which is the contributor and maintainer knowledge base. See
[D-10](../docs/roadmap.md#d-10-in-full-where-user-documentation-lives) for why the two are
kept apart.

**Status: first slice.** Four pages so far. The structure it will grow into is designed in
**Status: priority-1 set complete.** 10 of the IA's 34 pages so far — every page the
inventory marks priority 1. The structure it will grow into is designed in
[`design/docs-site/information-architecture.md`](../design/docs-site/information-architecture.md)
— 34 pages across 8 sections, with a content map showing what already exists elsewhere in
the repo and needs porting rather than writing.
Expand All @@ -14,17 +15,27 @@ the repo and needs porting rather than writing.

| Page | For |
| --- | --- |
| [What TOPS is — and what it isn't](start-here/what-tops-is.md) | Anyone arriving at the site for the first time |
| [How TOPS connects to AWS](start-here/how-tops-connects-to-aws.md) | Security reviewers approving TOPS for an AWS organisation |
| [What the IAM role can do](aws-accounts/what-the-iam-role-can-do.md) | The same reviewers, once they ask what the permissions actually grant |
| [Install](start-here/install.md) | Whoever is about to run it |
| [Connect your first AWS account](start-here/connect-your-first-aws-account.md) | The same person, once TOPS is up |
| [Run your first scan](start-here/run-your-first-scan.md) | The same person, once an account is connected |
| [Reading a finding](using-tops/reading-a-finding.md) | Whoever has just run their first scan and is looking at the results |
| [Resolving a finding](using-tops/resolving-a-finding.md) | The same person, once they have fixed something and want it off the list |
| [What the IAM role can do](aws-accounts/what-the-iam-role-can-do.md) | Reviewers, once they ask what the permissions actually grant |
| [The security model](security/the-security-model.md) | Reviewers asking about the product itself, not just the AWS boundary |
| [Reporting a vulnerability](security/reporting-a-vulnerability.md) | Anyone who found one |

## Conventions

**Plain Markdown, no framework.** The renderer is deliberately undecided until there is
enough content to need navigation and search. Nothing here may depend on renderer-specific
syntax — no MDX, no components, no shortcodes. If a page cannot be written in CommonMark plus
tables, that is a signal about the page, not about the tooling.
**Plain Markdown, minimal renderer.** Deployed via [MkDocs](https://www.mkdocs.org/) with
the [Material](https://squidfunk.github.io/mkdocs-material/) theme — see **D-13** in the
roadmap and [Tooling](../design/docs-site/information-architecture.md#tooling-mkdocs--material--decided-2026-08-03-recorded-as-d-13)
in the IA doc for why. Config is `mkdocs.yml` and `requirements.txt` at the repository
root; build locally with `pip install -r requirements.txt && mkdocs serve`. Nothing here
may depend on renderer-specific syntax regardless — no MDX, no components, no shortcodes.
If a page cannot be written in CommonMark plus tables, that is a signal about the page, not
about the tooling.

**Install instructions live in `README.md`, not here.** Linking beats copying; two canonical
copies drift. Pages reference it rather than restating commands.
Expand Down
8 changes: 8 additions & 0 deletions user-docs/assets/extra.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* The one rule the sample test (design/docs-site/information-architecture.md)
found necessary: Markdown wraps an image in a <p>, and Material caps prose at
a readable measure, which silently caps every diagram at that same width.
Diagrams are authored at a 900px viewBox specifically to read at ~1:1 without
that cap — see user-docs/README.md's Diagrams section. */
.md-content__inner p:has(img) {
max-width: none;
}
36 changes: 36 additions & 0 deletions user-docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# TOPS documentation

TOPS scans your AWS accounts against CIS and TOPS' own baseline, and turns what it finds
into findings you can act on. This site covers evaluating, connecting, and using it.

Install and upgrade commands live in the [`README.md`](https://github.com/teemops/tops)
at the repository root — this site links to them rather than restating them.

**This site is growing, not finished.** More sections arrive as the features behind them
ship. What exists so far:

## Start here

- [What TOPS is — and what it isn't](start-here/what-tops-is.md)
- [How TOPS connects to AWS](start-here/how-tops-connects-to-aws.md) — for whoever has to
approve TOPS before it's installed.
- [Install](start-here/install.md)
- [Connect your first AWS account](start-here/connect-your-first-aws-account.md)
- [Run your first scan](start-here/run-your-first-scan.md)

## Using TOPS

- [Reading a finding](using-tops/reading-a-finding.md) — what each field means, and where
to start on a long list.
- [Resolving a finding](using-tops/resolving-a-finding.md) — fixing, ignoring, and what the
next scan does.

## AWS accounts

- [What the IAM role can do](aws-accounts/what-the-iam-role-can-do.md) — the honest,
policy-by-policy answer to "what does this actually grant?"

## Security

- [The security model](security/the-security-model.md)
- [Reporting a vulnerability](security/reporting-a-vulnerability.md)
Loading
Loading