CAAMS Docs
Documentation

CAAMS Enterprise

A self-hosted, multi-user GRC platform. Select a security framework, map your tool stack, and run an auditor-ready compliance assessment — with full lifecycle management, evidence collection, findings tracking, RFIs, audit log, and multi-format exports.

5
Frameworks
0
External deps
3
Export formats
Users & assessments

Quick Start

1. Install dependencies

bash
pip install -r requirements.txt

2. Seed the database

Loads all framework definitions and the tool catalog. Safe to re-run — skips anything already present.

bash
python seed.py

3. Set the secret key

CAAMS requires a secret key to sign JWT tokens. The app will refuse to start without it.

bash
export CAAMS_SECRET_KEY="$(python3 -c 'import secrets; print(secrets.token_hex(32))')"

Add that line to your shell profile or a .env file for persistent dev setups.

4. Generate TLS certificates

The default configuration serves over HTTPS. Self-signed certificates are required for local development.

bash
mkdir -p certs
openssl req -x509 -newkey rsa:4096 \
  -keyout certs/key.pem -out certs/cert.pem \
  -sha256 -days 3650 -nodes \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

5. Start the server

bash
bash start.sh
# → https://localhost:8443
Dev shortcut — to skip TLS during local testing:
uvicorn app.main:app --reload --port 8000

First-Run Setup

On first visit, the UI shows a setup screen to create the initial admin account. You can also do this via the API:

bash
curl -X POST https://localhost:8443/auth/setup \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "YourPassword"}'

This endpoint is only available when no users exist. After setup, it is permanently disabled.

Features

FeatureDetails
Framework coverage mappingMap your tool stack against CIS v8, NIST CSF v2, SOC 2, PCI DSS v4, and HIPAA. Coverage is computed automatically from capability tags — no manual mapping.
Assessment lifecycleDraft → In Review → Approved → Archived, with signed-off stage transitions and full history on every action.
Recurring assessmentsSet a configurable recurrence interval (e.g. 90 days). CAAMS tracks the next review date and surfaces overdue renewals on the dashboard.
Evidence managementUpload files per-control with descriptions and expiry dates. Admins approve or reject with reasons. Download evidence packages as ZIP.
Findings trackerLog findings with severity (critical → informational), remediation owner, target date, and full status lifecycle (open → remediated → closed).
Risk acceptancesFormally accept residual risk with a rated justification, named approver, and expiry date. Surfaces in exports and the audit trail.
RFIsCreate Requests for Information with priority levels, assignees, and due dates. Assignees respond inline; admins close when resolved.
Control overridesManually override a control's computed status with a justification and optional expiry date.
Ownership trackingAssign owner, team, and evidence owner per control.
Control review workflowTrack per-control review status: not_reviewed → in_review → approved / rejected.
Statement of ApplicabilityMark controls as not applicable with exclusion reasons. Included in the SOA sheet of the XLSX export.
Executive dashboardOrg-wide compliance posture across all frameworks — scores, open findings, overdue controls, and the assessment renewal pipeline.
Framework crosswalkTag-based automatic overlap mapping between any two loaded frameworks.
Assessment cloneDuplicate any assessment including tools, ownership, and notes.
Tool recommendationsRanked list of tools not yet in scope that would close the most coverage gaps.
Auditor share linksScoped, time-limited share links for external auditors. No login required — access limited to exactly the controls you choose.
Immutable audit logEvery state-changing action recorded with user, timestamp, IP, and detail payload. Cannot be edited or deleted.
Role-based accessAdmin / Contributor / Viewer / Auditor roles enforced on every endpoint.
REST APIFull FastAPI backend with interactive Swagger UI at /docs. Long-lived API tokens for CI/CD pipelines.
Rate limitingLogin endpoint is rate-limited to 10 attempts per minute per IP.

Supported Frameworks

FrameworkVersionControls
CIS Controlsv818
NIST Cybersecurity Frameworkv2.06 functions / 22 categories
SOC 2 Trust Services Criteria20179
PCI DSSv4.012 requirements
HIPAA Security Rule45 CFR Part 16416 standards

Additional frameworks can be added by dropping a JSON file into app/data/. See Adding Frameworks.

Usage Guide

Creating an assessment

  1. Click Assessments → New Assessment
  2. Enter a name, pick a framework, add scope notes, and optionally set a recurrence schedule
  3. Click Submit — the assessment opens in Draft status

From the assessment detail view, switch to the Controls tab and click Edit on any control to set notes, evidence links, ownership, and override status.

Evidence

Upload files on the Evidence tab. Files are associated with a specific control, given a description and expiry date, and can be approved or rejected by admins. Full evidence packages (PDF report + all files + manifest CSV) are downloadable as a ZIP.

Findings

Log issues on the Findings tab. Each finding has severity (critical → informational), status, and a remediation owner + target date. Closing a finding automatically stamps the close date.

Requests for Information (RFIs)

Create RFIs with priority levels and due dates. Assignees respond inline; admins close RFIs when resolved. Useful for coordinating evidence requests with external auditors.

Dashboard

The Dashboard shows org-wide posture across all active assessments:

  • Overall compliance score and per-framework bar chart
  • Open findings by severity (doughnut chart)
  • Overdue controls count
  • Assessments due for renewal in the next 30 days
  • Assessment pipeline (draft / in_review / approved count)

Assessment Lifecycle

ActionAllowed byTransition
Submit for ReviewContributorDraft → In Review
ApproveAdminIn Review → Approved
Return to DraftAdmin / ContributorIn Review → Draft
ArchiveAdminAny → Archived

Each transition creates a signed-off record with comments, visible on the Audit Log tab.

Coverage Scoring

CAAMS uses partial-credit scoring rather than a simple pass/fail:

StatusMeaning
CoveredAll required capability tags are satisfied by selected tools
PartialSome required tags are present but not all
Not CoveredNo required capability tags are satisfied
Not ApplicableExcluded from scope with a documented justification
Coverage Score Formula
score = (covered + 0.5 × partial) / applicable_total × 100

Authentication

CAAMS uses JWT-based authentication (HS256, pure-Python — no C dependencies). All API endpoints except /health and /auth/setup require a valid bearer token.

Logging in

bash
curl -X POST https://localhost:8443/auth/login \
  -d "username=admin&password=YourPassword"
# returns {"access_token": "...", "token_type": "bearer", "role": "admin"}

# Pass the token in subsequent requests:
curl https://localhost:8443/assessments \
  -H "Authorization: Bearer <token>"

Tokens expire after 8 hours. Login is rate-limited to 10 attempts per minute per IP.

Roles

Roles are assigned at account creation and enforced on every endpoint.

RolePermissions
admin Full access — create/delete assessments, manage users, approve lifecycle transitions, approve evidence, manage API tokens
contributor Create and edit assessments, update notes, ownership, evidence, findings, and RFIs
viewer Read-only access to all assessments, results, evidence, and findings
auditor External access via scoped share link — no account needed. Read-only, limited to exactly the controls shared, with comment thread access

API Tokens

For CI/CD pipelines and external integrations, create long-lived API tokens via Admin → API Tokens or via the API:

bash
curl -X POST https://localhost:8443/api-tokens \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{"name": "ci-pipeline", "expires_in_days": 365}'
The plaintext token is returned once at creation. Store it securely — it cannot be retrieved again.

API Reference

The full interactive Swagger UI is available at /docs on any running CAAMS instance.

Auth

MethodPathDescription
GET/auth/setup-neededReturns {"needed": true} if no users exist
POST/auth/setupCreate the first admin account (one-time only)
POST/auth/loginExchange credentials for a JWT (form-encoded, rate-limited)
GET/auth/meCurrent user profile
GET/auth/usersList all users (admin only)
POST/auth/usersCreate a new user (admin only)
PATCH/auth/users/{id}Update role, password, or active flag (admin only)
DELETE/auth/users/{id}Delete a user (admin only)

Frameworks & Tools

MethodPathDescription
GET/frameworksList all frameworks (includes control_count)
GET/frameworks/{id}/controlsList controls for a framework
GET/toolsList all tools in the catalog
POST/toolsAdd a tool (admin only)
DELETE/tools/{id}Remove a tool (admin only)
POST/tools/uploadBulk-import tools from a JSON array (admin only)
GET/tools/template/downloadDownload the JSON import template

Assessments

MethodPathDescription
POST/assessmentsCreate an assessment (contributor)
GET/assessmentsList all assessments
GET/assessments/historyList with pre-computed metrics (scores, counts)
GET/assessments/{id}Get assessment metadata
DELETE/assessments/{id}Delete an assessment (admin only)
POST/assessments/{id}/cloneClone with tools, notes, and ownership (contributor)
POST/assessments/{id}/lifecycleSubmit / approve / return / archive
GET/assessments/{id}/signoffsLifecycle sign-off history
GET/assessments/{id}/resultsFull coverage results for all controls
GET/assessments/{id}/toolsTools currently in scope
PATCH/assessments/{id}/toolsUpdate tool selection
GET/assessments/{id}/recommendationsRanked tool recommendations to close coverage gaps

Controls

MethodPathDescription
PATCH/assessments/{id}/controls/{cid}/notesUpsert notes, evidence URL, override status, applicability (contributor)
PATCH/assessments/{id}/controls/{cid}/reviewSet review status (contributor)
PATCH/assessments/{id}/controls/{cid}/ownershipSet owner, team, and evidence owner (contributor)

Evidence

MethodPathDescription
GET/assessments/{id}/evidenceList evidence files for an assessment
POST/assessments/{id}/evidenceUpload a file (multipart/form-data)
PATCH/assessments/{id}/evidence/{fid}/approvalApprove or reject with reason (admin only)
GET/assessments/{id}/evidence/{fid}/downloadDownload the file
DELETE/assessments/{id}/evidence/{fid}Delete an evidence file

Findings & RFIs

MethodPathDescription
GET/assessments/{id}/findingsList findings
POST/assessments/{id}/findingsCreate a finding (contributor)
PATCH/assessments/{id}/findings/{fid}Update a finding (contributor)
DELETE/assessments/{id}/findings/{fid}Delete a finding (contributor)
GET/assessments/{id}/rfisList RFIs
POST/assessments/{id}/rfisCreate an RFI
PATCH/assessments/{id}/rfis/{rid}Update RFI status (close, reopen)
POST/assessments/{id}/rfis/{rid}/responsesSubmit a response to an RFI

Exports

MethodPathReturns
GET/assessments/{id}/exportXLSX workbook (6 sheets)
GET/assessments/{id}/export/soaStandalone SOA XLSX
GET/assessments/{id}/export/pdfPDF report
GET/assessments/{id}/export/evidence-packageZIP (PDF + evidence files + manifest CSV)

Dashboard, Audit Log & Misc

MethodPathDescription
GET/dashboardOrg-wide compliance dashboard data
GET/audit-logGlobal audit log (admin, paginated)
GET/audit-log/assessment/{id}Per-assessment audit log
GET/api-tokensList API tokens for current user
POST/api-tokensCreate a long-lived API token
DELETE/api-tokens/{id}Revoke an API token
GET/crosswalkTag-based crosswalk between two frameworks
GET/crosswalk/multi-frameworkCoverage of all frameworks from one assessment
GET/healthHealth check (no auth required)

Exports

XLSX Workbook GET /assessments/{id}/export

SheetContents
SummaryAssessment name, framework, status, dates, and aggregate compliance metrics
Coverage ReportAll controls with status, override, owners, covered-by tools, missing tags, notes, evidence URL, and finding counts
Evidence ChecklistOne row per required evidence item per control, with owners and status
SOAStatement of Applicability — applicable flag, exclusion reason, override, and reviewer per control
FindingsAll findings with severity (color-coded), status, remediation owner, and dates
RecommendationsTools not in scope ranked by number of additional controls they would cover

PDF Report GET /assessments/{id}/export/pdf

  • Branded cover page with assessment name, framework, and date
  • Executive summary with aggregate metrics
  • Tools-in-scope table
  • Color-coded per-control coverage table
  • Findings table with severity

Evidence ZIP Package GET /assessments/{id}/export/evidence-package

  • Complete PDF report included at the root
  • All evidence files grouped by control ID in subdirectories
  • Manifest CSV mapping each file to its control, description, uploader, and approval status

Environment Variables

VariableRequiredDefaultDescription
CAAMS_SECRET_KEY Required 64-char hex string to sign JWTs. Generate with python3 -c "import secrets; print(secrets.token_hex(32))". App refuses to start without it.
CAAMS_CORS_ORIGIN Optional Your intranet hostname (e.g. https://caams.corp.local) to enable credentialed cross-origin requests. If unset, CORS is open with credentials disabled.
CAAMS_HOST Optional 0.0.0.0 Bind address
CAAMS_PORT Optional 8443 Port to listen on

In production these are loaded from /etc/caams.env by the systemd unit (created automatically by install_service.sh).

Production Deployment

install_service.sh automates a full production install on any systemd-based Linux host (tested on Ubuntu 22.04+).

bash
sudo bash install_service.sh

The installer:

  1. Verifies prerequisites (systemctl, python3)
  2. Copies the app to /opt/caams/
  3. Creates a virtualenv at /opt/caams/venv and installs all dependencies
  4. Requires TLS certificates at certs/cert.pem + certs/key.pem — prints generation instructions and aborts if missing
  5. Creates a dedicated caams system user and group
  6. Generates a random CAAMS_SECRET_KEY and writes it to /etc/caams.env (readable only by root and the service account)
  7. Seeds the database if caams.db does not exist
  8. Writes the systemd unit to /etc/systemd/system/caams.service, enables, and starts it
bash — post-install
sudo systemctl status caams
sudo journalctl -u caams -f              # live log stream
sudo tail -f /opt/caams/logs/app.log     # app events only

# Swap in a CA-signed certificate:
sudo cp your-cert.pem /opt/caams/certs/cert.pem
sudo cp your-key.pem  /opt/caams/certs/key.pem
sudo systemctl restart caams

Logging

Two rotating log files are written to logs/ (10 MB per file, 5 backups). All entries also appear in stdout / journalctl -u caams.

logs/access.log — every HTTP request

access.log
2026-02-21 12:34:56 | 10.0.0.1 | POST /auth/login | 200 | 13ms
2026-02-21 12:34:57 | 10.0.0.1 | GET /assessments/5/results | 200 | 87ms

logs/app.log — application events

app.log
2026-02-21 12:34:55 | INFO    | STARTUP | CAAMS v1.0.0 | database ready
2026-02-21 12:34:56 | WARNING | LOGIN failed | username=badguy | ip=10.0.0.3
2026-02-21 12:34:57 | INFO    | LOGIN success | user=admin | role=admin | ip=10.0.0.1
2026-02-21 12:35:10 | INFO    | ASSESSMENT created | id=5 | name=Q1 Audit | by=admin
2026-02-21 12:36:00 | INFO    | LIFECYCLE | assessment=5 | action=approve | by=admin

Adding Frameworks

Create a JSON file in app/data/:

app/data/my_framework.json
{
  "name": "My Framework",
  "version": "v1.0",
  "description": "Optional description.",
  "controls": [
    {
      "control_id": "MF-1",
      "title": "Control Title",
      "description": "What this control requires.",
      "required_tags": ["tag-a", "tag-b"],
      "optional_tags": ["tag-c"],
      "evidence": [
        "Evidence item description 1",
        "Evidence item description 2"
      ]
    }
  ]
}

Then add the filename to FRAMEWORK_FILES in seed.py and re-run:

bash
python seed.py    # safe to re-run — existing data is not affected
Tags must match capability tags in app/data/tools_catalog.json. To list all available tags:
python3 -c "import json; data=json.load(open('app/data/tools_catalog.json')); print('\n'.join(sorted({t for tool in data for t in tool['capabilities']})))"

Adding Tools

Three ways to add tools:

1. Edit the catalog JSON

app/data/tools_catalog.json
{
  "name": "My Tool",
  "category": "EDR",
  "description": "Endpoint detection and response.",
  "capabilities": ["endpoint-protection", "malware-detection", "EDR"]
}

Re-run python seed.py to load it.

2. UI — Tools → Add Tool

Available to admins in the web UI under the Tools section.

3. API bulk import

bash
curl -X POST https://localhost:8443/tools/upload \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '[{"name":"My Tool","category":"EDR","capabilities":["EDR"]}]'

Project Structure

caams/
caams/
├── app/
│   ├── data/                       # Framework JSON files and tool catalog
│   ├── engine/
│   │   └── mapper.py               # Coverage computation engine
│   ├── importers/
│   │   └── cis_xlsx.py             # CIS Controls XLSX importer
│   ├── routers/
│   │   ├── api_tokens.py           # Long-lived API token management
│   │   ├── assessments.py          # Assessment CRUD, lifecycle, notes, clone
│   │   ├── audit_log.py            # Immutable audit log endpoints
│   │   ├── auditor_shares.py       # Scoped external auditor share links
│   │   ├── auth.py                 # Login, setup, user management
│   │   ├── crosswalk.py            # Framework crosswalk
│   │   ├── dashboard.py            # Org-wide executive dashboard
│   │   ├── evidence.py             # Evidence upload, approval, download
│   │   ├── export.py               # XLSX export
│   │   ├── findings.py             # Findings and risk acceptance tracker
│   │   ├── frameworks.py           # Framework and control endpoints
│   │   ├── pdf_export.py           # PDF report + evidence ZIP
│   │   ├── rfi.py                  # Request for Information endpoints
│   │   └── tools.py                # Tool catalog endpoints
│   ├── auth.py                     # JWT, password hashing, role dependencies
│   ├── database.py                 # SQLAlchemy engine + session factory
│   ├── jwt_utils.py                # Pure-Python HS256 JWT (no C deps)
│   ├── limiter.py                  # Shared rate limiter
│   ├── logging_config.py           # Rotating file handler
│   ├── main.py                     # FastAPI app, middleware, CORS, lifespan
│   ├── models.py                   # SQLAlchemy ORM models
│   └── schemas.py                  # Pydantic v2 request/response schemas
├── static/
│   ├── index.html                  # SPA shell and all view templates
│   ├── app.js                      # Alpine.js — all state and API calls
│   └── app.css                     # Inputs, buttons, cards, badges
├── caams.service                   # systemd unit file
├── install_service.sh              # Production installer
├── seed.py                         # Database seeder
├── start.sh                        # Dev start script (HTTPS)
└── requirements.txt