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.
Quick Start
1. Install dependencies
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.
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.
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.
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 start.sh
# → https://localhost:8443
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:
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
| Feature | Details |
|---|---|
| Framework coverage mapping | Map 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 lifecycle | Draft → In Review → Approved → Archived, with signed-off stage transitions and full history on every action. |
| Recurring assessments | Set a configurable recurrence interval (e.g. 90 days). CAAMS tracks the next review date and surfaces overdue renewals on the dashboard. |
| Evidence management | Upload files per-control with descriptions and expiry dates. Admins approve or reject with reasons. Download evidence packages as ZIP. |
| Findings tracker | Log findings with severity (critical → informational), remediation owner, target date, and full status lifecycle (open → remediated → closed). |
| Risk acceptances | Formally accept residual risk with a rated justification, named approver, and expiry date. Surfaces in exports and the audit trail. |
| RFIs | Create Requests for Information with priority levels, assignees, and due dates. Assignees respond inline; admins close when resolved. |
| Control overrides | Manually override a control's computed status with a justification and optional expiry date. |
| Ownership tracking | Assign owner, team, and evidence owner per control. |
| Control review workflow | Track per-control review status: not_reviewed → in_review → approved / rejected. |
| Statement of Applicability | Mark controls as not applicable with exclusion reasons. Included in the SOA sheet of the XLSX export. |
| Executive dashboard | Org-wide compliance posture across all frameworks — scores, open findings, overdue controls, and the assessment renewal pipeline. |
| Framework crosswalk | Tag-based automatic overlap mapping between any two loaded frameworks. |
| Assessment clone | Duplicate any assessment including tools, ownership, and notes. |
| Tool recommendations | Ranked list of tools not yet in scope that would close the most coverage gaps. |
| Auditor share links | Scoped, time-limited share links for external auditors. No login required — access limited to exactly the controls you choose. |
| Immutable audit log | Every state-changing action recorded with user, timestamp, IP, and detail payload. Cannot be edited or deleted. |
| Role-based access | Admin / Contributor / Viewer / Auditor roles enforced on every endpoint. |
| REST API | Full FastAPI backend with interactive Swagger UI at /docs. Long-lived API tokens for CI/CD pipelines. |
| Rate limiting | Login endpoint is rate-limited to 10 attempts per minute per IP. |
Supported Frameworks
| Framework | Version | Controls |
|---|---|---|
| CIS Controls | v8 | 18 |
| NIST Cybersecurity Framework | v2.0 | 6 functions / 22 categories |
| SOC 2 Trust Services Criteria | 2017 | 9 |
| PCI DSS | v4.0 | 12 requirements |
| HIPAA Security Rule | 45 CFR Part 164 | 16 standards |
Additional frameworks can be added by dropping a JSON file into app/data/. See Adding Frameworks.
Usage Guide
Creating an assessment
- Click Assessments → New Assessment
- Enter a name, pick a framework, add scope notes, and optionally set a recurrence schedule
- 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
| Action | Allowed by | Transition |
|---|---|---|
| Submit for Review | Contributor | Draft → In Review |
| Approve | Admin | In Review → Approved |
| Return to Draft | Admin / Contributor | In Review → Draft |
| Archive | Admin | Any → 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:
| Status | Meaning |
|---|---|
| Covered | All required capability tags are satisfied by selected tools |
| Partial | Some required tags are present but not all |
| Not Covered | No required capability tags are satisfied |
| Not Applicable | Excluded from scope with a documented justification |
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
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.
| Role | Permissions |
|---|---|
| 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:
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}'
API Reference
The full interactive Swagger UI is available at /docs on any running CAAMS instance.
Auth
| Method | Path | Description |
|---|---|---|
| GET | /auth/setup-needed | Returns {"needed": true} if no users exist |
| POST | /auth/setup | Create the first admin account (one-time only) |
| POST | /auth/login | Exchange credentials for a JWT (form-encoded, rate-limited) |
| GET | /auth/me | Current user profile |
| GET | /auth/users | List all users (admin only) |
| POST | /auth/users | Create 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
| Method | Path | Description |
|---|---|---|
| GET | /frameworks | List all frameworks (includes control_count) |
| GET | /frameworks/{id}/controls | List controls for a framework |
| GET | /tools | List all tools in the catalog |
| POST | /tools | Add a tool (admin only) |
| DELETE | /tools/{id} | Remove a tool (admin only) |
| POST | /tools/upload | Bulk-import tools from a JSON array (admin only) |
| GET | /tools/template/download | Download the JSON import template |
Assessments
| Method | Path | Description |
|---|---|---|
| POST | /assessments | Create an assessment (contributor) |
| GET | /assessments | List all assessments |
| GET | /assessments/history | List with pre-computed metrics (scores, counts) |
| GET | /assessments/{id} | Get assessment metadata |
| DELETE | /assessments/{id} | Delete an assessment (admin only) |
| POST | /assessments/{id}/clone | Clone with tools, notes, and ownership (contributor) |
| POST | /assessments/{id}/lifecycle | Submit / approve / return / archive |
| GET | /assessments/{id}/signoffs | Lifecycle sign-off history |
| GET | /assessments/{id}/results | Full coverage results for all controls |
| GET | /assessments/{id}/tools | Tools currently in scope |
| PATCH | /assessments/{id}/tools | Update tool selection |
| GET | /assessments/{id}/recommendations | Ranked tool recommendations to close coverage gaps |
Controls
| Method | Path | Description |
|---|---|---|
| PATCH | /assessments/{id}/controls/{cid}/notes | Upsert notes, evidence URL, override status, applicability (contributor) |
| PATCH | /assessments/{id}/controls/{cid}/review | Set review status (contributor) |
| PATCH | /assessments/{id}/controls/{cid}/ownership | Set owner, team, and evidence owner (contributor) |
Evidence
| Method | Path | Description |
|---|---|---|
| GET | /assessments/{id}/evidence | List evidence files for an assessment |
| POST | /assessments/{id}/evidence | Upload a file (multipart/form-data) |
| PATCH | /assessments/{id}/evidence/{fid}/approval | Approve or reject with reason (admin only) |
| GET | /assessments/{id}/evidence/{fid}/download | Download the file |
| DELETE | /assessments/{id}/evidence/{fid} | Delete an evidence file |
Findings & RFIs
| Method | Path | Description |
|---|---|---|
| GET | /assessments/{id}/findings | List findings |
| POST | /assessments/{id}/findings | Create a finding (contributor) |
| PATCH | /assessments/{id}/findings/{fid} | Update a finding (contributor) |
| DELETE | /assessments/{id}/findings/{fid} | Delete a finding (contributor) |
| GET | /assessments/{id}/rfis | List RFIs |
| POST | /assessments/{id}/rfis | Create an RFI |
| PATCH | /assessments/{id}/rfis/{rid} | Update RFI status (close, reopen) |
| POST | /assessments/{id}/rfis/{rid}/responses | Submit a response to an RFI |
Exports
| Method | Path | Returns |
|---|---|---|
| GET | /assessments/{id}/export | XLSX workbook (6 sheets) |
| GET | /assessments/{id}/export/soa | Standalone SOA XLSX |
| GET | /assessments/{id}/export/pdf | PDF report |
| GET | /assessments/{id}/export/evidence-package | ZIP (PDF + evidence files + manifest CSV) |
Dashboard, Audit Log & Misc
| Method | Path | Description |
|---|---|---|
| GET | /dashboard | Org-wide compliance dashboard data |
| GET | /audit-log | Global audit log (admin, paginated) |
| GET | /audit-log/assessment/{id} | Per-assessment audit log |
| GET | /api-tokens | List API tokens for current user |
| POST | /api-tokens | Create a long-lived API token |
| DELETE | /api-tokens/{id} | Revoke an API token |
| GET | /crosswalk | Tag-based crosswalk between two frameworks |
| GET | /crosswalk/multi-framework | Coverage of all frameworks from one assessment |
| GET | /health | Health check (no auth required) |
Exports
XLSX Workbook GET /assessments/{id}/export
| Sheet | Contents |
|---|---|
| Summary | Assessment name, framework, status, dates, and aggregate compliance metrics |
| Coverage Report | All controls with status, override, owners, covered-by tools, missing tags, notes, evidence URL, and finding counts |
| Evidence Checklist | One row per required evidence item per control, with owners and status |
| SOA | Statement of Applicability — applicable flag, exclusion reason, override, and reviewer per control |
| Findings | All findings with severity (color-coded), status, remediation owner, and dates |
| Recommendations | Tools 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
| Variable | Required | Default | Description |
|---|---|---|---|
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+).
sudo bash install_service.sh
The installer:
- Verifies prerequisites (
systemctl,python3) - Copies the app to
/opt/caams/ - Creates a virtualenv at
/opt/caams/venvand installs all dependencies - Requires TLS certificates at
certs/cert.pem+certs/key.pem— prints generation instructions and aborts if missing - Creates a dedicated
caamssystem user and group - Generates a random
CAAMS_SECRET_KEYand writes it to/etc/caams.env(readable only by root and the service account) - Seeds the database if
caams.dbdoes not exist - Writes the systemd unit to
/etc/systemd/system/caams.service, enables, and starts it
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
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
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/:
{
"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:
python seed.py # safe to re-run — existing data is not affected
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
{
"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
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/ ├── 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