Project Index

CERIA Logo
Crypto Evaluation & Readiness for Infrastructure Adaptation (Post-Quantum)

Overview

Post-Quantum Cryptography Scanner implementing Malaysia's NCII SCAD Framework. Author: Yusri (yusri@dysfunktion.tech)
Version: 1.0Last Updated: 2026-07-22

Project Details

Key Features


Last Updated: 2026-07-22

Current Release Highlights


Getting Started

1. First Time Setup (5 minutes)

# Install dependencies
pip install -e .

# Create admin user
ceria --verbose dashboard
# Then access http://localhost:8080/login
# Click "Register" to create first admin user (auto-assigned)

2. Run Your First Scan (5 minutes)

# Option A: CLI
ceria scan 192.168.1.0/24 --name "My First Scan"

# Option B: Web Dashboard
# 1. Open http://localhost:8080
# 2. Login
# 3. Go to "Scan" page
# 4. Enter target: 192.168.1.0/24
# 5. Click "Start Scan"

3. View Results (2 minutes)

# CLI
ceria report --session 1 --format table

# Web
# Go to "Scan Detail" → Expand devices

4. Key Actions to Take

Set Device Priorities

  1. Go to Inventory
  2. Click on high-risk device
  3. Select Tier: Critical / High / Medium / Low
  4. Set Data Retention Years for HNDL calculation

Load Enterprise Demo Data

ceria demo-data --count 500 --snapshots 4 --seed 20260722

This creates a timestamped SQLite backup before loading deterministic data for all supported vendors, device types, collector detections, risk priorities, and HNDL severities.

Export Reports

  1. Open /report.
  2. Select Export to and choose PDF, PQC Excel, CBOM, CSV, or JSON.
  3. Follow backend progress in the on-page status panel.
  4. Open /report-exports to download or delete completed files.

Common Commands Cheat Sheet

TaskCommand
Start dashboardceria dashboard
Scan networkceria scan 192.168.1.0/24
View sessionsceria sessions
Generate demo dataceria demo-data --count 500
Export CSV from CLIceria report --session 1 --format csv --output out.csv

Web Dashboard URLs

PageURL
Dashboard/
Start Scan/scan
Scan Results/scan/{id}
All Devices/inventory
Device Detail/inventory/{id}
Certificates/certificates
Risk Report/report
Generated Reports/report-exports
Settings/settings

Need Help?

See MANUAL.md for complete documentation.


Installation

Requirements

Local Installation

# Clone repository
git clone <repo-url>
cd pqc

# Install with pip
pip install -e .

# Or install development dependencies
pip install -e ".[dev]"

# Verify installation
ceria --version

Environment Setup

Create .env file:

# Required
SECRET_KEY=your-secure-secret-key-here
DATABASE_URL=sqlite:///ceria.db

# Optional
DASHBOARD_HOST=0.0.0.0
DASHBOARD_PORT=8080
NCII_SECTOR=digital

# SSH Credentials (for remote collectors)
SSH_USERNAME=admin
SSH_PASSWORD=your-ssh-password
SSH_KEY_PATH=/path/to/private/key

# Database Scan Credentials
DB_SCAN_USERNAME=db_user
DB_SCAN_PASSWORD=db_password

NCII Sectors

Available sectors: defense, finance, healthcare, telecom, energy, transport, water, waste, gov, broadcast, digital


→ Next: Quick Start

Report Generation Dependencies

Install the project with its declared dependencies to enable all exports. WeasyPrint generates PDFs, while openpyxl populates the official PQC Excel workbook. The application account must be able to write to outputs/reports.


Overview

What is CERIA?

CERIA discovers cryptographic assets across your network infrastructure and prioritizes them for Post-Quantum Cryptography (PQC) migration.

03-dashboard.png Main dashboard showing network-wide PQC readiness

Why PQC Migration?

Quantum computers will break current cryptographic algorithms:

The Threat Timeline

YearMilestone
2024-2029PQC standards finalized (NIST)
2030Malaysia NCII: Symmetric crypto migration deadline
2035Malaysia NCII: Asymmetric crypto migration deadline
2035+Cryptographically relevant quantum computer expected

Key Features

1. Automated Discovery

04-scan-page.png Configure network scans with multiple collectors

2. Risk-Based Prioritization

topic-risk-dashboard.png SCAD framework ranks devices by migration urgency

3. Certificate Management

topic-certificates-list.png Track all certificates with PQC readiness status

4. Compliance Reporting

Generate reports for:

Who Should Use CERIA?

Supported Platforms

PlatformStatus
Web Dashboard✅ Full support
CLI✅ Full support
Linux✅ Primary
macOS✅ Supported
Windows (WSL)✅ Supported
cPanel Hosting✅ Documented

Next: Architecture

Enterprise Dataset and Reporting

CERIA supports demo assessments with up to 500 devices. Inventory and risk tables paginate these datasets, while export jobs run in the backend and remain available from Generated Reports.


Architecture

System Overview

architecture.png CERIA Architecture Diagram

Data Flow

1. Network Discovery

Target Range → nmap scan → Live hosts → Port detection → Device records

2. Cryptographic Collection

Device + Open Ports → Collector selection → Asset scanning → Crypto assets

3. Risk Analysis

Crypto assets → SCAD scoring → HNDL calculation → Priority ranking

4. Reporting

Prioritized assets → Visualization → Export (PDF/CSV/CBOM)

Collector Types

topic-device-assets.png Example: Device showing multiple crypto asset types
CollectorPortProtocolCredential Required
TLS/SSL443HTTPSNo
SSH22SSHOptional
VPN500/4500IKE/IPSecNo
Database5432/3306VariousYes
OS Crypto-SSHYes
App Config-SSHYes
Cert Store-SSHYes

Database Schema

Core Tables

Relationships

ScanSession (1) ──▶ (N) Device (1) ──▶ (N) CryptoAsset
                                    ──▶ (N) Certificate
                                     ──▶ (1) RiskScore

Next: Scanning Features

Report Export Pipeline

/report Export to -> POST /api/v1/reports/exports -> persistent ReportExport job
    -> background worker -> WeasyPrint / openpyxl / CSV / JSON / CycloneDX
    -> unique file in outputs/reports -> download or delete from /report-exports

Queued and processing jobs continue when users navigate away. Startup recovery resumes queued work and safely marks interrupted jobs.


User Manual

A comprehensive guide for the Post-Quantum Cryptography Scanner implementing Malaysia's NCII (National Critical Information Infrastructure) SCAD Framework.


Table of Contents

  1. Overview
  2. Architecture
  3. Installation & Setup
  4. CLI Usage
  5. Web Dashboard
  6. Scanning Features
  7. Cryptographic Collectors
  8. Risk Scoring (SCAD Framework)
  9. HNDL Risk Calculator
  10. Certificate Management
  11. Compliance & Reporting
  12. Migration Planning
  13. API Reference
  14. Troubleshooting

Overview

PQC Scanner discovers cryptographic assets across your network and prioritizes them for Post-Quantum Cryptography (PQC) migration based on the 7-criterion SCAD Framework.

Key Features

Technology Stack


Architecture

Scanner Pipeline:
Network Discovery (nmap) → Port Detection → Collector Execution → Risk Scoring → DB Storage

├── Network Discovery
│   └── nmap host discovery, OS fingerprinting, port scanning
│
├── Cryptographic Collectors
│   ├── TLS/SSL (sslyze + banner fallback)
│   ├── SSH (ssh-audit + nmap scripts)
│   ├── VPN (IKE probes)
│   ├── Database (TDE check)
│   ├── OS Crypto (certificate stores)
│   ├── App Config (configuration files)
│   └── Certificate Store (system stores)
│
├── Risk Analysis
│   ├── SCAD 7-criterion scoring
│   ├── HNDL risk calculation
│   ├── Certificate chain analysis
│   └── Vendor readiness assessment
│
└── Reports & Compliance
    ├── MySEAL compliance checking
    ├── Migration recommendations
    ├── CBOM export (CycloneDX)
    ├── PDF/CSV/JSON reports
    └── G6 dependency graphs

Installation & Setup

Requirements

Installation

# Clone repository
git clone <repo-url>
cd pqc

# Install with pip
pip install -e .

# Or install development dependencies
pip install -e ".[dev]"

# Verify installation
pqcs --version

Configuration

Environment Variables (.env file)

# Required
SECRET_KEY=your-secure-secret-key-here
DATABASE_URL=sqlite:///pqcs.db

# Optional
DASHBOARD_HOST=0.0.0.0
DASHBOARD_PORT=8080
NCII_SECTOR=digital

# SSH Credentials (for remote collectors)
SSH_USERNAME=admin
SSH_PASSWORD=your-ssh-password
SSH_KEY_PATH=/path/to/private/key

# Database Scan Credentials
DB_SCAN_USERNAME=db_user
DB_SCAN_PASSWORD=db_password

NCII Sectors

Available sectors: defense, finance, healthcare, telecom, energy, transport, water, waste, gov, broadcast, digital


CLI Usage

Global Options

pqcs [OPTIONS] COMMAND [ARGS]...

Options:
  --db-url TEXT       Database URL (env: DATABASE_URL)
  --verbose, -v       Enable debug logging
  --version           Show version
  --help              Show help

Commands

1. Scan Network

# Basic scan
ceria scan 192.168.1.0/24

# Named scan
ceria scan 192.168.1.0/24 --name "Production Network Scan"

# Specific collectors only
ceria scan 192.168.1.0/24 --collectors tls,ssh,vpn

# All available collectors
ceria scan 192.168.1.0/24 --collectors tls,ssh,vpn,db,os_crypto,app_config,cert_store
Available Collectors:

2. Network Discovery

# Discover hosts without full crypto scan
ceria discover 192.168.1.0/24 --ports 22,443,8443

3. Generate Reports

# Table format (console)
ceria report --session 1 --format table

# Filter by priority
ceria report --session 1 --priority CRITICAL --format table

# Export to CSV
ceria report --session 1 --format csv --output report.csv

# Export to JSON
ceria report --session 1 --format json --output report.json

# Export to PDF
ceria report --session 1 --format pdf --output report.pdf

# Export CBOM (CycloneDX)
ceria report --session 1 --format cbom --output cbom.json

4. List Sessions

ceria sessions

5. Launch Dashboard

# Default port 8080
ceria dashboard

# Custom port
ceria dashboard --port 3000 --host 127.0.0.1

Web Dashboard

Access

http://localhost:8080 (local)
https://pqcs.dysfunktion.tech (deployed)

Pages

PageURLDescription
Login/loginJWT authentication
Dashboard/Summary, device table, charts, topology
Scan Control/scanConfigure and run network scans
Scan Detail/scan/{id}Full scan results with expandable devices
Inventory/inventoryPaginated devices with filters and sorting
Device Detail/inventory/{id}Device tiering, migration tracking, HNDL
Port Detail/inventory/{device_id}/ports/{port}Port-specific crypto details
Certificates/certificatesCertificate store with PQC assessment
Cert Detail/certificates/{id}Chain analysis, PEM viewer, CA roadmap
HNDL Detail/hndlHarvest Now Decrypt Later risk analysis
Report/reportPaginated risk assessment, visualizations, and Export to menu
Generated Reports/report-exportsExport queue progress, downloads, type tabs, and deletion
Settings/settingsCredentials, ports, scoring weights, scheduling
Profile/profileUser management, password change
Asset Detail/assetIndividual crypto asset deep dive

Enterprise Demo Data

ceria demo-data --count 500 --snapshots 4 --seed 20260722

The command backs up SQLite before creating a deterministic enterprise dataset. It covers all supported vendors, infrastructure archetypes, collector detection types, priority bands, and varied HNDL outcomes. Use --append to retain existing scan-domain data.

Queued Report Exports

See Report Exports for the complete workflow and screenshots.

The /report Export to dropdown queues PDF, PQC Excel, CBOM, CSV, or JSON generation. Progress comes from the backend worker. Completed exports receive timestamped UUID-based filenames.

The /report-exports page lists all jobs with tabs by type. Active jobs show progress; completed jobs can be downloaded or deleted. PDF generation uses WeasyPrint, and PQC Excel generation populates the official workbook while preserving template formatting and reference content.


Scanning Features

Network Discovery

Uses nmap for:

Scanning Modes

1. Quick Scan

2. Deep Scan

3. Credential-based Scan

Requires SSH credentials for:

Port Coverage

Default ports scanned:

SSH: 22, 2222
TLS/HTTPS: 443, 8443, 4443, 993, 995, 990, 636, 3269, 8080, 9090
VPN: 500, 4500, 1194, 1723
Database: 5432, 1433, 3306, 1521, 27017, 6379, 9042
Mail: 143, 993, 110, 995, 25, 465, 587
LDAP/Kerberos: 389, 636, 88, 3268, 3269
RDP/VNC: 3389, 5900, 5901
SMB: 135, 139, 445
DNS: 53
Docker/K8s: 2375, 2376, 6443, 10250, 10255
Monitoring: 9090, 3000, 9100
MQTT: 1883, 8883
Redis/Memcached: 6379, 11211
Elasticsearch: 9200, 9300
RabbitMQ: 5672, 15672
Zookeeper: 2181
Cassandra: 9042, 9160
MongoDB: 27017, 27018

Add custom ports in Settings page (supports ranges like 9000-9010).


Cryptographic Collectors

1. TLS/SSL Collector

Scans for: Tools: sslyze, nmap scripts Output:
{
  "port": 443,
  "tls_version": "TLSv1.2",
  "cipher_suite": "ECDHE_RSA_WITH_AES_256_GCM_SHA384",
  "certificate_info": {...},
  "chain_analysis": {...},
  "confidence": 0.95
}

2. SSH Collector

Scans for: Tools: ssh-audit, paramiko, nmap scripts Confidence Scores:

3. VPN Collector

Scans for: Tools: ike-scan probes

4. Database Collector

Scans for: Tools: SQL queries (requires credentials)

5. OS Crypto Collector

Scans for: Tools: SSH remote commands (requires credentials)

6. App Config Collector

Scans for: Tools: SSH file reading (requires credentials)

7. Certificate Store Collector

Scans for: Tools: SSH directory listing (requires credentials)

Risk Scoring (SCAD Framework)

The SCAD (System Cryptographic Asset Demand) Framework uses 7 criteria with adjustable weights (must sum to 100%):

Criteria Descriptions

CriterionWeightDescription
Data Lifespan20%How long data must remain confidential
Algorithm Vulnerability25%Known vulnerabilities in current algorithms
Key Size15%RSA key size or ECC curve strength
System Criticality15%Importance to business operations
Legacy Status10%End-of-life systems harder to migrate
Third Party Dependency10%External dependencies affecting migration
NCII Sector5%Malaysia NCII sector classification

Priority Levels

Score RangePriorityAction
80-100CRITICALImmediate migration required
60-79HIGHPlan migration within 6 months
40-59MEDIUMPlan migration within 12 months
20-39LOWPlan migration within 24 months
0-19MINIMALTrack for future migration

Adjusting Weights

  1. Go to Settings page
  2. Modify criterion weights (must sum to 100%)
  3. Click Save
  4. Re-run scan to apply new weights

HNDL Risk Calculator

Harvest Now, Decrypt Later (HNDL)

Implements Mosca's Inequality: X + Y > Z

Where:

Risk Levels

X+Y vs ZRisk LevelColor
X+Y >> ZCRITICALRed
X+Y > ZHIGHOrange
X+Y ≈ ZMODERATEYellow
X+Y < ZLOWGreen
X+Y << ZMINIMALBlue

Setting Data Retention

  1. Go to Inventory → Select Device
  2. Click Edit on tiering section
  3. Set Data Retention (Years)
  4. Save changes

HNDL risk auto-recalculates.


Certificate Management

Certificate Discovery

Automatically extracts and stores:

Certificate Analysis

For each certificate:

Chain Analysis

Root CA (PQC Status)
  ↓ trusts
Intermediate CA (PQC Status, Hybrid?)
  ↓ issues
Leaf Certificate (PQC Status, Algorithm)

Chain risk based on weakest link.

CA Roadmap Database

Tracks vendor PQC readiness:

Download Certificates

From certificate detail page:


Compliance & Reporting

MySEAL Compliance

Malaysia's MySEAL (Malaysia Security Evaluation and Assurance Lab) approved algorithms.

Checks: Report includes:

MyKriptografi NCII

Malaysia NCII (National Critical Information Infrastructure) requirements.

PQC Transition Timeline:

Schedule Re-Scans

  1. Go to Settings
  2. Under Automatic Re-Scan:
  1. Save settings

Drift Detection

Compares scan results over time:

Alerts for:


Migration Planning

Migration Advisor

File: pqcs/analysis/migration_advisor.py

Analyzes crypto patterns and provides step-by-step migration plans for:

Patterns Supported

PatternDescription
rsa-2048-tlsTLS using RSA-2048 certificates
rsa-4096-tlsTLS using RSA-4096 certificates
ecdsa-p256-tlsTLS using ECDSA P-256
ssh-rsa-deprecatedSSH using deprecated ssh-rsa
ssh-ecdsaSSH using ECDSA host keys
vpn-ikev2-rsaVPN IKEv2 with RSA authentication
db-tde-rsaDatabase TDE using RSA
java-keystore-rsaJava keystore with RSA certs
openssl-server-rsaOpenSSL server config

Migration Plan Structure

For each pattern:

  1. Current State: What was detected
  2. Target State: PQC-ready replacement
  3. Migration Steps: Numbered procedures
  4. Caveats: Known issues or blockers
  5. Rollback: How to revert if needed
  6. Resources: Official docs and guides

Critical Path Analysis

File: pqcs/analysis/dependency_graph.py

Identifies migration order:

1. Root CAs (highest priority)
2. Intermediate CAs
3. Leaf certificates
4. Device configurations

Dependencies visualized as G6 graph with risk-based coloring.


API Reference

Authentication Endpoints

POST /api/v1/auth/login
Content-Type: application/json

{
  "username": "admin",
  "password": "password"
}

Response:
{
  "status": "ok",
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": 1,
    "username": "admin",
    "role": "admin"
  }
}

Scan Endpoints

# Start scan
POST /api/v1/scans/start
Authorization: Bearer <token>
Content-Type: application/json

{
  "target_range": "192.168.1.0/24",
  "session_name": "Test Scan",
  "collectors": ["tls", "ssh"]
}

# Get scan status
GET /api/v1/scans/{session_id}/status

# Get scan results
GET /api/v1/scans/{session_id}/results

# Get scan log
GET /api/v1/scans/{session_id}/log

Device Endpoints

# List devices
GET /api/v1/devices

# Get device details
GET /api/v1/devices/{device_id}

# Update device tiering
PUT /api/v1/devices/{device_id}/tiering
Content-Type: application/json

{
  "tier": "critical",
  "data_retention_years": 10,
  "criticality": "high"
}

# Get device assets
GET /api/v1/devices/{device_id}/assets

# Get score history
GET /api/v1/devices/{device_id}/score-history

Certificate Endpoints

# List certificates
GET /api/v1/certificates

# Get certificate details
GET /api/v1/certificates/{cert_id}

# Download certificate
GET /api/v1/certificates/{cert_id}/download

# Get certificate chain
GET /api/v1/certificates/{cert_id}/chain

Visualization Endpoints

# Network topology (G6 format)
GET /api/v1/devices/network-topology

# Risk heatmap data
GET /api/v1/devices/risk-heatmap

# Expiry timeline
GET /api/v1/devices/expiry-timeline

# Certificate chain graph
GET /api/v1/certificates/graph

Settings Endpoints

# Get settings
GET /api/v1/settings

# Update settings
PUT /api/v1/settings
Content-Type: application/json

{
  "scan_rate_limit": "10",
  "default_scan_timeout": "300"
}

# Get scoring weights
GET /api/v1/scoring/weights

# Update weights
PUT /api/v1/scoring/weights
Content-Type: application/json

{
  "data_lifespan": "20",
  "algorithm_vulnerability": "25",
  "key_size": "15",
  "system_criticality": "15",
  "legacy_status": "10",
  "third_party_dependency": "10",
  "ncii_sector": "5"
}

Troubleshooting

Common Issues

1. Port Permission Denied (macOS/Linux)

# macOS
sudo ceria dashboard --port 80

# Linux (CAP_NET_BIND_SERVICE)
sudo setcap cap_net_bind_service=+ep $(which python)

2. Nmap Not Found

# Install nmap
# Ubuntu/Debian
sudo apt-get install nmap

# macOS
brew install nmap

# Windows
choco install nmap

3. Database Connection Issues

# Check database permissions
ls -la pqcs.db
chmod 644 pqcs.db

# Or use absolute path
DATABASE_URL=sqlite:////home/user/db/pqcs.db ceria dashboard

4. SSH Connection Failures

5. Certificate Parsing Errors

Some certificates may fail to parse due to:

These are logged but don't stop the scan.

Log Locations

PlatformLog Location
LocalConsole output (use --verbose)
cPanel/home2/yusricom/logs/passenger.log
systemdjournalctl -u pqcs

Debug Mode

# Enable debug logging
pqcs --verbose dashboard

# Or set environment variable
export LOG_LEVEL=DEBUG
ceria dashboard

Security Considerations

Credential Storage

Network Security

Data Protection


Advanced Topics

Custom Collectors

Create new collectors by extending BaseCollector:

from pqcs.scanner.collectors.base import BaseCollector

class MyCollector(BaseCollector):
    asset_type = "myprotocol"

    def collect(self, device: Device) -> list[dict]:
        assets = []
        # Your scanning logic here
        if self._port_is_open(device, 1234):
            assets.append({
                "port": 1234,
                "asset_type": "myprotocol",
                "protocol_version": "1.0",
                "algorithm": "RSA-2048",
            })
        return assets

Custom Scoring

Modify criteria.py to add new algorithm checks.

API Integration

Use the REST API to integrate with external systems:

import requests

# Login
r = requests.post("https://pqcs/api/v1/auth/login", json={
    "username": "admin",
    "password": "password"
})
token = r.json()["token"]

# Start scan
requests.post("/api/v1/scans/start",
    headers={"Authorization": f"Bearer {token}"},
    json={"target_range": "10.0.0.0/24"}
)

Development

Running Tests

pytest tests/
pytest tests/ -v  # verbose
pytest tests/test_scoring.py  # specific test

Code Style

# Lint
ruff check pqcs/

# Format
ruff format pqcs/

# Type check
mypy pqcs/

Project Structure

pqcs/
├── __init__.py
├── __main__.py          # Entry point
├── cli.py               # CLI commands
├── config.py            # Configuration
├── db.py                # Database models
├── web/                 # Web dashboard
│   ├── app.py           # FastAPI app
│   ├── routes.py        # API routes
│   ├── middleware.py    # Auth & rate limiting
│   └── templates/       # HTML templates
├── scanner/             # Scanning engine
│   ├── engine.py        # Orchestrator
│   ├── network_discovery.py
│   ├── runner.py        # Web scan runner
│   └── collectors/      # Crypto collectors
├── scoring/             # Risk scoring
│   ├── engine.py
│   ├── criteria.py      # Algorithm DB
│   └── hndl_calculator.py
├── analysis/            # Analysis tools
│   ├── migration_advisor.py
│   └── dependency_graph.py
├── compliance/          # Compliance checking
│   └── myseal.py
└── reporting/           # Export formats
    ├── csv_export.py
    ├── json_export.py
    ├── pdf_export.py
    └── template_export.py

License

MIT License - See LICENSE file


Support

For issues, feature requests, or questions:


Version: 0.1.0 Last Updated: 2026-07-22

Web Dashboard

Access

http://localhost:8080 (local)
https://pqcs.dysfunktion.tech (deployed)

Pages

Public Pages

PageURLDescriptionScreenshot
Login/loginJWT authentication01-login.png

Protected Pages

PageURLDescription
Dashboard/Summary, device table, charts, topology
Scan Control/scanConfigure and run network scans
Scan Detail/scan/{id}Full scan results with expandable devices
Inventory/inventoryAll devices with filters and sorting
Device Detail/inventory/{id}Device tiering, migration tracking, HNDL
Certificates/certificatesCertificate store with PQC assessment
HNDL Detail/hndlHarvest Now Decrypt Later risk analysis
Report/reportVisualization: heatmap, timeline, expiry charts
Settings/settingsCredentials, ports, scoring weights, scheduling
Profile/profileUser management, password change

Dashboard Overview

The main dashboard provides a comprehensive view of your network's PQC readiness:

03-dashboard.png Main dashboard with device summary, risk distribution, and PQC readiness gauge

Scan Configuration

Configure and start network scans from the Scan Control page:

04-scan-page.png Scan configuration - select collectors, set target range, and start scanning

Device Management

View all discovered devices in the Inventory:

05-inventory.png Device inventory with filtering, sorting, and risk scores

Device Detail View

Deep dive into individual device analysis:

topic-hndl-device.png Device detail showing HNDL risk, crypto assets, and score breakdown topic-device-assets.png Individual cryptographic assets per device

Certificate Management

Browse discovered certificates with PQC status:

topic-certificates-list.png Certificate store with PQC assessment and expiry tracking

Reports & Visualization

View risk heatmaps and compliance reports:

08-report.png Risk heatmap, certificate expiry timeline, and PQC readiness charts topic-reports.png Detailed analytics and reporting visualizations

HNDL Risk Analysis

Access Mosca's Inequality calculator:

07-hndl.png Harvest Now Decrypt Later risk analysis dashboard

Settings & Configuration

Configure scanning parameters and scoring weights:

06-settings.png Application settings with SSH credentials, custom ports, and auto-scan scheduling topic-settings-weights.png SCAD risk scoring weights configuration

Risk Priority Dashboard

Monitor critical devices requiring immediate attention:

topic-risk-dashboard.png Risk priority cards highlighting CRITICAL and HIGH priority devices

Navigation

Use the sidebar menu to navigate between pages. The dashboard shows:


Screenshots

See detailed screenshots in Screenshots Gallery

Quick preview:

Large Inventory Navigation

Device Inventory and Device Risk Assessment paginate large device collections. Use the previous/next controls and page-size selector instead of loading every device into one table.

Exporting Reports

See complete export guide for formats, queue behavior, downloads, deletion, and troubleshooting.

On /report, select Export to and choose PDF, PQC Excel, CBOM, CSV, or JSON. CERIA queues the job and displays backend progress. Completion exposes a direct download link and success message.

Open Generated Reports at /report-exports to view all jobs. Tabs filter by type, active jobs show progress, and completed or failed jobs can be deleted.

09-report-export-menu.png 10-generated-reports.png

Report Exports

Overview

CERIA generates reports through a persistent backend queue. Export requests continue if you leave the page, and completed files remain available from Generated Reports until they are deleted.

Open the Export Menu

  1. Open /report.
  2. Select Export to in the report header.
  3. Choose the required format.
09-report-export-menu.png

The report page immediately queues the export and shows live backend progress. A successful job displays its unique filename and a download action.

Export Formats

FormatContentsRecommended use
PDFVisual executive assessment generated with WeasyPrintManagement review and formal reporting
PQC ExcelOfficial workbook template populated with project, inventory, discovery, and risk dataStructured assessment and migration planning
CBOMCycloneDX cryptographic bill of materialsTool integration and machine-readable inventory
CSVDevice risk registerSpreadsheet analysis and filtering
JSONDetailed devices, scores, assets, and certificate dataAPI integration and complete data exchange

Generated Reports

Open /report-exports or select Generated Reports from the report page.

10-generated-reports.png

The page provides:

Active jobs cannot be deleted. CERIA returns a conflict response until generation completes or fails.

Job Lifecycle

Queued -> Processing -> Completed
                    -> Failed

Job metadata is stored in the database. Files are written safely through a temporary .part file and moved into place only when complete. On startup, CERIA recovers queued jobs and reconciles interrupted work.

API Workflow

Queue an export:

POST /api/v1/reports/exports
Content-Type: application/json

{"export_type": "pdf"}

Monitor and manage it:

GET /api/v1/reports/exports
GET /api/v1/reports/exports/{job_id}
GET /api/v1/reports/exports/{job_id}/download
DELETE /api/v1/reports/exports/{job_id}

PDF Export

PDF generation uses WeasyPrint and a dedicated print layout. The report contains the current assessment details, visual summaries, risk information, migration context, and device-level findings without requiring browser rendering.

PQC Excel Export

PQC Excel generation uses docs/PQC Template.xlsx and preserves the template structure and formatting. The operational sheets are populated with:

  1. Project and assessment information.
  2. Enterprise asset inventory.
  3. Cryptographic discovery results.
  4. Device risk assessment data.

Troubleshooting


CLI Usage

Global Options

ceria [OPTIONS] COMMAND [ARGS]...

Options:
  --db-url TEXT       Database URL (env: DATABASE_URL)
  --verbose, -v       Enable debug logging
  --version           Show version
  --help              Show help

Commands

topic-cli-dashboard.png CLI dashboard view when running ceria dashboard

1. Scan Network

# Basic scan
ceria scan 192.168.1.0/24

# Named scan
ceria scan 192.168.1.0/24 --name "Production Network Scan"

# Specific collectors only
ceria scan 192.168.1.0/24 --collectors tls,ssh,vpn

# All available collectors
ceria scan 192.168.1.0/24 --collectors tls,ssh,vpn,db,os_crypto,app_config,cert_store
Available Collectors:

Sample scan output in terminal:

[2026-07-22 10:00:01] INFO: Starting scan: My First Scan
[2026-07-22 10:00:05] INFO: Port scan: Found 24 hosts
[2026-07-22 10:00:12] INFO: TLS collector: 15 certificates collected
[2026-07-22 10:00:18] INFO: SSH collector: 8 SSH servers found
[2026-07-22 10:00:25] INFO: Cert collector: 22 certificates discovered
[2026-07-22 10:00:30] INFO: Scan completed in 29.4s

2. Network Discovery

# Discover hosts without full crypto scan
ceria discover 192.168.1.0/24 --ports 22,443,8443

3. Generate Reports

# Table format (console)
ceria report --session 1 --format table

# Filter by priority
ceria report --session 1 --priority CRITICAL --format table

# Export to CSV
ceria report --session 1 --format csv --output report.csv

# Export to JSON
ceria report --session 1 --format json --output report.json

# Export to PDF
ceria report --session 1 --format pdf --output report.pdf

# Export CBOM (CycloneDX)
ceria report --session 1 --format cbom --output cbom.json

Sample table output:

IP Address    | Hostname    | Priority  | Algorithms
--------------+-------------+-----------+------------------
192.168.1.10  | web-server  | CRITICAL  | RSA-1024 
192.168.1.20  | mail-server | HIGH      | RSA-2048, SHA-1
192.168.1.30  | db-server   | MEDIUM    | RSA-2048

4. List Sessions

ceria sessions

Output:

ID  | Name                    | Status     | Devices | Started
----+-------------------------+------------+---------+-------------------
1   | My First Scan           | completed  | 24      | 2026-07-22 10:00
2   | Production Network Scan | running    | 12      | 2026-07-22 11:30

5. Launch Dashboard

# Default port 8080
ceria dashboard

# Custom port
ceria dashboard --port 3000 --host 127.0.0.1

Opens web interface at http://localhost:8080 (or your specified port).

Enterprise Demo Data

ceria demo-data --count 500 --snapshots 4 --seed 20260722

This creates a timestamped SQLite backup before replacing scan-domain records. Use --append to retain scan data, --backup-dir to choose the backup location, and --seed to reproduce a dataset.


Scanning Features

Network Discovery

CERIA uses nmap for initial host discovery:

Discovery Methods

Port Scanning

Default ports include:

Custom Ports

Add custom ports in Settings:

Format: ports or ranges
Example: 9000,9001,9002-9010,10000

06-settings.png Configure custom ports in Settings

Scanning Modes

Quick Scan

Deep Scan

Credential-Based Scan

Requires SSH credentials for: topic-scan-results.png Scan results showing discovered devices

Scan Scheduling

Automated Re-scans

  1. Go to Settings
  2. Enable Automatic Re-scan
  3. Select frequency:

Drift Detection

Compares scan results over time:

Alerts for significant changes.

Scan Performance

Network Size Recommendations

Network SizeTimeoutConcurrent Hosts
/24 (254)5 min50
/23 (510)10 min50
/22 (1,022)20 min50
/16 (65k)60 min25

Tune via Settings page.


See also: Cryptographic Collectors

Demonstration Coverage

The generator creates network infrastructure, security appliances, servers, endpoints, cloud services, databases, operational technology, storage, identity, and communications devices. Findings cover all supported vendors and collector detection types, including vulnerable, transitional, and PQC-ready algorithms.


Risk Scoring

The SCAD (System Cryptographic Asset Demand) Framework uses 7 criteria with adjustable weights (must sum to 100%).

Dashboard View

topic-risk-dashboard.png Risk priority distribution on main dashboard

Configuring Weights

topic-settings-weights.png Adjust SCAD framework weights in Settings page

Criteria Descriptions

CriterionWeightDescription
Algorithm Vulnerability25%Known vulnerabilities in current algorithms
Data Lifespan20%How long data must remain confidential
Key Size15%RSA key size or ECC curve strength
System Criticality15%Importance to business operations
Legacy Status10%End-of-life systems harder to migrate
Third Party Dependency10%External dependencies affecting migration
NCII Sector5%Malaysia NCII sector classification

Priority Levels

Score RangePriorityAction
80-100CRITICALImmediate migration required
60-79HIGHPlan migration within 6 months
40-59MEDIUMPlan migration within 12 months
20-39LOWPlan migration within 24 months
0-19MINIMALTrack for future migration

Adjusting Weights

  1. Go to Settings page
  2. Modify criterion weights (must sum to 100%)
  3. Click Save
  4. Re-run scan to apply new weights

Industry-Specific Configurations

Financial Services

Algorithm Vulnerability: 30%
Data Lifespan: 25%
System Criticality: 20%
Key Size: 10%
Legacy Status: 5%
Third Party: 5%
NCII Sector: 5%

Healthcare

Data Lifespan: 35%
Algorithm Vulnerability: 25%
System Criticality: 15%
Key Size: 10%
Legacy Status: 5%
Third Party: 5%
NCII Sector: 5%

See Screenshots Gallery for more views.

Demo Risk Distribution

Demo datasets distribute SCAD scores and priorities across the supported range. HNDL values come from varied retention, migration, and quantum-timeline inputs, so devices do not receive identical severity scores.


HNDL Calculator

HNDL Overview Dashboard

07-hndl.png Harvest Now Decrypt Later summary on dashboard

Mosca's Inequality

Implements Mosca's Inequality: X + Y > Z

Where:

Device-Level HNDL Analysis

topic-hndl-device.png Mosca's Inequality calculation per device with X+Y vs Z breakdown

Risk Levels

X+Y vs ZRisk LevelRecommended Action
X+Y >> ZCRITICALImmediate migration
X+Y > ZHIGHBegin migration now
X+Y ≈ ZMODERATEPlan within 6 months
X+Y < ZLOWPlan within 12 months
X+Y << ZMINIMALTrack and monitor

Setting Data Retention

  1. Go to Inventory → Select Device
  2. Click Edit on tiering section
  3. Set Data Retention (Years)
  4. Save changes

HNDL risk auto-recalculates when you update the value.

Examples

Critical Risk Example

Data Retention: 15 years (medical records)
Migration Time: 2 years
Quantum Timeline: 10 years (2035 estimate)

X + Y = 17 > Z = 10
Result: CRITICAL - Data needs protection beyond quantum capability

Low Risk Example

Data Retention: 1 year (temporary cache)
Migration Time: 2 years
Quantum Timeline: 10 years

X + Y = 3 < Z = 10
Result: MINIMAL - Data expires before quantum threat

See Screenshots Gallery for more visuals.

HNDL in Demo Assessments

Seeded datasets include CRITICAL, HIGH, MODERATE, LOW, and MINIMAL HNDL outcomes. Each device retains its own input values and calculated years until exposure.


Certificate Management

Certificate Discovery

Automatically extracts and stores X.509 certificates from:

topic-certificates-list.png Certificate inventory with PQC status

Certificate Analysis

For each certificate, CERIA analyzes:

Basic Properties

Algorithm Assessment

AlgorithmPQC Status
RSA-2048+⚠️ Vulnerable
ECDSA P-256⚠️ Vulnerable
ECDSA P-384⚠️ Vulnerable
Ed25519✅ PQC Ready
Ed448✅ PQC Ready
ML-DSA✅ PQC Ready
ML-KEM✅ PQC Ready

Signature Analysis

Chain Analysis

Root CA (ISRG Root X1)
  └── Intermediate (R3)
       └── Leaf (example.com)

Each level assessed for:

06-settings.png Certificate chain detail view

CA Roadmap Database

Tracks vendor PQC transition plans:

CA VendorCurrent StatusPQC ETA
DigiCertTransitioning2025
Let's EncryptTransitioning2025
SectigoTransitioning2025
GlobalSignPlanning2026
AWS ACMReadyAvailable
Azure KVReadyAvailable

Certificate Actions

Download

Expiry Alerts

Automatic warnings for certificates expiring within:

PQC Assessment Report

Generates report showing:
See Screenshots Gallery for certificate views

Compliance

MySEAL Compliance

MySEAL (Malaysia Security Evaluation and Assurance Lab) approved algorithms for government and critical infrastructure.

MySEAL Approved Algorithms

Encryption

Digital Signatures

Key Exchange

MySEAL Compliance Check

CERIA checks devices against MySEAL approved algorithms and flags non-compliant crypto.

MyKriptografi NCII Framework

Malaysia's National Critical Information Infrastructure (NCII) requirements for Post-Quantum Cryptography.

PQC Transition Timeline

PhaseDeadlineRequirements
Phase 12030Symmetric crypto migration
Phase 22035Asymmetric crypto migration

NCII Sectors

Priority weighting based on sector:

SectorWeight
DefenseCritical
FinanceCritical
HealthcareCritical
TelecomHigh
EnergyHigh
GovernmentHigh
Digital ServicesMedium

Compliance Report

Generated report includes:

topic-reports.png Compliance visualization in Reports

NACSA Alignment

National Cyber Security Agency (NACSA) guidelines:

  1. Crypto Inventory ✅ - Automated discovery
  2. Risk Assessment ✅ - SCAD scoring
  3. Migration Plan ✅ - Priority-based roadmap
  4. Progress Tracking ✅ - Scheduled re-scans

Export Formats

CBOM (CycloneDX)

Cryptographic Bill of Materials in standard format for:

NACSA XML (Future)

Direct NACSA reporting format support planned.
See also: Risk Scoring for compliance weighting

PQC Excel Assessment Export

The PQC Excel export populates the official workbook without replacing template formatting or references. Its first four operational sheets cover project details, asset inventory, cryptographic discovery, and risk assessment.


Migration Planning

Migration Advisor

CERIA analyzes crypto patterns and generates step-by-step migration plans.

Detected Patterns

Common cryptographic configurations requiring migration:

PatternCurrent StateTarget State
rsa-2048-tlsRSA-2048 TLSML-KEM/ML-DSA or X25519/Ed25519
rsa-4096-tlsRSA-4096 TLSPQC algorithms
ecdsa-p256-tlsECDSA P-256ML-DSA-65 or Ed25519
ssh-rsa-deprecatedssh-rsarsa-sha2-512 or Ed25519
ssh-ecdsaECDSA host keysEd25519 host keys
vpn-ikev2-rsaIKEv2 with RSAIKEv2 with ECDSA or PQC
topic-device-assets.png Device showing detected crypto patterns

Migration Plan Structure

Each migration plan includes:

1. Current State

2. Target State

3. Migration Steps

Numbered procedures:
1. Generate new key pair
2. Create CSR with new algorithm
3. Obtain PQC-ready certificate
4. Configure service to use new cert
5. Test connectivity
6. Schedule cutover

4. Caveats & Risks

Known issues:

5. Rollback Procedure

How to revert if issues occur.

6. Resources

Dependency Graph

Visualize migration dependencies:

Root CA must migrate first
  → Intermediate CAs
    → Leaf certificates
      → Device configurations
topic-reports.png Migration critical path visualization

Critical Path Analysis

Identifies the order in which to migrate:

  1. Root CAs (blocks everything)
  2. Intermediate CAs (blocks leaf certs)
  3. High-risk devices (SCAD priority)
  4. Remaining devices (by tier)

Migration Tracking

Per-device migration status:

StatusMeaning
Not StartedNo action taken
In PlanningMigration designed
TestingIn test environment
ScheduledProduction date set
CompleteMigrated to PQC
BlockedDependency pending

Update status in device detail page.

Migration Timeline

Recommended timeline based on HNDL risk:

HNDL RiskMigration Window
CRITICALImmediate - 3 months
HIGH3 - 6 months
MEDIUM6 - 12 months
LOW12 - 24 months
MINIMALTrack only

See HNDL Calculator for risk assessment

Exporting Migration Evidence

Use PDF for the executive assessment, PQC Excel for the migration workbook, CBOM for machine-readable cryptographic inventory, CSV for risk-register analysis, and JSON for complete device and asset details.


API Reference

Authentication

All API endpoints require a JWT token except for /api/v1/auth/login and /api/v1/auth/register.

Login

POST /api/v1/auth/login
Content-Type: application/json

{
  "username": "string",
  "password": "string"
}
Response:
{
  "status": "ok",
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": 1,
    "username": "admin",
    "role": "admin"
  }
}
Error Responses:

Get Current User

GET /api/v1/auth/me
Authorization: Bearer <token>

Scans

Start New Scan

POST /api/v1/scans/start
Authorization: Bearer <token>
Content-Type: application/json

{
  "target_range": "192.168.1.0/24",
  "session_name": "Production Scan",
  "collectors": ["tls", "ssh", "vpn"]
}
Parameters:
FieldTypeRequiredDescription
target_rangestringYesNetwork CIDR or IP
session_namestringNoScan name
collectorsarrayNo[tls, ssh, vpn, db, os_crypto, app_config, cert_store]
Response:
{
  "status": "ok",
  "session_id": 1
}

Get Scan Status

GET /api/v1/scans/{session_id}/status
Authorization: Bearer <token>
Response:
{
  "session_id": 1,
  "status": "running",
  "progress": {
    "phase": "collecting",
    "current_device": "5 of 10",
    "collector": "tls"
  },
  "log": [
    {"time": "10:00:01", "level": "info", "message": "Starting scan..."},
    {"time": "10:00:15", "level": "info", "message": "Found 10 hosts"}
  ]
}

Stop Scan

POST /api/v1/scans/{session_id}/stop
Authorization: Bearer <token>

Get Scan Results

GET /api/v1/scans/{session_id}/results
Authorization: Bearer <token>

List All Scan Sessions

GET /api/v1/scans
Authorization: Bearer <token>

Devices

List All Devices

GET /api/v1/devices
Authorization: Bearer <token>
Query Parameters: Response:
{
  "items": [
    {
      "id": 1,
      "ip_address": "192.168.1.10",
      "hostname": "server01",
      "os_fingerprint": "Linux 5.4",
      "tier": "critical",
      "open_ports": [{"port": 22, "service": "ssh"}, {"port": 443, "service": "https"}],
      "risk_score": {
        "overall": 85.5,
        "priority_label": "CRITICAL"
      },
      "hndl": {
        "risk_level": "HIGH",
        "years_until_exposure": 3.5
      }
    }
  ],
  "total": 42,
  "page": 1,
  "page_size": 50
}

Get Device Details

GET /api/v1/devices/{device_id}
Authorization: Bearer <token>

Update Device Tiering

PUT /api/v1/devices/{device_id}/tiering
Authorization: Bearer <token>
Content-Type: application/json

{
  "tier": "critical",
  "data_retention_years": 10,
  "system_criticality": "high",
  "migration_notes": "Business critical payment gateway"
}
Fields:
FieldTypeDescription
tierenumcritical, high, medium, low
data_retention_yearsintYears data must remain confidential
system_criticalityenumhigh, medium, low
legacy_statusenumeol_soon, maintained, modern
migration_notesstringFree text notes

Get Device Assets

GET /api/v1/devices/{device_id}/assets
Authorization: Bearer <token>

Get Device Score History

GET /api/v1/devices/{device_id}/score-history
Authorization: Bearer <token>

Certificates

List Certificates

GET /api/v1/certificates
Authorization: Bearer <token>
Query Parameters:

Get Certificate Details

GET /api/v1/certificates/{cert_id}
Authorization: Bearer <token>
Response:
{
  "id": 1,
  "subject": "CN=example.com",
  "issuer": "CN=Let's Encrypt R3",
  "serial_number": "00:01:02:...",
  "valid_from": "2024-01-01T00:00:00",
  "valid_until": "2024-12-31T23:59:59",
  "days_remaining": 180,
  "algorithm": "ECDSA P-256",
  "signature_hash": "SHA256",
  "pqc_status": "vulnerable",
  "is_hybrid": false,
  "has_weak_signature": false,
  "chain_analysis": {
    "root_ca": "ISRG Root X1",
    "root_pqc_status": "transitioning",
    "intermediate_ca": "R3",
    "intermediate_pqc_status": "transitioning",
    "chain_trust": "trusted",
    "chain_risk_score": 25
  },
  "pem_data": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWg..."
}

Download Certificate

GET /api/v1/certificates/{cert_id}/download
Authorization: Bearer <token>
Response: PEM-encoded certificate file

Get Certificate Chain

GET /api/v1/certificates/{cert_id}/chain
Authorization: Bearer <token>

Risk Scoring

Get Scoring Weights

GET /api/v1/scoring/weights
Authorization: Bearer <token>
Response:
{
  "data_lifespan": 20,
  "algorithm_vulnerability": 25,
  "key_size": 15,
  "system_criticality": 15,
  "legacy_status": 10,
  "third_party_dependency": 10,
  "ncii_sector": 5,
  "total": 100
}

Update Scoring Weights

PUT /api/v1/scoring/weights
Authorization: Bearer <token>
Content-Type: application/json

{
  "data_lifespan": 25,
  "algorithm_vulnerability": 20,
  "key_size": 15,
  "system_criticality": 15,
  "legacy_status": 10,
  "third_party_dependency": 10,
  "ncii_sector": 5
}
Note: Weights must sum to exactly 100.

HNDL (Harvest Now Decrypt Later)

Get HNDL Risk Calculation

POST /api/v1/hndl/calculate
Authorization: Bearer <token>
Content-Type: application/json

{
  "data_retention_years": 10,
  "migration_years": 2
}
Response:
{
  "hndl_risk_level": "HIGH",
  "years_until_exposure": 12,
  "risk_factors": {
    "data_retention": 10,
    "migration_time": 2,
    "quantum_timeline": 10,
    "mosca_inequality": "X + Y > Z (CRITICAL)"
  },
  "recommendations": [
    "Immediate migration recommended",
    "Current algorithms will be broken before data expires"
  ]
}

Compliance

MySEAL Compliance Check

GET /api/v1/compliance/myseal
Authorization: Bearer <token>

MyKriptografi NCII Report

GET /api/v1/compliance/ncii-report
Authorization: Bearer <token>

Migration

Get Migration Recommendation

GET /api/v1/devices/{device_id}/migration
Authorization: Bearer <token>
Response:
{
  "detected_patterns": ["rsa-2048-tls", "ssh-rsa-deprecated"],
  "recommendations": [
    {
      "pattern": "rsa-2048-tls",
      "current_state": "RSA-2048 for TLS authentication",
      "target_state": "ML-KEM/ML-DSA or X25519/Ed25519",
      "migration_steps": [
        "1. Generate new PQC key pair",
        "2. Install hybrid certificate",
        "3. Update TLS configuration"
      ],
      "caveats": ["Hybrid certificates may increase handshake size"],
      "resources": ["https://csrc.nist.gov/pqc-standardization"]
    }
  ]
}

Analytics & Visualization

Network Topology

GET /api/v1/analytics/topology
Authorization: Bearer <token>
Response: G6 graph format
{
  "nodes": [
    {"id": "device_1", "type": "device", "label": "192.168.1.10", "risk": 85},
    {"id": "cert_1", "type": "certificate", "label": "CN=example.com"}
  ],
  "edges": [
    {"source": "device_1", "target": "cert_1", "type": "uses"}
  ]
}

Risk Heatmap Data

GET /api/v1/analytics/risk-heatmap
Authorization: Bearer <token>

Certificate Expiry Timeline

GET /api/v1/analytics/expiry-timeline
Authorization: Bearer <token>

PQC Readiness Gauge

GET /api/v1/analytics/pqc-readiness
Authorization: Bearer <token>
{
  "total_devices": 100,
  "ready": 15,
  "transitioning": 35,
  "vulnerable": 50,
  "percentage_ready": 15
}

Settings

Get All Settings

GET /api/v1/settings
Authorization: Bearer <token>

Update Setting

PUT /api/v1/settings/{key}
Authorization: Bearer <token>
Content-Type: application/json

{
  "value": "new_value"
}

Common Settings

KeyDescriptionDefault
scan_rate_limitMax concurrent scans per minute5
default_scan_timeoutScan timeout in seconds300
nmap_argsDefault nmap arguments-sV -O -sS
ssh_usernameSSH username for collectors""
target_rangeDefault scan target192.168.1.0/24

Export

Exports are asynchronous. Queue a job, poll its status, then use its download_url when it reaches completed.

Export types: pdf, pqc_excel, cbom, csv, json.

List Export Types

GET /api/v1/reports/exports/types
Authorization: Bearer <token>

Queue Export

POST /api/v1/reports/exports
Authorization: Bearer <token>
Content-Type: application/json

{"export_type": "pdf"}

Returns 202 Accepted with the persistent job record.

List and Filter Exports

GET /api/v1/reports/exports
GET /api/v1/reports/exports?export_type=pqc_excel
Authorization: Bearer <token>

Read Export Status

GET /api/v1/reports/exports/{job_id}
Authorization: Bearer <token>

Statuses are queued, processing, completed, and failed. Responses include progress, unique filename, timestamps, errors, and a download URL when ready.

Download Export

GET /api/v1/reports/exports/{job_id}/download
Authorization: Bearer <token>

Delete Export

DELETE /api/v1/reports/exports/{job_id}
Authorization: Bearer <token>

Queued or processing jobs return 409 Conflict and cannot be deleted.


User Management

Register User (Admin only)

POST /api/v1/auth/register
Authorization: Bearer <token>
Content-Type: application/json

{
  "username": "newuser",
  "password": "securepassword",
  "role": "user"
}

List Users (Admin only)

GET /api/v1/auth/users
Authorization: Bearer <token>

Update User (Admin only)

PUT /api/v1/auth/users/{user_id}
Authorization: Bearer <token>
Content-Type: application/json

{
  "role": "admin",
  "is_active": true
}

Delete User (Admin only)

DELETE /api/v1/auth/users/{user_id}
Authorization: Bearer <token>

Change Password

POST /api/v1/auth/change-password
Authorization: Bearer <token>
Content-Type: application/json

{
  "current_password": "oldpass",
  "new_password": "newpass"
}

Error Codes

CodeMeaningDescription
400Bad RequestInvalid JSON or parameters
401UnauthorizedInvalid or missing token
403ForbiddenInsufficient permissions
404Not FoundResource doesn't exist
409ConflictResource already exists
422Unprocessable EntityValidation error
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer error

Python Example

import requests

BASE_URL = "https://pqcs.dysfunktion.tech/api/v1"

# Login
resp = requests.post(f"{BASE_URL}/auth/login", json={
    "username": "admin",
    "password": "password"
})
token = resp.json()["token"]
headers = {"Authorization": f"Bearer {token}"}

# Start scan
resp = requests.post(
    f"{BASE_URL}/scans/start",
    headers=headers,
    json={"target_range": "10.0.0.0/24"}
)
session_id = resp.json()["session_id"]

# Wait for scan to complete
import time
while True:
    status = requests.get(
        f"{BASE_URL}/scans/{session_id}/status",
        headers=headers
    ).json()
    if status["status"] == "completed":
        break
    time.sleep(5)

# Get results
results = requests.get(
    f"{BASE_URL}/scans/{session_id}/results",
    headers=headers
).json()

# Export PDF
pdf = requests.get(
    f"{BASE_URL}/export/{session_id}?format=pdf",
    headers=headers
)
with open("report.pdf", "wb") as f:
    f.write(pdf.content)

cURL Examples

Full Scan Workflow

# 1. Login
TOKEN=$(curl -s -X POST https://pqcs/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"pass"}' | jq -r '.token')

# 2. Start scan
curl -X POST https://pqcs/api/v1/scans/start \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"target_range":"192.168.1.0/24"}'

# 3. Get devices
curl https://pqcs/api/v1/devices \
  -H "Authorization: Bearer $TOKEN" | jq

# 4. Export CSV
curl "https://pqcs/api/v1/export/1?format=csv" \
  -H "Authorization: Bearer $TOKEN" \
  -o report.csv

Last Updated: 2026-07-22


Administration Guide

Guide for administrators managing PQC Scanner in production environments.


Table of Contents

  1. User Management
  2. Scan Management
  3. Database Administration
  4. Configuration Management
  5. Risk Scoring Configuration
  6. Monitoring & Alerts
  7. Maintenance Tasks
  8. Backup & Recovery
  9. Troubleshooting

User Management

Creating Users

Via Web Interface

  1. Login as admin
  2. Go to Profile page (/profile)
  3. Click User Management
  4. Click Add User
  5. Fill in:
  1. Save

Via CLI

# Interactive
pqcs user create

# Or programmatic (custom script)
python << 'EOF'
import bcrypt
from pqcs.db import init_db, User
from pqcs.config import AppConfig

config = AppConfig()
db = init_db(config.database.url)

pw_hash = bcrypt.hashpw(b"newpassword", bcrypt.gensalt())
user = User(
    username="newuser",
    password_hash=pw_hash.decode(),
    role="user",
    is_active=True
)
db.add(user)
db.commit()
print(f"Created user: {user.username}")
EOF

User Roles

RolePermissions
adminFull access: scans, reports, settings, user management
userCan run scans, view reports, modify device data
viewerRead-only: view scans, reports, cannot modify data

Disabling Users

  1. Go to ProfileUser Management
  2. Find user
  3. Toggle Active switch to OFF
  4. User can no longer login

Password Reset

# Via CLI
python << 'EOF'
import bcrypt
from pqcs.db import init_db, User
from pqcs.config import AppConfig

config = AppConfig()
db = init_db(config.database.url)

user = db.query(User).filter_by(username="user_to_reset").first()
if user:
    new_pw = bcrypt.hashpw(b"temppassword123", bcrypt.gensalt())
    user.password_hash = new_pw.decode()
    db.commit()
    print("Password reset to: temppassword123")
    print("User must change on next login")
EOF

Scan Management

Scheduling Scans

Via Web Settings

  1. Go to Settings page
  2. Scroll to Automatic Re-scan
  3. Enable scheduled scans
  4. Select frequency:
  1. Set scan time (24h format)
  2. Save settings

Via CLI

from pqcs.scheduler.scan_scheduler import ScanScheduler
from pqcs.config import AppConfig

config = AppConfig()
scheduler = ScanScheduler(config)

# Schedule daily scan
job = scheduler.schedule_scan(
    target_range="192.168.1.0/24",
    schedule_type="daily",
    name="Daily Network Scan"
)

# List scheduled jobs
jobs = scheduler.list_scheduled_scans()
for job in jobs:
    print(f"{job['id']}: {job['name']} - {job['schedule']}")

# Cancel schedule
scheduler.cancel_schedule(job_id=1)

Managing Running Scans

Check Scan Status

# Via CLI
ceria sessions

# Get specific scan status
curl http://localhost:8080/api/v1/scans/1/status \
  -H "Authorization: Bearer <token>"

Stop Running Scan

  1. Go to Scan Detail page
  2. Click Stop Scan button
  3. Or via API:
curl -X POST http://localhost:8080/api/v1/scans/1/stop \
  -H "Authorization: Bearer <token>"

Delete Old Scans

# Via CLI script
python << 'EOF'
from pqcs.db import init_db, ScanSession
from pqcs.config import AppConfig
from datetime import datetime, timedelta

config = AppConfig()
db = init_db(config.database.url)

# Delete scans older than 90 days
cutoff = datetime.utcnow() - timedelta(days=90)
old_scans = db.query(ScanSession).filter(ScanSession.finished_at < cutoff).all()

for scan in old_scans:
    print(f"Deleting scan {scan.id}: {scan.name}")
    db.delete(scan)

db.commit()
print(f"Deleted {len(old_scans)} old scans")
EOF

Scan Optimization

Custom Ports

  1. Go to Settings
  2. Edit Custom Ports
  3. Add ports (comma-separated or ranges):
  4.    9000,9001,9002-9010,10000
       
  5. Save

Scan Timeouts

Network SizeRecommended Timeout
/24 (254 hosts)300 seconds
/23 (510 hosts)600 seconds
/22 (1022 hosts)1200 seconds
/16 (65534 hosts)3600 seconds

Database Administration

Database Schema

Key tables:

Direct Database Access

# SQLite CLI
sqlite3 /var/lib/pqcs/pqcs.db

# Common queries
.tables
.schema devices
SELECT COUNT(*) FROM devices;
SELECT * FROM scan_sessions ORDER BY started_at DESC LIMIT 5;
.quit

Database Maintenance

Vacuum (SQLite)

# Reclaim space after deletions
sqlite3 /var/lib/pqcs/pqcs.db "VACUUM;"

Analyze (performance)

# Update query planner statistics
sqlite3 /var/lib/pqcs/pqcs.db "ANALYZE;"

Check Integrity

sqlite3 /var/lib/pqcs/pqcs.db "PRAGMA integrity_check;"

Migration to PostgreSQL (Advanced)

# Install PostgreSQL adapter
pip install psycopg2-binary

# Export SQLite
sqlite3 pqcs.db .dump > dump.sql

# Import to PostgreSQL
psql -U postgres -d pqcs < dump.sql

# Update DATABASE_URL
export DATABASE_URL="postgresql://user:pass@localhost/pqcs"

Configuration Management

Settings Storage

Settings stored in database table settings:

settings = {
    "scan_rate_limit": "5",
    "default_scan_timeout": "300",
    "nmap_args": "-sV -O -sS",
    "ssh_username": "",
    "ssh_password": "",
    "custom_ports": "",
}

Updating Settings

Via API

# Update single setting
curl -X PUT http://localhost:8080/api/v1/settings/scan_rate_limit \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"value": "10"}'

# Get all settings
curl http://localhost:8080/api/v1/settings \
  -H "Authorization: Bearer <token>"

Via Python

from pqcs.db import init_db, Setting
from pqcs.config import AppConfig

config = AppConfig()
db = init_db(config.database.url)

# Update setting
setting = db.query(Setting).filter_by(key="scan_rate_limit").first()
if setting:
    setting.value = "10"
else:
    setting = Setting(key="scan_rate_limit", value="10")
    db.add(setting)
db.commit()

Environment Variables Priority

Priority order (highest first):

  1. Environment variables (.env)
  2. Database settings
  3. Default values in code


Risk Scoring Configuration

Understanding SCAD Weights

Default weights (must sum to 100%):

CriterionDefaultWhen to Increase
Algorithm Vulnerability25%Known CVEs, weak algorithms
Data Lifespan20%Long-term sensitive data
Key Size15%Large RSA keys (>2048)
System Criticality15%Business-critical systems
Legacy Status10%EOL systems
Third Party Dependency10%Vendor-managed crypto
NCII Sector5%Regulated industries

Adjusting for Industry

Financial Services

Algorithm Vulnerability: 30%
Data Lifespan: 25%
System Criticality: 20%
Key Size: 10%
Legacy Status: 5%
Third Party: 5%
NCII Sector: 5%

Healthcare

Data Lifespan: 35%
Algorithm Vulnerability: 25%
System Criticality: 15%
Key Size: 10%
Legacy Status: 5%
Third Party: 5%
NCII Sector: 5%

Recalculating Scores

After changing weights, rescore existing devices:

from pqcs.scoring.engine import ScoringEngine
from pqcs.db import init_db, Device
from pqcs.config import AppConfig

config = AppConfig()
db = init_db(config.database.url)
scoring = ScoringEngine(config.scoring, config.ncii_sector)

devices = db.query(Device).all()
for device in devices:
    score = scoring.calculate(device, device.assets)
    # Update score in database
    print(f"Rescored {device.ip_address}: {score.overall_score}")

db.commit()

Monitoring & Alerts

Health Check

# Basic health check
curl http://localhost:8080/health

# Expected response:
{
  "status": "ok",
  "timestamp": "2026-07-22T12:00:00",
  "version": "0.1.0",
  "database": "connected"
}

Log Monitoring

Important Log Entries

PatternMeaningAction
scan completedNormal operationNone
collector errorScanning issueCheck collector config
database errorDB issueCheck connectivity
authentication failedLogin attemptReview if suspicious
rate limit exceededToo many scansAdjust thresholds

Log Rotation

# Add to /etc/logrotate.d/pqcs
/opt/pqcs/logs/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    create 644 pqcs pqcs
}

Setting Up Alerts

Email Notifications

# In settings
alert_email = "admin@example.com"
alert_events = ["scan_failed", "high_priority_device", "certificate_expiring"]

Slack Notifications

# Custom webhook
slack_webhook = "https://hooks.slack.com/..."

Maintenance Tasks

Daily

Weekly

Monthly

Quarterly


Backup & Recovery

Demo Dataset Backup and Loading

ceria demo-data --count 500 --snapshots 4 --seed 20260722

The command creates a timestamped SQLite backup in backups/ before changing scan-domain records. Configuration and users are preserved. Use --backup-dir PATH to change the destination, --append to retain existing scan data, and --seed to reproduce a dataset.

Generated Report Retention

Report metadata is stored in the database and completed files under outputs/reports. Back up both locations if generated reports must survive recovery. Administrators can download or delete completed files from /report-exports; active jobs cannot be deleted.

Backup Strategy

Automated Daily Backup

#!/bin/bash
# /opt/pqcs/backup.sh

DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/pqcs"
SOURCE_DB="/var/lib/pqcs/pqcs.db"

# Create backup dir
mkdir -p $BACKUP_DIR

# Backup database
cp $SOURCE_DB "$BACKUP_DIR/pqcs_$DATE.db"

# Compress
gzip "$BACKUP_DIR/pqcs_$DATE.db"

# Backup configuration
tar czf "$BACKUP_DIR/config_$DATE.tar.gz" \
  /opt/pqcs/.env \
  /opt/pqcs/settings.json

# Upload to S3 (optional)
aws s3 cp "$BACKUP_DIR/pqcs_$DATE.db.gz" s3://pqcs-backups/

# Keep only last 30 days
find $BACKUP_DIR -name "*.gz" -mtime +30 -delete

echo "Backup completed: pqcs_$DATE.db.gz"

Crontab:

0 2 * * * /opt/pqcs/backup.sh >> /var/log/pqcs/backup.log 2>&1

Recovery Procedure

Scenario 1: Database Corruption

# Stop application
sudo systemctl stop pqcs

# Restore from backup
sudo cp /backups/pqcs/pqcs_20260701_020000.db /var/lib/pqcs/pqcs.db
sudo chown pqcs:pqcs /var/lib/pqcs/pqcs.db

# Restart
sudo systemctl start pqcs
sudo systemctl status pqcs

Scenario 2: Complete Server Failure

# On new server

# 1. Install PQC Scanner
pip install -e .

# 2. Restore database
cp /backups/pqcs/pqcs_latest.db /var/lib/pqcs/pqcs.db

# 3. Restore config
cp /backups/pqcs/.env /opt/pqcs/.env

# 4. Set permissions
chown -R pqcs:pqcs /var/lib/pqcs /opt/pqcs

# 5. Start service
systemctl start pqcs

Troubleshooting

Database Locked

# Find and kill blocking process
fuser /var/lib/pqcs/pqcs.db

# Or wait for timeout (SQLite release locks on timeout)

Scan Stuck

# Check if nmap is running
ps aux | grep nmap

# Kill hanging nmap
sudo pkill -9 nmap

# Restart scan

Memory Issues

If scanning large networks:

# Reduce concurrent hosts
export MAX_CONCURRENT_HOSTS=25

# Or use smaller subnets
ceria scan 192.168.1.0/25  # /25 instead of /24

SSL Certificate Errors

# Update certificates
sudo update-ca-certificates

# Or disable verification (testing only)
export REQUESTS_CA_BUNDLE=""

High CPU Usage

# Limit scan threads
export OMP_NUM_THREADS=4

# Or schedule scans during off-hours

Last Updated: 2026-07-22


Deployment Guide

Complete guide for deploying PQC Scanner in production environments.


Table of Contents

  1. Local Development
  2. Production Server
  3. cPanel Shared Hosting
  4. Docker Deployment
  5. Cloud Deployment
  6. SSL/TLS Setup
  7. Security Hardening

Local Development

Installation

# Clone repository
git clone <repo-url>
cd pqc

# Create virtual environment
python -m venv venv
source venv/bin/activate  # Linux/macOS
# or
venv\Scripts\activate  # Windows

# Install dependencies
pip install -e ".[dev]"

# Run database migrations (if using Alembic)
# alembic upgrade head

# Start development server
ceria dashboard --port 8080 --verbose

Environment Variables

Create .env file:

# Required
SECRET_KEY=$(openssl rand -hex 32)
DATABASE_URL=sqlite:///pqcs.db

# Optional
DEBUG=true
LOG_LEVEL=DEBUG
DASHBOARD_HOST=127.0.0.1
DASHBOARD_PORT=8080

Production Server

System Requirements

Installation on Ubuntu/Debian

# Update system
sudo apt-get update
sudo apt-get upgrade -y

# Install dependencies
sudo apt-get install -y python3 python3-pip python3-venv nmap openssl libpango-1.0-0 libpangoft2-1.0-0

# Create user
sudo useradd -r -s /bin/false pqcs

# Create directories
sudo mkdir -p /opt/pqcs
sudo mkdir -p /var/lib/pqcs
sudo mkdir -p /var/log/pqcs
sudo mkdir -p /var/lib/pqcs/reports
sudo chown -R pqcs:pqcs /opt/pqcs /var/lib/pqcs /var/log/pqcs

# Clone application
cd /opt/pqcs
sudo -u pqcs git clone <repo-url> .

# Setup virtual environment
sudo -u pqcs python3 -m venv venv
sudo -u pqcs venv/bin/pip install -e .

# Create .env file
sudo -u pqcs tee /opt/pqcs/.env << 'EOF'
SECRET_KEY=$(openssl rand -hex 32)
DATABASE_URL=sqlite:////var/lib/pqcs/pqcs.db
DASHBOARD_HOST=0.0.0.0
DASHBOARD_PORT=8080
EOF

# Initialize database
sudo -u pqcs venv/bin/python -c "
from pqcs.db import init_db
from pqcs.config import AppConfig
config = AppConfig.from_env()
init_db(config.database.url)
"

Systemd Service

Create /etc/systemd/system/pqcs.service:

[Unit]
Description=PQC Scanner Dashboard
After=network.target

[Service]
Type=simple
User=pqcs
Group=pqcs
WorkingDirectory=/opt/pqcs
Environment="PATH=/opt/pqcs/venv/bin"
EnvironmentFile=/opt/pqcs/.env
ExecStart=/opt/pqcs/venv/bin/uvicorn pqcs.web.app:create_app --host 0.0.0.0 --port 8080
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Start the service:

sudo systemctl daemon-reload
sudo systemctl enable pqcs
sudo systemctl start pqcs
sudo systemctl status pqcs

Nginx Reverse Proxy

Install Nginx:

sudo apt-get install nginx

Create /etc/nginx/sites-available/pqcs:

server {
    listen 80;
    server_name pqcs.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name pqcs.example.com;

    ssl_certificate /etc/letsencrypt/live/pqcs.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/pqcs.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }

    location /static {
        alias /opt/pqcs/pqcs/web/static;
    }
}

Enable site:

sudo ln -s /etc/nginx/sites-available/pqcs /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

cPanel Shared Hosting

Prerequisites

Deployment Steps

1. Upload Files

Upload via FTP or SSH to:

/home/username/public_html/pqcs.example.com/

Files needed:

2. Configure .htaccess

Create .htaccess in application root:

PassengerPython /home/username/virtualenv/pqcs/3.11/bin/python
PassengerAppRoot /home/username/public_html/pqcs.example.com
PassengerAppType wsgi
PassengerStartupFile passenger_wsgi.py

3. Create WSGI File

passenger_wsgi.py:
#!/usr/bin/env python3
import os
import sys

APP_ROOT = "/home/username/public_html/pqcs.example.com"
sys.path.insert(0, APP_ROOT)
os.chdir(APP_ROOT)

# Use existing database or create outside web root
DB_PATH = "/home/username/db/pqcs.db"
os.environ['DATABASE_URL'] = f'sqlite:///{DB_PATH}'

from pqcs.web.app import create_app
from pqcs.config import AppConfig

config = AppConfig()
config.database.url = f'sqlite:///{DB_PATH}'

asgi_app = create_app(config)

# WSGI wrapper for FastAPI
import asyncio

class WSGIWrapper:
    def __init__(self, asgi_app):
        self.asgi_app = asgi_app

    def __call__(self, environ, start_response):
        scope = {
            'type': 'http',
            'asgi': {'version': '3.0'},
            'method': environ.get('REQUEST_METHOD', 'GET'),
            'scheme': environ.get('wsgi.url_scheme', 'https'),
            'path': environ.get('PATH_INFO', '/'),
            'query_string': environ.get('QUERY_STRING', '').encode(),
            'headers': [],
            'server': (environ.get('SERVER_NAME', 'localhost'), int(environ.get('SERVER_PORT', 443))),
            'client': (environ.get('REMOTE_ADDR', '127.0.0.1'), int(environ.get('REMOTE_PORT', 0) or 0)),
        }

        # Handle headers and body
        body = b''
        if 'wsgi.input' in environ:
            cl = environ.get('CONTENT_LENGTH')
            if cl:
                try:
                    body = environ['wsgi.input'].read(int(cl))
                except:
                    pass

        messages = []

        async def receive():
            return {'type': 'http.request', 'body': body, 'more_body': False}

        async def send(message):
            messages.append(message)

        loop = asyncio.new_event_loop()
        try:
            asyncio.set_event_loop(loop)
            loop.run_until_complete(self.asgi_app(scope, receive, send))

            start_msg = next(m for m in messages if m['type'] == 'http.response.start')
            status = f"{start_msg['status']}"
            headers = [(k.decode() if isinstance(k, bytes) else k,
                       v.decode() if isinstance(v, bytes) else v)
                      for k, v in start_msg.get('headers', [])]
            start_response(status, headers)

            body_parts = [m.get('body', b'') for m in messages if m['type'] == 'http.response.body']
            return body_parts if body_parts else [b'']
        finally:
            loop.close()

application = WSGIWrapper(asgi_app)

4. Setup Python App in cPanel

  1. Login to cPanel
  2. Go to Setup Python App
  3. Click Create Application
  4. Configure:
  1. Click Create

5. Install Dependencies

SSH into server:

cd /home/username/public_html/pqcs.example.com
source /home/username/virtualenv/pqcs/3.11/bin/activate
pip install -r requirements.txt

# Create database directory outside web root
mkdir -p /home/username/db
chmod 755 /home/username/db

# Initialize database
python << 'EOF'
import sys
sys.path.insert(0, '/home/username/public_html/pqcs.example.com')
from pqcs.db import init_db
init_db('sqlite:////home/username/db/pqcs.db')
print("Database initialized")
EOF

# Create restart signal
mkdir -p tmp
touch tmp/restart.txt

6. Environment Variables

In cPanel Python App settings, add:

VariableValue
SECRET_KEY(openssl rand -hex 32)
DATABASE_URLsqlite:////home/username/db/pqcs.db

7. Troubleshooting cPanel

Common issues:

IssueSolution
403 ForbiddenCheck file permissions (chmod 644 .htaccess passenger_wsgi.py)
500 ErrorCheck Passenger log in cPanel
Database not foundUse absolute path in DATABASE_URL
Import errorsRun pip install in virtualenv

Docker Deployment

Dockerfile

FROM python:3.11-slim

# Install system dependencies
RUN apt-get update && apt-get install -y \
    nmap \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Create app directory
WORKDIR /app

# Copy requirements first for caching
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application
COPY . .
RUN pip install -e .

# Create directories
RUN mkdir -p /data /logs

# Environment
ENV DATABASE_URL=sqlite:////data/pqcs.db
ENV DASHBOARD_HOST=0.0.0.0
ENV DASHBOARD_PORT=8080
ENV PYTHONUNBUFFERED=1

# Initialize database
RUN python -c "from pqcs.db import init_db; init_db('$DATABASE_URL')"

EXPOSE 8080

CMD ["pqcs", "dashboard", "--host", "0.0.0.0", "--port", "8080"]

docker-compose.yml

version: '3.8'

services:
  pqcs:
    build: .
    container_name: pqcs
    ports:
      - "8080:8080"
    volumes:
      - ./data:/data
      - ./logs:/logs
    environment:
      - SECRET_KEY=${SECRET_KEY}
      - DATABASE_URL=sqlite:////data/pqcs.db
    restart: unless-stopped
    networks:
      - pqcs-network

  # Optional: Nginx reverse proxy
  nginx:
    image: nginx:alpine
    container_name: pqcs-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - pqcs
    networks:
      - pqcs-network

networks:
  pqcs-network:
    driver: bridge

Build and Run

# Generate secret key
export SECRET_KEY=$(openssl rand -hex 32)

# Create directories
mkdir -p data logs

# Build and run
docker-compose up -d --build

# View logs
docker-compose logs -f pqcs

# Stop
docker-compose down

Cloud Deployment

AWS Elastic Beanstalk

# Install EB CLI
pip install awsebcli

# Initialize
eb init -p python-3.11 pqcs-app

# Create environment
eb create pqcs-env

# Deploy
eb deploy

Heroku

# Create Procfile
echo "web: ceria dashboard --port \$PORT" > Procfile

# Create app
heroku create pqcs-app

# Set environment variables
heroku config:set SECRET_KEY=$(openssl rand -hex 32)
heroku config:set DATABASE_URL=$(heroku config:get DATABASE_URL)

# Deploy
git push heroku main

Railway

# Install Railway CLI
npm install -g @railway/cli

# Login
railway login

# Initialize
railway init

# Deploy
railway up

# Add environment variables in Railway dashboard

SSL/TLS Setup

Let's Encrypt (certbot)

# Install certbot
sudo apt-get install certbot python3-certbot-nginx

# Obtain certificate
sudo certbot --nginx -d pqcs.example.com

# Auto-renewal
sudo certbot renew --dry-run

Self-Signed Certificate (testing)

# Generate certificate
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout /etc/ssl/private/pqcs.key \
  -out /etc/ssl/certs/pqcs.crt

# Update Nginx config to use these

Security Hardening

File Permissions

# Database
chmod 600 /var/lib/pqcs/pqcs.db
chown pqcs:pqcs /var/lib/pqcs/pqcs.db

# Application files
chmod 755 /opt/pqcs
chmod 644 /opt/pqcs/.env
chown -R pqcs:pqcs /opt/pqcs

# Logs
chmod 755 /var/log/pqcs
chown pqcs:pqcs /var/log/pqcs

Firewall Rules

# Allow only HTTPS
sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow 443/tcp
sudo ufw enable

# Or use iptables
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -P INPUT DROP

Fail2Ban

Install and configure for brute force protection:

sudo apt-get install fail2ban

Create /etc/fail2ban/jail.local:

[pqcs]
enabled = true
port = http,https
filter = pqcs
logpath = /var/log/pqcs/access.log
maxretry = 5
bantime = 3600

Security Headers

Add to Nginx config:

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' cdn.jsdelivr.net unpkg.com; style-src 'self' 'unsafe-inline' cdn.jsdelivr.net; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';" always;

Backup Strategy

Persist and back up both the database and generated-report directory. If outputs/reports remains inside the application checkout, include it in deployment backups or configure the service working directory on persistent storage.

# Daily database backup
#!/bin/bash
BACKUP_DIR="/backups/pqcs"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR

cp /var/lib/pqcs/pqcs.db "$BACKUP_DIR/pqcs_$DATE.db"
gzip "$BACKUP_DIR/pqcs_$DATE.db"

# Keep only last 30 days
find $BACKUP_DIR -name "*.gz" -mtime +30 -delete

Add to crontab:

0 2 * * * /opt/pqcs/backup.sh


Monitoring

Health Check Endpoint

GET /health

Returns:

{
  "status": "ok",
  "timestamp": "2026-07-22T12:00:00",
  "version": "0.1.0",
  "database": "connected"
}

Prometheus Metrics (optional)

Add to requirements:

prometheus-client>=0.17.0


Last Updated: 2026-07-22


Cryptographic Collectors

1. TLS/SSL Collector

Scans for: Visual output in scan results: topic-scan-results.png TLS collector finds certificates and cipher suites on HTTPS services Tools: sslyze, nmap scripts Confidence: 0.95 Default Ports: 443, 8443, 9443 Example findings in device detail: topic-device-assets.png Detail view of TLS assets including certificates and cipher suites

2. SSH Collector

Scans for: Tools: ssh-audit, paramiko Confidence: 0.90

3. VPN Collector

Scans for: Tools: ike-scan

4. Database Collector

Scans for: Requires: Database credentials

5. OS Crypto Collector

Scans for: Requires: SSH credentials

6. App Config Collector

Scans for: Requires: SSH credentials

7. Certificate Store Collector

Scans for: Requires: SSH credentials

Demo Collector Coverage

Enterprise demo data includes findings associated with TLS, SSH, VPN, database encryption, operating-system crypto, application configuration, and certificate-store collectors.


Default Ports

SSH

TLS/HTTPS

VPN

Database

Mail

LDAP/Kerberos

RDP/VNC

SMB

Custom Ports

Add custom ports in Settings:

Format: comma-separated or ranges
Example: 9000,9001,9002-9010,10000



Troubleshooting

Common Issues

403 Forbidden (cPanel)

Symptom: Browser shows "403 Forbidden" error Visual indicator: 01-login.png Expected: This login page should appear. If you see 403 instead, check permissions. Solution:
# Fix file permissions via SSH
cd /home/username/public_html/pqcs.example.com
chmod 644 .htaccess passenger_wsgi.py
chmod 755 pqcs/

500 Internal Server Error

Symptom: "500 Internal Server Error" page Solution:
  1. Check cPanel > Logs > Error Log
  2. Verify Python dependencies:
  3. source /home/username/virtualenv/pqcs/3.11/bin/activate
    pip install -r requirements.txt
    
  1. Check passenger_wsgi.py syntax:
  2. python -m py_compile passenger_wsgi.py
    

Database Connection Issues

Symptom: "OperationalError: unable to open database file" Solution:
# Create database directory outside web root
mkdir -p /home/username/db
chmod 755 /home/username/db
chmod 666 /home/username/db/pqcs.db  # or 644 after creation

# Update DATABASE_URL environment variable:
# sqlite:////home/username/db/pqcs.db

Login Failures (422 Error)

Symptom: "Failed to load resource: the server responded with a status of 422" 02-login-filled.png If login fails after entering correct credentials: Solution:
  1. Ensure SECRET_KEY environment variable is set in cPanel
  2. Check bcrypt and pyjwt are installed:
  3. pip list | grep -E 'bcrypt|PyJWT'
    
  1. Reset password via CLI if needed:
  2. python << 'EOF'
    import bcrypt
    from pqcs.db import init_db, User
    from pqcs.config import AppConfig
    
    config = AppConfig()
    db = init_db(config.database.url)
    
    user = db.query(User).filter_by(username="admin").first()
    if user:
        new_pw = bcrypt.hashpw(b"admin", bcrypt.gensalt())
        user.password_hash = new_pw.decode()
        db.commit()
        print("Password reset to: admin")
    EOF
    

SSH Connection Failures

06-settings.png Configure SSH credentials in Settings page

Application Won't Start

cPanel specific:
  1. Check app shows as "Running" in cPanel Python App
  2. Verify passenger_wsgi.py exists and exposes application
  3. Restart the app:
  4. cd /home/username/public_html/pqcs.example.com
    mkdir -p tmp && touch tmp/restart.txt
    

Debug Mode

# Enable debug logging
ceria --verbose dashboard

# Or set environment variable
export LOG_LEVEL=DEBUG
ceria dashboard

Log Locations

PlatformLocation
LocalConsole (--verbose)
cPanel/home2/yusricom/logs/passenger.log
systemdjournalctl -u pqcs

Getting Help

  1. Check this troubleshooting guide
  2. Review relevant section in User Manual
  3. Check application logs
  4. Open issue in repository

Report Job Troubleshooting


About

CERIA Logo

Project Information

AttributeDetails
NameCERIA
Full TitleCrypto Evaluation & Readiness for Infrastructure Adaptation (Post-Quantum)
Version1.0
StatusProduction Ready
AuthorYusri
Contactyusri@dysfunktion.tech
Live URLhttps://pqcs.dysfunktion.tech
Last Updated2026-07-22

About the Author

CERIA was developed by Yusri to address the growing need for post-quantum cryptography assessment tools in Malaysia.

Mission

To help organizations prepare for the quantum computing threat by providing accessible, comprehensive cryptographic assessment tools aligned with Malaysia's NCII framework.

What is CERIA?

CERIA is a Post-Quantum Cryptography Scanner that implements Malaysia's NCII SCAD (Sectoral Cybersecurity Assessment Dashboard) Framework. It helps organizations:


Deployment

CERIA is currently deployed and running on cPanel Shared Hosting at https://pqcs.dysfunktion.tech.

Technical Stack


License

CERIA - Post-Quantum Cryptography Scanner

Author: Yusri (yusri@dysfunktion.tech)



Built with care for the Malaysian cybersecurity community.

Documentation Release

This documentation reflects the 2026-07-22 release with enterprise demo data, paginated analysis, persistent report queues, WeasyPrint PDF generation, and populated PQC Excel exports.