Documentation and resource hub for the MITRE Security Automation Framework (SAF).
Built with VitePress and Pocketbase.
Before setting up the project, ensure you have the following installed:
| Tool | Version | Installation |
|---|---|---|
| Node.js | 22.x | nodejs.org or use nvm |
| pnpm | 10.x | npm install -g pnpm or corepack enable |
Optional but recommended:
- nvm - Node version manager (auto-switches via
.nvmrc)
After cloning, run the setup script:
git clone https://github.com/mitre/saf-site-vitepress.git
cd saf-site-vitepress
./scripts/setup.shThe script is idempotent - safe to run anytime (first-time setup, after git pull, etc.).
Script options:
./scripts/setup.sh # Normal setup (safe, idempotent)
./scripts/setup.sh --check # Validate setup without changes
./scripts/setup.sh --dry-run # Preview what would happen
./scripts/setup.sh --force # Force fresh database restore
./scripts/setup.sh --force -y # CI/CD: non-interactive fresh setup
./scripts/setup.sh --help # Show all optionsIf you prefer manual steps or need to troubleshoot:
git clone https://github.com/mitre/saf-site-vitepress.git
cd saf-site-vitepress# If using nvm, this switches to the correct Node version
nvm use
# Install dependencies
pnpm installThe database content is stored in a git-friendly format and must be restored before first use:
pnpm db:loadThis creates data.db from the version-controlled diffable/ directory.
The diffable export includes the complete schema, so migrations are not needed. Clear them to avoid conflicts:
rm -rf .pocketbase/pb_migrations/*Note: Migrations are auto-generated during development. They're only needed if you're iterating on the schema, not for running the existing database.
cd .pocketbase && ./pocketbase serveLeave this terminal running. Pocketbase serves at:
- API: http://localhost:8090
- Admin UI: http://localhost:8090/_/
- Login:
admin@localhost.com/testpassword123
In a new terminal:
pnpm devSite runs at http://localhost:5173
# Start Pocketbase (in background)
cd .pocketbase && ./pocketbase serve &
# Start dev server
pnpm dev| Command | Description |
|---|---|
pnpm dev |
Start development server |
pnpm build |
Build for production |
pnpm preview |
Preview production build |
pnpm reload-data |
Reload data loaders (after Pocketbase edits) |
| Command | Description |
|---|---|
pnpm dev:setup |
Run setup (idempotent, safe anytime) |
pnpm dev:setup:check |
Validate setup without changes |
pnpm dev:setup:force |
Force fresh database restore |
pnpm db:export |
Export database to git-friendly format |
pnpm db:export:diff |
Export and show git diff |
Fetch README content from GitHub and populate reference URLs:
| Command | Description |
|---|---|
pnpm db:populate |
Fetch README content from GitHub repos |
pnpm db:populate:refs |
Populate reference URLs (STIG/CIS links) |
Script options:
npx tsx scripts/fetch-readmes.ts --dry-run # Preview changes
npx tsx scripts/fetch-readmes.ts --force # Re-fetch all records
npx tsx scripts/fetch-readmes.ts --refs-only # Only update reference URLs
npx tsx scripts/fetch-readmes.ts --limit 10 # Process first 10 recordsInteractive CLI for managing content and database operations.
| Command | Description |
|---|---|
pnpm cli --help |
Show CLI help |
pnpm cli content list |
List all content records |
pnpm cli content add <url> |
Add content from GitHub repo |
pnpm cli content show <id> |
Show content details |
pnpm cli db status |
Check database connection |
pnpm cli db validate |
Validate database integrity |
pnpm cli db lookups |
Show FK lookup values |
Examples:
# Add a new InSpec profile (interactive)
pnpm cli content add https://github.com/mitre/redhat-enterprise-linux-9-stig-baseline
# Add with all options (non-interactive)
pnpm cli content add https://github.com/mitre/some-repo \
--type validation \
--vendor MITRE \
--standard "DISA STIG" \
--technology InSpec \
--target "RHEL 9" \
--yes
# List validation profiles
pnpm cli content list --type validation
# Validate database
pnpm cli db validateSee cli/README.md for full CLI documentation.
Optional CLI for advanced database operations. Requires pb-cli.
| Command | Description |
|---|---|
pnpm pb:setup |
Configure pb-cli for this project (recommended) |
pnpm db:backup |
Create a binary backup |
pnpm db:backup:list |
List available backups |
pnpm db:backup:download <name> |
Download a backup file |
pnpm db:backup:restore <name> |
Restore from a backup |
First-time setup:
# Install pb-cli (Go required)
go install github.com/skeeeon/pb-cli/cmd/pb@latest
# Configure for this project (auto-configures all 33 collections)
pnpm pb:setupThe setup script creates a saf-site context with all collections from the database. Run it anytime to sync pb-cli with the current schema.
| Command | Description |
|---|---|
pnpm test |
Run tests in watch mode |
pnpm test:run |
Run tests once |
pnpm test:coverage |
Run tests with coverage |
| Command | Description |
|---|---|
pnpm story:dev |
Start Histoire dev server (generates docs first) |
pnpm story:build |
Build stories for production |
pnpm story:preview |
Preview built stories |
pnpm story:docs |
Regenerate component documentation |
pnpm story:docs:check |
Verify docs are up-to-date (for CI) |
| Command | Description |
|---|---|
pnpm lint |
Check code style (ESLint + @antfu/eslint-config) |
pnpm lint:fix |
Auto-fix code style issues |
pnpm typecheck |
TypeScript type checking (vue-tsc) |
| Command | Description |
|---|---|
pnpm ci |
Run full CI pipeline (typecheck + lint + test + docs check) |
pnpm ci:security |
Run security audit (pnpm audit) |
This project uses Beads for task tracking. Tasks live in .beads/ and sync with git.
| Command | Description |
|---|---|
bd ready |
See available work (unblocked tasks) |
bd list --status=open |
List all open tasks |
bd show <id> |
View task details |
bd update <id> --status=in_progress |
Claim a task |
bd close <id> |
Complete a task |
bd sync |
Sync tasks with git remote |
# Terminal 1: Start Pocketbase
cd .pocketbase && ./pocketbase serve
# Terminal 2: Start dev server
pnpm devSite available at http://localhost:5173
# Run setup to restore any database changes
pnpm dev:setup
# Restart Pocketbase if it was running
cd .pocketbase && ./pocketbase serve- Open Pocketbase Admin: http://localhost:8090/_/
- Login:
admin@localhost.com/testpassword123 - Navigate to collection (e.g., content, organizations, standards)
- Edit or add records - FK fields show searchable dropdowns
- Refresh dev server:
pnpm reload-data - Export for git:
pnpm db:export - Commit:
git add .pocketbase/pb_data/diffable/ && git commit
- Open Pocketbase Admin → content collection
- Click New record
- Fill required fields:
name: Display name (e.g., "RHEL 9 STIG")slug: URL-friendly ID (e.g., "rhel-9-stig")content_type: Select "validation" or "hardening"description: Brief descriptiongithub: GitHub repository URLvendor: Select from dropdown (MITRE, etc.)standard: Select from dropdown (DISA STIG, CIS, etc.)target: Select from dropdown (RHEL 9, etc.)technology: Select from dropdown (InSpec, Ansible, etc.)
- Save → Export → Commit
- Optional: Run
pnpm db:populateto fetch README from GitHub
# Run all tests
pnpm test:run
# Run with coverage
pnpm test:coverage
# Watch mode during development
pnpm test# Ensure Pocketbase is running
cd .pocketbase && ./pocketbase serve &
# Build static site
pnpm build
# Preview the build
pnpm previewOutput is in docs/.vitepress/dist/
- Create component in
docs/.vitepress/theme/components/with JSDoc (@component,@example) - Create colocated test file (
ComponentName.spec.ts) - Create a story file (
ComponentName.story.vue) with variants - Run
pnpm story:docsto generate documentation - Register globally in
docs/.vitepress/theme/index.tsif needed - Run tests:
pnpm test:run - Run lint:
pnpm lint:fix - View in Histoire:
pnpm story:dev
See docs/.vitepress/theme/components/CLAUDE.md for detailed patterns and conventions.
Histoire provides an interactive component explorer (like Storybook).
# Start Histoire (separate from main dev server)
pnpm story:devHistoire runs at http://localhost:6006
Documentation is generated automatically from component JSDoc comments. This keeps docs in sync with code (single source of truth).
In your component, use @component and @example tags:
<script setup lang="ts">
/**
* @component LogoGrid - Display partner logos in a responsive grid.
* Supports multiple variants and layouts.
*
* @example Basic usage
* <LogoGrid :items="partners" :showNames="true" />
*
* @example Card variant
* <LogoGrid :items="partners" variant="card" />
*/
export interface LogoGridProps {
/** Array of logo items to display */
items: LogoItem[]
/** Logo size in pixels */
size?: number
}
</script>Run pnpm story:docs to generate <docs> blocks in story files.
The generated docs include:
- Component description (from
@component) - Usage examples (from
@example) - Props table (from TypeScript interface)
- Exported types
See docs/.vitepress/theme/components/STYLE-GUIDE.md for full conventions.
<!-- ComponentName.story.vue (colocated with component) -->
<script setup lang="ts">
import ComponentName from './ComponentName.vue'
</script>
<template>
<Story title="Category/ComponentName">
<Variant title="Default">
<ComponentName />
</Variant>
<Variant title="With Props">
<ComponentName :someProp="value" />
</Variant>
</Story>
</template>
<!-- <docs> block auto-generated by pnpm story:docs -->Key features:
- Dark mode toggle syncs with component themes
- Docs panel shows auto-generated documentation
- Interactive controls panel for props
- Source code viewer
- Multiple variants per component
# After editing in Pocketbase, refresh the dev server
pnpm reload-data# Force restore from git
pnpm dev:setup:force# Clear migrations (common fix)
rm -rf .pocketbase/pb_migrations/*
# Try again
cd .pocketbase && ./pocketbase servedocs/
├── .vitepress/
│ ├── config.ts # VitePress configuration
│ ├── database/
│ │ └── schema.ts # Drizzle schema (source of truth)
│ ├── loaders/
│ │ └── profiles.data.ts # Build-time Pocketbase queries
│ └── theme/
│ ├── components/ # Vue components
│ │ ├── ui/ # shadcn-vue components
│ │ ├── CLAUDE.md # AI context for component development
│ │ └── STYLE-GUIDE.md # Documentation conventions
│ ├── composables/ # Vue composables (logic)
│ └── custom.css # Theme + Tailwind customization
├── index.md # Home page
├── content/ # Content library (validation + hardening)
│ ├── index.md # Browse page
│ ├── [slug].md # Dynamic detail page template
│ └── [slug].paths.ts # Dynamic route generator
└── validate/ # Legacy validation profiles route
scripts/
├── setup.sh # Idempotent project setup
├── setup-pb-cli.sh # Configure pb-cli for this project
├── fetch-readmes.ts # Populate content from GitHub
├── export-db.sh # Export database to diffable/
├── inject-story-docs.ts # Generate component docs for Histoire
└── inject-story-docs.spec.ts # Tests for doc generation
.pocketbase/
├── pocketbase # Pocketbase binary (macOS ARM64)
├── pb_data/
│ ├── data.db # SQLite database (gitignored)
│ └── diffable/ # Git-tracked NDJSON export
└── pb_migrations/ # Auto-generated (gitignored)
Content is managed in Pocketbase and queried at build time via data loaders.
cd .pocketbase && ./pocketbase serve- Admin UI: http://localhost:8090/_/
- Default login:
admin@localhost.com/testpassword123
| Collection | Purpose |
|---|---|
content |
Validation profiles and hardening content (82 records) |
standards |
Compliance frameworks (STIG, CIS, etc.) |
technologies |
InSpec, Ansible, Chef, Terraform, etc. |
targets |
What gets secured (RHEL 8, MySQL, etc.) |
organizations |
MITRE, DISA, CIS, vendors |
tags |
Categorization tags |
- Start Pocketbase
- Open admin UI at http://localhost:8090/_/
- Edit collections (content, standards, technologies, etc.)
- Run
pnpm reload-datato refresh dev server
Content records can auto-populate README content and reference URLs:
# Fetch README.md from GitHub for all content
pnpm db:populate
# Populate reference URLs (STIG/CIS links)
pnpm db:populate:refsThe database is exported to git-friendly NDJSON format:
# Export (after editing in Pocketbase)
pnpm db:export
# Import (after cloning/pulling - handled by setup script)
pnpm db:loadEdit in Pocketbase UI → pnpm reload-data → Dev server refreshes
Pocketbase API → Data Loaders → Composables → Vue Components → Static HTML
Composables (logic) ← Unit tests
Components (presentation) ← Component tests
Tests use Vitest with @vue/test-utils.
pnpm test:run # Run once
pnpm test:coverage # With coverage
pnpm test # Watch modeTest files are colocated with source:
composables/useContentDetail.ts→composables/useContentDetail.spec.tscomponents/ContentDetail.vue→components/ContentDetail.spec.ts
| Layer | Technology |
|---|---|
| Static Site | VitePress 2.0 (alpha) |
| UI Framework | Vue 3.5 |
| Components | Reka UI (shadcn-vue) |
| Styling | Tailwind CSS 4 |
| Content Database | Pocketbase 0.36 |
| Schema Definition | Drizzle ORM |
| Testing | Vitest 4 |
| Component Stories | Histoire 1.0 (beta) |
| Linting | ESLint 9 + @antfu/eslint-config |
| Type Checking | vue-tsc |
| Package Manager | pnpm 10 |
Build output (docs/.vitepress/dist/) can be deployed to any static hosting:
- GitHub Pages
- Netlify
- Vercel
- Cloudflare Pages
Note: Pocketbase must be running during pnpm build to query content.
Error: Failed to apply migration ... due to existing relation references
Fix: Clear the migrations folder - they're not needed with a restored database:
rm -rf .pocketbase/pb_migrations/*The included binary is for macOS ARM64 (Apple Silicon). For other platforms:
- Download from pocketbase.io/docs
- Replace
.pocketbase/pocketbasewith your platform's binary - Make executable:
chmod +x .pocketbase/pocketbase
Ensure Pocketbase is running before starting the dev server:
# Check if running
curl http://localhost:8090/api/health
# If not, start it
cd .pocketbase && ./pocketbase serveVitePress data loaders run at build time. After editing in Pocketbase:
pnpm reload-dataEnsure dependencies are installed:
pnpm installSee CONTRIBUTING.md
Apache 2.0 - See LICENSE.md
- GitHub Issues: https://github.com/mitre/saf-site-vitepress/issues
- SAF Discussions: https://github.com/mitre/saf/discussions